let reacts=[],tr=[],lt=0,testcraft,rnd,rd2,r=[0,0],fr=0,fpv=0,grTex,glContext,anchor,testMode=0,lxz=300,
socket=io('https://echoserver.lrCr.repl.co'),players=[]
createCanvas(Math.min(windowWidth-10,800),Math.min(windowHeight-10,550))
grTex=createGraphics(300,300)
grTex.fill(196,120,57);grTex.noStroke()
grTex.circle(Math.floor(Math.random()*300),Math.floor(Math.random()*300),10)
rnd=createGraphics(width,height,WEBGL)
rd2=createGraphics(1,3,WEBGL)
glContext=rnd._renderer.GL
rnd.frustum(-Math.min(width,height)/height/10,Math.min(width,height)/height/10,Math.min(width,height)/width/10,-Math.min(width,height)/width/10,0.1,1e5)
rd2.frustum(-Math.min(width,height)/height/10,Math.min(width,height)/height/10,Math.min(width,height)/width/10,-Math.min(width,height)/width/10,0.1,1e5)
reactive:{anchor:{p:[60,-30,60]}}
let fix=[[-0.5,-0.5,-0.5],[0.5,-0.5,-0.5],[-0.5,0.5,-0.5],[0.5,0.5,-0.5],[-0.5,-0.5,0.5],[0.5,-0.5,0.5],[-0.5,0.5,0.5],[0.5,0.5,0.5],[-3.5,0,-0.5],[-2.5,0,-0.5],[-4.1,0.6,-0.8],
edg=[[0,1],[2,3],[4,5],[6,7],[0,2],[1,3],[4,6],[5,7],[0,4],[1,5],[2,6],[3,7],[8,0],[8,4],[8,2],[8,6],[8,9],[8,10],[8,11],[9,10],[9,11]]
for(let i=0;i<fix.length;i++){testcraft.addFixture(fix[i])}
for(let i=0;i<edg.length;i++){testcraft.addEdge(...edg[i],18,'#BBB')}
testcraft.addJoint([0,0,0],[0,0,1],-1)
testcraft.addJoint([0.3,-1.4,0],[0,1,0],0)
testcraft.addJoint([0.3,1.4,0],[0,1,0],0)
testcraft.addJoint([-3.5,0,0.5],[0,0,1],-1)
testcraft.addSpring([0,0,0],[0,0,-1],3)
testcraft.addJoint([0,0,0],[0,1,0],4)
for(let i=0;i<wl_vrts;i++){
testcraft.addFixture([Math.cos(i/wl_vrts*2*Math.PI)*0.9,-0.7,Math.sin(i/wl_vrts*2*Math.PI)*0.9],{grip:0.7,rebound:0.04},1)
testcraft.addEdge(fix.length+i,fix.length+(i+1)%wl_vrts)
for(let i=0;i<wl_vrts;i++){
testcraft.addFixture([Math.cos(i/wl_vrts*2*Math.PI)*0.9,0.7,Math.sin(i/wl_vrts*2*Math.PI)*0.9],{grip:0.7,rebound:0.04},2)
testcraft.addEdge(fix.length+wl_vrts+i,fix.length+wl_vrts+(i+1)%wl_vrts)
for(let i=0;i<wl_vrts;i++){
testcraft.addFixture([Math.cos(i/wl_vrts*2*Math.PI)*0.9,0.9,Math.sin(i/wl_vrts*2*Math.PI)*0.9],{grip:0.7,rebound:0.04},5)
testcraft.addEdge(fix.length+wl_vrts*2+i,fix.length+wl_vrts*2+(i+1)%wl_vrts)
for(let i=0;i<wl_vrts;i++){
testcraft.addFixture([Math.cos(i/wl_vrts*2*Math.PI)*0.9,-0.9,Math.sin(i/wl_vrts*2*Math.PI)*0.9],{grip:0.7,rebound:0.04},5)
testcraft.addEdge(fix.length+wl_vrts*3+i,fix.length+wl_vrts*3+(i+1)%wl_vrts)
testcraft.addFixture([0,0.2,1],{},4)
testcraft.addFixture([0,-0.2,1],{},4)
testcraft.addFixture([0,0.9,0],{},4)
testcraft.addFixture([0,-0.9,0],{},4)
testcraft.addEdge(fix.length+wl_vrts*4+0,fix.length+wl_vrts*4+1,18,'#BBB')
testcraft.addEdge(fix.length+wl_vrts*4+0,fix.length+wl_vrts*4+2,18,'#BBB')
testcraft.addEdge(fix.length+wl_vrts*4+1,fix.length+wl_vrts*4+3,18,'#BBB')
testcraft.addFixture([-0.3,1.1,0],{},0)
testcraft.addFixture([-0.3,-1.1,0],{},0)
testcraft.addFixture([0.1,1.8,0],{},0)
testcraft.addFixture([0.1,-1.8,0],{},0)
testcraft.addEdge(fix.length+wl_vrts*4+4,fix.length+wl_vrts*4+5,18,'#BBB')
testcraft.addEdge(fix.length+wl_vrts*4+4,fix.length+wl_vrts*4+6,18,'#BBB')
testcraft.addEdge(fix.length+wl_vrts*4+5,fix.length+wl_vrts*4+7,18,'#BBB')
r.push({p:[fix.length+i*wl_vrts/6,fix.length+((i+1)%6)*wl_vrts/6],w:1,c:'#FF0'})
r.push({p:[fix.length+i*wl_vrts/6+wl_vrts,fix.length+((i+1)%6)*wl_vrts/6+wl_vrts],w:1,c:'#FF0'})
r.push({p:[fix.length+i*wl_vrts/6+wl_vrts*2,fix.length+((i+1)%6)*wl_vrts/6+wl_vrts*2],w:1,c:'#FF0'})
r.push({p:[fix.length+i*wl_vrts/6+wl_vrts*3,fix.length+((i+1)%6)*wl_vrts/6+wl_vrts*3],w:1,c:'#FF0'})
testcraft.edges=[...testcraft.edges.slice(0,testcraft.edges.length-4*wl_vrts-6),...r,...testcraft.edges.slice(testcraft.edges.length-6,testcraft.edges.length)]
testcraft.reactive.anchor.a=[[0,-1,0],[1,0,0]]
socket.emit('init','3dcar')
if(e.type[0]!='3dcar')return
if(e.id==socket.id)return
let a=players.filter(x=>x.id==e.id)
a[0].a=e2.a||[[0,0,1],[0,1,0]]
if(mouseButton==LEFT&&mouseIsPressed){
r[0]-=(mouseX-pmouseX)/200
r[1]=Math.max(-Math.PI/2,Math.min(Math.PI/2,r[1]+(mouseY-pmouseY)/200))
lt=(lt+reacts.length)%reacts.length
anchor=reacts[lt].reactive
testcraft.applyJointForce(1,[1,0,0],[0,0,-6])
testcraft.applyJointForce(2,[1,0,0],[0,0,-6])
testcraft.applyJointForce(5,[1,0,0],[0,0,-6])
testcraft.turnJoint(0,[1,-0.5,0],[1,0,0],10,200)
testcraft.turnJoint(3,[1,2,0],[1,0,0],10,200)
testcraft.turnJoint(0,[1,0.5,0],[1,0,0],10,200)
testcraft.turnJoint(3,[1,-2,0],[1,0,0],10,200)
testcraft.turnJoint(0,[1,0,0],[1,0,0],10,200)
testcraft.turnJoint(3,[1,0,0],[1,0,0],10,200)
testcraft.slideSpring(4,1,500,250)
testcraft.slideSpring(4,0,500,250)
testcraft.applyJointForce(1,[1,0,0],[0,0,3])
testcraft.applyJointForce(2,[1,0,0],[0,0,3])
testcraft.applyJointForce(5,[1,0,0],[0,0,3])
testcraft.applyJointForce(1,[1,0,0],vmultf(testcraft.joints[1].a.r[0],50))
testcraft.applyJointForce(1,[0,1,0],vmultf(testcraft.joints[1].a.r[1],-50))
testcraft.applyJointForce(2,[1,0,0],vmultf(testcraft.joints[2].a.r[0],50))
testcraft.applyJointForce(2,[0,1,0],vmultf(testcraft.joints[2].a.r[1],-50))
testcraft.applyJointForce(5,[1,0,0],vmultf(testcraft.joints[5].a.r[0],50))
testcraft.applyJointForce(5,[0,1,0],vmultf(testcraft.joints[5].a.r[1],-50))
if(keyIsDown(32))anchor.applyForce([0,0,0],anchor.absolNormal([0,-9,0]))
if(keyIsDown(16))anchor.applyForce([0,0,0],anchor.absolNormal([0,5,0]))
if(keyIsDown(LEFT_ARROW))anchor.applyForce([0,0,0],anchor.absolNormal([-5*Math.cos(-r[0]),0,-5*Math.sin(-r[0])]))
if(keyIsDown(RIGHT_ARROW))anchor.applyForce([0,0,0],anchor.absolNormal([5*Math.cos(-r[0]),0,5*Math.sin(-r[0])]))
if(keyIsDown(UP_ARROW))anchor.applyForce([0,0,0],anchor.absolNormal([5*Math.cos(-r[0]-Math.PI/2),0,5*Math.sin(-r[0]-Math.PI/2)]))
if(keyIsDown(DOWN_ARROW))anchor.applyForce([0,0,0],anchor.absolNormal([-5*Math.cos(-r[0]-Math.PI/2),0,-5*Math.sin(-r[0]-Math.PI/2)]))
let xz=Math.min(300,lxz+20),rt=50,sp=10
if(fpv)rd2.camera(0,0,0,...anchor.gridNormal([-Math.sin(r[1]),Math.sin(r[0]),Math.cos(r[0])]),...anchor.gridNormal([-1,0,0]))
else rd2.camera(xz*Math.sin(r[0])*Math.cos(r[1]),-Math.sin(r[1])*xz,xz*Math.cos(r[0])*Math.cos(r[1]),0,0,0,0,1,0)
rd2.fill(255,255,255);rd2.stroke(255,255,255);rd2.strokeWeight(10000)
glContext.clear(glContext.DEPTH_BUFFER_BIT)
rd2.translate(...vmultf(anchor.anchor.p,-30))
rd2.fill(255);rd2.stroke(0)
rd2.directionalLight(255,255,255,0.3,-1,0)
if(i<rt-1&&rd2.get(0,2)[2]<170){
if(!fpv)rnd.camera(xz*Math.sin(r[0])*Math.cos(r[1]),-Math.sin(r[1])*xz,xz*Math.cos(r[0])*Math.cos(r[1]),0,0,0,0,1,0)
rnd.fill(130,176,250);rnd.noStroke()
glContext.clear(glContext.DEPTH_BUFFER_BIT)
if(fpv)rnd.camera(0,0,0,...anchor.gridNormal([-Math.sin(r[1]),Math.sin(r[0]),Math.cos(r[0])]),...anchor.gridNormal([-1,0,0]))
rnd.translate(...vmultf(anchor.anchor.p,-30))
rnd.fill(255);rnd.stroke(0)
for(let i=0;i<players.length;i++){
if(players[i].p)rnd.strokeWeight(5/(1+vmag(vadd(testcraft.reactive.anchor.p,vmultf(players[i].p,-1)))/30))
for(let f=0;f<players[i].edges.length;f++){
rnd.stroke(players[i].edges[f].c)
let a=[...players[i].p],b=[...players[i].p],c=[...players[i].a,vcross(...players[i].a)]
for(let m=0;m<3;m++){for(let h=0;h<3;h++){a[h]+=c[m][h]*players[i].fix[players[i].edges[f].p[0]][m];b[h]+=c[m][h]*players[i].fix[players[i].edges[f].p[1]][m]}}
players=players.filter(x=>x.m)
for(let i=0;i<reacts.length;i++){
rnd.line(...reacts[i].reactive.anchor.p,...vadd(reacts[i].reactive.anchor.p,reacts[i].reactive.anchor.a[0]))
rnd.line(...reacts[i].reactive.anchor.p,...vadd(reacts[i].reactive.anchor.p,reacts[i].reactive.anchor.a[1]))
rnd.line(...reacts[i].reactive.anchor.p,...vadd(reacts[i].reactive.anchor.p,vcross(...reacts[i].reactive.anchor.a)))
for(let f=0;f<reacts[i].edges.length;f++){
rnd.stroke(reacts[i].edges[f].c)
rnd.line(...reacts[i].reactive.fromGrid(reacts[i].reactive.fix[reacts[i].edges[f].p[0]]),...reacts[i].reactive.fromGrid(reacts[i].reactive.fix[reacts[i].edges[f].p[1]]))
for(let f=0;f<reacts[i].reactive.fix.length;f++){
for(let f=0;f<reacts[i].joints.length;f++){
rnd.line(...reacts[i].reactive.fromGrid(vadd(reacts[i].p_jt[0][f],reacts[i].p_jt[2])),
...reacts[i].reactive.fromGrid(vadd(vadd(reacts[i].p_jt[0][f],reacts[i].p_jt[1][f][1]),reacts[i].p_jt[2])))
for(let f=0;f<reacts[i].joints.length;f++){
if(([4]).includes(f)||1){
let axis=[0,0,0],b=[...reacts[i].p_jt[1][f],vcross(...reacts[i].p_jt[1][f])]
for(let h=0;h<3;h++){for(let m=0;m<3;m++){axis[m]+=b[h][m]*reacts[i].joints[f].axis[h]}}
rnd.line(...reacts[i].reactive.fromGrid(vadd(reacts[i].p_jt[0][f],reacts[i].p_jt[2])),
...reacts[i].reactive.fromGrid(vadd(vadd(reacts[i].p_jt[0][f],axis),reacts[i].p_jt[2])))
rnd.stroke(0);rnd.strokeWeight(2)
if(testMode&&!fpv)rnd.line(...reacts[i].reactive.anchor.p,...vadd(reacts[i].reactive.anchor.p,vmultf(reacts[i].reactive.anchor.v,100)))
reacts[i].applyForce([0,0,0],reacts[i].reactive.absolNormal([0,8,0]))
reacts[i].collide([0,0,0],[[0,0,1],[-1,0,0]])
reacts[i].collide([0,0,0],[[1,0,0],[0,0,-1]])
reacts[i].collide([ns.dm[0]*ns.sc-ns.sc*1,0,ns.dm[0]*ns.sc-ns.sc*1],[[0,0,1],[1,0,0]])
reacts[i].collide([ns.dm[0]*ns.sc-ns.sc*1,0,ns.dm[0]*ns.sc-ns.sc*1],[[1,0,0],[0,0,1]])
reacts[i].collide([0,10,0],[[0,0,1],[0,1,0]])
let s=[Math.max(0,Math.min(ns.dm[0]-1,Math.floor(p[0]/ns.sc))),Math.max(0,Math.min(ns.dm[1]-1,Math.floor(p[2]/ns.sc)))]
if((p[0]/ns.sc+p[2]/ns.sc-Math.floor(p[0]/ns.sc)-Math.floor(p[2]/ns.sc))>1){
if((p[2]/ns.sc+Math.ceil(p[0]/ns.sc)-p[0]/ns.sc-Math.floor(p[2]/ns.sc))>1){
if((p[2]/ns.sc+Math.ceil(p[0]/ns.sc)-p[0]/ns.sc-Math.floor(p[2]/ns.sc))>1){
return {p:[0,20,0],d:[[0,0,1],[0,1,0]]}
let x=reacts[i].joints.length
reacts[i].applyJointForce(y,[1,0,0],vmultf(reacts[i].joints[y].a.r[0],10))
reacts[i].applyJointForce(y,[0,1,0],vmultf(reacts[i].joints[y].a.r[1],-10))
socket.emit('data',{id:socket.id,type:['3dcar','pos'],m:150,t:frameCount,
d:JSON.stringify({edges:testcraft.edges,fix:testcraft.reactive.fix,p:testcraft.reactive.anchor.p,a:testcraft.reactive.anchor.a})})
image(rnd,0,0,width,height)
if(frameCount%10==0)fr=Math.floor(frameRate())
text('FPS: '+fr.toString(),10,10)
text(players.length.toString()+' other player'+(players.length==1?'':'s')+' online',10,30)
text('Controls:\nWASD for movement, X to flip when upside down\nMouse to rotate camera, Q to toggle seat view\nArrow keys + Space to fly',width/2,10)
let a=anchor.gridNormal([Math.sin(r[1]),-Math.sin(r[0]),-Math.cos(r[0])])
r=[Math.atan2(a[0],a[2]),Math.atan2(a[1],Math.sqrt(a[0]*a[0]+a[2]*a[2]))]