Ver código fonte

start of gltf loader dev...

leonsal 8 anos atrás
pai
commit
8df97e7445

+ 372 - 0
loader/gltf/gltf.go

@@ -0,0 +1,372 @@
+// 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 gltf
+
+// GLTF is the root object for a glTF asset
+type GLTF struct {
+	ExtensionsUsed     []string
+	ExtensionsRequired []string
+	Accessors          []Accessor
+	Animations         []Animation
+	Asset              Asset
+	Buffers            []Buffer
+	BufferViews        []BufferView
+	Cameras            []Camera
+	Images             []Image
+	Materials          []Material
+	Meshes             []Mesh
+	Nodes              []Node
+	Samplers           []Sampler
+	Scene              *int
+	Scenes             []Scene
+	Skins              []Skin
+	Textures           []Texture
+	Extensions         map[string]interface{}
+	Extras             interface{}
+	path               string // file path for resources
+	data               []byte // binary file Chunk 1 data
+}
+
+// Accessor describes a view into a BufferView
+type Accessor struct {
+	BufferView    *int                   // The index of the buffer view
+	ByteOffset    *int                   // The offset relative to the start of the BufferView in bytes
+	ComponentType int                    // The datatype of components in the attribute
+	Normalized    bool                   // Specifies whether integer data values should be normalized
+	Count         int                    // The number of attributes referenced by this accessor
+	Type          string                 // Specifies if the attribute is a scalar, vector or matrix
+	Max           []float32              // Maximum value of each component in this attribute
+	Min           []float32              // Minimum value of each component in this attribute
+	Sparse        *Sparse                // Sparse storage attribute that deviates from their initialization value
+	Name          string                 // The user-defined name of this object
+	Extensions    map[string]interface{} // Dictionary object with extension specific objects
+	Extras        interface{}            // Application-specific data
+}
+
+// A Keyframe animation
+type Animation struct {
+	Channels   []Channel              // An array of Channels
+	Samplers   []Sampler              // An array of samplers that combines input and output accessors with an interpolation algorithm to define a keyframe graph
+	Name       string                 // The user-defined name of this object
+	Extensions map[string]interface{} // Dictionary object with extension specific objects
+	Extras     interface{}            // Application-specific data
+}
+
+// Combines input and output accessors with an interpolation algorithm to define a keyframe graph
+type AnimationSampler struct {
+	Input         int                    // The index of the accessor containing keyframe input values
+	Interpolation string                 // Interpolation algorithm
+	Output        int                    // The index of an accessor containing keyframe output values
+	Extensions    map[string]interface{} // Dictionary object with extension specific objects
+	Extras        interface{}            // Application-specific data
+}
+
+// Metadata about the glTF asset.
+type Asset struct {
+	Copyright  string                 // A copyright message suitable for display to credit the content creator
+	Generator  string                 // Tool that generated this glTF model. Useful for debugging
+	Version    string                 // The glTF version that this asset targets
+	MinVersion string                 // The minimum glTF version that this asset targets
+	Extensions map[string]interface{} // Dictionary object with extension specific objects
+	Extras     interface{}            // Application-specific data
+}
+
+// A Buffer points to binary geometry, animation or skins
+type Buffer struct {
+	Uri        string                 // The URI of the buffer
+	ByteLength int                    // The length of the buffer in bytes
+	Name       string                 // The user-defined name of this object
+	Extensions map[string]interface{} // Dictionary object with extension specific objects
+	Extras     interface{}            // Application-specific data
+	data       []byte                 // cached buffer data
+}
+
+// A view into a buffer generally representing a subset of the buffer.
+type BufferView struct {
+	Buffer     int                    // The index of the buffer
+	ByteOffset *int                   // The offset into the buffer in bytes
+	ByteLength int                    // The length of the buffer view in bytes
+	ByteStride *int                   // The stride in bytes
+	Target     *int                   // The target that the GPU buffer should be bound to
+	Name       string                 // The user-defined name of this object
+	Extensions map[string]interface{} // Dictionary object with extension specific objects
+	Extras     interface{}            // Application-specific data
+}
+
+// A camera's projection.
+type Camera struct {
+	Orthographic *Orthographic          // An orthographic camera containing properties to create an orthographic projection matrix
+	Perspective  *Perspective           // A perspective camera containing properties to create a perspective projection matrix
+	Type         string                 // Specifies if the camera uses a perspective or orthographic projection
+	Name         string                 // The user-defined name of this object
+	Extensions   map[string]interface{} // Dictionary object with extension specific objects
+	Extras       interface{}            // Application-specific data
+}
+
+// Targets an animation's sampler at a node's property
+type Channel struct {
+	Sampler    int                    // The index of a sampler in this animation used to compute the value of the target
+	Target     Target                 // The index of the node and TRS property to target
+	Extensions map[string]interface{} // Dictionary object with extension specific objects
+	Extras     interface{}            // Application-specific data
+}
+
+// Image data used to create a texture
+type Image struct {
+	Uri        string                 // The URI of the image
+	MimeType   string                 // The image's MIME type
+	BufferView *int                   // The index of the BufferView the contains the image
+	Name       string                 // The user-defined name of this object
+	Extensions map[string]interface{} // Dictionary object with extension specific objects
+	Extras     interface{}            // Application-specific data
+}
+
+// Indices of those attributes that deviate from their initialization value.
+type Indices struct {
+	BufferView    int                    // The index of the BufferView with sparse indices
+	ByteOffset    int                    // The offset relative to the start of the BufferView in bytes
+	ComponentType int                    // The indices data type
+	Extensions    map[string]interface{} // Dictionary object with extension specific objects
+	Extras        interface{}            // Application-specific data
+}
+
+// Material describes the material appearance of a primitive
+type Material struct {
+	Name                 string                 // The user-defined name of this object
+	Extensions           map[string]interface{} // Dictionary object with extension specific objects
+	Extras               interface{}            // Application-specific data
+	PbrMetallicRoughness *PbrMetallicRoughness
+	NormalTexture        *NormalTextureInfo    // The normal map texture
+	OcclusionTexture     *OcclusionTextureInfo // The occlusion map texture
+	EmissiveTexture      *TextureInfo          // The emissive map texture
+	EmissiveFactor       [3]float32            // The emissive color of the material
+	AlphaMode            string                // The alpha rendering mode of the material
+	AlphaCutoff          float32               // The alpha cutoff value of the material
+	DoubleSided          bool                  // Specifies whether the material is double sided
+}
+
+// Mesh is a set of primitives to be rendered.
+type Mesh struct {
+	Primitives []Primitive            // Array of primitives
+	Weights    []float32              // Array of weights to be applied to the Morph Targets
+	Name       string                 // The user-define name of this object
+	Extensions map[string]interface{} // Dictionary object with extension specific objects
+	Extras     interface{}            // Application-specific data
+}
+
+// A Node in the hierarchy
+type Node struct {
+	Camera      *int                   // Index of the camera referenced by this node
+	Children    []int                  // The indices of this node's children
+	Skin        *int                   // The index of the skin referenced by this node
+	Matrix      *[16]float32           // Floating point 4x4 transformation matrix in column-major order
+	Mesh        *int                   // The index of the mesh in this node
+	Rotation    *[4]float32            // The node's unit quaternion rotation in the order x,y,z,w
+	Scale       *[3]float32            // The node's non-uniform scale
+	Translation *[3]float32            // The node's translation
+	Weights     []float32              // The weight's of the instantiated Morph Target
+	Name        string                 // User-defined name of this object
+	Extensions  map[string]interface{} // Dictionary object with extension specific objects
+	Extras      interface{}            // Application-specific data
+}
+
+// Reference to a texture
+type NormalTextureInfo struct {
+	Index      int                    // The index of the texture
+	TexCoord   int                    // The set index of texture's TEXCOORD attribute used for texture coordinate mapping
+	Scale      float32                // The scalar multiplier applied to each normal vector of the normal texture
+	Extensions map[string]interface{} // Dictionary object with extension specific objects
+	Extras     interface{}            // Application-specific data
+}
+
+// Reference to a texture
+type OcclusionTextureInfo struct {
+	Index      int                    // The index of the texture
+	TexCoord   int                    // The set index of texture's TEXCOORD attribute used for texture coordinate mapping
+	Strength   float32                // A scalar multiplier controlling the amount of occlusion applied
+	Extensions map[string]interface{} // Dictionary object with extension specific objects
+	Extras     interface{}            // Application-specific data
+}
+
+// An orthographic camera containing properties to create an orthographic projection matrix
+type Orthographic struct {
+	Xmag       float32                // The floating-point horizontal magnification of the view
+	Ymag       float32                // The floating-point vertical magnification of the view
+	Zfar       float32                // The floating-point distance to the far clipping plane. zfar must be greater than znear
+	Znear      float32                // The floating-point distance to the near clipping plane
+	Extensions map[string]interface{} // Dictionary object with extension specific objects
+	Extras     interface{}            // Application-specific data
+}
+
+// A set of parameter values that are used to define the metallic-roughness material model
+// from Physically-Based Rendering (PBR) methodology.
+type PbrMetallicRoughness struct {
+	BaseColorFactor          [4]float32             // The material's base color factor
+	BaseColorTexture         *TextureInfo           // The base color texture
+	MetallicFactor           float32                // The metalness of the material
+	RoughnessFactor          float32                // The roughness of the material
+	MetallicRoughnessTexture *TextureInfo           // The metallic-roughness texture
+	Extensions               map[string]interface{} // Dictionary object with extension specific objects
+	Extras                   interface{}            // Application-specific data
+}
+
+// A perspective camera containing properties to create a perspective projection matrix
+type Perspective struct {
+	AspectRatio *float32               // The floating-point aspect ratio of the field of view
+	Yfov        float32                // The floating-point vertical field of view in radians
+	Zfar        *float32               // The floating-point distance to the far clipping plane
+	Znear       float32                // The floating-point distance to the near clipping plane.
+	Extensions  map[string]interface{} // Dictionary object with extension specific objects
+	Extras      interface{}            // Application-specific data
+}
+
+// Geometry to be rendered with the given material
+type Primitive struct {
+	Attributes map[string]int         // A dictionary object, where each key corresponds to mesh attribute semantic and each value is the index of the accessor containing attribute's data
+	Indices    *int                   // The index of the accessor that contains the indices
+	Material   *int                   // The index of the material to apply to this primitive when rendering
+	Mode       *int                   // The type of primitive to render
+	Targets    []map[string]int       // An array of Morph Targets
+	Extensions map[string]interface{} // Dictionary object with extension specific objects
+	Extras     interface{}            // Application-specific data
+}
+
+// Texture sampler properties for filtering and wrapping modes
+type Sampler struct {
+	MagFilter  *int                   // Magnification filter
+	MinFilter  *int                   // Minification filter
+	WrapS      *int                   // s coordinate wrapping mode
+	WrapT      *int                   // t coordinate wrapping mode
+	Name       string                 // The user-define name for this object
+	Extensions map[string]interface{} // Dictionary object with extension specific objects
+	Extras     interface{}            // Application-specific data
+}
+
+// The root nodes of a scene
+type Scene struct {
+	Nodes      []int                  // The indices of each root node
+	Name       string                 // The user-define name for this object
+	Extensions map[string]interface{} // Dictionary object with extension specific objects
+	Extras     interface{}            // Application-specific data
+}
+
+// Joints and matrices defining a skin.
+type Skin struct {
+	InverseBindMatrices int                    // The index of the accessor containing the 4x4 inverse-bind matrices
+	Skeleton            int                    // The index of the node used as a skeleton root
+	Joints              []int                  // Indices of skeleton nodes, used as joints in this skin
+	Name                string                 // The user-define name for this object
+	Extensions          map[string]interface{} // Dictionary object with extension specific objects
+	Extras              interface{}            // Application-specific data
+}
+
+// Sparse storage of attributes that deviate from their initialization value.
+type Sparse struct {
+	Count int // Number of entries stored in the sparse array
+	//Indices
+	//Values
+	Extensions map[string]interface{} // Dictionary object with extension specific objects
+	Extras     interface{}            // Application-specific data
+}
+
+// The index of the node and TRS property than an animation channel targets
+type Target struct {
+	Node       int                    // The index of the node to target
+	Path       string                 // The name of the node's TRS property to modify
+	Extensions map[string]interface{} // Dictionary object with extension specific objects
+	Extras     interface{}            // Application-specific data
+}
+
+// A texture and its sampler.
+type Texture struct {
+	Sampler    int                    // The index of the sampler used by this texture
+	Source     int                    // The index of the image used by this texture
+	Name       string                 // The user-define name for this object
+	Extensions map[string]interface{} // Dictionary object with extension specific objects
+	Extras     interface{}            // Application-specific data
+}
+
+// Reference to a texture.
+type TextureInfo struct {
+	Index      int                    // The index of the texture
+	TexCoord   int                    // The set index of texture's TEXCOORD attribute used for texture coordinate mapping
+	Extensions map[string]interface{} // Dictionary object with extension specific objects
+	Extras     interface{}            // Application-specific data
+}
+
+// Array of size accessor.sparse.count times number of components storing
+// the displaced accessor attributes pointed by accessor.sparse.indices.
+type Values struct {
+	BufferView int                    // The index of the bufferView with sparse values
+	ByteOffset int                    // he offset relative to the start of the bufferView in bytes
+	Extensions map[string]interface{} // Dictionary object with extension specific objects
+	Extras     interface{}            // Application-specific data
+}
+
+const (
+	POINTS                 = 0
+	LINES                  = 1
+	LINE_LOOP              = 2
+	LINE_STRIP             = 3
+	TRIANGLES              = 4
+	TRIANGLE_STRIP         = 5
+	TRIANGLE_FAN           = 6
+	ARRAY_BUFFER           = 34962
+	ELEMENT_ARRAY_BUFFER   = 34963
+	NEAREST                = 9728
+	LINEAR                 = 9729
+	NEAREST_MIPMAP_NEAREST = 9984
+	LINEAR_MIPMAP_NEAREST  = 9985
+	NEAREST_MIPMAP_LINEAR  = 9986
+	LINEAR_MIPMAP_LINEAR   = 9987
+	CLAMP_TO_EDGE          = 33071
+	MIRRORED_REPEAT        = 33648
+	REPEAT                 = 10497
+	UNSIGNED_BYTE          = 5121
+	UNSIGNED_SHORT         = 5123
+	UNSIGNED_INT           = 5125
+	FLOAT                  = 5126
+)
+
+const (
+	POSITION   = "POSITION"
+	NORMAL     = "NORMAL"
+	TANGENT    = "TANGENT"
+	TEXCOORD_0 = "TEXCOORD_0"
+	TEXCOORD_1 = "TEXCOORD_1"
+	COLOR_0    = "COLOR_0"
+	JOINTS_0   = "JOINTS_0"
+	WEIGHTS_0  = "WEIGHTS_0"
+	SCALAR     = "SCALAR"
+	VEC2       = "VEC2"
+	VEC3       = "VEC3"
+	VEC4       = "VEC4"
+	MAT2       = "MAT2"
+	MAT3       = "MAT3"
+	MAT4       = "MAT4"
+)
+
+type GLB struct {
+	Header GLBHeader
+	JSON   GLBChunk
+	Data   GLBChunk
+}
+
+type GLBHeader struct {
+	Magic   uint32
+	Version uint32
+	Length  uint32
+}
+
+type GLBChunk struct {
+	Length uint32
+	Type   uint32
+}
+
+const (
+	GLBMagic = 0x46546C67
+	GLBJson  = 0x4E4F534A
+	GLBBin   = 0x004E4942
+)

