1import * as THREE from '../../assets/vendor/three.module.js'; 2 3export const ROBOT_04 = Object.freeze({id:'robot-04',name:'Robot 04',height:1.07,radius:.4,reservation:1.2,maxTriangles:2500,maxDrawCalls:12,maxMaterials:4}); 4const C={ivory:0xded6c5,blue:0x4f6478,bronze:0x827354,dark:0x303a34,amber:0xe5b759}; 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// Original pedestal archivist: a bell base, slender waist, lantern head and 31// an open three-pronged greeting hand. The support never turns or travels. 32export function createRobot04(){ 33 const root=new THREE.Group();root.name=ROBOT_04.name;root.userData.robotId=ROBOT_04.id; 34 const painted=new THREE.MeshStandardMaterial({vertexColors:true,metalness:.12,roughness:.48}); 35 const metal=new THREE.MeshStandardMaterial({vertexColors:true,metalness:.76,roughness:.34}); 36 const optics=new THREE.MeshStandardMaterial({vertexColors:true,roughness:.4,emissive:0x765019,emissiveIntensity:.38}); 37 const put=(parent,parts,material,name)=>{const mesh=merged(parts,material,name);parent.add(mesh);return mesh;}; 38 put(root,[piece(new THREE.CylinderGeometry(.192,.205,.035,12),C.dark,0,.0175,0),piece(new THREE.CylinderGeometry(.082,.19,.195,12),C.ivory,0,.1325,0),piece(cylinder(.083,.055),C.blue,0,.2575,0)],painted,'Fixed bell pedestal'); 39 const baseTrim=[piece(cylinder(.191,.015),C.bronze,0,.04,0),piece(cylinder(.084,.018),C.bronze,0,.230,0)]; 40 for(const x of [-.045,0,.045])baseTrim.push(piece(shell(.013,.033,.006,.002),C.bronze,x,.119,.137)); 41 put(root,baseTrim,metal,'Pedestal trim'); 42 const torso=new THREE.Group();torso.name='Torso pivot';torso.position.y=.285;root.add(torso); 43 put(torso,[piece(cylinder(.042,.205),C.blue,0,.10,0),piece(shell(.278,.146,.172,.023,.77),C.ivory,0,.276,0),piece(shell(.091,.073,.009,.006),C.blue,0,.274,.091)],painted,'Slender waist and shoulder shell'); 44 const hardware=[piece(cylinder(.061,.028),C.bronze,0,.021,0),piece(cylinder(.063,.028),C.bronze,0,.191,0),piece(cylinder(.027,.105),C.bronze,0,.39,0)]; 45 for(const side of [-1,1])hardware.push(piece(cylinder(.029,.028,'x'),C.bronze,side*.147,.30,0)); 46 for(let i=0;i<3;i++)hardware.push(piece(new THREE.BoxGeometry(.10,.01,.01),C.bronze,0,.244+i*.028,-.089)); 47 put(torso,hardware,metal,'Waist collars, neck and rear vents'); 48 const head=new THREE.Group();head.name='Head pivot';
48head.position.y=.61;torso.add(head); 49 put(head,[piece(shell(.196,.35,.172,.022,.76),C.ivory),piece(shell(.123,.213,.012,.012,.8),C.dark,0,-.006,.092),piece(shell(.145,.042,.198,.012),C.blue,0,.16,0)],painted,'Tall lantern head'); 50 put(head,[piece(shell(.027,.224,.020,.006),C.bronze,-.081,-.009,.077),piece(shell(.027,.224,.020,.006),C.bronze,.081,-.009,.077),piece(shell(.092,.056,.015,.008),C.bronze,0,-.116,-.091)],metal,'Lantern rails and rear service plate'); 51 const eyes=new THREE.Group();eyes.name='Following optics';head.add(eyes); 52 put(eyes,[piece(shell(.026,.092,.009,.008),C.amber,-.027,.019,.106),piece(shell(.026,.059,.009,.008),C.amber,.027,.011,.106),piece(shell(.040,.009,.006,.002),C.amber,0,-.068,.104)],optics,'Asymmetric amber optics'); 53 const arms=[]; 54 for(const side of [-1,1]){ 55 const arm=new THREE.Group();arm.name=side<0?'Greeting hand pivot':'Resting hand pivot';arm.position.set(side*.164,.298,0);torso.add(arm);arms.push(arm); 56 const parts=[bar([0,0,0],[side*.027,-.108,.019],.032,.036,C.blue),piece(cylinder(.022,.043,'x'),C.bronze,side*.027,-.108,.019),bar([side*.027,-.108,.019],[side*.033,-.184,.076],.028,.031,C.bronze),piece(shell(.060,.041,.037,.006),C.blue,side*.033,-.193,.086)]; 57 for(const finger of [-1,0,1])parts.push(piece(shell(.011,.043,.018,.003),C.ivory,side*.033+finger*.021,-.226,.094)); 58 put(arm,parts,painted,side<0?'Three-pronged greeting hand':'Resting three-pronged hand'); 59 } 60 let disposed=false,time=0,greeting=-1; 61 function pose({headYaw=0,headPitch=0,torsoYaw=0,armLift=0,eyeYaw=0,eyePitch=0,tilt=0}={}){ 62 if(disposed)return; 63 torso.rotation.y=rad(clamp(torsoYaw,-15,15));head.rotation.set(rad(clamp(headPitch,-12,12)),rad(clamp(headYaw,-45,45)),rad(clamp(tilt,-2,2)),'YXZ'); 64 arms[0].rotation.x=-rad(clamp(armLift,0,32));arms[1].rotation.x=-rad(clamp(armLift,0,32)*.12); 65 eyes.position.set(clamp(eyeYaw,-1,1)*.004,clamp(eyePitch,-1,1)*.004,0);root.updateMatrixWorld(true); 66 } 67 function update(dt,{active=true,reducedMotion=false,target=null}={}){ 68 if(disposed)return false;if(reducedMotion){greeting=-1;pose();return false;}if(!active)return false; 69 const step=clamp(dt,0,.05);time+=step;let wave=0,nod=0; 70 if(greeting>=0){greeting+=step;if(greeting>=1.8*3)greeting=-1;else{const phase=greeting%1.8;wave=Math.sin(Math.PI*phase/1.8);nod=Math.sin(2*Math.PI*phase/1.8)*4;}} 71 const yaw=target?clamp(target.yaw,-45,45):Math.sin(time*.23)*6,pitch=target?clamp(target.pitch,-7,7):0; 72 pose({headYaw:yaw,headPitch:pitch+nod,armLift:wave*32,eyeYaw:yaw/45,eyePitch:-pitch/12,tilt:Math.sin(time*.17)*.7});return true; 73 } 74 function greet(){if(disposed)return false;greeting=0;return true;} 75 function reset(){time=0;greeting=-1;pose();} 76 function dispose(){if(disposed)return;disposed=true;root.removeFromParent();root.traverse(o=>{if(o.isMesh)o.geometry.dispose();});[painted,metal,optics].forEach(m=>m.dispose());root.clear();} 77 reset();return {root,pose,update,greet,reset,dispose,spec:ROBOT_04}; 78}
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.