geometry.go 7.8 KB

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