html-trap-2026 / src /three-console.js
Vintendopower's picture
Fix FMV audio and update media repository name
83fa3b0 verified
Raw History Blame Contribute Delete
6.68 kB
// Progressive WebGL presentation layer. The game remains usable if this optional
// module or WebGL is unavailable.
import * as THREE from 'three';
import {assetUrl} from './asset-resolver.js';
import {OBJLoader} from 'https://huggingface.co/datasets/Vintendopower/nighttrap-media/resolve/main/assets/vendor/OBJLoader.js';
const canvas=document.getElementById('threeConsole');
if(canvas){
const host=canvas.parentElement;
const renderer=new THREE.WebGLRenderer({canvas,alpha:true,antialias:true,powerPreference:'low-power'});
renderer.setPixelRatio(Math.min(window.devicePixelRatio||1,1.5));
renderer.outputColorSpace=THREE.SRGBColorSpace;
const scene=new THREE.Scene();
const camera=new THREE.PerspectiveCamera(34,1,.1,100);
camera.position.set(0,1.8,8.4);camera.lookAt(0,.2,0);
scene.add(new THREE.AmbientLight(0x6b8290,.38));
const redLight=new THREE.PointLight(0xff3024,1.3,12);redLight.position.set(-3,2,3);scene.add(redLight);
const greenLight=new THREE.PointLight(0x7dffb0,1.1,10);greenLight.position.set(3,1,2);scene.add(greenLight);
const consoleGroup=new THREE.Group();scene.add(consoleGroup);
const frameMaterial=new THREE.MeshStandardMaterial({color:0x0a1115,metalness:.82,roughness:.3,transparent:true,opacity:.92});
const trimMaterial=new THREE.MeshStandardMaterial({color:0x334650,metalness:.7,roughness:.26,emissive:0x08151b,emissiveIntensity:.8});
const frame=new THREE.Mesh(new THREE.BoxGeometry(8.2,4.5,.42),frameMaterial);frame.position.set(0,.1,-.3);consoleGroup.add(frame);
const trim=new THREE.Mesh(new THREE.BoxGeometry(7.8,4.1,.12),trimMaterial);trim.position.set(0,.1,-.05);consoleGroup.add(trim);
const roomMaterials=[];
for(let i=0;i<8;i++){
const material=new THREE.MeshStandardMaterial({color:0x15252c,emissive:0x143e4a,emissiveIntensity:.25,metalness:.4,roughness:.36});
roomMaterials.push(material);
const x=-3.25+(i%4)*2.17,y=-2.0+Math.floor(i/4)*.72;
const slot=new THREE.Mesh(new THREE.BoxGeometry(1.72,.46,.08),material);slot.position.set(x,y,.08);consoleGroup.add(slot);
}
const sweep=new THREE.Mesh(new THREE.BoxGeometry(7.1,.018,.02),new THREE.MeshBasicMaterial({color:0x79ffc4,transparent:true,opacity:.55}));sweep.position.set(0,.2,.2);consoleGroup.add(sweep);
const grid=new THREE.GridHelper(14,28,0x2f5360,0x172c35);grid.position.y=-2.35;grid.rotation.x=.03;grid.material.transparent=true;grid.material.opacity=.25;scene.add(grid);
const textureLoader=new THREE.TextureLoader();
const mapTexture=textureLoader.load(assetUrl('assets/images/sharedassets1_369.png'),texture=>{texture.colorSpace=THREE.SRGBColorSpace;});
const mapPanel=new THREE.Mesh(new THREE.PlaneGeometry(1.55,.98),new THREE.MeshBasicMaterial({map:mapTexture,transparent:true,opacity:.72}));
mapPanel.position.set(3.02,.52,.19);mapPanel.rotation.y=-.08;consoleGroup.add(mapPanel);
const buttonTexture=textureLoader.load(assetUrl('assets/images/sharedassets1_425.png'),texture=>{texture.colorSpace=THREE.SRGBColorSpace;});
const buttonPanel=new THREE.Mesh(new THREE.PlaneGeometry(1.2,.64),new THREE.MeshBasicMaterial({map:buttonTexture,transparent:true,opacity:.42}));
buttonPanel.position.set(-3.12,.55,.2);buttonPanel.rotation.y=.08;consoleGroup.add(buttonPanel);
const noiseTexture=textureLoader.load(assetUrl('assets/images/sharedassets1_409.png'),texture=>{texture.colorSpace=THREE.SRGBColorSpace;texture.wrapS=texture.wrapT=THREE.RepeatWrapping;});
const noiseMaterial=new THREE.MeshBasicMaterial({map:noiseTexture,transparent:true,opacity:.10,depthWrite:false,blending:THREE.AdditiveBlending});
const noisePlane=new THREE.Mesh(new THREE.PlaneGeometry(7.6,4.0),noiseMaterial);noisePlane.position.set(0,.1,.24);consoleGroup.add(noisePlane);
const extractedLayouts={};
const objLoader=new OBJLoader();
const layoutVariants={night:assetUrl('assets/models/sharedassets2_12_PlayUp_1994-layout.obj'),sotc:assetUrl('assets/models/sharedassets3_4_PlayUp_1993.obj'),survivor:assetUrl('assets/models/sharedassets4_5_PlayUp_1992.obj')};
function selectLayout(){const variant=eraOverride==='auto'?(currentMode.startsWith('sotc')?'sotc':currentMode.startsWith('survivor')?'survivor':'night'):({1992:'survivor',1993:'sotc',1994:'night'}[eraOverride]||'night');Object.entries(extractedLayouts).forEach(([key,object])=>{object.visible=key===variant;});}
Object.entries(layoutVariants).forEach(([key,url])=>objLoader.load(url,object=>{
const bounds=new THREE.Box3().setFromObject(object),size=bounds.getSize(new THREE.Vector3()),center=bounds.getCenter(new THREE.Vector3());
object.position.sub(center);object.scale.setScalar(6.6/Math.max(size.x,size.y,size.z));object.position.set(0,.05,-.18);
object.traverse(node=>{if(!node.isMesh)return;node.material=new THREE.MeshStandardMaterial({color:key==='sotc'?0x8b3438:key==='survivor'?0x6670a0:0x4f7486,emissive:key==='sotc'?0x260607:key==='survivor'?0x11182d:0x081a26,emissiveIntensity:.55,metalness:.7,roughness:.38,transparent:true,opacity:.72});node.material.depthWrite=false;});
extractedLayouts[key]=object;scene.add(object);selectLayout();
},undefined,()=>{}));
let currentRoom=0,currentMode='night',eraOverride='auto',alert=false,last=performance.now();
function resize(){const w=host.clientWidth,h=host.clientHeight;if(!w||!h)return;renderer.setSize(w,h,false);camera.aspect=w/h;camera.updateProjectionMatrix();}
function setMode(mode){currentMode=mode;const sotc=String(mode).startsWith('sotc');frameMaterial.color.setHex(sotc?0x170b0d:0x0a1115);trimMaterial.color.setHex(sotc?0x5e2426:0x334650);redLight.color.setHex(sotc?0xff5b39:0xff3024);selectLayout();}
function setEra(value){eraOverride=['auto','1992','1993','1994'].includes(value)?value:'auto';selectLayout();}
function setRoom(index){currentRoom=Math.max(0,Math.min(7,Number(index)||0));roomMaterials.forEach((m,i)=>{m.emissiveIntensity=i===currentRoom ? .95 : .16;m.color.setHex(i===currentRoom?0x284b50:0x15252c);});}
function setAlert(value){alert=!!value;greenLight.intensity=alert?.35:1.1;redLight.intensity=alert?2.2:1.3;}
function animate(now){const dt=Math.min(.05,(now-last)/1000);last=now;sweep.position.y=1.8-((now*.00023)%1)*3.6;sweep.material.opacity=.28+.18*Math.sin(now*.003);if(noiseTexture)noiseTexture.offset.x=(now*.00008)%1,noiseTexture.offset.y=(now*.00011)%1;noiseMaterial.opacity=alert?.18:.10;consoleGroup.rotation.y=Math.sin(now*.00035)*.012;redLight.intensity+=(alert?2.2:1.3-redLight.intensity)*dt*3;renderer.render(scene,camera);requestAnimationFrame(animate);}
window.addEventListener('resize',resize);resize();setRoom(0);requestAnimationFrame(animate);
window.nightTrapVisuals={setMode,setEra,setRoom,setAlert};
}