Files
blackroad-metaverse/deploy-temp/physics-engine.js
Your Name 5e3404b1cd 🚀 EPIC METAVERSE UPGRADE: All Features Complete
 DESIGN COHESION (40% → 95%)
- Applied official BlackRoad brand colors across ALL HTML files
- Implemented golden ratio spacing system (φ = 1.618)
- Updated CSS variables: --sunrise-orange, --hot-pink, --vivid-purple, --cyber-blue
- Fixed 3D agent colors: Alice (0x0066FF), Aria (0xFF0066), Lucidia (0x7700FF)

📦 NEW PRODUCTION MODULES
- audio-system.js: Procedural music, biome sounds, weather effects
- api-client.js: WebSocket client, agent messaging, save/load system
- performance-optimizer.js: LOD system, object pooling, FPS monitoring

🎯 FILES UPDATED
- universe.html, index.html, pangea.html, ultimate.html

🛠 DEPLOYMENT TOOLS
- deploy-quick.sh: Automated Cloudflare Pages deployment

📚 DOCUMENTATION
- Complete feature documentation and deployment records

🌐 LIVE: https://2bb3d69b.blackroad-metaverse.pages.dev

This commit represents a complete metaverse transformation! 🔥
2026-01-30 15:39:26 -06:00

609 lines
18 KiB
JavaScript

