-
-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #4 from qmuntal/gltf
gltf/draco: implement mesh decoding
- Loading branch information
Showing
6 changed files
with
404 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,136 @@ | ||
package draco | ||
|
||
import ( | ||
"encoding/json" | ||
"fmt" | ||
"reflect" | ||
"unsafe" | ||
|
||
"github.com/qmuntal/draco-go/draco" | ||
"github.com/qmuntal/gltf" | ||
"github.com/qmuntal/gltf/binary" | ||
"github.com/qmuntal/gltf/modeler" | ||
) | ||
|
||
const ( | ||
// ExtensionName defines the KHR_draco_mesh_compression unique key. | ||
ExtensionName = "KHR_draco_mesh_compression" | ||
) | ||
|
||
func init() { | ||
gltf.RegisterExtension(ExtensionName, Unmarshal) | ||
} | ||
|
||
// Unmarshal decodes the json data into the correct type. | ||
func Unmarshal(data []byte) (interface{}, error) { | ||
drc := new(PrimitiveExt) | ||
err := json.Unmarshal(data, drc) | ||
return drc, err | ||
} | ||
|
||
// PrimitiveExt extends the gltf.Primtive info to handle draco compressed meshes. | ||
type PrimitiveExt struct { | ||
Extensions gltf.Extensions `json:"extensions,omitempty"` | ||
Extras interface{} `json:"extras,omitempty"` | ||
BufferView uint32 `json:"bufferView"` | ||
Attributes gltf.Attribute `json:"attributes"` | ||
} | ||
|
||
// GetPrimitiveExt retrieve a PrimitiveExt from p. | ||
// If p does not contain the draco extensions it returns nil. | ||
func GetPrimitiveExt(p *gltf.Primitive) *PrimitiveExt { | ||
var pe *PrimitiveExt | ||
if ext, ok := p.Extensions[ExtensionName]; ok { | ||
if pe, ok = ext.(*PrimitiveExt); !ok { | ||
return nil | ||
} | ||
} else { | ||
return nil | ||
} | ||
return pe | ||
} | ||
|
||
// Mesh contains the necessary information to process a draco-encoded | ||
// in a gltf context. | ||
type Mesh struct { | ||
doc *gltf.Document | ||
m *draco.Mesh | ||
} | ||
|
||
// UnmarshalMesh unmarshal the draco-encoded mesh from a gltf.BufferView | ||
func UnmarshalMesh(doc *gltf.Document, bv *gltf.BufferView) (*Mesh, error) { | ||
data, err := modeler.ReadBufferView(doc, bv) | ||
if err != nil { | ||
return nil, err | ||
} | ||
if tp := draco.GetEncodedGeometryType(data); tp != draco.EGT_TRIANGULAR_MESH { | ||
return nil, fmt.Errorf("draco-go: unsupported geometry type %v", tp) | ||
} | ||
m := draco.NewMesh() | ||
d := draco.NewDecoder() | ||
if err := d.DecodeMesh(m, data); err != nil { | ||
return nil, err | ||
} | ||
return &Mesh{ | ||
doc: doc, | ||
m: m, | ||
}, nil | ||
} | ||
|
||
// ReadIndices reads the faces of the Mesh. | ||
// buffer can be nil. | ||
func (m Mesh) ReadIndices(buffer []uint32) []uint32 { | ||
return m.m.Faces(buffer) | ||
} | ||
|
||
// ReadAttr reads the named attribute of a gltf.Primitive. | ||
// If the attribute is defined in the primitive but not in the mesh | ||
// it fallbacks to modeler.ReadAccessor. | ||
// buffer can be nil. | ||
func (m Mesh) ReadAttr(p *gltf.Primitive, name string, buffer interface{}) (interface{}, error) { | ||
var ( | ||
gltfIndex, dracoID uint32 | ||
ok bool | ||
) | ||
if gltfIndex, ok = p.Attributes[name]; !ok { | ||
return nil, nil | ||
} | ||
pe := GetPrimitiveExt(p) | ||
ok = false | ||
if pe != nil { | ||
dracoID, ok = pe.Attributes[name] | ||
} | ||
acr := m.doc.Accessors[gltfIndex] | ||
if !ok { | ||
return modeler.ReadAccessor(m.doc, acr, buffer) | ||
} | ||
attr := m.m.AttrByUniqueID(dracoID) | ||
if attr == nil { | ||
return nil, fmt.Errorf("draco: mesh does not contain attribute %v", dracoID) | ||
} | ||
buffer = binary.MakeSliceBuffer(acr.ComponentType, acr.Type, acr.Count, buffer) | ||
sh := new(reflect.SliceHeader) | ||
sh.Data = reflect.ValueOf(buffer).Pointer() | ||
sh.Len = int(acr.Type.Components() * acr.Count) | ||
sh.Cap = sh.Len | ||
var data interface{} | ||
switch acr.ComponentType { | ||
case gltf.ComponentByte: | ||
data = *(*[]int8)(unsafe.Pointer(sh)) | ||
case gltf.ComponentUbyte: | ||
data = *(*[]uint8)(unsafe.Pointer(sh)) | ||
case gltf.ComponentShort: | ||
data = *(*[]int16)(unsafe.Pointer(sh)) | ||
case gltf.ComponentUshort: | ||
data = *(*[]uint16)(unsafe.Pointer(sh)) | ||
case gltf.ComponentUint: | ||
data = *(*[]uint32)(unsafe.Pointer(sh)) | ||
case gltf.ComponentFloat: | ||
data = *(*[]float32)(unsafe.Pointer(sh)) | ||
default: | ||
panic("draco-go: unsupported data type") | ||
} | ||
|
||
_, _ = m.m.AttrData(attr, data) | ||
return buffer, nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,102 @@ | ||
package draco | ||
|
||
import ( | ||
"reflect" | ||
"testing" | ||
|
||
"github.com/qmuntal/gltf" | ||
) | ||
|
||
func TestUnmarshal(t *testing.T) { | ||
type args struct { | ||
data []byte | ||
} | ||
tests := []struct { | ||
name string | ||
args args | ||
want interface{} | ||
wantErr bool | ||
}{ | ||
{"base", args{[]byte(`{ | ||
"bufferView" : 5, | ||
"attributes" : { | ||
"POSITION" : 0, | ||
"NORMAL" : 1, | ||
"TEXCOORD_0" : 2, | ||
"WEIGHTS_0" : 3, | ||
"JOINTS_0" : 4 | ||
} | ||
}`)}, &PrimitiveExt{BufferView: 5, Attributes: gltf.Attribute{ | ||
"JOINTS_0": 4, | ||
"NORMAL": 1, | ||
"POSITION": 0, | ||
"TEXCOORD_0": 2, | ||
"WEIGHTS_0": 3, | ||
}}, false}, | ||
} | ||
for _, tt := range tests { | ||
t.Run(tt.name, func(t *testing.T) { | ||
got, err := Unmarshal(tt.args.data) | ||
if (err != nil) != tt.wantErr { | ||
t.Errorf("Unmarshal() error = %v, wantErr %v", err, tt.wantErr) | ||
return | ||
} | ||
if !reflect.DeepEqual(got, tt.want) { | ||
t.Errorf("Unmarshal() = %v, want %v", got, tt.want) | ||
} | ||
}) | ||
} | ||
} | ||
|
||
func TestUnmarshalMesh(t *testing.T) { | ||
doc, err := gltf.Open("testdata/box/Box.gltf") | ||
if err != nil { | ||
t.Fatal(err) | ||
} | ||
pd, err := UnmarshalMesh(doc, doc.BufferViews[0]) | ||
if err != nil { | ||
t.Fatal(err) | ||
} | ||
p := doc.Meshes[0].Primitives[0] | ||
indWant := []uint32{2, 5, 6, 3, 11, 8, 8, 11, 12, 14, 9, 17} | ||
if got := pd.ReadIndices(nil); !reflect.DeepEqual(indWant, got) { | ||
t.Errorf("ReadIndices want %v, got %v", indWant, got) | ||
} | ||
_, err = pd.ReadAttr(p, "POSITION", nil) | ||
if err != nil { | ||
t.Error(err) | ||
} | ||
_, err = pd.ReadAttr(p, "NORMAL", [][3]float32{{1, 2, 3}}) | ||
if err != nil { | ||
t.Error(err) | ||
} | ||
_, err = pd.ReadAttr(p, "OTHER", nil) | ||
if err != nil { | ||
t.Error(err) | ||
} | ||
} | ||
|
||
func TestGetPrimitiveExt(t *testing.T) { | ||
type args struct { | ||
p *gltf.Primitive | ||
} | ||
tests := []struct { | ||
name string | ||
args args | ||
want *PrimitiveExt | ||
}{ | ||
{"no extension", args{&gltf.Primitive{}}, nil}, | ||
{"other extension", args{&gltf.Primitive{Extensions: gltf.Extensions{"other": nil}}}, nil}, | ||
{"draco other extension", args{&gltf.Primitive{Extensions: gltf.Extensions{ExtensionName: nil}}}, nil}, | ||
{"draco extension", args{&gltf.Primitive{Extensions: gltf.Extensions{ExtensionName: &PrimitiveExt{ | ||
BufferView: 1, | ||
}}}}, &PrimitiveExt{BufferView: 1}}, | ||
} | ||
for _, tt := range tests { | ||
t.Run(tt.name, func(t *testing.T) { | ||
if got := GetPrimitiveExt(tt.args.p); !reflect.DeepEqual(got, tt.want) { | ||
t.Errorf("GetPrimitiveExt() = %v, want %v", got, tt.want) | ||
} | ||
}) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
module github.com/qmuntal/draco-go/gltf/draco | ||
|
||
go 1.16 | ||
|
||
require ( | ||
github.com/qmuntal/draco-go v0.4.0 | ||
github.com/qmuntal/gltf v0.19.0 | ||
) | ||
|
||
replace github.com/qmuntal/draco-go => ../.. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
github.com/go-test/deep v1.0.1 h1:UQhStjbkDClarlmv0am7OXXO4/GaPdCGiUiMTvi28sg= | ||
github.com/go-test/deep v1.0.1/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA= | ||
github.com/qmuntal/gltf v0.19.0 h1:FFPoHZBNIHzlPPEUudQvQgV32VMohGE2pAqIBzdu0RQ= | ||
github.com/qmuntal/gltf v0.19.0/go.mod h1:ENqYfECmeaqs2BWXWe6OKtMC8ucZII6s9OHr6F5oZ94= |
Binary file not shown.
Oops, something went wrong.