1import * as THREE from '../../assets/vendor/three.module.js'; 2 3export const ROBOT_01 = Object.freeze({id:'robot-01',name:'Robot 01',height:1.05,radius:.4,reservation:1.2,maxTriangles:2500,maxDrawCalls:12,maxMaterials:4}); 4const C={ivory:0xded6c5,blue:0x4f6478,bronze:0x827354,dark:0x303a34}; 5const rad=THREE.MathUtils.degToRad; 6const finite=(n,fallback=0)=>Number.isFinite(n)?n:fallback; 7const clamp=(n,lo,hi)=>THREE.MathUtils.clamp(finite(n),lo,hi); 8 9// Four bevel rings, flat normals, no texture or external model dependency. 10function shell(w,h,d,bevel=.01,taper=1){ 11 const positions=[],rings=[]; 12 for(const [z,inset] of [[-d/2,bevel],[-d/2+bevel,0],[d/2-bevel,0],[d/2,bevel]]){ 13 const x=w/2-inset,y=h/2-inset,c=Math.min(bevel*1.5,x*.3,y*.3); 14 rings.push([[-x+c,-y], [x-c,-y], [x,-y+c], [x,y-c], [x-c,y],[-x+c,y],[-x,y-c],[-x,-y+c]].map(([px,py])=>[px*(taper+(1-taper)*(py+h/2)/h),py,z])); 15 } 16 const tri=(a,b,c)=>positions.push(...a,...b,...c); 17 for(let j=0;j<3;j++)for(let i=0;i<8;i++){const n=(i+1)%8;tri(rings[j][i],rings[j][n],rings[j+1][n]);tri(rings[j][i],rings[j+1][n],rings[j+1][i]);} 18 for(let i=0;i<8;i++){const n=(i+1)%8;tri([0,0,-d/2],rings[0][n],rings[0][i]);tri([0,0,d/2],rings[3][i],rings[3][n]);} 19 const g=new THREE.BufferGeometry();g.setAttribute('position',new THREE.Float32BufferAttribute(positions,3));g.computeVertexNormals();return g; 20} 21function cylinder(r,len,axis='y'){const g=new THREE.CylinderGeometry(r,r,len,12,1,false);if(axis==='x')g.rotateZ(Math.PI/2);if(axis==='z')g.rotateX(Math.PI/2);return g;} 22function piece(g,color,x=0,y=0,z=0,rotation=null){if(rotation){g.rotateX(rotation[0]||0);g.rotateY(rotation[1]||0);g.rotateZ(rotation[2]||0);}g.translate(x,y,z);return {g,color};} 23function bar(a,b,width,depth,color){const va=new THREE.Vector3(...a),vb=new THREE.Vector3(...b),g=shell(width,va.distanceTo(vb),depth,.006);g.applyQuaternion(new THREE.Quaternion().setFromUnitVectors(new THREE.Vector3(0,1,0),vb.clone().sub(va).normalize()));g.translate(...va.add(vb).multiplyScalar(.5).toArray());return {g,color};} 24function merged(parts,material,name){ 25 const pos=[],norm=[],colors=[]; 26 for(const {g,color} of parts){const flat=g.index?g.toNonIndexed():g,c=new THREE.Color(color),p=flat.attributes.position,n=flat.attributes.normal;pos.push(...p.array);norm.push(...n.array);for(let i=0;i<p.count;i++)colors.push(c.r,c.g,c.b);if(flat!==g)flat.dispose();g.dispose();} 27 const g=new THREE.BufferGeometry();g.setAttribute('position',new THREE.Float32BufferAttribute(pos,3));g.setAttribute('normal',new THREE.Float32BufferAttribute(norm,3));g.setAttribute('color',new THREE.Float32BufferAttribute(colors,3));g.computeBoundingSphere();const m=new THREE.Mesh(g,material);m.name=name;m.castShadow=false;m.receiveShadow=true;return m; 28} 29 30// Each instance owns its resources; disposing one never invalidates another. 31// Joint contract is intentionally narrower than the maximum shared contract. 32export function createRobot01(){ 33 const root=new THREE.Group();root.name=ROBOT_01.name;root.userData.robotId=ROBOT_01.id; 34 const painted=new THREE.MeshStandardMaterial({vertexColors:true,metalness:.12,roughness:.43}); 35 const metal=new THREE.MeshStandardMaterial({vertexColors:true,metalness:.8,roughness:.32}); 36 const eyesMaterial=new THREE.MeshStandardMaterial({vertexColors:true,metalness:0,roughness:.55}); 37 const put=(parent,parts,material,name)=>{const mesh=merged(parts,material,name);parent.add(mesh);return mesh;}; 38 const base=[],baseMetal=[]; 39 for(const side of [-1,1]){ 40 base.push(piece(shell(.145,.09,.23,.012,.92),C.blue,side*.099,.045,.035)); 41 base.push(piece(shell(.085,.22,.095,.009,.82),C.ivory,side*.099,.185,-.012)); 42 baseMetal.push(piece(cylinder(.039,.034,'x'),C.bronze,side*.106,.298,0)); 43 } 44 baseMetal.push(piece(shell(.21,.1,.13,.009,.72),C.bronze,0,.335,0)); 45 put(root,base,painted,'Fixed feet and legs');put(root,baseMetal,metal,'Hip joints'); 46 const torso=new THREE.Group();torso.name='Torso pivot';torso.position.y=.385;root.add(torso); 47 put(torso,[piece(shell(.30,.405,.19,.012,.70),C.ivory,0,.2075,0),piece(shell(.235,.325,.012,.005,.70),C.blue,0,.222,.102)],painted,'Ivory torso and slate chest'); 48 const chest=[];for(let i=0;i<3;i++)chest.push(piece(shell(.098-i*.008,.014,.011,.002),C.bronze,0,.278-i*.037,.116)); 49 put(torso,chest,metal,'Three bronze chest vents'); 50 const head=new THREE.Group();head.name='Head pivot';head.position.y=.445;torso.add(head); 51 put(head,[piece(cylinder(.036,.07),C.bronze,0,.017,0)],metal,'Swivel neck'); 52 put(head,[piece(shell(.36,.20,.17,.016),C.ivory,0,.120,0),piece(shell(.307,.141,.009,.008),C.dark,0,.12,.088)],painted,'Chamfered head and dark face'); 53 const eyes=new THREE.Group();eyes.name='Following eyes';head.add(eyes); 54 put(eyes,[-1,1].map(s=>piece(shell(.036,.038,.006,.003),C.ivory,s*.077,.125,.096)),eyesMaterial,'Square ivory optics'); 55 const arms=[]; 56 for(const side of [-1,1]){ 57 const arm=new THREE.Group();arm.name=side===-1?'Greeting arm':'Resting arm';arm.position.set(side*.179,.343,0);torso.add(arm);arms.push(arm); 58 const elbow=[side*.014,-.122,.004],wrist=side===-1?[side*.003,-.071,.125]:[side*.014,-.244,.025]; 59 const parts=[bar([side*.016,-.013,0],elbow,.065,.063,C.blue),bar(elbow,wrist,.073,.066,C.ivory)]; 60 const hand=new THREE.Vector3(...wrist),direction=new THREE.Vector3(...wrist).sub(new THREE.Vector3(...elbow)).normalize(),end=hand.clone().addScaledVector(direction,.064); 61 for(const dx of [-.023,.023]){const a=hand.clone().add(new THREE.Vector3(dx,0,0)),b=end.clone().add(new THREE.Vector3(dx,0,0));parts.push(bar(a.toArray(),b.toArray(),.018,.026,C.ivory));} 62 put(arm,parts,painted,side===-1?'Bent greeting arm and gripper':'Resting arm and gripper'); 63 put(arm,[piece(cylinder(.040,.050,'x'),C.bronze,side*.009,0,0),piece(cylinder(.025,.075,'x'),C.bronze,...elbow)],metal,'Shoulder and elbow
63joints'); 64 } 65 let disposed=false,time=0,greeting=-1; 66 function pose({headYaw=0,headPitch=0,torsoYaw=0,armLift=0,eyeYaw=0,eyePitch=0,tilt=-3}={}){ 67 if(disposed)return; 68 torso.rotation.y=rad(clamp(torsoYaw,-15,15));head.rotation.set(rad(clamp(headPitch,-15,15)),rad(clamp(headYaw,-50,50)),rad(clamp(tilt,-3,3)),'YXZ'); 69 arms[0].rotation.x=-rad(clamp(armLift,0,25));arms[1].rotation.x=0; 70 eyes.position.set(clamp(eyeYaw,-1,1)*.008,clamp(eyePitch,-1,1)*.006,0); 71 root.updateMatrixWorld(true); 72 } 73 function update(dt,{active=true,reducedMotion=false,target=null}={}){ 74 if(disposed)return false; 75 if(reducedMotion){greeting=-1;pose();return false;} 76 if(!active)return false; 77 time+=clamp(dt,0,.05); 78 let wave=0,nod=0; 79 if(greeting>=0){greeting+=clamp(dt,0,.05);if(greeting>=1.8*3)greeting=-1;else{const phase=greeting%1.8;wave=Math.sin(Math.PI*phase/1.8);nod=Math.sin(Math.PI*2*phase/1.8)*4;}} 80 const yaw=target?clamp(target.yaw,-50,50):Math.sin(time*.32)*5; 81 const pitch=target?clamp(target.pitch,-11,11):0; 82 pose({headYaw:yaw,headPitch:pitch+nod,armLift:wave*21,eyeYaw:yaw/50,eyePitch:-pitch/15,tilt:-3+Math.sin(time*.24)*.6});return true; 83 } 84 function greet(){if(disposed)return false;greeting=0;return true;} 85 function reset(){time=0;greeting=-1;pose();} 86 function dispose(){if(disposed)return;disposed=true;root.removeFromParent();root.traverse(o=>{if(o.isMesh)o.geometry.dispose();});for(const m of [painted,metal,eyesMaterial])m.dispose();root.clear();} 87 reset();return {root,pose,update,greet,reset,dispose,spec:ROBOT_01}; 88}
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.