+ 803 - 0
loader/gltf/loader.go

@@ -0,0 +1,803 @@
+// 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 gltf
+
+import (
+	"bytes"
+	"encoding/base64"
+	"encoding/binary"
+	"encoding/json"
+	"fmt"
+	"image"
+	"image/draw"
+	"io"
+	"io/ioutil"
+	"os"
+	"path/filepath"
+	"strings"
+	"unsafe"
+
+	"github.com/g3n/engine/camera"
+	"github.com/g3n/engine/core"
+	"github.com/g3n/engine/geometry"
+	"github.com/g3n/engine/gls"
+	"github.com/g3n/engine/graphic"
+	"github.com/g3n/engine/material"
+	"github.com/g3n/engine/math32"
+	"github.com/g3n/engine/texture"
+)
+
+// ParseJSON parses the glTF data from the specified JSON file
+// and returns a pointer to the parsed structure.
+func ParseJSON(filename string) (*GLTF, error) {
+
+	// Opens file
+	f, err := os.Open(filename)
+	if err != nil {
+		return nil, err
+	}
+
+	// Extract path from file
+	path := filepath.Dir(filename)
+
+	defer f.Close()
+
+	return ParseJSONReader(f, path)
+}
+
+// ParseJSONReader parses the glTF JSON data from the specified reader
+// and returns a pointer to the parsed structure
+func ParseJSONReader(r io.Reader, path string) (*GLTF, error) {
+
+	g := new(GLTF)
+	g.path = path
+
+	dec := json.NewDecoder(r)
+	err := dec.Decode(g)
+	if err != nil {
+		return nil, err
+	}
+	return g, nil
+}
+
+// ParseBin parses the glTF data from the specified binary file
+// and returns a pointer to the parsed structure.
+func ParseBin(filename string) (*GLTF, error) {
+
+	// Opens file
+	f, err := os.Open(filename)
+	if err != nil {
+		return nil, err
+	}
+	// Extract path from file
+	path := filepath.Dir(filename)
+	defer f.Close()
+	return ParseBinReader(f, path)
+}
+
+// ParseBinReader parses the glTF data from the specified binary reader
+// and returns a pointer to the parsed structure
+func ParseBinReader(r io.Reader, path string) (*GLTF, error) {
+
+	// Reads header
+	var header GLBHeader
+	err := binary.Read(r, binary.LittleEndian, &header)
+	if err != nil {
+		return nil, err
+	}
+
+	// Checks magic and version
+	if header.Magic != GLBMagic {
+		return nil, fmt.Errorf("Invalid GLB Magic field")
+	}
+	if header.Version < 2 {
+		return nil, fmt.Errorf("GLB version:%v not supported", header.Version)
+	}
+
+	// Reads first chunk header (JSON)
+	var chunk0 GLBChunk
+	err = binary.Read(r, binary.LittleEndian, &chunk0)
+	if err != nil {
+		return nil, err
+	}
+	// Checks chunk type
+	if chunk0.Type != GLBJson {
+		return nil, fmt.Errorf("GLB Chunk0 type:%v not recognized", chunk0.Type)
+	}
+	// Reads first chunck data (JSON)
+	buf := make([]byte, chunk0.Length)
+	err = binary.Read(r, binary.LittleEndian, &buf)
+	if err != nil {
+		return nil, err
+	}
+	// Parses JSON
+	bb := bytes.NewBuffer(buf)
+	g, err := ParseJSONReader(bb, path)
+	if err != nil {
+		return nil, err
+	}
+
+	// Checks for chunk 1 (optional)
+	var chunk1 GLBChunk
+	err = binary.Read(r, binary.LittleEndian, &chunk1)
+	if err != nil {
+		if err != io.EOF {
+			return nil, err
+		}
+		return g, nil
+	}
+	// Check chunk1 type
+	if chunk1.Type != GLBBin {
+		return nil, fmt.Errorf("GLB Chunk1 type:%v not recognized", chunk1.Type)
+	}
+	// Reads chunk1 data
+	data := make([]byte, chunk1.Length)
+	err = binary.Read(r, binary.LittleEndian, &data)
+	if err != nil {
+		return nil, err
+	}
+	g.data = data
+
+	return g, nil
+}
+
+// NewScene creates a parent Node which contains all nodes contained by
+// the specified scene index from the GLTF Scenes array
+func (g *GLTF) NewScene(si int) (core.INode, error) {
+
+	if si < 0 || si >= len(g.Scenes) {
+		return nil, fmt.Errorf("Invalid Scene index")
+	}
+	s := g.Scenes[si]
+
+	scene := core.NewNode()
+	scene.SetName(s.Name)
+	for i := 0; i < len(s.Nodes); i++ {
+		child, err := g.NewNode(i)
+		if err != nil {
+			return nil, err
+		}
+		scene.Add(child)
+	}
+	return scene, nil
+}
+
+// NewNode creates and returns a new Node described by the specified index
+// in the decoded GLTF Nodes array.
+func (g *GLTF) NewNode(i int) (core.INode, error) {
+
+	var in core.INode
+	var err error
+	node := g.Nodes[i]
+
+	// Checks if the node is a Mesh (triangles, lines, etc...)
+	if node.Mesh != nil {
+		in, err = g.loadMesh(*node.Mesh)
+		if err != nil {
+			return nil, err
+		}
+		// Checks if the node is Camera
+	} else if node.Camera != nil {
+		in, err = g.loadCamera(*node.Camera)
+		if err != nil {
+			return nil, err
+		}
+		// Other cases, returns empty node
+	} else {
+		in = core.NewNode()
+	}
+
+	// Get *core.Node from core.INode
+	n := in.GetNode()
+	n.SetName(node.Name)
+
+	// If defined, sets node local transformation matrix
+	if node.Matrix != nil {
+		n.SetMatrix((*math32.Matrix4)(node.Matrix))
+		// Otherwise, checks rotation, scale and translation fields.
+	} else {
+		// Rotation quaternion
+		if node.Rotation != nil {
+			n.SetQuaternion(node.Rotation[0], node.Rotation[1], node.Rotation[2], node.Rotation[3])
+		} else {
+			n.SetQuaternion(0, 0, 0, 1)
+		}
+		// Scale
+		if node.Scale != nil {
+			n.SetScale(node.Scale[0], node.Scale[1], node.Scale[2])
+		} else {
+			n.SetScale(1, 1, 1)
+		}
+		// Translation
+		if node.Translation != nil {
+			log.Error("translation:%v", node.Translation)
+			n.SetPosition(node.Translation[0], node.Translation[1], node.Translation[2])
+		} else {
+			n.SetPosition(0, 0, 0)
+		}
+	}
+
+	// Loads node children recursively and adds to the parent
+	for _, ci := range node.Children {
+		child, err := g.NewNode(ci)
+		if err != nil {
+			return nil, err
+		}
+		n.Add(child)
+	}
+
+	return in, nil
+}
+
+// loadCamera creates and returns a Camera Node
+// from the specified GLTF.Cameras index
+func (g *GLTF) loadCamera(ci int) (core.INode, error) {
+
+	camDesc := g.Cameras[ci]
+	if camDesc.Type == "perspective" {
+		desc := camDesc.Perspective
+		fov := 360 * (desc.Yfov) / 2 * math32.Pi
+		aspect := float32(2) // TODO how to get the current aspect ratio of the viewport from here ?
+		if desc.AspectRatio != nil {
+			aspect = *desc.AspectRatio
+		}
+		far := float32(2E6)
+		if desc.Zfar != nil {
+			far = *desc.Zfar
+		}
+		return camera.NewPerspective(fov, aspect, desc.Znear, far), nil
+	}
+
+	if camDesc.Type == "orthographic" {
+		desc := camDesc.Orthographic
+		cam := camera.NewOrthographic(desc.Xmag/-2, desc.Xmag/2, desc.Ymag/2, desc.Ymag/-2,
+			desc.Znear, desc.Zfar)
+		return cam, nil
+
+	}
+	return nil, fmt.Errorf("Unsupported camera type:%s", camDesc.Type)
+}
+
+// loadMesh creates and returns a Graphic Node (graphic.Mesh, graphic.Lines, graphic.Points, etc)
+// from the specified GLTF Mesh index
+func (g *GLTF) loadMesh(mi int) (core.INode, error) {
+
+	// Create attribute buffers
+	indices := math32.NewArrayU32(0, 0)
+	positions := math32.NewArrayF32(0, 0)
+	normals := math32.NewArrayF32(0, 0)
+	uvs0 := math32.NewArrayF32(0, 0)
+
+	// Array of primitive materials
+	type matGroup struct {
+		imat  material.IMaterial
+		start int
+		count int
+	}
+	grMats := make([]matGroup, 0)
+
+	// Process mesh primitives
+	var mode int = -1
+	var err error
+	m := g.Meshes[mi]
+
+	for i := 0; i < len(m.Primitives); i++ {
+		p := m.Primitives[i]
+		// Indexed Geometry
+		if p.Indices != nil {
+			pidx, err := g.loadIndices(*p.Indices)
+			if err != nil {
+				return nil, err
+			}
+			indices = append(indices, pidx...)
+		} else {
+			// Non-indexed primitive
+			// indices array stay empty
+		}
+
+		// Currently the mode MUST be the same for all primitives
+		if p.Mode != nil {
+			mode = *p.Mode
+		} else {
+			mode = TRIANGLES
+		}
+
+		// Load primitive material
+		var mat material.IMaterial
+		if p.Material != nil {
+			mat, err = g.loadMaterial(*p.Material)
+			if err != nil {
+				return nil, err
+			}
+		} else {
+			mat = g.newDefaultMaterial()
+		}
+		grMats = append(grMats, matGroup{
+			imat:  mat,
+			start: int(0),
+			count: len(indices),
+		})
+
+		// Load primitive attributes
+		for name, aci := range p.Attributes {
+			if name == "POSITION" {
+				ppos, err := g.loadVec3(aci)
+				if err != nil {
+					return nil, err
+				}
+				positions = append(positions, ppos...)
+				continue
+			}
+			if name == "NORMAL" {
+				pnorms, err := g.loadVec3(aci)
+				if err != nil {
+					return nil, err
+				}
+				normals = append(normals, pnorms...)
+				continue
+			}
+			if name == "TEXCOORD_0" {
+				puvs, err := g.loadVec2(aci)
+				if err != nil {
+					return nil, err
+				}
+				uvs0 = append(uvs0, puvs...)
+				continue
+			}
+		}
+	}
+
+	// Creates Geometry and add attribute VBOs
+	geom := geometry.NewGeometry()
+	if len(indices) > 0 {
+		geom.SetIndices(indices)
+	}
+	if len(positions) > 0 {
+		geom.AddVBO(gls.NewVBO().AddAttrib("VertexPosition", 3).SetBuffer(positions))
+	}
+	if len(normals) > 0 {
+		geom.AddVBO(gls.NewVBO().AddAttrib("VertexNormal", 3).SetBuffer(normals))
+	}
+	if len(uvs0) > 0 {
+		geom.AddVBO(gls.NewVBO().AddAttrib("VertexTexcoord", 2).SetBuffer(uvs0))
+	}
+
+	//log.Error("positions:%v", positions)
+	//log.Error("indices..:%v", indices)
+	//log.Error("normals..:%v", normals)
+	//log.Error("uvs0.....:%v", uvs0)
+
+	// Create Mesh
+	if mode == TRIANGLES {
+		node := graphic.NewMesh(geom, nil)
+		for i := 0; i < len(grMats); i++ {
+			grm := grMats[i]
+			node.AddMaterial(grm.imat, grm.start, grm.count)
+		}
+		return node, nil
+	}
+
+	// Create Lines
+	if mode == LINES {
+		node := graphic.NewLines(geom, grMats[0].imat)
+		return node, nil
+	}
+
+	// Create LineStrip
+	if mode == LINE_STRIP {
+		node := graphic.NewLineStrip(geom, grMats[0].imat)
+		return node, nil
+	}
+
+	// Create Points
+	if mode == POINTS {
+		node := graphic.NewPoints(geom, grMats[0].imat)
+		return node, nil
+	}
+	return nil, fmt.Errorf("Unsupported primitive:%v", mode)
+}
+
+func (g *GLTF) newDefaultMaterial() material.IMaterial {
+
+	return material.NewStandard(&math32.Color{0.5, 0.5, 0.5})
+}
+
+// loadMaterials loads the material specified by the material index
+func (g *GLTF) loadMaterial(mi int) (material.IMaterial, error) {
+
+	mat := g.Materials[mi]
+	// Checks for material extensions
+	if mat.Extensions != nil {
+		for ext, v := range mat.Extensions {
+			if ext == "KHR_materials_common" {
+				return g.loadMaterialCommon(v)
+			} else {
+				return nil, fmt.Errorf("Unsupported extension:%s", ext)
+			}
+		}
+		return nil, fmt.Errorf("Empty material extensions")
+		// Material should be PBR
+	} else {
+		return g.loadMaterialPBR(&mat)
+	}
+}
+
+// loadTextureInfo loads the texture specified by the TextureInfo pointer
+func (g *GLTF) loadTextureInfo(ti *TextureInfo) (*texture.Texture2D, error) {
+
+	log.Error("loadTexture:%+v", ti)
+	// loads texture image
+	texDesc := g.Textures[ti.Index]
+	img, err := g.loadImage(texDesc.Source)
+	if err != nil {
+		return nil, err
+	}
+	tex := texture.NewTexture2DFromRGBA(img)
+
+	// Get sampler and apply texture parameters
+	samp := g.Samplers[texDesc.Sampler]
+
+	// Magnification filter
+	magFilter := gls.NEAREST
+	if samp.MagFilter != nil {
+		magFilter = *samp.MagFilter
+	}
+	tex.SetMagFilter(uint32(magFilter))
+
+	// Minification filter
+	minFilter := gls.NEAREST
+	if samp.MinFilter != nil {
+		minFilter = *samp.MinFilter
+	}
+	tex.SetMinFilter(uint32(minFilter))
+
+	// S coordinate wrapping mode
+	wrapS := gls.REPEAT
+	if samp.WrapS != nil {
+		wrapS = *samp.WrapS
+	}
+	tex.SetWrapS(uint32(wrapS))
+
+	// T coordinate wrapping mode
+	wrapT := gls.REPEAT
+	if samp.WrapT != nil {
+		wrapT = *samp.WrapT
+	}
+	tex.SetWrapT(uint32(wrapT))
+
+	return tex, nil
+}
+
+// loadImage loads the image specified by the index of GLTF.Images
+// Image can be loaded from binary chunk file or data URI or external file.
+func (g *GLTF) loadImage(ii int) (*image.RGBA, error) {
+
+	log.Error("loadImage:%v", ii)
+	imgDesc := g.Images[ii]
+	var data []byte
+	var err error
+	// If Uri is empty, load image from chunk
+	if imgDesc.Uri == "" {
+		bvi := imgDesc.BufferView
+		if bvi == nil {
+			return nil, fmt.Errorf("Image has empty URI and no BufferView")
+		}
+		bv := g.BufferViews[*bvi]
+		offset := 0
+		if bv.ByteOffset != nil {
+			offset = *bv.ByteOffset
+		}
+		data = g.data[offset : offset+bv.ByteLength]
+		// Checks if image URI is data URL
+	} else if isDataURL(imgDesc.Uri) {
+		data, err = loadDataURL(imgDesc.Uri)
+		if err != nil {
+			return nil, err
+		}
+		// Loads external buffer file
+	} else {
+		// Load image data from file
+		fpath := filepath.Join(g.path, imgDesc.Uri)
+		f, err := os.Open(fpath)
+		if err != nil {
+			return nil, err
+		}
+		defer f.Close()
+		data, err = ioutil.ReadAll(f)
+		if err != nil {
+			return nil, err
+		}
+	}
+
+	// Decodes image data
+	bb := bytes.NewBuffer(data)
+	img, _, err := image.Decode(bb)
+	if err != nil {
+		return nil, err
+	}
+
+	// Converts image to RGBA format
+	rgba := image.NewRGBA(img.Bounds())
+	if rgba.Stride != rgba.Rect.Size().X*4 {
+		return nil, fmt.Errorf("unsupported stride")
+	}
+	draw.Draw(rgba, rgba.Bounds(), img, image.Point{0, 0}, draw.Src)
+	return rgba, nil
+}
+
+// loadVec3 load array of Vector3 from the specified accessor index
+func (g *GLTF) loadVec3(ai int) (math32.ArrayF32, error) {
+
+	// Get Accessor for the specified index
+	ac := g.Accessors[ai]
+	if ac.BufferView == nil {
+		return nil, fmt.Errorf("Accessor.BufferView == nil NOT SUPPORTED")
+	}
+
+	// Checks acessor ComponentType
+	if ac.ComponentType != FLOAT {
+		return nil, fmt.Errorf("Accessor.ComponentType != FLOAT NOT SUPPORTED")
+	}
+
+	// Checks acessor Type
+	if ac.Type != VEC3 {
+		return nil, fmt.Errorf("Accessor.ComponentType != VEC3 NOT SUPPORTED")
+	}
+
+	// Loads data from associated BufferView
+	data, err := g.loadBufferView(*ac.BufferView)
+	if err != nil {
+		return nil, err
+	}
+
+	// Accessor offset into BufferView
+	offset := 0
+	if ac.ByteOffset != nil {
+		offset = *ac.ByteOffset
+	}
+	data = data[offset:]
+
+	arr := (*[1 << 30]float32)(unsafe.Pointer(&data[0]))[:ac.Count*3]
+	return math32.ArrayF32(arr), nil
+}
+
+// loadVec2 load array of Vector2 from the specified accessor index
+func (g *GLTF) loadVec2(ai int) (math32.ArrayF32, error) {
+
+	// Get Accessor for the specified index
+	ac := g.Accessors[ai]
+	if ac.BufferView == nil {
+		return nil, fmt.Errorf("Accessor.BufferView == nil NOT SUPPORTED")
+	}
+
+	// Checks acessor ComponentType
+	if ac.ComponentType != FLOAT {
+		return nil, fmt.Errorf("Accessor.ComponentType != FLOAT NOT SUPPORTED")
+	}
+
+	// Checks acessor Type
+	if ac.Type != VEC2 {
+		return nil, fmt.Errorf("Accessor.ComponentType != VEC2 NOT SUPPORTED")
+	}
+
+	// Loads data from associated BufferView
+	data, err := g.loadBufferView(*ac.BufferView)
+	if err != nil {
+		return nil, err
+	}
+
+	// Accessor offset into BufferView
+	offset := 0
+	if ac.ByteOffset != nil {
+		offset = *ac.ByteOffset
+	}
+	data = data[offset:]
+
+	arr := (*[1 << 30]float32)(unsafe.Pointer(&data[0]))[:ac.Count*2]
+	return math32.ArrayF32(arr), nil
+}
+
+// loadIndices load the indices array specified by the Accessor index.
+func (g *GLTF) loadIndices(ai int) (math32.ArrayU32, error) {
+
+	// Get Accessor for the specified index
+	ac := g.Accessors[ai]
+	if ac.BufferView == nil {
+		return nil, fmt.Errorf("Accessor.BufferView == nil NOT SUPPORTED YET")
+	}
+
+	// Loads indices data from associated BufferView
+	data, err := g.loadBufferView(*ac.BufferView)
+	if err != nil {
+		return nil, err
+	}
+
+	// Accessor offset into BufferView
+	offset := 0
+	if ac.ByteOffset != nil {
+		offset = *ac.ByteOffset
+	}
+	data = data[offset:]
+
+	// If index component is UNSIGNED_INT nothing to do
+	if ac.ComponentType == UNSIGNED_INT {
+		arr := (*[1 << 30]uint32)(unsafe.Pointer(&data[0]))[:ac.Count]
+		return math32.ArrayU32(arr), nil
+	}
+
+	// Converts UNSIGNED_SHORT indices to UNSIGNED_INT
+	if ac.ComponentType == UNSIGNED_SHORT {
+		indices := math32.NewArrayU32(ac.Count, ac.Count)
+		for i := 0; i < ac.Count; i++ {
+			indices[i] = uint32(data[i*2]) + uint32(data[i*2+1])*256
+		}
+		return indices, nil
+	}
+
+	// Converts UNSIGNED_BYTE indices to UNSIGNED_INT
+	if ac.ComponentType == UNSIGNED_BYTE {
+		indices := math32.NewArrayU32(ac.Count, ac.Count)
+		for i := 0; i < ac.Count; i++ {
+			indices[i] = uint32(data[i*4]) + uint32(data[i*4+1])*256 +
+				uint32(data[i*4+2])*256*256 + uint32(data[i*4+3])*256*256*256
+		}
+		return indices, nil
+	}
+	return nil, fmt.Errorf("Unsupported Accessor ComponentType:%v", ac.ComponentType)
+}
+
+// loadBufferView loads and returns a byte slice with data from the specified
+// BufferView index
+func (g *GLTF) loadBufferView(bvi int) ([]byte, error) {
+
+	bv := g.BufferViews[bvi]
+	buf, err := g.loadBuffer(bv.Buffer)
+	if err != nil {
+		return nil, err
+	}
+
+	offset := 0
+	if bv.ByteOffset != nil {
+		offset = *bv.ByteOffset
+	}
+	return buf[offset : offset+bv.ByteLength], nil
+}
+
+// loadBuffer loads and returns the data from the specified GLTF Buffer index
+func (g *GLTF) loadBuffer(bi int) ([]byte, error) {
+
+	buf := &g.Buffers[bi]
+	// If Buffer URI uses the chunk data field
+	if buf.Uri == "" {
+		return g.data, nil
+	}
+
+	// If buffer already loaded:
+	log.Error("loadBuffer cache:%v", len(buf.data))
+	if len(buf.data) > 0 {
+		return buf.data, nil
+	}
+
+	// Checks if buffer URI is a data URI
+	var data []byte
+	var err error
+	if isDataURL(buf.Uri) {
+		data, err = loadDataURL(buf.Uri)
+		if err != nil {
+			return nil, err
+		}
+		// Loads external buffer file
+	} else {
+		log.Error("loadBuffer: loading file")
+		// Try to load buffer from file
+		fpath := filepath.Join(g.path, buf.Uri)
+		f, err := os.Open(fpath)
+		if err != nil {
+			return nil, err
+		}
+		defer f.Close()
+		data, err = ioutil.ReadAll(f)
+		if err != nil {
+			return nil, err
+		}
+	}
+	// Checks data length
+	if len(data) != buf.ByteLength {
+		return nil, fmt.Errorf("Buffer:%d read data length:%d expected:%d", bi, len(data), buf.ByteLength)
+	}
+	// Cache buffer data
+	buf.data = data
+	log.Error("cache data:%v", len(buf.data))
+	return data, nil
+}
+
+// dataURL describes a decoded data url string
+type dataURL struct {
+	MediaType string
+	Encoding  string
+	Data      string
+}
+
+const (
+	dataURLprefix = "data:"
+	mimeBIN       = "application/octet-stream"
+	mimePNG       = "image/png"
+	mimeJPEG      = "image/jpeg"
+)
+
+var validMediaTypes = []string{mimeBIN, mimePNG, mimeJPEG}
+
+// isDataURL checks if the specified string has the prefix of data URL
+func isDataURL(url string) bool {
+
+	if strings.HasPrefix(url, dataURLprefix) {
+		return true
+	}
+	return false
+}
+
+// loadDataURL decodes the specified data URI string (base64)
+func loadDataURL(url string) ([]byte, error) {
+
+	var du dataURL
+	err := parseDataURL(url, &du)
+	if err != nil {
+		return nil, err
+	}
+
+	// Checks for valid media type
+	found := false
+	for i := 0; i < len(validMediaTypes); i++ {
+		if validMediaTypes[i] == du.MediaType {
+			found = true
+			break
+		}
+	}
+	if !found {
+		return nil, fmt.Errorf("Data URI media type:%s not supported", du.MediaType)
+	}
+
+	// Checks encoding
+	if du.Encoding != "base64" {
+		return nil, fmt.Errorf("Data URI encoding:%s not supported", du.Encoding)
+	}
+
+	// Decodes data from BASE64
+	data, err := base64.StdEncoding.DecodeString(du.Data)
+	if err != nil {
+		return nil, err
+	}
+	return data, nil
+}
+
+// parseDataURL tries to parse the specified string as a data URL with the format:
+// data:[<mediatype>][;base64],<data>
+// and if successfull returns true and updates the specified pointer with the parsed fields.
+func parseDataURL(url string, du *dataURL) error {
+
+	// Checks prefix
+	if !isDataURL(url) {
+		return fmt.Errorf("Specified string is not a data URL")
+	}
+
+	// Separates header from data
+	body := url[len(dataURLprefix):]
+	parts := strings.Split(body, ",")
+	if len(parts) != 2 {
+		return fmt.Errorf("Data URI contains more than one ','")
+	}
+	du.Data = parts[1]
+
+	// Separates media type from optional encoding
+	res := strings.Split(parts[0], ";")
+	du.MediaType = res[0]
+	if len(res) < 2 {
+		return nil
+	}
+	if len(res) >= 2 {
+		du.Encoding = res[1]
+	}
+	return nil
+}

