34_DynamicGeometry.lua 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340
  1. -- Dynamic geometry example.
  2. -- This sample demonstrates:
  3. -- - Cloning a Model resource
  4. -- - Modifying the vertex buffer data of the cloned models at runtime to efficiently animate them
  5. -- - Creating a Model resource and its buffer data from scratch
  6. require "LuaScripts/Utilities/Sample"
  7. local boxNodes = {}
  8. local animate = false
  9. local useGroups = false
  10. local animate = true;
  11. local animTime = 0.0
  12. local originalVertexData = VectorBuffer()
  13. local animatingBuffers = {}
  14. local originalVertices = {}
  15. local vertexDuplicates = {}
  16. function Start()
  17. -- Execute the common startup for samples
  18. SampleStart()
  19. -- Create the scene content
  20. CreateScene()
  21. -- Create the UI content
  22. CreateInstructions()
  23. -- Setup the viewport for displaying the scene
  24. SetupViewport()
  25. -- Set the mouse mode to use in the sample
  26. SampleInitMouseMode(MM_RELATIVE)
  27. -- Hook up to the frame update events
  28. SubscribeToEvents()
  29. end
  30. function CreateScene()
  31. scene_ = Scene()
  32. -- Create the Octree component to the scene so that drawable objects can be rendered. Use default volume
  33. -- (-1000, -1000, -1000) to (1000, 1000, 1000)
  34. scene_:CreateComponent("Octree")
  35. -- Create a Zone for ambient light & fog control
  36. local zoneNode = scene_:CreateChild("Zone")
  37. local zone = zoneNode:CreateComponent("Zone")
  38. zone.boundingBox = BoundingBox(-1000.0, 1000.0)
  39. zone.fogColor = Color(0.2, 0.2, 0.2)
  40. zone.fogStart = 200.0
  41. zone.fogEnd = 300.0
  42. -- Create a directional light
  43. local lightNode = scene_:CreateChild("DirectionalLight")
  44. lightNode.direction = Vector3(-0.6, -1.0, -0.8) -- The direction vector does not need to be normalized
  45. local light = lightNode:CreateComponent("Light")
  46. light.lightType = LIGHT_DIRECTIONAL
  47. light.color = Color(0.4, 1.0, 0.4)
  48. light.specularIntensity = 1.5
  49. -- Get the original model and its unmodified vertices, which are used as source data for the animation
  50. local originalModel = cache:GetResource("Model", "Models/Box.mdl")
  51. if originalModel == nil then
  52. print("Model not found, cannot initialize example scene")
  53. return
  54. end
  55. -- Get the vertex buffer from the first geometry's first LOD level
  56. local buffer = originalModel:GetGeometry(0, 0):GetVertexBuffer(0)
  57. originalVertexData = buffer:GetData()
  58. local numVertices = buffer.vertexCount
  59. local vertexSize = buffer.vertexSize
  60. -- Copy the original vertex positions
  61. for i = 0, numVertices - 1 do
  62. originalVertexData:Seek(i * vertexSize)
  63. originalVertices[i+1] = originalVertexData:ReadVector3()
  64. end
  65. -- Detect duplicate vertices to allow seamless animation
  66. for i = 1, table.getn(originalVertices) do
  67. vertexDuplicates[i] = i -- Assume not a duplicate
  68. for j = 1, i - 1 do
  69. if originalVertices[i]:Equals(originalVertices[j]) then
  70. vertexDuplicates[i] = j
  71. break
  72. end
  73. end
  74. end
  75. -- Create StaticModels in the scene. Clone the model for each so that we can modify the vertex data individually
  76. for y = -1, 1 do
  77. for x = -1, 1 do
  78. local node = scene_:CreateChild("Object")
  79. node.position = Vector3(x * 2.0, 0.0, y * 2.0)
  80. local object = node:CreateComponent("StaticModel")
  81. local cloneModel = originalModel:Clone()
  82. object.model = cloneModel
  83. -- Store the cloned vertex buffer that we will modify when animating
  84. table.insert(animatingBuffers, cloneModel:GetGeometry(0, 0):GetVertexBuffer(0))
  85. end
  86. end
  87. -- Finally create one model (pyramid shape) and a StaticModel to display it from scratch
  88. -- Note: there are duplicated vertices to enable face normals. We will calculate normals programmatically
  89. local numVertices = 18
  90. local vertexData = {
  91. -- Position Normal
  92. 0.0, 0.5, 0.0, 0.0, 0.0, 0.0,
  93. 0.5, -0.5, 0.5, 0.0, 0.0, 0.0,
  94. 0.5, -0.5, -0.5, 0.0, 0.0, 0.0,
  95. 0.0, 0.5, 0.0, 0.0, 0.0, 0.0,
  96. -0.5, -0.5, 0.5, 0.0, 0.0, 0.0,
  97. 0.5, -0.5, 0.5, 0.0, 0.0, 0.0,
  98. 0.0, 0.5, 0.0, 0.0, 0.0, 0.0,
  99. -0.5, -0.5, -0.5, 0.0, 0.0, 0.0,
  100. -0.5, -0.5, 0.5, 0.0, 0.0, 0.0,
  101. 0.0, 0.5, 0.0, 0.0, 0.0, 0.0,
  102. 0.5, -0.5, -0.5, 0.0, 0.0, 0.0,
  103. -0.5, -0.5, -0.5, 0.0, 0.0, 0.0,
  104. 0.5, -0.5, -0.5, 0.0, 0.0, 0.0,
  105. 0.5, -0.5, 0.5, 0.0, 0.0, 0.0,
  106. -0.5, -0.5, 0.5, 0.0, 0.0, 0.0,
  107. 0.5, -0.5, -0.5, 0.0, 0.0, 0.0,
  108. -0.5, -0.5, 0.5, 0.0, 0.0, 0.0,
  109. -0.5, -0.5, -0.5, 0.0, 0.0, 0.0
  110. }
  111. local indexData = {
  112. 0, 1, 2,
  113. 3, 4, 5,
  114. 6, 7, 8,
  115. 9, 10, 11,
  116. 12, 13, 14,
  117. 15, 16, 17
  118. }
  119. -- Calculate face normals now
  120. for i = 0, numVertices - 1, 3 do
  121. local v1 = Vector3(vertexData[6 * i + 1], vertexData[6 * i + 2], vertexData[6 * i + 3])
  122. local v2 = Vector3(vertexData[6 * i + 7], vertexData[6 * i + 8], vertexData[6 * i + 9])
  123. local v3 = Vector3(vertexData[6 * i + 13], vertexData[6 * i + 14], vertexData[6 * i + 15])
  124. local edge1 = v1 - v2
  125. local edge2 = v1 - v3
  126. local normal = edge1:CrossProduct(edge2):Normalized()
  127. vertexData[6 * i + 4] = normal.x
  128. vertexData[6 * i + 5] = normal.y
  129. vertexData[6 * i + 6] = normal.z
  130. vertexData[6 * i + 10] = normal.x
  131. vertexData[6 * i + 11] = normal.y
  132. vertexData[6 * i + 12] = normal.z
  133. vertexData[6 * i + 16] = normal.x
  134. vertexData[6 * i + 17] = normal.y
  135. vertexData[6 * i + 18] = normal.z
  136. end
  137. -- Create model, buffers and geometry without garbage collection, as they will be managed
  138. -- by the StaticModel component once assigned to it
  139. local fromScratchModel = Model:new()
  140. local vb = VertexBuffer:new()
  141. local ib = IndexBuffer:new()
  142. local geom = Geometry:new()
  143. -- Shadowed buffer needed for raycasts to work, and so that data can be automatically restored on device loss
  144. vb.shadowed = true
  145. -- We could use the "legacy" element bitmask to define elements for more compact code, but let's demonstrate
  146. -- defining the vertex elements explicitly to allow any element types and order
  147. local elements = {
  148. VertexElement(TYPE_VECTOR3, SEM_POSITION),
  149. VertexElement(TYPE_VECTOR3, SEM_NORMAL)
  150. }
  151. vb:SetSize(numVertices, elements)
  152. local temp = VectorBuffer()
  153. for i = 1, numVertices * 6 do
  154. temp:WriteFloat(vertexData[i])
  155. end
  156. vb:SetData(temp)
  157. ib.shadowed = true
  158. ib:SetSize(numVertices, false)
  159. temp:Clear()
  160. for i = 1, numVertices do
  161. temp:WriteUShort(indexData[i])
  162. end
  163. ib:SetData(temp)
  164. geom:SetVertexBuffer(0, vb)
  165. geom:SetIndexBuffer(ib)
  166. geom:SetDrawRange(TRIANGLE_LIST, 0, numVertices)
  167. fromScratchModel.numGeometries = 1
  168. fromScratchModel:SetGeometry(0, 0, geom)
  169. fromScratchModel.boundingBox = BoundingBox(Vector3(-0.5, -0.5, -0.5), Vector3(0.5, 0.5, 0.5))
  170. local node = scene_:CreateChild("FromScratchObject")
  171. node.position = Vector3(0.0, 3.0, 0.0)
  172. local object = node:CreateComponent("StaticModel")
  173. object.model = fromScratchModel
  174. -- Create the camera. Create it outside the scene so that we can clear the whole scene without affecting it
  175. cameraNode = Node()
  176. cameraNode.position = Vector3(0.0, 2.0, -20.0)
  177. local camera = cameraNode:CreateComponent("Camera")
  178. camera.farClip = 300.0
  179. end
  180. function CreateInstructions()
  181. -- Construct new Text object, set string to display and font to use
  182. local instructionText = ui.root:CreateChild("Text")
  183. instructionText:SetText("Use WASD keys and mouse/touch to move\n"..
  184. "Space to toggle animation")
  185. instructionText:SetFont(cache:GetResource("Font", "Fonts/Anonymous Pro.ttf"), 15)
  186. -- The text has multiple rows. Center them in relation to each other
  187. instructionText.textAlignment = HA_CENTER
  188. -- Position the text relative to the screen center
  189. instructionText.horizontalAlignment = HA_CENTER
  190. instructionText.verticalAlignment = VA_CENTER
  191. instructionText:SetPosition(0, ui.root.height / 4)
  192. end
  193. function SetupViewport()
  194. -- Set up a viewport to the Renderer subsystem so that the 3D scene can be seen
  195. local viewport = Viewport:new(scene_, cameraNode:GetComponent("Camera"))
  196. renderer:SetViewport(0, viewport)
  197. end
  198. function SubscribeToEvents()
  199. -- Subscribe HandleUpdate() function for processing update events
  200. SubscribeToEvent("Update", "HandleUpdate")
  201. end
  202. function MoveCamera(timeStep)
  203. -- Do not move if the UI has a focused element (the console)
  204. if ui.focusElement ~= nil then
  205. return
  206. end
  207. -- Movement speed as world units per second
  208. local MOVE_SPEED = 20.0
  209. -- Mouse sensitivity as degrees per pixel
  210. local MOUSE_SENSITIVITY = 0.1
  211. -- Use this frame's mouse motion to adjust camera node yaw and pitch. Clamp the pitch between -90 and 90 degrees
  212. local mouseMove = input.mouseMove
  213. yaw = yaw + MOUSE_SENSITIVITY * mouseMove.x
  214. pitch = pitch + MOUSE_SENSITIVITY * mouseMove.y
  215. pitch = Clamp(pitch, -90.0, 90.0)
  216. -- Construct new orientation for the camera scene node from yaw and pitch. Roll is fixed to zero
  217. cameraNode.rotation = Quaternion(pitch, yaw, 0.0)
  218. -- Read WASD keys and move the camera scene node to the corresponding direction if they are pressed
  219. if input:GetKeyDown(KEY_W) then
  220. cameraNode:Translate(Vector3(0.0, 0.0, 1.0) * MOVE_SPEED * timeStep)
  221. end
  222. if input:GetKeyDown(KEY_S) then
  223. cameraNode:Translate(Vector3(0.0, 0.0, -1.0) * MOVE_SPEED * timeStep)
  224. end
  225. if input:GetKeyDown(KEY_A) then
  226. cameraNode:Translate(Vector3(-1.0, 0.0, 0.0) * MOVE_SPEED * timeStep)
  227. end
  228. if input:GetKeyDown(KEY_D) then
  229. cameraNode:Translate(Vector3(1.0, 0.0, 0.0) * MOVE_SPEED * timeStep)
  230. end
  231. end
  232. function AnimateObjects(timeStep)
  233. animTime = animTime + timeStep * 100.0;
  234. -- Repeat for each of the cloned vertex buffers
  235. for i = 1, table.getn(animatingBuffers) do
  236. local startPhase = animTime + i * 30.0
  237. local buffer = animatingBuffers[i]
  238. -- Need to prepare a VectorBuffer with all data (positions, normals, uvs...)
  239. local newData = VectorBuffer()
  240. local numVertices = buffer.vertexCount
  241. local vertexSize = buffer.vertexSize
  242. for j = 1, numVertices do
  243. -- If there are duplicate vertices, animate them in phase of the original
  244. local phase = startPhase + vertexDuplicates[j] * 10.0
  245. local src = originalVertices[j]
  246. local dest = Vector3(src.x * (1.0 + 0.1 * Sin(phase)),
  247. src.y * (1.0 + 0.1 * Sin(phase + 60.0)),
  248. src.z * (1.0 + 0.1 * Sin(phase + 120.0)))
  249. -- Write position
  250. newData:WriteVector3(dest)
  251. -- Copy other vertex elements
  252. originalVertexData:Seek((j - 1) * vertexSize + 12) -- Seek past the vertex position
  253. for k = 12, vertexSize - 4, 4 do
  254. newData:WriteFloat(originalVertexData:ReadFloat())
  255. end
  256. end
  257. buffer:SetData(newData)
  258. end
  259. end
  260. function HandleUpdate(eventType, eventData)
  261. -- Take the frame time step, which is stored as a float
  262. local timeStep = eventData["TimeStep"]:GetFloat()
  263. -- Toggle animation with space
  264. if input:GetKeyPress(KEY_SPACE) then
  265. animate = not animate
  266. end
  267. -- Move the camera, scale movement with time step
  268. MoveCamera(timeStep)
  269. -- Animate scene if enabled
  270. if animate then
  271. AnimateObjects(timeStep)
  272. end
  273. end
  274. -- Create XML patch instructions for screen joystick layout specific to this sample app
  275. function GetScreenJoystickPatchString()
  276. return
  277. "<patch>" ..
  278. " <remove sel=\"/element/element[./attribute[@name='Name' and @value='Button0']]/attribute[@name='Is Visible']\" />" ..
  279. " <replace sel=\"/element/element[./attribute[@name='Name' and @value='Button0']]/element[./attribute[@name='Name' and @value='Label']]/attribute[@name='Text']/@value\">Animation</replace>" ..
  280. " <add sel=\"/element/element[./attribute[@name='Name' and @value='Button0']]\">" ..
  281. " <element type=\"Text\">" ..
  282. " <attribute name=\"Name\" value=\"KeyBinding\" />" ..
  283. " <attribute name=\"Text\" value=\"SPACE\" />" ..
  284. " </element>" ..
  285. " </add>" ..
  286. "</patch>"
  287. end