| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462 |
- // 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 collada
- import (
- "fmt"
- "github.com/g3n/engine/geometry"
- "github.com/g3n/engine/gls"
- "github.com/g3n/engine/math32"
- "reflect"
- "strings"
- )
- // GetGeometry returns a pointer to an instance of the geometry
- // with the specified id in the Collada document, its primitive type
- // and an error. If no previous instance of the geometry was found
- // the geometry is created
- func (d *Decoder) GetGeometry(id string) (geometry.IGeometry, uint32, error) {
- // If geometry already created, returns it
- ginst, ok := d.geometries[id]
- if ok {
- return ginst.geom, ginst.ptype, nil
- }
- // Creates geometry and saves it associated with its id
- geom, ptype, err := d.NewGeometry(id)
- if err != nil {
- return nil, 0, err
- }
- d.geometries[id] = geomInstance{geom, ptype}
- return geom, ptype, nil
- }
- // NewGeometry creates and returns a pointer to a new instance of the geometry
- // with the specified id in the Collada document, its primitive type and and error.
- func (d *Decoder) NewGeometry(id string) (geometry.IGeometry, uint32, error) {
- id = strings.TrimPrefix(id, "#")
- // Look for geometry with specified id in the dom
- var geo *Geometry
- for _, g := range d.dom.LibraryGeometries.Geometry {
- if g.Id == id {
- geo = g
- break
- }
- }
- if geo == nil {
- return nil, 0, fmt.Errorf("Geometry:%s not found", id)
- }
- // Geometry type
- switch gt := geo.GeometricElement.(type) {
- // Collada mesh category includes points, lines, linestrips, triangles,
- // triangle fans, triangle strips and polygons.
- case *Mesh:
- return newMesh(gt)
- // B-Spline
- // Bezier
- // NURBS
- // Patch
- default:
- return nil, 0, fmt.Errorf("GeometryElement:%T not supported", gt)
- }
- }
- func newMesh(m *Mesh) (*geometry.Geometry, uint32, error) {
- // If no primitive elements present, it is a mesh of points
- if len(m.PrimitiveElements) == 0 {
- return newMeshPoints(m)
- }
- // All the primitive elements must be of the same type
- var etype reflect.Type
- for i := 0; i < len(m.PrimitiveElements); i++ {
- el := m.PrimitiveElements[i]
- if i == 0 {
- etype = reflect.TypeOf(el)
- } else {
- if reflect.TypeOf(el) != etype {
- return nil, 0, fmt.Errorf("primitive elements of different types")
- }
- }
- }
- pei := m.PrimitiveElements[0]
- switch pet := pei.(type) {
- case *Polylist:
- return newMeshPolylist(m, m.PrimitiveElements)
- case *Triangles:
- return newMeshTriangles(m, pet)
- case *Lines:
- return newMeshLines(m, pet)
- case *LineStrips:
- return newMeshLineStrips(m, pet)
- case *Trifans:
- return newMeshTrifans(m, pet)
- case *Tristrips:
- return newMeshTristrips(m, pet)
- default:
- return nil, 0, fmt.Errorf("PrimitiveElement:%T not supported", pet)
- }
- }
- // Creates a geometry from a polylist
- // Only triangles are supported
- func newMeshPolylist(m *Mesh, pels []interface{}) (*geometry.Geometry, uint32, error) {
- // Get vertices positions
- if len(m.Vertices.Input) != 1 {
- return nil, 0, fmt.Errorf("Mesh.Vertices.Input length not supported")
- }
- vinp := m.Vertices.Input[0]
- if vinp.Semantic != "POSITION" {
- return nil, 0, fmt.Errorf("Mesh.Vertices.Input.Semantic:%s not supported", vinp.Semantic)
- }
- // Get vertices input source
- inps := getMeshSource(m, vinp.Source)
- if inps == nil {
- return nil, 0, fmt.Errorf("Source:%s not found", vinp.Source)
- }
- // Get vertices input float array
- // Ignore Accessor (??)
- posArray, ok := inps.ArrayElement.(*FloatArray)
- if !ok {
- return nil, 0, fmt.Errorf("Mesh.Vertices.Input.Source not FloatArray")
- }
- // Creates buffers
- positions := math32.NewArrayF32(0, 0)
- normals := math32.NewArrayF32(0, 0)
- uvs := math32.NewArrayF32(0, 0)
- indices := math32.NewArrayU32(0, 0)
- // Creates vertices attributes map for reusing indices
- mVindex := make(map[[8]float32]uint32)
- var index uint32
- geomGroups := make([]geometry.Group, 0)
- groupMatindex := 0
- // For each Polylist
- for _, pel := range pels {
- // Checks if element is Polylist
- pl, ok := pel.(*Polylist)
- if !ok {
- return nil, 0, fmt.Errorf("Element is not a Polylist")
- }
- // If Polylist has not inputs, ignore
- if pl.Input == nil || len(pl.Input) == 0 {
- continue
- }
- // Checks if all Vcount elements are triangles
- for _, v := range pl.Vcount {
- if v != 3 {
- return nil, 0, fmt.Errorf("Only triangles are supported in Polylist")
- }
- }
- // Get VERTEX input
- inpVertex := getInputSemantic(pl.Input, "VERTEX")
- if inpVertex == nil {
- return nil, 0, fmt.Errorf("VERTEX input not found")
- }
- // Get optional NORMAL input
- inpNormal := getInputSemantic(pl.Input, "NORMAL")
- var normArray *FloatArray
- if inpNormal != nil {
- // Get normals source
- source := getMeshSource(m, inpNormal.Source)
- if source == nil {
- return nil, 0, fmt.Errorf("NORMAL source:%s not found", inpNormal.Source)
- }
- // Get normals source float array
- normArray, ok = source.ArrayElement.(*FloatArray)
- if !ok {
- return nil, 0, fmt.Errorf("NORMAL source:%s not float array", inpNormal.Source)
- }
- }
- // Get optional TEXCOORD input
- inpTexcoord := getInputSemantic(pl.Input, "TEXCOORD")
- var texArray *FloatArray
- if inpTexcoord != nil {
- // Get texture coordinates source
- source := getMeshSource(m, inpTexcoord.Source)
- if source == nil {
- return nil, 0, fmt.Errorf("TEXCOORD source:%s not found", inpTexcoord.Source)
- }
- // Get texture coordinates source float array
- texArray, ok = source.ArrayElement.(*FloatArray)
- if !ok {
- return nil, 0, fmt.Errorf("TEXCOORD source:%s not float array", inpTexcoord.Source)
- }
- }
- // Initialize geometry group
- groupStart := indices.Size()
- // For each primitive index
- inputCount := len(pl.Input)
- for i := 0; i < len(pl.P); i += inputCount {
- // Vertex attributes: position(3) + normal(3) + uv(2)
- var vx [8]float32
- // Vertex position
- posIndex := pl.P[i+inpVertex.Offset] * 3
- // Get position vector and appends to its buffer
- vx[0] = posArray.Data[posIndex]
- vx[1] = posArray.Data[posIndex+1]
- vx[2] = posArray.Data[posIndex+2]
- // Optional vertex normal
- if inpNormal != nil {
- // Get normal index from P
- normIndex := pl.P[i+inpNormal.Offset] * 3
- // Get normal vector and appends to its buffer
- vx[3] = normArray.Data[normIndex]
- vx[4] = normArray.Data[normIndex+1]
- vx[5] = normArray.Data[normIndex+2]
- }
- // Optional vertex texture coordinate
- if inpTexcoord != nil {
- // Get normal index from P
- texIndex := pl.P[i+inpTexcoord.Offset] * 2
- // Get normal vector and appends to its buffer
- vx[6] = texArray.Data[texIndex]
- vx[7] = texArray.Data[texIndex+1]
- }
- // If this vertex and its attributes has already been appended,
- // reuse it, adding its index to the index buffer
- // to reuse its index
- idx, ok := mVindex[vx]
- if ok {
- indices.Append(idx)
- continue
- }
- // Appends new vertex position and attributes to its buffers
- positions.Append(vx[0], vx[1], vx[2])
- if inpNormal != nil {
- normals.Append(vx[3], vx[4], vx[5])
- }
- if inpTexcoord != nil {
- uvs.Append(vx[6], vx[7])
- }
- indices.Append(index)
- // Save the index to this vertex position and attributes for
- // future reuse
- mVindex[vx] = index
- index++
- }
- // Adds this geometry group to the list
- geomGroups = append(geomGroups, geometry.Group{
- Start: groupStart,
- Count: indices.Size() - groupStart,
- Matindex: groupMatindex,
- Matid: pl.Material,
- })
- groupMatindex++
- }
- // Debug dump
- //for i := 0; i < positions.Size()/3; i++ {
- // vidx := i*3
- // msg := fmt.Sprintf("i:%2d position:%v %v %v",
- // i, positions.Get(vidx), positions.Get(vidx+1), positions.Get(vidx+2))
- // if normals.Size() > 0 {
- // msg += fmt.Sprintf("\tnormal:%v %v %v",
- // normals.Get(vidx), normals.Get(vidx+1), normals.Get(vidx+2))
- // }
- // if uvs.Size() > 0 {
- // msg += fmt.Sprintf("\tuv:%v %v", uvs.Get(i*2), uvs.Get(i*2+1))
- // }
- // log.Debug("%s", msg)
- //}
- //log.Debug("indices(%d):%v", indices.Size(), indices)
- //log.Debug("groups:%v", geomGroups)
- // Creates geometry
- geom := geometry.NewGeometry()
- // Creates VBO with vertex positions
- geom.AddVBO(gls.NewVBO(positions).AddAttrib(geometry.VertexPosition, 3))
- // Creates VBO with vertex normals
- if normals.Size() > 0 {
- geom.AddVBO(gls.NewVBO(normals).AddAttrib(geometry.VertexNormal, 3))
- }
- // Creates VBO with uv coordinates
- if uvs.Size() > 0 {
- geom.AddVBO(gls.NewVBO(uvs).AddAttrib(geometry.VertexTexcoord, 2))
- }
- // Sets the geometry indices buffer
- geom.SetIndices(indices)
- // Add material groups to the geometry
- geom.AddGroupList(geomGroups)
- return geom, gls.TRIANGLES, nil
- }
- func newMeshTriangles(m *Mesh, tr *Triangles) (*geometry.Geometry, uint32, error) {
- return nil, 0, fmt.Errorf("not implemented yet")
- }
- func newMeshLines(m *Mesh, ln *Lines) (*geometry.Geometry, uint32, error) {
- if ln.Input == nil || len(ln.Input) == 0 {
- return nil, 0, fmt.Errorf("No inputs in lines")
- }
- // Get vertices positions
- if len(m.Vertices.Input) != 1 {
- return nil, 0, fmt.Errorf("Mesh.Vertices.Input length not supported")
- }
- vinp := m.Vertices.Input[0]
- if vinp.Semantic != "POSITION" {
- return nil, 0, fmt.Errorf("Mesh.Vertices.Input.Semantic:%s not supported", vinp.Semantic)
- }
- // Get vertices input source
- inps := getMeshSource(m, vinp.Source)
- if inps == nil {
- return nil, 0, fmt.Errorf("Source:%s not found", vinp.Source)
- }
- // Get vertices input float array
- // Ignore Accessor (??)
- posArray, ok := inps.ArrayElement.(*FloatArray)
- if !ok {
- return nil, 0, fmt.Errorf("Mesh.Vertices.Input.Source not FloatArray")
- }
- // Get VERTEX input
- inpVertex := getInputSemantic(ln.Input, "VERTEX")
- if inpVertex == nil {
- return nil, 0, fmt.Errorf("VERTEX input not found")
- }
- // Creates buffers
- positions := math32.NewArrayF32(0, 0)
- indices := math32.NewArrayU32(0, 0)
- mVindex := make(map[[3]float32]uint32)
- inputCount := len(ln.Input)
- var index uint32
- for i := 0; i < len(ln.P); i += inputCount {
- // Vertex position
- var vx [3]float32
- // Get position index from P
- posIndex := ln.P[i+inpVertex.Offset] * 3
- // Get position vector and appends to its buffer
- vx[0] = posArray.Data[posIndex]
- vx[1] = posArray.Data[posIndex+1]
- vx[2] = posArray.Data[posIndex+2]
- // If this vertex and its attributes has already been appended,
- // reuse it, adding its index to the index buffer
- // to reuse its index
- idx, ok := mVindex[vx]
- if ok {
- indices.Append(idx)
- continue
- }
- // Appends new vertex position and attributes to its buffers
- positions.Append(vx[0], vx[1], vx[2])
- indices.Append(index)
- // Save the index to this vertex position and attributes for
- // future reuse
- //mVindex[vx] = index
- index++
- }
- // Creates geometry
- geom := geometry.NewGeometry()
- // Creates VBO with vertex positions
- geom.AddVBO(gls.NewVBO(positions).AddAttrib(geometry.VertexPosition, 3))
- // Sets the geometry indices buffer
- geom.SetIndices(indices)
- return geom, gls.LINES, nil
- }
- func newMeshLineStrips(m *Mesh, ls *LineStrips) (*geometry.Geometry, uint32, error) {
- return nil, 0, fmt.Errorf("not implemented yet")
- }
- func newMeshTrifans(m *Mesh, ls *Trifans) (*geometry.Geometry, uint32, error) {
- return nil, 0, fmt.Errorf("not implemented yet")
- }
- func newMeshTristrips(m *Mesh, ls *Tristrips) (*geometry.Geometry, uint32, error) {
- return nil, 0, fmt.Errorf("not implemented yet")
- }
- // Creates and returns pointer to a new geometry for POINTS
- func newMeshPoints(m *Mesh) (*geometry.Geometry, uint32, error) {
- // Get vertices positions
- if len(m.Vertices.Input) != 1 {
- return nil, 0, fmt.Errorf("Mesh.Vertices.Input length not supported")
- }
- vinp := m.Vertices.Input[0]
- if vinp.Semantic != "POSITION" {
- return nil, 0, fmt.Errorf("Mesh.Vertices.Input.Semantic:%s not supported", vinp.Semantic)
- }
- // Get vertices input source
- inps := getMeshSource(m, vinp.Source)
- if inps == nil {
- return nil, 0, fmt.Errorf("Source:%s not found", vinp.Source)
- }
- // Get vertices input float array
- // Ignore Accessor (??)
- posArray, ok := inps.ArrayElement.(*FloatArray)
- if !ok {
- return nil, 0, fmt.Errorf("Mesh.Vertices.Input.Source not FloatArray")
- }
- // Creates buffer and copy data
- positions := math32.NewArrayF32(posArray.Count, posArray.Count)
- //positions.CopyFrom(posArray.Data)
- copy(positions, posArray.Data)
- // Creates geometry and add VBO with vertex positions
- geom := geometry.NewGeometry()
- geom.AddVBO(gls.NewVBO(positions).AddAttrib(geometry.VertexPosition, 3))
- return geom, gls.POINTS, nil
- }
- func getMeshSource(m *Mesh, uri string) *Source {
- id := strings.TrimPrefix(uri, "#")
- for _, s := range m.Source {
- if s.Id == id {
- return s
- }
- }
- return nil
- }
- func getInputSemantic(inps []InputShared, semantic string) *InputShared {
- for i := 0; i < len(inps); i++ {
- if inps[i].Semantic == semantic {
- return &inps[i]
- }
- }
- return nil
- }
|