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:
sebseb7
2025-08-26 00:56:40 +02:00
parent 60871b3963
commit e66e26764e
4 changed files with 172 additions and 67 deletions

View File

@@ -2,6 +2,7 @@ import * as THREE from 'three';
// FPV Mode variables // FPV Mode variables
let fpvMode = false; let fpvMode = false;
let flyMode = false; // New fly mode state
let fpvControls = { let fpvControls = {
moveForward: false, moveForward: false,
moveBackward: false, moveBackward: false,
@@ -9,6 +10,8 @@ let fpvControls = {
moveRight: false, moveRight: false,
jump: false, jump: false,
crouch: false, crouch: false,
moveUp: false, // For fly mode
moveDown: false, // For fly mode
canJump: true, canJump: true,
velocity: new THREE.Vector3(), velocity: new THREE.Vector3(),
direction: new THREE.Vector3(), direction: new THREE.Vector3(),
@@ -69,6 +72,14 @@ function onKeyDown(event) {
return false; return false;
} }
break; break;
case 'KeyG':
if (fpvMode) {
event.preventDefault();
event.stopPropagation();
toggleFlyMode();
return false;
}
break;
case 'KeyW': case 'KeyW':
if (fpvMode) fpvControls.moveForward = true; if (fpvMode) fpvControls.moveForward = true;
break; break;
@@ -84,15 +95,23 @@ function onKeyDown(event) {
case 'Space': case 'Space':
if (fpvMode) { if (fpvMode) {
event.preventDefault(); event.preventDefault();
fpvControls.jump = true; if (flyMode) {
fpvControls.moveUp = true;
} else {
fpvControls.jump = true;
}
} }
break; break;
case 'ShiftLeft': case 'ShiftLeft':
case 'ShiftRight': case 'ShiftRight':
if (fpvMode) { if (fpvMode) {
event.preventDefault(); event.preventDefault();
fpvControls.crouch = true; if (flyMode) {
fpvControls.isCrouching = true; fpvControls.moveDown = true;
} else {
fpvControls.crouch = true;
fpvControls.isCrouching = true;
}
} }
break; break;
} }
@@ -113,13 +132,23 @@ function onKeyUp(event) {
if (fpvMode) fpvControls.moveRight = false; if (fpvMode) fpvControls.moveRight = false;
break; break;
case 'Space': case 'Space':
if (fpvMode) fpvControls.jump = false; if (fpvMode) {
if (flyMode) {
fpvControls.moveUp = false;
} else {
fpvControls.jump = false;
}
}
break; break;
case 'ShiftLeft': case 'ShiftLeft':
case 'ShiftRight': case 'ShiftRight':
if (fpvMode) { if (fpvMode) {
fpvControls.crouch = false; if (flyMode) {
fpvControls.isCrouching = false; fpvControls.moveDown = false;
} else {
fpvControls.crouch = false;
fpvControls.isCrouching = false;
}
} }
break; break;
} }
@@ -194,7 +223,29 @@ function enterFPVMode(renderer) {
renderer.domElement.requestPointerLock(); 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() { function exitFPVMode() {
@@ -234,12 +285,17 @@ function exitFPVMode() {
fpvControls.moveRight = false; fpvControls.moveRight = false;
fpvControls.jump = false; fpvControls.jump = false;
fpvControls.crouch = false; fpvControls.crouch = false;
fpvControls.moveUp = false;
fpvControls.moveDown = false;
fpvControls.verticalVelocity = 0; fpvControls.verticalVelocity = 0;
fpvControls.isJumping = false; fpvControls.isJumping = false;
fpvControls.isCrouching = false; fpvControls.isCrouching = false;
fpvControls.currentHeight = window.eyeHeight; fpvControls.currentHeight = window.eyeHeight;
fpvControls.targetHeight = window.eyeHeight; fpvControls.targetHeight = window.eyeHeight;
// Reset fly mode
flyMode = false;
console.log('Orbit controls restored'); console.log('Orbit controls restored');
}, 50); }, 50);
} }
@@ -248,11 +304,12 @@ export function updateFPVMovement(camera) {
if (!fpvMode) return; if (!fpvMode) return;
const delta = 0.016; // Approximate 60fps const delta = 0.016; // Approximate 60fps
const moveSpeed = 100.0; // Units per second (1000x faster) const moveSpeed = 100.0; // Units per second
const jumpSpeed = 220.0; // Jump initial velocity (4x higher) const jumpSpeed = 220.0; // Jump initial velocity
const gravity = 420.0; // Gravity strength (4x faster) const gravity = 420.0; // Gravity strength
const crouchHeight = window.eyeHeight * 0.5; // Crouch height (60% of normal eye height) const crouchHeight = window.eyeHeight * 0.5; // Crouch height
const crouchSpeed = 100.0; // Speed of crouching transition const crouchSpeed = 100.0; // Speed of crouching transition
const flySpeed = 80.0; // Vertical fly speed
// Reset direction // Reset direction
fpvControls.direction.set(0, 0, 0); fpvControls.direction.set(0, 0, 0);
@@ -260,63 +317,82 @@ export function updateFPVMovement(camera) {
// Calculate movement direction based on current rotation // Calculate movement direction based on current rotation
const forward = new THREE.Vector3(0, 0, -1); const forward = new THREE.Vector3(0, 0, -1);
const right = new THREE.Vector3(1, 0, 0); const right = new THREE.Vector3(1, 0, 0);
const up = new THREE.Vector3(0, 1, 0);
// Apply horizontal rotation to movement vectors // Apply horizontal rotation to movement vectors
forward.applyAxisAngle(new THREE.Vector3(0, 1, 0), fpvControls.rotation.y); forward.applyAxisAngle(new THREE.Vector3(0, 1, 0), fpvControls.rotation.y);
right.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.moveForward) fpvControls.direction.add(forward);
if (fpvControls.moveBackward) fpvControls.direction.sub(forward); if (fpvControls.moveBackward) fpvControls.direction.sub(forward);
if (fpvControls.moveLeft) fpvControls.direction.sub(right); if (fpvControls.moveLeft) fpvControls.direction.sub(right);
if (fpvControls.moveRight) fpvControls.direction.add(right); if (fpvControls.moveRight) fpvControls.direction.add(right);
// Normalize and apply speed // Normalize and apply horizontal speed
if (fpvControls.direction.length() > 0) { if (fpvControls.direction.length() > 0) {
fpvControls.direction.normalize(); fpvControls.direction.normalize();
fpvControls.direction.multiplyScalar(moveSpeed * delta); fpvControls.direction.multiplyScalar(moveSpeed * delta);
// Apply movement to camera position // Apply horizontal movement to camera position
camera.position.add(fpvControls.direction); camera.position.add(fpvControls.direction);
} }
// Handle jumping if (flyMode) {
if (fpvControls.jump && fpvControls.canJump && !fpvControls.isJumping) { // FLY MODE - No gravity, direct vertical control
fpvControls.verticalVelocity = jumpSpeed; let verticalMovement = 0;
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 (fpvControls.moveUp) {
if (camera.position.y <= fpvControls.targetHeight) { verticalMovement += flySpeed * delta;
camera.position.y = fpvControls.targetHeight; }
fpvControls.currentHeight = fpvControls.targetHeight; if (fpvControls.moveDown) {
fpvControls.verticalVelocity = 0; verticalMovement -= flySpeed * delta;
fpvControls.isJumping = false; }
fpvControls.canJump = true;
if (verticalMovement !== 0) {
camera.position.y += verticalMovement;
} }
} else { } else {
// Smooth crouching transition when not jumping // WALK MODE - Gravity, jumping, crouching
if (Math.abs(fpvControls.currentHeight - fpvControls.targetHeight) > 0.01) {
const direction = fpvControls.targetHeight > fpvControls.currentHeight ? 1 : -1; // Handle jumping
fpvControls.currentHeight += direction * crouchSpeed * delta; if (fpvControls.jump && fpvControls.canJump && !fpvControls.isJumping) {
fpvControls.verticalVelocity = jumpSpeed;
// Clamp to target fpvControls.isJumping = true;
if (direction > 0 && fpvControls.currentHeight >= fpvControls.targetHeight) { fpvControls.canJump = false;
fpvControls.currentHeight = fpvControls.targetHeight; }
} else if (direction < 0 && fpvControls.currentHeight <= fpvControls.targetHeight) {
fpvControls.currentHeight = fpvControls.targetHeight; // 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 // Apply rotation to camera

View File

@@ -23,7 +23,8 @@
<p>✌️ Two finger drag: Pan around scene</p> <p>✌️ Two finger drag: Pan around scene</p>
<hr style="margin: 10px 0; border: 1px solid #555;"> <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>🚶 <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> <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> </div>

View File

@@ -18,18 +18,25 @@ export function setupLighting(scene, camera) {
pointLight.position.set(-10, 10, 10); pointLight.position.set(-10, 10, 10);
scene.add(pointLight); scene.add(pointLight);
// Camera light - follows the camera to illuminate from viewing angle // Enhanced camera point light - follows the camera to illuminate from viewing angle
window.cameraLight = new THREE.PointLight(0xffffff, 1.0, 200); window.cameraLight = new THREE.PointLight(0xffffff, 2.0, 300);
window.cameraLight.position.copy(camera.position); window.cameraLight.position.copy(camera.position);
scene.add(window.cameraLight); scene.add(window.cameraLight);
// Additional camera spotlight for focused illumination // Camera spotlight for focused illumination (like a headlamp)
window.cameraSpotlight = new THREE.SpotLight(0xffffff, 0.8, 100, Math.PI / 4, 0.3); window.cameraSpotlight = new THREE.SpotLight(0xffffff, 1.5, 150, Math.PI / 6, 0.2);
window.cameraSpotlight.position.copy(camera.position); window.cameraSpotlight.position.copy(camera.position);
window.cameraSpotlight.target.position.set(0, 0, 0); // Point at origin initially window.cameraSpotlight.target.position.set(0, 0, 0); // Point at origin initially
scene.add(window.cameraSpotlight); scene.add(window.cameraSpotlight);
scene.add(window.cameraSpotlight.target); 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 // Interior point lights (will be repositioned when model loads) - increased intensity
window.interiorLight = new THREE.PointLight(0xffffff, 0.8, 50); window.interiorLight = new THREE.PointLight(0xffffff, 0.8, 50);
window.interiorLight.position.set(0, 0, 0); // Will be moved to model center 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); const hemiLight = new THREE.HemisphereLight(0xffffff, 0x444444, 0.8);
scene.add(hemiLight); 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 // Create environment map for glass reflections
setupEnvironmentMap(); setupEnvironmentMap();
@@ -91,10 +98,26 @@ export function updateCameraLights(camera, controls) {
if (window.cameraSpotlight) { if (window.cameraSpotlight) {
window.cameraSpotlight.position.copy(camera.position); window.cameraSpotlight.position.copy(camera.position);
// Make spotlight point towards the orbit controls target (model center)
if (controls && controls.target) { // Calculate where the camera is looking
window.cameraSpotlight.target.position.copy(controls.target); 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; window.interiorLight2.distance = maxDimension * 1.5;
} }
// Update camera lights to target the model center // Update camera lights based on model size
if (window.cameraLight) { 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) { if (window.cameraSpotlight) {
window.cameraSpotlight.target.position.copy(center); window.cameraSpotlight.distance = maxDimension * 3;
window.cameraSpotlight.distance = maxDimension * 2; }
if (window.cameraDirectionalLight) {
// Directional lights don't have distance, but we can adjust intensity
window.cameraDirectionalLight.intensity = 1.0;
} }
} }

View File

@@ -12,10 +12,11 @@ export function loadCubeModel(scene, camera, controls, onLoadComplete) {
mtlLoader.load('cube.mtl', function(materials) { mtlLoader.load('cube.mtl', function(materials) {
materials.preload(); materials.preload();
// Configure materials for double-sided rendering with proper transparency // Configure materials with proper transparency
Object.keys(materials.materials).forEach(function(key) { Object.keys(materials.materials).forEach(function(key) {
const material = materials.materials[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 // Handle glass materials specially
if (key.toLowerCase().includes('glass')) { if (key.toLowerCase().includes('glass')) {
@@ -120,12 +121,12 @@ export function loadCubeModel(scene, camera, controls, onLoadComplete) {
child.add(wireframeLines); child.add(wireframeLines);
} }
} else { } else {
// Non-glass materials // Non-glass materials - fix texture appearing on both sides
child.material.side = THREE.DoubleSide; child.material.side = THREE.FrontSide;
child.material.transparent = false; child.material.transparent = false;
child.material.opacity = 1.0; child.material.opacity = 1.0;
// Ensure proper lighting on both sides // Ensure proper lighting
if (child.material.type === 'MeshLambertMaterial' || child.material.type === 'MeshPhongMaterial') { if (child.material.type === 'MeshLambertMaterial' || child.material.type === 'MeshPhongMaterial') {
child.material.emissive = new THREE.Color(0x222222); // Increased self-illumination for brightness 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 // Apply default material with better interior visibility
const defaultMaterial = new THREE.MeshPhongMaterial({ const defaultMaterial = new THREE.MeshPhongMaterial({
color: 0xffffff, color: 0xffffff,
side: THREE.DoubleSide, side: THREE.FrontSide, // Fix texture appearing on both sides
transparent: false, transparent: false,
opacity: 1.0, opacity: 1.0,
emissive: new THREE.Color(0x222222), // Increased self-illumination for brightness emissive: new THREE.Color(0x222222), // Increased self-illumination for brightness