geometry.go 15 KB

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