├── Clases_and_Methods.txt ├── LICENSE ├── LjES ├── Action.lua ├── Animation.lua ├── Background.lua ├── BonePhong.lua ├── Collada.lua ├── ColladaShape.lua ├── CoordinateSystem.lua ├── Font.lua ├── Frame.lua ├── FrameBufferObject.lua ├── GamePad.lua ├── Matrix.lua ├── Mesh.lua ├── Message.lua ├── Node.lua ├── Object.lua ├── Phong.lua ├── Quat.lua ├── Schedule.lua ├── Screen.lua ├── Shader.lua ├── Shape.lua ├── Skeleton.lua ├── Space.lua ├── Stack.lua ├── Task.lua ├── Text.lua ├── Texture.lua ├── bcm.lua ├── demo.lua ├── egl.lua ├── font512.png ├── gles2.lua ├── joystick.lua ├── png.lua ├── termios.lua └── util.lua ├── README └── examples ├── CubeMap_dice.png ├── axis.lua ├── dae_anim.lua ├── dae_anim0.lua ├── dae_gifanim.lua ├── demo_shape.lua ├── demo_spheres.lua ├── donut.lua ├── double_cone.lua ├── fbshot.lua ├── hand.blend ├── hand.dae ├── hand.lua ├── num512.png ├── pad.lua ├── skin.lua ├── sphere.lua ├── term.lua ├── test_task.lua ├── tiny.lua └── truncated_cone.lua /LICENSE: -------------------------------------------------------------------------------- 1 | LjES 2 | 3 | Copyright (c) 2013-2017 Jun Mizutani. https://www.mztn.org/ 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining 6 | a copy of this software and associated documentation files (the 7 | "Software"), to deal in the Software without restriction, including 8 | without limitation the rights to use, copy, modify, merge, publish, 9 | distribute, sublicense, and/or sell copies of the Software, and to 10 | permit persons to whom the Software is furnished to do so, subject to 11 | the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 20 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 22 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /LjES/Action.lua: -------------------------------------------------------------------------------- 1 | -- --------------------------------------------- 2 | -- Node.lua 2014/03/16 3 | -- Copyright (c) 2014 Jun Mizutani, 4 | -- released under the MIT open source license. 5 | -- --------------------------------------------- 6 | 7 | require("Object") 8 | local util = require("util") 9 | 10 | Action = Object:new() 11 | 12 | function Action.new(self, anim) 13 | local obj = Object.new(self) 14 | obj.anim = anim 15 | obj.actions = {} 16 | obj.patterns = {} 17 | obj.pat_index = 0 18 | obj.verbose = false 19 | obj.currentAction = {} 20 | obj.currentPattern = {} 21 | return obj 22 | end 23 | 24 | function Action.addKeyPattern(self, name, time, from, to) 25 | table.insert(self.patterns, {name, time, from, to}) 26 | return #self.patterns 27 | end 28 | 29 | function Action.addAction(self, name, pattern_list) 30 | self.actions[ name ] = pattern_list 31 | return #self.actions 32 | end 33 | 34 | function Action.setVerbose(self, true_or_false) 35 | self.verbose = true_or_false 36 | end 37 | 38 | function Action.startAction(self, action_name) 39 | self.currentAction = self.actions[action_name] 40 | self.pat_index = 1 41 | self.currentPattern = self.currentAction[self.pat_index] 42 | return self:startTimeFromTo(self.currentPattern) 43 | end 44 | 45 | function Action.getPattern(self) 46 | return unpack(self.currentPattern) -- name, time, from ,to 47 | end 48 | 49 | function Action.playAction(self) 50 | local ip = self.anim:play() 51 | if ip < 0 then -- end of current pattern 52 | self.pat_index = self.pat_index + 1 53 | if self.pat_index > #self.currentAction then 54 | return -1 -- end of current action 55 | end 56 | self.currentPattern = self.currentAction[self.pat_index] 57 | return self:startTimeFromTo(self.currentPattern) 58 | end 59 | return ip 60 | end 61 | 62 | function Action.startTimeFromTo(self, pat) 63 | local name, time, from, to = unpack(self.patterns[pat]) 64 | if self.verbose then 65 | util.printf("\npattern:%2d %10s %4d msec %2d -> %2d\n", 66 | self.currentPattern, name, time, from, to) 67 | end 68 | self.anim:startTimeFromTo(time, from, to) 69 | return from * 2 - 1 70 | end 71 | -------------------------------------------------------------------------------- /LjES/Animation.lua: -------------------------------------------------------------------------------- 1 | -- --------------------------------------------- 2 | -- Animation.lua 2014/06/05 3 | -- Copyright (c) 2014 Jun Mizutani, 4 | -- released under the MIT open source license. 5 | -- --------------------------------------------- 6 | 7 | require("Schedule") 8 | require("Node") 9 | local util = require("util") 10 | 11 | Animation = Object:new() 12 | 13 | function Animation.new(self, name) 14 | local obj = Object.new(self) 15 | obj.name = name 16 | obj.boneCount = 0 17 | obj.times = {} 18 | obj.poses = {} 19 | obj.boneNames = {} 20 | obj.diffPoses = {} 21 | obj.tasks = {} 22 | obj.schedule = Schedule:new() 23 | obj.skeleton = nil 24 | obj.endFrameIP = -1 25 | return obj 26 | end 27 | 28 | -- times = time of key frame[1 .. n] 29 | function Animation.setTimes(self, times) 30 | self.times = times 31 | end 32 | 33 | -- bone_poses = mat, mat, .. 34 | function Animation.setBonePoses(self, bone_poses) 35 | table.insert(self.poses, bone_poses) 36 | self.boneCount = #self.poses 37 | return #self.poses 38 | end 39 | 40 | function Animation.addBoneName(self, bone_name) 41 | table.insert(self.boneNames, bone_name) 42 | end 43 | 44 | function Animation.getBoneName(self, i) 45 | if #self.boneNames >= i then 46 | return self.boneNames[i] 47 | else 48 | return nil 49 | end 50 | end 51 | 52 | function Animation.getType(self) 53 | return self.type 54 | end 55 | 56 | function Animation.getName(self) 57 | return self.name 58 | end 59 | 60 | function Animation.countPoses(self) 61 | return #self.times 62 | end 63 | 64 | function Animation.getNoOfBones(self) 65 | return self.boneCount 66 | end 67 | 68 | function Animation.close(self) 69 | end 70 | 71 | local function difference(matA, matB) 72 | local quat 73 | local pos = {} 74 | local qA = Quat:new() 75 | local qB = Quat:new() 76 | local posA = matA:getPosition() 77 | local posB = matB:getPosition() 78 | qA:matrixToQuat(matA) 79 | qB:matrixToQuat(matB) 80 | local dp = qA:dotProduct(qB) 81 | if dp < 0 then qB:negate() end -- shortest path 82 | qA:condugate() 83 | quat = qB:clone() 84 | quat:lmulQuat(qA) 85 | pos[1] = posB[1] - posA[1] 86 | pos[2] = posB[2] - posA[2] 87 | pos[3] = posB[3] - posA[3] 88 | return quat, pos 89 | end 90 | 91 | function Animation.setData(self, skeleton, bind_shape_matrix) 92 | local time 93 | local q, pos 94 | self.skeleton = skeleton 95 | local bsm = bind_shape_matrix:clone() 96 | local m = bsm.mat 97 | m[ 1], m[ 2] = m[ 2], -m[ 1] 98 | m[ 5], m[ 6] = m[ 6], -m[ 5] 99 | m[ 9], m[10] = m[10], -m[ 9] 100 | m[13], m[14] = m[14], -m[13] 101 | for i = 1, self:getNoOfBones() do 102 | local task = self.schedule:addTask(self.boneNames[i]) 103 | local b = skeleton:getBone(self.boneNames[i]) 104 | if b == nil then 105 | skeleton:printJointNames() 106 | skeleton:printBone() 107 | util.printf("skeleton:getBone(self.boneNames[%d]) is nil.\n", i) 108 | end 109 | task:setTargetObject(b) 110 | table.insert(self.tasks, task) 111 | if b.rootBone then 112 | util.printf("root bone = %s\n", self.boneNames[i]) 113 | for key = 1, #self.times do 114 | self.poses[i][key]:lmul(bsm) 115 | end 116 | end 117 | end 118 | 119 | for key = 2, #self.times do 120 | time = (self.times[key] - self.times[key-1]) * 1000 -- msec 121 | for i = 1, self:getNoOfBones() do 122 | self.tasks[i]:addCommand(0, Node.putRotTransByMatrix, 123 | {self.poses[i][key]}) 124 | self.tasks[i]:addCommand(time, Node.doRotTrans, {1.0}) 125 | end 126 | end 127 | end 128 | 129 | function Animation.appendData(self, time, key_frame_no) 130 | local q, pos 131 | local bone 132 | local bone_diff 133 | for i = 1, self:getNoOfBones() do 134 | bone = self.poses[i] 135 | local last_frame = #self.times 136 | q, pos = difference(bone[last_frame], bone[key_frame_no]) 137 | bone_diff = {q, pos} 138 | table.insert(self.diffPoses, bone_diff) 139 | self.tasks[i]:addCommand(0, Node.putRotTrans, {q , pos}) 140 | self.tasks[i]:addCommand(time, Node.doRotTrans, {1.0}) 141 | end 142 | end 143 | 144 | function Animation.getPeriodFromTo(self, from, to) 145 | if (from < 1) or (from > #self.times) then return -1 end 146 | if (to < from) or (to > #self.times) then return -1 end 147 | return (self.times[to] - self.times[from]) * 1000 148 | end 149 | 150 | function Animation.setPose(self, key) 151 | for i = 1, self:getNoOfBones() do 152 | local b = self.skeleton:getBone(self.boneNames[i]) 153 | b:setByMatrix(self.poses[i][key]) 154 | end 155 | end 156 | 157 | function Animation.transitionTo(self, time, keyFrom, keyTo) 158 | local args = {} 159 | local ones = {} 160 | for i = 1, self:getNoOfBones() do 161 | table.insert(args, self.poses[i][keyFrom]) -- matrix 162 | table.insert(ones, 1.0) 163 | end 164 | self.schedule:directExecution(0, Node.putRotTransByMatrix, args) 165 | self.schedule:directExecution(time, Node.doRotTrans, ones, 166 | keyFrom * 2 - 1, (keyTo - 1) * 2) 167 | end 168 | 169 | function Animation.start(self) 170 | self:setPose(1) 171 | self.schedule:startFrom(1) 172 | end 173 | 174 | function Animation.play(self) 175 | return self.schedule:doCommand() 176 | end 177 | 178 | function Animation.playFps(self, frame_per_sec) 179 | return self.schedule:doCommandFps(frame_per_sec) 180 | end 181 | 182 | function Animation.startFromTo(self, keyFrom, keyTo) 183 | self:setPose(keyFrom) 184 | self.schedule:startFromTo(keyFrom * 2 -1, (keyTo - 1) * 2) 185 | end 186 | 187 | function Animation.startTimeFromTo(self, time, keyFrom, keyTo) 188 | self:transitionTo(time, keyFrom, keyTo) 189 | end 190 | 191 | function Animation.list(self, print_matrix) 192 | local time 193 | local mat = Matrix:new() 194 | local q, pos 195 | local h, p, b 196 | 197 | util.printf("\nAnimetion:%s, No. of keyframe=%d\n", self.name, #self.times) 198 | for i = 1, self:getNoOfBones() do 199 | util.printf("[%02d]---- %s ----\n", i, self:getBoneName(i)) 200 | for key = 1, #self.times do 201 | util.printf("[%6.2f]", self.times[key]) 202 | mat:copyFrom(self.poses[i][key]) 203 | if print_matrix then mat:print() end 204 | pos = mat:getPosition() 205 | h, p, b = mat:matToEuler() 206 | util.printf(" h:%8.3f p:%8.3f b:%8.3f ", h, p, b) 207 | util.printf(" x:%9.4f y:%9.4f z:%9.4f\n", unpack(pos)) 208 | end 209 | end 210 | end 211 | -------------------------------------------------------------------------------- /LjES/Background.lua: -------------------------------------------------------------------------------- 1 | -- --------------------------------------------- 2 | -- Background.lua 2014/06/15 3 | -- Copyright (c) 2013-2014 Jun Mizutani, 4 | -- released under the MIT open source license. 5 | -- --------------------------------------------- 6 | 7 | local ffi = require("ffi") 8 | local gl = require("gles2") 9 | 10 | require("Shader") 11 | require("Texture") 12 | 13 | Background = Shader:new() 14 | 15 | function Background.new(self) 16 | local obj = Shader.new(self) 17 | obj.aPosition = 0 18 | obj.aTexCoord = 0 19 | obj.uTexUnit = 0 20 | obj.uColor = 0 21 | obj.uAspect = 0 22 | obj.uWindow = 0 23 | obj.uOrder = 0 24 | obj.color = {1.0, 1.0, 1.0} 25 | obj.aspect = 1.0 26 | obj.window = {0.0, 0.0, 1.0, 1.0} 27 | obj.order = 0.0 28 | return obj 29 | end 30 | 31 | function Background.initShaderParameter(self) 32 | self:useProgram() 33 | local prog = self.program 34 | self.aPosition = gl.getAttribLocation(prog, "aPosition") 35 | self.aTexCoord = gl.getAttribLocation(prog, "aTexCoord") 36 | self.uColor = gl.getUniformLocation(prog, "uColor") 37 | self.uAspect = gl.getUniformLocation(prog, "uAspect") 38 | self.uWindow = gl.getUniformLocation(prog, "uWindow") 39 | self.uOrder = gl.getUniformLocation(prog, "uOrder") 40 | self.uTexUnit = gl.getUniformLocation(prog, "uTexUnit") 41 | end 42 | 43 | Background.vShaderSrc = [[ 44 | attribute vec3 aPosition; 45 | attribute vec2 aTexCoord; 46 | varying vec2 vTexCoord; 47 | uniform float uAspect; 48 | uniform float uOrder; 49 | uniform vec4 uWindow; // left, top, width, height 50 | void main() { 51 | vec3 pos; 52 | pos.x = aPosition.x * uWindow.z + uWindow.x; 53 | pos.y = aPosition.y * uWindow.w + uWindow.y; 54 | pos.z = aPosition.z - 0.00001*uOrder; 55 | gl_Position = vec4(pos.xyz, 1.0); 56 | vTexCoord.s = aTexCoord.s * uAspect; 57 | vTexCoord.t = aTexCoord.t; 58 | } 59 | ]] 60 | 61 | Background.fShaderSrc = [[ 62 | precision mediump float; 63 | varying vec2 vTexCoord; 64 | uniform vec3 uColor; 65 | uniform sampler2D uTexUnit; 66 | 67 | void main(void) { 68 | vec4 tex; 69 | tex = texture2D(uTexUnit, vec2(vTexCoord.s,vTexCoord.t)); 70 | gl_FragColor = vec4(tex.xyz*uColor, tex.w); 71 | } 72 | ]] 73 | 74 | function Background.setTextureUnit(self, tex_unit) 75 | self:useProgram() 76 | gl.uniform1i(self.uTexUnit, tex_unit) 77 | end 78 | 79 | function Background.setColor(self, r, g, b) 80 | self.color = {r, g, b} 81 | end 82 | 83 | -- aspect: 0.5 .. 8.0 (default: 1.0) 84 | function Background.setAspect(self, aspect) 85 | self.aspect = aspect 86 | end 87 | 88 | -- 0.0 .. 1.0 (default: 0, 0, 1, 1) 89 | function Background.setWindow(self, left, top, width, height) 90 | self.window = {left, top, width, height} 91 | end 92 | 93 | -- order: 0.0, 1.0, 2.0, .. (default: 0.0) 94 | function Background.setOrder(self, order) 95 | self.order = order 96 | end 97 | 98 | 99 | function Background.init(self) 100 | self.status = self:initShaders() 101 | self:initShaderParameter() 102 | self:useProgram() 103 | self:setColor(1.0, 1.0, 1.0) 104 | self.vbo = 0 105 | self:makeShape() 106 | end 107 | 108 | function Background.setBackground(self, texture) 109 | self.texture = texture 110 | end 111 | 112 | function Background.makeShape (self, scale) 113 | local vObj = ffi.new("float[?]", 20, -- (2) (3) 114 | -- position XYZ, texcoord UV : 5 floats 1.0 +---------+ 115 | -1.0, -1.0, 0.999999, 0.0, 0.0, -- (0) | | 116 | 1.0, -1.0, 0.999999, 1.0, 0.0, -- (1) Y | | 117 | -1.0, 1.0, 0.999999, 0.0, 1.0, -- (2) 0.0 +---------+ 118 | 1.0, 1.0, 0.999999, 1.0, 1.0 -- (3) 0.0 -X-> 1.0 119 | ) -- (0) (1) 120 | 121 | local vbo = ffi.new("uint32_t[1]") 122 | gl.genBuffers(1, vbo) 123 | self.vbo = vbo[0] 124 | gl.bindBuffer(gl.ARRAY_BUFFER, self.vbo) 125 | gl.bufferData(gl.ARRAY_BUFFER, ffi.sizeof(vObj), vObj, gl.STATIC_DRAW) 126 | end 127 | 128 | function Background.drawScreen(self) 129 | local shd = self 130 | self:useProgram() 131 | gl.uniform1f(self.uAspect, self.aspect) 132 | gl.uniform1f(self.uOrder, self.order) 133 | gl.uniform4f(self.uWindow, unpack(self.window)) 134 | gl.uniform3f(self.uColor, unpack(self.color)) 135 | -- gl.enable(gl.BLEND) 136 | -- gl.blendFunc(gl.SRC_ALPHA, gl.ONE) 137 | gl.bindBuffer(gl.ARRAY_BUFFER, self.vbo) 138 | gl.enableVertexAttribArray(shd.aPosition) 139 | gl.enableVertexAttribArray(shd.aTexCoord) 140 | gl.vertexAttribPointer(shd.aPosition,3,gl.FLOAT,gl.FALSE,5*4, 141 | ffi.cast("const void *", 0)) 142 | gl.vertexAttribPointer(shd.aTexCoord,2,gl.FLOAT,gl.FALSE,5*4, 143 | ffi.cast("const void *", 3*4)) 144 | self.texture:active() 145 | 146 | gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4) 147 | -- gl.disable(gl.BLEND) 148 | end 149 | 150 | -------------------------------------------------------------------------------- /LjES/BonePhong.lua: -------------------------------------------------------------------------------- 1 | -- --------------------------------------------- 2 | -- BonePhong.lua 2014/06/05 3 | -- Copyright (c) 2013-2014 Jun Mizutani, 4 | -- released under the MIT open source license. 5 | -- --------------------------------------------- 6 | 7 | local ffi = require("ffi") 8 | local gl = require("gles2") 9 | require("Shader") 10 | 11 | BonePhong = Shader:new() 12 | 13 | function BonePhong.new(self) 14 | local obj = Shader.new(self) 15 | obj.MAX_BONES = 40 16 | obj.PALETTE_SIZE = obj.MAX_BONES * 3 17 | obj.uProjMatrix = 0 18 | obj.uMVMatrix = 0 19 | obj.uNormalMatrix = 0 20 | obj.uTexUnit = 0 21 | obj.uTexFlag = 0 22 | obj.uEmit = 0 23 | obj.uAmb = 0 24 | obj.uSpec = 0 25 | obj.uSpecPower= 0 26 | obj.uLightPos = 0 27 | obj.uColor = 0 28 | obj.uHasBone = 0 29 | obj.aPosition = 0 30 | obj.aNormal = 0 31 | obj.aTexCoord = 0 32 | obj.aIndex = 0 33 | obj.aWeight = 0 34 | obj.default = {color = {0.8, 0.8, 1.0, 1.0}, tex_unit = 0, 35 | light = {0.0, 0.0, 100.0, 1}, use_texture = 0, 36 | emissive = 0, ambient = 0.3, specular = 0.6, 37 | power = 40, has_bone = 0 } 38 | obj.change = {} 39 | return obj 40 | end 41 | 42 | BonePhong.vShaderSrc = [[ 43 | attribute vec3 aPosition; 44 | attribute vec3 aNormal; 45 | attribute vec2 aTexCoord; 46 | attribute vec4 aIndex; 47 | attribute vec4 aWeight; 48 | 49 | varying vec3 vPosition; 50 | varying vec3 vNormal; 51 | varying vec2 vTexCoord; 52 | 53 | uniform int uHasBone; 54 | uniform mat4 uProjMatrix; 55 | uniform mat4 uMVMatrix; 56 | uniform mat4 uNormalMatrix; 57 | uniform vec4 uBones[120]; 58 | 59 | void main(void) { 60 | mat4 mat; 61 | vec4 v0, v1, v2; 62 | int i0, i1, i2, i3; 63 | if (uHasBone == 0) { 64 | mat = mat4(1.0); 65 | } else { 66 | i0 = int(aIndex.x) * 3; 67 | i1 = int(aIndex.y) * 3; 68 | i2 = int(aIndex.z) * 3; 69 | i3 = int(aIndex.w) * 3; 70 | v0 = uBones[i0] * aWeight.x + uBones[i1] * aWeight.y; 71 | v0 += uBones[i2] * aWeight.z + uBones[i3] * aWeight.w; 72 | v1 = uBones[i0 + 1] * aWeight.x + uBones[i1 + 1] * aWeight.y; 73 | v1 += uBones[i2 + 1] * aWeight.z + uBones[i3 + 1] * aWeight.w; 74 | v2 = uBones[i0 + 2] * aWeight.x + uBones[i1 + 2] * aWeight.y; 75 | v2 += uBones[i2 + 2] * aWeight.z + uBones[i3 + 2] * aWeight.w; 76 | mat[0] = vec4(v0.x, v1.x, v2.x, 0.0); 77 | mat[1] = vec4(v0.y, v1.y, v2.y, 0.0); 78 | mat[2] = vec4(v0.z, v1.z, v2.z, 0.0); 79 | mat[3] = vec4(v0.w, v1.w, v2.w, 1.0); 80 | } 81 | vTexCoord = aTexCoord; 82 | vPosition = vec3(uMVMatrix * mat * vec4(aPosition, 1.0)); 83 | vNormal = mat3(uNormalMatrix) * mat3(mat) * aNormal; 84 | gl_Position = uProjMatrix * uMVMatrix * mat * vec4(aPosition, 1.0); 85 | } 86 | ]] 87 | 88 | BonePhong.fShaderSrc = [[ 89 | precision mediump float; 90 | 91 | varying vec3 vPosition; 92 | varying vec3 vNormal; 93 | varying vec2 vTexCoord; 94 | uniform sampler2D uTexUnit; 95 | uniform int uTexFlag; 96 | uniform int uEmit; 97 | uniform float uAmb; 98 | uniform float uSpec; 99 | uniform float uSpecPower; 100 | uniform vec4 uLightPos; 101 | uniform vec4 uColor; 102 | 103 | void main(void) { 104 | vec4 color; 105 | vec3 lit_vec; 106 | float diff; 107 | float Ispec; 108 | vec4 white = vec4(1.0, 1.0, 1.0, 1.0); 109 | vec3 nnormal = normalize(vNormal); 110 | 111 | if (uLightPos.w!=0.0) { 112 | lit_vec = normalize(uLightPos.xyz - vPosition); 113 | } else { 114 | lit_vec = normalize(uLightPos.xyz); 115 | } 116 | vec3 eye_vec = normalize(-vPosition); 117 | vec3 ref_vec = normalize(reflect(-lit_vec, nnormal)); 118 | if (uEmit == 0) { 119 | diff = max(dot(nnormal, lit_vec), 0.0) * (1.0 - uAmb); 120 | Ispec = uSpec * pow(max(dot(ref_vec, eye_vec), 0.0), uSpecPower); 121 | } else { 122 | diff = 1.0 - uAmb; 123 | Ispec = 0.0; 124 | } 125 | if (uTexFlag != 0) { 126 | color = uColor * texture2D(uTexUnit,vec2(vTexCoord.s,vTexCoord.t)); 127 | color = mix(diff * uColor, color, uColor.w); 128 | } else { 129 | color = uColor; 130 | } 131 | gl_FragColor = vec4(color.rgb * (uAmb+diff) + white.rgb * Ispec,1); 132 | } 133 | ]] 134 | 135 | function BonePhong.initShaderParameter(self) 136 | self:useProgram() 137 | local prog = self.program 138 | self.uHasBone = gl.getUniformLocation(prog, "uHasBone") 139 | self.uProjMatrix = gl.getUniformLocation(prog, "uProjMatrix") 140 | self.uMVMatrix = gl.getUniformLocation(prog, "uMVMatrix") 141 | self.uNormalMatrix = gl.getUniformLocation(prog, "uNormalMatrix") 142 | self.uTexUnit = gl.getUniformLocation(prog, "uTexUnit") 143 | self.uTexFlag = gl.getUniformLocation(prog, "uTexFlag") 144 | self.uEmit = gl.getUniformLocation(prog, "uEmit") 145 | self.uAmb = gl.getUniformLocation(prog, "uAmb") 146 | self.uSpec = gl.getUniformLocation(prog, "uSpec") 147 | self.uSpecPower = gl.getUniformLocation(prog, "uSpecPower") 148 | self.uLightPos = gl.getUniformLocation(prog, "uLightPos") 149 | self.uColor = gl.getUniformLocation(prog, "uColor") 150 | self.aPosition = gl.getAttribLocation(prog, "aPosition") 151 | self.aNormal = gl.getAttribLocation(prog, "aNormal") 152 | self.aTexCoord = gl.getAttribLocation(prog, "aTexCoord") 153 | self.aIndex = gl.getAttribLocation(prog, "aIndex") 154 | self.aWeight = gl.getAttribLocation(prog, "aWeight") 155 | self.uBones = gl.getUniformLocation(prog, "uBones") 156 | end 157 | 158 | function BonePhong.init(self) 159 | self.status = self:initShaders() 160 | self:initShaderParameter() 161 | self:useProgram() 162 | self:setLightPosition(self.default.light) 163 | self:useTexture(self.default.use_texture) 164 | self:setColor(self.default.color) 165 | self:setEmissive(self.default.emissive) 166 | self:setAmbientLight(self.default.ambient) 167 | self:setSpecular(self.default.specular) 168 | self:setSpecularPower(self.default.power) 169 | self:setHasBone(self.default.has_bone) 170 | end 171 | 172 | function BonePhong.setDefaultParam(self, key, value) 173 | self.default[key] = value 174 | if key == "color" then self:setColor(value) 175 | elseif key == "tex_unit" then self:setTextureUnit(value) 176 | elseif key == "use_texture" then self:useTexture(value) 177 | elseif key == "light" then self:setLightPosition(value) 178 | elseif key == "emissive" then self:setEmissive(value) 179 | elseif key == "ambient" then self:setAmbientLight(value) 180 | elseif key == "specular" then self:setSpecular(value) 181 | elseif key == "power" then self:setSpecularPower(value) 182 | elseif key == "has_bone" then self:setHasBone(value) 183 | end 184 | end 185 | 186 | function BonePhong.setLightPosition(self, positionAndType) 187 | -- positionAndType : {x, y, z, type} 188 | -- type : 1/parallel light, 0/point light 189 | self:useProgram() 190 | gl.uniform4f(self.uLightPos, unpack(positionAndType)) 191 | end 192 | 193 | function BonePhong.useTexture(self, flag) 194 | self:useProgram() 195 | gl.uniform1i(self.uTexFlag, flag) 196 | self.textureFlag = flag 197 | end 198 | 199 | function BonePhong.setTextureUnit(self, tex_unit) 200 | self:useProgram() 201 | gl.uniform1i(self.uTexUnit, tex_unit) 202 | end 203 | 204 | function BonePhong.setEmissive(self, flag) 205 | self:useProgram() 206 | gl.uniform1i(self.uEmit, flag) 207 | end 208 | 209 | function BonePhong.setAmbientLight(self, intensity) 210 | self:useProgram() 211 | gl.uniform1f(self.uAmb, intensity) 212 | end 213 | 214 | function BonePhong.setSpecular(self, intensity) 215 | self:useProgram() 216 | gl.uniform1f(self.uSpec, intensity) 217 | end 218 | 219 | function BonePhong.setSpecularPower(self, power) 220 | self:useProgram() 221 | gl.uniform1f(self.uSpecPower, power) 222 | end 223 | 224 | function BonePhong.setColor(self, color) 225 | -- color : {r, g, b, a} 226 | self:useProgram() 227 | gl.uniform4f(self.uColor, unpack(color)) 228 | end 229 | 230 | function BonePhong.setProjectionMatrix(self, m) 231 | self:useProgram() 232 | gl.uniformMatrix4fv(self.uProjMatrix, 1, 0, m:convFloat()) 233 | end 234 | 235 | function BonePhong.setModelViewMatrix(self, m) 236 | self:useProgram() 237 | gl.uniformMatrix4fv(self.uMVMatrix, 1, 0, m:convFloat()) 238 | end 239 | 240 | function BonePhong.setNormalMatrix(self, m) 241 | self:useProgram() 242 | gl.uniformMatrix4fv(self.uNormalMatrix, 1, 0, m:convFloat()) 243 | end 244 | 245 | function BonePhong.updateTexture(self, param) 246 | if (param.texture ~= nil) then 247 | self.change.texture = param.texture 248 | param.texture:active() 249 | self:setTextureUnit(0) 250 | end 251 | end 252 | 253 | function BonePhong.setHasBone(self, flag) 254 | self:useProgram() 255 | gl.uniform1i(self.uHasBone, flag) 256 | end 257 | 258 | function BonePhong.setMatrixPalette(self, matrixPalette) 259 | self:useProgram() 260 | -- send 40 * 3 float4 to GPU 261 | gl.uniform4fv(self.uBones, self.PALETTE_SIZE, matrixPalette); 262 | end 263 | 264 | function BonePhong.doParameter(self, param) 265 | self:updateParam(param, "color", self.setColor) 266 | self:updateParam(param, "light", self.setLightPosition) 267 | self:updateParam(param, "use_texture", self.useTexture) 268 | self:updateParam(param, "ambient", self.setAmbientLight) 269 | self:updateParam(param, "specular", self.setSpecular) 270 | self:updateParam(param, "power", self.setSpecularPower) 271 | self:updateParam(param, "emissive", self.setEmissive) 272 | self:updateParam(param, "has_bone", self.setHasBone) 273 | self:updateTexture(param) 274 | end 275 | -------------------------------------------------------------------------------- /LjES/ColladaShape.lua: -------------------------------------------------------------------------------- 1 | -- --------------------------------------------- 2 | -- ColladaShape.lua 2014/03/23 3 | -- Copyright (c) 2014 Jun Mizutani, 4 | -- released under the MIT open source license. 5 | -- --------------------------------------------- 6 | 7 | package.path = "../LjES/?.lua;" .. package.path 8 | 9 | require("Collada") 10 | require("Shape") 11 | 12 | ColladaShape = Collada:new() 13 | 14 | function ColladaShape.new(self) 15 | local obj = Collada.new(self) 16 | return obj 17 | end 18 | 19 | -- ---------------------------- 20 | -- setBones called from setShape 21 | -- ---------------------------- 22 | function ColladaShape.setBones(self, mesh, shape, verts, newindex) 23 | local joint_names = mesh:getJointNames() 24 | if #joint_names == 0 then return end 25 | self:printf("bone count = %d\n", #joint_names) 26 | 27 | local skeleton = shape:getSkeleton() 28 | local skinweights = mesh:getSkinWeights() 29 | local ibp_matrices = mesh:getBindPoseMatrices() 30 | local bind_shape_matrix = mesh:getBindShapeMatrix() 31 | 32 | local found = false 33 | local frame = self.rootFrame:findFrame(joint_names[1]) 34 | repeat 35 | local count = frame:getNoOfBones(joint_names) 36 | if count < #joint_names then 37 | frame = frame.parent 38 | else 39 | found = true 40 | end 41 | until found or (frame == nil) 42 | self:printf("bone root = %s\n", frame:getName()) 43 | frame:copyToBone(joint_names, bind_shape_matrix, skeleton, 44 | nil, 0, self.printflag) 45 | 46 | skeleton:setBoneOrder(joint_names) 47 | skeleton:bindRestPose() 48 | shape:shaderParameter("has_bone", 1) 49 | 50 | -- register skin weights 51 | -- skinweights = { 52 | -- {bone_idx, weight, bone_idx, weight, .. } 53 | -- {bone_idx, weight, bone_idx, weight, .. } 54 | -- .. 55 | -- } 56 | local vert_count = #verts / 3 57 | for i = 1, vert_count do 58 | for n = 1, #newindex[i] do 59 | local vindex = newindex[i][n] 60 | local sw = skinweights[i] 61 | local weight_count = #sw / 2 62 | local tmp = {} 63 | local min = 1.0 64 | local imin = 0 65 | for j = 0, weight_count - 1 do 66 | local bone = sw[j * 2 + 1] 67 | local weight = sw[j * 2 + 2] 68 | if j < 4 then 69 | if weight < min then min = weight; imin = j+1 end 70 | table.insert(tmp, {bone, weight}) 71 | else 72 | if weight > min then 73 | tmp[imin] = {bone, weight} 74 | min = 1.0 75 | imin = 0 76 | for m = 1, #tmp do 77 | if tmp[m][2] < min then 78 | min = tmp[m][2] 79 | imin = m 80 | end 81 | end 82 | end 83 | end 84 | end 85 | -- normalize 86 | local total_weight = 0 87 | for m = 1, #tmp do 88 | total_weight = total_weight + tmp[m][2] 89 | end 90 | for m = 1, #tmp do 91 | shape:addVertexWeight(vindex, tmp[m][1], tmp[m][2] / total_weight) 92 | end 93 | end -- for n = 1, #newindex[i] do 94 | end -- for i = 1, vert_count do 95 | end 96 | 97 | -- ---------------------------- 98 | -- setShape called from makeShapes 99 | -- ---------------------------- 100 | function ColladaShape.setShape(self, nmesh, bone_enable, texture_select) 101 | local mesh = self.meshes[nmesh] 102 | local verts = mesh:getVertices() 103 | local normals = mesh:getNormals() 104 | local tex_table = mesh:getTextureCoord() 105 | local polygons = mesh:getPolygons() 106 | local bind_shape_matrix = mesh:getBindShapeMatrix() 107 | local originx, originy, originz 108 | 109 | if bind_shape_matrix ~= nil then 110 | originx, originy, originz = unpack(bind_shape_matrix:getPosition()) 111 | else 112 | originx, originy, originz = 0, 0, 0 113 | end 114 | 115 | self:printf("[%d]---- %s ----\n",nmesh , self.meshes[nmesh]:getName()) 116 | self:printf("vertices = %d\n", #verts/3) 117 | self:printf("normals = %d\n", #normals/3) 118 | 119 | local select 120 | if texture_select > #tex_table then 121 | select = #tex_table 122 | else 123 | select = texture_select 124 | end 125 | 126 | for m = 1, #tex_table do 127 | local tex = tex_table[m] 128 | self:printf("texture = %d\n", #tex/2) 129 | end 130 | self:printf("polygons = %d\n", #polygons) 131 | 132 | local shape = Shape:new() 133 | shape:setAutoCalcNormals(false) 134 | shape:setTextureMappingMode(-1) 135 | 136 | local joints = mesh:getJointNames() 137 | if bone_enable and (#joints > 0) then 138 | local skeleton = Skeleton:new() 139 | shape:setSkeleton(skeleton) 140 | end 141 | 142 | -- conversion table from pos_index to final-vert-indices for weight 143 | -- ex. newindex[pos_index] = {8, 10, 12} 144 | local newindex = {} 145 | for i = 1, #verts/3 do 146 | table.insert(newindex, {}) 147 | end 148 | 149 | local pos_index, nrm_index, tex_index 150 | local x, y, z, u, v, nx, ny, nz, h 151 | for i = 1, #polygons do 152 | local indices = {} 153 | for j = 1, #polygons[i] do 154 | pos_index = polygons[i][j][1] 155 | nrm_index = polygons[i][j][2] 156 | tex_index = polygons[i][j][3] 157 | x = verts[pos_index * 3 + 1] + originx 158 | z = -verts[pos_index * 3 + 2] - originy 159 | y = verts[pos_index * 3 + 3] + originz 160 | nx = normals[nrm_index * 3 + 1] 161 | nz = - normals[nrm_index * 3 + 2] 162 | ny = normals[nrm_index * 3 + 3] 163 | if #tex_table > 0 then 164 | if (#tex_table[select] > 0) then 165 | u = tex_table[select][tex_index * 2 + 1] 166 | v = tex_table[select][tex_index * 2 + 2] 167 | h = shape:addVertexPosUV({x, y, z}, {u, v}) 168 | end 169 | else 170 | h = shape:addVertex(x, y, z) 171 | end 172 | shape:setVertNormal(h, nx, ny, nz) 173 | table.insert(indices, h - 1) 174 | -- register h to conversion table 175 | table.insert(newindex[pos_index+1], h) 176 | end 177 | shape:addPlane(indices) 178 | end 179 | 180 | if bone_enable and (#joints > 0) then 181 | self:setBones(mesh, shape, verts, newindex) 182 | shape:setAnimation(self.anim) 183 | local skeleton = shape:getSkeleton() 184 | self.anim:setData(skeleton, bind_shape_matrix) 185 | end 186 | 187 | return shape 188 | end 189 | 190 | -- ---------------------------- 191 | -- makeShapes 192 | -- ---------------------------- 193 | function ColladaShape.makeShapes(self, bone_enable, verbose, tex_select) 194 | self.printflag = verbose 195 | if self.mesh_count == 0 then return nil end 196 | 197 | -- if multi-texture, select one of these. 198 | local texture_select = 1 199 | if tex_select then 200 | if tex_select > 1 and tex_select <= 3 then 201 | texture_select = tex_select -- for multi-texture: 1 or 2 or 3 202 | end 203 | end 204 | 205 | local shapes = {} 206 | local shape 207 | for k = 1, self.mesh_count do 208 | shape = self:setShape(k, bone_enable, texture_select) 209 | shape:endShape() 210 | table.insert(shapes, shape) 211 | end 212 | 213 | return shapes 214 | end 215 | 216 | -------------------------------------------------------------------------------- /LjES/CoordinateSystem.lua: -------------------------------------------------------------------------------- 1 | -- --------------------------------------------- 2 | -- CoordinateSystem.lua 2014/06/05 3 | -- Copyright (c) 2013-2014 Jun Mizutani, 4 | -- released under the MIT open source license. 5 | -- --------------------------------------------- 6 | 7 | require("Matrix") 8 | require("Quat") 9 | local util = require("util") 10 | 11 | CoordinateSystem = Object:new() 12 | CoordinateSystem.COORD_T = 0 13 | CoordinateSystem.NODE_T = 1 14 | CoordinateSystem.BONE_T = 2 15 | 16 | function CoordinateSystem.new(self, parent_node, name) 17 | local obj = Object.new(self) 18 | obj.name = name 19 | obj.parent = parent_node 20 | obj.children = {} 21 | obj.type = CoordinateSystem.COORD_T 22 | obj.matrix = Matrix:new() 23 | obj.worldMatrix = Matrix:new() 24 | obj.position = {0, 0, 0} 25 | obj.quat = Quat:new() 26 | obj.dirty = true 27 | obj.accumulatedRatio = 0 28 | obj.startRotation = Quat:new() 29 | obj.endRotation = Quat:new() 30 | obj.startPosition = {} 31 | obj.transDistance = {} 32 | return obj 33 | end 34 | 35 | function CoordinateSystem.print(self, str, q, pos) 36 | util.printf("%s h:%8.3f p:%8.3f b:%8.3f ", str, q:quatToEuler()) 37 | util.printf(" x:%9.4f y:%9.4f z:%9.4f\n", unpack(pos)) 38 | end 39 | 40 | function CoordinateSystem.printMoveRange(self) 41 | self:print("start", self.startRotation, self.startPosition) 42 | self:print("end ", self.endRotation, self.transDistance) 43 | end 44 | 45 | function CoordinateSystem.setType(self, type) 46 | self.type = type 47 | end 48 | 49 | function CoordinateSystem.getType(self) 50 | return self.type 51 | end 52 | 53 | function CoordinateSystem.addChild(self, child) 54 | table.insert(self.children, child) 55 | end 56 | 57 | function CoordinateSystem.getNoOfChildren(self) 58 | return #self.children 59 | end 60 | 61 | function CoordinateSystem.getChild(self, n) 62 | if n > #self.children then 63 | return nil 64 | else 65 | return self.children[n] 66 | end 67 | end 68 | 69 | function CoordinateSystem.setParent(self, parent) 70 | self.parent = parent 71 | end 72 | 73 | function CoordinateSystem.getParent(self) 74 | return self.parent 75 | end 76 | 77 | function CoordinateSystem.setName(self, name) 78 | self.name = name 79 | end 80 | 81 | function CoordinateSystem.getName(self) 82 | return self.name 83 | end 84 | 85 | function CoordinateSystem.setAttitude(self, head, pitch, bank) 86 | self.quat:eulerToQuat(head, pitch, bank) 87 | self.dirty = true 88 | end 89 | 90 | function CoordinateSystem.getWorldAttitude(self) 91 | self:setWorldMatrix() 92 | return self.worldMatrix:matToEuler() 93 | end 94 | 95 | function CoordinateSystem.getLocalAttitude(self) 96 | self:setMatrix() 97 | return self.matrix:matToEuler() 98 | end 99 | 100 | function CoordinateSystem.getWorldPosition(self) 101 | self:setWorldMatrix() 102 | return self.worldMatrix:getPosition() 103 | end 104 | 105 | function CoordinateSystem.getPosition(self) 106 | return self.position 107 | end 108 | 109 | function CoordinateSystem.setPosition(self, x, y, z) 110 | self.position[1] = x 111 | self.position[2] = y 112 | self.position[3] = z 113 | end 114 | 115 | function CoordinateSystem.setPositionX(self, x) 116 | self.position[1] = x 117 | end 118 | 119 | function CoordinateSystem.setPositionY(self, y) 120 | self.position[2] = y 121 | end 122 | 123 | function CoordinateSystem.setPositionZ(self, z) 124 | self.position[3] = z 125 | end 126 | 127 | function CoordinateSystem.rotateX(self, degree) 128 | local qq = Quat:new() 129 | qq:setRotateX(degree) 130 | self.quat:mulQuat(qq) 131 | self.dirty = true 132 | end 133 | 134 | function CoordinateSystem.rotateY(self, degree) 135 | local qq = Quat:new() 136 | qq:setRotateY(degree) 137 | self.quat:mulQuat(qq) 138 | self.dirty = true 139 | end 140 | 141 | function CoordinateSystem.rotateZ(self, degree) 142 | local qq = Quat:new() 143 | qq:setRotateZ(degree) 144 | self.quat:mulQuat(qq) 145 | self.dirty = true 146 | end 147 | 148 | function CoordinateSystem.rotate(self, head, pitch, bank) 149 | local qq = Quat:new() 150 | qq:eulerToQuat(head, pitch, bank) 151 | self.quat:mulQuat(qq) 152 | self.dirty = true 153 | end 154 | 155 | function CoordinateSystem.move(self, x, y, z) 156 | self:setMatrix() 157 | self.position = self.matrix:mulVector({x, y, z}) 158 | end 159 | 160 | function CoordinateSystem.setMatrix(self) 161 | if self.dirty then 162 | self.matrix:setByQuat(self.quat) 163 | self.dirty = false 164 | end 165 | self.matrix:position(self.position) 166 | end 167 | 168 | function CoordinateSystem.setWorldMatrix(self) 169 | local parent = self.parent 170 | self:setMatrix() 171 | if (parent) then 172 | parent:setWorldMatrix() 173 | self.worldMatrix = self.matrix:clone() 174 | self.worldMatrix:lmul(parent.worldMatrix) 175 | else 176 | self.worldMatrix = self.matrix:clone() 177 | end 178 | end 179 | 180 | function CoordinateSystem.setWorldMatrixAll(self, wmat) 181 | self.matrix:setByQuat(self.quat) 182 | self.matrix:position(self.position) 183 | self.worldMatrix:copyFrom(self.matrix) 184 | if self.parent ~= nil and wmat ~= nil then 185 | self.worldMatrix:lmul(wmat) -- [Cn] = [Q0] x ... x [Qn] 186 | end 187 | local children = self.children 188 | for j=1, #children do 189 | children[j]:setWorldMatrixAll(self.worldMatrix) 190 | end 191 | end 192 | 193 | function CoordinateSystem.getWorldMatrix(self) 194 | self:setWorldMatrix() 195 | return self.worldMatrix:clone() 196 | end 197 | 198 | function CoordinateSystem.setByMatrix(self, matrix) 199 | self.matrix:copyFrom(matrix) 200 | self.quat:matrixToQuat(matrix) 201 | self.position = self.matrix:getPosition() 202 | end 203 | 204 | function CoordinateSystem.setQuat(self, quat) 205 | self.quat = quat 206 | end 207 | 208 | function CoordinateSystem.getQuat(self) 209 | return self.quat 210 | end 211 | 212 | function CoordinateSystem.getQuatFromMatrix(self) 213 | local quat = Quat:new() 214 | quat:matrixToQuat(self.matrix) 215 | return quat 216 | end 217 | 218 | function CoordinateSystem.getPositionFromMatrix(self) 219 | return self.matrix:getPosition() 220 | end 221 | 222 | function CoordinateSystem.detach(self) 223 | local parent = self.parent 224 | if (parent~=nil) then 225 | self:setWorldMatrix() 226 | self.quat:matrixToQuat(self.worldMatrix) 227 | self.position = self.worldMatrix:getPosition() 228 | self.dirty = true 229 | if (#parent.children > 0) then 230 | for i=1, #parent.children do 231 | if (parent.children[i] == self) then 232 | table.remove(parent.children, i) 233 | end 234 | end 235 | end 236 | self.parent = nil 237 | end 238 | end 239 | 240 | function CoordinateSystem.attach(self, parent_node) 241 | local q = Quat:new() 242 | if ((self.parent == nil) and (parent_node)) then 243 | local p = parent_node:getWorldPosition() 244 | local r = self:getWorldPosition() 245 | q:matrixToQuat(parent_node.worldMatrix) 246 | q:condugate() 247 | self.quat:matrixToQuat(self.worldMatrix) 248 | self.quat:lmulQuat(q) 249 | self.position[1] = r[1] - p[1] 250 | self.position[2] = r[2] - p[2] 251 | self.position[3] = r[3] - p[3] 252 | self.position = parent_node.worldMatrix:tmul3x3Vector(self.position) 253 | self.parent = parent_node 254 | table.insert(parent_node.children, self) 255 | self.dirty = true 256 | end 257 | end 258 | 259 | function CoordinateSystem.inverse(self, new_parent) 260 | if (self.parent) then 261 | local p = self.parent 262 | self:detach() 263 | p:inverse(self) 264 | end 265 | self:attach(new_parent) 266 | end 267 | 268 | function CoordinateSystem.distance(self, node) 269 | local a = self:getWorldPosition() 270 | local b = node:getWorldPosition() 271 | local x = b[1] - a[1] 272 | local y = b[2] - a[2] 273 | local z = b[3] - a[3] 274 | return math.sqrt(x * x + y * y + z * z) 275 | end 276 | 277 | function CoordinateSystem.putRotation(self, head, pitch, bank) 278 | self.accumulatedRatio = 0 279 | local qq = Quat:new() 280 | qq:eulerToQuat(head, pitch, bank) 281 | self.startRotation:copyFrom(self.quat) 282 | self.endRotation:copyFrom(self.quat) 283 | self.endRotation:mulQuat(qq) 284 | end 285 | 286 | function CoordinateSystem.putRotationByQuat(self, quat) 287 | self.accumulatedRatio = 0 288 | self.startRotation:copyFrom(self.quat) 289 | self.endRotation:copyFrom(self.quat) 290 | self.endRotation:mulQuat(quat) 291 | end 292 | 293 | function CoordinateSystem.putAttitudeByQuat(self, quat) 294 | self.accumulatedRatio = 0 295 | self.startRotation:copyFrom(self.quat) 296 | self.endRotation:copyFrom(quat) 297 | end 298 | 299 | function CoordinateSystem.putAttitude(self, head, pitch, bank) 300 | self.accumulatedRatio = 0 301 | self.startRotation:copyFrom(self.quat) 302 | self.endRotation:eulerToQuat(head, pitch, bank) 303 | end 304 | 305 | function CoordinateSystem.putDistance(self, x, y, z) 306 | self.accumulatedRatio = 0 307 | self.startPosition = { unpack(self.position) } 308 | self.transDistance = {x, y, z} 309 | end 310 | 311 | function CoordinateSystem.putRotTrans(self, quat, pos) 312 | self.accumulatedRatio = 0 313 | self.startRotation:copyFrom(self.quat) 314 | self.endRotation:copyFrom(self.quat) 315 | self.endRotation:mulQuat(quat) 316 | self.startPosition = { unpack(self.position) } 317 | self.transDistance = { unpack(pos) } 318 | end 319 | 320 | function CoordinateSystem.putRotTransByMatrix(self, matrix) 321 | self.accumulatedRatio = 0 322 | self.startRotation:copyFrom(self.quat) 323 | self.endRotation:matrixToQuat(matrix) 324 | self.startPosition = { unpack(self.position) } 325 | local endPosition = matrix:getPosition() 326 | for i=1, 3 do 327 | self.transDistance[i] = endPosition[i] - self.startPosition[i] 328 | end 329 | end 330 | 331 | function CoordinateSystem.execRotation(self, t) 332 | self.quat:slerp(self.startRotation, self.endRotation, t) 333 | self.dirty = true 334 | end 335 | 336 | function CoordinateSystem.execTranslation(self, t) 337 | local distance = self.transDistance 338 | local pos = self.startPosition 339 | self.position[1] = pos[1] + distance[1] * t 340 | self.position[2] = pos[2] + distance[2] * t 341 | self.position[3] = pos[3] + distance[3] * t 342 | end 343 | 344 | function CoordinateSystem.doRotation(self, t) 345 | self.accumulatedRatio = self.accumulatedRatio + t 346 | local accum = self.accumulatedRatio 347 | self:execRotation(accum) 348 | end 349 | 350 | function CoordinateSystem.doTranslation(self, t) 351 | self.accumulatedRatio = self.accumulatedRatio + t 352 | local accum = self.accumulatedRatio 353 | self:execTranslation(accum) 354 | end 355 | 356 | function CoordinateSystem.doRotTrans(self, t) 357 | self.accumulatedRatio = self.accumulatedRatio + t 358 | local accum = self.accumulatedRatio 359 | self:execRotation(accum) 360 | self:execTranslation(accum) 361 | end 362 | 363 | -------------------------------------------------------------------------------- /LjES/Font.lua: -------------------------------------------------------------------------------- 1 | -- --------------------------------------------- 2 | -- Font.lua 2014/07/24 3 | -- Copyright (c) 2013-2014 Jun Mizutani, 4 | -- released under the MIT open source license. 5 | -- --------------------------------------------- 6 | 7 | local gl = require("gles2") 8 | 9 | require("Shader") 10 | 11 | Font = Shader:new() 12 | 13 | function Font.new(self) 14 | local obj = Shader.new(self) 15 | obj.aPosition = 0 16 | obj.aTexCoord = 0 17 | obj.uTexUnit = 0 18 | obj.uColor = 0 19 | obj.uChar = 0 20 | obj.uScale = 0 21 | obj.scale = 1.0 22 | return obj 23 | end 24 | 25 | function Font.initShaderParameter(self) 26 | self:useProgram() 27 | local prog = self.program 28 | self.aPosition = gl.getAttribLocation(prog, "aPosition") 29 | self.aTexCoord = gl.getAttribLocation(prog, "aTexCoord") 30 | self.uChar = gl.getUniformLocation(prog, "uChar") 31 | self.uColor = gl.getUniformLocation(prog, "uColor") 32 | self.uScale = gl.getUniformLocation(prog, "uScale") 33 | self.uTexUnit = gl.getUniformLocation(prog, "uTexUnit") 34 | self:setTextureUnit(0) 35 | self:setChar(0, 0, 0x20) 36 | self:setColor(1.0, 1.0, 1.0) 37 | self:setScale(1.0) 38 | end 39 | 40 | Font.vShaderSrc = [[ 41 | attribute vec3 aPosition; 42 | attribute vec2 aTexCoord; 43 | varying vec2 vTexCoord; 44 | uniform vec3 uChar; 45 | uniform float uScale; 46 | void main() { 47 | float x, y; 48 | vec4 pos; 49 | y = floor((uChar.z + 0.5) / 16.0); 50 | x = uChar.z - y * 16.0; 51 | pos.x = (aPosition.x + uChar.x * 0.025) * uScale - 1.0; 52 | pos.y = 1.0 + (aPosition.y - 0.08 * (uChar.y+1.0)) * uScale; 53 | gl_Position = vec4(pos.xy, -0.9, 1.0); 54 | vTexCoord.s = aTexCoord.s + 0.0625 * x; 55 | vTexCoord.t = aTexCoord.t + 0.125 * y; 56 | } 57 | ]] 58 | 59 | Font.fShaderSrc = [[ 60 | precision mediump float; 61 | varying vec2 vTexCoord; 62 | uniform vec3 uColor; 63 | uniform sampler2D uTexUnit; 64 | 65 | void main(void) { 66 | vec4 tex; 67 | tex = texture2D(uTexUnit, vec2(vTexCoord.s,vTexCoord.t)); 68 | gl_FragColor = vec4(tex.xyz * uColor, tex.w); 69 | } 70 | ]] 71 | 72 | function Font.setTextureUnit(self, tex_unit) 73 | self:useProgram() 74 | gl.uniform1i(self.uTexUnit, tex_unit) 75 | end 76 | 77 | -- ($20<= ch <=$7F) 78 | function Font.setChar(self, x, y, ch) 79 | -- self:useProgram() 80 | gl.uniform3f(self.uChar, x, y, ch) 81 | end 82 | 83 | -- x: 0..79, y: 0..24 84 | function Font.setPos(self, x, y) 85 | self:useProgram() 86 | gl.uniform3f(self.uChar, x, y, 32.0) 87 | end 88 | 89 | function Font.setColor(self, r, g, b) 90 | self:useProgram() 91 | gl.uniform3f(self.uColor, r, g, b) 92 | end 93 | 94 | -- scale: 0.5 .. 8.0 (default: 1.0) 95 | function Font.setScale(self, scale) 96 | self:useProgram() 97 | self.scale = scale 98 | gl.uniform1f(self.uScale, scale) 99 | end 100 | 101 | function Font.getScale(self) 102 | return self.scale 103 | end 104 | -------------------------------------------------------------------------------- /LjES/Frame.lua: -------------------------------------------------------------------------------- 1 | -- --------------------------------------------- 2 | -- Frame.lua 2014/06/05 3 | -- treat elements of COLLADA format 4 | -- Copyright (c) 2014 Jun Mizutani, 5 | -- released under the MIT open source license. 6 | -- --------------------------------------------- 7 | 8 | require("CoordinateSystem") 9 | local util = require("util") 10 | 11 | Frame = CoordinateSystem:new(nil) 12 | 13 | function Frame.new(self, parent, name) 14 | local obj = CoordinateSystem.new(self, parent) 15 | obj.name = name 16 | obj.usedAsBone = false 17 | obj.boneCount = 0 18 | obj.hasMesh = false 19 | obj.bofMatrix = Matrix:new() 20 | obj.type = "" 21 | if parent then 22 | parent:addChild(obj) 23 | end 24 | return obj 25 | end 26 | 27 | -- override CoordinateSystem.setByMatrix 28 | function Frame.setByMatrix(self, matrix) 29 | self.matrix:copyFrom(matrix) 30 | self.quat:matrixToQuat(matrix) 31 | self.position = self.matrix:getPosition() 32 | end 33 | 34 | function Frame.setWeights(self) 35 | self.hasWeights = true 36 | end 37 | 38 | function Frame.setType(self, type_name) 39 | self.type = type_name 40 | end 41 | 42 | function Frame.getType(self) 43 | return self.type 44 | end 45 | 46 | function Frame.getName(self) 47 | return self.name 48 | end 49 | 50 | function Frame.findFrame(self, name) 51 | if self.name == name then return self end 52 | if #self.children > 0 then 53 | for i=1, #self.children do 54 | local frame = self.children[i]:findFrame(name) 55 | if frame then return frame end 56 | end 57 | end 58 | return nil 59 | end 60 | 61 | function Frame.getNoOfBones(self, names) 62 | local count = 0 63 | if #self.children > 0 then 64 | for i = 1, #self.children do 65 | count = count + self.children[i]:getNoOfBones(names) 66 | end 67 | end 68 | for i = 1, #names do 69 | if names[i] == self.name then 70 | count = count + 1 71 | break 72 | end 73 | end 74 | return count 75 | end 76 | 77 | function Frame.findChildFrames(self, names) 78 | for i = 1, #names do 79 | if names[i] == self.name then return self end 80 | end 81 | if #self.children > 0 then 82 | local frame 83 | for i = 1, #self.children do 84 | frame = self.children[i]:findChildFrames(names) 85 | if frame ~= nil then return frame end 86 | end 87 | end 88 | return nil 89 | end 90 | 91 | function Frame.getFramesFromNames(self, joint_names) 92 | local frames = {} 93 | local frame 94 | for i = 1, #joint_names do 95 | frame = self:findFrame(joint_names[i]) 96 | table.insert(frames, frame) 97 | end 98 | return frames 99 | end 100 | 101 | function Frame.copyToBone(self, joint_names, bind_shape_matrix, 102 | skeleton, parent_bone, count, verbose) 103 | if not self:findChildFrames(joint_names) then return end 104 | 105 | local bone 106 | if self.type == "JOINT" then 107 | if parent_bone == nil then 108 | local bsm = bind_shape_matrix:clone() 109 | local m = bsm.mat 110 | m[ 1], m[ 2] = m[ 2], -m[ 1] 111 | m[ 5], m[ 6] = m[ 6], -m[ 5] 112 | m[ 9], m[10] = m[10], -m[ 9] 113 | m[13], m[14] = m[14], -m[13] 114 | self.matrix:lmul(bsm) 115 | end 116 | bone = skeleton:addBone(parent_bone, self.name) 117 | if verbose then 118 | util.printf("bones[%2d] %s\n", skeleton:getBoneCount(), self.name) 119 | end 120 | bone:setByMatrix(self.matrix) 121 | bone:setRestByMatrix(self.matrix) 122 | if self.hasWeights then bone:setWeights() end 123 | end 124 | if #self.children > 0 then 125 | for i=1, #self.children do 126 | self.children[i]:copyToBone(joint_names, bind_shape_matrix, 127 | skeleton, bone, count, verbose) 128 | end 129 | end 130 | return 131 | end 132 | 133 | function Frame.list(self, level) 134 | local pos, pos2 135 | local q = Quat:new() 136 | local h, p, b 137 | local head = string.rep("+", level) 138 | local left = string.rep(" ", level) 139 | 140 | util.printf("%snode:%s type=%s\n", head, self.name, self.type) 141 | -- local matrix 142 | self.matrix:print() 143 | pos2 = self:getPosition() 144 | util.printf("%slocal x:% 12.5f, y:% 12.5f, z:% 12.5f\n", left, 145 | pos2[1], pos2[2], pos2[3]) 146 | h, p, b = self:getLocalAttitude() 147 | util.printf("%s h:% 12.5f, p:% 12.5f, b:% 12.5f\n", left, h, p, b) 148 | q = self:getQuat() 149 | --util.printf("%s q0:% 10.5f, q1:% 10.5f, q2:% 10.5f, q3:% 10.5f\n\n", 150 | -- left, q.q[0], q.q[1], q.q[2], q.q[3]) 151 | end 152 | 153 | function Frame.listAll(self, level) 154 | self:list(level) 155 | if self.children then 156 | for i=1, #self.children do 157 | self.children[i]:listAll(level + 1) 158 | end 159 | end 160 | end 161 | 162 | -------------------------------------------------------------------------------- /LjES/FrameBufferObject.lua: -------------------------------------------------------------------------------- 1 | -- --------------------------------------------- 2 | -- FrameBufferObject.lua 2014/06/05 3 | -- Copyright (c) 2013-2014 Jun Mizutani, 4 | -- released under the MIT open sondurce license. 5 | -- --------------------------------------------- 6 | 7 | local ffi = require("ffi") 8 | local gl = require("gles2") 9 | local png = require("png") 10 | 11 | require("Texture") 12 | 13 | FrameBufferObject = Object:new() 14 | 15 | function FrameBufferObject.create(self, texture_class) 16 | self.texture = texture_class 17 | self.width = texture_class.width 18 | self.height = texture_class.height 19 | self.clearColor = {0.0, 0.0, 0.0, 1.0} 20 | local width = self.texture.width 21 | local height = self.texture.height 22 | 23 | local valueArray = ffi.new("uint32_t[1]") 24 | gl.getIntegerv(gl.MAX_RENDERBUFFER_SIZE, valueArray) 25 | local maxRenderbufferSize = valueArray[0] 26 | if((maxRenderbufferSize <= width) or (maxRenderbufferSize <= height)) then 27 | -- >2048x2048 28 | return false 29 | end 30 | 31 | local framebuffer = ffi.new("uint32_t[1]") 32 | gl.genFramebuffers(1, framebuffer) 33 | gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer[0]) 34 | 35 | local depthRenderbuffer = ffi.new("uint32_t[1]") 36 | gl.genRenderbuffers(1, depthRenderbuffer) 37 | gl.bindRenderbuffer(gl.RENDERBUFFER, depthRenderbuffer[0]) 38 | gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_COMPONENT24_OES, 39 | width, height) 40 | gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, 41 | gl.TEXTURE_2D, texture_class:name(), 0) 42 | 43 | gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, 44 | gl.RENDERBUFFER, depthRenderbuffer[0]) 45 | 46 | self.framebuffer = framebuffer 47 | self.depthRenderbuffer = depthRenderbuffer 48 | 49 | local status = gl.checkFramebufferStatus(gl.FRAMEBUFFER) 50 | if (status == gl.FRAMEBUFFER_COMPLETE) then 51 | return true 52 | end 53 | return false 54 | end 55 | 56 | function FrameBufferObject.setClearColor(self, r, g, b, alpha) 57 | self.clearColor = {r, g, b, alpha} 58 | end 59 | 60 | function FrameBufferObject.clear(self) 61 | gl.bindFramebuffer(gl.FRAMEBUFFER, self.framebuffer[0]) 62 | gl.viewport(0, 0, self.width, self.height) 63 | gl.clearColor(unpack(self.clearColor)) 64 | gl.clear(gl.COLOR_BUFFER_BIT + gl.DEPTH_BUFFER_BIT) 65 | --self.texture:active() 66 | end 67 | 68 | function FrameBufferObject.endDraw(self) 69 | gl.bindFramebuffer(gl.FRAMEBUFFER, 0) -- window system fb 70 | end 71 | 72 | function FrameBufferObject.destroy(self) 73 | glDeleteRenderbuffers(1, self.depthRenderbuffer) 74 | glDeleteFramebuffers(1, self.framebuffer) 75 | return true 76 | end 77 | 78 | function FrameBufferObject.writeToFile(self) 79 | gl.bindFramebuffer(gl.FRAMEBUFFER, self.framebuffer[0]) 80 | local w = self.width 81 | local h = self.height 82 | local buflen = w * h * 3 83 | local buf = ffi.new("uint8_t[?]", buflen) 84 | gl.readPixels(0, 0, w, h, gl.RGB, gl.UNSIGNED_BYTE, buf) 85 | local filename = "fbo" .. os.date("%Y%m%d_%H%M%S") .. ".png" 86 | png.flipImage(buf, w, h, 3) 87 | png.writePNG(filename, buf, w, h, 8, 3) 88 | gl.bindFramebuffer(gl.FRAMEBUFFER, 0) -- window system fb 89 | end 90 | 91 | -------------------------------------------------------------------------------- /LjES/GamePad.lua: -------------------------------------------------------------------------------- 1 | -- --------------------------------------------- 2 | -- GamePad.lua 2014/09/16 3 | -- Copyright (c) 2014 Jun Mizutani, 4 | -- released under the MIT open source license. 5 | -- --------------------------------------------- 6 | 7 | local joystick = require("joystick") 8 | local util = require("util") 9 | 10 | require("Object") 11 | 12 | GamePad = Object:new() 13 | 14 | GamePad.NumOfDevices = 0 15 | GamePad.Allocated = 0 16 | GamePad.A = 1 17 | GamePad.B = 2 18 | GamePad.X = 3 19 | GamePad.Y = 4 20 | GamePad.L = 5 21 | GamePad.R = 6 22 | GamePad.START = 7 23 | GamePad.SELECT = 8 24 | GamePad.DirX = 1 25 | GamePad.DirY = 2 26 | 27 | function GamePad.new(self) 28 | local obj = Object.new(self) 29 | obj.numDevice = 0 30 | obj.numAxes = 0 31 | obj.numButtons = 0 32 | obj.threshold = 1000 33 | if not joystick.initialized then 34 | GamePad.NumOfDevices = joystick.init() 35 | GamePad.Allocated = GamePad.Allocated + 1 36 | if GamePad.Allocated <= GamePad.NumOfDevices then 37 | obj.numDevice = GamePad.Allocated 38 | obj.numAxes = joystick.getNoOfAxes(obj.numDevice) 39 | obj.numButtons = joystick.getNoOfButtons(obj.numDevice) 40 | end 41 | end 42 | return obj 43 | end 44 | 45 | function GamePad.available(self) 46 | if self.numDevice > 0 then 47 | return true 48 | end 49 | return false 50 | end 51 | 52 | function GamePad.getNumOfAxes(self) 53 | return self.numAxes 54 | end 55 | 56 | function GamePad.getNumOfButtons(self) 57 | return self.numButtons 58 | end 59 | 60 | function GamePad.readEvents(self) 61 | return joystick.readAllEvents(self.numDevice) 62 | end 63 | 64 | function GamePad.checkButton(self, button) 65 | if button <= self.numButtons then 66 | if joystick.devices[self.numDevice].buttons[button].value ~= 0 then 67 | return true 68 | end 69 | end 70 | return false 71 | end 72 | 73 | function GamePad.checkAxis(self, axis) 74 | local THRES = self.threshold 75 | if axis <= self.numAxes then 76 | if joystick.devices[self.numDevice].axes[axis].value > THRES then 77 | return 1 78 | elseif joystick.devices[self.numDevice].axes[axis].value < -THRES then 79 | return -1 80 | end 81 | end 82 | return 0 83 | end 84 | 85 | function GamePad.list() 86 | local num_device = joystick.getNoOfDevices() 87 | for i = 1, num_device do 88 | util.printf("Name: %s\n", joystick.getName(i)) 89 | util.printf("Ver.: %s\n", joystick.getVersion(i)) 90 | local num_axes = joystick.getNoOfAxes(i) 91 | util.printf(" No. of Axes : %d\n", num_axes) 92 | local num_buttons = joystick.getNoOfButtons(i) 93 | util.printf(" No. of Buttons : %d\n", num_buttons) 94 | end 95 | end 96 | -------------------------------------------------------------------------------- /LjES/Mesh.lua: -------------------------------------------------------------------------------- 1 | -- --------------------------------------------- 2 | -- Mesh.lua 2014/06/05 3 | -- for collada.lua 4 | -- Copyright (c) 2014 Jun Mizutani, 5 | -- released under the MIT open source license. 6 | -- --------------------------------------------- 7 | 8 | require("Object") 9 | local util = require("util") 10 | 11 | Mesh = Object:new() 12 | 13 | function Mesh.new(self, frame) 14 | local obj = Object.new(self) 15 | obj.frame = frame 16 | obj.verts = {} 17 | obj.polygons = {} 18 | obj.texure_cood = {} 19 | obj.joint_names = {} 20 | obj.skinweights = {} 21 | obj.bind_shape_matrix = nil 22 | obj.nMaxSkinWeightsPerVertex = 0 23 | obj.nMaxSkinWeightsPerFace = 0 24 | obj.nBones = 0 25 | obj.hasNormals = false 26 | obj.normals = {} 27 | obj.box = { 28 | minx = 1.0E10, maxx = -1.0E10, 29 | miny = 1.0E10, maxy = -1.0E10, 30 | minz = 1.0E10, maxz = -1.0E10 31 | } 32 | 33 | return obj 34 | end 35 | 36 | function Mesh.setName(self, name) 37 | self.name = name 38 | end 39 | 40 | function Mesh.getName(self) 41 | return self.name 42 | end 43 | 44 | function Mesh.setVertices(self, verts) 45 | self.verts = verts 46 | end 47 | 48 | function Mesh.getVertices(self) 49 | return self.verts 50 | end 51 | 52 | function Mesh.setPolygons(self, polygons) 53 | self.polygons = polygons 54 | end 55 | 56 | function Mesh.getPolygons(self) 57 | return self.polygons 58 | end 59 | 60 | function Mesh.setTextureCoord(self, texure_coord) 61 | self.texure_cood = texure_coord 62 | end 63 | 64 | function Mesh.getTextureCoord(self) 65 | return self.texure_cood 66 | end 67 | 68 | function Mesh.setSkinWeights(self, skin_weights) 69 | -- skin_weights : 70 | -- { {bone_index, weight, bone_index, weight, ..}, .. } 71 | self.skinweights = skin_weights 72 | end 73 | 74 | function Mesh.getSkinWeights(self) 75 | return self.skinweights 76 | end 77 | 78 | function Mesh.setNormals(self, normals) 79 | self.hasNormals = true 80 | self.normals = normals 81 | end 82 | 83 | function Mesh.getNormals(self) 84 | return self.normals 85 | end 86 | 87 | function Mesh.setJointNames(self, joint_names) 88 | self.joint_names = joint_names 89 | end 90 | 91 | function Mesh.getJointNames(self) 92 | return self.joint_names 93 | end 94 | 95 | function Mesh.setBindPoseMatrices(self, bindPoseMatrices) 96 | self.bindPoseMatrices = bindPoseMatrices 97 | end 98 | 99 | -- get inverse bind shape matrices from collada file 100 | function Mesh.getBindPoseMatrices(self) 101 | return self.bindPoseMatrices 102 | end 103 | 104 | function Mesh.setBindShapeMatrix(self, bind_shape_matrix) 105 | self.bind_shape_matrix = bind_shape_matrix 106 | end 107 | 108 | function Mesh.getBindShapeMatrix(self) 109 | return self.bind_shape_matrix 110 | end 111 | 112 | function Mesh.updateBoundingBox(self, x, y, z) 113 | local box = self.box 114 | if box.minx > x then box.minx = x end 115 | if box.maxx < x then box.maxx = x end 116 | if box.miny > y then box.miny = y end 117 | if box.maxy < y then box.maxy = y end 118 | if box.minz > z then box.minz = z end 119 | if box.maxz < z then box.maxz = z end 120 | end 121 | 122 | function Mesh.printInfo(self) 123 | local verts = self.verts 124 | local normals = self.normals 125 | local tex_table = self.texure_cood 126 | local polygons = self.polygons 127 | local joint_names = self.joint_names 128 | local skinweights = self.skinweights 129 | local bind_shape_matrix = self.bind_shape_matrix 130 | 131 | util.printf("Mesh:---- %s ----\n",i , self.name) 132 | util.printf("vertices = %d\n", #verts/3) 133 | util.printf("normals = %d\n", #normals/3) 134 | for m = 1, #tex_table do 135 | local tex = tex_table[m] 136 | util.printf("texture = %d\n", #tex/2) 137 | end 138 | util.printf("polygons = %d\n", #polygons) 139 | 140 | util.printf("bind_shape_matrix\n") 141 | bind_shape_matrix:print() 142 | util.printf("bone count = %d\n", #joint_names) 143 | for n = 1, #joint_names do 144 | util.printf("[%3d] %s\n", n - 1, joint_names[n]) 145 | end 146 | 147 | for i = 0, #verts/3 - 1 do 148 | self:updateBoundingBox(verts[i*3+1], verts[i*3+2], verts[i*3+3]) 149 | end 150 | local bbox = self.box 151 | util.printf(" X: %10.5f -- %10.5f center:%10.5f, size:%10.5f\n", 152 | bbox.minx, bbox.maxx, (bbox.maxx+bbox.minx)/2, bbox.maxx - bbox.minx) 153 | util.printf(" Y: %10.5f -- %10.5f center:%10.5f, size:%10.5f\n", 154 | bbox.miny, bbox.maxy, (bbox.maxy+bbox.miny)/2, bbox.maxy - bbox.miny) 155 | util.printf(" Z: %10.5f -- %10.5f center:%10.5f, size:%10.5f\n", 156 | bbox.minz, bbox.maxz, (bbox.maxz+bbox.minz)/2, bbox.maxz - bbox.minz) 157 | end 158 | 159 | -------------------------------------------------------------------------------- /LjES/Message.lua: -------------------------------------------------------------------------------- 1 | -- --------------------------------------------- 2 | -- Message.lua 2014/07/24 3 | -- Copyright (c) 2013-2014 Jun Mizutani, 4 | -- released under the MIT open source license. 5 | -- --------------------------------------------- 6 | 7 | local ffi = require("ffi") 8 | local gl = require("gles2") 9 | require("Text") 10 | 11 | Message = Text:new() 12 | 13 | function Message.new(self) 14 | local obj = Text.new(self) 15 | obj.color = {1.0, 1.0, 1.0} 16 | return obj 17 | end 18 | 19 | function Message.init(self, font_texture_file) 20 | Text.init(self, font_texture_file) 21 | self.messages = {} 22 | self.last = 1 23 | end 24 | 25 | function Message.setMessage(self, n, x, y, text) 26 | self.messages[n] = {x, y, text, self.color} 27 | return n 28 | end 29 | 30 | function Message.writeMessage(self, x, y, text) 31 | self.last = self:setMessage(self.last, x, y, text) + 1 32 | return self.last 33 | end 34 | 35 | function Message.delMessage(self, n) 36 | self.messages[n] = nil 37 | end 38 | 39 | function Message.clearMessages(self) 40 | self.messages = {} 41 | self.last = 1 42 | end 43 | 44 | function Message.listMessages(self) 45 | for i,v in pairs(self.messages) do 46 | if v ~= nil then 47 | util.printf("%2d x:%2d y:%2d r:%2f g:%2f b:%2f %s\n", i, 48 | v[1], v[2], v[4][1], v[4][2], v[4][3], v[3]) 49 | end 50 | end 51 | end 52 | 53 | function Message.setColor(self, r, g, b) 54 | self.color = {r, g, b} 55 | end 56 | 57 | function Message.drawScreen(self) 58 | local x, y, str, color, c 59 | local shd = self.shader 60 | gl.useProgram(shd.program) 61 | gl.enable(gl.BLEND) 62 | gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA) 63 | gl.bindBuffer(gl.ARRAY_BUFFER, self.vbo) 64 | gl.enableVertexAttribArray(shd.aPosition) 65 | gl.enableVertexAttribArray(shd.aTexCoord) 66 | gl.vertexAttribPointer(shd.aPosition,3,gl.FLOAT,gl.FALSE,5*4, 67 | ffi.cast("const void *", 0)) 68 | gl.vertexAttribPointer(shd.aTexCoord,2,gl.FLOAT,gl.FALSE,5*4, 69 | ffi.cast("const void *", 3*4)) 70 | self.tex:active() 71 | for i,v in ipairs(self.messages) do 72 | x = v[1] 73 | y = v[2] 74 | str = v[3] 75 | color = v[4] 76 | shd:useProgram() 77 | if ((x >= 0) and (x < 80)) and ((y >= 0) and (y < 25)) then 78 | for j=1, #str do 79 | c = string.byte(str, j) 80 | if (c ~= 32) then 81 | shd:setColor(unpack(color)) 82 | shd:setChar(x+j-1, y, c) 83 | gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4) 84 | end 85 | end 86 | end -- if ((x>=0) and (x<80)) and ((y>=0) and (y<25)) 87 | end -- for i,v 88 | gl.disable(gl.BLEND) 89 | end 90 | 91 | -------------------------------------------------------------------------------- /LjES/Node.lua: -------------------------------------------------------------------------------- 1 | -- --------------------------------------------- 2 | -- Node.lua 2014/07/21 3 | -- Copyright (c) 2013-2014 Jun Mizutani, 4 | -- released under the MIT open source license. 5 | -- --------------------------------------------- 6 | 7 | require("CoordinateSystem") 8 | 9 | Node = CoordinateSystem:new(nil) -- inherit CoordinateSystem 10 | 11 | function Node.new(self, parent_bone, name) 12 | local obj = CoordinateSystem.new(self, parent_bone, name) 13 | obj.type = obj.NODE_T 14 | obj.modelViewMatrix = Matrix:new() 15 | obj.normalMatrix = Matrix:new() 16 | obj.attachable = false 17 | obj.restPos = {0.0, 0.0, 0.0} 18 | obj.restQuat = Quat:new() 19 | obj.restMatrix = Matrix:new() 20 | obj.bofMatrix = Matrix:new() 21 | obj.modelMatrix = Matrix:new() -- for rest position 22 | obj.hasWeights = false 23 | if parent_bone ~= nil then 24 | obj.rootBone = false 25 | else 26 | obj.rootBone = true 27 | end 28 | obj.shapes = {} 29 | obj.hideShape = true 30 | return obj 31 | end 32 | 33 | -- override CoordinateSystem.setParent(self, parent) 34 | function Node.setParent(self, parent) 35 | self.parent = parent 36 | if parent ~= nil then 37 | self.rootBone = false 38 | else 39 | self.rootBone = true 40 | end 41 | end 42 | 43 | function Node.hide(self, true_or_false) 44 | self.hideShape = true_or_false 45 | local shapes = self.shapes 46 | if #shapes > 0 then 47 | for i=1, #shapes do 48 | shapes[i]:hide(true_or_false) 49 | end 50 | end 51 | end 52 | 53 | function Node.setAttachable(self, true_or_false) 54 | self.attachable = true_or_false 55 | end 56 | 57 | function Node.detach(self) 58 | if (self.type == self.NODE_T) or self.attachable then 59 | CoordinateSystem.detach(self) 60 | end 61 | end 62 | 63 | function Node.attach(self, parent_node) 64 | if (self.type == self.NODE_T) or self.attachable then 65 | CoordinateSystem.attach(self, parent_node) 66 | end 67 | end 68 | 69 | function Node.setWeights(self) 70 | self.hasWeights = true 71 | end 72 | 73 | function Node.setRestPosition(self, x, y, z) 74 | self.restPos[1] = x 75 | self.restPos[2] = y 76 | self.restPos[3] = z 77 | self:setPosition(x, y, z) 78 | end 79 | 80 | function Node.setRestByMatrix(self, matrix) 81 | self.restMatrix:copyFrom(matrix) 82 | self.restQuat:matrixToQuat(matrix) 83 | self.restPos = self.matrix:getPosition() 84 | end 85 | 86 | function Node.rotateRest(self, head, pitch, bank) 87 | local qq = Quat:new() 88 | qq:eulerToQuat(head, pitch, bank) 89 | self.restQuat:mulQuat(qq) 90 | self.quat:copyFrom(self.restQuat) 91 | end 92 | 93 | function Node.moveRest(self, x, y, z) 94 | self:setRestMatrix() 95 | self.restPos = self.restMatrix:mulVector({x, y, z}) 96 | self:setPosition(unpack(self.restPos)) 97 | end 98 | 99 | function Node.setRestMatrix(self) 100 | self.restMatrix:setByQuat(self.restQuat) 101 | self.restMatrix:position(self.restPos) 102 | end 103 | 104 | -- for rest position 105 | function Node.setModelMatrixAll(self, mmat) 106 | self:setRestMatrix() 107 | self.modelMatrix:copyFrom(self.restMatrix) 108 | if self.parent ~= nil then 109 | self.modelMatrix:lmul(mmat) -- [Mn]=[J0]x[J1]x ..[Jn] 110 | end 111 | local children = self.children 112 | for j=1, #children do 113 | children[j]:setModelMatrixAll(self.modelMatrix) 114 | end 115 | self.bofMatrix:copyFrom(self.modelMatrix) 116 | self.bofMatrix:inverse() -- [Mn]^-1 117 | end 118 | 119 | -- for pose position 120 | -- overide CoordinateSystem.setWorldMatrixAll(self, wmat) 121 | function Node.setGlobalMatrixAll(self, wmat) 122 | -- self:setWorldMatrixAll(self, wmat) 123 | self.matrix:setByQuat(self.quat) 124 | self.matrix:position(self.position) 125 | self.worldMatrix:copyFrom(self.matrix) 126 | if self.rootBone == false and wmat ~= nil then 127 | self.worldMatrix:lmul(wmat) -- [Cn] = [Q0] x ... x [Qn] 128 | end 129 | local children = self.children 130 | for j=1, #children do 131 | children[j]:setWorldMatrixAll(self.worldMatrix) 132 | end 133 | end 134 | 135 | function Node.getRestMatrix(self) 136 | return self.restMatrix:clone() 137 | end 138 | 139 | function Node.getModelMatrix(self) 140 | return self.modelMatrix:clone() 141 | end 142 | 143 | function Node.getBofMatrix(self) 144 | return self.bofMatrix:clone() 145 | end 146 | 147 | function Node.getGlobalMatrix(self) 148 | return self:getWorldMatrix() 149 | end 150 | 151 | function Node.addShape(self, shape) 152 | if shape == nil then 153 | util.printf("Error, try to add nil shape to %s.", self.name) 154 | return 155 | end 156 | table.insert(self.shapes, shape) 157 | if shape.skeleton == nil then return end 158 | local bones = shape.skeleton.bones 159 | if #bones > 0 then 160 | for i = 1, #bones do 161 | if bones[i].parent == nil then 162 | bones[i].parent = self 163 | self:addChild(bones[i]) 164 | end 165 | end 166 | end 167 | end 168 | 169 | function Node.delShape(self) 170 | table.remove(self.shapes) 171 | end 172 | 173 | function Node.setShape(self, shape) 174 | self.shapes = {} 175 | self:addShape(shape) 176 | end 177 | 178 | function Node.getShape(self, n) 179 | if (n > #self.shapes) or (n <= 0) then 180 | return nil 181 | else 182 | return self.shapes[n] 183 | end 184 | end 185 | 186 | function Node.getShapeCount(self) 187 | return #self.shapes 188 | end 189 | 190 | function Node.draw(self, view_matrix, light_vec) 191 | if (self.type == self.BONE_T) and not self.attachable then 192 | return 193 | end 194 | 195 | local modelview = self.modelViewMatrix 196 | local normal = self.normalMatrix 197 | self:setMatrix() 198 | modelview:copyFrom(self.matrix) 199 | modelview:lmul(view_matrix) -- eye = [view] * [model] * local 200 | normal:copyFrom(modelview) 201 | normal:position({0, 0, 0}) 202 | 203 | if self.type == self.NODE_T then 204 | local shapes = self.shapes 205 | for i=1, #shapes do 206 | if light_vec ~= nil then 207 | shapes[i]:shaderParameter("light", light_vec) 208 | end 209 | shapes[i]:draw(modelview, normal) 210 | end 211 | end 212 | 213 | local children = self.children 214 | for j=1, #children do 215 | children[j]:draw(modelview, light_vec) 216 | end 217 | end 218 | 219 | function Node.drawBones(self) 220 | local modelview = self.modelViewMatrix 221 | local normal = self.normalMatrix 222 | 223 | if self.type == self.BONE_T then 224 | local shapes = self.shapes 225 | for i=1, #shapes do 226 | shapes[i]:draw(modelview, normal) 227 | end 228 | end 229 | local children = self.children 230 | for j=1, #children do 231 | children[j]:drawBones() 232 | end 233 | end 234 | -------------------------------------------------------------------------------- /LjES/Object.lua: -------------------------------------------------------------------------------- 1 | -- --------------------------------------------- 2 | -- Object.lua 2014/02/07 3 | -- Copyright (c) 2013-2014 Jun Mizutani, 4 | -- released under the MIT open source license. 5 | -- --------------------------------------------- 6 | 7 | Object = {} 8 | 9 | function Object.new(self) 10 | local obj = {} 11 | setmetatable(obj, obj) 12 | obj.__index = self 13 | return obj 14 | end 15 | 16 | -------------------------------------------------------------------------------- /LjES/Phong.lua: -------------------------------------------------------------------------------- 1 | -- --------------------------------------------- 2 | -- Phong.lua 2014/06/05 3 | -- Copyright (c) 2013-2014 Jun Mizutani, 4 | -- released under the MIT open source license. 5 | -- --------------------------------------------- 6 | 7 | local ffi = require("ffi") 8 | local gl = require("gles2") 9 | require("Shader") 10 | 11 | Phong = Shader:new() 12 | 13 | function Phong.new(self) 14 | local obj = Shader.new(self) 15 | obj.uProjMatrix = 0 16 | obj.uMVMatrix = 0 17 | obj.uNormalMatrix = 0 18 | obj.uTexUnit = 0 19 | obj.uTexFlag = 0 20 | obj.uEmit = 0 21 | obj.uAmb = 0 22 | obj.uSpec = 0 23 | obj.uSpecPower= 0 24 | obj.uLightPos = 0 25 | obj.uColor = 0 26 | obj.aPosition = 0 27 | obj.aNormal = 0 28 | obj.aTexCoord = 0 29 | obj.default = {color = {0.8, 0.8, 1.0, 1.0}, tex_unit = 0, 30 | light = {0.0, 0.0, 100.0, 1}, use_texture = 0, 31 | emissive = 0, ambient = 0.3, specular = 0.6, power = 40 } 32 | obj.change = {} 33 | return obj 34 | end 35 | 36 | Phong.vShaderSrc = [[ 37 | attribute vec3 aPosition; 38 | attribute vec3 aNormal; 39 | attribute vec2 aTexCoord; 40 | 41 | varying vec3 vPosition; 42 | varying vec3 vNormal; 43 | varying vec2 vTexCoord; 44 | 45 | uniform mat4 uProjMatrix; 46 | uniform mat4 uMVMatrix; 47 | uniform mat4 uNormalMatrix; 48 | 49 | void main(void) { 50 | gl_Position = uProjMatrix * uMVMatrix * vec4(aPosition, 1.0); 51 | 52 | vTexCoord = aTexCoord; 53 | vPosition = vec3(uMVMatrix * vec4(aPosition, 1.0)); 54 | 55 | vNormal = vec3(uNormalMatrix * vec4(aNormal, 1)); 56 | } 57 | ]] 58 | 59 | Phong.fShaderSrc = [[ 60 | precision mediump float; 61 | 62 | varying vec3 vPosition; 63 | varying vec3 vNormal; 64 | varying vec2 vTexCoord; 65 | uniform sampler2D uTexUnit; 66 | uniform int uTexFlag; 67 | uniform int uEmit; 68 | uniform float uAmb; 69 | uniform float uSpec; 70 | uniform float uSpecPower; 71 | uniform vec4 uLightPos; 72 | uniform vec4 uColor; 73 | 74 | void main(void) { 75 | vec4 color; 76 | vec3 lit_vec; 77 | float diff; 78 | float Ispec; 79 | vec4 white = vec4(1.0, 1.0, 1.0, 1.0); 80 | vec3 nnormal = normalize(vNormal); 81 | 82 | if (uLightPos.w!=0.0) { 83 | lit_vec = normalize(uLightPos.xyz - vPosition); 84 | } else { 85 | lit_vec = normalize(uLightPos.xyz); 86 | } 87 | vec3 eye_vec = normalize(-vPosition); 88 | vec3 ref_vec = normalize(reflect(-lit_vec, nnormal)); 89 | if (uEmit == 0) { 90 | diff = max(dot(nnormal, lit_vec), 0.0) * (1.0 - uAmb); 91 | Ispec = uSpec * pow(max(dot(ref_vec, eye_vec), 0.0), uSpecPower); 92 | } else { 93 | diff = 1.0 - uAmb; 94 | Ispec = 0.0; 95 | } 96 | if (uTexFlag != 0) { 97 | color = uColor * texture2D(uTexUnit,vec2(vTexCoord.s,vTexCoord.t)); 98 | color = mix(diff * uColor, color, uColor.w); 99 | } else { 100 | color = uColor; 101 | } 102 | gl_FragColor = vec4(color.rgb * (uAmb+diff) + white.rgb * Ispec,1); 103 | } 104 | ]] 105 | 106 | function Phong.initShaderParameter(self) 107 | self:useProgram() 108 | local prog = self.program 109 | self.uProjMatrix = gl.getUniformLocation(prog, "uProjMatrix") 110 | self.uMVMatrix = gl.getUniformLocation(prog, "uMVMatrix") 111 | self.uNormalMatrix = gl.getUniformLocation(prog, "uNormalMatrix") 112 | self.uTexUnit = gl.getUniformLocation(prog, "uTexUnit") 113 | self.uTexFlag = gl.getUniformLocation(prog, "uTexFlag") 114 | self.uEmit = gl.getUniformLocation(prog, "uEmit") 115 | self.uAmb = gl.getUniformLocation(prog, "uAmb") 116 | self.uSpec = gl.getUniformLocation(prog, "uSpec") 117 | self.uSpecPower = gl.getUniformLocation(prog, "uSpecPower") 118 | self.uLightPos = gl.getUniformLocation(prog, "uLightPos") 119 | self.uColor = gl.getUniformLocation(prog, "uColor") 120 | self.aPosition = gl.getAttribLocation(prog, "aPosition") 121 | self.aNormal = gl.getAttribLocation(prog, "aNormal") 122 | self.aTexCoord = gl.getAttribLocation(prog, "aTexCoord") 123 | end 124 | 125 | function Phong.init(self) 126 | self.status = self:initShaders() 127 | self:initShaderParameter() 128 | self:useProgram() 129 | self:setLightPosition(self.default.light) 130 | self:useTexture(self.default.use_texture) 131 | self:setColor(self.default.color) 132 | self:setEmissive(self.default.emissive) 133 | self:setAmbientLight(self.default.ambient) 134 | self:setSpecular(self.default.specular) 135 | self:setSpecularPower(self.default.power) 136 | end 137 | 138 | function Phong.setDefaultParam(self, key, value) 139 | self.default[key] = value 140 | if key == "color" then self:setColor(value) 141 | elseif key == "tex_unit" then self:setTextureUnit(value) 142 | elseif key == "use_texture" then self:useTexture(value) 143 | elseif key == "light" then self:setLightPosition(value) 144 | elseif key == "emissive" then self:setEmissive(value) 145 | elseif key == "ambient" then self:setAmbientLight(value) 146 | elseif key == "specular" then self:setSpecular(value) 147 | elseif key == "power" then self:setSpecularPower(value) 148 | end 149 | end 150 | 151 | function Phong.setLightPosition(self, positionAndType) 152 | -- positionAndType : {x, y, z, type} 153 | -- type : 1/parallel light, 0/point light 154 | self:useProgram() 155 | gl.uniform4f(self.uLightPos, unpack(positionAndType)) 156 | end 157 | 158 | function Phong.useTexture(self, flag) 159 | self:useProgram() 160 | gl.uniform1i(self.uTexFlag, flag) 161 | self.textureFlag = flag 162 | end 163 | 164 | function Phong.setTextureUnit(self, tex_unit) 165 | self:useProgram() 166 | gl.uniform1i(self.uTexUnit, tex_unit) 167 | end 168 | 169 | function Phong.setEmissive(self, flag) 170 | self:useProgram() 171 | gl.uniform1i(self.uEmit, flag) 172 | end 173 | 174 | function Phong.setAmbientLight(self, intensity) 175 | self:useProgram() 176 | gl.uniform1f(self.uAmb, intensity) 177 | end 178 | 179 | function Phong.setSpecular(self, intensity) 180 | self:useProgram() 181 | gl.uniform1f(self.uSpec, intensity) 182 | end 183 | 184 | function Phong.setSpecularPower(self, power) 185 | self:useProgram() 186 | gl.uniform1f(self.uSpecPower, power) 187 | end 188 | 189 | function Phong.setColor(self, color) 190 | -- color : {r, g, b, a} 191 | self:useProgram() 192 | gl.uniform4f(self.uColor, unpack(color)) 193 | end 194 | 195 | function Phong.setProjectionMatrix(self, m) 196 | self:useProgram() 197 | gl.uniformMatrix4fv(self.uProjMatrix, 1, 0, m:convFloat()) 198 | end 199 | 200 | function Phong.setModelViewMatrix(self, m) 201 | self:useProgram() 202 | gl.uniformMatrix4fv(self.uMVMatrix, 1, 0, m:convFloat()) 203 | end 204 | 205 | function Phong.setNormalMatrix(self, m) 206 | self:useProgram() 207 | gl.uniformMatrix4fv(self.uNormalMatrix, 1, 0, m:convFloat()) 208 | end 209 | 210 | function Phong.updateTexture(self, param) 211 | if (param.texture ~= nil) then 212 | self.change.texture = param.texture 213 | param.texture:active() 214 | self:setTextureUnit(0) 215 | end 216 | end 217 | 218 | function Phong.doParameter(self, param) 219 | self:updateParam(param, "color", self.setColor) 220 | self:updateParam(param, "light", self.setLightPosition) 221 | self:updateParam(param, "use_texture", self.useTexture) 222 | self:updateParam(param, "ambient", self.setAmbientLight) 223 | self:updateParam(param, "specular", self.setSpecular) 224 | self:updateParam(param, "power", self.setSpecularPower) 225 | self:updateParam(param, "emissive", self.setEmissive) 226 | self:updateTexture(param) 227 | end 228 | -------------------------------------------------------------------------------- /LjES/Quat.lua: -------------------------------------------------------------------------------- 1 | -- --------------------------------------------- 2 | -- Quat.lua 2014/06/05 3 | -- Copyright (c) 2014 Jun Mizutani, 4 | -- released under the MIT open source license. 5 | -- --------------------------------------------- 6 | 7 | local ffi = require("ffi") 8 | local util = require("util") 9 | 10 | require("Object") 11 | 12 | local function RAD(degree) 13 | return degree * math.pi / 180.0 14 | end 15 | 16 | local function DEG(radian) 17 | return radian * 180.0 / math.pi 18 | end 19 | 20 | Quat = Object:new() 21 | 22 | function Quat.new(self) 23 | local obj = Object.new(self) 24 | obj.q = ffi.new("double[?]", 4, 1.0, 0.0, 0.0, 0.0) 25 | return obj 26 | end 27 | 28 | -- qa = qa * qb 29 | function Quat.mulQuat(self, qb) 30 | local q0 = self.q[0] 31 | local q1 = self.q[1] 32 | local q2 = self.q[2] 33 | local q3 = self.q[3] 34 | local b0 = qb.q[0] 35 | local b1 = qb.q[1] 36 | local b2 = qb.q[2] 37 | local b3 = qb.q[3] 38 | self.q[0] = q0 * b0 - q1 * b1 - q2 * b2 - q3 * b3 39 | self.q[1] = q0 * b1 + q1 * b0 + q2 * b3 - q3 * b2 40 | self.q[2] = q0 * b2 + q2 * b0 - q1 * b3 + q3 * b1 41 | self.q[3] = q0 * b3 + q3 * b0 + q1 * b2 - q2 * b1 42 | end 43 | 44 | -- qa = qb * qa 45 | function Quat.lmulQuat(self, qb) 46 | local q0 = self.q[0] 47 | local q1 = self.q[1] 48 | local q2 = self.q[2] 49 | local q3 = self.q[3] 50 | local b0 = qb.q[0] 51 | local b1 = qb.q[1] 52 | local b2 = qb.q[2] 53 | local b3 = qb.q[3] 54 | self.q[0] = b0 * q0 - b1 * q1 - b2 * q2 - b3 * q3 55 | self.q[1] = b0 * q1 + b1 * q0 + b2 * q3 - b3 * q2 56 | self.q[2] = b0 * q2 + b2 * q0 - b1 * q3 + b3 * q1 57 | self.q[3] = b0 * q3 + b3 * q0 + b1 * q2 - b2 * q1 58 | end 59 | 60 | -- q0 + q1i + q2j + q3k (w = q0) 61 | function Quat.condugate(self) 62 | -- self.q[0] = self.q[0] 63 | self.q[1] = -self.q[1] 64 | self.q[2] = -self.q[2] 65 | self.q[3] = -self.q[3] 66 | end 67 | 68 | function Quat.normalize(self) 69 | local q0 = self.q[0] 70 | local q1 = self.q[1] 71 | local q2 = self.q[2] 72 | local q3 = self.q[3] 73 | 74 | local s = math.sqrt(q0*q0 + q1*q1 + q2*q2 + q3*q3) 75 | self.q[0] = q0 / s 76 | self.q[1] = q1 / s 77 | self.q[2] = q2 / s 78 | self.q[3] = q3 / s 79 | end 80 | 81 | function Quat.setRotateX(self, degree) 82 | local r = RAD(degree) * 0.5 83 | self.q[0] = math.cos(r) 84 | self.q[1] = math.sin(r) 85 | self.q[2] = 0.0 86 | self.q[3] = 0.0 87 | end 88 | 89 | function Quat.setRotateY(self, degree) 90 | local r = RAD(degree) * 0.5 91 | self.q[0] = math.cos(r) 92 | self.q[1] = 0.0 93 | self.q[2] = math.sin(r) 94 | self.q[3] = 0.0 95 | end 96 | 97 | function Quat.setRotateZ(self, degree) 98 | local r = RAD(degree) * 0.5 99 | self.q[0] = math.cos(r) 100 | self.q[1] = 0.0 101 | self.q[2] = 0.0 102 | self.q[3] = math.sin(r) 103 | end 104 | 105 | -- convert into Quaternion from Euler Angle(Eest Up North) 106 | function Quat.eulerToQuat(self, head, pitch, bank) 107 | local cosB = math.cos(RAD(bank ) * 0.5) 108 | local cosP = math.cos(RAD(pitch) * 0.5) 109 | local cosH = math.cos(RAD(head ) * 0.5) 110 | local sinB = math.sin(RAD(bank ) * 0.5) 111 | local sinP = math.sin(RAD(pitch) * 0.5) 112 | local sinH = math.sin(RAD(head ) * 0.5) 113 | 114 | local cosBcosP = cosB * cosP 115 | local sinBsinP = sinB * sinP 116 | local cosBsinP = cosB * sinP 117 | local sinBcosP = sinB * cosP 118 | 119 | self.q[0] = cosBcosP * cosH - sinBsinP * sinH 120 | self.q[1] = cosBsinP * cosH - sinBcosP * sinH 121 | self.q[2] = cosBcosP * sinH + sinBsinP * cosH 122 | self.q[3] = sinBcosP * cosH + cosBsinP * sinH 123 | end 124 | 125 | function Quat.dotProduct(self, qr) 126 | local dp = 0 127 | for i=0, 3 do 128 | dp = dp + self.q[i] * qr.q[i] 129 | end 130 | return dp 131 | end 132 | 133 | function Quat.negate(self) 134 | local q = self.q 135 | q[0] = -q[0] 136 | q[1] = -q[1] 137 | q[2] = -q[2] 138 | q[3] = -q[3] 139 | end 140 | 141 | -- Spherical Linear Iterporation 142 | -- a,b : quaternion, t : 0.0 - 1.0 143 | -- aQuat:slerp(a, b, t) 144 | function Quat.slerp(self, a, b, t) 145 | local cosp = a.q[0]*b.q[0] + a.q[1]*b.q[1] + a.q[2]*b.q[2] + a.q[3]*b.q[3] 146 | local p = math.acos(cosp) 147 | local sinp = math.sin(p) 148 | 149 | local s = sinp 150 | if (sinp < 0.0) then s = -sinp end 151 | 152 | if (s > 0.0002) then -- 0.01146 degree 153 | local scale0 = math.sin((1.0 - t) * p) / sinp 154 | local scale1 = math.sin(t * p) / sinp 155 | for i=0, 3 do 156 | self.q[i] = scale0 * a.q[i] + scale1 * b.q[i] 157 | end 158 | else 159 | for i=0, 3 do 160 | self.q[i] = b.q[i] 161 | end 162 | end 163 | end 164 | 165 | function Quat.matrixToQuat(self, m) 166 | local S 167 | if (m.mat[0] + m.mat[5] + m.mat[10] > -1.0) then 168 | self.q[0] = math.sqrt(m.mat[0] + m.mat[5] + m.mat[10] + 1)/2 169 | S = 4 * self.q[0] 170 | if (S ~= 0.0) then 171 | self.q[1] = (m.mat[6] - m.mat[9])/S 172 | self.q[2] = (m.mat[8] - m.mat[2])/S 173 | self.q[3] = (m.mat[1] - m.mat[4])/S 174 | end 175 | else 176 | local i = 0 177 | if (m.mat[5] > m.mat[0]) then 178 | i=1 179 | if (m.mat[10] > m.mat[5]) then i=2 end 180 | elseif (m.mat[10] > m.mat[0]) then i=2 end 181 | 182 | if (i==0) then 183 | self.q[1] = math.sqrt(1 + m.mat[0] - m.mat[5] - m.mat[10])/2 184 | S = 4 * self.q[1] 185 | if (S ~= 0.0) then 186 | self.q[0] = (m.mat[6] - m.mat[9])/S 187 | self.q[2] = (m.mat[4] + m.mat[1])/S 188 | self.q[3] = (m.mat[8] + m.mat[2])/S 189 | end 190 | elseif (i==1) then 191 | self.q[2] = math.sqrt(1 -m.mat[0] + m.mat[5] - m.mat[10])/2 192 | S = 4 * self.q[2] 193 | if (S ~= 0.0) then 194 | self.q[0] = (m.mat[8]-m.mat[2])/S 195 | self.q[1] = (m.mat[4]+m.mat[1])/S 196 | self.q[3] = (m.mat[9]+m.mat[6])/S 197 | end 198 | elseif (i==2) then 199 | self.q[3] = math.sqrt(1 -m.mat[0] - m.mat[5] + m.mat[10])/2 200 | S = 4 * self.q[3] 201 | if (S ~= 0.0) then 202 | self.q[0] = (m.mat[1] - m.mat[4])/S 203 | self.q[1] = (m.mat[8] + m.mat[2])/S 204 | self.q[2] = (m.mat[9] + m.mat[6])/S 205 | end 206 | end 207 | end 208 | self:normalize() 209 | end 210 | 211 | function Quat.print(self) 212 | local q = self.q 213 | util.printf("% 16.11e % 16.11e % 16.11e % 16.11e\n", q[0], q[1], q[2], q[3]) 214 | end 215 | 216 | function Quat.clone(self) 217 | local quat = Quat:new() 218 | for i=0,3 do quat.q[i] = self.q[i] end 219 | return quat 220 | end 221 | 222 | function Quat.copyFrom(self, quat) 223 | for i=0,3 do self.q[i] = quat.q[i] end 224 | end 225 | 226 | function Quat.check(self) 227 | local q = self.q 228 | local nan = false 229 | for i=0,3 do 230 | if (q[i]~=q[i]) then nan = true break end 231 | end 232 | if nan then 233 | self:print() 234 | assert(false, "NaN") 235 | end 236 | end 237 | 238 | function Quat.quatToEuler(self) 239 | local mat = Matrix:new() 240 | mat:setByQuat(self) 241 | return mat:matToEuler() -- return head, pitch, bank 242 | end 243 | -------------------------------------------------------------------------------- /LjES/Schedule.lua: -------------------------------------------------------------------------------- 1 | -- --------------------------------------------- 2 | -- Schedule.lua 2014/06/06 3 | -- Copyright (c) 2014 Jun Mizutani, 4 | -- released under the MIT open source license. 5 | -- --------------------------------------------- 6 | 7 | require("Task") 8 | local util = require("util") 9 | 10 | Schedule = Object:new() 11 | 12 | function Schedule.new(self) 13 | local obj = Object.new(self) 14 | obj.last = 0 15 | obj.sequenceNo = 0 16 | obj.pause = false 17 | obj.stopped = false 18 | obj.tasks = {} 19 | return obj 20 | end 21 | 22 | function Schedule.addTask(self, name) 23 | self.sequenceNo = self.sequenceNo + 1 24 | local task = Task:new(name, self.sequenceNo) 25 | local n = self:getEmptyTask() 26 | if n then 27 | self.tasks[n] = task 28 | else 29 | table.insert(self.tasks, task) -- no empty slot 30 | end 31 | return task 32 | end 33 | 34 | function Schedule.delTask(self, task) 35 | for i=1, #self.tasks do 36 | if self.tasks[i] == task then 37 | self.tasks[i] = 0 38 | end 39 | end 40 | end 41 | 42 | function Schedule.getEmptyTask(self) 43 | for i=1, #self.tasks do 44 | if self.tasks[i] == 0 then return i end 45 | end 46 | return nil -- no empty slot 47 | end 48 | 49 | function Schedule.getNoOfTasks(self) 50 | return #self.tasks 51 | end 52 | 53 | function Schedule.getTask(self, n) 54 | if (n > 0) and (n <= #self.tasks) then 55 | return self.tasks[i] 56 | else 57 | return nil 58 | end 59 | end 60 | 61 | function Schedule.getTaskByName(self, name) 62 | for i = 1, #self.tasks do 63 | if self.tasks[i] ~= 0 then 64 | if (self.tasks[i]:getName() == name) then 65 | return self.tasks[i] 66 | end 67 | end 68 | end 69 | return nil 70 | end 71 | 72 | function Schedule.pause(self) 73 | self.pause = true 74 | end 75 | 76 | function Schedule.start(self) 77 | self:startFromTo(1, -1) 78 | end 79 | 80 | function Schedule.startFrom(self, start_ip) 81 | self:startFromTo(start_ip, -1) 82 | end 83 | 84 | function Schedule.startFromTo(self, start_ip, stop_ip) 85 | self.stopped = false 86 | self.pause = false 87 | for i = 1, #self.tasks do 88 | if self.tasks[i] ~= 0 then 89 | self.tasks[i]:startFromTo(start_ip, stop_ip) 90 | end 91 | end 92 | end 93 | 94 | function Schedule.doCommandFps(self, frame_per_sec) 95 | local ip = -1 96 | local delta_msec = 1000 / frame_per_sec -- msec 97 | for i = 1, #self.tasks do 98 | if self.tasks[i] ~= 0 then 99 | ip = self.tasks[i]:execute(delta_msec) 100 | end 101 | end 102 | return ip 103 | end 104 | 105 | function Schedule.doCommand(self) 106 | local ip 107 | local running_ip = -1 108 | if self.stopped or self.pause then 109 | self.last = util.now() 110 | return -1 111 | end 112 | if self.last == 0 then 113 | self.last = util.now() 114 | end 115 | local new = util.now() 116 | local delta_msec = (new - self.last) * 1000 117 | self.last = new 118 | if not self.stopped then 119 | for i = 1, #self.tasks do 120 | if self.tasks[i] ~= 0 then 121 | ip = self.tasks[i]:execute(delta_msec) 122 | if ip > 0 then running_ip = ip end 123 | end 124 | end 125 | end 126 | if running_ip < 0 then self.stopped = true end 127 | return running_ip -- when stopped, return -1 128 | end 129 | 130 | -- rate : 0.0 - 1.0 131 | function Schedule.doOneCommand(self, ip, rate) 132 | for i = 1, #self.tasks do 133 | if self.tasks[i] ~= 0 then 134 | self.tasks[i]:executeOneCommand(ip, rate) 135 | end 136 | end 137 | end 138 | 139 | function Schedule.directExecution(self, time, command, args, start_ip, stop_ip) 140 | for i = 1, #self.tasks do 141 | if time > 0 then 142 | self.stopped = false 143 | self.pause = false 144 | self.tasks[i]:insertCurrentCommand(time, command, {args[i]}, 145 | start_ip, stop_ip) 146 | else 147 | self.tasks[i]:directExecution(command, {args[i]}) 148 | end 149 | end 150 | end 151 | 152 | -- time_scale = 1.0:normal, 0.5:fast, 2.0:slow 153 | function Schedule.setSpeed(self, time_scale) 154 | for i = 1, #self.tasks do 155 | if self.tasks[i] ~= 0 then 156 | self.tasks[i].time_scale = time_scale 157 | end 158 | end 159 | end 160 | -------------------------------------------------------------------------------- /LjES/Screen.lua: -------------------------------------------------------------------------------- 1 | -- --------------------------------------------- 2 | -- Screen.lua 2014/10/13 3 | -- Copyright (c) 2013-2014 Jun Mizutani, 4 | -- released under the MIT open source license. 5 | -- --------------------------------------------- 6 | 7 | local ffi = require("ffi") 8 | local bit = require("bit") 9 | 10 | local gl = require("gles2") 11 | local bcm = require("bcm") 12 | local egl = require("egl") 13 | local png = require("png") 14 | local util = require("util") 15 | 16 | require("Object") 17 | 18 | Screen = Object:new() 19 | 20 | ffi.cdef[[ 21 | 22 | struct fb_fix_screeninfo { 23 | char id[16]; 24 | unsigned long smem_start; 25 | unsigned int smem_len; 26 | unsigned int type; 27 | unsigned int type_aux; 28 | unsigned int visual; 29 | unsigned short xpanstep; 30 | unsigned short ypanstep; 31 | unsigned short ywrapstep; 32 | unsigned int line_length; 33 | unsigned long mmio_start; 34 | unsigned int mmio_len; 35 | unsigned int accel; 36 | unsigned short reserved[3]; 37 | }; 38 | 39 | struct fb_bitfield { 40 | unsigned int offset; 41 | unsigned int length; 42 | unsigned int msb_right; 43 | }; 44 | 45 | struct fb_var_screeninfo { 46 | unsigned int xres; 47 | unsigned int yres; 48 | unsigned int xres_virtual; 49 | unsigned int yres_virtual; 50 | unsigned int xoffset; 51 | unsigned int yoffset; 52 | unsigned int bits_per_pixel; 53 | unsigned int grayscale; 54 | struct fb_bitfield red; 55 | struct fb_bitfield green; 56 | struct fb_bitfield blue; 57 | struct fb_bitfield transp; 58 | unsigned int nonstd; 59 | unsigned int activate; 60 | unsigned int height; 61 | unsigned int width; 62 | unsigned int accel_flags; 63 | unsigned int pixclock; 64 | unsigned int left_margin; 65 | unsigned int right_margin; 66 | unsigned int upper_margin; 67 | unsigned int lower_margin; 68 | unsigned int hsync_len; 69 | unsigned int vsync_len; 70 | unsigned int sync; 71 | unsigned int vmode; 72 | unsigned int rotate; 73 | unsigned int reserved[5]; 74 | }; 75 | 76 | int ioctl(int, unsigned long, ...); 77 | int open(const char* filename, int flags); 78 | int close(int fd); 79 | ]] 80 | 81 | -- get framebufer resolution for checking overscan. 82 | function Screen.fbinfo(self) 83 | local FBIOGET_VSCREENINFO = 17920 -- 0x4600 84 | local FBIOPUT_VSCREENINFO = 17921 -- 0x4601 85 | local FBIOGET_FSCREENINFO = 17922 -- 0x4602 86 | local O_RDONLY = 0 87 | 88 | local fsinfo = ffi.new("struct fb_fix_screeninfo[1]") 89 | local vsinfo = ffi.new("struct fb_var_screeninfo[1]") 90 | local fd = ffi.C.open("/dev/fb0", O_RDONLY) 91 | ffi.C.ioctl(fd, FBIOGET_FSCREENINFO, fsinfo) 92 | ffi.C.ioctl(fd, FBIOGET_VSCREENINFO, vsinfo) 93 | self.fsinfo = fsinfo[0] 94 | self.vsinfo = vsinfo[0] 95 | util.printf("x-res:%d\n", self.vsinfo.xres) 96 | util.printf("y-res:%d\n", self.vsinfo.yres) 97 | ffi.C.close(fd) 98 | end 99 | 100 | function Screen.new(self) 101 | local obj = Object.new(self) 102 | obj.fullWidth = 0 103 | obj.fullHeight = 0 104 | obj.width = 0 105 | obj.height = 0 106 | obj.x = 0 107 | obj.y = 0 108 | obj.nativewindow = nil 109 | obj.majorVersion = 0 110 | obj.minorVersion = 0 111 | obj.display = 0 112 | obj.surface = 0 113 | obj.overscan = false 114 | obj.overscanX = 0 115 | obj.overscanY = 0 116 | obj.clearColor = {0.0, 0.0, 0.0, 1.0} 117 | obj.fsinfo = {} 118 | obj.vsinfo = {} 119 | return obj 120 | end 121 | 122 | function Screen.checkOverscan(self) 123 | local size = {} 124 | 125 | if self.vsinfo.xres < self.fullWidth then 126 | self.overscanX = (self.fullWidth - self.vsinfo.xres) / 2 127 | self.fullWidth = self.vsinfo.xres 128 | self.overscan = true 129 | end 130 | if self.vsinfo.yres < self.fullHeight then 131 | self.overscanY = (self.fullHeight - self.vsinfo.yres) / 2 132 | self.fullHeight = self.vsinfo.yres 133 | self.overscan = true 134 | end 135 | end 136 | 137 | function Screen.checkSize(self, w, h, x, y) 138 | local size = {} 139 | 140 | if (w > 0) then 141 | if w < self.fullWidth then 142 | size.width = w 143 | else 144 | size.width = self.fullWidth 145 | if self.overscan then 146 | size.x = self.overscanX 147 | else 148 | size.x = 0 149 | end 150 | end 151 | elseif (w == 0) then 152 | size.width = self.fullWidth 153 | size.x = 0 154 | elseif (w < 0) then 155 | size.width = self.fullWidth + w 156 | x = - w/2 157 | if size.width <=0 then 158 | size.width = self.fullWidth 159 | size.x = 0 160 | end 161 | end 162 | 163 | if (h > 0) then 164 | if h < self.fullHeight then 165 | size.height = h 166 | else 167 | size.height = self.fullHeight 168 | size.y = 0 169 | end 170 | elseif (h == 0) then 171 | size.height = self.fullHeight 172 | size.y = 0 173 | elseif (h < 0) then 174 | size.height = self.fullHeight + h 175 | y = - h/2 176 | if size.height <= 0 then 177 | size.height =self.fullHeight 178 | size.y = 0 179 | end 180 | end 181 | 182 | if (x < 0) then 183 | size.x = (self.fullWidth - size.width) / 2 184 | else 185 | size.x = x 186 | end 187 | 188 | if (y < 0) then 189 | size.y = (self.fullHeight - size.height) / 2 190 | else 191 | size.y = y 192 | end 193 | 194 | if self.overscan then 195 | size.x = size.x + self.overscanX 196 | size.y = size.y + self.overscanY 197 | end 198 | 199 | return size.width, size.height, size.x, size.y 200 | end 201 | 202 | function Screen.bcm_init(self, w, h, x, y) 203 | bcm.bcm_host_init() 204 | self.init = {width = w, height = h, offsetx = x, offsety = y} 205 | 206 | local ww = ffi.new("uint32_t[1]") 207 | local hh = ffi.new("uint32_t[1]") 208 | local s = bcm.graphics_get_display_size(0, ww, hh) 209 | self.fullWidth = ww[0] 210 | self.fullHeight = hh[0] 211 | 212 | self:checkOverscan() 213 | self.width, self.height, self.x, self.y = self:checkSize(w, h, x, y) 214 | 215 | local VC_DISPMANX_ALPHA_T = ffi.typeof("VC_DISPMANX_ALPHA_T") 216 | local EGL_DISPMANX_WINDOW_T = ffi.typeof("EGL_DISPMANX_WINDOW_T") 217 | 218 | local dst_rect = bcm.VC_RECT_T(self.x, self.y, self.width, self.height) 219 | local src_rect = bcm.VC_RECT_T(0, 0, bit.lshift(self.width, 16), 220 | bit.lshift(self.height,16)) 221 | local dispman_display = bcm.vc_dispmanx_display_open(0) 222 | local dispman_update = bcm.vc_dispmanx_update_start(0) 223 | 224 | -- local alpha = VC_DISPMANX_ALPHA_T(1, 255, 0) 225 | -- local dispman_element = bcm.vc_dispmanx_element_add(dispman_update, 226 | -- dispman_display, 0, dst_rect, 0, src_rect, 0, alpha, nil, 0) 227 | 228 | local dispman_element = bcm.vc_dispmanx_element_add(dispman_update, 229 | dispman_display, 0, dst_rect, 0, src_rect, 0, nil, nil, 0) 230 | bcm.vc_dispmanx_update_submit_sync(dispman_update) 231 | 232 | self.dispman_display = dispman_display 233 | self.element = dispman_element 234 | self.nativewindow = EGL_DISPMANX_WINDOW_T(dispman_element, 235 | self.width, self.height) 236 | end 237 | 238 | function Screen.deinit(self) 239 | bcm.bcm_host_deinit() 240 | end 241 | 242 | function Screen.restoreSize(self) 243 | local ini = self.init 244 | self:move(ini.width, ini.height, ini.offsetx, ini.offsety) 245 | end 246 | 247 | function Screen.move(self, w, h, x, y) 248 | local size = {} 249 | size.width, size.height, size.x, size.y = self:checkSize(w, h, x, y) 250 | 251 | local dst_rect = bcm.VC_RECT_T(size.x, size.y, size.width, size.height) 252 | local dispman_update = bcm.vc_dispmanx_update_start(0) 253 | 254 | local res = bcm.vc_dispmanx_element_change_attributes(dispman_update, 255 | self.element, bcm.ELEMENT_CHANGE_DEST_RECT, 0, 256 | 0, dst_rect, nil, 0, 0) 257 | bcm.vc_dispmanx_update_submit_sync(dispman_update) 258 | end 259 | 260 | function Screen.egl_init(self) 261 | local CONTEXT = ffi.new('int[3]', {egl.CONTEXT_CLIENT_VERSION, 2, 262 | egl.NONE}) 263 | local attrib = ffi.new('int[11]', {egl.RED_SIZE, 8, 264 | egl.GREEN_SIZE, 8, egl.BLUE_SIZE, 8, egl.ALPHA_SIZE, 8, 265 | egl.DEPTH_SIZE, 24, egl.NONE}) 266 | 267 | local numConfigs = ffi.new('int[1]') 268 | local config = ffi.new('void *[1]') 269 | local display = egl.getDisplay(ffi.cast("EGLDisplay",egl.DEFAULT_DISPLAY)) 270 | assert(display, "eglGetDisplay failed.") 271 | 272 | local major = ffi.new("uint32_t[1]") 273 | local minor = ffi.new("uint32_t[1]") 274 | local res = egl.initialize(display, major, minor) 275 | assert(res ~= 0, "eglInitialize failed.") 276 | self.majorVersion = major[0] 277 | self.minorVersion = minor[0] 278 | 279 | res = egl.chooseConfig(display, attrib, config, 1, numConfigs) 280 | assert(res ~= 0, "eglChooseConfig failed.") 281 | local surface = egl.createWindowSurface(display, config[0], 282 | self.nativewindow, nil) 283 | 284 | assert(surface, "eglCreateWindowSurface failed.") 285 | 286 | local context = egl.createContext(display, config[0], nil, CONTEXT) 287 | assert(context, "eglCreateContext failed.") 288 | 289 | res = egl.makeCurrent(display, surface, surface, context) 290 | assert(res ~= 0, "eglMakeCurrent failed.") 291 | 292 | self.frames = 0 293 | self.display = display 294 | self.surface = surface 295 | return true 296 | end 297 | 298 | function Screen.setClearColor(self, r, g, b, alpha) 299 | self.clearColor = {r, g, b, alpha} 300 | end 301 | 302 | function Screen.cullFace(self) 303 | gl.cullFace(gl.BACK) 304 | gl.frontFace(gl.CCW) 305 | gl.enable(gl.CULL_FACE) 306 | gl.enable(gl.DEPTH_TEST) 307 | end 308 | 309 | function Screen.init(self, w, h, x, y) 310 | self:fbinfo() 311 | self:bcm_init(w, h, x, y) 312 | self:egl_init() 313 | self:cullFace() 314 | end 315 | 316 | function Screen.getFrameCount(self) 317 | return self.frames 318 | end 319 | 320 | function Screen.getAspect(self) 321 | return self.width / self.height 322 | end 323 | 324 | function Screen.getWidth(self) 325 | return self.width 326 | end 327 | 328 | function Screen.getHeight(self) 329 | return self.height 330 | end 331 | 332 | function Screen.resetFrameCount(self) 333 | self.frames = 0 334 | end 335 | 336 | function Screen.viewport(self) 337 | gl.viewport(0, 0, self.width, self.height) 338 | end 339 | 340 | function Screen.clear(self) 341 | gl.bindFramebuffer(gl.FRAMEBUFFER, 0) 342 | gl.viewport(0, 0, self.width, self.height) 343 | gl.clearColor(unpack(self.clearColor)) 344 | gl.clear(gl.COLOR_BUFFER_BIT + gl.DEPTH_BUFFER_BIT) 345 | end 346 | 347 | function Screen.clearDepthBuffer(self) 348 | gl.bindFramebuffer(gl.FRAMEBUFFER, 0) 349 | gl.viewport(0, 0, self.width, self.height) 350 | gl.clear(gl.DEPTH_BUFFER_BIT) 351 | end 352 | 353 | function Screen.update(self) 354 | self.frames = self.frames + 1 355 | egl.swapBuffers(self.display, self.surface) 356 | end 357 | 358 | function Screen.swapInterval(self, interval) 359 | if interval >=0 and interval <= 10 then 360 | -- If interval ~= 0 then framerate = 60 / interval. 361 | -- If interval == 0 then no vsync. 362 | egl.swapInterval(self.display, interval) 363 | end 364 | end 365 | 366 | function Screen.screenShot(self, filename) 367 | local w = self.width 368 | local h = self.height 369 | local buflen = w * h * 3 370 | local buf = ffi.new("uint8_t[?]", buflen) 371 | gl.readPixels(0, 0, w, h, gl.RGB, gl.UNSIGNED_BYTE, buf) 372 | if filename == nil then 373 | filename = "ss" .. os.date("%Y%m%d_%H%M%S") .. ".png" 374 | end 375 | png.flipImage(buf, w, h, 3) 376 | png.writePNG(filename, buf, w, h, 8, 3) 377 | end 378 | 379 | -------------------------------------------------------------------------------- /LjES/Shader.lua: -------------------------------------------------------------------------------- 1 | -- --------------------------------------------- 2 | -- Shader.lua 2014/06/05 3 | -- Copyright (c) 2013-2014 Jun Mizutani, 4 | -- released under the MIT open source license. 5 | -- --------------------------------------------- 6 | 7 | local ffi = require("ffi") 8 | local gl = require("gles2") 9 | 10 | require("Object") 11 | 12 | Shader = Object:new() 13 | 14 | function Shader.new(self) 15 | local obj = Object.new(self) 16 | obj.program = 0 17 | return obj 18 | end 19 | 20 | Shader.vShaderSrc = "" 21 | Shader.fShaderSrc = "" 22 | 23 | function Shader.loadShader(self, type, shaderSource) 24 | local shader = gl.createShader(type) 25 | if shader == 0 then 26 | local shaderType = "FRAGMENT_SHADER" 27 | if type == gl.VERTEX_SHADER then 28 | shaderType = "VERTEX_SHADER" 29 | end 30 | util.printf("glCreateShader: %s failed.\n", shaderType) 31 | return 0 32 | end 33 | local CharP = ffi.typeof("const char*") 34 | local CharPP = ffi.typeof("const char* [1]") 35 | local cp = CharP(shaderSource) 36 | local cpp = CharPP(cp) 37 | gl.shaderSource(shader, 1, cpp, nil) 38 | gl.compileShader(shader) 39 | local compiled = ffi.new("uint32_t[1]") 40 | gl.getShaderiv(shader, gl.COMPILE_STATUS, compiled) 41 | if compiled[0] == 0 then 42 | local infoLen = ffi.new("uint32_t[1]") 43 | gl.getShaderiv(shader, gl.INFO_LOG_LENGTH, infoLen) 44 | if (infoLen[0] > 0) then 45 | local infoLog = ffi.new("char[?]", infoLen[0]) 46 | gl.getShaderInfoLog(shader, infoLen[0], nil, infoLog) 47 | util.printf("Error compiling shader: %s\n", infoLog) 48 | end 49 | gl.deleteShader(shader) 50 | return 0 51 | end 52 | return shader 53 | end 54 | 55 | function Shader.initShaders(self) 56 | local vShader = self:loadShader(gl.VERTEX_SHADER, self.vShaderSrc) 57 | local fShader = self:loadShader(gl.FRAGMENT_SHADER, self.fShaderSrc) 58 | 59 | local prog = gl.createProgram() 60 | self.program = prog 61 | if prog == 0 then 62 | self.program = 0 63 | util.printf("Failed to create program.\n") 64 | return false 65 | end 66 | 67 | gl.attachShader(prog, vShader) 68 | gl.attachShader(prog, fShader) 69 | gl.linkProgram(prog) 70 | local linked = ffi.new("uint32_t[1]") 71 | gl.getProgramiv(prog, gl.LINK_STATUS, linked) 72 | if linked[0] == 0 then 73 | local infoLen = ffi.new("uint32_t[1]") 74 | gl.getProgramiv(prog, gl.INFO_LOG_LENGTH, infoLen) 75 | if infoLen[0] > 0 then 76 | local infoLog = ffi.new("char[?]", infoLen[0]) 77 | gl.getProgramInfoLog(prog, infoLen[0], nil, infoLog) 78 | util.printf("Error linking program: %s\n", infoLog[0]) 79 | assert() 80 | end 81 | gl.deleteProgram(prog) 82 | return false 83 | end 84 | 85 | gl.deleteShader(vShader) 86 | gl.deleteShader(fShader) 87 | return prog 88 | end 89 | 90 | function Shader.setDefaultParam(self, key, value) 91 | self.default[key] = value 92 | end 93 | 94 | function Shader.updateParam(self, param, key, updateFunc) 95 | local function compare(table1, table2) 96 | if table1 == table2 then return true end -- identical table 97 | if #table1 ~= #table2 then return false end 98 | for i=1, #table1 do 99 | if table1[i] ~= table2[i] then return false end 100 | end 101 | return true -- same contents 102 | end 103 | 104 | if (param[key] ~= nil) then 105 | self.change[key] = param[key] 106 | updateFunc(self, param[key]) 107 | else 108 | local c = self.change 109 | if c[key] ~= nil then 110 | local d = self.default 111 | if type(d[key]) == "table" then 112 | if compare(c[key], d[key]) == false then 113 | c[key] = d[key] 114 | end 115 | elseif c[key] ~= d[key] then 116 | c[key] = d[key] 117 | updateFunc(self, d[key]) 118 | end 119 | end 120 | end 121 | end 122 | 123 | function Shader.initShaderParameter(self) 124 | end 125 | 126 | function Shader.doParameter(self, param) 127 | end 128 | 129 | function Shader.useProgram(self) 130 | gl.useProgram(self.program) 131 | end 132 | 133 | function Shader.init(self) 134 | self.status = self:initShaders() 135 | self:initShaderParameter() 136 | end 137 | -------------------------------------------------------------------------------- /LjES/Skeleton.lua: -------------------------------------------------------------------------------- 1 | -- --------------------------------------------- 2 | -- Skeleton.lua 2014/06/06 3 | -- Copyright (c) 2014 Jun Mizutani, 4 | -- released under the MIT open source license. 5 | -- --------------------------------------------- 6 | 7 | local ffi = require("ffi") 8 | require("Node") 9 | 10 | Skeleton = Object:new() 11 | 12 | function Skeleton.new(self) 13 | local obj = Object.new(self) 14 | obj.MAX_BONE = 40 15 | obj.bones = {} 16 | obj.attachable = false 17 | obj.boneOrder = {} 18 | obj.allJointNames = {} 19 | obj.boneNo = 0 20 | obj.boneShape = nil 21 | obj.matrixPalette = ffi.new("float[40 * 3 * 4]", {}) 22 | obj.show = false 23 | return obj 24 | end 25 | 26 | function Skeleton.clone(self) 27 | local skel = Skeleton:new() 28 | local num = 0 29 | skel:setBoneOrder(self.boneOrder) 30 | 31 | local function copyBoneToBone(src_bone, parent) 32 | if num < self.MAX_BONE then 33 | num = num + 1 34 | local bone = skel:addBone(parent, src_bone.name) 35 | bone:setRestByMatrix(src_bone.restMatrix) 36 | bone:setByMatrix(src_bone.restMatrix) 37 | bone.hasWeights = src_bone.hasWeights 38 | if #src_bone.children > 0 then 39 | for i=1, #src_bone.children do 40 | copyBoneToBone(src_bone.children[i], bone) 41 | end 42 | end 43 | end 44 | end 45 | copyBoneToBone(self.bones[1], nil) 46 | skel:bindRestPose() 47 | return skel 48 | end 49 | 50 | function Skeleton.addBone(self, parent_bone, name) 51 | local bone = Node:new(parent_bone, name) 52 | bone:setType(bone.BONE_T) 53 | if self.boneShape ~= nil then 54 | bone:addShape(self.boneShape) 55 | end 56 | table.insert(self.bones, bone) 57 | self.boneNo = #self.bones 58 | if parent_bone ~= nil then 59 | table.insert(parent_bone.children, bone) 60 | end 61 | return bone 62 | end 63 | 64 | function Skeleton.setBoneShape(self, shape) 65 | self.boneShape = shape 66 | if #self.bones == 0 then return end 67 | for i=1, #self.bones do 68 | self.bones[i]:setShape(self.boneShape) 69 | end 70 | end 71 | 72 | function Skeleton.setAttachable(self, true_or_false) 73 | self.attachable = true_or_false 74 | if #self.bones == 0 then return end 75 | for i=1, #self.bones do 76 | self.bones[i]:setAttachable(true_or_false) 77 | end 78 | end 79 | 80 | function Skeleton.isAttachable(self) 81 | return self.attachable 82 | end 83 | 84 | function Skeleton.isShown(self) 85 | return self.show 86 | end 87 | 88 | function Skeleton.showBone(self, true_or_false) 89 | self.show = true_or_false 90 | if #self.bones == 0 then return end 91 | for i=1, #self.bones do 92 | local bone = self.bones[i] 93 | bone:hide(not true_or_false) 94 | if true_or_false then 95 | bone:setAttachable(true) 96 | else 97 | bone:setAttachable(self.attachable) 98 | end 99 | end 100 | end 101 | 102 | function Skeleton.setBoneOrder(self, names) 103 | self.allJointNames = names 104 | local nBones = #names 105 | if nBones > self.MAX_BONE then nBones = self.MAX_BONE end 106 | for i=1, nBones do 107 | for j=1, #self.bones do 108 | if self.bones[j].name == names[i] then 109 | table.insert(self.boneOrder, self.bones[j]) 110 | end 111 | end 112 | end 113 | return self.boneOrder 114 | end 115 | 116 | function Skeleton.getBoneOrder(self) 117 | return self.boneOrder 118 | end 119 | 120 | function Skeleton.getBoneNo(self, name) 121 | for i=1, #self.bones do 122 | if self.bones[i].name == name then 123 | return i - 1 124 | end 125 | end 126 | util.printf("Bone [%s] not found. (getBoneNo)\n", name) 127 | return nil 128 | end 129 | 130 | function Skeleton.getBoneCount(self) 131 | return #self.bones 132 | end 133 | 134 | function Skeleton.getBone(self, name) 135 | for i=1, #self.bones do 136 | if self.bones[i].name == name then 137 | return self.bones[i] 138 | end 139 | end 140 | util.printf("Bone [%s] not found.(getBone)\n", name) 141 | return nil 142 | end 143 | 144 | -- num : Matrix palette No. can exceeds 39, starting at 0 145 | function Skeleton.getBoneFromJointNo(self, num) 146 | if #self.boneOrder > 0 then 147 | local bone_name = self.allJointNames[num + 1] 148 | return self:getBone(bone_name) 149 | else 150 | return self.bones[num + 1] 151 | end 152 | end 153 | 154 | function Skeleton.printJointNames(self) 155 | util.printf("#self.allJointNames = %4d\n", #self.allJointNames) 156 | for i = 1, #self.allJointNames do 157 | util.printf("%4d %s\n", i-1, self.allJointNames[i]) 158 | end 159 | end 160 | 161 | function Skeleton.printBone(self) 162 | util.printf("#self.bones = %4d\n", #self.bones) 163 | for i=1, #self.bones do 164 | util.printf("%4d %s\n", i, self.bones[i].name) 165 | end 166 | end 167 | 168 | function Skeleton.getJointFromBone(self, bone) 169 | if #self.allJointNames > 0 then 170 | for i = 1, #self.allJointNames do 171 | if bone.name == self.allJointNames[i] then 172 | return (i-1) -- starting at 0 173 | end 174 | end 175 | else 176 | return self:getBoneNoFromBone(bone) 177 | end 178 | return nil 179 | end 180 | 181 | function Skeleton.getBoneNoFromBone(self, bone) 182 | if #self.bones > 0 then 183 | for i=1, #self.bones do 184 | if self.bones[i] == bone then 185 | return (i-1) -- starting at 0 186 | end 187 | end 188 | end 189 | return nil 190 | end 191 | 192 | function Skeleton.bindRestPose(self) 193 | for i=1, #self.bones do 194 | local bone = self.bones[i] 195 | if bone.rootBone then 196 | bone:setModelMatrixAll(nil) 197 | end 198 | end 199 | end 200 | 201 | function Skeleton.updateMatrixPalette(self) 202 | local wm = Matrix:new() 203 | for i=1, #self.bones do 204 | local bone = self.bones[i] 205 | if bone.rootBone then 206 | bone:setGlobalMatrixAll(nil) 207 | end 208 | end 209 | local nBones = #self.boneOrder 210 | if nBones > 0 then 211 | for i=1, nBones do 212 | wm:copyFrom(self.boneOrder[i].worldMatrix) 213 | wm:mul(self.boneOrder[i].bofMatrix) -- [Pallete_n] = [Cn] x [Mn]^-1 214 | 215 | local n = (i-1) * 12 216 | for j=0, 2 do 217 | local k = n + j * 4 218 | self.matrixPalette[k ] = wm.mat[j] 219 | self.matrixPalette[k + 1] = wm.mat[j+4] 220 | self.matrixPalette[k + 2] = wm.mat[j+8] 221 | self.matrixPalette[k + 3] = wm.mat[j+12] 222 | end 223 | end 224 | else -- if #self.boneOrder == 0 225 | nBones = #self.bones 226 | if nBones > self.MAX_BONE then nBones = self.MAX_BONE end 227 | for i=1, nBones do 228 | wm:copyFrom(self.bones[i].worldMatrix) 229 | wm:mul(self.bones[i].bofMatrix) -- [Pallete_n] = [Cn] x [Mn]^-1 230 | local n = (i-1) * 12 231 | for j=0, 2 do 232 | local k = n + j * 4 233 | self.matrixPalette[k ] = wm.mat[j] 234 | self.matrixPalette[k + 1] = wm.mat[j+4] 235 | self.matrixPalette[k + 2] = wm.mat[j+8] 236 | self.matrixPalette[k + 3] = wm.mat[j+12] 237 | end 238 | end 239 | end 240 | return self.matrixPalette 241 | end 242 | 243 | function Skeleton.listBones(self) 244 | self:updateMatrixPalette() 245 | for i=1, #self.bones do 246 | local bone = self.bones[i] 247 | util.printf("--- [%d] : %s\n", i, bone.name) 248 | util.printf("<>\n") 249 | bone.restMatrix:print() 250 | util.printf("<>\n") 251 | bone.modelMatrix:print() 252 | util.printf("<>\n") 253 | bone.bofMatrix:print() 254 | util.printf("<>\n") 255 | bone.worldMatrix:print() 256 | util.printf("<>\n") 257 | bone.matrix:print() 258 | local pos = bone:getPosition() 259 | util.printf(" x:% 12.5f, y:% 12.5f, z:% 12.5f\n", 260 | pos[1], pos[2], pos[3]) 261 | local h, p, b = bone:getLocalAttitude() 262 | util.printf(" h:% 12.5f, p:% 12.5f, b:% 12.5f\n", h, p, b) 263 | end 264 | end 265 | 266 | function Skeleton.printMatrixPalette(self) 267 | self:bindRestPose() 268 | local m = self:updateMatrixPalette() 269 | self:listBones() 270 | util.printf("\n") 271 | 272 | local nBones = #self.boneOrder 273 | if nBones > 0 then 274 | if nBones > self.MAX_BONE then nBones = self.MAX_BONE end 275 | for i=1, nBones do 276 | local bone = self.boneOrder[i] 277 | local k = (i-1) * 16 278 | util.printf("------- Pallete [%2d %s] --------\n", i, bone.name) 279 | local fmt = "% 16.11e % 16.11e % 16.11e % 16.11e\n" 280 | util.printf(fmt, m[k+0], m[k+4], m[k+8], m[k+12]) 281 | util.printf(fmt, m[k+1], m[k+5], m[k+9], m[k+13]) 282 | util.printf(fmt, m[k+2], m[k+6], m[k+10], m[k+14]) 283 | util.printf(fmt, m[k+3], m[k+7], m[k+11], m[k+15]) 284 | end 285 | else 286 | for i=1, #self.bones do 287 | local k = (i-1) * 16 288 | util.printf("------- Pallete [%2d] --------\n", i) 289 | local fmt = "% 16.11e % 16.11e % 16.11e % 16.11e\n" 290 | util.printf(fmt, m[k+0], m[k+4], m[k+8], m[k+12]) 291 | util.printf(fmt, m[k+1], m[k+5], m[k+9], m[k+13]) 292 | util.printf(fmt, m[k+2], m[k+6], m[k+10], m[k+14]) 293 | util.printf(fmt, m[k+3], m[k+7], m[k+11], m[k+15]) 294 | end 295 | end 296 | end 297 | 298 | function Skeleton.drawBones(self, view_matrix) 299 | local bone 300 | if self.show == false then return end 301 | if #self.bones > 0 then 302 | for i=1, #self.bones do 303 | bone = self.bones[i] 304 | if bone.rootBone then bone:drawBones() end 305 | end 306 | end 307 | end 308 | -------------------------------------------------------------------------------- /LjES/Space.lua: -------------------------------------------------------------------------------- 1 | -- --------------------------------------------- 2 | -- Space.lua 2014/07/23 3 | -- Copyright (c) 2013-2014 Jun Mizutani, 4 | -- released under the MIT open source license. 5 | -- --------------------------------------------- 6 | 7 | local util = require("util") 8 | require("Node") 9 | 10 | Space = Object:new() 11 | 12 | function Space.new(self) 13 | local obj = Object.new(self) 14 | obj.nodes = {} 15 | obj.roots = {} 16 | obj.skeletons = {} 17 | obj.light = nil 18 | obj.lightType = 1.0 19 | local sec, usec = util.gettimeofday() 20 | obj.startTime = sec + usec/1000000 21 | obj.time = 0 22 | obj.elapsedTime = 0 23 | obj.drawCount = 0 24 | return obj 25 | end 26 | 27 | function Space.addNode(self, parent_node, name) 28 | local node = Node:new(parent_node, name) 29 | table.insert(self.nodes, node) 30 | if parent_node ~= nil then 31 | parent_node:addChild(node) 32 | end 33 | return node 34 | end 35 | 36 | function Space.delNode(self, name) 37 | local node = self:findNode(name) 38 | if (node) then 39 | for i=1, #self.nodes do 40 | if (self.nodes[i] == node) then 41 | table.remove(self.nodes, i) 42 | end 43 | end 44 | end 45 | end 46 | 47 | function Space.scanSkeletons(self) 48 | local shapes 49 | self.skeletons = {} 50 | for i=1, #self.nodes do 51 | shapes = self.nodes[i].shapes 52 | if #shapes > 0 then 53 | for j=1, #shapes do 54 | local skeleton = shapes[j].skeleton 55 | if skeleton ~= nil then 56 | if skeleton:isAttachable() or skeleton:isShown() then 57 | table.insert(self.skeletons, skeleton) 58 | end 59 | end 60 | end 61 | end 62 | end 63 | end 64 | 65 | function Space.findNode(self, name) 66 | for i=1, #self.nodes do 67 | if (self.nodes[i].name == name) then 68 | return self.nodes[i] 69 | end 70 | end 71 | util.printf("%s not found!\n", name) 72 | return nil 73 | end 74 | 75 | function Space.listNode(self) 76 | function listChildNodes(children, level) 77 | if #children == 0 then return end 78 | for j=1, #children do 79 | local fmt = '%' .. tostring(level*4) .. 's%s' 80 | util.printf(fmt, ' ', children[j].name) 81 | listChildNodes(children[j].children, level + 1) 82 | end 83 | end 84 | 85 | for i=1, #self.nodes do 86 | if (self.nodes[i].parent == nil) then 87 | util.printf("%s\n", self.nodes[i].name) 88 | listChildNodes(self.nodes[i].children, 1) 89 | end 90 | end 91 | end 92 | 93 | function Space.now(self) 94 | local sec, usec = util.gettimeofday() 95 | return sec + usec/1000000 96 | end 97 | 98 | function Space.timerStart(self) 99 | self.startTime = self:now() 100 | end 101 | 102 | function Space.uptime(self) 103 | return self:now() - self.startTime 104 | end 105 | 106 | function Space.deltaTime(self) 107 | return self.elapsedTime 108 | end 109 | 110 | function Space.count(self) 111 | return self.drawCount 112 | end 113 | 114 | function Space.setLight(self, node) 115 | self.light = node 116 | end 117 | 118 | function Space.setLightType(self, type) 119 | -- type = 0:spot, 1:parallel 120 | self.lightType = type 121 | end 122 | 123 | function Space.getLightType(self) 124 | -- type = 0:spot, 1:parallel 125 | return self.lightType 126 | end 127 | 128 | function Space.setEye(self, node) 129 | self.eye = node 130 | end 131 | 132 | function Space.draw(self, eye_node) 133 | local node 134 | local oldTime = self.time 135 | self.time = self.now() 136 | self.elapsedTime = self.time - oldTime 137 | 138 | if (eye_node == nil) and (self.eye ~= nil)then 139 | eye_node = self.eye 140 | end 141 | eye_node:setWorldMatrix() 142 | local view_matrix = Matrix:new() 143 | view_matrix:makeView(eye_node.worldMatrix) 144 | 145 | local lightVec 146 | if self.light ~= nil then 147 | if self.lightType == 1.0 then -- point light 148 | lightVec = view_matrix:mul3x3Vector(self.light:getWorldPosition()) 149 | else -- parallel light 150 | local lightInEye = view_matrix:clone() 151 | lightInEye:mul(self.light.worldMatrix) 152 | lightVec = lightInEye:mul3x3Vector( {0.0, 0.0, 1.0} ) 153 | end 154 | 155 | table.insert(lightVec, self.lightType) 156 | end 157 | 158 | for i=1, #self.nodes do 159 | node = self.nodes[i] 160 | if (node.parent == nil) then 161 | node:draw(view_matrix, lightVec) 162 | end 163 | end 164 | self.drawCount = self.drawCount + 1 165 | end 166 | 167 | function Space.drawBones(self) 168 | if #self.skeletons == 0 then return end 169 | for i=1, #self.skeletons do 170 | self.skeletons[i]:drawBones() 171 | end 172 | end 173 | -------------------------------------------------------------------------------- /LjES/Stack.lua: -------------------------------------------------------------------------------- 1 | -- --------------------------------------------- 2 | -- Stack.lua 2013/11/09 3 | -- Copyright (c) 2013 Jun Mizutani, 4 | -- released under the MIT open source license. 5 | -- --------------------------------------------- 6 | 7 | require("Object") 8 | 9 | Stack = Object:new() 10 | 11 | function Stack.new(self) 12 | local obj = Object.new(self) 13 | obj.stack = {} 14 | return obj 15 | end 16 | 17 | function Stack.push(self, contents) 18 | table.insert(self.stack, contents) 19 | end 20 | 21 | function Stack.pop(self) 22 | if #self.stack < 1 then return nil end 23 | return table.remove(self.stack) 24 | end 25 | 26 | function Stack.top(self) 27 | return self.stack[#self.stack] 28 | end 29 | 30 | function Stack.count(self) 31 | return #self.stack 32 | end 33 | 34 | -------------------------------------------------------------------------------- /LjES/Task.lua: -------------------------------------------------------------------------------- 1 | -- --------------------------------------------- 2 | -- Task.lua 2014/06/06 3 | -- Copyright (c) 2014 Jun Mizutani, 4 | -- released under the MIT open source license. 5 | -- --------------------------------------------- 6 | 7 | require("Object") 8 | 9 | Task = Object:new() 10 | 11 | function Task.new(self, name, no) 12 | local obj = Object.new(self) 13 | obj.name = name 14 | obj.id = no 15 | obj.stopped = false 16 | obj.ip = 0 17 | obj.stopIP = 0 18 | obj.commands = {} 19 | obj.time = 0 20 | obj.remaining_time = 0 21 | obj.targetObj = nil 22 | obj.arg = {} 23 | obj.currentCommand = nil 24 | obj.targetObject = nil 25 | obj.time_scale = 1.0 -- play speed 26 | return obj 27 | end 28 | 29 | function Task.setTargetObject(self, target) 30 | self.targetObject = target 31 | end 32 | 33 | -- task[1]:addCommand(1000, node.rotateX, arg) 34 | function Task.addCommand(self, time, func, arg) 35 | -- func({10, 10, 10}) 36 | table.insert(self.commands, {time, func, arg}) 37 | end 38 | 39 | function Task.setTime(self, ip, time) 40 | if (ip > 0) and (ip <= #self.commands) then 41 | self.commands[ip][1] = time 42 | end 43 | end 44 | 45 | function Task.getTime(self, ip) 46 | if (ip > 0) and (ip <= #self.commands) then 47 | return self.commands[ip][1] 48 | else 49 | return -1 50 | end 51 | end 52 | 53 | function Task.getName(self) 54 | return self.name 55 | end 56 | 57 | function Task.getNoOfCommands(self) 58 | return #self.commands 59 | end 60 | 61 | -- command_table = { 62 | -- { 100, func, {1, 20} }, 63 | -- { 0, func2, {"A"} }, 64 | -- { 300, funcA, {10} } 65 | -- } 66 | function Task.setCommand(self, command_table) 67 | self.commands = command_table 68 | self:start() 69 | end 70 | 71 | function Task.partial_arg(self, arg, total_time, dtime) 72 | local doarg = {} 73 | for i = 1, #arg do 74 | -- print(arg[1]) 75 | if total_time > 0.1 then 76 | doarg[i] = tonumber(arg[i]) * dtime / total_time 77 | else 78 | table.insert(doarg, arg[i]) 79 | end 80 | end 81 | return doarg 82 | end 83 | 84 | function Task.controlCommand(self, command, arg) 85 | if command == "jump" then 86 | -- jump to current command + arg position 87 | -- {0, "jump", {-1}} 88 | self.ip = self.ip + arg - 1 89 | elseif command == "quit" then 90 | -- quit task 91 | self.stopped = true 92 | end 93 | end 94 | 95 | function Task.execCommand(self, doarg) 96 | local command = self.currentCommand 97 | if type(command) == "string" then 98 | self:controlCommand(command, unpack(doarg)) 99 | elseif self.targetObject == nil then 100 | -- call function. {0, print, {"abc"} } 101 | self.ret_code = command(unpack(doarg)) 102 | else 103 | if command ~= nil then 104 | -- call method. {1000, Bone.ratateX, {90} } 105 | self.ret_code = command(self.targetObject, unpack(doarg)) 106 | else 107 | util.printf("Error (%s:execCommand):[%3d] %s:nil(..)\n", 108 | self.name, self.ip, self.targetObject.name) 109 | end 110 | end 111 | end 112 | 113 | function Task.getNextCommand(self) 114 | repeat 115 | self.ip = self.ip + 1 116 | if (self.ip <= self.stopIP) and (self.ip > 0) then 117 | self.time = self.commands[self.ip][1] * self.time_scale 118 | self.currentCommand = self.commands[self.ip][2] 119 | self.arg = self.commands[self.ip][3] 120 | self.remaining_time = self.time 121 | if self.time == 0 then 122 | self:execCommand(self.arg) 123 | end 124 | else 125 | self.stopped = true -- end of task 126 | self.ip = -1 127 | end 128 | until self.time > 0 or self.stopped 129 | end 130 | 131 | function Task.start(self) 132 | self:startFromTo(1, -1) 133 | end 134 | 135 | function Task.startFrom(self, start_ip) 136 | self:startFromTo(start_ip, -1) 137 | end 138 | 139 | function Task.startFromTo(self, start_ip, stop_ip) 140 | if stop_ip > #self.commands then 141 | stop_ip = #self.commands 142 | end 143 | if stop_ip < 0 then 144 | self.stopIP = #self.commands 145 | else 146 | self.stopIP = stop_ip 147 | end 148 | if (start_ip > 0) and (start_ip <= self.stopIP) then 149 | self.stopped = false 150 | -- to compensate self.ip+=1 in getNextCommand. 151 | self.ip = start_ip - 1 152 | -- getNextCommand may execute commands with time==0. 153 | self:getNextCommand() 154 | end 155 | end 156 | 157 | function Task.execute(self, delta_msec) 158 | local doarg 159 | if self.stopped then return -1 end 160 | if self.ip == 0 then self:getNextCommand() end 161 | 162 | if self.remaining_time > delta_msec then 163 | self.remaining_time = self.remaining_time - delta_msec 164 | doarg = self:partial_arg(self.arg, self.time, delta_msec) 165 | self:execCommand(doarg) 166 | else 167 | local time_next = delta_msec - self.remaining_time 168 | doarg = self:partial_arg(self.arg, self.time, self.remaining_time) 169 | self:execCommand(doarg) 170 | repeat 171 | self:getNextCommand() 172 | if not self.stopped then 173 | self.remaining_time = self.time - time_next 174 | if self.time > time_next then 175 | doarg = self:partial_arg(self.arg, self.time, time_next) 176 | self:execCommand(doarg) 177 | else -- time < time_next 178 | time_next = time_next - self.time 179 | doarg = self:partial_arg(self.arg, self.time, self.time) 180 | self:execCommand(doarg) 181 | end 182 | end 183 | until self.remaining_time > 0 or self.stopped 184 | end 185 | return self.ip 186 | end 187 | 188 | function Task.executeOneCommand(self, ip, arg_rate) 189 | local commands = self.commands 190 | local object = self.targetObject 191 | local time = commands[ip][1] 192 | local command = commands[ip][2] 193 | local arg = commands[ip][3] 194 | local doarg = self:partial_arg(arg, 1.0, arg_rate) 195 | if object == nil then 196 | command(unpack(doarg)) 197 | else 198 | command(object, unpack(doarg)) 199 | end 200 | end 201 | 202 | function Task.directExecution(self, command, doarg) 203 | if self.targetObject == nil then 204 | self.ret_code = command(unpack(doarg)) 205 | else 206 | if command ~= nil then 207 | self.ret_code = command(self.targetObject, unpack(doarg)) 208 | else 209 | util.printf("Error( %s<%d>:directExecution ):[%3d] %s:nil(..)\n", 210 | self.name, self.id, self.ip, self.targetObject.name) 211 | end 212 | end 213 | end 214 | 215 | function Task.insertCurrentCommand(self, time, command, arg, start_ip, stop_ip) 216 | self.time = time 217 | self.currentCommand = command 218 | self.arg = arg 219 | self.remaining_time = self.time 220 | if (start_ip ~= nil) and (stop_ip ~= nil) then 221 | if stop_ip > #self.commands then 222 | stop_ip = #self.commands 223 | end 224 | if stop_ip < 0 then 225 | self.stopIP = #self.commands 226 | else 227 | self.stopIP = stop_ip 228 | end 229 | if (start_ip > 0) and (start_ip <= self.stopIP) then 230 | self.stopped = false 231 | self.ip = start_ip - 1 232 | end 233 | end 234 | end 235 | -------------------------------------------------------------------------------- /LjES/Texture.lua: -------------------------------------------------------------------------------- 1 | -- --------------------------------------------- 2 | -- Texture.lua 2014/06/05 3 | -- Copyright (c) 2013-2014 Jun Mizutani, 4 | -- released under the MIT open sondurce license. 5 | -- --------------------------------------------- 6 | 7 | local ffi = require("ffi") 8 | local gl = require("gles2") 9 | local png = require("png") 10 | local util = require("util") 11 | 12 | require("Object") 13 | 14 | Texture = Object:new() 15 | 16 | function Texture.new(self) 17 | local obj = Object.new(self) 18 | obj.status = false 19 | obj.filename = nil 20 | obj.textureUnit = 0 21 | obj.image_buffer = nil 22 | obj.width = 0 23 | obj.height = 0 24 | obj.ncol = 0 25 | obj.texname = 0 26 | return obj 27 | end 28 | 29 | function Texture.setupTexture(self) 30 | local tex = ffi.new("uint32_t[1]") 31 | gl.genTextures(1, tex) 32 | self.texname = tex[0] 33 | gl.bindTexture(gl.TEXTURE_2D, self.texname) 34 | gl.pixelStorei(gl.UNPACK_ALIGNMENT, 1) 35 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT) 36 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT) 37 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR) 38 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR) 39 | end 40 | 41 | function Texture.setClamp(self) 42 | gl.bindTexture(gl.TEXTURE_2D, self.texname) 43 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE) 44 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE) 45 | end 46 | 47 | function Texture.readImageFromFile(self, textureFile) 48 | self:setupTexture() 49 | if util.isFileExist(textureFile) ~= true then 50 | textureFile = util.packagePath() .. textureFile 51 | if util.isFileExist(textureFile) ~= true then 52 | return false 53 | end 54 | end 55 | local image, bytes, w, h, bpp, ncol = png.readPNG(textureFile, 1) 56 | if image == nil then return false end 57 | self.filename = textureFile 58 | png.flipImage(image, w, h, ncol) 59 | self:setImage(image, w, h, ncol) 60 | return true 61 | end 62 | 63 | function Texture.setImage(self, image, width, height, ncol) 64 | if ncol == 4 then 65 | gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, width, height, 0, gl.RGBA, 66 | gl.UNSIGNED_BYTE, image) 67 | else 68 | gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, width, height, 0, gl.RGB, 69 | gl.UNSIGNED_BYTE, image) 70 | end 71 | self.image_buffer = image 72 | self.width = width 73 | self.height = height 74 | self.ncol = ncol 75 | end 76 | 77 | function Texture.writeImageToFile(self) 78 | local w = self.width 79 | local h = self.height 80 | local filename = "tex" .. os.date("%Y%m%d_%H%M%S") .. ".png" 81 | png.writePNG(filename, self.image_buffer, w * h * self.ncol, w, h, 82 | 8, self.ncol) 83 | end 84 | 85 | function Texture.createTexture(self, width, height, ncol) 86 | self:setupTexture() 87 | if ncol == 3 then 88 | self.image_buffer = ffi.new("uint8_t[?]", width * height * 3) 89 | self.ncol = 3 90 | else 91 | self.image_buffer = ffi.new("uint8_t[?]", width * height * 4) 92 | self.ncol = 4 93 | end 94 | self.width = width 95 | self.height = height 96 | end 97 | 98 | function Texture.fillTexture(self, r, g, b, a) 99 | local n, m 100 | for y=0, self.height - 1 do 101 | n = y * self.width 102 | for x=0, self.width - 1 do 103 | m = (n + x) * self.ncol 104 | self.image_buffer[m ] = r 105 | self.image_buffer[m+1] = g 106 | self.image_buffer[m+2] = b 107 | if self.ncol == 4 then 108 | self.image_buffer[m+3] = a 109 | end 110 | end 111 | end 112 | end 113 | 114 | function Texture.point(self, x, y, color) 115 | -- color : {r, b, g, a} 116 | if x >= self.width or y >= self.height then return false end 117 | if x < 0 or y < 0 then return false end 118 | local n = (y * self.width + x) * self.ncol 119 | self.image_buffer[n ] = color[1] 120 | self.image_buffer[n+1] = color[2] 121 | self.image_buffer[n+2] = color[3] 122 | if self.ncol == 4 then 123 | self.image_buffer[n+3] = color[4] 124 | end 125 | end 126 | 127 | function Texture.assignTexture(self) 128 | gl.bindTexture(gl.TEXTURE_2D, self.texname) 129 | if self.ncol == 3 then 130 | gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, self.width, self.height, 131 | 0, gl.RGB, gl.UNSIGNED_BYTE, self.image_buffer) 132 | else 133 | gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, self.width, self.height, 134 | 0, gl.RGBA, gl.UNSIGNED_BYTE, self.image_buffer) 135 | end 136 | end 137 | 138 | function Texture.name(self) 139 | return self.texname 140 | end 141 | 142 | function Texture.active(self) 143 | gl.activeTexture(gl.TEXTURE0) 144 | gl.bindTexture(gl.TEXTURE_2D, self.texname) 145 | end 146 | 147 | -------------------------------------------------------------------------------- /LjES/bcm.lua: -------------------------------------------------------------------------------- 1 | -- --------------------------------------------- 2 | -- bcm.lua 2014/06/05 3 | -- Copyright (c) 2013-2014 Jun Mizutani, 4 | -- released under the MIT open source license. 5 | -- --------------------------------------------- 6 | 7 | local ffi = require("ffi") 8 | local bcm_host = ffi.load("bcm_host") 9 | 10 | ffi.cdef[[ 11 | 12 | typedef uint32_t DISPMANX_DISPLAY_HANDLE_T; 13 | typedef uint32_t DISPMANX_UPDATE_HANDLE_T; 14 | typedef uint32_t DISPMANX_ELEMENT_HANDLE_T; 15 | typedef uint32_t DISPMANX_RESOURCE_HANDLE_T; 16 | typedef uint32_t DISPMANX_PROTECTION_T; 17 | 18 | typedef enum { 19 | DISPMANX_NO_ROTATE = 0, 20 | DISPMANX_ROTATE_90 = 1, 21 | DISPMANX_ROTATE_180 = 2, 22 | DISPMANX_ROTATE_270 = 3, 23 | 24 | DISPMANX_FLIP_HRIZ = 1 << 16, 25 | DISPMANX_FLIP_VERT = 1 << 17 26 | } DISPMANX_TRANSFORM_T; 27 | 28 | typedef enum { 29 | VC_IMAGE_ROT0 = 0, 30 | } VC_IMAGE_TRANSFORM_T; 31 | 32 | typedef struct { 33 | int32_t x; 34 | int32_t y; 35 | int32_t width; 36 | int32_t height; 37 | } VC_RECT_T; 38 | 39 | struct VC_IMAGE_T; 40 | typedef struct VC_IMAGE_T VC_IMAGE_T; 41 | 42 | typedef int EGLint; 43 | typedef unsigned int EGLBoolean; 44 | typedef unsigned int EGLenum; 45 | typedef void *EGLConfig; 46 | typedef void *EGLContext; 47 | typedef void *EGLDisplay; 48 | typedef void *EGLSurface; 49 | typedef void *EGLClientBuffer; 50 | typedef void *EGLNativeDisplayType; 51 | typedef void *EGLNativePixmapType; 52 | typedef void *EGLNativeWindowType; 53 | 54 | typedef enum { 55 | DISPMANX_FLAGS_ALPHA_FROM_SOURCE = 0, 56 | DISPMANX_FLAGS_ALPHA_FIXED_ALL_PIXELS = 1, 57 | DISPMANX_FLAGS_ALPHA_FIXED_NON_ZERO = 2, 58 | DISPMANX_FLAGS_ALPHA_FIXED_EXCEED_0X07 = 3, 59 | DISPMANX_FLAGS_ALPHA_PREMULT = 1 << 16, 60 | DISPMANX_FLAGS_ALPHA_MIX = 1 << 17 61 | } DISPMANX_FLAGS_ALPHA_T; 62 | 63 | typedef struct { 64 | DISPMANX_FLAGS_ALPHA_T flags; 65 | uint32_t opacity; 66 | VC_IMAGE_T *mask; 67 | } DISPMANX_ALPHA_T; 68 | 69 | typedef struct { 70 | DISPMANX_FLAGS_ALPHA_T flags; 71 | uint32_t opacity; 72 | DISPMANX_RESOURCE_HANDLE_T mask; 73 | } VC_DISPMANX_ALPHA_T; 74 | 75 | typedef enum { 76 | DISPMANX_FLAGS_CLAMP_NONE = 0, 77 | DISPMANX_FLAGS_CLAMP_LUMA_TRANSPARENT = 1, 78 | //#if __VCCOREVER__ >= 0x04000000 79 | DISPMANX_FLAGS_CLAMP_TRANSPARENT = 2, 80 | DISPMANX_FLAGS_CLAMP_REPLACE = 3 81 | //#else 82 | // DISPMANX_FLAGS_CLAMP_CHROMA_TRANSPARENT = 2, 83 | // DISPMANX_FLAGS_CLAMP_TRANSPARENT = 3 84 | //#endif 85 | } DISPMANX_FLAGS_CLAMP_T; 86 | 87 | typedef enum { 88 | DISPMANX_FLAGS_KEYMASK_OVERRIDE = 1, 89 | DISPMANX_FLAGS_KEYMASK_SMOOTH = 1 << 1, 90 | DISPMANX_FLAGS_KEYMASK_CR_INV = 1 << 2, 91 | DISPMANX_FLAGS_KEYMASK_CB_INV = 1 << 3, 92 | DISPMANX_FLAGS_KEYMASK_YY_INV = 1 << 4 93 | } DISPMANX_FLAGS_KEYMASK_T; 94 | 95 | typedef union { 96 | struct { 97 | uint8_t yy_upper; 98 | uint8_t yy_lower; 99 | uint8_t cr_upper; 100 | uint8_t cr_lower; 101 | uint8_t cb_upper; 102 | uint8_t cb_lower; 103 | } yuv; 104 | struct { 105 | uint8_t red_upper; 106 | uint8_t red_lower; 107 | uint8_t blue_upper; 108 | uint8_t blue_lower; 109 | uint8_t green_upper; 110 | uint8_t green_lower; 111 | } rgb; 112 | } DISPMANX_CLAMP_KEYS_T; 113 | 114 | typedef struct { 115 | DISPMANX_FLAGS_CLAMP_T mode; 116 | DISPMANX_FLAGS_KEYMASK_T key_mask; 117 | DISPMANX_CLAMP_KEYS_T key_value; 118 | uint32_t replace_value; 119 | } DISPMANX_CLAMP_T; 120 | 121 | typedef struct { 122 | DISPMANX_ELEMENT_HANDLE_T element; 123 | int width; 124 | int height; 125 | } EGL_DISPMANX_WINDOW_T; 126 | 127 | void bcm_host_init(void); 128 | void bcm_host_deinit(void); 129 | 130 | int32_t graphics_get_display_size(const uint16_t display_number, 131 | uint32_t *width, uint32_t *height); 132 | 133 | int vc_dispmanx_rect_set(VC_RECT_T *rect, uint32_t x_offset, 134 | uint32_t y_offset, uint32_t width, uint32_t height); 135 | 136 | DISPMANX_DISPLAY_HANDLE_T vc_dispmanx_display_open(uint32_t device); 137 | int vc_dispmanx_display_close(DISPMANX_DISPLAY_HANDLE_T display); 138 | 139 | DISPMANX_UPDATE_HANDLE_T vc_dispmanx_update_start(int32_t priority); 140 | 141 | DISPMANX_ELEMENT_HANDLE_T vc_dispmanx_element_add( 142 | DISPMANX_UPDATE_HANDLE_T update, 143 | DISPMANX_DISPLAY_HANDLE_T display, 144 | int32_t layer, const VC_RECT_T *dest_rect, 145 | DISPMANX_RESOURCE_HANDLE_T src, 146 | const VC_RECT_T *src_rect, DISPMANX_PROTECTION_T protection, 147 | VC_DISPMANX_ALPHA_T *alpha, 148 | DISPMANX_CLAMP_T *clamp, DISPMANX_TRANSFORM_T transform); 149 | 150 | int vc_dispmanx_element_change_attributes( 151 | DISPMANX_UPDATE_HANDLE_T update, 152 | DISPMANX_ELEMENT_HANDLE_T element, 153 | uint32_t change_flags, 154 | int32_t layer, 155 | uint8_t opacity, 156 | const VC_RECT_T *dest_rect, 157 | const VC_RECT_T *src_rect, 158 | DISPMANX_RESOURCE_HANDLE_T mask, 159 | VC_IMAGE_TRANSFORM_T transform); 160 | 161 | int vc_dispmanx_element_remove(DISPMANX_UPDATE_HANDLE_T update, 162 | DISPMANX_ELEMENT_HANDLE_T element ); 163 | 164 | int vc_dispmanx_update_submit_sync(DISPMANX_UPDATE_HANDLE_T update); 165 | 166 | ]] 167 | 168 | local bcm = { 169 | ELEMENT_CHANGE_LAYER = 1, 170 | ELEMENT_CHANGE_OPACITY = 2, 171 | ELEMENT_CHANGE_DEST_RECT = 4, 172 | ELEMENT_CHANGE_SRC_RECT = 8, 173 | ELEMENT_CHANGE_MASK_RESOURCE = 16, 174 | ELEMENT_CHANGE_TRANSFORM = 32, 175 | 176 | VC_RECT_T = ffi.typeof("VC_RECT_T"), 177 | VC_DISPMANX_ALPHA_T = ffi.typeof("VC_DISPMANX_ALPHA_T"), 178 | EGL_DISPMANX_WINDOW_T = ffi.typeof("EGL_DISPMANX_WINDOW_T"), 179 | bcm_host_init = bcm_host.bcm_host_init, 180 | bcm_host_deinit = bcm_host.bcm_host_deinit, 181 | graphics_get_display_size = bcm_host.graphics_get_display_size, 182 | vc_dispmanx_display_open = bcm_host.vc_dispmanx_display_open, 183 | vc_dispmanx_display_close = bcm_host.vc_dispmanx_display_close, 184 | vc_dispmanx_update_start = bcm_host.vc_dispmanx_update_start, 185 | vc_dispmanx_element_add = bcm_host.vc_dispmanx_element_add, 186 | vc_dispmanx_element_remove = bcm_host.vc_dispmanx_element_remove, 187 | vc_dispmanx_element_change_attributes = 188 | bcm_host.vc_dispmanx_element_change_attributes, 189 | vc_dispmanx_update_submit_sync = bcm_host.vc_dispmanx_update_submit_sync 190 | } 191 | 192 | return bcm 193 | -------------------------------------------------------------------------------- /LjES/demo.lua: -------------------------------------------------------------------------------- 1 | -- --------------------------------------------- 2 | -- demo.lua 2014/06/06 3 | -- Copyright (c) 2013-2014 Jun Mizutani, 4 | -- released under the MIT open source license. 5 | -- --------------------------------------------- 6 | 7 | util = require("util") 8 | local termios = require("termios") 9 | 10 | require("BonePhong") 11 | require("Screen") 12 | require("Matrix") 13 | require("Space") 14 | require("Shape") 15 | require("Text") 16 | require("Message") 17 | 18 | local demo = {} 19 | 20 | globalQuit = false 21 | demo.keyStatus = false 22 | 23 | function demo.startCheckKey() 24 | termios.setRawMode() 25 | demo.keyStatus = true 26 | end 27 | 28 | function demo.endCheckKey() 29 | termios.restoreTermios() 30 | demo.keyStatus = false 31 | end 32 | 33 | function demo.checkKey(func) 34 | if (demo.keyStatus == false) then 35 | demo.startCheckKey() 36 | end 37 | local key = termios.realtimeKey() 38 | local scr = demo.aScreen 39 | local fw = scr.fullWidth 40 | local fh = scr.fullHeight 41 | if key ~= 0 then 42 | if (func ~= nil) then key = func(key) end 43 | if (key == 'q') then globalQuit = true 44 | elseif (key == 'p') then scr:screenShot() 45 | elseif (key == 't') then scr:move(fw / 2, fh / 2, fw / 2, 0) 46 | elseif (key == 'b') then scr:move(fw / 2, fh / 2, fw / 2, fh / 2) 47 | elseif (key == 'g') then scr:restoreSize() 48 | elseif (key == 'f') then scr:move(fw, fh, 0, 0) 49 | end 50 | end 51 | end 52 | 53 | function demo.setViewAngle(angle) 54 | local aspect = demo.aScreen.width / demo.aScreen.height 55 | local projMat = Matrix:new() 56 | projMat:makeProjectionMatrix(0.1, 1000, angle, aspect) 57 | demo.phong:setProjectionMatrix(projMat) 58 | end 59 | 60 | function demo.getShapeSize(shapes) 61 | local size = { 62 | minx = 1.0E10, maxx = -1.0E10, 63 | miny = 1.0E10, maxy = -1.0E10, 64 | minz = 1.0E10, maxz = -1.0E10, 65 | centerx = 0.0, sizex = 0.0, 66 | centery = 0.0, sizey = 0.0, 67 | centerz = 0.0, sizez = 0.0, 68 | } 69 | for i = 1, #shapes do 70 | local shape = shapes[i] 71 | local b = shape:getBoundingBox() 72 | if size.minx > b.minx then size.minx = b.minx end 73 | if size.maxx < b.maxx then size.maxx = b.maxx end 74 | if size.miny > b.miny then size.miny = b.miny end 75 | if size.maxy < b.maxy then size.maxy = b.maxy end 76 | if size.minz > b.minz then size.minz = b.minz end 77 | if size.maxz < b.maxz then size.maxz = b.maxz end 78 | end 79 | size.centerx = (size.maxx + size.minx)/2 80 | size.sizex = size.maxx - size.minx 81 | size.centery = (size.maxy + size.miny)/2 82 | size.sizey = size.maxy - size.miny 83 | size.centerz = (size.maxz + size.minz)/2 84 | size.sizez = size.maxz - size.minz 85 | size.max = math.max(size.maxx, size.maxy, size.maxz) 86 | return size 87 | end 88 | 89 | function demo.screen(width, height) 90 | demo.aScreen = Screen:new() 91 | local scr = demo.aScreen 92 | termios.getTermios() 93 | 94 | scr:init(width, height, 0, 0) 95 | 96 | local phong = BonePhong:new() 97 | phong:setDefaultParam("light", {0, 100, 1000, 1}) 98 | phong:init() 99 | demo.phong = phong 100 | Shape.ClassShader(phong) 101 | demo.setViewAngle(53) 102 | 103 | demo.aText = Text:new() 104 | demo.aText:init("font512.png") 105 | demo.aMessage = Message:new() 106 | demo.aMessage:init("font512.png") 107 | 108 | demo.aSpace = Space:new() 109 | end 110 | 111 | function demo.backgroundColor(r, g, b) 112 | demo.aScreen:setClearColor(r, g, b, 1.0) 113 | end 114 | 115 | function demo.getSpace() 116 | return demo.aSpace 117 | end 118 | 119 | function demo.printf(...) 120 | return util.printf(...) 121 | end 122 | 123 | function demo.getFrameCount() 124 | return demo.aScreen:getFrameCount() 125 | end 126 | 127 | function demo.textColor(r, g, b) 128 | demo.aText.shader:setColor(r, g, b) 129 | end 130 | 131 | function demo.textFontScale(scale) 132 | demo.aText.shader:setScale(scale) 133 | end 134 | 135 | function demo.messageColor(r, g, b) 136 | demo.aMessage:setColor(r, g, b) 137 | end 138 | 139 | function demo.messageFontScale(scale) 140 | demo.aMessage.shader:setScale(scale) 141 | end 142 | 143 | function demo.messageWrite(x, y, str) 144 | demo.aMessage:setMessage(1, x, y, str) 145 | end 146 | 147 | function demo.messageDraw() 148 | demo.aMessage:drawScreen() 149 | end 150 | 151 | function demo.loop(drawFunc, fpsFlag, keyFunc) 152 | local text = demo.aText 153 | local space = demo.aSpace 154 | local quit = false 155 | demo.aSpace:scanSkeletons() 156 | space:timerStart() 157 | while (globalQuit == false) and (quit ~= true) 158 | and (demo.aScreen:getFrameCount() < 50000) do 159 | demo.checkKey(keyFunc) 160 | demo.aScreen:clear() 161 | 162 | quit = drawFunc() 163 | -- Draw Bones 164 | demo.aScreen:clearDepthBuffer() 165 | demo.aSpace:drawBones() 166 | 167 | if fpsFlag then 168 | local fps = space:count() / space:uptime() 169 | text:writefAt(0, 0, "FPS : %8.4f", fps) 170 | text:drawScreen() 171 | end 172 | demo.aScreen:update() 173 | end 174 | end 175 | 176 | function demo.exit() 177 | util.sleep(0.05) 178 | if (demo.keyStatus) then 179 | demo.endCheckKey() 180 | end 181 | termios.restoreTermios() 182 | demo.aScreen:deinit() 183 | end 184 | 185 | return demo 186 | -------------------------------------------------------------------------------- /LjES/egl.lua: -------------------------------------------------------------------------------- 1 | -- --------------------------------------------- 2 | -- egl.lua 2017/12/07 3 | -- Copyright (c) 2013-2017 Jun Mizutani, 4 | -- released under the MIT open source license. 5 | -- --------------------------------------------- 6 | 7 | local ffi = require("ffi") 8 | local libegl = ffi.load("brcmEGL") 9 | 10 | ffi.cdef[[ 11 | typedef int EGLint; 12 | typedef unsigned int EGLBoolean; 13 | typedef unsigned int EGLenum; 14 | typedef void *EGLConfig; 15 | typedef void *EGLContext; 16 | typedef void *EGLDisplay; 17 | typedef void *EGLSurface; 18 | typedef void *EGLClientBuffer; 19 | typedef void *EGLNativeDisplayType; 20 | typedef void *EGLNativePixmapType; 21 | typedef void *EGLNativeWindowType; 22 | 23 | EGLDisplay eglGetDisplay(EGLNativeDisplayType display_id); 24 | EGLBoolean eglInitialize(EGLDisplay dpy, EGLint *major, EGLint *minor); 25 | EGLBoolean eglChooseConfig(EGLDisplay dpy, const EGLint *attrib_list, 26 | EGLConfig *configs, EGLint config_size, 27 | EGLint *num_config); 28 | 29 | EGLSurface eglCreateWindowSurface(EGLDisplay dpy, EGLConfig config, 30 | EGLNativeWindowType win, 31 | const EGLint *attrib_list); 32 | const char * eglQueryString(EGLDisplay dpy, EGLint name); 33 | 34 | EGLContext eglCreateContext(EGLDisplay dpy, EGLConfig config, 35 | EGLContext share_context, 36 | const EGLint *attrib_list); 37 | 38 | EGLBoolean eglMakeCurrent(EGLDisplay dpy, EGLSurface draw, 39 | EGLSurface read, EGLContext ctx); 40 | 41 | EGLBoolean eglSwapBuffers(EGLDisplay dpy, EGLSurface surface); 42 | 43 | EGLBoolean eglSwapInterval(EGLDisplay dpy, EGLint interval); 44 | ]] 45 | 46 | local egl = { 47 | NO_CONTEXT = ffi.cast("EGLContext", 0), 48 | NO_DISPLAY = ffi.cast("EGLDisplay", 0), 49 | NO_SURFACE = ffi.cast("EGLSurface", 0), 50 | VENDOR = 0x3053, 51 | VERSION = 0x3054, 52 | EXTENSIONS = 0x3055, 53 | CLIENT_APIS = 0x308D, 54 | ALPHA_SIZE = 0x3021, 55 | BLUE_SIZE = 0x3022, 56 | GREEN_SIZE = 0x3023, 57 | RED_SIZE = 0x3024, 58 | DEPTH_SIZE = 0x3025, 59 | NONE = 0x3038, 60 | CONTEXT_CLIENT_VERSION = 0x3098, 61 | DEFAULT_DISPLAY = 0, 62 | NO_CONTEXT = 0, 63 | NO_DISPLAY = 0, 64 | NO_SURFACE = 0, 65 | getDisplay = libegl.eglGetDisplay, 66 | initialize = libegl.eglInitialize, 67 | chooseConfig = libegl.eglChooseConfig, 68 | createWindowSurface = libegl.eglCreateWindowSurface, 69 | queryString = libegl.eglQueryString, 70 | createContext = libegl.eglCreateContext, 71 | makeCurrent = libegl.eglMakeCurrent, 72 | swapBuffers = libegl.eglSwapBuffers, 73 | swapInterval = libegl.eglSwapInterval 74 | } 75 | 76 | return egl 77 | -------------------------------------------------------------------------------- /LjES/font512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jun-mizutani/ljes/e82d4ef860801c9444b254235f72fc768d48b780/LjES/font512.png -------------------------------------------------------------------------------- /LjES/joystick.lua: -------------------------------------------------------------------------------- 1 | -- --------------------------------------------- 2 | -- joystick.lua 2014/09/16 3 | -- Copyright (c) 2014 Jun Mizutani, 4 | -- released under the MIT open source license. 5 | -- --------------------------------------------- 6 | 7 | local ffi = require("ffi") 8 | local bit = require("bit") 9 | 10 | local bnot, bor, band = bit.bnot, bit.bor, bit.band 11 | 12 | ffi.cdef[[ 13 | struct js_event { 14 | unsigned int time; /* event timestamp in milliseconds */ 15 | short value; /* value */ 16 | char type; /* event type */ 17 | char number; /* axis/button number */ 18 | }; 19 | 20 | static const int F_SETFL = 4; 21 | 22 | int ioctl(int, unsigned long, ...); 23 | int open(const char* filename, int flags); 24 | int read(int fd, void *buf, unsigned int nbytes); 25 | int fcntl(int fd, int cmd, ...); 26 | int close(int fd); 27 | ]] 28 | 29 | local joystick = { 30 | JS_EVENT_BUTTON = 0x1, 31 | JS_EVENT_AXIS = 0x2, 32 | JS_EVENT_INIT = 0x80, 33 | JSIOCGVERSION = 0x80046a01, 34 | JSIOCGAXES = 0x80016a11, 35 | JSIOCGBUTTONS = 0x80016a12, 36 | JSIOCGNAME = 0x80006a13, 37 | JSIOCSCORR = 0x40246a21, 38 | JSIOCGCORR = 0x80246a22, 39 | JSIOCSAXMAP = 0x40406a31, 40 | JSIOCGAXMAP = 0x80406a32, 41 | JSIOCSBTNMAP = 0x44006a33, 42 | JSIOCGBTNMAP = 0x84006a34, 43 | JS_CORR_NONE = 0x0, 44 | JS_CORR_BROKEN = 0x1, 45 | JS_RETURN = 0xc, 46 | JS_TRUE = 0x1, 47 | JS_FALSE = 0x0, 48 | JS_X_0 = 0x1, 49 | JS_Y_0 = 0x2, 50 | JS_X_1 = 0x4, 51 | JS_Y_1 = 0x8, 52 | JS_MAX = 0x2, 53 | JS_DEF_TIMEOUT = 0x1300, 54 | JS_DEF_CORR = 0x0, 55 | JS_DEF_TIMELIMIT= 0xa, 56 | JS_SET_CAL = 0x1, 57 | JS_GET_CAL = 0x2, 58 | JS_SET_TIMEOUT = 0x3, 59 | JS_GET_TIMEOUT = 0x4, 60 | JS_SET_TIMELIMIT= 0x5, 61 | JS_GET_TIMELIMIT= 0x6, 62 | JS_GET_ALL = 0x7, 63 | JS_SET_ALL = 0x8, 64 | 65 | BUTTON_T = 0, 66 | AXIS_T = 1, 67 | A_BTN = 1, 68 | B_BTN = 2, 69 | X_BTN = 3, 70 | Y_BTN = 4, 71 | L_BTM = 5, 72 | R_BTN = 6, 73 | SELECT = 7, 74 | START = 8, 75 | LEFT = -16, 76 | RIGHT = 16, 77 | UP = -32, 78 | DOWN = 32, 79 | initialized = false 80 | } 81 | 82 | joystick.devices = {} 83 | 84 | function joystick.openBlockingMode(device) 85 | local O_RDONLY = 0 86 | local fd = ffi.C.open(device, O_RDONLY) 87 | return fd 88 | end 89 | 90 | function joystick.open(device) 91 | local O_RDONLY = 0 92 | local O_NONBLOCK = 0x800 93 | local fd = ffi.C.open(device, O_RDONLY + O_NONBLOCK) 94 | return fd 95 | end 96 | 97 | function joystick.openJoystick(n, mode) 98 | if (n >= 0) and (n <=7) then 99 | local m = n + 1 100 | local fd = joystick.open("/dev/input/js" .. tonumber(n)) 101 | if fd >= 0 then 102 | local device = {} 103 | device.num = n 104 | device.fd = fd 105 | table.insert(joystick.devices, device) 106 | return fd 107 | else 108 | return -1 109 | end 110 | else 111 | return -1 112 | end 113 | end 114 | 115 | function joystick.setDeviceInfo() 116 | local version = ffi.new("int[1]") 117 | local axes = ffi.new("unsigned char[1]") 118 | local buttons = ffi.new("unsigned char[1]") 119 | local name = ffi.new("char[128]") 120 | for i = 1, #joystick.devices do 121 | local fd = joystick.devices[i].fd 122 | ffi.C.ioctl(fd, joystick.JSIOCGVERSION, version) 123 | ffi.C.ioctl(fd, joystick.JSIOCGAXES, axes) 124 | ffi.C.ioctl(fd, joystick.JSIOCGBUTTONS, buttons) 125 | ffi.C.ioctl(fd, joystick.JSIOCGNAME + 128 * 0x10000, name) 126 | joystick.devices[i].version = version[0] 127 | joystick.devices[i].num_axes = axes[0] 128 | joystick.devices[i].num_buttons = buttons[0] 129 | joystick.devices[i].name = ffi.string(name) 130 | joystick.devices[i].axes = {} 131 | joystick.devices[i].buttons = {} 132 | for j = 1, axes[0] do 133 | table.insert(joystick.devices[i].axes, 134 | {type = 0, number = 0, value = 0, time = 0}) 135 | end 136 | for j = 1, buttons[0] do 137 | table.insert(joystick.devices[i].buttons, 138 | {type = 0, number = 0, value = 0, time = 0}) 139 | end 140 | end 141 | end 142 | 143 | function joystick.init() 144 | for i = 0, 7 do 145 | joystick.openJoystick(i) 146 | end 147 | if #joystick.devices > 0 then 148 | joystick.setDeviceInfo() 149 | joystick.initialized = true 150 | end 151 | return #joystick.devices 152 | end 153 | 154 | function joystick.readOneEvent(device) 155 | if (device < 1) or (device > #joystick.devices) then 156 | return nil -- invalid device 157 | end 158 | local fd = joystick.devices[device].fd 159 | local js = ffi.new("struct js_event[1]") 160 | local size = ffi.sizeof(js) 161 | local res = ffi.C.read(fd, js, size) 162 | if res == size then 163 | local event = js[0] 164 | event.type = band(event.type, bnot(joystick.JS_EVENT_INIT)) 165 | return event 166 | else 167 | return nil 168 | end 169 | end 170 | 171 | function joystick.readAllEvents(device) 172 | local event_list = {} 173 | local event = joystick.readOneEvent(device) 174 | while (event ~= nil) do 175 | table.insert(event_list, event) 176 | event = joystick.readOneEvent(device) 177 | end 178 | if #event_list == 0 then return 0 end 179 | for j = 1, #event_list do 180 | local js = event_list[j] 181 | local num = js.number + 1 182 | if js.type == joystick.JS_EVENT_BUTTON then 183 | joystick.devices[device].buttons[num].value = js.value 184 | joystick.devices[device].buttons[num].time = js.time 185 | elseif js.type == joystick.JS_EVENT_AXIS then 186 | joystick.devices[device].axes[num].value = js.value 187 | joystick.devices[device].axes[num].time = js.time 188 | end 189 | end 190 | return #event_list 191 | end 192 | 193 | function joystick.readAllDevices() 194 | -- for every device 195 | for i = 1, #joystick.devices do 196 | joystick.readAllEvents(i) 197 | end 198 | end 199 | 200 | function joystick.getNoOfDevices() 201 | return #joystick.devices 202 | end 203 | 204 | function joystick.getName(device_num) 205 | return joystick.devices[device_num].name 206 | end 207 | 208 | function joystick.getVersion(device_num) 209 | return joystick.devices[device_num].version 210 | end 211 | 212 | function joystick.getNoOfAxes(device_num) 213 | return joystick.devices[device_num].num_axes 214 | end 215 | 216 | function joystick.getNoOfButtons(device_num) 217 | return joystick.devices[device_num].num_buttons 218 | end 219 | 220 | -- return the button value 221 | -- device_num: 1..8, button_num: 1..n 222 | function joystick.getButton(device_num, button_num) 223 | return joystick.devices[device_num].buttons[button_num].value 224 | end 225 | 226 | -- return the time at button-value-change 227 | -- device_num: 1..8, button_num: 1..n 228 | function joystick.getButtonTime(device_num, button_num) 229 | return joystick.devices[device_num].buttons[button_num].time 230 | end 231 | 232 | -- return the axis value 233 | -- device_num: 1..8, axis_num: 1..m 234 | function joystick.getAxis(device_num, axis_num) 235 | return joystick.devices[device_num].axes[axis_num].value 236 | end 237 | 238 | -- return the time at axis-value-change 239 | -- device_num: 1..8, axis_num: 1..m 240 | function joystick.getAxisTime(device_num, axis_num) 241 | return joystick.devices[device_num].axes[axis_num].time 242 | end 243 | 244 | -- return the current bit pattern 245 | -- device_num: 1..8 246 | function joystick.getPattern(device_num) 247 | local btn_pattern = 0 248 | local axis_pattern = 0 249 | local device = joystick.devices[device_num] 250 | for I=1, device.num_buttons do 251 | if device.buttons[i].value > 0 then 252 | btn_pattern = bor(btn_pattern, bshl(1, I)) 253 | end 254 | end 255 | for I=1, device.num_axes do 256 | if device.axes[i].value > 10000 then 257 | axis_pattern = bor(axis_pattern, bshl(1, I*2)) 258 | elseif device.axes[i].value < -10000 then 259 | axis_pattern = bor(axis_pattern, bshl(2, I*2)) 260 | end 261 | end 262 | return btn_pattern, axis_pattern 263 | end 264 | 265 | -- return the current bit pattern 266 | -- device_num: 1..8 267 | function joystick.checkPattern(device_num, type, pattern) 268 | local btn, axis = joystick.getPattern(device_num) 269 | if type == joystick.BUTTON_T then 270 | if btn == pattern then 271 | return true 272 | else 273 | return false 274 | end 275 | elseif type == joystick.AXIS_T then 276 | if axis == pattern then 277 | return true 278 | else 279 | return false 280 | end 281 | end 282 | return false 283 | end 284 | 285 | return joystick 286 | -------------------------------------------------------------------------------- /LjES/termios.lua: -------------------------------------------------------------------------------- 1 | -- --------------------------------------------- 2 | -- termios.lua 2013/03/20 3 | -- Copyright (c) 2013 Jun Mizutani, 4 | -- released under the MIT open source license. 5 | -- --------------------------------------------- 6 | 7 | --[[ 8 | termios.getTermios() 9 | termios.setTermios() 10 | termios.restoreTermios() 11 | termios.setRawMode() 12 | termios.resetRawMode() 13 | termios.noEcho() 14 | termios.echo() 15 | termios.noWait() 16 | termios.wait() 17 | termios.realtimeKey() 18 | ]] 19 | 20 | local ffi = require("ffi") 21 | local bit = require("bit") 22 | 23 | ffi.cdef[[ 24 | typedef unsigned char cc_t; 25 | typedef unsigned int speed_t; 26 | typedef unsigned int tcflag_t; 27 | 28 | struct termios 29 | { 30 | tcflag_t c_iflag; /* input mode flags */ 31 | tcflag_t c_oflag; /* output mode flags */ 32 | tcflag_t c_cflag; /* control mode flags */ 33 | tcflag_t c_lflag; /* local mode flags */ 34 | cc_t c_line; /* line discipline */ 35 | cc_t c_cc[32]; /* control characters */ 36 | speed_t c_ispeed; /* input speed */ 37 | speed_t c_ospeed; /* output speed */ 38 | }; 39 | 40 | int tcgetattr(int fd, struct termios *termios_p); 41 | int tcsetattr(int fd, int optional_actions, const struct termios *termios_p); 42 | 43 | ]] 44 | 45 | local termios = { 46 | -- c_cc characters 47 | VINTR = 0, 48 | VQUIT = 1, 49 | VERASE = 2, 50 | VKILL = 3, 51 | VEOF = 4, 52 | VTIME = 5, 53 | VMIN = 6, 54 | VSWTC = 7, 55 | VSTART = 8, 56 | VSTOP = 9, 57 | VSUSP = 10, 58 | VEOL = 11, 59 | VREPRINT = 12, 60 | VDISCARD = 13, 61 | VWERASE = 14, 62 | VLNEXT = 15, 63 | VEOL2 = 16, 64 | -- c_lflag bits 65 | ICANON = 2, 66 | XCASE = 4, 67 | ECHO = 8, 68 | ECHOE = 16, 69 | ECHOK = 32, 70 | ECHONL = 64, 71 | NOFLSH = 128, 72 | TOSTOP = 256, 73 | -- tcsetattr 74 | TCSANOW = 0, 75 | TCSADRAIN = 1, 76 | TCSAFLUSH = 2 77 | } 78 | 79 | local bnot, bor, band = bit.bnot, bit.bor, bit.band 80 | 81 | termios.old_termios = ffi.new("struct termios[1]") 82 | termios.new_termios = ffi.new("struct termios[1]") 83 | termios.new = nil 84 | termios.old = nil 85 | termios.status = false 86 | 87 | function termios.getTermios() 88 | if termios.status == false then 89 | ffi.C.tcgetattr(0, termios.old_termios) 90 | ffi.C.tcgetattr(0, termios.new_termios) 91 | termios.new = termios.new_termios[0] 92 | termios.old = termios.old_termios[0] 93 | termios.status = true 94 | end 95 | end 96 | 97 | function termios.setTermios() 98 | if termios.status then 99 | ffi.C.tcsetattr(0, termios.TCSANOW, termios.new_termios) 100 | end 101 | end 102 | 103 | function termios.restoreTermios() 104 | if termios.status then 105 | ffi.C.tcsetattr(0, termios.TCSANOW, termios.old_termios) 106 | end 107 | end 108 | 109 | function termios.setRawMode() 110 | if termios.status then 111 | termios.new["c_lflag"] = band(termios.new["c_lflag"], bnot(termios.ECHO), 112 | bnot(termios.ECHONL), bnot(termios.ICANON)) 113 | termios.new["c_cc"][termios.VTIME] = 0 114 | termios.new["c_cc"][termios.VMIN] = 1 115 | termios.setTermios() 116 | end 117 | end 118 | 119 | function termios.resetRawMode() 120 | if termios.status then 121 | termios.new["c_lflag"] = bor(termios.new["c_lflag"], termios.ICANON, 122 | termios.ECHO, termios.ECHONL) 123 | termios.new["c_cc"][termios.VTIME] = 0 124 | termios.new["c_cc"][termios.VMIN] = 1 125 | termios.setTermios() 126 | end 127 | end 128 | 129 | function termios.noEcho() 130 | if termios.status then 131 | termios.new["c_lflag"] = band(termios.new["c_lflag"], 132 | bnot(termios.ECHO), bnot(termios.ECHONL)) 133 | termios.setTermios() 134 | end 135 | end 136 | 137 | function termios.echo() 138 | if termios.status then 139 | termios.new["c_lflag"] = bor(termios.new["c_lflag"], 140 | termios.ECHO, termios.ECHONL) 141 | termios.setTermios() 142 | end 143 | end 144 | 145 | function termios.noWait() 146 | if termios.status then 147 | termios.new["c_cc"][termios.VMIN] = 0 148 | termios.setTermios() 149 | end 150 | end 151 | 152 | function termios.wait() 153 | if termios.status then 154 | termios.new["c_cc"][termios.VMIN] = 1 155 | termios.setTermios() 156 | end 157 | end 158 | 159 | function termios.realtimeKey() 160 | local key, code 161 | termios.noWait() 162 | key = io.read(1) 163 | if key == nil then 164 | code = 0 165 | else 166 | code = string.byte(key) 167 | end 168 | termios.wait() 169 | return string.char(code) 170 | end 171 | 172 | return termios 173 | -------------------------------------------------------------------------------- /LjES/util.lua: -------------------------------------------------------------------------------- 1 | -- --------------------------------------------- 2 | -- util.lua 2014/06/05 3 | -- Copyright (c) 2013-2014 Jun Mizutani, 4 | -- released under the MIT open source license. 5 | -- --------------------------------------------- 6 | 7 | local ffi = require("ffi") 8 | 9 | ffi.cdef[[ 10 | typedef struct timeval { 11 | long tv_sec; 12 | long tv_usec; 13 | } timeval; 14 | 15 | int gettimeofday(struct timeval *tv, void *tz); 16 | int poll(struct pollfd *fds, unsigned long nfds, int timeout); 17 | ]] 18 | 19 | 20 | local util = {} 21 | 22 | function util.sleep(sec) 23 | ffi.C.poll(nil, 0, sec * 1000) 24 | end 25 | 26 | function util.gettimeofday() 27 | local t = ffi.new("timeval") 28 | ffi.C.gettimeofday(t, nil) 29 | return t.tv_sec, t.tv_usec 30 | end 31 | 32 | function util.now() 33 | local sec, usec = util.gettimeofday() 34 | return tonumber(sec) + tonumber(usec) / 1000000 35 | end 36 | 37 | function util.packagePath() 38 | for s in string.gmatch(package.path, ".-;") do 39 | local path = string.match(s, ".+LjES/") 40 | if path ~= nil then return path end 41 | end 42 | return nil 43 | end 44 | 45 | function util.isFileExist(file) 46 | local fh, errormsg = io.open(file) 47 | if fh then 48 | fh:close() 49 | return true 50 | else 51 | return false 52 | end 53 | end 54 | 55 | function util.readFile(filename) 56 | local f = assert(io.open(filename, "rb")) 57 | local text = f:read("*all") 58 | f:close() 59 | return text 60 | end 61 | 62 | function util.printf(fmt, ...) 63 | io.write(string.format(fmt, ...)) 64 | end 65 | 66 | function util.print() 67 | io.write("\n") 68 | end 69 | 70 | function util.countTableElements(tbl) 71 | local count = 0 72 | for _ in pairs(tbl) do 73 | count = count + 1 74 | end 75 | return count 76 | end 77 | 78 | -- simple table copy 79 | -- No:circular reference, keys which are tables, metatable 80 | function util.copyTable(dest, source) 81 | for key, value in pairs(source) do 82 | if type(value) == 'table' then 83 | local t ={} 84 | util.copyTable(t, value) 85 | dest[key] = t 86 | else 87 | dest[key] = value 88 | end 89 | end 90 | end 91 | 92 | function util.checkTable(Table) 93 | util.printf("------ %15s ---(meta)---> %s\n", Table, getmetatable(Table)) 94 | local nameList = {} 95 | for name, value in pairs(Table) do 96 | nameList[#nameList + 1] = name 97 | end 98 | table.sort(nameList) 99 | for i = 1, #nameList do 100 | util.printf("%24s -- %s\n", nameList[i], Table[nameList[i]]) 101 | end 102 | util.printf("\n") 103 | 104 | local mt = getmetatable(Table) 105 | if mt ~= nil then 106 | local next = mt.__index 107 | if next ~= nil then util.checkTable(next) end 108 | end 109 | end 110 | 111 | return util 112 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | LjES : 3D framework for LuaJIT on Raspberry Pi 2 | 3 | 2017/12/07 Jun Mizutani 4 | https://www.mztn.org/rpi/rpi_ljes2.html 5 | 6 | LjES is a small 3D framework for Raspberry Pi built on top of the 7 | OpenGL ES 2.0. LjES is written by 100% LuaJIT. The goal of this 8 | framework is to make 3D graphics programming easy. The Raspbian 9 | Linux disk image contains the LuaJIT-2.0. You don't need to install 10 | any packages on Raspberry Pi to use LjES. 11 | 12 | o LjES-2.0 supports Collada file loading and skeletal animation. 13 | o ColladaShape class can load the collada format exported by Blender. 14 | o BonePhong shader supports up to 40 bones per mesh. 15 | 16 | Usage : 17 | tar zxf ljes-2.01.tar.gz 18 | cd ljes-2.01 19 | cd examples 20 | luajit demo_spheres.lua 21 | luajit hand.lua 22 | 23 | Hit [q] key to quit, [p] for screenshot. 24 | 25 | Example : 26 | 27 | package.path = "../LjES/?.lua;" .. package.path 28 | local demo = require("demo") 29 | 30 | demo.screen(0, 0) 31 | local aSpace = demo.getSpace() 32 | local eye = aSpace:addNode(nil, "eye") 33 | eye:setPosition(0, 0, 30) 34 | demo.backgroundColor(0.2, 0.2, 0.4) 35 | 36 | local shape = Shape:new() 37 | shape:donut(8, 3, 16, 16) 38 | shape:endShape() 39 | shape:shaderParameter("color", {1.0, 0.5, 0.6, 1.0}) 40 | 41 | local node = aSpace:addNode(nil, "node1") 42 | node:setPosition(0, 0, 0) 43 | node:addShape(shape) 44 | 45 | function draw() 46 | node:rotateX(0.1) 47 | aSpace:draw(eye) 48 | end 49 | 50 | demo.loop(draw, true) 51 | demo.exit() 52 | -------------------------------------------------------------------------------- /examples/CubeMap_dice.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jun-mizutani/ljes/e82d4ef860801c9444b254235f72fc768d48b780/examples/CubeMap_dice.png -------------------------------------------------------------------------------- /examples/axis.lua: -------------------------------------------------------------------------------- 1 | #!/usr/bin/luajit 2 | -- --------------------------------------------- 3 | -- axis.lua 2013/04/10, 2014/03/30 4 | -- Copyright (c) 2013-2014 Jun Mizutani, 5 | -- released under the MIT open source license. 6 | -- --------------------------------------------- 7 | 8 | package.path = "../LjES/?.lua;" .. package.path 9 | 10 | require "Message" 11 | local demo = require("demo") -- changed from "demo2" 12 | 13 | function createArrowShape(length, r, g, b) 14 | local obj = Shape:new() 15 | obj:arrow(length, length / 10, length / 100, 12) 16 | obj:endShape() 17 | obj:shaderParameter("color", {r, g, b, 1.0}) 18 | obj:shaderParameter("ambient", 0.5) 19 | return obj 20 | end 21 | 22 | function createCoordNode(space, length) 23 | local shapeX = createArrowShape(length, 1.0, 0.0, 0.0) 24 | local shapeY = createArrowShape(length, 0.0, 0.8, 0.0) 25 | local shapeZ = createArrowShape(length, 0.0, 0.0, 1.0) 26 | local base = space:addNode(nil, "base") 27 | local nodeX = space:addNode(base, "X") 28 | local nodeY = space:addNode(base, "Y") 29 | local nodeZ = space:addNode(base, "Z") 30 | nodeX:addShape(shapeX) 31 | nodeY:addShape(shapeY) 32 | nodeZ:addShape(shapeZ) 33 | nodeX:setAttitude(0.0, 0.0, -90.0) 34 | nodeZ:setAttitude(0.0, 90.0, 0.0) 35 | return base 36 | end 37 | 38 | demo.screen(0, 0) 39 | demo.backgroundColor(1.0, 1.0, 1.0) 40 | local aSpace = demo.getSpace() 41 | local eye = aSpace:addNode(nil, "eye") 42 | eye:setPosition(0, 10, 23) 43 | eye:setAttitude(0, -18, 0) 44 | 45 | local aMes = Message:new() 46 | local ok = aMes:init("font512.png") 47 | aMes:setScale(4.0) 48 | aMes:setColor(1.0, 0.0, 0.0) 49 | aMes:writeMessage(16, 4, "X") 50 | aMes:setColor(0.0, 0.8, 0.0) 51 | aMes:writeMessage(11, 1, "Y") 52 | aMes:setColor(0.0, 0.0, 1.0) 53 | aMes:writeMessage(6, 4, "Z") 54 | 55 | local node = createCoordNode(aSpace, 10.0) 56 | node:setPosition(0, 0, 0) 57 | node:setAttitude(-15, 0, 0) 58 | 59 | local arrow_shape = Shape:new() 60 | arrow_shape:arrow(2, 1.0, 0.2, 12) 61 | arrow_shape:endShape() 62 | arrow_shape:shaderParameter("color", {0.9, 0.6, 0.0, 1.0}) 63 | arrow_shape:shaderParameter("ambient", 0.5) 64 | local arrow = aSpace:addNode(nil, "arrow") 65 | arrow:addShape(arrow_shape) 66 | arrow:setPosition(2, 2, 2) 67 | arrow:setAttitude(0, -45, -30) 68 | 69 | demo.textFontScale(2.0) 70 | demo.textColor(0, 0, 0) 71 | 72 | function draw() 73 | arrow:rotateX(0.2) 74 | aSpace:draw(eye) 75 | aMes:drawScreen() 76 | end 77 | 78 | demo.loop(draw, false) 79 | demo.exit() 80 | -------------------------------------------------------------------------------- /examples/dae_anim.lua: -------------------------------------------------------------------------------- 1 | #!/usr/bin/luajit 2 | -- --------------------------------------------- 3 | -- dae_anim.lua 2014/03/30 4 | -- Copyright (c) 2014 Jun Mizutani, 5 | -- released under the MIT open source license. 6 | -- --------------------------------------------- 7 | 8 | -- usage 9 | -- $ luajit dae_anim.lua collada.dae texture.png 10 | 11 | package.path = "../LjES/?.lua;" .. package.path 12 | 13 | local demo = require("demo") 14 | require("ColladaShape") 15 | require("Schedule") 16 | 17 | -- globals 18 | local TEXTUTE_FILE = "" 19 | local COLLADA_FILE = "hand.dae" 20 | local g_totalVertices = 0 21 | local g_totalTriangles = 0 22 | local skeletons = {} 23 | local bones = {} 24 | local BONE = 1 25 | local MESH = 1 26 | local MAXBONE = 1 27 | local HELP = false 28 | local MAXMESH = 1 29 | local anim_stopped = false 30 | local shapes 31 | 32 | demo.screen(0, 0) 33 | local aSpace = demo.getSpace() 34 | 35 | function setBoneParams() 36 | bones = skeletons[MESH] 37 | if bones then MAXBONE = #bones end 38 | end 39 | 40 | function showBones(true_or_false) 41 | for i = 1, #shapes do 42 | local shape = shapes[i] 43 | local skeleton = shape:getSkeleton() 44 | skeleton:showBone(true_or_false) 45 | end 46 | demo.aSpace:scanSkeletons() 47 | end 48 | 49 | function createBoneShape(a, r, g, b) 50 | local shape = Shape:new() 51 | shape:simpleBone(a) 52 | shape:shaderParameter("color", {r, g, b, 1.0}) 53 | return shape 54 | end 55 | 56 | -- -------------------------------------------------------------------- 57 | -- main 58 | -- -------------------------------------------------------------------- 59 | 60 | if arg[1] ~= nil then COLLADA_FILE = arg[1] end 61 | if arg[2] ~= nil then TEXTUTE_FILE = arg[2] end 62 | 63 | demo.backgroundColor(0.2, 0.2, 0.4) 64 | 65 | -- texture 66 | if TEXTUTE_FILE ~= "" then 67 | require("Texture") 68 | tex = Texture:new() 69 | tex:readImageFromFile(TEXTUTE_FILE) 70 | end 71 | local start = aSpace:now() 72 | local boneFlag = true 73 | local verboseFlag = false 74 | 75 | -- load collada 76 | local collada = ColladaShape:new() 77 | local collada_text = util.readFile(COLLADA_FILE) 78 | collada:parse(collada_text ,verboseFlag) 79 | shapes = collada:makeShapes(boneFlag, verboseFlag) 80 | collada:releaseMeshes() 81 | for i = 1, #shapes do 82 | shapes[i]:releaseObjects() 83 | end 84 | if shapes[1].anim ~= nil then 85 | shapes[1].anim:list() 86 | end 87 | MAXMESH = #shapes 88 | 89 | -- print loading time 90 | local time = aSpace:now() - start 91 | util.printf("loading time = %f sec \n", time) 92 | 93 | local node = aSpace:addNode(nil, "node1") 94 | node:setPosition(0, 0, 0) 95 | local fig = aSpace:addNode(node, "fig") 96 | fig:setPosition(0, 0, 0) 97 | fig:setAttitude(0, 0, 0) 98 | 99 | -- set bone shape 100 | local size = demo.getShapeSize(shapes) 101 | local bone_shape = createBoneShape(size.max/100, 1, 1, 1) 102 | 103 | -- set eye position 104 | local eye = aSpace:addNode(nil, "eye") 105 | eye:setPosition(size.centerx, size.centery, size.max * 1.2) 106 | 107 | for i = 1, #shapes do 108 | local shape = shapes[i] 109 | local skeleton = shape:getSkeleton() 110 | if (skeleton ~= nil) and (skeleton:getBoneCount() > 0) then 111 | skeleton:setBoneShape(bone_shape) 112 | table.insert(skeletons, skeleton:getBoneOrder()) 113 | else 114 | util.printf("No skeleton") 115 | end 116 | 117 | fig:addShape(shape) 118 | if TEXTUTE_FILE ~= "" then 119 | shape:shaderParameter("use_texture", 1) 120 | shape:shaderParameter("texture", tex) 121 | shape:shaderParameter("color", {1.0, 1.0, 1.0, 1.0}) 122 | else 123 | shape:shaderParameter("use_texture", 0) 124 | shape:shaderParameter("color", {0.8, 0.7, 0.6, 1.0}) 125 | end 126 | util.printf("object vertex#=%d triangle#=%d\n", shape:getVertexCount(), 127 | shape:getTriangleCount()) 128 | g_totalVertices = g_totalVertices + shape:getVertexCount() 129 | g_totalTriangles = g_totalTriangles + shape:getTriangleCount() 130 | end 131 | 132 | demo.aText:setScale(1) 133 | setBoneParams() 134 | 135 | -- -------------------------------------------- 136 | -- [1][2][3][4][5][6][7][8][9][0][-][^][\] 137 | -- [Q][W][e][r][T][y][u][I][o][P][@] 138 | -- [A][S][D][F][G][h][J][K][l] 139 | -- [Z][X][c][v][B][n][m][,][.][/] 140 | -- -------------------------------------------- 141 | function keyFunc(key) 142 | local ROT = 1.0 143 | local MOV = 0.3 144 | 145 | if (key == 'h') then 146 | demo.aText:clearScreen() 147 | if HELP then HELP = false else HELP = true end 148 | elseif (key == 'w') then node:rotateX(ROT) 149 | elseif (key == 's') then node:rotateX(-ROT) 150 | elseif (key == 'a') then node:rotateY(ROT) 151 | elseif (key == 'd') then node:rotateY(-ROT) 152 | elseif (key == 'z') then node:rotateZ(ROT) 153 | elseif (key == 'x') then node:rotateZ(-ROT) 154 | 155 | elseif (key == 'j') then 156 | BONE = BONE - 1 157 | if BONE < 1 then BONE = 1 end 158 | elseif (key == 'k') then 159 | BONE = BONE + 1 160 | if BONE > MAXBONE then BONE = MAXBONE end 161 | elseif (key == 'o') then fig.shapes[MESH]:hide(true) 162 | elseif (key == 'i') then fig.shapes[MESH]:hide(false) 163 | elseif (key == 'm') then 164 | MESH = MESH + 1 165 | if MESH > MAXMESH then MESH = MAXMESH end 166 | setBoneParams() 167 | if BONE > MAXBONE then BONE = MAXBONE end 168 | elseif (key == 'n') then 169 | MESH = MESH - 1 if MESH < 1 then MESH = 1 end 170 | setBoneParams() 171 | if BONE > MAXBONE then BONE = MAXBONE end 172 | elseif (key == '1') then eye:move( MOV, 0, 0) 173 | elseif (key == '2') then eye:move(-MOV, 0, 0) 174 | elseif (key == '3') then eye:move( 0, MOV, 0) 175 | elseif (key == '4') then eye:move( 0,-MOV, 0) 176 | elseif (key == '5') then eye:move( 0, 0, MOV) 177 | elseif (key == '6') then eye:move( 0, 0,-MOV) 178 | elseif (key == '9') then showBones(true) 179 | elseif (key == '0') then showBones(false) 180 | elseif (key == '@') then 181 | fig.shapes[1].anim:start() 182 | anim_stopped = false 183 | elseif (key == '/') then 184 | fig.shapes[1].anim:transitionTo(1000, 1, -1) 185 | -- BONE は MESH に依存することに注意 186 | elseif bones ~= nil then 187 | if (key == 'e') then bones[BONE]:rotateX(ROT) 188 | elseif (key == 'r') then bones[BONE]:rotateX(-ROT) 189 | elseif (key == 'c') then bones[BONE]:rotateZ(ROT) 190 | elseif (key == 'v') then bones[BONE]:rotateZ(-ROT) 191 | elseif (key == 'y') then bones[BONE]:rotateY(ROT) 192 | elseif (key == 'u') then bones[BONE]:rotateY(-ROT) 193 | end 194 | end 195 | return key 196 | end 197 | 198 | function draw_help() 199 | local t = demo.aText 200 | t:writeAt(3, 3, "+ -") 201 | t:writeAt(2, 4, "[w] - [s] : rotate X") 202 | t:writeAt(2, 5, "[a] - [d] : rotate Y") 203 | t:writeAt(2, 6, "[z] - [x] : rotate Z") 204 | t:writeAt(0, 7, "Bone") 205 | t:writeAt(2, 8, "[e] - [r] : rotate X") 206 | t:writeAt(2, 9, "[c] - [v] : rotate Z") 207 | t:writeAt(2,10, "[y] - [u] : rotate Y") 208 | t:writeAt(2,12, "[n] - [m] : select Mesh") 209 | t:writeAt(2,13, "[j] - [k] : select Bone") 210 | t:writeAt(2,14, "[o] - [i] : hide / show") 211 | t:writeAt(2,15, "[0] - [9] : hide/show bones") 212 | t:writeAt(2,16, "[p] : Screenshot") 213 | t:writeAt(2,17, "[t] : on top") 214 | t:writeAt(2,18, "[b] : on bottom") 215 | t:writeAt(2,19, "[g] : initial screen") 216 | t:writeAt(2,20, "[f] : full screen ") 217 | t:writeAt(2,21, "[h] : help on/off ") 218 | t:writeAt(2,22, "[@] : Replay animation") 219 | t:writeAt(2,24, "[q] : QUIT") 220 | end 221 | 222 | local g_count = 0 223 | 224 | function draw() 225 | local t = demo.aText 226 | if HELP then 227 | draw_help() 228 | else 229 | t:writefAt(0, 3, "Vertices :%6d", g_totalVertices) 230 | t:writefAt(0, 4, "Triangles :%6d", g_totalTriangles) 231 | t:writeAt(2, 21, "[h] : help on/off ") 232 | end 233 | 234 | --node:rotateY(0.5) 235 | if fig.shapes[1].anim ~= nil then 236 | local running = fig.shapes[1].anim:play() 237 | if running < 0 then fig.shapes[1].anim:start() end 238 | end 239 | aSpace:draw(eye) 240 | if bones ~= nil and bones[BONE] ~= nil then 241 | t:writefAt(0, 2, "Mesh:%2d/%2d Bone:%2d/%2d [%s] ", 242 | MESH, MAXMESH, BONE, MAXBONE, bones[BONE].name) 243 | t:writefAt(40, 0, "World H:%6.2f, P:%6.2f, B:%6.2f", 244 | bones[BONE]:getWorldAttitude()) 245 | t:writefAt(46, 1, "X:%6.2f, Y:%6.2f, Z:%6.2f", 246 | unpack(bones[BONE]:getWorldPosition())) 247 | t:writefAt(40, 2, "Local h:%6.2f, p:%6.2f, b:%6.2f", 248 | bones[BONE]:getLocalAttitude()) 249 | t:writefAt(46, 3, "x:%6.2f, y:%6.2f, z:%6.2f", 250 | unpack(bones[BONE]:getPosition())) 251 | --[[ 252 | t:writefAt(40, 5, "Node H:%6.2f, P:%6.2f, B:%6.2f", 253 | fig:getWorldAttitude()) 254 | t:writefAt(46, 6, "X:%6.2f, Y:%6.2f, Z:%6.2f", 255 | unpack(fig:getWorldPosition())) 256 | --]] 257 | else 258 | t:writefAt(0,2, "Mesh:%2d/%2d Bone:%2d/%2d ", 259 | MESH, MAXMESH, BONE, MAXBONE) 260 | end 261 | end 262 | 263 | --showBones(true) 264 | if fig.shapes[1].anim ~= nil then 265 | fig.shapes[1].anim:start() 266 | end 267 | demo.loop(draw, true, keyFunc) 268 | demo.exit() 269 | 270 | -------------------------------------------------------------------------------- /examples/dae_anim0.lua: -------------------------------------------------------------------------------- 1 | #!/usr/bin/luajit 2 | -- --------------------------------------------- 3 | -- dae_view.lua 2014/03/30 4 | -- Copyright (c) 2014 Jun Mizutani, 5 | -- released under the MIT open source license. 6 | -- --------------------------------------------- 7 | 8 | package.path = "../LjES/?.lua;" .. package.path 9 | 10 | local demo = require("demo") 11 | require("ColladaShape") 12 | 13 | if arg[1] ~= nil then 14 | collada_file = arg[1] 15 | else 16 | print("usage: luajit dae_anim0.lua your_collada.dae") 17 | return 18 | end 19 | 20 | demo.screen(0, 0) 21 | local aSpace = demo.getSpace() 22 | demo.backgroundColor(0.2, 0.2, 0.4) 23 | 24 | -- load collada 25 | local collada = ColladaShape:new() 26 | local collada_text = util.readFile(collada_file) 27 | collada:parse(collada_text ,true) 28 | local shapes = collada:makeShapes(true, true) 29 | 30 | -- set object 31 | local node = aSpace:addNode(nil, "node") 32 | for i = 1, #shapes do 33 | shapes[i]:shaderParameter("color", {0.8, 0.7, 0.6, 1.0}) 34 | node:addShape(shapes[i]) 35 | end 36 | 37 | -- set eye position 38 | local eye = aSpace:addNode(nil, "eye") 39 | local size = demo.getShapeSize(shapes) 40 | eye:setPosition(size.centerx, size.centery, size.max * 1.2) 41 | 42 | -- setup animation 43 | node.shapes[1].anim:start() 44 | 45 | -- call back function 46 | function draw() 47 | demo.aText:writeAt(2, 24, "[q] : quit") 48 | node.shapes[1].anim:play() -- play one frame of animation 49 | aSpace:draw(eye) 50 | end 51 | 52 | -- main loop 53 | demo.loop(draw, true) 54 | demo.exit() 55 | -------------------------------------------------------------------------------- /examples/dae_gifanim.lua: -------------------------------------------------------------------------------- 1 | #!/usr/bin/luajit 2 | -- --------------------------------------------- 3 | -- dae_gifanim.lua 2014/03/30 4 | -- Copyright (c) 2014 Jun Mizutani, 5 | -- released under the MIT open source license. 6 | -- --------------------------------------------- 7 | 8 | -- usage 9 | -- $ luajit dae_gifanim.lua collada.dae texture.png 10 | -- $ convert -delay 3 ss_*.png dae_anim.gif 11 | 12 | package.path = "../LjES/?.lua;" .. package.path 13 | 14 | local demo = require("demo") 15 | require("ColladaShape") 16 | require("Schedule") 17 | 18 | -- globals 19 | local TEXTUTE_FILE = "" 20 | local COLLADA_FILE = "hand.dae" 21 | local g_totalVertices = 0 22 | local g_totalTriangles = 0 23 | local skeletons = {} 24 | local bones = {} 25 | local BONE = 1 26 | local MESH = 1 27 | local MAXBONE = 1 28 | local HELP = false 29 | local MAXMESH = 1 30 | local anim_stopped = false 31 | local shapes 32 | 33 | demo.screen(0, 0) 34 | local aSpace = demo.getSpace() 35 | 36 | function setBoneParams() 37 | bones = skeletons[MESH] 38 | if bones then MAXBONE = #bones end 39 | end 40 | 41 | function showBones(true_or_false) 42 | for i = 1, #shapes do 43 | local shape = shapes[i] 44 | local skeleton = shape:getSkeleton() 45 | skeleton:showBone(true_or_false) 46 | end 47 | demo.aSpace:scanSkeletons() 48 | end 49 | 50 | function createBoneShape(a, r, g, b) 51 | local shape = Shape:new() 52 | shape:simpleBone(a) 53 | shape:shaderParameter("color", {r, g, b, 1.0}) 54 | return shape 55 | end 56 | 57 | -- -------------------------------------------------------------------- 58 | -- main 59 | -- -------------------------------------------------------------------- 60 | 61 | if arg[1] ~= nil then COLLADA_FILE = arg[1] end 62 | if arg[2] ~= nil then TEXTUTE_FILE = arg[2] end 63 | 64 | demo.backgroundColor(0.2, 0.2, 0.4) 65 | 66 | -- texture 67 | if TEXTUTE_FILE ~= "" then 68 | require("Texture") 69 | tex = Texture:new() 70 | tex:readImageFromFile(TEXTUTE_FILE) 71 | end 72 | local start = aSpace:now() 73 | local boneFlag = true 74 | local verboseFlag = false 75 | 76 | -- load collada 77 | local collada = ColladaShape:new() 78 | local collada_text = util.readFile(COLLADA_FILE) 79 | collada:parse(collada_text ,verboseFlag) 80 | shapes = collada:makeShapes(boneFlag, verboseFlag) 81 | collada:releaseMeshes() 82 | for i = 1, #shapes do 83 | shapes[i]:releaseObjects() 84 | end 85 | if shapes[1].anim ~= nil then 86 | shapes[1].anim:list() 87 | end 88 | MAXMESH = #shapes 89 | 90 | -- print loading time 91 | local time = aSpace:now() - start 92 | util.printf("loading time = %f sec \n", time) 93 | 94 | local node = aSpace:addNode(nil, "node1") 95 | node:setPosition(0, 0, 0) 96 | local fig = aSpace:addNode(node, "fig") 97 | fig:setPosition(0, 0, 0) 98 | fig:setAttitude(0, 0, 0) 99 | 100 | -- set bone shape 101 | local size = demo.getShapeSize(shapes) 102 | local bone_shape = createBoneShape(size.max/100, 1, 1, 1) 103 | 104 | -- set eye position 105 | local eye = aSpace:addNode(nil, "eye") 106 | eye:setPosition(size.centerx, size.centery, size.max * 1.2) 107 | 108 | for i = 1, #shapes do 109 | local shape = shapes[i] 110 | local skeleton = shape:getSkeleton() 111 | if (skeleton ~= nil) and (skeleton:getBoneCount() > 0) then 112 | skeleton:setBoneShape(bone_shape) 113 | table.insert(skeletons, skeleton:getBoneOrder()) 114 | else 115 | util.printf("No skeleton") 116 | end 117 | 118 | fig:addShape(shape) 119 | if TEXTUTE_FILE ~= "" then 120 | shape:shaderParameter("use_texture", 1) 121 | shape:shaderParameter("texture", tex) 122 | shape:shaderParameter("color", {1.0, 1.0, 1.0, 1.0}) 123 | else 124 | shape:shaderParameter("use_texture", 0) 125 | shape:shaderParameter("color", {0.8, 0.7, 0.6, 1.0}) 126 | end 127 | util.printf("object vertex#=%d triangle#=%d\n", shape:getVertexCount(), 128 | shape:getTriangleCount()) 129 | g_totalVertices = g_totalVertices + shape:getVertexCount() 130 | g_totalTriangles = g_totalTriangles + shape:getTriangleCount() 131 | end 132 | 133 | demo.aText:setScale(1) 134 | setBoneParams() 135 | 136 | -- -------------------------------------------- 137 | -- [1][2][3][4][5][6][7][8][9][0][-][^][\] 138 | -- [Q][W][e][r][T][y][u][I][o][P][@] 139 | -- [A][S][D][F][G][h][J][K][l] 140 | -- [Z][X][c][v][B][n][m][,][.][/] 141 | -- -------------------------------------------- 142 | function keyFunc(key) 143 | local ROT = 1.0 144 | local MOV = 0.3 145 | 146 | if (key == 'h') then 147 | demo.aText:clearScreen() 148 | if HELP then HELP = false else HELP = true end 149 | elseif (key == 'w') then node:rotateX(ROT) 150 | elseif (key == 's') then node:rotateX(-ROT) 151 | elseif (key == 'a') then node:rotateY(ROT) 152 | elseif (key == 'd') then node:rotateY(-ROT) 153 | elseif (key == 'z') then node:rotateZ(ROT) 154 | elseif (key == 'x') then node:rotateZ(-ROT) 155 | 156 | elseif (key == 'j') then 157 | BONE = BONE - 1 158 | if BONE < 1 then BONE = 1 end 159 | elseif (key == 'k') then 160 | BONE = BONE + 1 161 | if BONE > MAXBONE then BONE = MAXBONE end 162 | elseif (key == 'o') then fig.shapes[MESH]:hide(true) 163 | elseif (key == 'i') then fig.shapes[MESH]:hide(false) 164 | elseif (key == 'm') then 165 | MESH = MESH + 1 166 | if MESH > MAXMESH then MESH = MAXMESH end 167 | setBoneParams() 168 | if BONE > MAXBONE then BONE = MAXBONE end 169 | elseif (key == 'n') then 170 | MESH = MESH - 1 if MESH < 1 then MESH = 1 end 171 | setBoneParams() 172 | if BONE > MAXBONE then BONE = MAXBONE end 173 | elseif (key == '1') then eye:move( MOV, 0, 0) 174 | elseif (key == '2') then eye:move(-MOV, 0, 0) 175 | elseif (key == '3') then eye:move( 0, MOV, 0) 176 | elseif (key == '4') then eye:move( 0,-MOV, 0) 177 | elseif (key == '5') then eye:move( 0, 0, MOV) 178 | elseif (key == '6') then eye:move( 0, 0,-MOV) 179 | elseif (key == '9') then showBones(true) 180 | elseif (key == '0') then showBones(false) 181 | elseif (key == '@') then 182 | fig.shapes[1].anim:start() 183 | anim_stopped = false 184 | elseif (key == '/') then 185 | fig.shapes[1].anim:transitionTo(1000, 1, -1) 186 | -- BONE は MESH に依存することに注意 187 | elseif bones ~= nil then 188 | if (key == 'e') then bones[BONE]:rotateX(ROT) 189 | elseif (key == 'r') then bones[BONE]:rotateX(-ROT) 190 | elseif (key == 'c') then bones[BONE]:rotateZ(ROT) 191 | elseif (key == 'v') then bones[BONE]:rotateZ(-ROT) 192 | elseif (key == 'y') then bones[BONE]:rotateY(ROT) 193 | elseif (key == 'u') then bones[BONE]:rotateY(-ROT) 194 | end 195 | end 196 | return key 197 | end 198 | 199 | function draw_help() 200 | local t = demo.aText 201 | t:writeAt(3, 3, "+ -") 202 | t:writeAt(2, 4, "[w] - [s] : rotate X") 203 | t:writeAt(2, 5, "[a] - [d] : rotate Y") 204 | t:writeAt(2, 6, "[z] - [x] : rotate Z") 205 | t:writeAt(0, 7, "Bone") 206 | t:writeAt(2, 8, "[e] - [r] : rotate X") 207 | t:writeAt(2, 9, "[c] - [v] : rotate Z") 208 | t:writeAt(2,10, "[y] - [u] : rotate Y") 209 | t:writeAt(2,12, "[n] - [m] : select Mesh") 210 | t:writeAt(2,13, "[j] - [k] : select Bone") 211 | t:writeAt(2,14, "[o] - [i] : hide / show") 212 | t:writeAt(2,15, "[0] - [9] : hide/show bones") 213 | t:writeAt(2,16, "[p] : Screenshot") 214 | t:writeAt(2,17, "[t] : on top") 215 | t:writeAt(2,18, "[b] : on bottom") 216 | t:writeAt(2,19, "[g] : initial screen") 217 | t:writeAt(2,20, "[f] : full screen ") 218 | t:writeAt(2,21, "[h] : help on/off ") 219 | t:writeAt(2,22, "[@] : Replay animation") 220 | t:writeAt(2,24, "[q] : QUIT") 221 | end 222 | 223 | local g_count = 0 224 | 225 | function draw() 226 | local t = demo.aText 227 | if HELP then 228 | draw_help() 229 | else 230 | t:writefAt(0, 3, "Vertices :%6d", g_totalVertices) 231 | t:writefAt(0, 4, "Triangles :%6d", g_totalTriangles) 232 | t:writeAt(2, 21, "[h] : help on/off ") 233 | end 234 | 235 | --node:rotateY(0.5) 236 | 237 | if fig.shapes[1].anim ~= nil then 238 | local running = fig.shapes[1].anim:playFps(33) 239 | if running < 0 then fig.shapes[1].anim:start() end 240 | end 241 | aSpace:draw(eye) 242 | demo.aScreen:screenShot(string.format("ss_%03d.png", g_count)) 243 | g_count = g_count + 1 244 | if bones ~= nil and bones[BONE] ~= nil then 245 | t:writefAt(0, 2, "Mesh:%2d/%2d Bone:%2d/%2d [%s] ", 246 | MESH, MAXMESH, BONE, MAXBONE, bones[BONE].name) 247 | t:writefAt(40, 0, "World H:%6.2f, P:%6.2f, B:%6.2f", 248 | bones[BONE]:getWorldAttitude()) 249 | t:writefAt(46, 1, "X:%6.2f, Y:%6.2f, Z:%6.2f", 250 | unpack(bones[BONE]:getWorldPosition())) 251 | t:writefAt(40, 2, "Local h:%6.2f, p:%6.2f, b:%6.2f", 252 | bones[BONE]:getLocalAttitude()) 253 | t:writefAt(46, 3, "x:%6.2f, y:%6.2f, z:%6.2f", 254 | unpack(bones[BONE]:getPosition())) 255 | --[[ 256 | t:writefAt(40, 5, "Node H:%6.2f, P:%6.2f, B:%6.2f", 257 | fig:getWorldAttitude()) 258 | t:writefAt(46, 6, "X:%6.2f, Y:%6.2f, Z:%6.2f", 259 | unpack(fig:getWorldPosition())) 260 | --]] 261 | else 262 | t:writefAt(0,2, "Mesh:%2d/%2d Bone:%2d/%2d ", 263 | MESH, MAXMESH, BONE, MAXBONE) 264 | end 265 | end 266 | 267 | --showBones(true) 268 | if fig.shapes[1].anim ~= nil then 269 | fig.shapes[1].anim:start() 270 | end 271 | demo.loop(draw, true, keyFunc) 272 | demo.exit() 273 | 274 | -------------------------------------------------------------------------------- /examples/demo_shape.lua: -------------------------------------------------------------------------------- 1 | #!/usr/bin/luajit 2 | -- --------------------------------------------- 3 | -- demo_shape.lua 2013/04/11 4 | -- Copyright (c) 2013 Jun Mizutani, 5 | -- released under the MIT open source license. 6 | -- --------------------------------------------- 7 | 8 | package.path = "../LjES/?.lua;" .. package.path 9 | 10 | local demo = require("demo2") 11 | local shapes = {} 12 | local tex, tex2 13 | 14 | function createShapes() 15 | for i=1, 9 do 16 | shapes[i] = Shape:new() 17 | shapes[i]:setTextureMappingMode(0) 18 | shapes[i]:setTextureMappingAxis(1) 19 | shapes[i]:shaderParameter("use_texture", 1) 20 | shapes[i]:shaderParameter("texture", tex) 21 | if i == 1 then 22 | shapes[i]:sphere(8, 16, 16) 23 | elseif i == 2 then 24 | shapes[i]:cone(10, 6, 16) 25 | elseif i == 3 then 26 | shapes[i]:truncated_cone(8, 2, 5, 16) 27 | elseif i== 4 then 28 | shapes[i]:double_cone(10, 8, 16) 29 | elseif i== 5 then 30 | shapes[i]:prism(12, 4, 16) 31 | elseif i== 6 then 32 | shapes[i]:donut(8, 3, 16, 16) 33 | elseif i== 7 then 34 | shapes[i]:setTextureMappingMode(1) 35 | shapes[i]:setTextureMappingAxis(1) 36 | shapes[i]:setTextureScale(8, 8) 37 | shapes[i]:cube(8) 38 | elseif i== 8 then 39 | shapes[i]:setTextureMappingMode(1) 40 | shapes[i]:setTextureMappingAxis(1) 41 | shapes[i]:setTextureScale(4, 4) 42 | shapes[i]:cuboid(8, 7, 6) 43 | elseif i== 9 then 44 | shapes[i]:shaderParameter("texture", tex2) 45 | shapes[i]:setTextureScale(4, 4) 46 | shapes[i]:mapCube(10) 47 | end 48 | shapes[i]:endShape() 49 | local r = (math.random() * 0.5) + 0.5 50 | local g = (math.random() * 0.5) + 0.5 51 | local b = (math.random() * 0.5) + 0.5 52 | shapes[i]:shaderParameter("color", {r, g, b, 1.0}) 53 | end 54 | return shapes 55 | end 56 | 57 | 58 | demo.screen(-200, -200) 59 | math.randomseed(os.time()) 60 | 61 | local aSpace = demo.getSpace() 62 | 63 | local eye = aSpace:addNode(nil, "eye") 64 | eye:setPosition(0, 0, 50) 65 | 66 | tex = Texture:new() 67 | tex:readImageFromFile("num512.png") 68 | tex2 = Texture:new() 69 | tex2:readImageFromFile("CubeMap_dice.png") 70 | 71 | createShapes() 72 | 73 | local objNode = {} 74 | local rot = {} 75 | for i=1, 9 do 76 | objNode[i] = aSpace:addNode(nil, "objNode") 77 | local x = (math.random() - 0.5) * 50 78 | local y = (math.random() - 0.5) * 50 79 | local z = -math.random() * 30 - 15 80 | objNode[i]:setPosition(x, y, z) 81 | objNode[i]:addShape(shapes[i]) 82 | rot[i] = math.random() * 0.8 + 0.5 83 | end 84 | 85 | aSpace:timerStart() 86 | 87 | demo.backgroundColor(0.2, 0.2, 0.4) 88 | function draw() 89 | for i=1,9 do 90 | objNode[i]:rotateX(rot[i]) 91 | objNode[i]:rotateY(rot[i]) 92 | end 93 | eye:rotateZ(0.3) 94 | aSpace:draw(eye) 95 | end 96 | 97 | demo.loop(draw, true) 98 | demo.aText.shader:getInfo() 99 | demo.exit() 100 | -------------------------------------------------------------------------------- /examples/demo_spheres.lua: -------------------------------------------------------------------------------- 1 | #!/usr/bin/luajit 2 | -- --------------------------------------------- 3 | -- demo_spheres.lua 2013/04/11,2014/03/30 4 | -- Copyright (c) 2013-2014 Jun Mizutani, 5 | -- released under the MIT open source license. 6 | -- --------------------------------------------- 7 | 8 | package.path = "../LjES/?.lua;" .. package.path 9 | 10 | local util = require("util") 11 | local termios = require("termios") 12 | require("Phong") 13 | require("Screen") 14 | require("Matrix") 15 | require("Texture") 16 | require("Space") 17 | require("Shape") 18 | require("Text") -- changed from TexText 19 | 20 | function checkKey(eye, base, text) 21 | local key = termios.realtimeKey() 22 | local scr = g_Screen 23 | local fw = scr.fullWidth 24 | local fh = scr.fullHeight 25 | if (key == 'q') then g_Quit = true 26 | elseif (key == 'p') then scr:screenShot() 27 | elseif (key == 't') then scr:move(fw / 2, fh / 2, fw / 2, 0) 28 | elseif (key == 'b') then scr:move(fw / 2, fh / 2, fw / 2, fh / 2) 29 | elseif (key == 'g') then scr:restoreSize() 30 | elseif (key == 'f') then scr:move(fw, fh, 0, 0) 31 | elseif (key == 'w') then base:rotateX(0.3) 32 | elseif (key == 's') then base:rotateX(-0.3) 33 | elseif (key == 'a') then base:rotateY(0.3) 34 | elseif (key == 'd') then base:rotateY(-0.3) 35 | elseif (key == 'z') then eye:move(0,0,-1) 36 | elseif (key == 'x') then eye:move(0,0,1) 37 | elseif (key == 'j') then text:setScale(1) 38 | elseif (key == 'k') then text:setScale(2) 39 | elseif (key == 'l') then text:setScale(3) 40 | elseif (key == 'h') then 41 | if g_Help then 42 | hideHelp(text) 43 | g_Help = false 44 | else 45 | showHelp(text) 46 | g_Help = true 47 | end 48 | elseif (key == 'x') then eye:move(0,0,1) 49 | elseif (key >= '1') and (key <= '9') then 50 | local n = string.byte(key, 1) 51 | changeShape(g_Nodes, n - 48) 52 | end 53 | end 54 | 55 | function color(div, i, count) 56 | local d = 0.5 / div 57 | return {d * i + 0.5, (count % 2000)*0.00025 + 0.5, 1.0 - d * i, 1.0} 58 | end 59 | 60 | function makeShapes() 61 | local numShapes = 9 62 | local shapes = {} 63 | for i = 1, numShapes do 64 | shapes[i] = Shape:new() 65 | shapes[i]:shaderParameter("texture", tex) 66 | if i == 1 then 67 | shapes[i]:sphere(5, 16, 16) 68 | elseif i == 2 then 69 | shapes[i]:cone(8, 8, 16) 70 | elseif i == 3 then 71 | shapes[i]:truncated_cone(4, 3, 6, 16) 72 | elseif i== 4 then 73 | shapes[i]:double_cone(6, 6, 16) 74 | elseif i== 5 then 75 | shapes[i]:prism(6, 4, 16) 76 | elseif i== 6 then 77 | shapes[i]:donut(5, 4, 16, 16) 78 | elseif i== 7 then 79 | shapes[i]:setTextureMappingMode(1) 80 | shapes[i]:setTextureMappingAxis(1) 81 | shapes[i]:setTextureScale(8, 8) 82 | shapes[i]:cube(8) 83 | elseif i== 8 then 84 | shapes[i]:setTextureMappingMode(1) 85 | shapes[i]:setTextureMappingAxis(1) 86 | shapes[i]:setTextureScale(4, 4) 87 | shapes[i]:cuboid(10, 7, 4) 88 | elseif i== 9 then 89 | shapes[i]:shaderParameter("texture", tex2) 90 | shapes[i]:setTextureScale(4, 4) 91 | shapes[i]:mapCube(8) 92 | end 93 | shapes[i]:endShape() 94 | shapes[i]:shaderParameter("color", {1.0, 1.0, 1.0, 1.0}) 95 | shapes[i]:shaderParameter("use_texture", 1) 96 | end 97 | return shapes 98 | end 99 | 100 | function changeShape(nodes, shape_no) 101 | g_totalVertices = 0 102 | g_totalTriangles = 0 103 | for i = 1, #nodes do 104 | s = nodes[i].obj:getShape(1) 105 | s:referShape(g_Shapes[shape_no]) 106 | if i%2 == 0 then 107 | s:shaderParameter("use_texture", 1) 108 | s:shaderParameter("texture", g_tex) 109 | if shape_no == 9 then s:shaderParameter("texture", g_tex2) end 110 | end 111 | g_totalVertices = g_totalVertices + s:getVertexCount() 112 | g_totalTriangles = g_totalTriangles + s:getTriangleCount() 113 | end 114 | end 115 | 116 | function showHelp(text) 117 | local top = 6 118 | text:goTo(0, top) 119 | text:write("[q] Quit [h] Show/Hide Help") 120 | text:goTo(0, top + 1) 121 | text:write("[f] Full Screen [g] Resume Screen") 122 | text:goTo(0, top + 2) 123 | text:write("[t] Upper Right [b] Lower Right") 124 | text:goTo(0, top + 3) 125 | text:write("[w]/[s] Rotate X [a]/[d] Rotate Y") 126 | text:goTo(0, top + 4) 127 | text:write("[z] Move Near [x] Move Far") 128 | text:goTo(0, top + 5) 129 | text:write("[p] Screenshot [1]-[9] Change Shape") 130 | text:goTo(0, top + 6) 131 | text:write("Text Size : [j] = 1, [k] = 2, [l] = 3") 132 | end 133 | 134 | function hideHelp(text) 135 | local top = 6 136 | for i = top, top+6 do 137 | text:clearLine(i) 138 | end 139 | end 140 | 141 | g_Quit = false 142 | g_totalVertices = 0 143 | g_totalTriangles = 0 144 | g_Help = true; 145 | g_Nodes = {} 146 | g_Shapes = {} 147 | 148 | 149 | local FontScale = 1.0 150 | g_Screen = Screen:new() 151 | g_Screen:init(-120, -80, 0, 0) 152 | 153 | local phong = Phong:new() 154 | phong:setDefaultParam("light", {0, 100, 1000, 1}) 155 | phong:init() 156 | Shape.ClassShader(phong) 157 | 158 | local aspect = g_Screen.width / g_Screen.height 159 | local projMat = Matrix:new() 160 | projMat:makeProjectionMatrix(1, 1000, 53, aspect) 161 | phong:setProjectionMatrix(projMat) 162 | 163 | local aText = Text:new() 164 | aText:init("font512.png") 165 | local aSpace = Space:new() 166 | g_tex = Texture:new() 167 | g_tex:readImageFromFile("num512.png") 168 | g_tex2 = Texture:new() 169 | g_tex2:readImageFromFile("CubeMap_dice.png") 170 | 171 | g_Shapes = makeShapes() 172 | 173 | local numNodes = 40 174 | local nodes = g_Nodes 175 | for i = 1, numNodes do 176 | nodes[i] = {} -- Each nodes[] has two nodes. 177 | nodes[i].base = aSpace:addNode(nil, string.format("base%02d", i)) 178 | nodes[i].obj = aSpace:addNode(nodes[i].base, string.format("obj%02d", i)) 179 | nodes[i].base:setPosition(0, i*1.5, 0) 180 | nodes[i].obj:setPosition(0, 0, 22.0) 181 | 182 | local s = Shape:new() 183 | s:referShape(g_Shapes[1]) 184 | s:shaderParameter("color", color(numNodes, i, 0)) 185 | if i%2 == 0 then 186 | s:shaderParameter("use_texture", 1) 187 | s:shaderParameter("texture", g_tex) 188 | end 189 | nodes[i].obj:addShape(s) 190 | g_totalVertices = g_totalVertices + s:getVertexCount() 191 | g_totalTriangles = g_totalTriangles + s:getTriangleCount() 192 | end 193 | 194 | local eyeBase = aSpace:addNode(nil, "eyeBase") 195 | eyeBase:setPosition(0, 0, 0) 196 | local eye = aSpace:addNode(eyeBase, "eye") 197 | eye:setPosition(0, 30, 100) 198 | 199 | g_Screen:setClearColor(0.0, 0.0, 0.3, 1.0) 200 | aSpace:timerStart() 201 | aText:setScale(FontScale) 202 | aText:writeAt(0, 0, "LjES : A 3D Framework for LuaJIT") 203 | aText:writeAt(0, 1, " running on Raspberry Pi") 204 | showHelp(aText) 205 | termios.getTermios() 206 | termios.setRawMode() 207 | 208 | while (g_Quit == false) and (g_Screen:getFrameCount() < 6000) do 209 | checkKey(eye, eyeBase, aText) 210 | g_Screen:clear() 211 | local count = g_Screen:getFrameCount() 212 | for i = 1, numNodes do 213 | nodes[i].base:rotateY(0.2*i) 214 | local s = nodes[i].obj:getShape(1) 215 | s:shaderParameter("color", color(numNodes, i, count)) 216 | if i%2 == 0 then 217 | nodes[i].obj:rotateX(-2.0) 218 | else 219 | nodes[i].obj:rotateX(1.0) 220 | end 221 | end 222 | aSpace:draw(eye) 223 | 224 | aText:goTo(0,2) 225 | aText:write(string.format("FPS : %8.4f ", aSpace:count()/aSpace:uptime())) 226 | aText:goTo(0,3) 227 | aText:write(string.format("Vertices : %d ", g_totalVertices)) 228 | aText:goTo(0,4) 229 | aText:write(string.format("Triangles : %d ", g_totalTriangles)) 230 | aText:drawScreen() 231 | g_Screen:update() 232 | end 233 | util.sleep(0.10) 234 | termios.restoreTermios() 235 | 236 | -------------------------------------------------------------------------------- /examples/donut.lua: -------------------------------------------------------------------------------- 1 | package.path = "../LjES/?.lua;" .. package.path 2 | local demo = require("demo") 3 | 4 | demo.screen(0, 0) 5 | demo.backgroundColor(0.2, 0.2, 0.4) 6 | 7 | local aSpace = demo.getSpace() 8 | 9 | local eye = aSpace:addNode(nil, "eye") 10 | eye:setPosition(0, 0, 30) 11 | 12 | local shape = Shape:new() 13 | shape:donut(8, 3, 16, 16) 14 | shape:endShape() 15 | shape:shaderParameter("color", {0.5, 0.3, 0.0, 1.0}) 16 | 17 | local node = aSpace:addNode(nil, "node1") 18 | node:setPosition(0, 0, 0) 19 | node:addShape(shape) 20 | 21 | function draw() 22 | node:rotateX(0.1) 23 | aSpace:draw(eye) 24 | end 25 | 26 | demo.loop(draw, true) 27 | demo.exit() 28 | -------------------------------------------------------------------------------- /examples/double_cone.lua: -------------------------------------------------------------------------------- 1 | package.path = "../LjES/?.lua;" .. package.path 2 | local demo = require("demo") 3 | 4 | demo.screen(0, 0) 5 | demo.backgroundColor(0.2, 0.2, 0.4) 6 | 7 | local aSpace = demo.getSpace() 8 | 9 | local eye = aSpace:addNode(nil, "eye") 10 | eye:setPosition(0, 0, 30) 11 | 12 | local shape = Shape:new() 13 | shape:double_cone(8, 8, 16) 14 | shape:endShape() 15 | shape:shaderParameter("color", {0.5, 0.3, 0.0, 1.0}) 16 | 17 | local node = aSpace:addNode(nil, "node1") 18 | node:setPosition(0, 0, 0) 19 | node:addShape(shape) 20 | 21 | function draw() 22 | node:rotateX(0.1) 23 | aSpace:draw(eye) 24 | end 25 | 26 | demo.loop(draw, true) 27 | demo.exit() 28 | -------------------------------------------------------------------------------- /examples/fbshot.lua: -------------------------------------------------------------------------------- 1 | #!/usr/bin/luajit 2 | -- --------------------------------------------- 3 | -- fbshot.lua 2014/03/29 4 | -- Copyright (c) 2014 Jun Mizutani, 5 | -- released under the MIT open source license. 6 | -- 7 | -- Save framebuffer into PNG file. 8 | -- --------------------------------------------- 9 | package.path = "../LjES/?.lua;" .. package.path 10 | 11 | local bit = require("bit") 12 | local ffi = require("ffi") 13 | local png = require("png") 14 | local util = require("util") 15 | 16 | local filename = arg[1] 17 | local w = tonumber(arg[2]) 18 | local h = tonumber(arg[3]) 19 | 20 | if (filename == nil) or (arg[2] == nil) or (arg[2] == nil) then 21 | util.printf(" usage:\n") 22 | util.printf(" sudo ./fbshot.lua filename width height\n") 23 | return 24 | end 25 | 26 | local fb = util.readFile("/dev/fb0") 27 | local buflen = w * h * 3 28 | local buf = ffi.new("uint8_t[?]", buflen) 29 | 30 | for i = 0, w * h - 1 do 31 | local n = i * 2 + 1 32 | local fb1 = string.sub(fb, n, n):byte(1) 33 | local fb2 = string.sub(fb, n+1 , n+1):byte(1) 34 | rgb16 = fb2*256 + fb1 35 | red = bit.rshift(bit.band(rgb16, 0xF800), 8) 36 | green = bit.rshift(bit.band(rgb16, 0x07E0), 3) 37 | blue = bit.lshift(bit.band(rgb16, 0x1F), 3) 38 | buf[i*3 ] = red 39 | buf[i*3+1] = green 40 | buf[i*3+2] = blue 41 | end 42 | png.writePNG(filename, buf, w, h, 8, 3) 43 | 44 | 45 | -------------------------------------------------------------------------------- /examples/hand.blend: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jun-mizutani/ljes/e82d4ef860801c9444b254235f72fc768d48b780/examples/hand.blend -------------------------------------------------------------------------------- /examples/hand.lua: -------------------------------------------------------------------------------- 1 | #!/usr/bin/luajit 2 | -- --------------------------------------------- 3 | -- hand.lua 2014/03/30 4 | -- Copyright (c) 2014 Jun Mizutani, 5 | -- released under the MIT open source license. 6 | -- 7 | -- Finger-counting, Collada animation and Action. 8 | -- --------------------------------------------- 9 | 10 | -- usage 11 | -- $ luajit hand.lua 12 | 13 | package.path = "../LjES/?.lua;" .. package.path 14 | 15 | local demo = require("demo") 16 | require("ColladaShape") 17 | require("Schedule") 18 | require("Action") 19 | 20 | -- globals 21 | local TEXTUTE_FILE = "" 22 | local COLLADA_FILE = "hand.dae" 23 | local g_totalVertices = 0 24 | local g_totalTriangles = 0 25 | local skeletons = {} 26 | local bones = {} 27 | local BONE = 1 28 | local MESH = 1 29 | local MAXBONE = 1 30 | local HELP = false 31 | local MAXMESH = 1 32 | local anim_stopped = false 33 | local shapes 34 | 35 | demo.screen(0, 0) 36 | local aSpace = demo.getSpace() 37 | local eye = aSpace:addNode(nil, "eye") 38 | 39 | function setBoneParams() 40 | bones = skeletons[MESH] 41 | if bones then MAXBONE = #bones end 42 | end 43 | 44 | function showBones(node, true_or_false) 45 | for i = 1, node:getShapeCount() do 46 | local shape = node:getShape(i) 47 | local skeleton = shape:getSkeleton() 48 | skeleton:showBone(true_or_false) 49 | end 50 | demo.aSpace:scanSkeletons() 51 | end 52 | 53 | function createBoneShape(size, r, g, b) 54 | local shape = Shape:new() 55 | shape:simpleBone(size) 56 | shape:shaderParameter("color", {r, g, b, 1.0}) 57 | return shape 58 | end 59 | 60 | -- -------------------------------------------------------------------- 61 | -- main 62 | -- -------------------------------------------------------------------- 63 | 64 | demo.backgroundColor(0.2, 0.2, 0.4) 65 | 66 | -- texture 67 | if TEXTUTE_FILE ~= "" then 68 | require("Texture") 69 | tex = Texture:new() 70 | tex:readImageFromFile(TEXTUTE_FILE) 71 | end 72 | local start = aSpace:now() 73 | local boneFlag = true 74 | local verboseFlag = false 75 | 76 | -- load collada 77 | local collada = ColladaShape:new() 78 | local collada_text = util.readFile(COLLADA_FILE) 79 | collada:parse(collada_text ,verboseFlag) 80 | shapes = collada:makeShapes(boneFlag, verboseFlag) 81 | collada:releaseMeshes() 82 | for i = 1, #shapes do 83 | shapes[i]:releaseObjects() 84 | end 85 | shapes[1].anim:list() 86 | 87 | MAXMESH = #shapes 88 | 89 | -- print loading time 90 | local time = aSpace:now() - start 91 | util.printf("loading time = %f sec \n", time) 92 | 93 | local node = aSpace:addNode(nil, "node1") 94 | node:setPosition(0, 0, 0) 95 | local fig = aSpace:addNode(node, "fig") 96 | fig:setPosition(0, 0, 0) 97 | fig:setAttitude(0, 0, 0) 98 | 99 | -- set bone shape 100 | local size = demo.getShapeSize(shapes) 101 | local bone_shape = createBoneShape(size.max/70, 1, 1, 1) 102 | 103 | -- set eye position 104 | eye:setPosition(size.centerx, size.centery, size.max * 1.2) 105 | 106 | for i = 1, #shapes do 107 | local shape = shapes[i] 108 | local skeleton = shape:getSkeleton() 109 | if (skeleton ~= nil) and (skeleton:getBoneCount() > 0) then 110 | skeleton:setBoneShape(bone_shape) 111 | table.insert(skeletons, skeleton:getBoneOrder()) 112 | else 113 | util.printf("No skeleton") 114 | end 115 | 116 | fig:addShape(shape) 117 | shape:shaderParameter("ambient", 0.4) 118 | shape:shaderParameter("specular", 0.2) 119 | shape:shaderParameter("power", 20) 120 | if TEXTUTE_FILE ~= "" then 121 | shape:shaderParameter("use_texture", 1) 122 | shape:shaderParameter("texture", tex) 123 | shape:shaderParameter("color", {1.0, 1.0, 1.0, 1.0}) 124 | else 125 | shape:shaderParameter("use_texture", 0) 126 | shape:shaderParameter("color", {0.9, 0.75, 0.75, 1.0}) 127 | end 128 | util.printf("object vertex#=%d triangle#=%d\n", shape:getVertexCount(), 129 | shape:getTriangleCount()) 130 | g_totalVertices = g_totalVertices + shape:getVertexCount() 131 | g_totalTriangles = g_totalTriangles + shape:getTriangleCount() 132 | end 133 | 134 | demo.aText:setScale(1) 135 | setBoneParams() 136 | action = Action:new(fig.shapes[1].anim) 137 | action:addKeyPattern("N1", 500, 3, 4) 138 | action:addKeyPattern("N2", 500, 5, 6) 139 | action:addKeyPattern("N3", 500, 7, 8) 140 | action:addKeyPattern("N4", 500, 9, 10) 141 | action:addKeyPattern("N5", 500, 11, 12) 142 | action:addKeyPattern("N0", 500, 13, 14) 143 | 144 | action:addAction("N1", {1}) 145 | action:addAction("N2", {2}) 146 | action:addAction("N3", {3}) 147 | action:addAction("N4", {4}) 148 | action:addAction("N5", {5}) 149 | action:addAction("N0", {6}) 150 | 151 | action:startAction("N0") 152 | 153 | -- -------------------------------------------- 154 | -- [1][2][3][4][5][6][7][8][9][0][-][^][\] 155 | -- [Q][W][e][r][T][y][u][I][o][P][@] 156 | -- [A][S][D][F][G][h][J][K][l] 157 | -- [Z][X][c][v][B][n][m][,][.][/] 158 | -- -------------------------------------------- 159 | function keyFunc(key) 160 | local ROT = 1.0 161 | local MOV = 0.3 162 | local t = demo.aText 163 | if key ~= string.char(0) then 164 | demo.messageColor(1.0, 0, 0) 165 | demo.messageFontScale(2) 166 | local str = string.format("[%1s]", key) 167 | demo.messageWrite(30, 0, str) 168 | end 169 | if (key == 'h') then 170 | t:clearScreen() 171 | if HELP then HELP = false else HELP = true end 172 | elseif (key == 'w') then node:rotateX(ROT) 173 | elseif (key == 's') then node:rotateX(-ROT) 174 | elseif (key == 'a') then node:rotateY(ROT) 175 | elseif (key == 'd') then node:rotateY(-ROT) 176 | elseif (key == 'z') then node:rotateZ(ROT) 177 | elseif (key == 'x') then node:rotateZ(-ROT) 178 | 179 | elseif (key == 'j') then 180 | BONE = BONE - 1 181 | if BONE < 1 then BONE = 1 end 182 | elseif (key == 'k') then 183 | BONE = BONE + 1 184 | if BONE > MAXBONE then BONE = MAXBONE end 185 | elseif (key == 'o') then fig.shapes[MESH]:hide(true) 186 | elseif (key == 'i') then fig.shapes[MESH]:hide(false) 187 | elseif (key == 'm') then 188 | MESH = MESH + 1 189 | if MESH > MAXMESH then MESH = MAXMESH end 190 | setBoneParams() 191 | if BONE > MAXBONE then BONE = MAXBONE end 192 | elseif (key == 'n') then 193 | MESH = MESH - 1 if MESH < 1 then MESH = 1 end 194 | setBoneParams() 195 | if BONE > MAXBONE then BONE = MAXBONE end 196 | elseif (key == '1') then action:startAction("N1") 197 | elseif (key == '2') then action:startAction("N2") 198 | elseif (key == '3') then action:startAction("N3") 199 | elseif (key == '4') then action:startAction("N4") 200 | elseif (key == '5') then action:startAction("N5") 201 | elseif (key == '6') then action:startAction("N0") 202 | elseif (key == '9') then showBones(fig, true) 203 | elseif (key == '0') then showBones(fig, false) 204 | elseif (key == '@') then 205 | fig.shapes[1].anim:start() 206 | anim_stopped = false 207 | elseif (key == '/') then 208 | fig.shapes[1].anim:transitionTo(1000, 1, -1) 209 | -- BONE は MESH に依存することに注意 210 | elseif bones ~= nil then 211 | if (key == 'e') then bones[BONE]:rotateX(ROT) 212 | elseif (key == 'r') then bones[BONE]:rotateX(-ROT) 213 | elseif (key == 'c') then bones[BONE]:rotateZ(ROT) 214 | elseif (key == 'v') then bones[BONE]:rotateZ(-ROT) 215 | elseif (key == 'y') then bones[BONE]:rotateY(ROT) 216 | elseif (key == 'u') then bones[BONE]:rotateY(-ROT) 217 | end 218 | end 219 | return key 220 | end 221 | 222 | function draw_help() 223 | local t = demo.aText 224 | t:writeAt(3, 3, "+ -") 225 | t:writeAt(2, 4, "[w] - [s] : rotate X") 226 | t:writeAt(2, 5, "[a] - [d] : rotate Y") 227 | t:writeAt(2, 6, "[z] - [x] : rotate Z") 228 | t:writeAt(0, 7, "Bone") 229 | t:writeAt(2, 8, "[e] - [r] : rotate X") 230 | t:writeAt(2, 9, "[c] - [v] : rotate Z") 231 | t:writeAt(2,10, "[y] - [u] : rotate Y") 232 | t:writeAt(2,12, "[n] - [m] : select Mesh") 233 | t:writeAt(2,13, "[j] - [k] : select Bone") 234 | t:writeAt(2,14, "[o] - [i] : hide / show") 235 | t:writeAt(2,15, "[0] - [9] : hide/show bones") 236 | t:writeAt(2,16, "[p] : Screenshot") 237 | t:writeAt(2,17, "[t] : on top") 238 | t:writeAt(2,18, "[b] : on bottom") 239 | t:writeAt(2,19, "[g] : initial screen") 240 | t:writeAt(2,20, "[f] : full screen ") 241 | t:writeAt(2,21, "[h] : help on/off ") 242 | t:writeAt(2,22, "[@] : Replay animation") 243 | t:writeAt(2,24, "[q] : QUIT") 244 | end 245 | 246 | local g_count = 0 247 | 248 | function draw() 249 | local t = demo.aText 250 | if HELP then 251 | draw_help() 252 | else 253 | t:writefAt(0, 3, "Vertices :%6d", g_totalVertices) 254 | t:writefAt(0, 4, "Triangles :%6d", g_totalTriangles) 255 | t:writeAt(2, 21, "[h] : help on/off ") 256 | end 257 | 258 | action:playAction() 259 | 260 | aSpace:draw(eye) 261 | if bones ~= nil and bones[BONE] ~= nil then 262 | t:writefAt(0, 2, "Mesh:%2d/%2d Bone:%2d/%2d [%s] ", 263 | MESH, MAXMESH, BONE, MAXBONE, bones[BONE].name) 264 | else 265 | t:writefAt(0,2, "Mesh:%2d/%2d Bone:%2d/%2d ", 266 | MESH, MAXMESH, BONE, MAXBONE) 267 | end 268 | t:writefAt(20, 0, "Finger-counting. Hit [1] - [6]") 269 | demo.messageDraw() 270 | end 271 | 272 | demo.loop(draw, true, keyFunc) 273 | demo.exit() 274 | 275 | -------------------------------------------------------------------------------- /examples/num512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jun-mizutani/ljes/e82d4ef860801c9444b254235f72fc768d48b780/examples/num512.png -------------------------------------------------------------------------------- /examples/pad.lua: -------------------------------------------------------------------------------- 1 | #!/usr/bin/luajit 2 | -- --------------------------------------------- 3 | -- pad.lua 2014/09/16 4 | -- Copyright (c) 2014 Jun Mizutani, 5 | -- released under the MIT open source license. 6 | -- --------------------------------------------- 7 | package.path = "../LjES/?.lua;" .. package.path 8 | 9 | require("GamePad") 10 | local util = require("util") 11 | 12 | local pad = GamePad:new() 13 | 14 | pad:list() 15 | print(pad:getNumOfButtons()) 16 | 17 | if pad:available() then 18 | 19 | while (true) do 20 | if pad:readEvents() > 0 then 21 | if pad:checkButton(GamePad.A) then 22 | print("A") -- do something 23 | elseif pad:checkButton(GamePad.B) then 24 | print("B") -- do something 25 | end 26 | local dirX = pad:checkAxis(GamePad.DirX) 27 | local dirY = pad:checkAxis(GamePad.DirY) 28 | util.printf("dirX=%3d, dirY=%3d\n", dirX, dirY) 29 | end 30 | -- util.sleep(0.5) 31 | end 32 | 33 | end 34 | 35 | -------------------------------------------------------------------------------- /examples/skin.lua: -------------------------------------------------------------------------------- 1 | #!/usr/bin/luajit 2 | -- --------------------------------------------- 3 | -- skin.lua 2014/03/30 4 | -- Copyright (c) 2014 Jun Mizutani, 5 | -- released under the MIT open source license. 6 | -- 7 | -- Demonstrate skeletal animation without Collada. 8 | -- --------------------------------------------- 9 | 10 | package.path = "../LjES/?.lua;" .. package.path 11 | 12 | local demo = require("demo") 13 | local tex, tex2 14 | 15 | local aShape 16 | local j0, j1 -- Bone 17 | 18 | function revolution(latitude, longitude, verts) 19 | local n = #verts / 2 -- latitude + 1 20 | local v 21 | local bottom = verts[#verts] 22 | local height = verts[2] - verts[#verts] 23 | for i= 0, n-1 do 24 | local k = i * 2 + 1 -- index++ 25 | v = aShape:addVertex(verts[k], verts[k+1], 0) 26 | -- set vertex weight 27 | aShape:addVertexWeight(v, 0, 1 - (verts[k+1] - bottom)/height ) 28 | aShape:addVertexWeight(v, 1, (verts[k+1] - bottom)/height ) 29 | end 30 | T = math.pi * 2 / longitude 31 | for j = 1, longitude+1 do 32 | for i = 0, n-1 do 33 | local k = i * 2 + 1 -- index++ 34 | v = aShape:addVertex(verts[k] * math.cos(T*j), verts[k+1], 35 | - verts[k] * math.sin(T*j)) 36 | -- set vertex weight 37 | aShape:addVertexWeight(v, 0, 1 - (verts[k+1] - bottom)/height ) 38 | aShape:addVertexWeight(v, 1, (verts[k+1] - bottom)/height ) 39 | end 40 | end 41 | 42 | for j = 0, longitude-2 do 43 | for i = 0, n - 2 do 44 | aShape:addPlane( 45 | { j * n + i, 46 | j * n + i + 1, 47 | (j + 1) * n + i + 1, 48 | (j + 1) * n + i 49 | }) 50 | end 51 | end 52 | -- m-1 to 0 53 | for i = 0, n - 2 do 54 | aShape:addPlane( 55 | { (longitude-1) * n + i, 56 | (longitude-1) * n + i + 1, 57 | i + 1, 58 | i 59 | }) 60 | end 61 | end 62 | 63 | function prism(height, radius, n) 64 | vertices = {} 65 | table.insert(vertices, 0.0001 ) -- x axis TOP 66 | table.insert(vertices, height ) -- y axis 67 | 68 | table.insert(vertices, radius ) -- x axis 69 | table.insert(vertices, height ) -- y axis 70 | 71 | table.insert(vertices, radius ) -- x axis 72 | table.insert(vertices, height*2/3) -- y axis 73 | 74 | table.insert(vertices, radius ) -- x axis 75 | table.insert(vertices, height/3 ) -- y axis 76 | 77 | table.insert(vertices, radius ) -- x axis 78 | table.insert(vertices, 0 ) -- y axis 79 | 80 | table.insert(vertices, radius ) -- x axis 81 | table.insert(vertices, -height/3 ) -- y axis 82 | table.insert(vertices, radius ) -- x axis 83 | table.insert(vertices, -height*2/3) -- y axis 84 | table.insert(vertices, radius ) -- x axis 85 | table.insert(vertices, -height ) -- y axis 86 | 87 | table.insert(vertices, 0.0001 ) -- x axis BOTTOM 88 | table.insert(vertices, -height ) -- y axis 89 | revolution(8, n, vertices) 90 | end 91 | 92 | function createShapes() 93 | aShape = Shape:new() 94 | -- embed bones 95 | local skeleton = Skeleton:new() 96 | aShape:setSkeleton(skeleton) 97 | j0 = skeleton:addBone(nil, "j0") 98 | j1 = skeleton:addBone(j0, "j1") 99 | j0:setRestPosition(0.0, -10, 0.0) 100 | j1:setRestPosition(0.0, 10.0, 0.0) 101 | skeleton:bindRestPose() 102 | aShape:shaderParameter("has_bone", 1) 103 | 104 | aShape:setTextureMappingMode(1) 105 | aShape:setTextureMappingAxis(1) 106 | aShape:setTextureScale(16, 16) 107 | aShape:shaderParameter("use_texture", 1) 108 | aShape:shaderParameter("texture", tex) 109 | 110 | prism(10, 2, 16) 111 | aShape:endShape() 112 | aShape:shaderParameter("color", {1.0, 1.0, 1.0, 1.0}) 113 | end 114 | 115 | demo.screen(0, 0) 116 | math.randomseed(os.time()) 117 | local aSpace = demo.getSpace() 118 | local eye = aSpace:addNode(nil, "eye") 119 | eye:setPosition(0, 0, 25) 120 | tex = Texture:new() 121 | tex:readImageFromFile("num512.png") 122 | createShapes() 123 | 124 | local node = aSpace:addNode(nil, "objNode") 125 | node:setPosition(0, 0, 0) 126 | node:addShape(aShape) 127 | 128 | demo.backgroundColor(0.0, 0.0, 0.0) 129 | j1:rotateZ(75) 130 | aSpace:timerStart() 131 | 132 | function draw() 133 | local count = demo.aScreen:getFrameCount() 134 | if (count - math.floor(count/100)*100) < 50 then 135 | j1:rotateZ(-3) 136 | else 137 | j1:rotateZ(3) 138 | end 139 | j0:rotateY(-0.6) 140 | --node:rotateY(0.5) 141 | aSpace:draw(eye) 142 | --aShape.skeleton:listBones() 143 | end 144 | 145 | demo.loop(draw, true) 146 | demo.exit() 147 | -------------------------------------------------------------------------------- /examples/sphere.lua: -------------------------------------------------------------------------------- 1 | package.path = "../LjES/?.lua;" .. package.path 2 | local demo = require("demo") 3 | 4 | demo.screen(0, 0) 5 | demo.backgroundColor(0.2, 0.2, 0.4) 6 | 7 | local aSpace = demo.getSpace() 8 | 9 | local eye = aSpace:addNode(nil, "eye") 10 | eye:setPosition(0, 0, 30) 11 | 12 | local shape = Shape:new() 13 | shape:sphere(8, 16, 16) 14 | shape:endShape() 15 | shape:shaderParameter("color", {0.5, 0.3, 0.0, 1.0}) 16 | 17 | local node = aSpace:addNode(nil, "node1") 18 | node:setPosition(0, 0, 0) 19 | node:addShape(shape) 20 | 21 | function draw() 22 | node:rotateX(0.1) 23 | aSpace:draw(eye) 24 | end 25 | 26 | demo.loop(draw, true) 27 | demo.exit() 28 | -------------------------------------------------------------------------------- /examples/term.lua: -------------------------------------------------------------------------------- 1 | #!/usr/bin/luajit 2 | 3 | package.path = "../LjES/?.lua;" .. package.path 4 | local termios = require("termios") 5 | termios.getTermios() 6 | termios.resetRawMode() 7 | -------------------------------------------------------------------------------- /examples/test_task.lua: -------------------------------------------------------------------------------- 1 | #!/usr/bin/luajit 2 | -- --------------------------------------------- 3 | -- test_task.lua 2014/03/30 4 | -- Copyright (c) 2014 Jun Mizutani, 5 | -- released under the MIT open source license. 6 | -- 7 | -- Demonstrate Schedule and Task class. 8 | -- --------------------------------------------- 9 | package.path = "../LjES/?.lua;" .. package.path 10 | 11 | require("Schedule") 12 | local util = require("util") 13 | 14 | -- --------------------- 15 | -- function 16 | -- --------------------- 17 | function fa(a) 18 | print("A", a) 19 | end 20 | 21 | -- --------------------- 22 | -- class and method 23 | -- --------------------- 24 | Class = Object:new() 25 | function Class.new(self) 26 | local obj = Object.new(self) 27 | obj.value = 0 28 | return obj 29 | end 30 | 31 | function Class.print(self, val) 32 | print("Class", val) 33 | end 34 | 35 | local instance = Class:new() 36 | 37 | -- --------------------- 38 | -- another function 39 | -- --------------------- 40 | local fc = function(c) print("C", c) end 41 | local command_list = { 42 | { 100, fc, { 1000 }}, 43 | { 200, fc, { 2000 }}, 44 | { 3000, fc, { 3000 }} 45 | } 46 | 47 | schedule = Schedule:new() 48 | local task = schedule:addTask("task1") 49 | -- time, function, param 50 | task:addCommand(200, fa, { 100 }) -- fa( 100 * delta_time / total_time ) 51 | task:addCommand(200, fa, { 300 }) 52 | 53 | task = schedule:addTask("task2") 54 | task:setTargetObject(instance) -- instance:print(...) will be called. 55 | task:addCommand( 10, Class.print, { 100 }) 56 | task:addCommand( 1, Class.print, { 200 }) 57 | task:addCommand(500, Class.print, { 30 }) 58 | 59 | task = schedule:addTask("task3") 60 | task:setCommand(command_list) 61 | 62 | print("-- start!") 63 | schedule:start() 64 | 65 | -- execute commands every 50 msec for 5sec. 66 | for i = 1, 100 do 67 | schedule:doCommand() 68 | util.sleep(0.05) 69 | end 70 | print("-- finished!") 71 | -------------------------------------------------------------------------------- /examples/tiny.lua: -------------------------------------------------------------------------------- 1 | #!/usr/bin/luajit 2 | 3 | package.path = "../LjES/?.lua;" .. package.path 4 | local demo = require("demo") 5 | 6 | demo.screen(0, 0) 7 | local aSpace = demo.getSpace() 8 | local eye = aSpace:addNode(nil, "eye") 9 | eye:setPosition(0, 0, 30) 10 | demo.backgroundColor(0.2, 0.2, 0.4) 11 | 12 | local shape = Shape:new() 13 | shape:donut(8, 3, 16, 16) 14 | shape:endShape() 15 | shape:shaderParameter("color", {1.0, 0.5, 0.6, 1.0}) 16 | 17 | local node = aSpace:addNode(nil, "node1") 18 | node:setPosition(0, 0, 0) 19 | node:addShape(shape) 20 | 21 | function draw() 22 | node:rotateX(0.1) 23 | aSpace:draw(eye) 24 | end 25 | 26 | demo.loop(draw, true) 27 | demo.exit() 28 | -------------------------------------------------------------------------------- /examples/truncated_cone.lua: -------------------------------------------------------------------------------- 1 | package.path = "../LjES/?.lua;" .. package.path 2 | local demo = require("demo") 3 | 4 | demo.screen(0, 0) 5 | demo.backgroundColor(0.2, 0.2, 0.4) 6 | 7 | local aSpace = demo.getSpace() 8 | 9 | local eye = aSpace:addNode(nil, "eye") 10 | eye:setPosition(0, 0, 30) 11 | 12 | local shape = Shape:new() 13 | shape:truncated_cone(8, 2, 8, 16) 14 | shape:endShape() 15 | shape:shaderParameter("color", {0.5, 0.3, 0.0, 1.0}) 16 | 17 | local node = aSpace:addNode(nil, "node1") 18 | node:setPosition(0, 0, 0) 19 | node:addShape(shape) 20 | 21 | function draw() 22 | node:rotateX(0.1) 23 | aSpace:draw(eye) 24 | end 25 | 26 | demo.loop(draw, true) 27 | demo.exit() 28 | --------------------------------------------------------------------------------