Instantly share code, notes, and snippets.

# loopspace/1aTabOrder Created Jan 19, 2014

Flying Release v1.7 -Using quaternions to fly a plane.
 Flying Tab Order Version: 1.7 ------------------------------ This file should not be included in the Codea project. #Main #JoyStick #Quaternion #Flight #Plane #Sky
 --Flight class Flight=class() local yaw = vec3(0,1,0) local pitchroll = vec3(1,0,1) function Flight:init(m) --m is mode 1-3: 1=Absolute, 2=Increment, 3=Delta self:Reset(m) end function Flight:Reset(m) --m is mode 1-3: 1=Absolute, 2=Increment, 3=Delta self.mode=m self.y=vec4(1,0,0,0) self.pr=vec4(1,0,0,0) end function Flight:Turn(v,m) m = m or self.mode self.y = self.y * qTangent(v * yaw) self.pr = self.pr * qTangent(v * pitchroll) end function Flight:Position(s) --d is vec3 distance in pixels return s^(self.y*self.pr) end function Flight:Rotate() rotate(self.y*self.pr) end function Flight:rotation() return self.y*self.pr end function Flight:Interpolate() local l = self.pr:slen() local sl = self.pr:make_slerp() return function(t) if t > l then self.pr = vec4(1,0,0,0) return false end self.pr = sl(1-t/l) return true end 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.value = vec2(0,0) self.oldvalue = vec2(0,0) self.delta = vec2(0,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.position=v - self.centre self.value=self.position/(self.radius-self.stick) else --reset joystick to centre when touch ends self.position=vec2(0,0) self.value=vec2(0,0) self.mytouch = false end return true end return false end function JoyStick:update() self.delta = self.value - self.oldvalue self.oldvalue = self.value end
 --v1.6 --the RotateAndDraw function needs code VERSION = 1.7 function setup() -- [[ if AutoGist then autogist = AutoGist("Flying","Using quaternions to fly a plane.",VERSION) autogist:backup(true) end -- ]] --**Flight controls ** -- yaw = 0 -- mode not currently used mode=vec3(0,1,0) speed=vec3(0,0,-60) --pixels/sec --XXXXX --** Plane settings ** plane=Plane() --plane mesh planeQ=Flight(mode)--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 camDir = {vec3(0,3,-10),vec3(0,0,-1),vec3(0,0,0)} parameter.integer("state",1,3,1) parameter.integer("View",1,#camOffset,1) --** Joystick settings ** rotjoy = JoyStick({centre = vec2(105,105)}) mvjoy = JoyStick({centre = vec2(WIDTH - 105,105)}) --** rotation settings and limits ** --x, y, z map to pitch, yaw, roll --limits for slave rotation limit=vec3(90,90,90)*math.pi/180 --sensitivity of rotation for incremental rotation sens=vec3(1,1,1) -- planeQ:Position(speed*DeltaTime) SetupSky() cam = { at = vec3(0,0,1), frame = vec4(1,0,0,0), vel = vec3(0,0,0), avel = vec3(0,0,0) } -- parameter.watch("cam.avel") parameter.watch("planeQ:rotation()") parameter.watch("cam.frame") --[[ local n = 2 local q = v:exp(1/n) print(q^n) ]] 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 local dv = vec3(-rotjoy.delta.y,-rotjoy.delta.x,-rotjoy.delta.x) local vv = vec3(-rotjoy.value.y,-rotjoy.value.x,-rotjoy.value.x) local v = (1 - mode) * dv * limit + mode * vv * sens * DeltaTime planeQ:Turn(v,mode) turning = true elseif turning then interpolating = true turning = false stime = ElapsedTime slerpQ = planeQ:Interpolate() elseif interpolating then interpolating = slerpQ(ElapsedTime - stime) end if mvjoy.mytouch then planePos=planePos+planeQ:Position(mvjoy.value.y*speed*DeltaTime) end -- --XXXX plane position, going in wrong vertical direction! if state == 1 then -- stationary camera cam.at = camOffset[View] --planePos+camOffset --XXXX camera position - incorrect cam.frame = vec4(1,0,0,0) elseif state == 2 then -- slavishly follows plane cam.at = planePos + planeQ:Position(camOffset[View]) cam.frame = planeQ:rotation() elseif state == 3 then -- follows, but with a delay local ca,cq = cam.at,cam.frame cam.at = cam.at + cam.vel * DeltaTime cam.frame = cam.avel:exp(DeltaTime) * cam.frame -- cam.vel = cam.vel + (planePos + planeQ:Position(camOffset[View]) - ca) * DeltaTime - 1.5*cam.vel*DeltaTime cam.vel = (planePos + planeQ:Position(camOffset[View]) - ca) -- cam.avel = cam.avel + (planeQ:rotation() * cq^""):log()*DeltaTime - 1.5*cam.avel*DeltaTime cam.avel = (planeQ:rotation() * cq^""):log() --*DeltaTime end local up,look = vec3(0,1,0)^cam.frame,planePos + planeQ:Position(camDir[View]) camera(cam.at,look,up) pushMatrix() DrawOtherStuff() translate(planePos) planeQ:Rotate() plane:draw() popMatrix() end --** Utility functions - joystick and touch *** --manage joystick function touched(t) if not rotjoy:touched(t) and not mvjoy:touched(t) and t.state == BEGAN then print(planeQ:rotation()) print(cam.frame) print(cam.avel) print(cam.avel:exp()) print(cam.avel:exp(DeltaTime^2)) print(DeltaTime^2) end end function DrawOtherStuff() --XXXXX added just so we have frame of reference to see how the camera rotates pushMatrix() translate(0,0,-1000) -- sprite("Planet Cute:Character Princess Girl",0,0,400) popMatrix() planet1:draw(planePos) 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