+ 12 - 0
loader/gltf/logger.go

@@ -0,0 +1,12 @@
+// 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 gltf
+
+import (
+	"github.com/g3n/engine/util/logger"
+)
+
+// Package logger
+var log = logger.New("GLTF", logger.Default)

+ 146 - 0
loader/gltf/material_common.go

@@ -0,0 +1,146 @@
+package gltf
+
+import (
+	"github.com/g3n/engine/material"
+	"github.com/g3n/engine/math32"
+)
+
+// loadMaterialCommon receives an interface value describing a KHR_materials_common extension,
+// decodes it and returns a Material closest to the specified description
+// The specification of this extension is at:
+// https://github.com/KhronosGroup/glTF/tree/master/extensions/Khronos/KHR_materials_common
+func (g *GLTF) loadMaterialCommon(ext interface{}) (material.IMaterial, error) {
+
+	// The extension must be an object
+	m := ext.(map[string]interface{})
+
+	// Double sided
+	doubleSided := false
+	val, ok := m["doubleSided"]
+	if ok {
+		doubleSided = val.(bool)
+	}
+
+	// Technique
+	technique := ""
+	val, ok = m["technique"]
+	if ok {
+		technique = val.(string)
+	}
+
+	// Transparent
+	transparent := false
+	val, ok = m["transparent"]
+	if ok {
+		transparent = val.(bool)
+	}
+
+	// Defaul values
+	ambient := []float32{0, 0, 0, 1}
+	diffuse := []float32{0, 0, 0, 1}
+	emission := []float32{0, 0, 0, 1}
+	specular := []float32{0, 0, 0, 1}
+	shininess := float32(0)
+	transparency := float32(1)
+
+	// Converts a slice of interface values which should be float64 to
+	// to a slice of float32
+	convIF32 := func(v interface{}) []float32 {
+
+		si := v.([]interface{})
+		res := make([]float32, 0)
+		for i := 0; i < len(si); i++ {
+			res = append(res, float32(si[i].(float64)))
+		}
+		return res
+	}
+
+	// Values
+	values, ok := m["values"].(map[string]interface{})
+	if ok {
+
+		// Ambient light
+		val, ok = values["ambient"]
+		if ok {
+			ambient = convIF32(val)
+		}
+
+		// Diffuse light
+		val, ok = values["diffuse"]
+		if ok {
+			diffuse = convIF32(val)
+		}
+
+		// Emission light
+		val, ok = values["emission"]
+		if ok {
+			emission = convIF32(val)
+		}
+
+		// Specular light
+		val, ok = values["specular"]
+		if ok {
+			specular = convIF32(val)
+		}
+
+		// Shininess
+		val, ok = values["shininess"]
+		if ok {
+			s := convIF32(val)
+			shininess = s[0]
+		}
+
+		// Transparency
+		val, ok = values["transparency"]
+		if ok {
+			s := convIF32(val)
+			transparency = s[0]
+		}
+	}
+
+	//log.Error("doubleSided:%v", doubleSided)
+	//log.Error("technique:%v", technique)
+	//log.Error("transparent:%v", transparent)
+	//log.Error("values:%v", values)
+	//log.Error("ambient:%v", ambient)
+	//log.Error("diffuse:%v", diffuse)
+	//log.Error("emission:%v", emission)
+	//log.Error("specular:%v", specular)
+	//log.Error("shininess:%v", shininess)
+	//log.Error("transparency:%v", transparency)
+
+	var imat material.IMaterial
+	if technique == "PHONG" {
+		pm := material.NewPhong(&math32.Color{diffuse[0], diffuse[1], diffuse[2]})
+		pm.SetAmbientColor(&math32.Color{ambient[0], ambient[1], ambient[2]})
+		pm.SetEmissiveColor(&math32.Color{emission[0], emission[1], emission[2]})
+		pm.SetSpecularColor(&math32.Color{specular[0], specular[1], specular[2]})
+		pm.SetShininess(shininess)
+		pm.SetOpacity(transparency)
+		imat = pm
+	} else {
+		sm := material.NewStandard(&math32.Color{diffuse[0], diffuse[1], diffuse[2]})
+		sm.SetAmbientColor(&math32.Color{ambient[0], ambient[1], ambient[2]})
+		sm.SetEmissiveColor(&math32.Color{emission[0], emission[1], emission[2]})
+		sm.SetSpecularColor(&math32.Color{specular[0], specular[1], specular[2]})
+		sm.SetShininess(shininess)
+		sm.SetOpacity(transparency)
+		imat = sm
+	}
+
+	// Double Sided
+	mat := imat.GetMaterial()
+	if doubleSided {
+		mat.SetSide(material.SideDouble)
+	} else {
+		mat.SetSide(material.SideFront)
+	}
+
+	// Transparency
+	if transparent {
+		mat.SetDepthMask(true)
+	} else {
+		mat.SetDepthMask(false)
+	}
+	return imat, nil
+}

