geometry.go 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304
  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. )
  9. // Interface for all geometries
  10. type IGeometry interface {
  11. GetGeometry() *Geometry
  12. RenderSetup(gs *gls.GLS)
  13. Dispose()
  14. }
  15. type Geometry struct {
  16. refcount int // Current number of references
  17. vbos []*gls.VBO // Array of VBOs
  18. groups []Group // Array geometry groups
  19. indices math32.ArrayU32 // Buffer with indices
  20. gs *gls.GLS // Pointer to gl context. Valid after first render setup
  21. handleVAO uint32 // Handle to OpenGL VAO
  22. handleIndices uint32 // Handle to OpenGL buffer for indices
  23. updateIndices bool // Flag to indicate that indices must be transferred
  24. boundingBox math32.Box3 // Last calculated bounding box
  25. boundingBoxValid bool // Indicates if last calculated bounding box is valid
  26. boundingSphere math32.Sphere // Last calculated bounding sphere
  27. boundingSphereValid bool // Indicates if last calculated bounding sphere is valid
  28. }
  29. // Geometry group object
  30. type Group struct {
  31. Start int // Index of first element of the group
  32. Count int // Number of elements in the group
  33. Matindex int // Material index for this group
  34. Matid string // Material id used when loading external models
  35. }
  36. func NewGeometry() *Geometry {
  37. g := new(Geometry)
  38. g.Init()
  39. return g
  40. }
  41. // Init initializes the geometry
  42. func (g *Geometry) Init() {
  43. g.refcount = 1
  44. g.vbos = make([]*gls.VBO, 0)
  45. g.groups = make([]Group, 0)
  46. g.gs = nil
  47. g.handleVAO = 0
  48. g.handleIndices = 0
  49. g.updateIndices = true
  50. }
  51. // Incref increments the reference count for this geometry
  52. // and returns a pointer to the geometry.
  53. // It should be used when this geometry is shared by another
  54. // Graphic object.
  55. func (g *Geometry) Incref() *Geometry {
  56. g.refcount++
  57. return g
  58. }
  59. // Dispose decrements this geometry reference count and
  60. // if necessary releases OpenGL resources, C memory
  61. // and VBOs associated with this geometry.
  62. func (g *Geometry) Dispose() {
  63. if g.refcount > 1 {
  64. g.refcount--
  65. return
  66. }
  67. // Delete VAO and indices buffer
  68. if g.gs != nil {
  69. g.gs.DeleteVertexArrays(g.handleVAO)
  70. g.gs.DeleteBuffers(g.handleIndices)
  71. }
  72. // Delete this geometry VBO buffers
  73. for i := 0; i < len(g.vbos); i++ {
  74. g.vbos[i].Dispose()
  75. }
  76. g.Init()
  77. }
  78. func (g *Geometry) GetGeometry() *Geometry {
  79. return g
  80. }
  81. // AddGroup adds a geometry group (for multimaterial)
  82. func (g *Geometry) AddGroup(start, count, matIndex int) *Group {
  83. g.groups = append(g.groups, Group{start, count, matIndex, ""})
  84. return &g.groups[len(g.groups)-1]
  85. }
  86. // AddGroupList adds the specified list of groups to this geometry
  87. func (g *Geometry) AddGroupList(groups []Group) {
  88. for _, group := range groups {
  89. g.groups = append(g.groups, group)
  90. }
  91. }
  92. // GroupCount returns the number of geometry groups (for multimaterial)
  93. func (g *Geometry) GroupCount() int {
  94. return len(g.groups)
  95. }
  96. // GroupAt returns pointer to geometry group at the specified index
  97. func (g *Geometry) GroupAt(idx int) *Group {
  98. return &g.groups[idx]
  99. }
  100. // SetIndices sets the indices array for this geometry
  101. func (g *Geometry) SetIndices(indices math32.ArrayU32) {
  102. g.indices = indices
  103. g.boundingBoxValid = false
  104. g.boundingSphereValid = false
  105. }
  106. // Indices returns this geometry indices array
  107. func (g *Geometry) Indices() math32.ArrayU32 {
  108. return g.indices
  109. }
  110. // AddVBO adds a Vertex Buffer Object for this geometry
  111. func (g *Geometry) AddVBO(vbo *gls.VBO) {
  112. g.vbos = append(g.vbos, vbo)
  113. }
  114. // VBO returns a pointer to this geometry VBO for the specified attribute.
  115. // Returns nil if the VBO is not found.
  116. func (g *Geometry) VBO(attrib string) *gls.VBO {
  117. for _, vbo := range g.vbos {
  118. if vbo.Attrib(attrib) != nil {
  119. return vbo
  120. }
  121. }
  122. return nil
  123. }
  124. // Returns the number of items in the first VBO
  125. // (The number of items should be same for all VBOs)
  126. // An item is a complete group of attributes in the VBO buffer
  127. func (g *Geometry) Items() int {
  128. if len(g.vbos) == 0 {
  129. return 0
  130. }
  131. vbo := g.vbos[0]
  132. if vbo.AttribCount() == 0 {
  133. return 0
  134. }
  135. return vbo.Buffer().Bytes() / vbo.Stride()
  136. }
  137. // BoundingBox computes the bounding box of the geometry if necessary
  138. // and returns is value
  139. func (g *Geometry) BoundingBox() math32.Box3 {
  140. // If valid, returns its value
  141. if g.boundingBoxValid {
  142. return g.boundingBox
  143. }
  144. // Get buffer with position vertices
  145. vbPos := g.VBO("VertexPosition")
  146. if vbPos == nil {
  147. return g.boundingBox
  148. }
  149. positions := vbPos.Buffer()
  150. // Calculates bounding box
  151. var vertex math32.Vector3
  152. g.boundingBox.Min.Set(0, 0, 0)
  153. g.boundingBox.Max.Set(0, 0, 0)
  154. for i := 0; i < positions.Size(); i += 3 {
  155. positions.GetVector3(i, &vertex)
  156. g.boundingBox.ExpandByPoint(&vertex)
  157. }
  158. g.boundingBoxValid = true
  159. return g.boundingBox
  160. }
  161. // BoundingSphere computes the bounding sphere of this geometry
  162. // if necessary and returns its value.
  163. func (g *Geometry) BoundingSphere() math32.Sphere {
  164. // if valid, returns its value
  165. if g.boundingSphereValid {
  166. return g.boundingSphere
  167. }
  168. // Get buffer with position vertices
  169. vbPos := g.VBO("VertexPosition")
  170. if vbPos == nil {
  171. return g.boundingSphere
  172. }
  173. positions := vbPos.Buffer()
  174. // Get/calculates the bounding box
  175. box := g.BoundingBox()
  176. // Sets the center of the bounding sphere the same as the center of the bounding box.
  177. box.Center(&g.boundingSphere.Center)
  178. center := g.boundingSphere.Center
  179. // Find the radius of the bounding sphere
  180. maxRadiusSq := float32(0.0)
  181. for i := 0; i < positions.Size(); i += 3 {
  182. var vertex math32.Vector3
  183. positions.GetVector3(i, &vertex)
  184. maxRadiusSq = math32.Max(maxRadiusSq, center.DistanceToSquared(&vertex))
  185. }
  186. radius := math32.Sqrt(maxRadiusSq)
  187. if math32.IsNaN(radius) {
  188. panic("geometry.BoundingSphere: computed radius is NaN")
  189. }
  190. g.boundingSphere.Radius = float32(radius)
  191. g.boundingSphereValid = true
  192. return g.boundingSphere
  193. }
  194. // ApplyMatrix multiplies each of the geometry position vertices
  195. // by the specified matrix and apply the correspondent normal
  196. // transform matrix to the geometry normal vectors.
  197. // The geometry's bounding box and sphere are recomputed if needed.
  198. func (g *Geometry) ApplyMatrix(m *math32.Matrix4) {
  199. // Get positions buffer
  200. vboPos := g.VBO("VertexPosition")
  201. if vboPos == nil {
  202. return
  203. }
  204. positions := vboPos.Buffer()
  205. // Apply matrix to all position vertices
  206. for i := 0; i < positions.Size(); i += 3 {
  207. var vertex math32.Vector3
  208. positions.GetVector3(i, &vertex)
  209. vertex.ApplyMatrix4(m)
  210. positions.SetVector3(i, &vertex)
  211. }
  212. vboPos.Update()
  213. // Get normals buffer
  214. vboNormals := g.VBO("VertexNormal")
  215. if vboNormals == nil {
  216. return
  217. }
  218. normals := vboNormals.Buffer()
  219. // Apply normal matrix to all normal vectors
  220. var normalMatrix math32.Matrix3
  221. normalMatrix.GetNormalMatrix(m)
  222. for i := 0; i < normals.Size(); i += 3 {
  223. var vertex math32.Vector3
  224. normals.GetVector3(i, &vertex)
  225. vertex.ApplyMatrix3(&normalMatrix).Normalize()
  226. normals.SetVector3(i, &vertex)
  227. }
  228. vboNormals.Update()
  229. }
  230. // RenderSetup is called by the renderer before drawing the geometry
  231. func (g *Geometry) RenderSetup(gs *gls.GLS) {
  232. // First time initialization
  233. if g.gs == nil {
  234. // Generates VAO and binds it
  235. g.handleVAO = gs.GenVertexArray()
  236. gs.BindVertexArray(g.handleVAO)
  237. // Generates VBO for indices
  238. g.handleIndices = gs.GenBuffer()
  239. // Saves pointer to gl indicating initialization was done.
  240. g.gs = gs
  241. }
  242. // Update VBOs
  243. gs.BindVertexArray(g.handleVAO)
  244. for _, vbo := range g.vbos {
  245. vbo.Transfer(gs)
  246. }
  247. // Updates Indices buffer if necessary
  248. if g.indices.Size() > 0 && g.updateIndices {
  249. gs.BindBuffer(gls.ELEMENT_ARRAY_BUFFER, g.handleIndices)
  250. gs.BufferData(gls.ELEMENT_ARRAY_BUFFER, g.indices.Bytes(), g.indices, gls.STATIC_DRAW)
  251. g.updateIndices = false
  252. }
  253. }