geometry.go 13 KB

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