/**
* ENVIRONMENTAL PHYSICS ENGINE
*
* Realistic physics simulation with wind, gravity, water, temperature,
* and environmental forces that affect all objects in the metaverse.
*
* Philosophy: "THE UNIVERSE FOLLOWS NATURAL LAWS, BUT MAGIC EXISTS IN THE DETAILS"
*/
import * as THREE from 'three';
// ===== PHYSICS CONSTANTS =====
export const PHYSICS_CONSTANTS = {
GRAVITY: -9.81,
AIR_DENSITY: 1.225,
WATER_DENSITY: 1000,
TERMINAL_VELOCITY: 53,
FRICTION: {
GRASS: 0.8,
SAND: 0.6,
WATER: 0.95,
ICE: 0.05,
STONE: 0.7
}
};
// ===== PHYSICAL OBJECT CLASS =====
export class PhysicalObject {
constructor(mesh, properties = {}) {
this.mesh = mesh;
// Physical properties
this.mass = properties.mass || 1.0;
this.density = properties.density || 1.0;
this.drag = properties.drag || 0.1;
this.elasticity = properties.elasticity || 0.5; // Bounciness
this.friction = properties.friction || 0.7;
this.isStatic = properties.isStatic || false;
// State
this.velocity = new THREE.Vector3(0, 0, 0);
this.acceleration = new THREE.Vector3(0, 0, 0);
this.angularVelocity = new THREE.Vector3(0, 0, 0);
this.forces = [];
// Collision
this.radius = properties.radius || 0.5;
this.grounded = false;
this.submerged = false;
this.submersionDepth = 0;
// Temperature affects
this.temperature = 20; // Celsius
this.canFreeze = properties.canFreeze || false;
this.canBurn = properties.canBurn || false;
}
applyForce(force) {
if (this.isStatic) return;
this.forces.push(force.clone());
}
applyImpulse(impulse) {
if (this.isStatic) return;
this.velocity.add(impulse.clone().divideScalar(this.mass));
}
update(deltaTime) {
if (this.isStatic) return;
// Sum all forces
const totalForce = new THREE.Vector3(0, 0, 0);
this.forces.forEach(force => totalForce.add(force));
this.forces = [];
// Calculate acceleration (F = ma)
this.acceleration.copy(totalForce.divideScalar(this.mass));
// Update velocity
this.velocity.add(this.acceleration.clone().multiplyScalar(deltaTime));
// Air resistance / drag
const dragForce = this.velocity.clone()
.multiplyScalar(-this.drag * this.velocity.length());
this.velocity.add(dragForce.multiplyScalar(deltaTime));
// Update position
const deltaPosition = this.velocity.clone().multiplyScalar(deltaTime);
this.mesh.position.add(deltaPosition);
// Update rotation
this.mesh.rotation.x += this.angularVelocity.x * deltaTime;
this.mesh.rotation.y += this.angularVelocity.y * deltaTime;
this.mesh.rotation.z += this.angularVelocity.z * deltaTime;
// Reset acceleration
this.acceleration.set(0, 0, 0);
}
checkGroundCollision(groundHeight = 0) {
const objectBottom = this.mesh.position.y - this.radius;
if (objectBottom <= groundHeight) {
this.mesh.position.y = groundHeight + this.radius;
if (this.velocity.y < 0) {
// Bounce
this.velocity.y *= -this.elasticity;
// Stop bouncing if velocity is very small
if (Math.abs(this.velocity.y) < 0.1) {
this.velocity.y = 0;
this.grounded = true;
}
}
// Apply friction
this.velocity.x *= this.friction;
this.velocity.z *= this.friction;
} else {
this.grounded = false;
}
}
checkWaterSubmersion(waterLevel = 0) {
const objectBottom = this.mesh.position.y - this.radius;
const objectTop = this.mesh.position.y + this.radius;
if (objectTop > waterLevel && objectBottom < waterLevel) {
// Partially submerged
this.submerged = true;
this.submersionDepth = (waterLevel - objectBottom) / (this.radius * 2);
// Buoyancy force (Archimedes' principle)
const volume = (4/3) * Math.PI * Math.pow(this.radius, 3) * this.submersionDepth;
const buoyancyForce = PHYSICS_CONSTANTS.WATER_DENSITY * volume * -PHYSICS_CONSTANTS.GRAVITY;
this.applyForce(new THREE.Vector3(0, buoyancyForce, 0));
// Water resistance
this.velocity.multiplyScalar(0.95);
} else if (objectTop <= waterLevel) {
// Fully submerged
this.submerged = true;
this.submersionDepth = 1;
const volume = (4/3) * Math.PI * Math.pow(this.radius, 3);
const buoyancyForce = PHYSICS_CONSTANTS.WATER_DENSITY * volume * -PHYSICS_CONSTANTS.GRAVITY;
this.applyForce(new THREE.Vector3(0, buoyancyForce, 0));
this.velocity.multiplyScalar(0.9);
} else {
this.submerged = false;
this.submersionDepth = 0;
}
}
}
// ===== WIND SYSTEM =====
export class WindSystem {
constructor() {
this.globalWind = new THREE.Vector3(0, 0, 0);
this.gusts = [];
this.turbulence = 0.5;
this.baseSpeed = 2.0;
this.time = 0;
}
update(deltaTime) {
this.time += deltaTime;
// Slowly changing wind direction
const angle = Math.sin(this.time * 0.1) * Math.PI * 2;
const speed = this.baseSpeed + Math.sin(this.time * 0.2) * 1.0;
this.globalWind.set(
Math.cos(angle) * speed,
0,
Math.sin(angle) * speed
);
// Update gusts
this.gusts = this.gusts.filter(gust => {
gust.lifetime -= deltaTime;
gust.strength *= 0.99;
return gust.lifetime > 0;
});
// Spawn random gusts
if (Math.random() < 0.01) {
this.spawnGust();
}
}
spawnGust() {
this.gusts.push({
position: new THREE.Vector3(
(Math.random() - 0.5) * 100,
0,
(Math.random() - 0.5) * 100
),
direction: new THREE.Vector3(
(Math.random() - 0.5) * 2,
0,
(Math.random() - 0.5) * 2
).normalize(),
strength: 5 + Math.random() * 10,
radius: 10 + Math.random() * 20,
lifetime: 3 + Math.random() * 5
});
}
getWindAt(position) {
let wind = this.globalWind.clone();
// Add turbulence
wind.x += (Math.random() - 0.5) * this.turbulence;
wind.z += (Math.random() - 0.5) * this.turbulence;
// Add gusts
this.gusts.forEach(gust => {
const distance = position.distanceTo(gust.position);
if (distance < gust.radius) {
const influence = 1 - (distance / gust.radius);
const gustForce = gust.direction.clone()
.multiplyScalar(gust.strength * influence);
wind.add(gustForce);
}
});
return wind;
}
applyWindTo(object, position) {
const wind = this.getWindAt(position);
const windForce = wind.clone().multiplyScalar(object.drag);
object.applyForce(windForce);
}
}
// ===== GRAVITY FIELD SYSTEM =====
export class GravityField {
constructor() {
this.globalGravity = new THREE.Vector3(0, PHYSICS_CONSTANTS.GRAVITY, 0);
this.anomalies = [];
}
addAnomaly(position, strength, radius) {
this.anomalies.push({
position: position.clone(),
strength,
radius
});
}
removeAnomaly(index) {
this.anomalies.splice(index, 1);
}
getGravityAt(position) {
let gravity = this.globalGravity.clone();
// Add anomalies (attractors or repellers)
this.anomalies.forEach(anomaly => {
const direction = anomaly.position.clone().sub(position);
const distance = direction.length();
if (distance < anomaly.radius && distance > 0.1) {
const influence = 1 - (distance / anomaly.radius);
const force = direction.normalize()
.multiplyScalar(anomaly.strength * influence);
gravity.add(force);
}
});
return gravity;
}
applyGravityTo(object, position) {
const gravity = this.getGravityAt(position);
const gravityForce = gravity.clone().multiplyScalar(object.mass);
object.applyForce(gravityForce);
}
}
// ===== WATER FLOW SYSTEM =====
export class WaterFlowSystem {
constructor() {
this.currents = [];
this.waterLevel = 0;
this.waveHeight = 0.5;
this.waveSpeed = 1.0;
this.time = 0;
}
update(deltaTime) {
this.time += deltaTime;
}
addCurrent(position, direction, strength, radius) {
this.currents.push({
position: position.clone(),
direction: direction.normalize(),
strength,
radius
});
}
getWaterLevelAt(x, z) {
// Wave simulation using sine waves
const wave1 = Math.sin(x * 0.1 + this.time * this.waveSpeed) * this.waveHeight;
const wave2 = Math.sin(z * 0.15 - this.time * this.waveSpeed * 0.7) * this.waveHeight * 0.5;
return this.waterLevel + wave1 + wave2;
}
getCurrentAt(position) {
let current = new THREE.Vector3(0, 0, 0);
this.currents.forEach(c => {
const distance = position.distanceTo(c.position);
if (distance < c.radius) {
const influence = 1 - (distance / c.radius);
const force = c.direction.clone()
.multiplyScalar(c.strength * influence);
current.add(force);
}
});
return current;
}
applyWaterForceTo(object, position) {
if (object.submerged) {
const current = this.getCurrentAt(position);
object.applyForce(current.multiplyScalar(object.submersionDepth));
}
}
}
// ===== TEMPERATURE SYSTEM =====
export class TemperatureSystem {
constructor() {
this.ambientTemperature = 20; // Celsius
this.heatSources = [];
this.coldSources = [];
}
addHeatSource(position, temperature, radius) {
this.heatSources.push({ position: position.clone(), temperature, radius });
}
addColdSource(position, temperature, radius) {
this.coldSources.push({ position: position.clone(), temperature, radius });
}
getTemperatureAt(position) {
let temp = this.ambientTemperature;
// Heat sources
this.heatSources.forEach(source => {
const distance = position.distanceTo(source.position);
if (distance < source.radius) {
const influence = 1 - (distance / source.radius);
temp += (source.temperature - this.ambientTemperature) * influence;
}
});
// Cold sources
this.coldSources.forEach(source => {
const distance = position.distanceTo(source.position);
if (distance < source.radius) {
const influence = 1 - (distance / source.radius);
temp -= (this.ambientTemperature - source.temperature) * influence;
}
});
return temp;
}
updateObjectTemperature(object, position, deltaTime) {
const envTemp = this.getTemperatureAt(position);
const tempDiff = envTemp - object.temperature;
// Thermal equilibrium approach
object.temperature += tempDiff * 0.1 * deltaTime;
// Handle phase changes
if (object.canFreeze && object.temperature < 0) {
object.frozen = true;
object.friction *= 0.1; // Ice is slippery
} else if (object.frozen && object.temperature > 0) {
object.frozen = false;
}
if (object.canBurn && object.temperature > 200) {
object.burning = true;
}
}
}
// ===== COLLISION DETECTION =====
export class CollisionSystem {
constructor() {
this.objects = [];
}
addObject(object) {
this.objects.push(object);
}
removeObject(object) {
const index = this.objects.indexOf(object);
if (index > -1) {
this.objects.splice(index, 1);
}
}
checkCollisions() {
for (let i = 0; i < this.objects.length; i++) {
for (let j = i + 1; j < this.objects.length; j++) {
this.checkCollision(this.objects[i], this.objects[j]);
}
}
}
checkCollision(obj1, obj2) {
const distance = obj1.mesh.position.distanceTo(obj2.mesh.position);
const minDistance = obj1.radius + obj2.radius;
if (distance < minDistance) {
this.resolveCollision(obj1, obj2, distance, minDistance);
}
}
resolveCollision(obj1, obj2, distance, minDistance) {
// Separate objects
const direction = obj2.mesh.position.clone()
.sub(obj1.mesh.position)
.normalize();
const overlap = minDistance - distance;
const separation = direction.clone().multiplyScalar(overlap / 2);
if (!obj1.isStatic) obj1.mesh.position.sub(separation);
if (!obj2.isStatic) obj2.mesh.position.add(separation);
// Calculate collision response
const relativeVelocity = obj2.velocity.clone().sub(obj1.velocity);
const velocityAlongNormal = relativeVelocity.dot(direction);
if (velocityAlongNormal < 0) {
// Objects are moving towards each other
const e = Math.min(obj1.elasticity, obj2.elasticity);
const impulse = -(1 + e) * velocityAlongNormal / (1/obj1.mass + 1/obj2.mass);
const impulseVector = direction.clone().multiplyScalar(impulse);
if (!obj1.isStatic) {
obj1.velocity.sub(impulseVector.clone().divideScalar(obj1.mass));
}
if (!obj2.isStatic) {
obj2.velocity.add(impulseVector.clone().divideScalar(obj2.mass));
}
}
}
}
// ===== MAIN PHYSICS ENGINE =====
export class PhysicsEngine {
constructor(scene) {
this.scene = scene;
this.objects = [];
this.wind = new WindSystem();
this.gravity = new GravityField();
this.water = new WaterFlowSystem();
this.temperature = new TemperatureSystem();
this.collision = new CollisionSystem();
this.enabled = true;
}
addObject(mesh, properties) {
const physObject = new PhysicalObject(mesh, properties);
this.objects.push(physObject);
this.collision.addObject(physObject);
return physObject;
}
removeObject(physObject) {
const index = this.objects.indexOf(physObject);
if (index > -1) {
this.objects.splice(index, 1);
this.collision.removeObject(physObject);
}
}
update(deltaTime) {
if (!this.enabled) return;
// Clamp deltaTime to prevent physics explosions
deltaTime = Math.min(deltaTime, 0.033);
// Update environmental systems
this.wind.update(deltaTime);
this.water.update(deltaTime);
// Update all physical objects
this.objects.forEach(obj => {
const position = obj.mesh.position;
// Apply environmental forces
this.gravity.applyGravityTo(obj, position);
this.wind.applyWindTo(obj, position);
this.water.applyWaterForceTo(obj, position);
// Update temperature
this.temperature.updateObjectTemperature(obj, position, deltaTime);
// Update physics
obj.update(deltaTime);
// Check ground collision
obj.checkGroundCollision(0);
// Check water submersion
const waterLevel = this.water.getWaterLevelAt(position.x, position.z);
obj.checkWaterSubmersion(waterLevel);
});
// Check collisions
this.collision.checkCollisions();
}
// Utility functions
createFallingObject(position, mesh, mass = 1.0) {
const obj = this.addObject(mesh, {
mass,
elasticity: 0.6,
friction: 0.8
});
mesh.position.copy(position);
return obj;
}
createFloatingObject(position, mesh) {
const obj = this.addObject(mesh, {
mass: 0.5,
density: 0.3, // Less dense than water
elasticity: 0.4
});
mesh.position.copy(position);
return obj;
}
createProjectile(position, velocity, mesh, mass = 0.5) {
const obj = this.addObject(mesh, {
mass,
drag: 0.05,
elasticity: 0.3
});
mesh.position.copy(position);
obj.velocity.copy(velocity);
return obj;
}
// Effects
createExplosion(position, force, radius) {
this.objects.forEach(obj => {
const distance = obj.mesh.position.distanceTo(position);
if (distance < radius) {
const direction = obj.mesh.position.clone().sub(position).normalize();
const strength = force * (1 - distance / radius);
const impulse = direction.multiplyScalar(strength);
obj.applyImpulse(impulse);
}
});
}
createWind(position, direction, strength, radius, duration) {
// Temporary wind gust
this.wind.gusts.push({
position: position.clone(),
direction: direction.normalize(),
strength,
radius,
lifetime: duration
});
}
setWaterLevel(level) {
this.water.waterLevel = level;
}
getStats() {
return {
objects: this.objects.length,
windSpeed: this.wind.globalWind.length().toFixed(2),
gusts: this.wind.gusts.length,
gravityAnomalies: this.gravity.anomalies.length,
waterCurrent: this.water.currents.length,
heatSources: this.temperature.heatSources.length
};
}
}
export default PhysicsEngine;