geometry.go 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464
  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. // Geometry encapsulates a three-dimensional geometric shape.
  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. // Geometric properties
  26. boundingBox math32.Box3 // Last calculated bounding box
  27. boundingSphere math32.Sphere // Last calculated bounding sphere
  28. volume float32 // Last calculated volume
  29. area float32 // Last calculated area
  30. // Flags indicating whether geometric properties are valid
  31. boundingBoxValid bool // Indicates if last calculated bounding box is valid
  32. boundingSphereValid bool // Indicates if last calculated bounding sphere is valid
  33. volumeValid bool // Indicates if last calculated volume is valid
  34. areaValid bool // Indicates if last calculated area is valid
  35. }
  36. // Geometry group object
  37. type Group struct {
  38. Start int // Index of first element of the group
  39. Count int // Number of elements in the group
  40. Matindex int // Material index for this group
  41. Matid string // Material id used when loading external models
  42. }
  43. func NewGeometry() *Geometry {
  44. g := new(Geometry)
  45. g.Init()
  46. return g
  47. }
  48. // Init initializes the geometry
  49. func (g *Geometry) Init() {
  50. g.refcount = 1
  51. g.vbos = make([]*gls.VBO, 0)
  52. g.groups = make([]Group, 0)
  53. g.gs = nil
  54. g.handleVAO = 0
  55. g.handleIndices = 0
  56. g.updateIndices = true
  57. }
  58. // Incref increments the reference count for this geometry
  59. // and returns a pointer to the geometry.
  60. // It should be used when this geometry is shared by another
  61. // Graphic object.
  62. func (g *Geometry) Incref() *Geometry {
  63. g.refcount++
  64. return g
  65. }
  66. // Dispose decrements this geometry reference count and
  67. // if necessary releases OpenGL resources, C memory
  68. // and VBOs associated with this geometry.
  69. func (g *Geometry) Dispose() {
  70. if g.refcount > 1 {
  71. g.refcount--
  72. return
  73. }
  74. // Delete VAO and indices buffer
  75. if g.gs != nil {
  76. g.gs.DeleteVertexArrays(g.handleVAO)
  77. g.gs.DeleteBuffers(g.handleIndices)
  78. }
  79. // Delete this geometry VBO buffers
  80. for i := 0; i < len(g.vbos); i++ {
  81. g.vbos[i].Dispose()
  82. }
  83. g.Init()
  84. }
  85. func (g *Geometry) GetGeometry() *Geometry {
  86. return g
  87. }
  88. // AddGroup adds a geometry group (for multimaterial)
  89. func (g *Geometry) AddGroup(start, count, matIndex int) *Group {
  90. g.groups = append(g.groups, Group{start, count, matIndex, ""})
  91. return &g.groups[len(g.groups)-1]
  92. }
  93. // AddGroupList adds the specified list of groups to this geometry
  94. func (g *Geometry) AddGroupList(groups []Group) {
  95. for _, group := range groups {
  96. g.groups = append(g.groups, group)
  97. }
  98. }
  99. // GroupCount returns the number of geometry groups (for multimaterial)
  100. func (g *Geometry) GroupCount() int {
  101. return len(g.groups)
  102. }
  103. // GroupAt returns pointer to geometry group at the specified index
  104. func (g *Geometry) GroupAt(idx int) *Group {
  105. return &g.groups[idx]
  106. }
  107. // SetIndices sets the indices array for this geometry
  108. func (g *Geometry) SetIndices(indices math32.ArrayU32) {
  109. g.indices = indices
  110. g.updateIndices = true
  111. g.boundingBoxValid = false
  112. g.boundingSphereValid = false
  113. }
  114. // Indices returns this geometry indices array
  115. func (g *Geometry) Indices() math32.ArrayU32 {
  116. return g.indices
  117. }
  118. // AddVBO adds a Vertex Buffer Object for this geometry
  119. func (g *Geometry) AddVBO(vbo *gls.VBO) {
  120. // Check that the provided VBO doesn't have conflicting attributes with existing VBOs
  121. for _, existingVbo := range g.vbos {
  122. for _, attrib := range vbo.Attributes() {
  123. if existingVbo.Attrib(attrib.Name) != nil {
  124. panic("Geometry.AddVBO: geometry already has a VBO with attribute " + attrib.Name)
  125. }
  126. }
  127. }
  128. g.vbos = append(g.vbos, vbo)
  129. }
  130. // VBO returns a pointer to this geometry's VBO which contain the specified attribute.
  131. // Returns nil if the VBO is not found.
  132. func (g *Geometry) VBO(attrib string) *gls.VBO {
  133. for _, vbo := range g.vbos {
  134. if vbo.Attrib(attrib) != nil {
  135. return vbo
  136. }
  137. }
  138. return nil
  139. }
  140. // Returns the number of items in the first VBO
  141. // (The number of items should be same for all VBOs)
  142. // An item is a complete group of attributes in the VBO buffer
  143. func (g *Geometry) Items() int {
  144. if len(g.vbos) == 0 {
  145. return 0
  146. }
  147. vbo := g.vbos[0]
  148. if vbo.AttribCount() == 0 {
  149. return 0
  150. }
  151. return vbo.Buffer().Bytes() / vbo.StrideSize()
  152. }
  153. // BoundingBox computes the bounding box of the geometry if necessary
  154. // and returns is value
  155. func (g *Geometry) BoundingBox() math32.Box3 {
  156. // If valid, return its value
  157. if g.boundingBoxValid {
  158. return g.boundingBox
  159. }
  160. // Reset bounding box
  161. g.boundingBox.Min.Set(0, 0, 0)
  162. g.boundingBox.Max.Set(0, 0, 0)
  163. // Get buffer with position vertices
  164. vboPos := g.VBO("VertexPosition")
  165. if vboPos == nil {
  166. // Return zero-ed bounding box
  167. return g.boundingBox
  168. }
  169. stride := vboPos.Stride()
  170. offset := vboPos.AttribOffset("VertexPosition")
  171. positions := vboPos.Buffer()
  172. // Calculate bounding box
  173. var vertex math32.Vector3
  174. for i := offset; i < positions.Size(); i += stride {
  175. positions.GetVector3(i, &vertex)
  176. g.boundingBox.ExpandByPoint(&vertex)
  177. }
  178. g.boundingBoxValid = true
  179. return g.boundingBox
  180. }
  181. // BoundingSphere computes the bounding sphere of this geometry
  182. // if necessary and returns its value.
  183. func (g *Geometry) BoundingSphere() math32.Sphere {
  184. // If valid, return its value
  185. if g.boundingSphereValid {
  186. return g.boundingSphere
  187. }
  188. // Calculate bounding box
  189. box := g.BoundingBox()
  190. // Set the center of the bounding sphere to the center of the bounding box
  191. box.Center(&g.boundingSphere.Center)
  192. // Reset radius
  193. g.boundingSphere.Radius = float32(0)
  194. // Get buffer with position vertices
  195. vboPos := g.VBO("VertexPosition")
  196. if vboPos == nil {
  197. // Return zero-ed bounding sphere
  198. return g.boundingSphere
  199. }
  200. stride := vboPos.Stride()
  201. offset := vboPos.AttribOffset("VertexPosition")
  202. positions := vboPos.Buffer()
  203. // Find the radius of the bounding sphere
  204. center := g.boundingSphere.Center
  205. maxRadiusSq := float32(0.0)
  206. var vertex math32.Vector3
  207. for i := offset; i < positions.Size(); i += stride {
  208. positions.GetVector3(i, &vertex)
  209. maxRadiusSq = math32.Max(maxRadiusSq, center.DistanceToSquared(&vertex))
  210. }
  211. radius := math32.Sqrt(maxRadiusSq)
  212. if math32.IsNaN(radius) {
  213. panic("geometry.BoundingSphere: computed radius is NaN")
  214. }
  215. g.boundingSphere.Radius = float32(radius)
  216. g.boundingSphereValid = true
  217. return g.boundingSphere
  218. }
  219. // Area returns the surface area.
  220. // NOTE: This only works for triangle-based meshes.
  221. func (g *Geometry) Area() float32 {
  222. // If valid, return its value
  223. if g.areaValid {
  224. return g.area
  225. }
  226. // Reset area
  227. g.area = 0
  228. // Get buffer with position vertices
  229. vboPos := g.VBO("VertexPosition")
  230. if vboPos == nil {
  231. // Return zero-ed area
  232. return g.area
  233. }
  234. positions := vboPos.Buffer()
  235. // Calculate area
  236. var vA, vB, vC math32.Vector3
  237. // Geometry has indexed vertices
  238. if g.indices.Size() > 0 {
  239. for i := 0; i < g.indices.Size(); i += 3 {
  240. // Get face indices
  241. a := g.indices[i]
  242. b := g.indices[i+1]
  243. c := g.indices[i+2]
  244. // Get face position vectors
  245. positions.GetVector3(int(3*a), &vA)
  246. positions.GetVector3(int(3*b), &vB)
  247. positions.GetVector3(int(3*c), &vC)
  248. // Calculate triangle area
  249. vA.Sub(&vC)
  250. vB.Sub(&vC)
  251. vC.CrossVectors(&vA, &vB)
  252. g.area += vC.Length() / 2.0
  253. }
  254. // Geometry has NO indexed vertices
  255. } else {
  256. stride := vboPos.Stride()
  257. offset := vboPos.AttribOffset("VertexPosition")
  258. for i := offset; i < positions.Size(); i += 3*stride {
  259. // Get face indices
  260. a := i
  261. b := i + stride
  262. c := i + 2*stride
  263. // Set face position vectors
  264. positions.GetVector3(int(a), &vA)
  265. positions.GetVector3(int(b), &vB)
  266. positions.GetVector3(int(c), &vC)
  267. // Calculate triangle area
  268. vA.Sub(&vC)
  269. vB.Sub(&vC)
  270. vC.CrossVectors(&vA, &vB)
  271. g.area += vC.Length() / 2.0
  272. }
  273. }
  274. g.areaValid = true
  275. return g.area
  276. }
  277. // Volume returns the volume.
  278. // NOTE: This only works for closed triangle-based meshes.
  279. func (g *Geometry) Volume() float32 {
  280. // If valid, return its value
  281. if g.volumeValid {
  282. return g.volume
  283. }
  284. // Reset volume
  285. g.volume = 0
  286. // Get buffer with position vertices
  287. vboPos := g.VBO("VertexPosition")
  288. if vboPos == nil {
  289. // Return zero-ed area
  290. return g.area
  291. }
  292. positions := vboPos.Buffer()
  293. // Calculate volume
  294. var vA, vB, vC math32.Vector3
  295. // Geometry has indexed vertices
  296. if g.indices.Size() > 0 {
  297. for i := 0; i < g.indices.Size(); i += 3 {
  298. // Get face indices
  299. a := g.indices[i]
  300. b := g.indices[i+1]
  301. c := g.indices[i+2]
  302. // Get face position vectors
  303. positions.GetVector3(int(3*a), &vA)
  304. positions.GetVector3(int(3*b), &vB)
  305. positions.GetVector3(int(3*c), &vC)
  306. // Calculate tetrahedron volume
  307. vA.Sub(&vC)
  308. vB.Sub(&vC)
  309. g.volume += vC.Dot(vA.Cross(&vB)) / 6.0
  310. }
  311. // Geometry has NO indexed vertices
  312. } else {
  313. stride := vboPos.Stride()
  314. offset := vboPos.AttribOffset("VertexPosition")
  315. for i := offset; i < positions.Size(); i += 3*stride {
  316. // Get face indices
  317. a := i
  318. b := i + stride
  319. c := i + 2*stride
  320. // Set face position vectors
  321. positions.GetVector3(int(a), &vA)
  322. positions.GetVector3(int(b), &vB)
  323. positions.GetVector3(int(c), &vC)
  324. // Calculate tetrahedron volume
  325. vA.Sub(&vC)
  326. vB.Sub(&vC)
  327. g.volume += vC.Dot(vA.Cross(&vB)) / 6.0
  328. }
  329. }
  330. g.volumeValid = true
  331. return g.volume
  332. }
  333. // ApplyMatrix multiplies each of the geometry position vertices
  334. // by the specified matrix and apply the correspondent normal
  335. // transform matrix to the geometry normal vectors.
  336. // The geometry's bounding box and sphere are recomputed if needed.
  337. func (g *Geometry) ApplyMatrix(m *math32.Matrix4) {
  338. // Get positions buffer
  339. vboPos := g.VBO("VertexPosition")
  340. if vboPos == nil {
  341. return
  342. }
  343. stride := vboPos.Stride()
  344. offset := vboPos.AttribOffset("VertexPosition")
  345. positions := vboPos.Buffer()
  346. // Apply matrix to all position vertices
  347. for i := offset; i < positions.Size(); i += stride {
  348. var vertex math32.Vector3
  349. positions.GetVector3(i, &vertex)
  350. vertex.ApplyMatrix4(m)
  351. positions.SetVector3(i, &vertex)
  352. }
  353. vboPos.Update()
  354. // Get normals buffer
  355. vboNormals := g.VBO("VertexNormal")
  356. if vboNormals == nil {
  357. return
  358. }
  359. stride = vboNormals.Stride()
  360. offset = vboNormals.AttribOffset("VertexNormal")
  361. normals := vboNormals.Buffer()
  362. // Apply normal matrix to all normal vectors
  363. var normalMatrix math32.Matrix3
  364. normalMatrix.GetNormalMatrix(m)
  365. for i := offset; i < normals.Size(); i += stride {
  366. var vertex math32.Vector3
  367. normals.GetVector3(i, &vertex)
  368. vertex.ApplyMatrix3(&normalMatrix).Normalize()
  369. normals.SetVector3(i, &vertex)
  370. }
  371. vboNormals.Update()
  372. }
  373. // RenderSetup is called by the renderer before drawing the geometry
  374. func (g *Geometry) RenderSetup(gs *gls.GLS) {
  375. // First time initialization
  376. if g.gs == nil {
  377. // Generates VAO and binds it
  378. g.handleVAO = gs.GenVertexArray()
  379. gs.BindVertexArray(g.handleVAO)
  380. // Generates VBO for indices
  381. g.handleIndices = gs.GenBuffer()
  382. // Saves pointer to gl indicating initialization was done.
  383. g.gs = gs
  384. }
  385. // Update VBOs
  386. gs.BindVertexArray(g.handleVAO)
  387. for _, vbo := range g.vbos {
  388. vbo.Transfer(gs)
  389. }
  390. // Updates Indices buffer if necessary
  391. if g.indices.Size() > 0 && g.updateIndices {
  392. gs.BindBuffer(gls.ELEMENT_ARRAY_BUFFER, g.handleIndices)
  393. gs.BufferData(gls.ELEMENT_ARRAY_BUFFER, g.indices.Bytes(), g.indices, gls.STATIC_DRAW)
  394. g.updateIndices = false
  395. }
  396. }