clear
This commit is contained in:
1 parent
618ba02cb9
commit
83278b2998
124 files changed
-49298
No files matched your search
@@ -1,319 +0,0 @@
|
||||
/**
|
||||
* Procedural 3D Quadcopter Drone Builder in Three.js
|
||||
* Generates high-fidelity carbon-fiber military/tactical UAV models with animated rotors,
|
||||
* navigation LEDs, gimbal camera, landing skids, and thrust trail effects.
|
||||
*/
|
||||
|
||||
class DroneModel {
|
||||
constructor(scene, options = {}) {
|
||||
this.scene = scene;
|
||||
this.id = options.id || "drone";
|
||||
this.name = options.name || "UAV";
|
||||
this.primaryColor = options.primaryColor || 0x00f0ff;
|
||||
this.accentColor = options.accentColor || 0x111625;
|
||||
this.isLeader = !!options.isLeader;
|
||||
|
||||
this.group = new THREE.Group();
|
||||
this.rotors = [];
|
||||
this.rotorDiscs = [];
|
||||
this.strobeLeds = [];
|
||||
this.thrustParticles = [];
|
||||
|
||||
this._buildModel();
|
||||
this.scene.add(this.group);
|
||||
}
|
||||
|
||||
_buildModel() {
|
||||
// High quality PBR materials
|
||||
const carbonMat = new THREE.MeshStandardMaterial({
|
||||
color: 0x1a1e24,
|
||||
metalness: 0.85,
|
||||
roughness: 0.3,
|
||||
});
|
||||
|
||||
const metalMat = new THREE.MeshStandardMaterial({
|
||||
color: 0x8892a0,
|
||||
metalness: 0.95,
|
||||
roughness: 0.15,
|
||||
});
|
||||
|
||||
const canopyMat = new THREE.MeshStandardMaterial({
|
||||
color: 0x0d131a,
|
||||
metalness: 0.6,
|
||||
roughness: 0.1,
|
||||
clearcoat: 0.8,
|
||||
clearcoatRoughness: 0.1
|
||||
});
|
||||
|
||||
const accentMat = new THREE.MeshStandardMaterial({
|
||||
color: this.primaryColor,
|
||||
metalness: 0.7,
|
||||
roughness: 0.3,
|
||||
emissive: this.primaryColor,
|
||||
emissiveIntensity: 0.35
|
||||
});
|
||||
|
||||
const glowMat = new THREE.MeshBasicMaterial({
|
||||
color: this.primaryColor
|
||||
});
|
||||
|
||||
// 1. Central Body Chassis (Lower & Upper aerodynamic hull)
|
||||
const bodyGeo = new THREE.CylinderGeometry(0.7, 0.9, 0.32, 8);
|
||||
bodyGeo.rotateY(Math.PI / 8);
|
||||
const bodyMesh = new THREE.Mesh(bodyGeo, carbonMat);
|
||||
bodyMesh.castShadow = true;
|
||||
bodyMesh.receiveShadow = true;
|
||||
this.group.add(bodyMesh);
|
||||
|
||||
// Aerodynamic Top Canopy Shell
|
||||
const canopyGeo = new THREE.SphereGeometry(0.65, 16, 12);
|
||||
canopyGeo.scale(1.0, 0.45, 1.35);
|
||||
const canopyMesh = new THREE.Mesh(canopyGeo, canopyMat);
|
||||
canopyMesh.position.y = 0.18;
|
||||
canopyMesh.castShadow = true;
|
||||
this.group.add(canopyMesh);
|
||||
|
||||
// Center Accent Stripe
|
||||
const stripeGeo = new THREE.BoxGeometry(0.12, 0.04, 1.4);
|
||||
const stripeMesh = new THREE.Mesh(stripeGeo, accentMat);
|
||||
stripeMesh.position.y = 0.34;
|
||||
this.group.add(stripeMesh);
|
||||
|
||||
// 2. Gimbal Camera Sensor at Front Underbelly
|
||||
const gimbalGroup = new THREE.Group();
|
||||
gimbalGroup.position.set(0, -0.15, 0.7);
|
||||
|
||||
const gimbalBase = new THREE.Mesh(new THREE.CylinderGeometry(0.12, 0.12, 0.08, 12), metalMat);
|
||||
gimbalGroup.add(gimbalBase);
|
||||
|
||||
const cameraSphere = new THREE.Mesh(new THREE.SphereGeometry(0.16, 16, 16), carbonMat);
|
||||
cameraSphere.position.y = -0.1;
|
||||
gimbalGroup.add(cameraSphere);
|
||||
|
||||
// Camera Optical Lens (Glowing ring)
|
||||
const lensGeo = new THREE.CylinderGeometry(0.09, 0.09, 0.06, 16);
|
||||
lensGeo.rotateX(Math.PI / 2);
|
||||
const lensMesh = new THREE.Mesh(lensGeo, new THREE.MeshBasicMaterial({ color: 0x00ffff }));
|
||||
lensMesh.position.set(0, -0.1, 0.14);
|
||||
gimbalGroup.add(lensMesh);
|
||||
|
||||
this.group.add(gimbalGroup);
|
||||
|
||||
// 3. Carbon Fiber Rotor Arms (X-Configuration)
|
||||
const armLength = 1.9;
|
||||
const armRadius = 0.055;
|
||||
const armPositions = [
|
||||
{ angle: Math.PI * 0.25, dir: 1, ledColor: 0x00ff66 }, // Front-Right (Green nav)
|
||||
{ angle: Math.PI * 0.75, dir: -1, ledColor: 0xff0044 }, // Rear-Right (Red nav)
|
||||
{ angle: Math.PI * 1.25, dir: 1, ledColor: 0xff0044 }, // Rear-Left (Red nav)
|
||||
{ angle: Math.PI * 1.75, dir: -1, ledColor: 0x00ff66 }, // Front-Left (Green nav)
|
||||
];
|
||||
|
||||
this.rotors = [];
|
||||
this.rotorDiscs = [];
|
||||
|
||||
armPositions.forEach((arm, i) => {
|
||||
const armGroup = new THREE.Group();
|
||||
armGroup.rotation.y = arm.angle;
|
||||
|
||||
// Arm Carbon Tube
|
||||
const armGeo = new THREE.CylinderGeometry(armRadius, armRadius, armLength, 12);
|
||||
armGeo.rotateZ(Math.PI / 2);
|
||||
const armMesh = new THREE.Mesh(armGeo, carbonMat);
|
||||
armMesh.position.x = armLength * 0.5;
|
||||
armMesh.castShadow = true;
|
||||
armGroup.add(armMesh);
|
||||
|
||||
// Diagonal support truss
|
||||
const trussGeo = new THREE.CylinderGeometry(0.025, 0.025, armLength * 0.6, 8);
|
||||
trussGeo.rotateZ(Math.PI / 2.8);
|
||||
const trussMesh = new THREE.Mesh(trussGeo, carbonMat);
|
||||
trussMesh.position.set(armLength * 0.35, -0.1, 0);
|
||||
armGroup.add(trussMesh);
|
||||
|
||||
// Brushless Electric Motor Housing at Arm Tip
|
||||
const motorGroup = new THREE.Group();
|
||||
motorGroup.position.x = armLength;
|
||||
|
||||
const motorBase = new THREE.Mesh(new THREE.CylinderGeometry(0.18, 0.18, 0.22, 16), metalMat);
|
||||
motorBase.position.y = 0.05;
|
||||
motorBase.castShadow = true;
|
||||
motorGroup.add(motorBase);
|
||||
|
||||
// Motor Accent Ring
|
||||
const ringGeo = new THREE.TorusGeometry(0.185, 0.02, 8, 20);
|
||||
ringGeo.rotateX(Math.PI / 2);
|
||||
const ringMesh = new THREE.Mesh(ringGeo, accentMat);
|
||||
ringMesh.position.y = 0.08;
|
||||
motorGroup.add(ringMesh);
|
||||
|
||||
// Tip LED Navigation Light
|
||||
const ledGeo = new THREE.SphereGeometry(0.06, 8, 8);
|
||||
const ledMat = new THREE.MeshBasicMaterial({ color: arm.ledColor });
|
||||
const ledMesh = new THREE.Mesh(ledGeo, ledMat);
|
||||
ledMesh.position.set(0.12, -0.05, 0);
|
||||
motorGroup.add(ledMesh);
|
||||
this.strobeLeds.push(ledMesh);
|
||||
|
||||
// Rotating Rotor Assembly
|
||||
const propGroup = new THREE.Group();
|
||||
propGroup.position.y = 0.18;
|
||||
|
||||
// Rotor Hub Cap
|
||||
const hub = new THREE.Mesh(new THREE.ConeGeometry(0.08, 0.1, 12), metalMat);
|
||||
hub.position.y = 0.04;
|
||||
propGroup.add(hub);
|
||||
|
||||
// 3-Blade Propeller
|
||||
const bladeMat = new THREE.MeshStandardMaterial({
|
||||
color: 0x111111,
|
||||
roughness: 0.4,
|
||||
metalness: 0.5
|
||||
});
|
||||
|
||||
for (let b = 0; b < 3; b++) {
|
||||
const bladeAngle = (b * Math.PI * 2) / 3;
|
||||
const bladeGeo = new THREE.BoxGeometry(0.85, 0.015, 0.12);
|
||||
const bladeMesh = new THREE.Mesh(bladeGeo, bladeMat);
|
||||
bladeMesh.position.set(Math.cos(bladeAngle) * 0.42, 0, Math.sin(bladeAngle) * 0.42);
|
||||
bladeMesh.rotation.y = -bladeAngle;
|
||||
bladeMesh.rotation.x = 0.18; // Pitch twist
|
||||
bladeMesh.castShadow = true;
|
||||
propGroup.add(bladeMesh);
|
||||
}
|
||||
|
||||
// Motion blur rotor disc (semi-transparent glowing disc when spinning fast)
|
||||
const discGeo = new THREE.RingGeometry(0.1, 0.95, 24);
|
||||
discGeo.rotateX(Math.PI / 2);
|
||||
const discMat = new THREE.MeshBasicMaterial({
|
||||
color: this.primaryColor,
|
||||
transparent: true,
|
||||
opacity: 0.22,
|
||||
side: THREE.DoubleSide
|
||||
});
|
||||
const discMesh = new THREE.Mesh(discGeo, discMat);
|
||||
discMesh.position.y = 0.02;
|
||||
propGroup.add(discMesh);
|
||||
|
||||
motorGroup.add(propGroup);
|
||||
armGroup.add(motorGroup);
|
||||
this.group.add(armGroup);
|
||||
|
||||
this.rotors.push({ group: propGroup, dir: arm.dir });
|
||||
this.rotorDiscs.push(discMesh);
|
||||
});
|
||||
|
||||
// 4. Dual Carbon Landing Skids
|
||||
for (const side of [-1, 1]) {
|
||||
const skidGroup = new THREE.Group();
|
||||
skidGroup.position.set(side * 0.55, -0.22, 0);
|
||||
|
||||
// Struts
|
||||
const strut1 = new THREE.Mesh(new THREE.CylinderGeometry(0.03, 0.03, 0.4, 8), metalMat);
|
||||
strut1.position.set(0, -0.15, 0.4);
|
||||
strut1.rotation.z = side * 0.25;
|
||||
skidGroup.add(strut1);
|
||||
|
||||
const strut2 = new THREE.Mesh(new THREE.CylinderGeometry(0.03, 0.03, 0.4, 8), metalMat);
|
||||
strut2.position.set(0, -0.15, -0.4);
|
||||
strut2.rotation.z = side * 0.25;
|
||||
skidGroup.add(strut2);
|
||||
|
||||
// Longitudinal Runner
|
||||
const runnerGeo = new THREE.CylinderGeometry(0.04, 0.04, 1.4, 8);
|
||||
runnerGeo.rotateX(Math.PI / 2);
|
||||
const runner = new THREE.Mesh(runnerGeo, carbonMat);
|
||||
runner.position.set(side * 0.08, -0.35, 0);
|
||||
skidGroup.add(runner);
|
||||
|
||||
this.group.add(skidGroup);
|
||||
}
|
||||
|
||||
// 5. Underbelly Beacon Light
|
||||
const beaconLight = new THREE.PointLight(this.primaryColor, 1.8, 12);
|
||||
beaconLight.position.set(0, -0.25, 0);
|
||||
this.group.add(beaconLight);
|
||||
this.beaconLight = beaconLight;
|
||||
|
||||
// 6. Altitude Guide Drop Line & Ground Target Disc
|
||||
this.altLineGeo = new THREE.BufferGeometry().setFromPoints([
|
||||
new THREE.Vector3(0, 0, 0),
|
||||
new THREE.Vector3(0, -10, 0)
|
||||
]);
|
||||
this.altLineMat = new THREE.LineDashedMaterial({
|
||||
color: this.primaryColor,
|
||||
dashSize: 0.4,
|
||||
gapSize: 0.2,
|
||||
opacity: 0.5,
|
||||
transparent: true
|
||||
});
|
||||
this.altLine = new THREE.Line(this.altLineGeo, this.altLineMat);
|
||||
this.altLine.computeLineDistances();
|
||||
this.scene.add(this.altLine);
|
||||
|
||||
// Ground shadow/projection ring
|
||||
const groundRingGeo = new THREE.RingGeometry(0.3, 1.2, 24);
|
||||
groundRingGeo.rotateX(-Math.PI / 2);
|
||||
const groundRingMat = new THREE.MeshBasicMaterial({
|
||||
color: this.primaryColor,
|
||||
transparent: true,
|
||||
opacity: 0.35,
|
||||
side: THREE.DoubleSide
|
||||
});
|
||||
this.groundRing = new THREE.Mesh(groundRingGeo, groundRingMat);
|
||||
this.scene.add(this.groundRing);
|
||||
}
|
||||
|
||||
update(state, dt) {
|
||||
if (!state) return;
|
||||
|
||||
// Update Position & Attitude smoothly
|
||||
this.group.position.set(state.position.x, state.position.y, state.position.z);
|
||||
|
||||
// Pitch, Yaw, Roll rotations (Order: YXZ)
|
||||
const pitchRad = THREE.MathUtils.degToRad(state.rotation.pitch);
|
||||
const rollRad = THREE.MathUtils.degToRad(state.rotation.roll);
|
||||
const yawRad = state.rotation.yaw !== undefined ? THREE.MathUtils.degToRad(state.rotation.yaw) : 0;
|
||||
|
||||
this.group.rotation.set(0, 0, 0);
|
||||
this.group.rotation.order = "YXZ";
|
||||
this.group.rotation.y = yawRad;
|
||||
this.group.rotation.x = pitchRad;
|
||||
this.group.rotation.z = rollRad;
|
||||
|
||||
// Spin Propellers at RPM rate
|
||||
const speed = state.speed || 0;
|
||||
const baseSpin = (dt * 55.0) + (speed * 0.05);
|
||||
this.rotors.forEach((rotor, idx) => {
|
||||
rotor.group.rotation.y += baseSpin * rotor.dir;
|
||||
});
|
||||
|
||||
// Altitude Drop Line update
|
||||
const posY = this.group.position.y;
|
||||
const linePositions = new Float32Array([
|
||||
this.group.position.x, this.group.position.y, this.group.position.z,
|
||||
this.group.position.x, 0.02, this.group.position.z
|
||||
]);
|
||||
this.altLine.geometry.setAttribute('position', new THREE.BufferAttribute(linePositions, 3));
|
||||
this.altLine.computeLineDistances();
|
||||
|
||||
// Ground Shadow Ring update
|
||||
this.groundRing.position.set(this.group.position.x, 0.03, this.group.position.z);
|
||||
const ringScale = Math.max(0.6, 1.0 + (posY * 0.08));
|
||||
this.groundRing.scale.set(ringScale, ringScale, ringScale);
|
||||
|
||||
// Flashing Strobe LEDs
|
||||
const strobe = Math.sin(Date.now() * 0.008) > 0.4 ? 1.0 : 0.2;
|
||||
if (this.beaconLight) {
|
||||
this.beaconLight.intensity = 1.0 + strobe * 1.5;
|
||||
}
|
||||
}
|
||||
|
||||
destroy() {
|
||||
if (this.group && this.scene) this.scene.remove(this.group);
|
||||
if (this.altLine && this.scene) this.scene.remove(this.altLine);
|
||||
if (this.groundRing && this.scene) this.scene.remove(this.groundRing);
|
||||
}
|
||||
}
|
||||
|
||||
window.DroneModel = DroneModel;
|
||||
Reference in new issue
Block a user