ÎÌÌÌ̈ˆˆ¬ªªª¬ˆˆˆ¬ÌÌ̬ÌÀ̬ÌÀ̬ÌÀÌÌÌìÌÀÀî̬ÀÌÀ¬ÀÀÀ¬ÀÀÀ¬ÀÀÎÌÌÌ̈ˆˆ¬ªªª¬ˆˆˆ¬ÌÌ̬ÌÌ̬ÌÀ̬ÌÀÌÌÌìÌÀÀî̬ÀÌ̬ÀÀÀ¬ÀÀÀ¬ÀÀ¬ÌÌ̬ªªª¬ª¬ª¬ªÊ̬ªªªŒˆˆˆÌÀÌÎÌÌÎ̬̪ªÀ쬪ÀÀÀÌîÀìîÌÌîî¬ÌÌ̬ªªª¬ª¬ª¬ªÊ̬ªªªŒˆˆˆÌÀÌÎÌÌÎ̬̪ªÀ쬪ÀÀÀÌîÀìîÌÌîî B+=€0000000000000000000000000000Œ ™?ÀÌÌÌìÌÀÌÌîîÎÌÀìîîîîÎì Ìîîîîîìì ÀìîîîîÎîìÎììîîîîÎîìÎììîîîîìîìÎÀîìîîîÎîîìî ìîìîîîÎîîìî ìîìîîîìîîìîÎÀîîìîîÎîîîìîÎìîîìîîÎîîîìîÎìîîìîîìîîîìîî Àîîîìîîìîîîìîî ìîîîìîÎîîîîìîîÎÀîîîîìîìîîîîìîîÎÀîîîîìîìîîîîìîîÎìîîîÎîÎîîîîîìîîî ÀîîîîÎîìîîîîîìîîî ÀîîîîÎîìîîîîîìîîîÎìîîîîÎÎîîîîîÎîîîîÎÀîîîîîÎìîîîîîÎîîîîÎìîîîîîÎìîîîîîÎîîîîî ÀÌÌÌÌÌÌÌîîîîîîÎîîîîî ìîîîîîÎìîîîîîÎîîîîî ÀîîîîîÎìîîîîîÎîîîîÎÀîîîîîÎÎîîîîîÎîîîîÎìîîîîÎÎîîîîîÎîîîî ÀîîîîÎîìîîîîÎîîîî ÀîîîîÎîìîîîîÎîîîÎìîîîÎîÎîîîîÎîîî ÀîîîÎîîìîîîÎîîî ÀîîîÎîîìîîîÎîîÎìîîÎîîÎîîîÎîîÎÀîîÎîîÎîîîÎîî ìîÎîîîìîîÎîî ìîÎîîîÎîîÎîÎÀîÎîîîÎîîÎîÎÀÌìÎîîîîìîÎî ÀÌÌÌììÌÌÌìÎîîîîìîÎî ÀÌÌÌìîîîîìîîîÌÌÌÌîîîîÎîìÎÀÌÌÌìîîîîîîîîìîîîîîîÎÌÌÌîÎîìÎÌÌîîîîîîîîîîîìîîîîîîîîîîÌÌÌì ÌîÌÌîîîîîîîîîìîîîîîîîîîîÌÌÌì ÌîîîÌÌîîîîîîîìîîîîîîîÎÌÌîîÎÌììîîîîÌÌîîîîîìîîîîîÌÌìîîîîÎÌììîîîîîîÌÌîîîìîîîÌÌîîîîîîîÎ ìÎîîîîîîîîÌÌîìÎÌÌîîîîîîîîîÎìÎîîîîîîîîîîÌÌìîîîîîîîîîîîÎìîìîîîîîîîîîîÌîîîîîîîîîîîîÎìîìîîîîîîîîîîììîîîîîîîîîîîÎìîÎîîîîîîîîîîìÎîîîîîîîîîîî ÀîÎîîîîîîîîîîìÎîîîîîîîîîîî Àîîìîîîîîîîîîìîìîîîîîîîîîî ÀîîìîîîîîîîîîìîÎîîîîîîîîîî ÀîîÎîîîîîîîîîìîîìîîîîîîîîî ÀîîÎîîîîîîîîîìîîÎîîîîîîîîî Àîîîìîîîîîîîîìîîîìîîîîîîîî Àîîîìîîîîîîîîìîîîìîîîîîîîî ÀîîîÎîîîîîîîîìîîîÎîîîîîîîî ÀîîîÎîîîîîîîîìîîîîìîîîîîîî ÀîîîîìîîîîîîîìîîîîÎîîîîîîî Àîîîîìîîîîîîîìîîîîîìîîîîîî ÀîîîîÎîîîîîîîìîîîîîÎîîîîîî ÀîîîîÎîîîîîîîìîîîîîîìîîîîî Àîîîîîìîîîîîîìîîîîîîìîîîîî ÀîîîîîìîîîîîîìîîîîîîÎîîîîî ÀîîîîîÎîîîîîîìîîîîîîîìîîîî ÀîîîîîÎîîîîîîìîîîîîîîÎîîîî Àîîîîîîìîîîîîìîîîîîîîîìîîî ÀîîîîîîìîîîîîìîîîîîîîîÎîîî ÀîîîîîîÎîîîîîìîîîîîîîîÎîîî ÀîîîîîîÎîîîîîìîîîîîîîîîìîÎÀîîîîîîÎîîîîîìîîîîîîîîîÎîÎÀîîîîîîîìîîîîìîîîîîîîîîîìÎÀîîîîîîîìîîîîìîîîîîîîîîîÎÎÀìîîîîîîÎîîîîìîîîîîîîîîîîÌÀîîîîîîÎîîîîìîîîîîîîîîîîÎìîîîîîîìîîîìîîîîîîîîîîîÌÀìîîîîîìîîîìîîîîîîîîîîÌÀîîîîîÎîîîìîîîîîîîîîÎÌîîîîÎîîîìîîîîîîîîÎ ìîîîîìîîìîîîîîîîÎ Àîîîîìîîìîîîîîîî ÌîîîÎîîìîîîîîîÌìîîÎîîìîîîîîÌÀìîîìîìîîîîÎÀÌ ÀîîìîìîîîÎ ÀÌ ÌîÎîìîîÎ ÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌ ÀÌ Àîììî ÀÌ ÌììÌÌÌÀÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌ ÀÌ ÀÌ ÀÌ ÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌ UC-- title: Camera Test -- author: 0x30507DE -- desc: A 3D rendering test cart -- site: 0x30507de.xyz -- license: MIT License (change this to your license of choice) -- version: 0.4 -- script: lua ---------------------------------------------------------- -- Vector stuff ---------------------------------------------------------- vec3={x=0,y=0,z=0} vec3.__index=vec3 function vec3:new(x,y,z) return setmetatable({x=x,y=y,z=z}, self) end function vec3:len2() return self.x^2 + self.y^2 + self.z^2 end function vec3:slen() return self.x+self.y+self.z end function vec3:__len() return math.sqrt(self:len2()) end function vec3:__add(other) return vec3:new(self.x+other.x, self.y+other.y, self.z+other.z) end function vec3:__sub(other) return vec3:new(self.x-other.x, self.y-other.y, self.z-other.z) end function vec3:__mul(scl) if type(scl)=="number" then return vec3:new(self.x*scl,self.y*scl,self.z*scl) else return vec3:new(self.x*scl.x,self.y*scl.y,self.z*scl.z) end end function vec3:__div(scl) if type(scl)=="number" then return vec3:new(self.x/scl,self.y/scl,self.z/scl) else return vec3:new(self.x/scl.x,self.y/scl.y,self.z/scl.z) end end function vec3:__unm() return vec3:new(-self.x,-self.y,-self.z) end function vec3:__tostring() return string.format( "vec3(%7.4f, %7.4f, %7.4f)", self.x, self.y, self.z ) end function vec3:dot(other) return self.x*other.x + self.y*other.y + self.z*other.z end function vec3:cross(other) return vec3:new( (self.y*other.z)-(self.z*other.y), (self.z*other.x)-(self.x*other.z), (self.x*other.y)-(self.y*other.x) ) end function vec3:rotate(xt,yt,zt,origin) if origin == nil then origin = vec3:new() end local v = self - origin local cxt=math.cos(xt) local sxt=math.sin(xt) local cyt=math.cos(yt) local syt=math.sin(yt) local czt=math.cos(zt) local szt=math.sin(zt) return vec3:new( v.x*(czt*cyt)+v.y*((czt*syt*sxt)-(szt*cxt))+v.z*((czt*syt*cxt)+(szt*sxt)), v.x*(szt*cyt)+v.y*((szt*syt*sxt)+(czt*cxt))+v.z*((szt*syt*cxt)-(czt*sxt)), v.x*(-syt)+v.y*(cyt*sxt)+v.z*(cyt*cxt) ) end function vec3:normalize() if #self == 0 then return vec3:new(0,0,0) else return self/#self end end function vec3:ortho() local X_AXIS=vec3:new(1,0,0) local Y_AXIS=vec3:new(0,1,0) local Z_AXIS=vec3:new(0,0,1) local x=math.abs(self.x) local y=math.abs(self.y) local z=math.abs(self.z) local o if x < y then if x < z then o=X_AXIS else o=Z_AXIS end else if y < z then o=Y_AXIS else o=Z_AXIS end end return self:cross(o) end vec3.FRONT=vec3:new(0,0,1) vec3.RIGHT=vec3:new(1,0,0) vec3.DOWN=vec3:new(0,1,0) vec3.UP=-vec3.DOWN vec3.LEFT=-vec3.RIGHT vec3.BACK=-vec3.FRONT vec3.ZERO=vec3:new() vec2={x=0,y=0} vec2.__index=vec2 function vec2:new(x,y) return setmetatable({x=x,y=y}, self) end function vec2:len2() return self.x^2 + self.y^2 end function vec2:__len() return math.sqrt(self:len2()) end function vec2:__add(other) return vec2:new(self.x+other.x, self.y+other.y) end function vec2:__sub(other) return vec2:new(self.x-other.x, self.y-other.y) end function vec2:__mul(scl) if type(scl)=="number" then return vec2:new(self.x*scl,self.y*scl) else return vec2:new(self.x*scl.x,self.y*scl.y) end end function vec2:__div(scl) if type(scl)=="number" then return vec2:new(self.x/scl,self.y/scl) else return vec2:new(self.x/scl.x,self.y/scl.y) end end function vec2:__unm() return vec2:new(-self.x,-self.y) end function vec2:__tostring() return string.format( "vec2(%7.4f, %7.4f)", self.x, self.y ) end function vec2:dot(other) return self.x*other.x + self.y*other.y end function vec2:rotate(xt,yt,origin) if origin == nil then origin = vec2:new() end local v = self - origin return vec2:new( math.cos(v.x)-math.sin(v.y), math.sin(v.x)+math.cos(v.y) ) end function vec2:normalize() if #self == 0 then return vec2:new(0,0) else return self/#self end end function vec2:angle() return math.atan(self.y,self.x) end quat={x=0,y=0,z=0,w=1} quat.__index=quat function quat:new(x,y,z,w) return setmetatable({x=x,y=y,z=z,w=w},self) end function quat:__add(other) return quat:new( self.x+other.x, self.y+other.y, self.z+other.z, self.w+other.w ) end function quat:__sub(other) return quat:new( self.x-other.x, self.y-other.y, self.z-other.z, self.w-other.w ) end function quat:__mul(other) if type(other) == "number" then return quat:new( self.x*other, self.y*other, self.z*other, self.w*other ) else local v1=vec3:new(self.x,self.y,self.z) local w1=self.w local v2=vec3:new(other.x,other.y,other.z) local w2=other.w local vp=v2*w1+v1*w2+v1:cross(v2) local wp=w1*w2-v1:dot(v2) return quat:new(vp.x,vp.y,vp.z,wp) end end function quat:__div(other) return quat:new( self.x/other, self.y/other, self.z/other, self.w/other ) end function quat:__tostring() return string.format( "quat(%7.4f, %7.4f, %7.4f, %7.4f)", self.x, self.y, self.z, self.w ) end function quat:conjugate() return quat:new(-self.x,-self.y,-self.z,self.w) end function quat:len2() return self.x^2+self.y^2+self.z^2+self.w^2 end function quat:__len() return math.sqrt(self:len2()) end function quat:normalize() return self/#self end function quat:__unm() return self:conjugate()/self:len2() end function quat:fromAngleAxis(angle, axis) local a=axis:normalize()*math.sin(angle/2) return quat:new(a.x,a.y,a.z,math.cos(angle/2)) end function quat:toAngleAxis() local a=math.acos(self.w) local v=vec3:new(self.x,self.y,self.z):normalize() return a*2,v end function quat:rotateVec3(other) --local q=quat:new(other.x,other.y,other.z,0) --local rq=self*q*self:conjugate() --return vec3:new(rq.x,rq.y,rq.z) local q=quat:new(other.x,other.y,other.z,0) local r=self*q*self:conjugate() return vec3:new(r.x,r.y,r.z) end xform={ scl=vec3:new(1,1,1), pos=vec3:new(), rot=quat:new() } xform.__index=xform function xform:new(pos,rot,scl) return setmetatable({pos=pos,rot=rot,scl=scl},self) end function xform:rotate(r,o) local vp=self.pos-o vp=r:rotateVec3(vp) return xform:new(vp,self.rot*r,self.scl) end function xform:translate(v) return xform:new( self.pos+v, self.rot, self.scl ) end function xform:translateLocal(v) local rv=self.rot:rotateVec3(v) return xform:new( self.pos+rv, self.rot, self.scl ) end function xform:vec3ToWorld(v) local vs=v*self.scl local vr=self.rot:rotateVec3(vs) return vr+self.pos end function xform:vec3FromWorld(v) local vr=v-self.pos local vs=(-self.rot):rotateVec3(vr) return vs/self.scl end function xform:faceVec3(v,u) if u == nil then u=vec3.UP end u=u:normalize() local target=(v-self.pos):normalize() local yaw=vec2:new(target.z,target.x) local tyaw=-yaw:angle() local pitch=vec2:new(#yaw,target.y) local tpitch=-pitch:angle() local q1=quat:fromAngleAxis(tyaw,vec3.UP) local paxis=target:cross(u):normalize() local q2=quat:fromAngleAxis(tpitch,paxis) local troll=math.acos( vec3.UP:dot(u)/(#vec3.UP*#u) ) local q3=quat:fromAngleAxis(troll,target) local qr=(q3*q2*q1):normalize() return xform:new(self.pos,qr,self.scl) end function xform:faceXform(t) return self:faceVec3(t.pos) end function xform:__tostring() return string.format( "xform(%s,\n%s,\n%s)", self.pos, self.rot, self.scl ) end function xform:__lt(o) return self.pos=0 then t1=t[i] t2=t[(i%3)+1] t3=t[((i+1)%3)+1] td1=td[i] td2=td[(i%3)+1] td3=td[((i+1)%3)+1] break end end -- Offset to center t2=t2-t1 t3=t3-t1 -- Clip the vertices td2=math.abs(td2) td3=math.abs(td3) t2=t2:normalize()*#t2*(1-(td2/(td1+td2))) t3=t3:normalize()*#t3*(1-(td3/(td1+td3))) -- And revert the offset t2=t2+t1 t3=t3+t1 return true,{t1,t2,t3} else -- Only one vertex is out of bounds local t1,t2,t3 local td1,td2,td3 for i=1,#t do if td[i]<0 then t2=t[i] t3=t[(i%3)+1] t1=t[((i+1)%3)+1] td2=td[i] td3=td[(i%3)+1] td1=td[((i+1)%3)+1] break end end -- Offset to the clipped point local t4=t1-t2 local t5=t3-t2 td2=math.abs(td2) -- Clip the vertices t4=t4:normalize()*#t4*(1-(td1/(td1+td2))) t5=t5:normalize()*#t5*(1-(td3/(td3+td2))) -- And revert the offset t4=t4+t2 t5=t5+t2 return true,{t1,t4,t3,t3,t4,t5} end end function clipTri(ts,i) local ct={} local cplanes=getCplanes() local c if i==nil then i=1 end while i<=#cplanes do local p=cplanes[i] i=i+1 local cts c,cts=clipIndTri(ts,p) if c then if cts==nil then return nil elseif #cts==6 then local t1,t2,t3,t4,t5,t6=table.unpack(cts) local ct2=clipTri({t1,t2,t3},i) local ct3=clipTri({t4,t5,t6},i) if ct2~=nil then for k=1,#ct2 do table.insert(ct,ct2[k]) end end if ct3~=nil then for k=1,#ct3 do table.insert(ct,ct3[k]) end end else local t1,t2,t3=table.unpack(cts) local ct2=clipTri({t1,t2,t3},i) if ct2~=nil then for k=1,#ct2 do table.insert(ct,ct2[k]) end end end break end end if #ct==0 and not c then return {ts} else return ct end end function clipObj(v,r) -- Clip against the camera planes local c=true local cplanes=getCplanes() for i,p in pairs(cplanes) do local d=signDist(p,v) if d > r then c=c and true elseif d < -r then c=c and nil else c=c and false end end return c end function renderObject(object) local cplanes=getCplanes() --trace("Starting up...") local mesh=meshes[object.mesh] local oxf=object.xform local cxf=camera local oc=camera:vec3FromWorld(oxf.pos) local ocl=clipObj(oc,2) if ocl ~= nil then --trace("Object transform:") --trace(oxf) local cmesh={} local ins=table.insert local upk=table.unpack for i,t in pairs(mesh) do -- Get a clipped and translated mesh local t1,t2,t3=upk(t) local wt1=oxf:vec3ToWorld(t1) local wt2=oxf:vec3ToWorld(t2) local wt3=oxf:vec3ToWorld(t3) local ct1=cxf:vec3FromWorld(wt1) local ct2=cxf:vec3FromWorld(wt2) local ct3=cxf:vec3FromWorld(wt3) local draw=true -- back-face culling if not ocl then local cts=clipTri({ct1,ct2,ct3}) if cts~=nil then for i=1,#cts do table.insert(cmesh,cts[i]) end end else table.insert(cmesh,{ct1,ct2,ct3}) end end for i,t in pairs(cmesh) do local ct1,ct2,ct3=upk(t) local ctn=(ct2-ct1):cross(ct3-ct1) if ctn:dot(ct1) < 0 then local pt1=project(ct1)*40+scnctr --trace(pt1) local pt2=project(ct2)*40+scnctr local pt3=project(ct3)*40+scnctr tri(pt1.x,pt1.y,pt2.x,pt2.y,pt3.x,pt3.y,14) trib(pt1.x,pt1.y,pt2.x,pt2.y,pt3.x,pt3.y,12) end end end end function BOOT() decodeOBJ(meshString) local xf=objects[2].xform xf.rot=quat:fromAngleAxis(math.pi/4,vec3.UP) xf.rot=xf.rot*quat:fromAngleAxis( math.pi/4, vec3.RIGHT ) objects[2].xform=xf end pitch=-12.5 maxp=0 minp=-20 yaw=45 maxy=15 miny=80 zoom=50 maxz=15 minz=50 zslow=0.15 zfast=0.45 speed=0.45 sfxp=false function renderHUD() local y=pitch/minp y=(y*9)+125 local z=(zoom-maxz)/(minz-maxz) z=((1-z)*79)+80 local x=(yaw-maxy)/(miny-maxy) x=((1-x)*79)+80 line(80,118,158,118,12) rectb(78,123,83,13,12) rect(x-1,y-1,3,3,12) rect(z-2,116,4,5,12) end function oldRenderHUD() line(199,39,199,95,12) line(39,39,39,95,12) line(79,115,159,115,12) local y=pitch/minp y=(y*(136-80))+40 local z=(zoom-maxz)/(minz-maxz) z=(z*(136-80))+40 local x=(yaw-maxy)/(miny-maxy) x=((1-x)*(240-160))+80 rect(197,y-2,5,4,12) rect(37,z-2,5,4,12) rect(x-2,113,4,5,12) end function TIC() poke(0x3FFB,0) --Disable cursor cls(0) speed=zoom/150 local op,oy,oz=pitch,yaw,zoom if btn(0) and pitchminp then pitch=pitch-speed end if btn(2) and yawmaxy then yaw=yaw-speed end if btn(4) and zoommaxz then zoom=zoom-0.1 end local mvol=0 if pitch~=op then mvol=mvol+5 end if yaw~=oy then mvol=mvol+5 end if zoom~=oz then mvol=mvol+3 end if mvol>0 then if not sfxp then sfx(0,"A-4",2,0,mvol) sfxp=true else sfxp=false end else sfx(-1) end setFOV(zoom) camera.rot=quat:fromAngleAxis( deg2rad(yaw), vec3.UP ) camera.rot=camera.rot*quat:fromAngleAxis( deg2rad(pitch), vec3.RIGHT ) objects[2].xform=objects[2].xform:faceXform( objects[1].xform ) --[[vbank(1) cls(0) print(camera,0,0,15,true) print(objects[1].xform,0,18,15,true) if clipObj(camera:vec3FromWorld(vec3.ZERO),0) ~= nil then local y=project(camera:vec3FromWorld(vec3.UP))*40+scnctr local x=project(camera:vec3FromWorld(vec3.RIGHT))*40+scnctr local z=project(camera:vec3FromWorld(vec3.FRONT))*40+scnctr local o=project(camera:vec3FromWorld(vec3.ZERO))*40+scnctr line(o.x,o.y,x.x,x.y,2) line(o.x,o.y,y.x,y.y,6) line(o.x,o.y,z.x,z.y,9) end vbank(0)--]] local olist={} table.move(objects,1,#objects,1,olist) table.sort(olist, function(a,b) local ap=camera:vec3FromWorld(a.xform.pos) local bp=camera:vec3FromWorld(b.xform.pos) return ap:len2()>=bp:len2() end ) for i=1,#olist do renderObject(olist[i]) end renderHUD() end