mesh.monkey2 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669
  1. Namespace mojo3d
  2. #rem monkeydoc The Mesh class.
  3. #end
  4. Class Mesh Extends Resource
  5. #rem monkeydoc Creates a new mesh.
  6. Creates a new empty mesh.
  7. Meshes don't actual contain instances of materials. Instead, mesh triangles are added to 'logical' materials which are effectively just integer indices.
  8. Actual materials are stored in models, and can be accessed via the [[Model.Materials]] property.
  9. #end
  10. Method New()
  11. _dirty=Null
  12. _bounds=Boxf.EmptyBounds
  13. _vertices=New Stack<Vertex3f>
  14. _materials=New Stack<MaterialID>
  15. End
  16. Method New( vertices:Vertex3f[],indices:UInt[] )
  17. Self.New()
  18. AddVertices( vertices )
  19. AddTriangles( indices )
  20. End
  21. #rem monkeydoc Number of vertices.
  22. #end
  23. Property NumVertices:Int()
  24. Return _vertices.Length
  25. End
  26. #rem monkeydoc Total number of indices.
  27. #end
  28. Property NumIndices:int()
  29. Local n:=0
  30. For Local material:=Eachin _materials
  31. n+=material.indices.Length
  32. Next
  33. Return n
  34. End
  35. #rem monkeydoc Number of materials.
  36. This will always be at least one.
  37. #end
  38. Property NumMaterials:Int()
  39. Return _materials.Length
  40. End
  41. #rem monkeydoc Mesh bounding box.
  42. #end
  43. Property Bounds:Boxf()
  44. If _dirty & Dirty.Bounds
  45. _bounds=Boxf.EmptyBounds
  46. For Local i:=0 Until _vertices.Length
  47. _bounds|=_vertices[i].position
  48. Next
  49. _dirty&=~Dirty.Bounds
  50. Endif
  51. Return _bounds
  52. End
  53. #rem monkeydoc Compacts the mesh.
  54. Compacts all internal data used by the mesh so they occupy as little memory as possible.
  55. #end
  56. Method Compact()
  57. _vertices.Compact()
  58. _materials.Compact()
  59. For Local material:=Eachin _materials
  60. material.indices.Compact()
  61. Next
  62. End
  63. #rem monkeydoc Clears the mesh.
  64. Removes all vertices and primitives from the mesh, and resets the number of logical materials to '1'.
  65. #end
  66. Method Clear()
  67. _dirty=Null
  68. _bounds=Boxf.EmptyBounds
  69. _vertices.Clear()
  70. _materials.Clear()
  71. InvalidateVertices()
  72. End
  73. #rem monkeydoc Clear the mesh vertices.
  74. #end
  75. Method ClearVertices()
  76. ResizeVertices( 0 )
  77. End
  78. Method ResizeVertices( length:Int )
  79. _vertices.Resize( length )
  80. InvalidateVertices()
  81. End
  82. #rem monkeydoc Sets a range of vertices.
  83. #end
  84. Method SetVertices( vertices:Vertex3f Ptr,first:Int,count:Int )
  85. DebugAssert( first>=0 And count>=0 And first<=_vertices.Length And first+count<=_vertices.Length,"Invalid vertex range" )
  86. libc.memcpy( _vertices.Data.Data+first,vertices,count*Vertex3f.Pitch )
  87. InvalidateVertices( first,count )
  88. End
  89. Method SetVertices( vertices:Vertex3f[] )
  90. _vertices.Resize( vertices.Length )
  91. SetVertices( vertices.Data,0,vertices.Length )
  92. End
  93. #rem monkeydoc Sets a single vertex.
  94. #end
  95. Method SetVertex( index:Int,vertex:Vertex3f )
  96. DebugAssert( index>=0 And index<_vertices.Length,"Vertex index out of range" )
  97. _vertices[index]=vertex
  98. InvalidateVertices( index,1 )
  99. End
  100. #rem monkeydoc Adds vertices.
  101. #end
  102. Method AddVertices( vertices:Vertex3f Ptr,count:Int )
  103. Local first:=_vertices.Length
  104. _vertices.Resize( first+count )
  105. libc.memcpy( _vertices.Data.Data+first,vertices,count*Vertex3f.Pitch )
  106. InvalidateVertices( first,count )
  107. End
  108. Method AddVertices( vertices:Vertex3f[] )
  109. AddVertices( vertices.Data,vertices.Length )
  110. End
  111. #rem monkeydoc Adds a single vertex.
  112. #end
  113. Method AddVertex( vertex:Vertex3f )
  114. AddVertices( Varptr vertex,1 )
  115. End
  116. #rem monkeydoc Gets all vertices as an array.
  117. #end
  118. Method GetVertices:Vertex3f[]()
  119. Return _vertices.ToArray()
  120. End
  121. #rem monkeydoc Gets a single vertex.
  122. #end
  123. Method GetVertex:Vertex3f( index:Int )
  124. DebugAssert( index>=0 And index<_vertices.Length,"Vertex index out of range" )
  125. Return _vertices[index]
  126. End
  127. #rem monkeydoc Sets tthe triangles for a material in the mesh.
  128. `materialid` must be a valid material id in the range 0 to [[NumMaterials]] inclusive.
  129. #end
  130. Method SetTriangles( indices:UInt Ptr,first:Int,count:Int,materialid:Int=0 )
  131. DebugAssert( first Mod 3=0,"First must be a multiple of 3" )
  132. DebugAssert( count Mod 3=0,"Count must be a multiple of 3" )
  133. Local mindices:=GetMaterial( materialid ).indices
  134. DebugAssert( first>=0 And count>=0 And first<=mindices.Length And first+count<=mindices.Length,"Invalid range" )
  135. libc.memcpy( mindices.Data.Data+first,indices,count*IndexPitch )
  136. End
  137. Method SetTriangles( indices:UInt[],materialid:Int=0 )
  138. SetTriangles( indices.Data,indices.Length,materialid )
  139. End
  140. Method SetTriangle( index:Int,i0:Int,i1:Int,i2:Int,materialid:Int=0 )
  141. DebugAssert( index Mod 3=0,"Index must be a multiple of 3" )
  142. Local mindices:=GetMaterial( materialid ).indices
  143. DebugAssert( index>=0 And index+3<=mindices.Length,"Triangle index out of range" )
  144. mindices[index]=i0;mindices[index+1]=i1;mindices[index+2]=i2
  145. End
  146. #rem monkeydoc Adds triangles to the mesh.
  147. `count` is the number of indices to add and must be a multiple of 3.
  148. `materialid` must be a valid material id in the range 0 to [[NumMaterials]] inclusive.
  149. If `materialid` is equal to NumMaterials, a new material is automatically added first.
  150. #end
  151. Method AddTriangles( indices:UInt Ptr,count:Int,materialid:Int=0 )
  152. DebugAssert( count Mod 3=0,"Count must be a multiple of 3" )
  153. Local mindices:=GetMaterial( materialid ).indices
  154. Local first:=mindices.Length
  155. mindices.Resize( mindices.Length+count )
  156. libc.memcpy( mindices.Data.Data+first,indices,count*IndexPitch )
  157. End
  158. Method AddTriangles( indices:UInt[],materialid:Int=0 )
  159. AddTriangles( indices.Data,indices.Length,materialid )
  160. End
  161. #rem monkeydoc Adds a single triangle the mesh.
  162. #end
  163. Method AddTriangle( i0:UInt,i1:UInt,i2:UInt,materialid:Int=0 )
  164. Local indices:=GetMaterial( materialid ).indices
  165. indices.Add( i0 )
  166. indices.Add( i1 )
  167. indices.Add( i2 )
  168. End
  169. #rem monkeydoc Get indices for a material id.
  170. #end
  171. Method GetIndices:UInt[]( materialid:Int=0 )
  172. DebugAssert( materialid>=0 And materialid<_materials.Length,"Material id out of range" )
  173. Return _materials[materialid].indices.ToArray()
  174. End
  175. Method GetAllIndices:Uint[]()
  176. Local indices:=New Uint[NumIndices],ip:=indices.Data
  177. For Local material:=Eachin _materials
  178. Local mindices:=material.indices
  179. libc.memcpy( ip,mindices.Data.Data,mindices.Length*IndexPitch )
  180. ip+=mindices.Length
  181. Next
  182. Return indices
  183. End
  184. #rem monkeydoc Adds materials to the mesh.
  185. Adds `count` logical materials to the mesh.
  186. Returns the first material id of the newly added materials.
  187. #end
  188. Method AddMaterials:Int( count:Int )
  189. Local first:=_materials.Length
  190. For Local i:=0 Until count
  191. _materials.Push( New MaterialID )
  192. Next
  193. Return first
  194. End
  195. #rem monkeydoc Adds a mesh to this mesh.
  196. #end
  197. Method AddMesh( mesh:Mesh,materialid:Int=0 )
  198. Local v0:=_vertices.Length
  199. AddVertices( mesh._vertices.Data.Data,mesh._vertices.Length )
  200. For Local material:=Eachin mesh._materials
  201. Local count:=material.indices.Length
  202. Local mindices:=material.indices.Data
  203. If v0
  204. Local indices:=New UInt[count]
  205. For Local i:=0 Until count
  206. indices[i]=mindices[i]+v0
  207. Next
  208. mindices=indices
  209. Endif
  210. AddTriangles( mindices.Data,count,materialid )
  211. materialid+=1
  212. Next
  213. End
  214. #rem monkeydoc Transforms all vertices in the mesh.
  215. #end
  216. Method TransformVertices( matrix:AffineMat4f )
  217. Local vertices:=_vertices.Data
  218. Local cofactor:=matrix.m.Cofactor()
  219. For Local i:=0 Until _vertices.Length
  220. vertices[i].position=matrix * vertices[i].position
  221. vertices[i].normal=(cofactor * vertices[i].normal).Normalize()
  222. vertices[i].tangent.XYZ=(cofactor * vertices[i].tangent.XYZ).Normalize()
  223. Next
  224. InvalidateVertices()
  225. End
  226. #rem monkeydoc Fits all vertices in the mesh to a box.
  227. #end
  228. Method FitVertices( box:Boxf,uniform:Bool=True )
  229. Local bounds:=Bounds
  230. Local scale:=box.Size/bounds.Size
  231. If uniform scale=New Vec3f( Min( scale.x,Min( scale.y,scale.z ) ) )
  232. Local m:=Mat3f.Scaling( scale )
  233. Local t:=box.Center - m * bounds.Center
  234. TransformVertices( New AffineMat4f( m,t ) )
  235. End
  236. #rem monkeydoc Updates mesh normals.
  237. Recalculates all vertex normals based on triangle and vertex positions.
  238. #end
  239. Method UpdateNormals()
  240. Local vertices:=_vertices.Data
  241. For Local i:=0 Until _vertices.Length
  242. vertices[i].normal=New Vec3f(0)
  243. Next
  244. For Local material:=Eachin _materials
  245. Local indices:=material.indices.Data
  246. For Local i:=0 Until material.indices.Length Step 3
  247. Local i1:=indices[i+0]
  248. Local i2:=indices[i+1]
  249. Local i3:=indices[i+2]
  250. Local v1:=vertices[i1].position
  251. Local v2:=vertices[i2].position
  252. Local v3:=vertices[i3].position
  253. Local n:=(v2-v1).Cross(v3-v1).Normalize()
  254. vertices[i1].normal+=n
  255. vertices[i2].normal+=n
  256. vertices[i3].normal+=n
  257. Next
  258. Next
  259. For Local i:=0 Until _vertices.Length
  260. vertices[i].normal=vertices[i].normal.Normalize()
  261. Next
  262. InvalidateVertices()
  263. End
  264. #rem monkeydoc Updates mesh tangents.
  265. Recalculates all vertex tangents based on triangles, vertex normals and vertex texcoord0.
  266. #end
  267. Method UpdateTangents()
  268. Local vertices:=_vertices.Data.Data
  269. Local tan1:=New Vec3f[_vertices.Length]
  270. Local tan2:=New Vec3f[_vertices.Length]
  271. For Local material:=Eachin _materials
  272. Local indices:=material.indices.Data
  273. For Local i:=0 Until material.indices.Length Step 3
  274. Local i1:=indices[i+0]
  275. Local i2:=indices[i+1]
  276. Local i3:=indices[i+2]
  277. Local v1:=vertices+i1
  278. Local v2:=vertices+i2
  279. Local v3:=vertices+i3
  280. Local x1:=v2->Tx-v1->Tx
  281. Local x2:=v3->Tx-v1->Tx
  282. Local y1:=v2->Ty-v1->Ty
  283. Local y2:=v3->Ty-v1->Ty
  284. Local z1:=v2->Tz-v1->Tz
  285. Local z2:=v3->Tz-v1->Tz
  286. Local s1:=v2->Sx-v1->Sx
  287. Local s2:=v3->Sx-v1->Sx
  288. Local t1:=v2->Sy-v1->Sy
  289. Local t2:=v3->Sy-v1->Sy
  290. Local r:=1.0/(s1*t2-s2*t1)
  291. Local sdir:=New Vec3f( (t2 * x1 - t1 * x2) * r, (t2 * y1 - t1 * y2) * r, (t2 * z1 - t1 * z2) * r )
  292. Local tdir:=New Vec3f( (s1 * x2 - s2 * x1) * r, (s1 * y2 - s2 * y1) * r, (s1 * z2 - s2 * z1) * r )
  293. tan1[i1]+=sdir
  294. tan1[i2]+=sdir
  295. tan1[i3]+=sdir
  296. tan2[i1]+=tdir
  297. tan2[i2]+=tdir
  298. tan2[i3]+=tdir
  299. Next
  300. Next
  301. For Local i:=0 Until _vertices.Length
  302. Local v:=vertices+i
  303. Local n:=v->normal,t:=tan1[i]
  304. v->tangent.XYZ=( t - n * n.Dot( t ) ).Normalize()
  305. v->tangent.w=n.Cross( t ).Dot( tan2[i] ) < 0 ? -1 Else 1
  306. Next
  307. InvalidateVertices()
  308. End
  309. #rem monkeydoc Flips all triangles.
  310. #end
  311. Method FlipTriangles()
  312. For Local material:=Eachin _materials
  313. Local indices:=material.indices.Data
  314. For Local i:=0 Until material.indices.Length Step 3
  315. Local t:=indices[i]
  316. indices[i]=indices[i+1]
  317. indices[i+1]=t
  318. Next
  319. material.dirty|=Dirty.IndexBuffer
  320. Next
  321. End
  322. #rem monkeydoc Scales texture coordinates.
  323. #end
  324. Method ScaleTexCoords( scale:Vec2f )
  325. Local vertices:=_vertices.Data
  326. For Local i:=0 Until _vertices.Length
  327. vertices[i].texCoord0*=scale
  328. Next
  329. InvalidateVertices()
  330. End
  331. #rem monkeydoc Copies texcoord0 to texcoord1.
  332. #end
  333. Method CopyTexCoords()
  334. Local vertices:=_vertices.Data
  335. For Local i:=0 Until _vertices.Length
  336. vertices[i].texCoord1=vertices[i].texCoord0
  337. Next
  338. InvalidateVertices()
  339. End
  340. #rem monkeydoc Loads a mesh from a file.
  341. On its own, mojo3d can only load gltf2 format mesh and model files.
  342. To add more formats, #import the mojo3d-assimp module into your app, eg:
  343. ```
  344. #Import "<mojo3d>"
  345. #Import "<mojo3d-assimp>"
  346. ```
  347. This will allow you to load any format supported by the assimp module.
  348. However, importing the assimp module into your app will also increase its size.
  349. #end
  350. Function Load:Mesh( path:String )
  351. For Local loader:=Eachin Mojo3dLoader.Instances
  352. Local mesh:=loader.LoadMesh( path )
  353. If mesh Return mesh
  354. Next
  355. Return Null
  356. End
  357. Internal
  358. Method GetVertexBuffer:VertexBuffer()
  359. If _dirty & Dirty.VertexBuffer
  360. _vbuffer=New VertexBuffer( Vertex3f.Format,_vertices.Length )
  361. _vbuffer.SetVertices( _vertices.Data.Data,0,_vertices.Length )
  362. _dirty&=~Dirty.VertexBuffer
  363. End
  364. Return _vbuffer
  365. End
  366. Method GetIndexBuffer:IndexBuffer( materialid:Int )
  367. Local material:=_materials[materialid]
  368. If material.dirty & Dirty.IndexBuffer
  369. Local indices:=material.indices
  370. material.ibuffer=New IndexBuffer( IndexFormat.UINT32,indices.Length )
  371. material.ibuffer.SetIndices( indices.Data.Data,0,indices.Length )
  372. material.dirty&=~Dirty.IndexBuffer
  373. End
  374. Return material.ibuffer
  375. End
  376. Method GetIndexBufferBounds:Boxf( materialid:Int )
  377. Local material:=_materials[materialid]
  378. If material.dirty & Dirty.Bounds
  379. Local bounds:=Bounds.EmptyBounds
  380. Local vertices:=_vertices.Data.Data
  381. For Local i:=Eachin material.indices
  382. bounds|=vertices[i].position
  383. Next
  384. material.bounds=bounds
  385. material.dirty&=~Dirty.Bounds
  386. Endif
  387. Return material.bounds
  388. End
  389. Private
  390. Enum Dirty
  391. Bounds=1
  392. VertexBuffer=2
  393. IndexBuffer=4
  394. End
  395. class MaterialID
  396. Field indices:=New Stack<UInt>
  397. Field dirty:Dirty=Dirty.IndexBuffer|Dirty.Bounds
  398. Field ibuffer:IndexBuffer
  399. Field bounds:Boxf
  400. End
  401. Const IndexPitch:=4
  402. Field _vertices:=New Stack<Vertex3f>
  403. Field _materials:=New Stack<MaterialID>
  404. Field _dirty:Dirty=Null
  405. Field _bounds:Boxf=Boxf.EmptyBounds
  406. Field _vbuffer:VertexBuffer
  407. Field _minDirty:Int
  408. Field _maxDirty:Int
  409. Method InvalidateVertices( first:Int,count:Int )
  410. _minDirty=Min( _minDirty,first )
  411. _maxDirty=Max( _maxDirty,first+count )
  412. _dirty|=Dirty.Bounds|Dirty.VertexBuffer
  413. End
  414. Method InvalidateVertices()
  415. InvalidateVertices( 0,_vertices.Length )
  416. End
  417. Method GetMaterial:MaterialID( materialid:Int,dirty:Dirty=Dirty.IndexBuffer|Dirty.Bounds )
  418. DebugAssert( materialid>=0 And materialid<=_materials.Length,"Materialid out of range" )
  419. If materialid=_materials.Length AddMaterials( 1 )
  420. Local material:=_materials[materialid]
  421. material.dirty|=dirty
  422. Return material
  423. End
  424. End