+ 37 - 0
loader/gltf/material_pbr.go

@@ -0,0 +1,37 @@
+package gltf
+
+import (
+	"fmt"
+
+	"github.com/g3n/engine/material"
+	"github.com/g3n/engine/math32"
+	"github.com/g3n/engine/texture"
+)
+
+func (g *GLTF) loadMaterialPBR(m *Material) (material.IMaterial, error) {
+
+	// Currently simulating PBR material with our common materials
+	pbr := m.PbrMetallicRoughness
+	if pbr == nil {
+		return nil, fmt.Errorf("PbrMetallicRoughness not supplied")
+	}
+	pm := material.NewPhong(&math32.Color{pbr.BaseColorFactor[0], pbr.BaseColorFactor[1], pbr.BaseColorFactor[2]})
+	pm.SetAmbientColor(&math32.Color{1, 1, 1})
+	pm.SetEmissiveColor(&math32.Color{0, 0, 0})
+	//pm.SetSpecularColor(&math32.Color{0, 0, 0})
+	//pm.SetShininess(0)
+	//pm.SetOpacity(1)
+
+	// BaseColorTexture
+	var tex *texture.Texture2D
+	var err error
+	if pbr.BaseColorTexture != nil {
+		tex, err = g.loadTextureInfo(pbr.BaseColorTexture)
+		if err != nil {
+			return nil, err
+		}
+		pm.AddTexture(tex)
+	}
+
+	return pm, nil
+}