| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499 |
- // Copyright 2016 The G3N Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style
- // license that can be found in the LICENSE file.
- package geometry
- import (
- "github.com/g3n/engine/gls"
- "github.com/g3n/engine/math32"
- )
- // Interface for all geometries
- type IGeometry interface {
- GetGeometry() *Geometry
- RenderSetup(gs *gls.GLS)
- Dispose()
- }
- // Geometry encapsulates a three-dimensional geometric shape.
- type Geometry struct {
- refcount int // Current number of references
- vbos []*gls.VBO // Array of VBOs
- groups []Group // Array geometry groups
- indices math32.ArrayU32 // Buffer with indices
- gs *gls.GLS // Pointer to gl context. Valid after first render setup
- handleVAO uint32 // Handle to OpenGL VAO
- handleIndices uint32 // Handle to OpenGL buffer for indices
- updateIndices bool // Flag to indicate that indices must be transferred
- // Geometric properties
- boundingBox math32.Box3 // Last calculated bounding box
- boundingSphere math32.Sphere // Last calculated bounding sphere
- area float32 // Last calculated area
- volume float32 // Last calculated volume
- rotInertia math32.Matrix3 // Last calculated rotational inertia matrix
- // Flags indicating whether geometric properties are valid
- boundingBoxValid bool // Indicates if last calculated bounding box is valid
- boundingSphereValid bool // Indicates if last calculated bounding sphere is valid
- areaValid bool // Indicates if last calculated area is valid
- volumeValid bool // Indicates if last calculated volume is valid
- rotInertiaValid bool // Indicates if last calculated rotational inertia matrix is valid
- }
- // Geometry group object
- type Group struct {
- Start int // Index of first element of the group
- Count int // Number of elements in the group
- Matindex int // Material index for this group
- Matid string // Material id used when loading external models
- }
- func NewGeometry() *Geometry {
- g := new(Geometry)
- g.Init()
- return g
- }
- // Init initializes the geometry
- func (g *Geometry) Init() {
- g.refcount = 1
- g.vbos = make([]*gls.VBO, 0)
- g.groups = make([]Group, 0)
- g.gs = nil
- g.handleVAO = 0
- g.handleIndices = 0
- g.updateIndices = true
- }
- // Incref increments the reference count for this geometry
- // and returns a pointer to the geometry.
- // It should be used when this geometry is shared by another
- // Graphic object.
- func (g *Geometry) Incref() *Geometry {
- g.refcount++
- return g
- }
- // Dispose decrements this geometry reference count and
- // if necessary releases OpenGL resources, C memory
- // and VBOs associated with this geometry.
- func (g *Geometry) Dispose() {
- if g.refcount > 1 {
- g.refcount--
- return
- }
- // Delete VAO and indices buffer
- if g.gs != nil {
- g.gs.DeleteVertexArrays(g.handleVAO)
- g.gs.DeleteBuffers(g.handleIndices)
- }
- // Delete this geometry VBO buffers
- for i := 0; i < len(g.vbos); i++ {
- g.vbos[i].Dispose()
- }
- g.Init()
- }
- func (g *Geometry) GetGeometry() *Geometry {
- return g
- }
- // AddGroup adds a geometry group (for multimaterial)
- func (g *Geometry) AddGroup(start, count, matIndex int) *Group {
- g.groups = append(g.groups, Group{start, count, matIndex, ""})
- return &g.groups[len(g.groups)-1]
- }
- // AddGroupList adds the specified list of groups to this geometry
- func (g *Geometry) AddGroupList(groups []Group) {
- for _, group := range groups {
- g.groups = append(g.groups, group)
- }
- }
- // GroupCount returns the number of geometry groups (for multimaterial)
- func (g *Geometry) GroupCount() int {
- return len(g.groups)
- }
- // GroupAt returns pointer to geometry group at the specified index
- func (g *Geometry) GroupAt(idx int) *Group {
- return &g.groups[idx]
- }
- // SetIndices sets the indices array for this geometry
- func (g *Geometry) SetIndices(indices math32.ArrayU32) {
- g.indices = indices
- g.updateIndices = true
- g.boundingBoxValid = false
- g.boundingSphereValid = false
- }
- // Indices returns this geometry indices array
- func (g *Geometry) Indices() math32.ArrayU32 {
- return g.indices
- }
- // AddVBO adds a Vertex Buffer Object for this geometry
- func (g *Geometry) AddVBO(vbo *gls.VBO) {
- // Check that the provided VBO doesn't have conflicting attributes with existing VBOs
- for _, existingVbo := range g.vbos {
- for _, attrib := range vbo.Attributes() {
- if existingVbo.Attrib(attrib.Name) != nil {
- panic("Geometry.AddVBO: geometry already has a VBO with attribute " + attrib.Name)
- }
- }
- }
- g.vbos = append(g.vbos, vbo)
- }
- // VBO returns a pointer to this geometry's VBO which contain the specified attribute.
- // Returns nil if the VBO is not found.
- func (g *Geometry) VBO(attrib string) *gls.VBO {
- for _, vbo := range g.vbos {
- if vbo.Attrib(attrib) != nil {
- return vbo
- }
- }
- return nil
- }
- // Returns the number of items in the first VBO
- // (The number of items should be same for all VBOs)
- // An item is a complete group of attributes in the VBO buffer
- func (g *Geometry) Items() int {
- if len(g.vbos) == 0 {
- return 0
- }
- vbo := g.vbos[0]
- if vbo.AttribCount() == 0 {
- return 0
- }
- return vbo.Buffer().Bytes() / vbo.StrideSize()
- }
- // BoundingBox computes the bounding box of the geometry if necessary
- // and returns is value
- func (g *Geometry) BoundingBox() math32.Box3 {
- // If valid, return its value
- if g.boundingBoxValid {
- return g.boundingBox
- }
- // Reset bounding box
- g.boundingBox.Min.Set(0, 0, 0)
- g.boundingBox.Max.Set(0, 0, 0)
- // Get buffer with position vertices
- vboPos := g.VBO("VertexPosition")
- if vboPos == nil {
- // Return zero-ed bounding box
- return g.boundingBox
- }
- stride := vboPos.Stride()
- offset := vboPos.AttribOffset("VertexPosition")
- positions := vboPos.Buffer()
- // Calculate bounding box
- var vertex math32.Vector3
- for i := offset; i < positions.Size(); i += stride {
- positions.GetVector3(i, &vertex)
- g.boundingBox.ExpandByPoint(&vertex)
- }
- g.boundingBoxValid = true
- return g.boundingBox
- }
- // BoundingSphere computes the bounding sphere of this geometry
- // if necessary and returns its value.
- func (g *Geometry) BoundingSphere() math32.Sphere {
- // If valid, return its value
- if g.boundingSphereValid {
- return g.boundingSphere
- }
- // Reset radius
- g.boundingSphere.Radius = float32(0)
- // Calculate bounding box
- box := g.BoundingBox()
- // Set the center of the bounding sphere to the center of the bounding box
- box.Center(&g.boundingSphere.Center)
- // Get buffer with position vertices
- vboPos := g.VBO("VertexPosition")
- if vboPos == nil {
- // Return zero-ed bounding sphere
- return g.boundingSphere
- }
- stride := vboPos.Stride()
- offset := vboPos.AttribOffset("VertexPosition")
- positions := vboPos.Buffer()
- // Find the radius of the bounding sphere
- center := g.boundingSphere.Center
- maxRadiusSq := float32(0.0)
- var vertex math32.Vector3
- for i := offset; i < positions.Size(); i += stride {
- positions.GetVector3(i, &vertex)
- maxRadiusSq = math32.Max(maxRadiusSq, center.DistanceToSquared(&vertex))
- }
- radius := math32.Sqrt(maxRadiusSq)
- if math32.IsNaN(radius) {
- panic("geometry.BoundingSphere: computed radius is NaN")
- }
- g.boundingSphere.Radius = float32(radius)
- g.boundingSphereValid = true
- return g.boundingSphere
- }
- // Area returns the surface area.
- // NOTE: This only works for triangle-based meshes.
- func (g *Geometry) Area() float32 {
- // If valid, return its value
- if g.areaValid {
- return g.area
- }
- // Reset area
- g.area = 0
- // Get buffer with position vertices
- vboPos := g.VBO("VertexPosition")
- if vboPos == nil {
- // Return zero-ed area
- return g.area
- }
- positions := vboPos.Buffer()
- // Calculate area
- var vA, vB, vC math32.Vector3
- // Geometry has indexed vertices
- if g.indices.Size() > 0 {
- for i := 0; i < g.indices.Size(); i += 3 {
- // Get face indices
- a := g.indices[i]
- b := g.indices[i+1]
- c := g.indices[i+2]
- // Get face position vectors
- positions.GetVector3(int(3*a), &vA)
- positions.GetVector3(int(3*b), &vB)
- positions.GetVector3(int(3*c), &vC)
- // Calculate triangle area
- vA.Sub(&vC)
- vB.Sub(&vC)
- vC.CrossVectors(&vA, &vB)
- g.area += vC.Length() / 2.0
- }
- // Geometry has NO indexed vertices
- } else {
- stride := vboPos.Stride()
- offset := vboPos.AttribOffset("VertexPosition")
- for i := offset; i < positions.Size(); i += 3*stride {
- // Get face indices
- a := i
- b := i + stride
- c := i + 2*stride
- // Set face position vectors
- positions.GetVector3(int(a), &vA)
- positions.GetVector3(int(b), &vB)
- positions.GetVector3(int(c), &vC)
- // Calculate triangle area
- vA.Sub(&vC)
- vB.Sub(&vC)
- vC.CrossVectors(&vA, &vB)
- g.area += vC.Length() / 2.0
- }
- }
- g.areaValid = true
- return g.area
- }
- // Volume returns the volume.
- // NOTE: This only works for closed triangle-based meshes.
- func (g *Geometry) Volume() float32 {
- // If valid, return its value
- if g.volumeValid {
- return g.volume
- }
- // Reset volume
- g.volume = 0
- // Get buffer with position vertices
- vboPos := g.VBO("VertexPosition")
- if vboPos == nil {
- // Return zero-ed area
- return g.area
- }
- positions := vboPos.Buffer()
- // Calculate volume
- var vA, vB, vC math32.Vector3
- // Geometry has indexed vertices
- if g.indices.Size() > 0 {
- for i := 0; i < g.indices.Size(); i += 3 {
- // Get face indices
- a := g.indices[i]
- b := g.indices[i+1]
- c := g.indices[i+2]
- // Get face position vectors
- positions.GetVector3(int(3*a), &vA)
- positions.GetVector3(int(3*b), &vB)
- positions.GetVector3(int(3*c), &vC)
- // Calculate tetrahedron volume
- vA.Sub(&vC)
- vB.Sub(&vC)
- g.volume += vC.Dot(vA.Cross(&vB)) / 6.0
- }
- // Geometry has NO indexed vertices
- } else {
- stride := vboPos.Stride()
- offset := vboPos.AttribOffset("VertexPosition")
- for i := offset; i < positions.Size(); i += 3*stride {
- // Get face indices
- a := i
- b := i + stride
- c := i + 2*stride
- // Set face position vectors
- positions.GetVector3(int(a), &vA)
- positions.GetVector3(int(b), &vB)
- positions.GetVector3(int(c), &vC)
- // Calculate tetrahedron volume
- vA.Sub(&vC)
- vB.Sub(&vC)
- g.volume += vC.Dot(vA.Cross(&vB)) / 6.0
- }
- }
- g.volumeValid = true
- return g.volume
- }
- // RotationalInertia returns the rotational inertia tensor, also known as the moment of inertia.
- // This assumes constant density of 1 (kg/m^2).
- // To adjust for a different constant density simply scale the returning matrix by the density.
- func (g *Geometry) RotationalInertia() math32.Matrix3 {
- // If valid, return its value
- if g.rotInertiaValid {
- return g.rotInertia
- }
- // Reset rotational inertia
- g.rotInertia.Zero()
- // For now approximate result based on bounding box
- b := math32.NewVec3()
- box := g.BoundingBox()
- box.Size(b)
- vol := g.Volume()
- multiplier := vol / 12.0
- x := (b.Y*b.Y + b.Z*b.Z) * multiplier
- y := (b.X*b.X + b.Z*b.Z) * multiplier
- z := (b.Y*b.Y + b.X*b.X) * multiplier
- g.rotInertia.Set(
- x, 0, 0,
- 0, y, 0,
- 0, 0, z,
- )
- return g.rotInertia
- }
- // ApplyMatrix multiplies each of the geometry position vertices
- // by the specified matrix and apply the correspondent normal
- // transform matrix to the geometry normal vectors.
- // The geometry's bounding box and sphere are recomputed if needed.
- func (g *Geometry) ApplyMatrix(m *math32.Matrix4) {
- // Get positions buffer
- vboPos := g.VBO("VertexPosition")
- if vboPos == nil {
- return
- }
- stride := vboPos.Stride()
- offset := vboPos.AttribOffset("VertexPosition")
- positions := vboPos.Buffer()
- // Apply matrix to all position vertices
- for i := offset; i < positions.Size(); i += stride {
- var vertex math32.Vector3
- positions.GetVector3(i, &vertex)
- vertex.ApplyMatrix4(m)
- positions.SetVector3(i, &vertex)
- }
- vboPos.Update()
- // Get normals buffer
- vboNormals := g.VBO("VertexNormal")
- if vboNormals == nil {
- return
- }
- stride = vboNormals.Stride()
- offset = vboNormals.AttribOffset("VertexNormal")
- normals := vboNormals.Buffer()
- // Apply normal matrix to all normal vectors
- var normalMatrix math32.Matrix3
- normalMatrix.GetNormalMatrix(m)
- for i := offset; i < normals.Size(); i += stride {
- var vertex math32.Vector3
- normals.GetVector3(i, &vertex)
- vertex.ApplyMatrix3(&normalMatrix).Normalize()
- normals.SetVector3(i, &vertex)
- }
- vboNormals.Update()
- }
- // RenderSetup is called by the renderer before drawing the geometry
- func (g *Geometry) RenderSetup(gs *gls.GLS) {
- // First time initialization
- if g.gs == nil {
- // Generates VAO and binds it
- g.handleVAO = gs.GenVertexArray()
- gs.BindVertexArray(g.handleVAO)
- // Generates VBO for indices
- g.handleIndices = gs.GenBuffer()
- // Saves pointer to gl indicating initialization was done.
- g.gs = gs
- }
- // Update VBOs
- gs.BindVertexArray(g.handleVAO)
- for _, vbo := range g.vbos {
- vbo.Transfer(gs)
- }
- // Updates Indices buffer if necessary
- if g.indices.Size() > 0 && g.updateIndices {
- gs.BindBuffer(gls.ELEMENT_ARRAY_BUFFER, g.handleIndices)
- gs.BufferData(gls.ELEMENT_ARRAY_BUFFER, g.indices.Bytes(), g.indices, gls.STATIC_DRAW)
- g.updateIndices = false
- }
- }
|