geometry.go 8.7 KB

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