geometry.go 15 KB

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