mpush
This commit is contained in:
0
src-tauri/src/serial_core/codecs/hand_gatway.rs
Normal file
0
src-tauri/src/serial_core/codecs/hand_gatway.rs
Normal file
@@ -17,7 +17,6 @@ const ACTIVE_CELL_MIN_VALUE: f32 = 18.0;
|
|||||||
const ACTIVE_CELL_PEAK_RATIO: f32 = 0.14;
|
const ACTIVE_CELL_PEAK_RATIO: f32 = 0.14;
|
||||||
const MIN_ACTIVE_CELLS: usize = 3;
|
const MIN_ACTIVE_CELLS: usize = 3;
|
||||||
|
|
||||||
const ANCHOR_LERP_ALPHA: f32 = 0.018;
|
|
||||||
const VECTOR_SMOOTHING_ALPHA: f32 = 0.16;
|
const VECTOR_SMOOTHING_ALPHA: f32 = 0.16;
|
||||||
|
|
||||||
const REPORT_MAGNITUDE_ENTER: f32 = 0.12;
|
const REPORT_MAGNITUDE_ENTER: f32 = 0.12;
|
||||||
@@ -29,6 +28,12 @@ const REPORT_HOLD_FRAMES: usize = 10;
|
|||||||
const ASYMMETRY_WEIGHT: f32 = 1.1;
|
const ASYMMETRY_WEIGHT: f32 = 1.1;
|
||||||
const DRIFT_WEIGHT: f32 = 0.65;
|
const DRIFT_WEIGHT: f32 = 0.65;
|
||||||
const MOTION_WEIGHT: f32 = 0.25;
|
const MOTION_WEIGHT: f32 = 0.25;
|
||||||
|
const EDGE_ASYMMETRY_DAMPING: f32 = 0.35;
|
||||||
|
const EDGE_INWARD_ROLLING_BIAS: f32 = 0.55;
|
||||||
|
const EDGE_START_COP_THRESHOLD: f32 = 0.45;
|
||||||
|
const EDGE_START_BIAS_WEIGHT: f32 = 1.1;
|
||||||
|
const ROLLING_FRICTION_ALPHA: f32 = 0.68;
|
||||||
|
const ROLLING_FRICTION_MIN_MAGNITUDE: f32 = 0.05;
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
pub struct PztSpatialAnalysis {
|
pub struct PztSpatialAnalysis {
|
||||||
@@ -50,6 +55,8 @@ pub struct PztProcessor {
|
|||||||
anchor_cop_y: Option<f32>,
|
anchor_cop_y: Option<f32>,
|
||||||
last_cop_x: Option<f32>,
|
last_cop_x: Option<f32>,
|
||||||
last_cop_y: Option<f32>,
|
last_cop_y: Option<f32>,
|
||||||
|
edge_start_bias_x: f32,
|
||||||
|
edge_start_bias_y: f32,
|
||||||
smoothed_x: f32,
|
smoothed_x: f32,
|
||||||
smoothed_y: f32,
|
smoothed_y: f32,
|
||||||
report_active: bool,
|
report_active: bool,
|
||||||
@@ -84,6 +91,8 @@ impl PztProcessor {
|
|||||||
anchor_cop_y: None,
|
anchor_cop_y: None,
|
||||||
last_cop_x: None,
|
last_cop_x: None,
|
||||||
last_cop_y: None,
|
last_cop_y: None,
|
||||||
|
edge_start_bias_x: 0.0,
|
||||||
|
edge_start_bias_y: 0.0,
|
||||||
smoothed_x: 0.0,
|
smoothed_x: 0.0,
|
||||||
smoothed_y: 0.0,
|
smoothed_y: 0.0,
|
||||||
report_active: false,
|
report_active: false,
|
||||||
@@ -100,6 +109,8 @@ impl PztProcessor {
|
|||||||
self.anchor_cop_y = None;
|
self.anchor_cop_y = None;
|
||||||
self.last_cop_x = None;
|
self.last_cop_x = None;
|
||||||
self.last_cop_y = None;
|
self.last_cop_y = None;
|
||||||
|
self.edge_start_bias_x = 0.0;
|
||||||
|
self.edge_start_bias_y = 0.0;
|
||||||
self.smoothed_x = 0.0;
|
self.smoothed_x = 0.0;
|
||||||
self.smoothed_y = 0.0;
|
self.smoothed_y = 0.0;
|
||||||
}
|
}
|
||||||
@@ -273,6 +284,112 @@ impl PztProcessor {
|
|||||||
(angle, magnitude)
|
(angle, magnitude)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn contact_touches_edge(stats: &ContactStats) -> bool {
|
||||||
|
stats.min_row == 0
|
||||||
|
|| stats.max_row == SENSOR_ROWS - 1
|
||||||
|
|| stats.min_col == 0
|
||||||
|
|| stats.max_col == SENSOR_COLS - 1
|
||||||
|
}
|
||||||
|
|
||||||
|
fn damp_edge_asymmetry(
|
||||||
|
stats: &ContactStats,
|
||||||
|
kinematic_x: f32,
|
||||||
|
kinematic_y: f32,
|
||||||
|
) -> (f32, f32) {
|
||||||
|
let mut asymmetry_x = stats.asymmetry_x * ASYMMETRY_WEIGHT;
|
||||||
|
let mut asymmetry_y = stats.asymmetry_y * ASYMMETRY_WEIGHT;
|
||||||
|
|
||||||
|
if stats.min_col == 0 && asymmetry_x < 0.0 {
|
||||||
|
asymmetry_x = -asymmetry_x * EDGE_INWARD_ROLLING_BIAS;
|
||||||
|
}
|
||||||
|
if stats.max_col == SENSOR_COLS - 1 && asymmetry_x > 0.0 {
|
||||||
|
asymmetry_x = -asymmetry_x * EDGE_INWARD_ROLLING_BIAS;
|
||||||
|
}
|
||||||
|
if stats.min_row == 0 && asymmetry_y < 0.0 {
|
||||||
|
asymmetry_y = -asymmetry_y * EDGE_INWARD_ROLLING_BIAS;
|
||||||
|
}
|
||||||
|
if stats.max_row == SENSOR_ROWS - 1 && asymmetry_y > 0.0 {
|
||||||
|
asymmetry_y = -asymmetry_y * EDGE_INWARD_ROLLING_BIAS;
|
||||||
|
}
|
||||||
|
|
||||||
|
if Self::contact_touches_edge(stats) {
|
||||||
|
let opposing_dot = asymmetry_x * kinematic_x + asymmetry_y * kinematic_y;
|
||||||
|
let kinematic_mag = (kinematic_x * kinematic_x + kinematic_y * kinematic_y).sqrt();
|
||||||
|
if opposing_dot < 0.0 && kinematic_mag >= ROLLING_FRICTION_MIN_MAGNITUDE {
|
||||||
|
asymmetry_x *= EDGE_ASYMMETRY_DAMPING;
|
||||||
|
asymmetry_y *= EDGE_ASYMMETRY_DAMPING;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
(asymmetry_x, asymmetry_y)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn edge_start_bias(stats: &ContactStats) -> (f32, f32) {
|
||||||
|
let center_x = (SENSOR_COLS - 1) as f32 * 0.5;
|
||||||
|
let center_y = (SENSOR_ROWS - 1) as f32 * 0.5;
|
||||||
|
let normalized_x = ((stats.cop_x - center_x) / center_x.max(1.0)).clamp(-1.0, 1.0);
|
||||||
|
let normalized_y = ((stats.cop_y - center_y) / center_y.max(1.0)).clamp(-1.0, 1.0);
|
||||||
|
|
||||||
|
let mut bias_x = 0.0;
|
||||||
|
let mut bias_y = 0.0;
|
||||||
|
|
||||||
|
if stats.min_col == 0 || stats.max_col == SENSOR_COLS - 1 {
|
||||||
|
bias_x = Self::edge_start_axis_bias(normalized_x);
|
||||||
|
}
|
||||||
|
if stats.min_row == 0 || stats.max_row == SENSOR_ROWS - 1 {
|
||||||
|
bias_y = Self::edge_start_axis_bias(normalized_y);
|
||||||
|
}
|
||||||
|
|
||||||
|
(bias_x, bias_y)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn edge_start_axis_bias(normalized_axis: f32) -> f32 {
|
||||||
|
let distance = normalized_axis.abs();
|
||||||
|
if distance <= EDGE_START_COP_THRESHOLD {
|
||||||
|
return 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
let strength = ((distance - EDGE_START_COP_THRESHOLD) / (1.0 - EDGE_START_COP_THRESHOLD))
|
||||||
|
.clamp(0.0, 1.0);
|
||||||
|
-normalized_axis.signum() * strength * EDGE_START_BIAS_WEIGHT
|
||||||
|
}
|
||||||
|
|
||||||
|
fn apply_rolling_friction(
|
||||||
|
previous_x: f32,
|
||||||
|
previous_y: f32,
|
||||||
|
current_x: f32,
|
||||||
|
current_y: f32,
|
||||||
|
) -> (f32, f32) {
|
||||||
|
let previous_mag = (previous_x * previous_x + previous_y * previous_y).sqrt();
|
||||||
|
let current_mag = (current_x * current_x + current_y * current_y).sqrt();
|
||||||
|
|
||||||
|
if previous_mag < ROLLING_FRICTION_MIN_MAGNITUDE
|
||||||
|
|| current_mag < ROLLING_FRICTION_MIN_MAGNITUDE
|
||||||
|
{
|
||||||
|
return (current_x, current_y);
|
||||||
|
}
|
||||||
|
|
||||||
|
let dot = previous_x * current_x + previous_y * current_y;
|
||||||
|
if dot >= 0.0 {
|
||||||
|
return (current_x, current_y);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mixed_x = current_x * (1.0 - ROLLING_FRICTION_ALPHA)
|
||||||
|
+ previous_x * ROLLING_FRICTION_ALPHA;
|
||||||
|
let mixed_y = current_y * (1.0 - ROLLING_FRICTION_ALPHA)
|
||||||
|
+ previous_y * ROLLING_FRICTION_ALPHA;
|
||||||
|
|
||||||
|
if mixed_x * previous_x + mixed_y * previous_y >= 0.0 {
|
||||||
|
return (mixed_x, mixed_y);
|
||||||
|
}
|
||||||
|
|
||||||
|
let keep_mag = previous_mag.min(current_mag) * 0.5;
|
||||||
|
(
|
||||||
|
previous_x / previous_mag * keep_mag,
|
||||||
|
previous_y / previous_mag * keep_mag,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
fn update_contact_state(&mut self, raw_frame: &[f32], frame: &[f32]) -> bool {
|
fn update_contact_state(&mut self, raw_frame: &[f32], frame: &[f32]) -> bool {
|
||||||
if self.contact_active {
|
if self.contact_active {
|
||||||
if Self::is_contact_exit_frame(frame) {
|
if Self::is_contact_exit_frame(frame) {
|
||||||
@@ -376,6 +493,9 @@ impl PztProcessor {
|
|||||||
self.anchor_cop_y = Some(stats.cop_y);
|
self.anchor_cop_y = Some(stats.cop_y);
|
||||||
self.last_cop_x = Some(stats.cop_x);
|
self.last_cop_x = Some(stats.cop_x);
|
||||||
self.last_cop_y = Some(stats.cop_y);
|
self.last_cop_y = Some(stats.cop_y);
|
||||||
|
let (edge_start_bias_x, edge_start_bias_y) = Self::edge_start_bias(&stats);
|
||||||
|
self.edge_start_bias_x = edge_start_bias_x;
|
||||||
|
self.edge_start_bias_y = edge_start_bias_y;
|
||||||
|
|
||||||
return Ok(self.stabilize_report(Self::weak_contact_analysis()));
|
return Ok(self.stabilize_report(Self::weak_contact_analysis()));
|
||||||
};
|
};
|
||||||
@@ -388,18 +508,25 @@ impl PztProcessor {
|
|||||||
let motion_x = stats.cop_x - last_x;
|
let motion_x = stats.cop_x - last_x;
|
||||||
let motion_y = stats.cop_y - last_y;
|
let motion_y = stats.cop_y - last_y;
|
||||||
|
|
||||||
let combined_x = stats.asymmetry_x * ASYMMETRY_WEIGHT
|
let kinematic_x = drift_x * DRIFT_WEIGHT + motion_x * MOTION_WEIGHT;
|
||||||
+ drift_x * DRIFT_WEIGHT
|
let kinematic_y = drift_y * DRIFT_WEIGHT + motion_y * MOTION_WEIGHT;
|
||||||
+ motion_x * MOTION_WEIGHT;
|
let edge_bias_x = self.edge_start_bias_x;
|
||||||
let combined_y = stats.asymmetry_y * ASYMMETRY_WEIGHT
|
let edge_bias_y = self.edge_start_bias_y;
|
||||||
+ drift_y * DRIFT_WEIGHT
|
let (asymmetry_x, asymmetry_y) =
|
||||||
+ motion_y * MOTION_WEIGHT;
|
Self::damp_edge_asymmetry(&stats, kinematic_x + edge_bias_x, kinematic_y + edge_bias_y);
|
||||||
|
|
||||||
|
let combined_x = asymmetry_x + kinematic_x + edge_bias_x;
|
||||||
|
let combined_y = asymmetry_y + kinematic_y + edge_bias_y;
|
||||||
|
let (combined_x, combined_y) = Self::apply_rolling_friction(
|
||||||
|
self.smoothed_x,
|
||||||
|
self.smoothed_y,
|
||||||
|
combined_x,
|
||||||
|
combined_y,
|
||||||
|
);
|
||||||
|
|
||||||
self.smoothed_x += (combined_x - self.smoothed_x) * VECTOR_SMOOTHING_ALPHA;
|
self.smoothed_x += (combined_x - self.smoothed_x) * VECTOR_SMOOTHING_ALPHA;
|
||||||
self.smoothed_y += (combined_y - self.smoothed_y) * VECTOR_SMOOTHING_ALPHA;
|
self.smoothed_y += (combined_y - self.smoothed_y) * VECTOR_SMOOTHING_ALPHA;
|
||||||
|
|
||||||
self.anchor_cop_x = Some(anchor_x + drift_x * ANCHOR_LERP_ALPHA);
|
|
||||||
self.anchor_cop_y = Some(anchor_y + drift_y * ANCHOR_LERP_ALPHA);
|
|
||||||
self.last_cop_x = Some(stats.cop_x);
|
self.last_cop_x = Some(stats.cop_x);
|
||||||
self.last_cop_y = Some(stats.cop_y);
|
self.last_cop_y = Some(stats.cop_y);
|
||||||
|
|
||||||
@@ -446,7 +573,7 @@ impl PztProcessor {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::{PztProcessor, SENSOR_COLS, SENSOR_ROWS};
|
use super::{ContactStats, PztProcessor, SENSOR_COLS, SENSOR_ROWS};
|
||||||
|
|
||||||
fn index(row: usize, col: usize) -> usize {
|
fn index(row: usize, col: usize) -> usize {
|
||||||
row * SENSOR_COLS + col
|
row * SENSOR_COLS + col
|
||||||
@@ -460,6 +587,23 @@ mod tests {
|
|||||||
frame
|
frame
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn stats_touching_bottom_edge() -> ContactStats {
|
||||||
|
ContactStats {
|
||||||
|
total: 1000.0,
|
||||||
|
peak: 300.0,
|
||||||
|
active_total: 900.0,
|
||||||
|
active_cells: 6,
|
||||||
|
min_row: SENSOR_ROWS - 2,
|
||||||
|
max_row: SENSOR_ROWS - 1,
|
||||||
|
min_col: 2,
|
||||||
|
max_col: 4,
|
||||||
|
cop_x: 3.0,
|
||||||
|
cop_y: 10.5,
|
||||||
|
asymmetry_x: 0.0,
|
||||||
|
asymmetry_y: 1.0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn idle_frame_does_not_report_contact() {
|
fn idle_frame_does_not_report_contact() {
|
||||||
let mut processor = PztProcessor::new();
|
let mut processor = PztProcessor::new();
|
||||||
@@ -524,4 +668,29 @@ mod tests {
|
|||||||
let analysis = processor.get_pzt_analysis(&weak).unwrap();
|
let analysis = processor.get_pzt_analysis(&weak).unwrap();
|
||||||
assert!(analysis.reportable);
|
assert!(analysis.reportable);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn bottom_edge_outward_gradient_is_turned_inward() {
|
||||||
|
let stats = stats_touching_bottom_edge();
|
||||||
|
let (_asymmetry_x, asymmetry_y) = PztProcessor::damp_edge_asymmetry(&stats, 0.0, -0.2);
|
||||||
|
|
||||||
|
assert!(asymmetry_y < 0.0);
|
||||||
|
assert!(asymmetry_y > -1.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn bottom_edge_start_adds_fixed_upward_bias() {
|
||||||
|
let stats = stats_touching_bottom_edge();
|
||||||
|
let (_bias_x, bias_y) = PztProcessor::edge_start_bias(&stats);
|
||||||
|
|
||||||
|
assert!(bias_y < 0.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn rolling_friction_resists_one_frame_reversal() {
|
||||||
|
let (x, y) = PztProcessor::apply_rolling_friction(0.4, 0.0, -0.6, 0.0);
|
||||||
|
|
||||||
|
assert!(x > 0.0);
|
||||||
|
assert_eq!(y, 0.0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -48,7 +48,7 @@
|
|||||||
export let colorMapPreset: PressureColorMapPreset = "emerald";
|
export let colorMapPreset: PressureColorMapPreset = "emerald";
|
||||||
export let matrixDisplayMode: MatrixDisplayMode = "dots";
|
export let matrixDisplayMode: MatrixDisplayMode = "dots";
|
||||||
export let stageViewMode: StageViewMode = "webgl";
|
export let stageViewMode: StageViewMode = "webgl";
|
||||||
export let modelUrl = "/models/je-skin-model.glb";
|
export let modelUrl = "/models/je-skin-model.gltf";
|
||||||
export let replaySectionLabel = "";
|
export let replaySectionLabel = "";
|
||||||
export let replayPlayLabel = "";
|
export let replayPlayLabel = "";
|
||||||
export let replayPauseLabel = "";
|
export let replayPauseLabel = "";
|
||||||
|
|||||||
@@ -19,12 +19,13 @@
|
|||||||
|
|
||||||
const FLOOR_Y = -1.15;
|
const FLOOR_Y = -1.15;
|
||||||
const MODEL_FLOOR_CLEARANCE = 0.035;
|
const MODEL_FLOOR_CLEARANCE = 0.035;
|
||||||
const MODEL_TARGET_HEIGHT = 8.4;
|
const MODEL_TARGET_HEIGHT = 7.4;
|
||||||
const MODEL_MIN_SCALE = 0.02;
|
const MODEL_MIN_SCALE = 0.02;
|
||||||
const MODEL_MAX_SCALE = 80;
|
const MODEL_MAX_SCALE = 80;
|
||||||
const CAMERA_DISTANCE_FACTOR = 1.35;
|
const FIXED_CAMERA_POSITION = new THREE.Vector3(0.8, 3.25, 12.2);
|
||||||
const CAMERA_DISTANCE_MIN = 7.5;
|
const FIXED_CAMERA_TARGET = new THREE.Vector3(0, 2.55, 0);
|
||||||
const CAMERA_DISTANCE_MAX = 24;
|
const MODEL_FRONT_ROTATION_Y = -Math.PI / 2;
|
||||||
|
const MODEL_UPRIGHT_ROTATION_Z = Math.PI * 1.5;
|
||||||
|
|
||||||
$: copy =
|
$: copy =
|
||||||
locale === "zh-CN"
|
locale === "zh-CN"
|
||||||
@@ -128,7 +129,41 @@
|
|||||||
return Math.min(max, Math.max(min, value));
|
return Math.min(max, Math.max(min, value));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function materialToUnlit(material: THREE.Material): THREE.Material {
|
||||||
|
const source = material as THREE.MeshStandardMaterial & THREE.MeshPhysicalMaterial;
|
||||||
|
const unlit = new THREE.MeshBasicMaterial({
|
||||||
|
color: source.color?.clone() ?? new THREE.Color(0xffffff),
|
||||||
|
map: source.map ?? null,
|
||||||
|
transparent: source.transparent,
|
||||||
|
opacity: source.opacity,
|
||||||
|
alphaMap: source.alphaMap ?? null,
|
||||||
|
side: source.side,
|
||||||
|
depthWrite: source.depthWrite,
|
||||||
|
depthTest: source.depthTest,
|
||||||
|
vertexColors: source.vertexColors,
|
||||||
|
toneMapped: false
|
||||||
|
});
|
||||||
|
|
||||||
|
return unlit;
|
||||||
|
}
|
||||||
|
|
||||||
|
function applyUnlitMaterials(object: THREE.Object3D): void {
|
||||||
|
object.traverse((child) => {
|
||||||
|
const mesh = child as THREE.Mesh;
|
||||||
|
if (!mesh.isMesh || !mesh.material) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
mesh.castShadow = false;
|
||||||
|
mesh.receiveShadow = false;
|
||||||
|
mesh.material = Array.isArray(mesh.material)
|
||||||
|
? mesh.material.map(materialToUnlit)
|
||||||
|
: materialToUnlit(mesh.material);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
function normalizeObjectToStage(object: THREE.Object3D): THREE.Box3 {
|
function normalizeObjectToStage(object: THREE.Object3D): THREE.Box3 {
|
||||||
|
object.rotation.set(0, MODEL_FRONT_ROTATION_Y, MODEL_UPRIGHT_ROTATION_Z);
|
||||||
object.updateMatrixWorld(true);
|
object.updateMatrixWorld(true);
|
||||||
let bounds = new THREE.Box3().setFromObject(object);
|
let bounds = new THREE.Box3().setFromObject(object);
|
||||||
const size = bounds.getSize(new THREE.Vector3());
|
const size = bounds.getSize(new THREE.Vector3());
|
||||||
@@ -149,19 +184,14 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
function frameObject(object: THREE.Object3D, camera: THREE.PerspectiveCamera, controls: OrbitControls): void {
|
function frameObject(object: THREE.Object3D, camera: THREE.PerspectiveCamera, controls: OrbitControls): void {
|
||||||
const bounds = normalizeObjectToStage(object);
|
normalizeObjectToStage(object);
|
||||||
const size = bounds.getSize(new THREE.Vector3());
|
camera.position.copy(FIXED_CAMERA_POSITION);
|
||||||
const maxAxis = Math.max(size.x, size.y, size.z, 1);
|
camera.near = 0.05;
|
||||||
const distance = clamp(maxAxis * CAMERA_DISTANCE_FACTOR, CAMERA_DISTANCE_MIN, CAMERA_DISTANCE_MAX);
|
camera.far = 600;
|
||||||
const targetY = FLOOR_Y + Math.max(size.y * 0.46, 1.4);
|
|
||||||
|
|
||||||
camera.position.set(distance * 0.48, targetY + distance * 0.24, distance * 0.68);
|
|
||||||
camera.near = Math.max(distance / 80, 0.01);
|
|
||||||
camera.far = distance * 24;
|
|
||||||
camera.updateProjectionMatrix();
|
camera.updateProjectionMatrix();
|
||||||
controls.target.set(0, targetY, 0);
|
controls.target.copy(FIXED_CAMERA_TARGET);
|
||||||
controls.minDistance = Math.max(distance * 0.32, 2);
|
controls.minDistance = 2.4;
|
||||||
controls.maxDistance = Math.max(distance * 2.5, 12);
|
controls.maxDistance = 32;
|
||||||
controls.update();
|
controls.update();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -186,14 +216,15 @@
|
|||||||
scene.fog = new THREE.FogExp2(0x03070d, 0.028);
|
scene.fog = new THREE.FogExp2(0x03070d, 0.028);
|
||||||
|
|
||||||
const camera = new THREE.PerspectiveCamera(38, 1, 0.05, 600);
|
const camera = new THREE.PerspectiveCamera(38, 1, 0.05, 600);
|
||||||
camera.position.set(8, 6, 9);
|
camera.position.copy(FIXED_CAMERA_POSITION);
|
||||||
|
camera.lookAt(FIXED_CAMERA_TARGET);
|
||||||
|
|
||||||
const controls = new OrbitControls(camera, canvasEl);
|
const controls = new OrbitControls(camera, canvasEl);
|
||||||
controls.enableDamping = true;
|
controls.enableDamping = true;
|
||||||
controls.dampingFactor = 0.08;
|
controls.dampingFactor = 0.08;
|
||||||
controls.minDistance = 2.4;
|
controls.minDistance = 2.4;
|
||||||
controls.maxDistance = 32;
|
controls.maxDistance = 32;
|
||||||
controls.target.set(0, FLOOR_Y + 3.2, 0);
|
controls.target.copy(FIXED_CAMERA_TARGET);
|
||||||
|
|
||||||
const labGroup = new THREE.Group();
|
const labGroup = new THREE.Group();
|
||||||
scene.add(labGroup);
|
scene.add(labGroup);
|
||||||
@@ -241,16 +272,8 @@
|
|||||||
floor.position.y = FLOOR_Y - 0.018;
|
floor.position.y = FLOOR_Y - 0.018;
|
||||||
labGroup.add(floor);
|
labGroup.add(floor);
|
||||||
|
|
||||||
const ambient = new THREE.AmbientLight(0x9fb8d0, 0.22);
|
|
||||||
const keyLight = new THREE.DirectionalLight(0x7be7ff, 1.5);
|
|
||||||
keyLight.position.set(8, 12, 8);
|
|
||||||
const rimLight = new THREE.PointLight(0xa6ff7a, 26, 24, 2.1);
|
|
||||||
rimLight.position.set(-4.5, 4.8, -3.6);
|
|
||||||
const sideLight = new THREE.PointLight(0x5c8cff, 15, 28, 1.7);
|
|
||||||
sideLight.position.set(5.8, 3.2, -5.4);
|
|
||||||
scene.add(ambient, keyLight, rimLight, sideLight);
|
|
||||||
|
|
||||||
let activeModel: THREE.Object3D = buildPlaceholderModel();
|
let activeModel: THREE.Object3D = buildPlaceholderModel();
|
||||||
|
applyUnlitMaterials(activeModel);
|
||||||
scene.add(activeModel);
|
scene.add(activeModel);
|
||||||
frameObject(activeModel, camera, controls);
|
frameObject(activeModel, camera, controls);
|
||||||
|
|
||||||
@@ -261,13 +284,7 @@
|
|||||||
scene.remove(activeModel);
|
scene.remove(activeModel);
|
||||||
disposeObject3D(activeModel);
|
disposeObject3D(activeModel);
|
||||||
activeModel = gltf.scene;
|
activeModel = gltf.scene;
|
||||||
activeModel.traverse((child) => {
|
applyUnlitMaterials(activeModel);
|
||||||
const mesh = child as THREE.Mesh;
|
|
||||||
if (mesh.isMesh) {
|
|
||||||
mesh.castShadow = true;
|
|
||||||
mesh.receiveShadow = true;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
scene.add(activeModel);
|
scene.add(activeModel);
|
||||||
frameObject(activeModel, camera, controls);
|
frameObject(activeModel, camera, controls);
|
||||||
loadState = "ready";
|
loadState = "ready";
|
||||||
|
|||||||
BIN
static/models/je-skin-model.bin
Normal file
BIN
static/models/je-skin-model.bin
Normal file
Binary file not shown.
Binary file not shown.
1
static/models/je-skin-model.gltf
Normal file
1
static/models/je-skin-model.gltf
Normal file
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user