geometry.go 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515
  1. // Copyright 2016 The G3N Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style
  3. // license that can be found in the LICENSE file.
  4. package geometry
  5. import (
  6. "github.com/g3n/engine/gls"
  7. "github.com/g3n/engine/math32"
  8. "strconv"
  9. )
  10. // IGeometry is the interface for all geometries.
  11. type IGeometry interface {
  12. GetGeometry() *Geometry
  13. RenderSetup(gs *gls.GLS)
  14. Dispose()
  15. }
  16. // Geometry encapsulates a three-dimensional vertex-based geometry.
  17. type Geometry struct {
  18. refcount int // Current number of references
  19. vbos []*gls.VBO // Array of VBOs
  20. groups []Group // Array geometry groups
  21. indices math32.ArrayU32 // Buffer with indices
  22. gs *gls.GLS // Pointer to gl context. Valid after first render setup
  23. ShaderDefines gls.ShaderDefines // Geometry-specific shader defines
  24. handleVAO uint32 // Handle to OpenGL VAO
  25. handleIndices uint32 // Handle to OpenGL buffer for indices
  26. updateIndices bool // Flag to indicate that indices must be transferred
  27. // Geometric properties
  28. boundingBox math32.Box3 // Last calculated bounding box
  29. boundingSphere math32.Sphere // Last calculated bounding sphere
  30. area float32 // Last calculated area
  31. volume float32 // Last calculated volume
  32. rotInertia math32.Matrix3 // Last calculated rotational inertia matrix
  33. // Flags indicating whether geometric properties are valid
  34. boundingBoxValid bool // Indicates if last calculated bounding box is valid
  35. boundingSphereValid bool // Indicates if last calculated bounding sphere is valid
  36. areaValid bool // Indicates if last calculated area is valid
  37. volumeValid bool // Indicates if last calculated volume is valid
  38. rotInertiaValid bool // Indicates if last calculated rotational inertia matrix is valid
  39. }
  40. // Group is a geometry group object.
  41. type Group struct {
  42. Start int // Index of first element of the group
  43. Count int // Number of elements in the group
  44. Matindex int // Material index for this group
  45. Matid string // Material id used when loading external models
  46. }
  47. // NewGeometry creates and returns a pointer to a new Geometry.
  48. func NewGeometry() *Geometry {
  49. g := new(Geometry)
  50. g.Init()
  51. return g
  52. }
  53. // Init initializes the geometry.
  54. func (g *Geometry) Init() {
  55. g.refcount = 1
  56. g.vbos = make([]*gls.VBO, 0)
  57. g.groups = make([]Group, 0)
  58. g.gs = nil
  59. g.handleVAO = 0
  60. g.handleIndices = 0
  61. g.updateIndices = true
  62. g.ShaderDefines = *gls.NewShaderDefines()
  63. }
  64. // Incref increments the reference count for this geometry
  65. // and returns a pointer to the geometry.
  66. // It should be used when this geometry is shared by another
  67. // Graphic object.
  68. func (g *Geometry) Incref() *Geometry {
  69. g.refcount++
  70. return g
  71. }
  72. // Dispose decrements this geometry reference count and
  73. // if possible releases OpenGL resources, C memory
  74. // and VBOs associated with this geometry.
  75. func (g *Geometry) Dispose() {
  76. if g.refcount > 1 {
  77. g.refcount--
  78. return
  79. }
  80. // Delete VAO and indices buffer
  81. if g.gs != nil {
  82. g.gs.DeleteVertexArrays(g.handleVAO)
  83. g.gs.DeleteBuffers(g.handleIndices)
  84. }
  85. // Delete this geometry VBO buffers
  86. for i := 0; i < len(g.vbos); i++ {
  87. g.vbos[i].Dispose()
  88. }
  89. g.Init()
  90. }
  91. // GetGeometry satisfies the IGeometry interface.
  92. func (g *Geometry) GetGeometry() *Geometry {
  93. return g
  94. }
  95. // AddGroup adds a geometry group (for multimaterial).
  96. func (g *Geometry) AddGroup(start, count, matIndex int) *Group {
  97. g.groups = append(g.groups, Group{start, count, matIndex, ""})
  98. return &g.groups[len(g.groups)-1]
  99. }
  100. // AddGroupList adds the specified list of groups to this geometry.
  101. func (g *Geometry) AddGroupList(groups []Group) {
  102. for _, group := range groups {
  103. g.groups = append(g.groups, group)
  104. }
  105. }
  106. // GroupCount returns the number of geometry groups (for multimaterial).
  107. func (g *Geometry) GroupCount() int {
  108. return len(g.groups)
  109. }
  110. // GroupAt returns pointer to geometry group at the specified index.
  111. func (g *Geometry) GroupAt(idx int) *Group {
  112. return &g.groups[idx]
  113. }
  114. // SetIndices sets the indices array for this geometry.
  115. func (g *Geometry) SetIndices(indices math32.ArrayU32) {
  116. g.indices = indices
  117. g.updateIndices = true
  118. g.boundingBoxValid = false
  119. g.boundingSphereValid = false
  120. }
  121. // Indices returns this geometry indices array.
  122. func (g *Geometry) Indices() math32.ArrayU32 {
  123. return g.indices
  124. }
  125. // AddVBO adds a Vertex Buffer Object for this geometry.
  126. func (g *Geometry) AddVBO(vbo *gls.VBO) {
  127. // Check that the provided VBO doesn't have conflicting attributes with existing VBOs
  128. for _, existingVbo := range g.vbos {
  129. for _, attrib := range vbo.Attributes() {
  130. if existingVbo.AttribName(attrib.Name) != nil {
  131. panic("Geometry.AddVBO: geometry already has a VBO with attribute name:" + attrib.Name)
  132. }
  133. if existingVbo.Attrib(attrib.Type) != nil {
  134. panic("Geometry.AddVBO: geometry already has a VBO with attribute type:" + strconv.Itoa(int(attrib.Type)))
  135. }
  136. }
  137. }
  138. g.vbos = append(g.vbos, vbo)
  139. }
  140. // VBO returns a pointer to this geometry's VBO which contain the specified attribute.
  141. // Returns nil if the VBO is not found.
  142. func (g *Geometry) VBO(atype gls.AttribType) *gls.VBO {
  143. for _, vbo := range g.vbos {
  144. if vbo.Attrib(atype) != nil {
  145. return vbo
  146. }
  147. }
  148. return nil
  149. }
  150. // VBOs returns all of this geometry's VBOs.
  151. func (g *Geometry) VBOs() []*gls.VBO {
  152. return g.vbos
  153. }
  154. // Items returns the number of items in the first VBO.
  155. // (The number of items should be same for all VBOs)
  156. // An item is a complete group of attributes in the VBO buffer.
  157. func (g *Geometry) Items() int {
  158. if len(g.vbos) == 0 {
  159. return 0
  160. }
  161. vbo := g.vbos[0]
  162. if vbo.AttribCount() == 0 {
  163. return 0
  164. }
  165. return vbo.Buffer().Bytes() / vbo.StrideSize()
  166. }
  167. // SetAttributeName sets the name of the VBO attribute associated with the provided attribute type.
  168. func (g *Geometry) SetAttributeName(atype gls.AttribType, attribName string) {
  169. vbo := g.VBO(atype)
  170. if vbo != nil {
  171. vbo.Attrib(atype).Name = attribName
  172. }
  173. }
  174. // AttributeName returns the name of the VBO attribute associated with the provided attribute type.
  175. func (g *Geometry) AttributeName(atype gls.AttribType) string {
  176. return g.VBO(atype).Attrib(atype).Name
  177. }
  178. // OperateOnVertices iterates over all the vertices and calls
  179. // the specified callback function with a pointer to each vertex.
  180. // The vertex pointers can be modified inside the callback and
  181. // the modifications will be applied to the buffer at each iteration.
  182. // The callback function returns false to continue or true to break.
  183. func (g *Geometry) OperateOnVertices(cb func(vertex *math32.Vector3) bool) {
  184. // Get buffer with position vertices
  185. vbo := g.VBO(gls.VertexPosition)
  186. if vbo == nil {
  187. return
  188. }
  189. vbo.OperateOnVectors3(gls.VertexPosition, cb)
  190. }
  191. // ReadVertices iterates over all the vertices and calls
  192. // the specified callback function with the value of each vertex.
  193. // The callback function returns false to continue or true to break.
  194. func (g *Geometry) ReadVertices(cb func(vertex math32.Vector3) bool) {
  195. // Get buffer with position vertices
  196. vbo := g.VBO(gls.VertexPosition)
  197. if vbo == nil {
  198. return
  199. }
  200. vbo.ReadVectors3(gls.VertexPosition, cb)
  201. }
  202. // OperateOnVertexNormals iterates over all the vertex normals
  203. // and calls the specified callback function with a pointer to each normal.
  204. // The vertex pointers can be modified inside the callback and
  205. // the modifications will be applied to the buffer at each iteration.
  206. // The callback function returns false to continue or true to break.
  207. func (g *Geometry) OperateOnVertexNormals(cb func(normal *math32.Vector3) bool) {
  208. // Get buffer with position vertices
  209. vbo := g.VBO(gls.VertexNormal)
  210. if vbo == nil {
  211. return
  212. }
  213. vbo.OperateOnVectors3(gls.VertexNormal, cb)
  214. }
  215. // ReadVertexNormals iterates over all the vertex normals and calls
  216. // the specified callback function with the value of each normal.
  217. // The callback function returns false to continue or true to break.
  218. func (g *Geometry) ReadVertexNormals(cb func(vertex math32.Vector3) bool) {
  219. // Get buffer with position vertices
  220. vbo := g.VBO(gls.VertexNormal)
  221. if vbo == nil {
  222. return
  223. }
  224. vbo.ReadVectors3(gls.VertexNormal, cb)
  225. }
  226. // ReadFaces iterates over all the vertices and calls
  227. // the specified callback function with face-forming vertex triples.
  228. // The callback function returns false to continue or true to break.
  229. func (g *Geometry) ReadFaces(cb func(vA, vB, vC math32.Vector3) bool) {
  230. // Get buffer with position vertices
  231. vbo := g.VBO(gls.VertexPosition)
  232. if vbo == nil {
  233. return
  234. }
  235. // If geometry has indexed vertices need to loop over indexes
  236. if g.Indexed() {
  237. var vA, vB, vC math32.Vector3
  238. positions := vbo.Buffer()
  239. for i := 0; i < g.indices.Size(); i += 3 {
  240. // Get face vertices
  241. positions.GetVector3(int(3*g.indices[i]), &vA)
  242. positions.GetVector3(int(3*g.indices[i+1]), &vB)
  243. positions.GetVector3(int(3*g.indices[i+2]), &vC)
  244. // Call callback with face vertices
  245. brk := cb(vA, vB, vC)
  246. if brk {
  247. break
  248. }
  249. }
  250. } else {
  251. // Geometry does NOT have indexed vertices - can read vertices in sequence
  252. vbo.ReadTripleVectors3(gls.VertexPosition, cb)
  253. }
  254. }
  255. // TODO Read and Operate on Texcoords, Faces, Edges, FaceNormals, etc...
  256. // Indexed returns whether the geometry is indexed or not.
  257. func (g *Geometry) Indexed() bool {
  258. return g.indices.Size() > 0
  259. }
  260. // BoundingBox computes the bounding box of the geometry if necessary
  261. // and returns is value.
  262. func (g *Geometry) BoundingBox() math32.Box3 {
  263. // If valid, return its value
  264. if g.boundingBoxValid {
  265. return g.boundingBox
  266. }
  267. // Reset bounding box
  268. g.boundingBox.Min.Set(0, 0, 0)
  269. g.boundingBox.Max.Set(0, 0, 0)
  270. // Expand bounding box by each vertex
  271. g.ReadVertices(func(vertex math32.Vector3) bool {
  272. g.boundingBox.ExpandByPoint(&vertex)
  273. return false
  274. })
  275. g.boundingBoxValid = true
  276. return g.boundingBox
  277. }
  278. // BoundingSphere computes the bounding sphere of this geometry
  279. // if necessary and returns its value.
  280. func (g *Geometry) BoundingSphere() math32.Sphere {
  281. // If valid, return its value
  282. if g.boundingSphereValid {
  283. return g.boundingSphere
  284. }
  285. // Reset radius, calculate bounding box and copy center
  286. g.boundingSphere.Radius = float32(0)
  287. box := g.BoundingBox()
  288. box.Center(&g.boundingSphere.Center)
  289. // Find the radius of the bounding sphere
  290. maxRadiusSq := float32(0)
  291. g.ReadVertices(func(vertex math32.Vector3) bool {
  292. maxRadiusSq = math32.Max(maxRadiusSq, g.boundingSphere.Center.DistanceToSquared(&vertex))
  293. return false
  294. })
  295. g.boundingSphere.Radius = float32(math32.Sqrt(maxRadiusSq))
  296. g.boundingSphereValid = true
  297. return g.boundingSphere
  298. }
  299. // Area returns the surface area.
  300. // NOTE: This only works for triangle-based meshes.
  301. func (g *Geometry) Area() float32 {
  302. // If valid, return its value
  303. if g.areaValid {
  304. return g.area
  305. }
  306. // Reset area
  307. g.area = 0
  308. // Sum area of all triangles
  309. g.ReadFaces(func(vA, vB, vC math32.Vector3) bool {
  310. vA.Sub(&vC)
  311. vB.Sub(&vC)
  312. vC.CrossVectors(&vA, &vB)
  313. g.area += vC.Length() / 2.0
  314. return false
  315. })
  316. g.areaValid = true
  317. return g.area
  318. }
  319. // Volume returns the volume.
  320. // NOTE: This only works for closed triangle-based meshes.
  321. func (g *Geometry) Volume() float32 {
  322. // If valid, return its value
  323. if g.volumeValid {
  324. return g.volume
  325. }
  326. // Reset volume
  327. g.volume = 0
  328. // Calculate volume of all tetrahedrons
  329. g.ReadFaces(func(vA, vB, vC math32.Vector3) bool {
  330. vA.Sub(&vC)
  331. vB.Sub(&vC)
  332. g.volume += vC.Dot(vA.Cross(&vB)) / 6.0
  333. return false
  334. })
  335. g.volumeValid = true
  336. return g.volume
  337. }
  338. // RotationalInertia returns the rotational inertia tensor, also known as the moment of inertia.
  339. // This assumes constant density of 1 (kg/m^2).
  340. // To adjust for a different constant density simply scale the returning matrix by the density.
  341. func (g *Geometry) RotationalInertia(mass float32) math32.Matrix3 {
  342. // If valid, return its value
  343. if g.rotInertiaValid {
  344. return g.rotInertia
  345. }
  346. // Reset rotational inertia
  347. g.rotInertia.Zero()
  348. // For now approximate result based on bounding box
  349. b := math32.NewVec3()
  350. box := g.BoundingBox()
  351. box.Size(b)
  352. multiplier := mass / 12.0
  353. x := (b.Y*b.Y + b.Z*b.Z) * multiplier
  354. y := (b.X*b.X + b.Z*b.Z) * multiplier
  355. z := (b.Y*b.Y + b.X*b.X) * multiplier
  356. g.rotInertia.Set(
  357. x, 0, 0,
  358. 0, y, 0,
  359. 0, 0, z,
  360. )
  361. return g.rotInertia
  362. }
  363. // ProjectOntoAxis projects the geometry onto the specified axis,
  364. // effectively squashing it into a line passing through the local origin.
  365. // Returns the maximum and the minimum values on that line (i.e. signed distances from the local origin).
  366. func (g *Geometry) ProjectOntoAxis(localAxis *math32.Vector3) (float32, float32) {
  367. var max, min float32
  368. g.ReadVertices(func(vertex math32.Vector3) bool {
  369. val := vertex.Dot(localAxis)
  370. if val > max {
  371. max = val
  372. }
  373. if val < min {
  374. min = val
  375. }
  376. return false
  377. })
  378. return max, min
  379. }
  380. // TODO:
  381. // https://stackoverflow.com/questions/21640545/how-to-check-for-convexity-of-a-3d-mesh
  382. // func (g *Geometry) IsConvex() bool {
  383. //
  384. // {
  385. // ApplyMatrix multiplies each of the geometry position vertices
  386. // by the specified matrix and apply the correspondent normal
  387. // transform matrix to the geometry normal vectors.
  388. // The geometry's bounding box and sphere are recomputed if needed.
  389. func (g *Geometry) ApplyMatrix(m *math32.Matrix4) {
  390. // Apply matrix to all vertices
  391. g.OperateOnVertices(func(vertex *math32.Vector3) bool {
  392. vertex.ApplyMatrix4(m)
  393. return false
  394. })
  395. // Apply normal matrix to all normal vectors
  396. var normalMatrix math32.Matrix3
  397. normalMatrix.GetNormalMatrix(m)
  398. g.OperateOnVertexNormals(func(normal *math32.Vector3) bool {
  399. normal.ApplyMatrix3(&normalMatrix).Normalize()
  400. return false
  401. })
  402. }
  403. // RenderSetup is called by the renderer before drawing the geometry.
  404. func (g *Geometry) RenderSetup(gs *gls.GLS) {
  405. // First time initialization
  406. if g.gs == nil {
  407. // Generate VAO and binds it
  408. g.handleVAO = gs.GenVertexArray()
  409. gs.BindVertexArray(g.handleVAO)
  410. // Generate VBO for indices
  411. g.handleIndices = gs.GenBuffer()
  412. // Saves pointer to gs indicating initialization was done.
  413. g.gs = gs
  414. }
  415. // Update VBOs
  416. gs.BindVertexArray(g.handleVAO)
  417. for _, vbo := range g.vbos {
  418. vbo.Transfer(gs)
  419. }
  420. // Updates Indices buffer if necessary
  421. if g.indices.Size() > 0 && g.updateIndices {
  422. gs.BindBuffer(gls.ELEMENT_ARRAY_BUFFER, g.handleIndices)
  423. gs.BufferData(gls.ELEMENT_ARRAY_BUFFER, g.indices.Bytes(), g.indices, gls.STATIC_DRAW)
  424. g.updateIndices = false
  425. }
  426. }