Instantly share code, notes, and snippets.

# loopspace/1aTabOrder Created Jan 27, 2014

Flying Ignatz Release v1.12 -Using quaternions to fly a plane.
 Flying Ignatz Tab Order Version: 1.12 ------------------------------ This file should not be included in the Codea project. #Main #Flight #Joystick #Plane #Quaternion #Sky #VecExt
 --Flight class Flight=class() function Flight:init(t) t = t or {} self.mode= t.mode or vec3(0,1,0) --- AAA self.sens=t.sens or vec3(1,1,1)*0.25 self.limit=t.limit or 2*vec3(45,90,22.5)*math.pi/180 --- AAA self.steady=t.steady or 5 self:Reset() end function Flight:Reset() self.dv=vec3(0,0,0) self.pr = vec4(1,0,0,0) self.y = vec4(1,0,0,0) --CCCC initialise self.cam = { at = vec3(0,0,1), frame = vec4(1,0,0,0), vel = vec3(0,0,0), avel = vec3(0,0,0)} end --[[ self.turning needs to use a ternary logic so we can detect not only its state but also when it flips --]] function Flight:Turn(r) self.dv = self.mode * vec3(-r.value.y,-r.value.x,-r.value.x) * self.sens * DeltaTime self.pr = self.pr * qTangent((1 - self.mode) * vec3(-r.delta.y,-r.delta.x,-r.delta.x) * self.limit) self.y = self.y * qTangent(self.dv) self.turning = 1 end function Flight:Position(s) --d is vec3 distance in pixels return s^(self.y * self.pr) end function Flight:Rotate() self:Interpolate() --RRRR update interpolation when rotating rotate(self.y * self.pr) end function Flight:rotation() return self.y * self.pr end function Flight:Interpolate() --RRRR this has the interpolation code from Main --don't update if we've done it already in this timeslice if self.stime==ElapsedTime then return end --interpolate if self.turning == 2 then self.interpolating = true self.stime = ElapsedTime self.slerpQ = self:InterpolateSlerp() self.turning = 0 elseif self.turning == 1 then self.turning = 2 elseif self.interpolating then self.interpolating = self.slerpQ(ElapsedTime) end end function Flight:InterpolateSlerp() --RRRR this is now called from Interpolate above local rt, stime, l, rl, dy, y stime = ElapsedTime dy = self.dv rt = vec4(self.pr.x,0,self.pr.z,0):normalise() l = self.pr:sdist(rt) * self.steady rl = self.pr:make_slerp(rt) return function(t) t = t - stime if t > l then self.pr = vec4(1,0,0,0) self.y = self.y * rt return false end self.y = self.y * qTangent(edge(t,l,0) * dy) self.pr = rl(smootherstep(t,0,l)) return true end end -- CCCC new function to manage camera position --p = vec3, plane position --offset = vec3, location of camera relative to self, in object space --lag = scalar, camera follow lag, from 0 (no lag) to 5 (very slow) function Flight:SetCamera(p,offset,lag) if not lag or lag==0 then --slavishly follow plane self.cam.at = p + self:Position(offset) self.cam.frame= self:rotation() else --lag plane rotation local ca,cq = self.cam.at,self.cam.frame self.cam.at = self.cam.at + self.cam.vel * DeltaTime/lag self.cam.frame = self.cam.avel:exp(DeltaTime/lag) * self.cam.frame self.cam.vel = (p + self:Position(offset) - ca) self.cam.avel = (self:rotation() * cq^""):log() end local up = vec3(0,1,0)^self.cam.frame --NOTE - addition to end of next line, so camera looks in front of plane (and not at it) -- local look = self.cam.at - offset^self.cam.frame + self:Position(vec3(0,0,-50)) local look = p + self:Position(vec3(0,0,-20)) camera(self.cam.at,look,up) end
 JoyStick = class() function JoyStick:init(t) t = t or {} self.radius = t.radius or 100 self.stick = t.stick or 30 self.centre = t.centre or self.radius * vec2(1,1) + vec2(5,5) self.position = vec2(0,0) self.target = vec2(0,0) self.value = vec2(0,0) self.delta = vec2(0,0) self.mspeed = 60 self.moving = 0 end function JoyStick:draw() -- Codea does not automatically call this method ortho() viewMatrix(matrix()) pushStyle() fill(160, 182, 191, 50) stroke(118, 154, 195, 100) strokeWidth(1) ellipse(self.centre.x,self.centre.y,2*self.radius) fill(78, 131, 153, 50) ellipse(self.centre.x+self.position.x, self.centre.y+self.position.y, self.stick*2) popStyle() end function JoyStick:touched(t) if t.state == BEGAN then local v = vec2(t.x,t.y) if v:dist(self.centre)self.radius-self.stick then v = (v - self.centre):normalize()*(self.radius - self.stick) + self.centre end --set x,y values for joy based on touch self.target=v - self.centre else --reset joystick to centre when touch ends self.target=vec2(0,0) self.mytouch = false end end end function JoyStick:update() local p = self.target - self.position if p:len() < DeltaTime * self.mspeed then self.position = self.target if not self.mytouch then if self.moving ~= 0 then self.moving = self.moving - 1 end else self.moving = 2 end else self.position = self.position + p:normalize() * DeltaTime * self.mspeed self.moving = 2 end local v = self.position/(self.radius - self.stick) self.delta = v - self.value self.value = v end function JoyStick:isMoving() return self.moving end
 VERSION = "1.12" function setup() -- [[ if AutoGist then autogist = AutoGist("Flying Ignatz","Using quaternions to fly a plane.",VERSION) autogist:backup(true) end -- ]] --**Flight controls ** speed=vec3(0,0,-60) --pixels/sec --** Plane settings ** plane=Plane() --plane mesh planeQ=Flight()--plane quaternion planePos=vec3(0,0,0) --starting position camOffset={vec3(0,3,-9),vec3(0,0,50),vec3(0,40,100)} --camera position relative to plane parameter.integer("state",-1,5,0) parameter.integer("View",1,#camOffset,2) --** Joystick settings ** rotjoy = JoyStick({centre = vec2(105,105)}) mvjoy = JoyStick({centre = vec2(WIDTH - 105,105)}) --set up sky globe sky=SkyGlobe("Documents:SkyDome","Documents:SkyDome") --SSSS end function draw() rotjoy:update() mvjoy:update() background(179, 205, 220, 255) fill(255) perspective() camera(0,0,100,0,0,0) RotateAndDraw() rotjoy:draw() mvjoy:draw() end --this function adjusts the rotation based on joystick position, and draws the plane function RotateAndDraw() if rotjoy.mytouch then planeQ:Turn(rotjoy) end if mvjoy:isMoving() then planePos=planePos+planeQ:Position(mvjoy.value.y*speed*DeltaTime) end -- CCCC -see changes in this set of if statements, and SetCamera function at bottom of Flight tab if state == -1 then -- stationary camera --user can set it any way they want, this is not part of the flight library camera(0,0,50,0,0,-1000) --this just looks straight ahead else -- slavishly follows plane planeQ:SetCamera(planePos,camOffset[View],state) end pushMatrix() translate(planePos) sky:draw() --SSSS planeQ:Rotate() plane:draw() popMatrix() end --** Utility functions - joystick and touch *** --manage joystick function touched(t) rotjoy:touched(t) mvjoy:touched(t) end
 --plane Plane=class() function Plane:init() a={} c1=color(107, 139, 184, 255) c2=color(72, 148, 195, 255) c3=color(176, 159, 101, 255) c4=color(255,0,0) a[1]=Block(3,3,25,c1,vec3(0,0,0)) a[2]=Block(15,1,5,c2,vec3(-9,1,-6)) a[3]=Block(15,1,5,c2,vec3(9,1,-6)) a[4]=Block(1,4,3,c2,vec3(0,3.5,11)) a[5]=Block(4,1,2,c2,vec3(-3.5,1,11)) a[6]=Block(4,1,2,c2,vec3(3.5,1,11)) a[7]=Block(3,2,5,c3,vec3(0,2.5,-6)) a[8]=Block(3,3,1,c4,vec3(0,0,-13)) self.blocks=a end function Plane:draw() for i=1,#self.blocks do self.blocks[i]:draw() end end Block = class() --taken from 3D lab project function Block:init(w,h,d,c,p,t,r) --width,height,depth,colour,position,texture, (optional) texture range (see above) self.width=w self.height=h self.depth=d self.color=c self.pos=p self.tex=t --if no limits specified on which part of image to draw, set to 0,1 if r~=nil then self.texR=r else self.texR={0,0,1,1} end self.blk=self:createBlock() end function Block:createBlock() -- all the unique vertices that make up a block --There are only 8 corners in a cube - we define them as vertices --all measurements are taken from the centre of the block --so bottom left front has x of -1/2 width, y of -1/2 height, and z of 1/2 depth local w,h,d=self.width,self.height,self.depth local x,y,z=self.pos.x,self.pos.y,self.pos.z local v = { vec3(x-0.5*w, y-0.5*h, z+0.5*d), -- Left bottom front vec3(x+0.5*w, y-0.5*h, z+0.5*d), -- Right bottom front vec3(x+0.5*w, y+0.5*h, z+0.5*d), -- Right top front vec3(x-0.5*w, y+0.5*h, z+0.5*d), -- Left top front vec3(x-0.5*w, y-0.5*h, z-0.5*d), -- Left bottom back vec3(x+0.5*w, y-0.5*h, z-0.5*d), -- Right bottom back vec3(x+0.5*w, y+0.5*h, z-0.5*d), -- Right top back vec3(x-0.5*w, y+0.5*h, z-0.5*d), -- Left top back } local cubeverts = { -- Front, Right, Back, Left, Top, Bottom v[1], v[2], v[3], v[1], v[3], v[4], v[2], v[6], v[7], v[2], v[7], v[3], v[6], v[5], v[8], v[6], v[8], v[7], v[5], v[1], v[4], v[5], v[4], v[8], v[4], v[3], v[7], v[4], v[7], v[8], v[5], v[6], v[2], v[5], v[2], v[1], } local cc={} --assign colors local c=self.color local x=0.5 local BL=color(c.r,c.g,c.b) --bottom left local BR=color(c.r,c.g,c.b) --bottom right local TR=color(c.r*x,c.g*x,c.b*x) --top right local TL=color(c.r*x,c.g*x,c.b*x) --top left for i=1,6 do cc[#cc+1]=BL cc[#cc+1]=BR cc[#cc+1]=TR cc[#cc+1]=BL cc[#cc+1]=TR cc[#cc+1]=TL end --put it all together local ms = mesh() ms.vertices = cubeverts if self.tex then ms.texture = self.tex ms.texCoords = cubetexCoords else ms.colors=cc end --ms:setColors(self.color) return ms end function Block:draw() self.blk:draw() end