Implement fly mode in FPV controls: added vertical movement capabilities, toggle functionality with 'G' key, and updated control logic for jumping and crouching. Enhanced user instructions in index.html. Improved lighting setup in lighting.js with additional directional light and adjustments to existing lights for better illumination. Refined material handling in model-loader.js to ensure proper rendering of glass and non-glass materials.
This commit is contained in:
@@ -2,6 +2,7 @@ import * as THREE from 'three';
|
||||
|
||||
// FPV Mode variables
|
||||
let fpvMode = false;
|
||||
let flyMode = false; // New fly mode state
|
||||
let fpvControls = {
|
||||
moveForward: false,
|
||||
moveBackward: false,
|
||||
@@ -9,6 +10,8 @@ let fpvControls = {
|
||||
moveRight: false,
|
||||
jump: false,
|
||||
crouch: false,
|
||||
moveUp: false, // For fly mode
|
||||
moveDown: false, // For fly mode
|
||||
canJump: true,
|
||||
velocity: new THREE.Vector3(),
|
||||
direction: new THREE.Vector3(),
|
||||
@@ -69,6 +72,14 @@ function onKeyDown(event) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case 'KeyG':
|
||||
if (fpvMode) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
toggleFlyMode();
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case 'KeyW':
|
||||
if (fpvMode) fpvControls.moveForward = true;
|
||||
break;
|
||||
@@ -84,15 +95,23 @@ function onKeyDown(event) {
|
||||
case 'Space':
|
||||
if (fpvMode) {
|
||||
event.preventDefault();
|
||||
fpvControls.jump = true;
|
||||
if (flyMode) {
|
||||
fpvControls.moveUp = true;
|
||||
} else {
|
||||
fpvControls.jump = true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'ShiftLeft':
|
||||
case 'ShiftRight':
|
||||
if (fpvMode) {
|
||||
event.preventDefault();
|
||||
fpvControls.crouch = true;
|
||||
fpvControls.isCrouching = true;
|
||||
if (flyMode) {
|
||||
fpvControls.moveDown = true;
|
||||
} else {
|
||||
fpvControls.crouch = true;
|
||||
fpvControls.isCrouching = true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -113,13 +132,23 @@ function onKeyUp(event) {
|
||||
if (fpvMode) fpvControls.moveRight = false;
|
||||
break;
|
||||
case 'Space':
|
||||
if (fpvMode) fpvControls.jump = false;
|
||||
if (fpvMode) {
|
||||
if (flyMode) {
|
||||
fpvControls.moveUp = false;
|
||||
} else {
|
||||
fpvControls.jump = false;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'ShiftLeft':
|
||||
case 'ShiftRight':
|
||||
if (fpvMode) {
|
||||
fpvControls.crouch = false;
|
||||
fpvControls.isCrouching = false;
|
||||
if (flyMode) {
|
||||
fpvControls.moveDown = false;
|
||||
} else {
|
||||
fpvControls.crouch = false;
|
||||
fpvControls.isCrouching = false;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -194,7 +223,29 @@ function enterFPVMode(renderer) {
|
||||
renderer.domElement.requestPointerLock();
|
||||
}
|
||||
|
||||
console.log('FPV mode active - WASD to move, mouse to look, ESC to exit');
|
||||
console.log('FPV mode active - WASD to move, mouse to look, G to toggle fly mode, ESC to exit');
|
||||
}
|
||||
|
||||
function toggleFlyMode() {
|
||||
flyMode = !flyMode;
|
||||
|
||||
// Reset vertical states when switching modes
|
||||
fpvControls.verticalVelocity = 0;
|
||||
fpvControls.isJumping = false;
|
||||
fpvControls.isCrouching = false;
|
||||
fpvControls.moveUp = false;
|
||||
fpvControls.moveDown = false;
|
||||
fpvControls.jump = false;
|
||||
fpvControls.crouch = false;
|
||||
|
||||
if (flyMode) {
|
||||
console.log('🚁 Fly mode enabled - SPACE to ascend, SHIFT to descend, no gravity');
|
||||
} else {
|
||||
console.log('🚶 Walk mode enabled - SPACE to jump, SHIFT to crouch, with gravity');
|
||||
// Set current height to maintain position when switching back to walk mode
|
||||
fpvControls.currentHeight = window.camera.position.y;
|
||||
fpvControls.targetHeight = fpvControls.currentHeight;
|
||||
}
|
||||
}
|
||||
|
||||
function exitFPVMode() {
|
||||
@@ -234,12 +285,17 @@ function exitFPVMode() {
|
||||
fpvControls.moveRight = false;
|
||||
fpvControls.jump = false;
|
||||
fpvControls.crouch = false;
|
||||
fpvControls.moveUp = false;
|
||||
fpvControls.moveDown = false;
|
||||
fpvControls.verticalVelocity = 0;
|
||||
fpvControls.isJumping = false;
|
||||
fpvControls.isCrouching = false;
|
||||
fpvControls.currentHeight = window.eyeHeight;
|
||||
fpvControls.targetHeight = window.eyeHeight;
|
||||
|
||||
// Reset fly mode
|
||||
flyMode = false;
|
||||
|
||||
console.log('Orbit controls restored');
|
||||
}, 50);
|
||||
}
|
||||
@@ -248,11 +304,12 @@ export function updateFPVMovement(camera) {
|
||||
if (!fpvMode) return;
|
||||
|
||||
const delta = 0.016; // Approximate 60fps
|
||||
const moveSpeed = 100.0; // Units per second (1000x faster)
|
||||
const jumpSpeed = 220.0; // Jump initial velocity (4x higher)
|
||||
const gravity = 420.0; // Gravity strength (4x faster)
|
||||
const crouchHeight = window.eyeHeight * 0.5; // Crouch height (60% of normal eye height)
|
||||
const moveSpeed = 100.0; // Units per second
|
||||
const jumpSpeed = 220.0; // Jump initial velocity
|
||||
const gravity = 420.0; // Gravity strength
|
||||
const crouchHeight = window.eyeHeight * 0.5; // Crouch height
|
||||
const crouchSpeed = 100.0; // Speed of crouching transition
|
||||
const flySpeed = 80.0; // Vertical fly speed
|
||||
|
||||
// Reset direction
|
||||
fpvControls.direction.set(0, 0, 0);
|
||||
@@ -260,63 +317,82 @@ export function updateFPVMovement(camera) {
|
||||
// Calculate movement direction based on current rotation
|
||||
const forward = new THREE.Vector3(0, 0, -1);
|
||||
const right = new THREE.Vector3(1, 0, 0);
|
||||
const up = new THREE.Vector3(0, 1, 0);
|
||||
|
||||
// Apply horizontal rotation to movement vectors
|
||||
forward.applyAxisAngle(new THREE.Vector3(0, 1, 0), fpvControls.rotation.y);
|
||||
right.applyAxisAngle(new THREE.Vector3(0, 1, 0), fpvControls.rotation.y);
|
||||
|
||||
// Apply movement inputs
|
||||
// Apply horizontal movement inputs
|
||||
if (fpvControls.moveForward) fpvControls.direction.add(forward);
|
||||
if (fpvControls.moveBackward) fpvControls.direction.sub(forward);
|
||||
if (fpvControls.moveLeft) fpvControls.direction.sub(right);
|
||||
if (fpvControls.moveRight) fpvControls.direction.add(right);
|
||||
|
||||
// Normalize and apply speed
|
||||
// Normalize and apply horizontal speed
|
||||
if (fpvControls.direction.length() > 0) {
|
||||
fpvControls.direction.normalize();
|
||||
fpvControls.direction.multiplyScalar(moveSpeed * delta);
|
||||
|
||||
// Apply movement to camera position
|
||||
// Apply horizontal movement to camera position
|
||||
camera.position.add(fpvControls.direction);
|
||||
}
|
||||
|
||||
// Handle jumping
|
||||
if (fpvControls.jump && fpvControls.canJump && !fpvControls.isJumping) {
|
||||
fpvControls.verticalVelocity = jumpSpeed;
|
||||
fpvControls.isJumping = true;
|
||||
fpvControls.canJump = false;
|
||||
}
|
||||
if (flyMode) {
|
||||
// FLY MODE - No gravity, direct vertical control
|
||||
let verticalMovement = 0;
|
||||
|
||||
// Update target height based on crouching state
|
||||
fpvControls.targetHeight = fpvControls.isCrouching ? crouchHeight : window.eyeHeight;
|
||||
if (fpvControls.moveUp) {
|
||||
verticalMovement += flySpeed * delta;
|
||||
}
|
||||
if (fpvControls.moveDown) {
|
||||
verticalMovement -= flySpeed * delta;
|
||||
}
|
||||
|
||||
// Apply gravity and vertical movement
|
||||
if (fpvControls.isJumping) {
|
||||
fpvControls.verticalVelocity -= gravity * delta;
|
||||
camera.position.y += fpvControls.verticalVelocity * delta;
|
||||
|
||||
// Check if landed back on ground
|
||||
if (camera.position.y <= fpvControls.targetHeight) {
|
||||
camera.position.y = fpvControls.targetHeight;
|
||||
fpvControls.currentHeight = fpvControls.targetHeight;
|
||||
fpvControls.verticalVelocity = 0;
|
||||
fpvControls.isJumping = false;
|
||||
fpvControls.canJump = true;
|
||||
if (verticalMovement !== 0) {
|
||||
camera.position.y += verticalMovement;
|
||||
}
|
||||
} else {
|
||||
// Smooth crouching transition when not jumping
|
||||
if (Math.abs(fpvControls.currentHeight - fpvControls.targetHeight) > 0.01) {
|
||||
const direction = fpvControls.targetHeight > fpvControls.currentHeight ? 1 : -1;
|
||||
fpvControls.currentHeight += direction * crouchSpeed * delta;
|
||||
// WALK MODE - Gravity, jumping, crouching
|
||||
|
||||
// Clamp to target
|
||||
if (direction > 0 && fpvControls.currentHeight >= fpvControls.targetHeight) {
|
||||
fpvControls.currentHeight = fpvControls.targetHeight;
|
||||
} else if (direction < 0 && fpvControls.currentHeight <= fpvControls.targetHeight) {
|
||||
fpvControls.currentHeight = fpvControls.targetHeight;
|
||||
}
|
||||
// Handle jumping
|
||||
if (fpvControls.jump && fpvControls.canJump && !fpvControls.isJumping) {
|
||||
fpvControls.verticalVelocity = jumpSpeed;
|
||||
fpvControls.isJumping = true;
|
||||
fpvControls.canJump = false;
|
||||
}
|
||||
|
||||
// Update target height based on crouching state
|
||||
fpvControls.targetHeight = fpvControls.isCrouching ? crouchHeight : window.eyeHeight;
|
||||
|
||||
// Apply gravity and vertical movement
|
||||
if (fpvControls.isJumping) {
|
||||
fpvControls.verticalVelocity -= gravity * delta;
|
||||
camera.position.y += fpvControls.verticalVelocity * delta;
|
||||
|
||||
// Check if landed back on ground
|
||||
if (camera.position.y <= fpvControls.targetHeight) {
|
||||
camera.position.y = fpvControls.targetHeight;
|
||||
fpvControls.currentHeight = fpvControls.targetHeight;
|
||||
fpvControls.verticalVelocity = 0;
|
||||
fpvControls.isJumping = false;
|
||||
fpvControls.canJump = true;
|
||||
}
|
||||
} else {
|
||||
// Smooth crouching transition when not jumping
|
||||
if (Math.abs(fpvControls.currentHeight - fpvControls.targetHeight) > 0.01) {
|
||||
const direction = fpvControls.targetHeight > fpvControls.currentHeight ? 1 : -1;
|
||||
fpvControls.currentHeight += direction * crouchSpeed * delta;
|
||||
|
||||
// Clamp to target
|
||||
if (direction > 0 && fpvControls.currentHeight >= fpvControls.targetHeight) {
|
||||
fpvControls.currentHeight = fpvControls.targetHeight;
|
||||
} else if (direction < 0 && fpvControls.currentHeight <= fpvControls.targetHeight) {
|
||||
fpvControls.currentHeight = fpvControls.targetHeight;
|
||||
}
|
||||
}
|
||||
camera.position.y = fpvControls.currentHeight;
|
||||
}
|
||||
camera.position.y = fpvControls.currentHeight;
|
||||
}
|
||||
|
||||
// Apply rotation to camera
|
||||
|
||||
@@ -23,7 +23,8 @@
|
||||
<p>✌️ Two finger drag: Pan around scene</p>
|
||||
<hr style="margin: 10px 0; border: 1px solid #555;">
|
||||
<p>🚶 <button id="fpv-btn" style="background: #2196F3; color: white; border: none; padding: 8px 12px; border-radius: 4px; cursor: pointer; font-size: 12px; margin-right: 10px;">Enter FPV Mode</button> <span style="font-size: 11px; color: #aaa;">(or press F key to toggle)</span></p>
|
||||
<p style="font-size: 11px; color: #aaa; margin: 5px 0;">In FPV: WASD to move, mouse to look, SPACE to jump, SHIFT to crouch, F or ESC to exit</p>
|
||||
<p style="font-size: 11px; color: #aaa; margin: 5px 0;">In FPV: WASD to move, mouse to look, G to toggle fly mode, F or ESC to exit</p>
|
||||
<p style="font-size: 10px; color: #888; margin: 2px 0;">Walk mode: SPACE jump, SHIFT crouch | Fly mode: SPACE ascend, SHIFT descend</p>
|
||||
<button id="print-btn" style="background: #4CAF50; color: white; border: none; padding: 8px 12px; border-radius: 4px; cursor: pointer; font-size: 12px;">🖨️ Print</button>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -18,18 +18,25 @@ export function setupLighting(scene, camera) {
|
||||
pointLight.position.set(-10, 10, 10);
|
||||
scene.add(pointLight);
|
||||
|
||||
// Camera light - follows the camera to illuminate from viewing angle
|
||||
window.cameraLight = new THREE.PointLight(0xffffff, 1.0, 200);
|
||||
// Enhanced camera point light - follows the camera to illuminate from viewing angle
|
||||
window.cameraLight = new THREE.PointLight(0xffffff, 2.0, 300);
|
||||
window.cameraLight.position.copy(camera.position);
|
||||
scene.add(window.cameraLight);
|
||||
|
||||
// Additional camera spotlight for focused illumination
|
||||
window.cameraSpotlight = new THREE.SpotLight(0xffffff, 0.8, 100, Math.PI / 4, 0.3);
|
||||
// Camera spotlight for focused illumination (like a headlamp)
|
||||
window.cameraSpotlight = new THREE.SpotLight(0xffffff, 1.5, 150, Math.PI / 6, 0.2);
|
||||
window.cameraSpotlight.position.copy(camera.position);
|
||||
window.cameraSpotlight.target.position.set(0, 0, 0); // Point at origin initially
|
||||
scene.add(window.cameraSpotlight);
|
||||
scene.add(window.cameraSpotlight.target);
|
||||
|
||||
// Additional camera directional light (like a flashlight)
|
||||
window.cameraDirectionalLight = new THREE.DirectionalLight(0xffffff, 1.0);
|
||||
window.cameraDirectionalLight.position.copy(camera.position);
|
||||
window.cameraDirectionalLight.target.position.set(0, 0, -1);
|
||||
scene.add(window.cameraDirectionalLight);
|
||||
scene.add(window.cameraDirectionalLight.target);
|
||||
|
||||
// Interior point lights (will be repositioned when model loads) - increased intensity
|
||||
window.interiorLight = new THREE.PointLight(0xffffff, 0.8, 50);
|
||||
window.interiorLight.position.set(0, 0, 0); // Will be moved to model center
|
||||
@@ -44,7 +51,7 @@ export function setupLighting(scene, camera) {
|
||||
const hemiLight = new THREE.HemisphereLight(0xffffff, 0x444444, 0.8);
|
||||
scene.add(hemiLight);
|
||||
|
||||
console.log('✨ Camera lights added - they will follow your view');
|
||||
console.log('✨ Enhanced camera lights added - headlamp-style illumination will follow your view');
|
||||
|
||||
// Create environment map for glass reflections
|
||||
setupEnvironmentMap();
|
||||
@@ -91,10 +98,26 @@ export function updateCameraLights(camera, controls) {
|
||||
|
||||
if (window.cameraSpotlight) {
|
||||
window.cameraSpotlight.position.copy(camera.position);
|
||||
// Make spotlight point towards the orbit controls target (model center)
|
||||
if (controls && controls.target) {
|
||||
window.cameraSpotlight.target.position.copy(controls.target);
|
||||
}
|
||||
|
||||
// Calculate where the camera is looking
|
||||
const lookDirection = new THREE.Vector3(0, 0, -1);
|
||||
lookDirection.applyQuaternion(camera.quaternion);
|
||||
|
||||
// Set spotlight target based on camera direction
|
||||
const targetPosition = camera.position.clone().add(lookDirection.multiplyScalar(10));
|
||||
window.cameraSpotlight.target.position.copy(targetPosition);
|
||||
}
|
||||
|
||||
if (window.cameraDirectionalLight) {
|
||||
window.cameraDirectionalLight.position.copy(camera.position);
|
||||
|
||||
// Calculate where the camera is looking for directional light
|
||||
const lookDirection = new THREE.Vector3(0, 0, -1);
|
||||
lookDirection.applyQuaternion(camera.quaternion);
|
||||
|
||||
// Set directional light target based on camera direction
|
||||
const targetPosition = camera.position.clone().add(lookDirection.multiplyScalar(5));
|
||||
window.cameraDirectionalLight.target.position.copy(targetPosition);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -119,13 +142,17 @@ export function adjustLightsForModel(modelBounds) {
|
||||
window.interiorLight2.distance = maxDimension * 1.5;
|
||||
}
|
||||
|
||||
// Update camera lights to target the model center
|
||||
// Update camera lights based on model size
|
||||
if (window.cameraLight) {
|
||||
window.cameraLight.distance = maxDimension * 3; // Adjust range based on model size
|
||||
window.cameraLight.distance = maxDimension * 4; // Adjust range based on model size
|
||||
}
|
||||
|
||||
if (window.cameraSpotlight) {
|
||||
window.cameraSpotlight.target.position.copy(center);
|
||||
window.cameraSpotlight.distance = maxDimension * 2;
|
||||
window.cameraSpotlight.distance = maxDimension * 3;
|
||||
}
|
||||
|
||||
if (window.cameraDirectionalLight) {
|
||||
// Directional lights don't have distance, but we can adjust intensity
|
||||
window.cameraDirectionalLight.intensity = 1.0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,10 +12,11 @@ export function loadCubeModel(scene, camera, controls, onLoadComplete) {
|
||||
mtlLoader.load('cube.mtl', function(materials) {
|
||||
materials.preload();
|
||||
|
||||
// Configure materials for double-sided rendering with proper transparency
|
||||
// Configure materials with proper transparency
|
||||
Object.keys(materials.materials).forEach(function(key) {
|
||||
const material = materials.materials[key];
|
||||
material.side = THREE.DoubleSide;
|
||||
// Only set DoubleSide for glass materials, others use FrontSide
|
||||
material.side = key.toLowerCase().includes('glass') ? THREE.DoubleSide : THREE.FrontSide;
|
||||
|
||||
// Handle glass materials specially
|
||||
if (key.toLowerCase().includes('glass')) {
|
||||
@@ -120,12 +121,12 @@ export function loadCubeModel(scene, camera, controls, onLoadComplete) {
|
||||
child.add(wireframeLines);
|
||||
}
|
||||
} else {
|
||||
// Non-glass materials
|
||||
child.material.side = THREE.DoubleSide;
|
||||
// Non-glass materials - fix texture appearing on both sides
|
||||
child.material.side = THREE.FrontSide;
|
||||
child.material.transparent = false;
|
||||
child.material.opacity = 1.0;
|
||||
|
||||
// Ensure proper lighting on both sides
|
||||
// Ensure proper lighting
|
||||
if (child.material.type === 'MeshLambertMaterial' || child.material.type === 'MeshPhongMaterial') {
|
||||
child.material.emissive = new THREE.Color(0x222222); // Increased self-illumination for brightness
|
||||
}
|
||||
@@ -177,7 +178,7 @@ export function loadObjWithoutMaterials(scene, camera, controls, onLoadComplete)
|
||||
// Apply default material with better interior visibility
|
||||
const defaultMaterial = new THREE.MeshPhongMaterial({
|
||||
color: 0xffffff,
|
||||
side: THREE.DoubleSide,
|
||||
side: THREE.FrontSide, // Fix texture appearing on both sides
|
||||
transparent: false,
|
||||
opacity: 1.0,
|
||||
emissive: new THREE.Color(0x222222), // Increased self-illumination for brightness
|
||||
|
||||
Reference in New Issue
Block a user