geometry.go 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537
  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 implements several primitive geometry generators.
  5. package geometry
  6. import (
  7. "math"
  8. "strconv"
  9. "github.com/g3n/engine/gls"
  10. "github.com/g3n/engine/math32"
  11. "github.com/g3n/engine/util/logger"
  12. )
  13. // Package logger
  14. var log = logger.New("GEOMETRY", logger.Default)
  15. // IGeometry is the interface for all geometries.
  16. type IGeometry interface {
  17. GetGeometry() *Geometry
  18. RenderSetup(gs *gls.GLS)
  19. Dispose()
  20. }
  21. // Geometry encapsulates a three-dimensional vertex-based geometry.
  22. type Geometry struct {
  23. gs *gls.GLS // Reference to OpenGL state (valid after first RenderSetup)
  24. refcount int // Current number of references
  25. groups []Group // Array geometry groups
  26. vbos []*gls.VBO // Array of VBOs
  27. handleVAO uint32 // Handle to OpenGL VAO
  28. indices math32.ArrayU32 // Buffer with indices
  29. handleIndices uint32 // Handle to OpenGL buffer for indices
  30. updateIndices bool // Flag to indicate that indices must be transferred
  31. ShaderDefines gls.ShaderDefines // Geometry-specific shader defines
  32. // Geometric properties
  33. boundingBox math32.Box3 // Last calculated bounding box
  34. boundingSphere math32.Sphere // Last calculated bounding sphere
  35. area float32 // Last calculated area
  36. volume float32 // Last calculated volume
  37. rotInertia math32.Matrix3 // Last calculated rotational inertia matrix
  38. // Flags indicating whether geometric properties are valid
  39. boundingBoxValid bool // Indicates if last calculated bounding box is valid
  40. boundingSphereValid bool // Indicates if last calculated bounding sphere is valid
  41. areaValid bool // Indicates if last calculated area is valid
  42. volumeValid bool // Indicates if last calculated volume is valid
  43. rotInertiaValid bool // Indicates if last calculated rotational inertia matrix is valid
  44. }
  45. // Group is a geometry group object.
  46. type Group struct {
  47. Start int // Index of first element of the group
  48. Count int // Number of elements in the group
  49. Matindex int // Material index for this group
  50. Matid string // Material id used when loading external models
  51. }
  52. // NewGeometry creates and returns a pointer to a new Geometry.
  53. func NewGeometry() *Geometry {
  54. g := new(Geometry)
  55. g.Init()
  56. return g
  57. }
  58. // Init initializes the geometry.
  59. func (g *Geometry) Init() {
  60. g.refcount = 1
  61. g.vbos = make([]*gls.VBO, 0)
  62. g.groups = make([]Group, 0)
  63. g.gs = nil
  64. g.handleVAO = 0
  65. g.handleIndices = 0
  66. g.updateIndices = true
  67. g.ShaderDefines = *gls.NewShaderDefines()
  68. }
  69. // GetGeometry satisfies the IGeometry interface.
  70. func (g *Geometry) GetGeometry() *Geometry {
  71. return g
  72. }
  73. // AddGroup adds a geometry group (for multimaterial).
  74. func (g *Geometry) AddGroup(start, count, matIndex int) *Group {
  75. g.groups = append(g.groups, Group{start, count, matIndex, ""})
  76. return &g.groups[len(g.groups)-1]
  77. }
  78. // AddGroupList adds the specified list of groups to this geometry.
  79. func (g *Geometry) AddGroupList(groups []Group) {
  80. g.groups = append(g.groups, groups...)
  81. }
  82. // GroupCount returns the number of geometry groups (for multimaterial).
  83. func (g *Geometry) GroupCount() int {
  84. return len(g.groups)
  85. }
  86. // GroupAt returns pointer to geometry group at the specified index.
  87. func (g *Geometry) GroupAt(idx int) *Group {
  88. return &g.groups[idx]
  89. }
  90. // SetIndices sets the indices array for this geometry.
  91. func (g *Geometry) SetIndices(indices math32.ArrayU32) {
  92. g.indices = indices
  93. g.updateIndices = true
  94. g.boundingBoxValid = false
  95. g.boundingSphereValid = false
  96. }
  97. // Indices returns the indices array for this geometry.
  98. func (g *Geometry) Indices() math32.ArrayU32 {
  99. return g.indices
  100. }
  101. // AddVBO adds a Vertex Buffer Object for this geometry.
  102. func (g *Geometry) AddVBO(vbo *gls.VBO) {
  103. // Check that the provided VBO doesn't have conflicting attributes with existing VBOs
  104. for _, existingVbo := range g.vbos {
  105. for _, attrib := range vbo.Attributes() {
  106. if existingVbo.AttribName(attrib.Name) != nil {
  107. panic("Geometry.AddVBO: geometry already has a VBO with attribute name:" + attrib.Name)
  108. }
  109. if attrib.Type != gls.Undefined && existingVbo.Attrib(attrib.Type) != nil {
  110. panic("Geometry.AddVBO: geometry already has a VBO with attribute type:" + strconv.Itoa(int(attrib.Type)))
  111. }
  112. }
  113. }
  114. g.vbos = append(g.vbos, vbo)
  115. }
  116. // VBO returns a pointer to this geometry's VBO which contain the specified attribute.
  117. // Returns nil if the VBO is not found.
  118. func (g *Geometry) VBO(atype gls.AttribType) *gls.VBO {
  119. for _, vbo := range g.vbos {
  120. if vbo.Attrib(atype) != nil {
  121. return vbo
  122. }
  123. }
  124. return nil
  125. }
  126. // VBOName 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) VBOName(name string) *gls.VBO {
  129. for _, vbo := range g.vbos {
  130. if vbo.AttribName(name) != nil {
  131. return vbo
  132. }
  133. }
  134. return nil
  135. }
  136. // VBOs returns all of this geometry's VBOs.
  137. func (g *Geometry) VBOs() []*gls.VBO {
  138. return g.vbos
  139. }
  140. // Items 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. // SetAttributeName sets the name of the VBO attribute associated with the provided attribute type.
  154. func (g *Geometry) SetAttributeName(atype gls.AttribType, attribName string) {
  155. vbo := g.VBO(atype)
  156. if vbo != nil {
  157. vbo.Attrib(atype).Name = attribName
  158. }
  159. }
  160. // AttributeName returns the name of the VBO attribute associated with the provided attribute type.
  161. func (g *Geometry) AttributeName(atype gls.AttribType) string {
  162. return g.VBO(atype).Attrib(atype).Name
  163. }
  164. // OperateOnVertices iterates over all the vertices and calls
  165. // the specified callback function with a pointer to each vertex.
  166. // The vertex pointers can be modified inside the callback and
  167. // the modifications will be applied to the buffer at each iteration.
  168. // The callback function returns false to continue or true to break.
  169. func (g *Geometry) OperateOnVertices(cb func(vertex *math32.Vector3) bool) {
  170. // Get buffer with position vertices
  171. vbo := g.VBO(gls.VertexPosition)
  172. if vbo == nil {
  173. return
  174. }
  175. vbo.OperateOnVectors3(gls.VertexPosition, cb)
  176. // Geometric properties may have changed
  177. g.boundingBoxValid = false
  178. g.boundingSphereValid = false
  179. g.areaValid = false
  180. g.volumeValid = false
  181. g.rotInertiaValid = false
  182. }
  183. // ReadVertices iterates over all the vertices and calls
  184. // the specified callback function with the value of each vertex.
  185. // The callback function returns false to continue or true to break.
  186. func (g *Geometry) ReadVertices(cb func(vertex math32.Vector3) bool) {
  187. // Get buffer with position vertices
  188. vbo := g.VBO(gls.VertexPosition)
  189. if vbo == nil {
  190. return
  191. }
  192. vbo.ReadVectors3(gls.VertexPosition, cb)
  193. }
  194. // OperateOnVertexNormals iterates over all the vertex normals
  195. // and calls the specified callback function with a pointer to each normal.
  196. // The vertex pointers can be modified inside the callback and
  197. // the modifications will be applied to the buffer at each iteration.
  198. // The callback function returns false to continue or true to break.
  199. func (g *Geometry) OperateOnVertexNormals(cb func(normal *math32.Vector3) bool) {
  200. // Get buffer with position vertices
  201. vbo := g.VBO(gls.VertexNormal)
  202. if vbo == nil {
  203. return
  204. }
  205. vbo.OperateOnVectors3(gls.VertexNormal, cb)
  206. }
  207. // ReadVertexNormals iterates over all the vertex normals and calls
  208. // the specified callback function with the value of each normal.
  209. // The callback function returns false to continue or true to break.
  210. func (g *Geometry) ReadVertexNormals(cb func(vertex math32.Vector3) bool) {
  211. // Get buffer with position vertices
  212. vbo := g.VBO(gls.VertexNormal)
  213. if vbo == nil {
  214. return
  215. }
  216. vbo.ReadVectors3(gls.VertexNormal, cb)
  217. }
  218. // ReadFaces iterates over all the vertices and calls
  219. // the specified callback function with face-forming vertex triples.
  220. // The callback function returns false to continue or true to break.
  221. func (g *Geometry) ReadFaces(cb func(vA, vB, vC math32.Vector3) bool) {
  222. // Get buffer with position vertices
  223. vbo := g.VBO(gls.VertexPosition)
  224. if vbo == nil {
  225. return
  226. }
  227. // If geometry has indexed vertices need to loop over indexes
  228. if g.Indexed() {
  229. var vA, vB, vC math32.Vector3
  230. positions := vbo.Buffer()
  231. for i := 0; i < g.indices.Size(); i += 3 {
  232. // Get face vertices
  233. positions.GetVector3(int(3*g.indices[i]), &vA)
  234. positions.GetVector3(int(3*g.indices[i+1]), &vB)
  235. positions.GetVector3(int(3*g.indices[i+2]), &vC)
  236. // Call callback with face vertices
  237. brk := cb(vA, vB, vC)
  238. if brk {
  239. break
  240. }
  241. }
  242. } else {
  243. // Geometry does NOT have indexed vertices - can read vertices in sequence
  244. vbo.ReadTripleVectors3(gls.VertexPosition, cb)
  245. }
  246. }
  247. // TODO Read and Operate on Texcoords, Faces, Edges, FaceNormals, etc...
  248. // Indexed returns whether the geometry is indexed or not.
  249. func (g *Geometry) Indexed() bool {
  250. return g.indices.Size() > 0
  251. }
  252. // BoundingBox computes the bounding box of the geometry if necessary
  253. // and returns is value.
  254. func (g *Geometry) BoundingBox() math32.Box3 {
  255. // If valid, return its value
  256. if g.boundingBoxValid {
  257. return g.boundingBox
  258. }
  259. // Reset bounding box
  260. g.boundingBox.Min.Set(math.MaxFloat32, math.MaxFloat32, math.MaxFloat32)
  261. g.boundingBox.Max.Set(-math.MaxFloat32, -math.MaxFloat32, -math.MaxFloat32)
  262. // Expand bounding box by each vertex
  263. g.ReadVertices(func(vertex math32.Vector3) bool {
  264. g.boundingBox.ExpandByPoint(&vertex)
  265. return false
  266. })
  267. g.boundingBoxValid = true
  268. return g.boundingBox
  269. }
  270. // BoundingSphere computes the bounding sphere of this geometry
  271. // if necessary and returns its value.
  272. func (g *Geometry) BoundingSphere() math32.Sphere {
  273. // If valid, return its value
  274. if g.boundingSphereValid {
  275. return g.boundingSphere
  276. }
  277. // Reset radius, calculate bounding box and copy center
  278. g.boundingSphere.Radius = float32(0)
  279. box := g.BoundingBox()
  280. box.Center(&g.boundingSphere.Center)
  281. // Find the radius of the bounding sphere
  282. maxRadiusSq := float32(0)
  283. g.ReadVertices(func(vertex math32.Vector3) bool {
  284. maxRadiusSq = math32.Max(maxRadiusSq, g.boundingSphere.Center.DistanceToSquared(&vertex))
  285. return false
  286. })
  287. g.boundingSphere.Radius = float32(math32.Sqrt(maxRadiusSq))
  288. g.boundingSphereValid = true
  289. return g.boundingSphere
  290. }
  291. // Area returns the surface area.
  292. // NOTE: This only works for triangle-based meshes.
  293. func (g *Geometry) Area() float32 {
  294. // If valid, return its value
  295. if g.areaValid {
  296. return g.area
  297. }
  298. // Reset area
  299. g.area = 0
  300. // Sum area of all triangles
  301. g.ReadFaces(func(vA, vB, vC math32.Vector3) bool {
  302. vA.Sub(&vC)
  303. vB.Sub(&vC)
  304. vC.CrossVectors(&vA, &vB)
  305. g.area += vC.Length() / 2.0
  306. return false
  307. })
  308. g.areaValid = true
  309. return g.area
  310. }
  311. // Volume returns the volume.
  312. // NOTE: This only works for closed triangle-based meshes.
  313. func (g *Geometry) Volume() float32 {
  314. // If valid, return its value
  315. if g.volumeValid {
  316. return g.volume
  317. }
  318. // Reset volume
  319. g.volume = 0
  320. // Calculate volume of all tetrahedrons
  321. g.ReadFaces(func(vA, vB, vC math32.Vector3) bool {
  322. vA.Sub(&vC)
  323. vB.Sub(&vC)
  324. g.volume += vC.Dot(vA.Cross(&vB)) / 6.0
  325. return false
  326. })
  327. g.volumeValid = true
  328. return g.volume
  329. }
  330. // RotationalInertia returns the rotational inertia tensor, also known as the moment of inertia.
  331. // This assumes constant density of 1 (kg/m^2).
  332. // To adjust for a different constant density simply scale the returning matrix by the density.
  333. func (g *Geometry) RotationalInertia(mass float32) math32.Matrix3 {
  334. // If valid, return its value
  335. if g.rotInertiaValid {
  336. return g.rotInertia
  337. }
  338. // Reset rotational inertia
  339. g.rotInertia.Zero()
  340. // For now approximate result based on bounding box
  341. b := math32.NewVec3()
  342. box := g.BoundingBox()
  343. box.Size(b)
  344. multiplier := mass / 12.0
  345. x := (b.Y*b.Y + b.Z*b.Z) * multiplier
  346. y := (b.X*b.X + b.Z*b.Z) * multiplier
  347. z := (b.Y*b.Y + b.X*b.X) * multiplier
  348. g.rotInertia.Set(
  349. x, 0, 0,
  350. 0, y, 0,
  351. 0, 0, z,
  352. )
  353. return g.rotInertia
  354. }
  355. // ProjectOntoAxis projects the geometry onto the specified axis,
  356. // effectively squashing it into a line passing through the local origin.
  357. // Returns the maximum and the minimum values on that line (i.e. signed distances from the local origin).
  358. func (g *Geometry) ProjectOntoAxis(localAxis *math32.Vector3) (float32, float32) {
  359. var max, min float32
  360. g.ReadVertices(func(vertex math32.Vector3) bool {
  361. val := vertex.Dot(localAxis)
  362. if val > max {
  363. max = val
  364. }
  365. if val < min {
  366. min = val
  367. }
  368. return false
  369. })
  370. return max, min
  371. }
  372. // TODO:
  373. // https://stackoverflow.com/questions/21640545/how-to-check-for-convexity-of-a-3d-mesh
  374. // func (g *Geometry) IsConvex() bool {
  375. //
  376. // {
  377. // ApplyMatrix multiplies each of the geometry position vertices
  378. // by the specified matrix and apply the correspondent normal
  379. // transform matrix to the geometry normal vectors.
  380. // The geometry's bounding box and sphere are recomputed if needed.
  381. func (g *Geometry) ApplyMatrix(m *math32.Matrix4) {
  382. // Apply matrix to all vertices
  383. g.OperateOnVertices(func(vertex *math32.Vector3) bool {
  384. vertex.ApplyMatrix4(m)
  385. return false
  386. })
  387. // Apply normal matrix to all normal vectors
  388. var normalMatrix math32.Matrix3
  389. normalMatrix.GetNormalMatrix(m)
  390. g.OperateOnVertexNormals(func(normal *math32.Vector3) bool {
  391. normal.ApplyMatrix3(&normalMatrix).Normalize()
  392. return false
  393. })
  394. }
  395. // Incref increments the reference count for this geometry
  396. // and returns a pointer to the geometry.
  397. // It should be used when this geometry is shared by another
  398. // Graphic object.
  399. func (g *Geometry) Incref() *Geometry {
  400. g.refcount++
  401. return g
  402. }
  403. // Dispose decrements this geometry reference count and
  404. // if possible releases OpenGL resources, C memory
  405. // and VBOs associated with this geometry.
  406. func (g *Geometry) Dispose() {
  407. // Only dispose if last
  408. if g.refcount > 1 {
  409. g.refcount--
  410. return
  411. }
  412. // Delete VAO and indices buffer
  413. if g.gs != nil {
  414. g.gs.DeleteVertexArrays(g.handleVAO)
  415. g.gs.DeleteBuffers(g.handleIndices)
  416. }
  417. // Delete VBOs
  418. for i := 0; i < len(g.vbos); i++ {
  419. g.vbos[i].Dispose()
  420. }
  421. g.Init()
  422. }
  423. // RenderSetup is called by the renderer before drawing the geometry.
  424. func (g *Geometry) RenderSetup(gs *gls.GLS) {
  425. // First time initialization
  426. if g.gs == nil {
  427. // Generate VAO
  428. g.handleVAO = gs.GenVertexArray()
  429. // Generate buffer for indices
  430. g.handleIndices = gs.GenBuffer()
  431. // Save pointer to gs indicating initialization was done
  432. g.gs = gs
  433. }
  434. // Update VBOs
  435. gs.BindVertexArray(g.handleVAO)
  436. for _, vbo := range g.vbos {
  437. vbo.Transfer(gs)
  438. }
  439. // Update Indices buffer if necessary
  440. if g.indices.Size() > 0 && g.updateIndices {
  441. gs.BindBuffer(gls.ELEMENT_ARRAY_BUFFER, g.handleIndices)
  442. gs.BufferData(gls.ELEMENT_ARRAY_BUFFER, g.indices.Bytes(), g.indices.ToUint32(), gls.STATIC_DRAW)
  443. g.updateIndices = false
  444. }
  445. }