File size: 8,601 Bytes
638c90a 8b6ea51 638c90a b7f4ac5 8b6ea51 c4c386f b7f4ac5 011ac1b b7f4ac5 011ac1b b7f4ac5 638c90a b79e6f7 011ac1b c4c386f 011ac1b c4c386f b79e6f7 b7f4ac5 011ac1b 8b6ea51 b79e6f7 c4c386f 011ac1b c4c386f 011ac1b c4c386f 011ac1b c4c386f 011ac1b 107476b c4c386f 011ac1b c4c386f b79e6f7 c4c386f 011ac1b c4c386f 011ac1b c4c386f 011ac1b b79e6f7 011ac1b b79e6f7 011ac1b c4c386f b79e6f7 c4c386f b79e6f7 c4c386f b79e6f7 c4c386f b79e6f7 c4c386f 011ac1b c4c386f 011ac1b c4c386f 8b6ea51 b7f4ac5 b79e6f7 c4c386f 011ac1b c4c386f 011ac1b c4c386f 011ac1b c4c386f 011ac1b c4c386f 011ac1b c4c386f b79e6f7 c4c386f 011ac1b b79e6f7 c4c386f b79e6f7 011ac1b b7f4ac5 638c90a |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 |
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Ultimate Three.js Flight Simulator</title>
<style>
body { margin: 0; background-color: #000; overflow: hidden; }
canvas { display: block; }
#instruments { position: absolute; top: 10px; right: 10px; background: rgba(0,0,0,0.7); color: #0F0; padding: 10px; font-family: monospace; font-size: 14px; }
</style>
</head>
<body>
<div id="instruments">
<b>Cockpit Instruments:</b><br>
Alt: <span id="altimeter">0 ft</span><br>
Airspeed: <span id="airspeed">0 kts</span><br>
Heading: <span id="heading">0°</span><br>
RPM: <span id="rpm">0</span><br>
Fuel: <span id="fuel">100%</span><br>
Flaps: <span id="flaps">0%</span>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
<script>
// Scene
let scene = new THREE.Scene();
// Skybox with sun (gradient sky)
let sun = new THREE.DirectionalLight(0xFFFFFF, 1);
sun.position.set(100, 100, 100);
scene.add(sun);
let skyGeom = new THREE.SphereGeometry(500, 32, 32);
let skyMat = new THREE.ShaderMaterial({
vertexShader: `
varying vec3 vWorldPosition;
void main() {
vec4 worldPosition = modelMatrix * vec4(position, 1.0);
vWorldPosition = worldPosition.xyz;
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}
`,
fragmentShader: `
uniform vec3 sunPosition;
varying vec3 vWorldPosition;
void main() {
vec3 viewDirection = normalize(vWorldPosition);
vec3 sunDir = normalize(sunPosition);
float sunDot = max(dot(viewDirection, sunDir), 0.0);
vec3 dayColor = vec3(0.2, 0.5, 1.0); // Blue sky
vec3 sunsetColor = vec3(1.0, 0.5, 0.2); // Orange
vec3 nightColor = vec3(0.05, 0.05, 0.1); // Dark blue
float sunHeight = sunPosition.y / 100.0;
vec3 skyColor = mix(nightColor, mix(sunsetColor, dayColor, sunHeight), smoothstep(-0.1, 0.1, sunHeight));
gl_FragColor = vec4(skyColor + sunDot * 0.5, 1.0);
}
`,
side: THREE.BackSide,
uniforms: { sunPosition: { value: sun.position } }
});
let sky = new THREE.Mesh(skyGeom, skyMat);
scene.add(sky);
// Terrain (Perlin noise hills)
let terrainSize = 256;
let terrainGeom = new THREE.PlaneGeometry(200, 200, terrainSize, terrainSize);
for (let i = 0; i < terrainGeom.attributes.position.count; i++) {
let x = terrainGeom.attributes.position.getX(i);
let z = terrainGeom.attributes.position.getZ(i);
let noise = (Math.sin(x * 0.1) + Math.sin(z * 0.1)) * 5 + (Math.random() * 2 - 1) * 2; // Simple noise
terrainGeom.attributes.position.setY(i, noise);
}
let terrainMat = new THREE.MeshLambertMaterial({ color: 0x228B22 });
let terrain = new THREE.Mesh(terrainGeom, terrainMat);
terrain.rotation.x = -Math.PI / 2;
terrain.receiveShadow = true;
scene.add(terrain);
// Airplane (detailed mesh)
let planeGeom = new THREE.Group();
let fuselageGeom = new THREE.BoxGeometry(2, 0.4, 6);
let fuselageMat = new THREE.MeshLambertMaterial({ color: 0xFFFFFF });
let fuselage = new THREE.Mesh(fuselageGeom, fuselageMat);
fuselage.castShadow = true;
let wingGeom = new THREE.BufferGeometry();
// ... (same wing vertices as before)
let wingMat = new THREE.MeshLambertMaterial({ color: 0xFFFFFF });
let wingLeft = new THREE.Mesh(wingGeom, wingMat);
wingLeft.position.x = -0.5;
let wingRight = wingLeft.clone();
wingRight.position.x = 0.5;
planeGeom.add(fuselage);
planeGeom.add(wingLeft);
planeGeom.add(wingRight);
// ... (add tail, wheels)
// Camera
let camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
planeGeom.add(camera);
camera.position.y = 1.5;
camera.position.z = 2;
// Lighting
let ambientLight = new THREE.AmbientLight(0x333333);
scene.add(ambientLight);
// Renderer
let renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.shadowMap.enabled = true;
document.body.appendChild(renderer.domElement);
// Flight dynamics
let mass = 1500; // kg
let wingArea = 10; // m²
let wingLiftCoeff = 5.0;
let dragCoeff = 0.5;
let thrustMax = 5000; // Newtons (engine force)
let thrust = 0;
let rpm = 0;
let fuel = 100; // %
let flapsDeploy = 0; // 0-100%
let velocity = new THREE.Vector3();
let wind = new THREE.Vector3((Math.random() * 2 - 1) * 5, 0, (Math.random() * 2 - 1) * 5); // m/s
// Controls
let keys = { w: false, s: false, a: false, d: false, q: false, e: false, f: false };
document.addEventListener('keydown', (e) => {
switch (e.key) {
case 'w': keys.w = true; break;
case 's': keys.s = true; break;
case 'a': keys.a = true; break;
case 'd': keys.d = true; break;
case 'q': keys.q = true; break;
case 'e': keys.e = true; break;
case 'f': if (flapsDeploy < 100) flapsDeploy += 10; break;
}
});
document.addEventListener('keyup', (e) => {
switch (e.key) {
case 'w': keys.w = false; break;
case 's': keys.s = false; break;
case 'a': keys.a = false; break;
case 'd': keys.d = false; break;
case 'q': keys.q = false; break;
case 'e': keys.e = false; break;
}
});
// Instruments update
function updateInstruments() {
document.getElementById('altimeter').innerText = Math.round(planeGeom.position.y * 3.28084) + ' ft';
document.getElementById('airspeed').innerText = Math.round(velocity.length() * 1.94384) + ' kts';
document.getElementById('heading').innerText = Math.round(THREE.Math.radToDeg(planeGeom.rotation.y)) % 360 + '°';
document.getElementById('rpm').innerText = Math.round(rpm);
document.getElementById('fuel').innerText = Math.round(fuel) + '%';
document.getElementById('flaps').innerText = flapsDeploy + '%';
}
let pitch = 0, yaw = 0, roll = 0;
function animate() {
requestAnimationFrame(animate);
// Engine
if (keys.w && fuel > 0) {
thrust = Math.min(thrustMax, thrust + 100);
rpm = Math.min(2500, rpm + 50);
fuel -= 0.05; // Consume fuel
} else {
thrust = Math.max(0, thrust - 100);
rpm = Math.max(0, rpm - 50);
}
// Aerodynamics
let airspeedVec = velocity.clone().sub(wind); // Relative airspeed
let airspeed = airspeedVec.length();
let angleOfAttack = Math.acos(airspeedVec.dot(new THREE.Vector3(0, 1, 0).applyQuaternion(planeGeom.quaternion)));
let lift = 0.5 * 1.225 * wingArea * wingLiftCoeff * airspeed * airspeed * Math.sin(angleOfAttack) * (1 + flapsDeploy / 100);
let drag = 0.5 * 1.225 * dragCoeff * wingArea * airspeed * airspeed;
if (angleOfAttack > Math.PI / 4) lift *= 0.5; // Stall
// Forces
let liftVec = new THREE.Vector3(0, lift, 0).applyQuaternion(planeGeom.quaternion);
let dragVec = airspeedVec.clone().multiplyScalar(-drag / airspeed);
let thrustVec = new THREE.Vector3(0, 0, thrust).applyQuaternion(planeGeom.quaternion);
let totalForce = new THREE.Vector3().add(liftVec).add(dragVec).add(thrustVec).add(new THREE.Vector3(0, -mass * 9.81, 0));
// Update velocity & position
velocity.add(totalForce.divideScalar(mass).multiplyScalar(1/60));
planeGeom.position.add(velocity.clone().multiplyScalar(1/60));
// Collision (raycast down)
let raycaster = new THREE.Raycaster(planeGeom.position, new THREE.Vector3(0, -1, 0));
let intersects = raycaster.intersectObject(terrain);
if (intersects.length > 0 && intersects[0].distance < 5) {
planeGeom.position.y = intersects[0].point.y + 2; // Don't go through ground
velocity.y = Math.max(0, velocity.y * 0.8);
}
// Controls
if (keys.a) yaw -= 0.01;
if (keys.d) yaw += 0.01;
if (keys.q) roll -= 0.02;
if (keys.e) roll += 0.02;
if (keys.s) pitch += 0.005;
if (keys.w) pitch -= 0.005;
// Apply rotations
planeGeom.rotation.order = 'ZXY';
planeGeom.rotation.z = roll;
planeGeom.rotation.x = pitch;
planeGeom.rotation.y = yaw;
// Update sun position (day/night cycle)
let time = Date.now() * 0.00005;
sun.position.x = Math.sin(time) * 100;
sun.position.z = Math.cos(time) * 100;
sun.position.y = Math.sin(time * 0.5) * 50 + 50; // Rise/set
skyMat.uniforms.sunPosition.value.copy(sun.position);
ambientLight.intensity = 0.2 + sun.position.y / 200;
updateInstruments();
renderer.render(scene, camera);
}
animate();
</script>
</body>
</html> |