Skip to content

Commit

Permalink
Merge pull request #61 from qmuntal/spaces
Browse files Browse the repository at this point in the history
Support whitespaces in local URIs
  • Loading branch information
qmuntal committed Sep 28, 2022
2 parents 3ea49a1 + 5a53238 commit 5ee7dab
Show file tree
Hide file tree
Showing 11 changed files with 340 additions and 13 deletions.
38 changes: 29 additions & 9 deletions decoder.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,21 @@ func (d *Decoder) Decode(doc *Document) error {
return err
}

for _, b := range doc.Buffers {
if !b.IsEmbeddedResource() {
if uri, ok := sanitizeURI(b.URI); ok {
b.URI = uri
}
}
}
for _, im := range doc.Images {
if !im.IsEmbeddedResource() {
if uri, ok := sanitizeURI(im.URI); ok {
im.URI = uri
}
}
}

var externalBufferIndex = 0
if isBinary && len(doc.Buffers) > 0 {
externalBufferIndex = 1
Expand Down Expand Up @@ -141,12 +156,9 @@ func (d *Decoder) decodeBuffer(buffer *Buffer) error {
} else {
err = validateBufferURI(buffer.URI)
if err == nil && d.Fsys != nil {
uri, ok := sanitizeURI(buffer.URI)
if ok {
buffer.Data, err = fs.ReadFile(d.Fsys, uri)
if len(buffer.Data) > int(buffer.ByteLength) {
buffer.Data = buffer.Data[:buffer.ByteLength:buffer.ByteLength]
}
buffer.Data, err = fs.ReadFile(d.Fsys, buffer.URI)
if len(buffer.Data) > int(buffer.ByteLength) {
buffer.Data = buffer.Data[:buffer.ByteLength:buffer.ByteLength]
}
}
}
Expand Down Expand Up @@ -191,9 +203,17 @@ func sanitizeURI(uri string) (string, bool) {
uri = strings.Replace(uri, "/./", "/", -1)
uri = strings.TrimPrefix(uri, "./")
u, err := url.Parse(uri)
if err != nil || u.IsAbs() {
// Only relative paths supported.
if err != nil {
return "", false
}
return strings.TrimPrefix(u.RequestURI(), "/"), true
if u.Scheme == "" {
// URI should always be decoded before using it in a file path.
uri, err = url.PathUnescape(uri)
if err != nil {
return "", false
}
} else {
uri = u.String()
}
return uri, true
}
38 changes: 34 additions & 4 deletions decoder_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,36 @@ func TestOpen(t *testing.T) {
Scene: Index(0),
Scenes: []*Scene{{Name: "Root Scene", Nodes: []uint32{0}}},
}, false},
{args{"testdata/Box With Spaces/glTF/Box With Spaces.gltf", ""}, &Document{
Accessors: []*Accessor{
{BufferView: Index(0), ComponentType: ComponentFloat, Count: 24, Max: []float32{1, 1, 1}, Min: []float32{-1, -1, -1}, Type: AccessorVec3},
{BufferView: Index(1), ComponentType: ComponentFloat, Count: 24, Type: AccessorVec3},
{BufferView: Index(2), ComponentType: ComponentFloat, Count: 24, Type: AccessorVec2},
{BufferView: Index(3), ComponentType: ComponentUshort, Count: 36, Type: AccessorScalar},
},
Asset: Asset{Generator: "Khronos glTF Blender I/O v1.3.48", Version: "2.0", Copyright: "CC0 by Ed Mackey, AGI"},
BufferViews: []*BufferView{
{ByteLength: 288, ByteOffset: 0},
{ByteLength: 288, ByteOffset: 288},
{ByteLength: 192, ByteOffset: 576},
{ByteLength: 72, ByteOffset: 768},
},
Buffers: []*Buffer{{ByteLength: 840, URI: "Box With Spaces.bin", Data: readFile("testdata/Box With Spaces/glTF/Box With Spaces.bin")}},
Images: []*Image{
{Name: "Normal Map", MimeType: "image/png", URI: "Normal Map.png"},
{Name: "glTF Logo With Spaces", MimeType: "image/png", URI: "glTF Logo With Spaces.png"},
{Name: "Roughness Metallic", MimeType: "image/png", URI: "Roughness Metallic.png"},
},
Materials: []*Material{{
Name: "Material", AlphaMode: AlphaOpaque, AlphaCutoff: Float(0.5), NormalTexture: &NormalTexture{Index: Index(0), Scale: Float(1)}, PBRMetallicRoughness: &PBRMetallicRoughness{
BaseColorFactor: &[4]float32{1, 1, 1, 1}, MetallicFactor: Float(1), RoughnessFactor: Float(1), BaseColorTexture: &TextureInfo{Index: 1}, MetallicRoughnessTexture: &TextureInfo{Index: 2},
}}},
Meshes: []*Mesh{{Name: "Cube", Primitives: []*Primitive{{Indices: Index(3), Material: Index(0), Mode: PrimitiveTriangles, Attributes: map[string]uint32{NORMAL: 1, POSITION: 0, TEXCOORD_0: 2}}}}},
Nodes: []*Node{{Mesh: Index(0), Name: "Cube", Matrix: [16]float32{1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1}, Rotation: [4]float32{0, 0, 0, 1}, Scale: [3]float32{1, 1, 1}}},
Scene: Index(0),
Scenes: []*Scene{{Name: "Scene", Nodes: []uint32{0}}},
Textures: []*Texture{{Source: Index(0)}, {Source: Index(1)}, {Source: Index(2)}},
}, false},
}
for _, tt := range tests {
t.Run(tt.args.name, func(t *testing.T) {
Expand All @@ -122,15 +152,15 @@ func TestOpen(t *testing.T) {
}
if tt.args.embedded != "" {
got, err = Open(tt.args.embedded)
if (err != nil) != tt.wantErr {
t.Errorf("Open() error = %v, wantErr %v", err, tt.wantErr)
return
}
for i, b := range got.Buffers {
if b.IsEmbeddedResource() {
tt.want.Buffers[i].EmbeddedResource()
}
}
if (err != nil) != tt.wantErr {
t.Errorf("Open() error = %v, wantErr %v", err, tt.wantErr)
return
}
if diff := deep.Equal(got, tt.want); diff != nil {
t.Errorf("Open() = %v", diff)
return
Expand Down
102 changes: 102 additions & 0 deletions simple.gltf
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
{
"scenes": [
{
"nodes": [0, 1]
}
],
"nodes": [
{
"mesh": 0
},
{
"mesh": 0,
"name": "guid",
"matrix": [
2.0,
0.0,
0.0,
0.0,
0.0,
0.866,
0.5,
0.0,
0.0,
-0.25,
0.433,
0.0,
10.0,
20.0,
30.0,
1.0
]
}
],

"meshes": [
{
"primitives": [
{
"attributes": {
"POSITION": 1,
"NORMAL": 2
},
"indices": 0
}
]
}
],

"buffers": [
{
"uri": "data:application/octet-stream;base64,AAABAAIAAAAAAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8=",
"byteLength": 80
}
],
"bufferViews": [
{
"buffer": 0,
"byteOffset": 0,
"byteLength": 6,
"target": 34963
},
{
"buffer": 0,
"byteOffset": 8,
"byteLength": 72,
"target": 34962
}
],
"accessors": [
{
"bufferView": 0,
"byteOffset": 0,
"componentType": 5123,
"count": 3,
"type": "SCALAR",
"max": [2],
"min": [0]
},
{
"bufferView": 1,
"byteOffset": 0,
"componentType": 5126,
"count": 3,
"type": "VEC3",
"max": [1.0, 1.0, 0.0],
"min": [0.0, 0.0, 0.0]
},
{
"bufferView": 1,
"byteOffset": 36,
"componentType": 5126,
"count": 3,
"type": "VEC3",
"max": [0.0, 0.0, 1.0],
"min": [0.0, 0.0, 1.0]
}
],

"asset": {
"version": "2.0"
}
}
21 changes: 21 additions & 0 deletions testdata/Box With Spaces/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Box With Spaces

## Screenshot

![screenshot](screenshot/screenshot_large.png)

## Description

The binary file is called `Box With Spaces.bin`, testing runtime support for the presence of spaces in a URI. Three textures
also have spaces in their URIs, but each space character is URI-encoded as `%20`.

Client implementations are expected to URI-decode all URIs present in a glTF model, even when they represent files on a
local disk. See [#1449](https://github.com/KhronosGroup/glTF/issues/1449) for additional comments.

## License Information

glTF™ is a trademark of the Khronos Group Inc. Follow the [Khronos Logo Usage Guidelines](https://www.khronos.org/legal/trademarks/)
when reproducing the glTF™ logo.

The cube model is [![CC0](http://i.creativecommons.org/p/zero/1.0/88x31.png)](http://creativecommons.org/publicdomain/zero/1.0/)
To the extent possible under law, Analytical Graphics has waived all copyright and related or neighboring rights to this asset.
Binary file added testdata/Box With Spaces/glTF/Box With Spaces.bin
Binary file not shown.
154 changes: 154 additions & 0 deletions testdata/Box With Spaces/glTF/Box With Spaces.gltf
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
{
"asset" : {
"copyright" : "CC0 by Ed Mackey, AGI",
"generator" : "Khronos glTF Blender I/O v1.3.48",
"version" : "2.0"
},
"scene" : 0,
"scenes" : [
{
"name" : "Scene",
"nodes" : [
0
]
}
],
"nodes" : [
{
"mesh" : 0,
"name" : "Cube"
}
],
"materials" : [
{
"emissiveFactor" : [
0,
0,
0
],
"name" : "Material",
"normalTexture" : {
"index" : 0,
"texCoord" : 0
},
"pbrMetallicRoughness" : {
"baseColorTexture" : {
"index" : 1,
"texCoord" : 0
},
"metallicRoughnessTexture" : {
"index" : 2,
"texCoord" : 0
}
}
}
],
"meshes" : [
{
"name" : "Cube",
"primitives" : [
{
"attributes" : {
"POSITION" : 0,
"NORMAL" : 1,
"TEXCOORD_0" : 2
},
"indices" : 3,
"material" : 0
}
]
}
],
"textures" : [
{
"source" : 0
},
{
"source" : 1
},
{
"source" : 2
}
],
"images" : [
{
"mimeType" : "image/png",
"name" : "Normal Map",
"uri" : "Normal%20Map.png"
},
{
"mimeType" : "image/png",
"name" : "glTF Logo With Spaces",
"uri" : "glTF%20Logo%20With%20Spaces.png"
},
{
"mimeType" : "image/png",
"name" : "Roughness Metallic",
"uri" : "Roughness%20Metallic.png"
}
],
"accessors" : [
{
"bufferView" : 0,
"componentType" : 5126,
"count" : 24,
"max" : [
1,
1,
1
],
"min" : [
-1,
-1,
-1
],
"type" : "VEC3"
},
{
"bufferView" : 1,
"componentType" : 5126,
"count" : 24,
"type" : "VEC3"
},
{
"bufferView" : 2,
"componentType" : 5126,
"count" : 24,
"type" : "VEC2"
},
{
"bufferView" : 3,
"componentType" : 5123,
"count" : 36,
"type" : "SCALAR"
}
],
"bufferViews" : [
{
"buffer" : 0,
"byteLength" : 288,
"byteOffset" : 0
},
{
"buffer" : 0,
"byteLength" : 288,
"byteOffset" : 288
},
{
"buffer" : 0,
"byteLength" : 192,
"byteOffset" : 576
},
{
"buffer" : 0,
"byteLength" : 72,
"byteOffset" : 768
}
],
"buffers" : [
{
"byteLength" : 840,
"uri" : "Box With Spaces.bin"
}
]
}
Binary file added testdata/Box With Spaces/glTF/Normal Map.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit 5ee7dab

Please sign in to comment.