mittsu-gltf 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/README.md +25 -0
- data/Rakefile +11 -0
- data/lib/mittsu/gltf/exporter.rb +168 -0
- data/lib/mittsu/gltf/version.rb +5 -0
- data/lib/mittsu/gltf.rb +3 -0
- metadata +149 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 7357b9b37036619cd1dcac6a0272db90d88f6e0caf567805c4df6f15ada7c272
|
4
|
+
data.tar.gz: e9ee54d8195e9b1635163fccc9855dc9bd3a3b61333f2c8b61ac620e65899f84
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 9c66bb2249b83c4b1b7f8291ab8e619851c08dc462eb0310f08ca0198f9c20bd8cd5f9199f82d438d0dc9cc6dedadd7013129a19eec387942318efa8ef55f41b
|
7
|
+
data.tar.gz: 4dc4dd25b9001add1c635608dcd3d769a5c4c3828f7a915f33e0694b3c91c697af9120b46b293b56cad9a5338096f1dea88031ee38827048077a2d7ccff370fc
|
data/README.md
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
# Mittsu: GLTF
|
2
|
+
GLTF support for [Mittsu](https://github.com/danini-the-panini/mittsu).
|
3
|
+
|
4
|
+
## Installation
|
5
|
+
|
6
|
+
Just install:
|
7
|
+
|
8
|
+
`bundle add mittsu-gltf`
|
9
|
+
|
10
|
+
Then require in your code:
|
11
|
+
|
12
|
+
`require 'mittsu/gltf'`
|
13
|
+
|
14
|
+
## Usage
|
15
|
+
|
16
|
+
Currently this gem just includes an exporter. Loading GLTF files might happen at some point.
|
17
|
+
|
18
|
+
```
|
19
|
+
exporter = Mittsu::GLTFExporter.new
|
20
|
+
exporter.export(object, "output.gltf")
|
21
|
+
```
|
22
|
+
|
23
|
+
## About
|
24
|
+
|
25
|
+
This code was originally written for [Manyfold](https://manyfold.app), supported by funding from [NLNet](https://nlnet.nl) and [NGI Zero](https://ngi.eu/ngi-projects/ngi-zero/).
|
data/Rakefile
ADDED
@@ -0,0 +1,168 @@
|
|
1
|
+
require "jbuilder"
|
2
|
+
require "base64"
|
3
|
+
|
4
|
+
module Mittsu
|
5
|
+
class GLTFExporter
|
6
|
+
COMPONENT_TYPES = {
|
7
|
+
# 8 bit
|
8
|
+
byte: 5120,
|
9
|
+
unsigned_byte: 5121,
|
10
|
+
# 16 bit
|
11
|
+
short: 5122,
|
12
|
+
unsigned_short: 5123,
|
13
|
+
# 32 bit
|
14
|
+
unsigned_int: 5125,
|
15
|
+
float: 5126
|
16
|
+
}.freeze
|
17
|
+
|
18
|
+
GPU_BUFFER_TYPES = {
|
19
|
+
array_buffer: 34962,
|
20
|
+
element_array_buffer: 34963
|
21
|
+
}
|
22
|
+
|
23
|
+
ELEMENT_TYPES = [
|
24
|
+
"SCALAR",
|
25
|
+
"VEC2",
|
26
|
+
"VEC3",
|
27
|
+
"VEC4",
|
28
|
+
"MAT2",
|
29
|
+
"MAT3",
|
30
|
+
"MAT4"
|
31
|
+
].freeze
|
32
|
+
|
33
|
+
def initialize(options = {})
|
34
|
+
@node_indexes = []
|
35
|
+
@nodes = []
|
36
|
+
@buffers = []
|
37
|
+
@meshes = []
|
38
|
+
@buffer_views = []
|
39
|
+
@accessors = []
|
40
|
+
end
|
41
|
+
|
42
|
+
def export(object, filename)
|
43
|
+
object.traverse do |obj|
|
44
|
+
@node_indexes << add_mesh(obj) if obj.is_a? Mittsu::Mesh
|
45
|
+
end
|
46
|
+
File.write(
|
47
|
+
filename,
|
48
|
+
Jbuilder.new do |json|
|
49
|
+
json.asset do
|
50
|
+
json.generator "Mittsu-GLTF"
|
51
|
+
json.version "2.0"
|
52
|
+
end
|
53
|
+
json.scene 0
|
54
|
+
json.scenes [{
|
55
|
+
nodes: @node_indexes
|
56
|
+
}]
|
57
|
+
json.nodes { json.array! @nodes }
|
58
|
+
json.meshes { json.array! @meshes }
|
59
|
+
json.buffers { json.array! @buffers }
|
60
|
+
json.bufferViews { json.array! @buffer_views }
|
61
|
+
json.accessors { json.array! @accessors }
|
62
|
+
end.target!
|
63
|
+
)
|
64
|
+
end
|
65
|
+
|
66
|
+
# Parse is here for consistency with THREE.js's weird naming of exporter methods
|
67
|
+
alias_method :parse, :export
|
68
|
+
|
69
|
+
private
|
70
|
+
|
71
|
+
def add_mesh(mesh)
|
72
|
+
# Pack faces into an array
|
73
|
+
pack_string = (mesh.geometry.faces.count > (2**16)) ? "L<*" : "S<*"
|
74
|
+
faces = mesh.geometry.faces.map { |x| [x.a, x.b, x.c] }
|
75
|
+
data = faces.flatten.pack(pack_string)
|
76
|
+
# Add bufferView and accessor for faces
|
77
|
+
face_accessor_index = add_accessor(
|
78
|
+
buffer_view: add_buffer_view(
|
79
|
+
buffer: @buffers.count,
|
80
|
+
offset: 0,
|
81
|
+
length: data.length,
|
82
|
+
target: :element_array_buffer
|
83
|
+
),
|
84
|
+
component_type: (mesh.geometry.faces.count > (2**16)) ? :unsigned_int : :unsigned_short,
|
85
|
+
count: mesh.geometry.faces.count * 3,
|
86
|
+
type: "SCALAR",
|
87
|
+
min: 0,
|
88
|
+
max: mesh.geometry.vertices.count - 1
|
89
|
+
)
|
90
|
+
# Add padding to get to integer multiple of float size
|
91
|
+
padding = 4 - (data.length % 4)
|
92
|
+
data += Array.new(padding, 0).pack("C*")
|
93
|
+
# Pack vertices in as floats
|
94
|
+
offset = data.length
|
95
|
+
vertices = mesh.geometry.vertices.map(&:elements)
|
96
|
+
data += vertices.flatten.pack("f*")
|
97
|
+
# Add bufferView and accessor for vertices
|
98
|
+
mesh.geometry.compute_bounding_box
|
99
|
+
vertex_accessor_index = add_accessor(
|
100
|
+
buffer_view: add_buffer_view(
|
101
|
+
buffer: @buffers.count,
|
102
|
+
offset: offset,
|
103
|
+
length: data.length - offset,
|
104
|
+
target: :array_buffer
|
105
|
+
),
|
106
|
+
component_type: :float,
|
107
|
+
count: mesh.geometry.vertices.count,
|
108
|
+
type: "VEC3",
|
109
|
+
min: mesh.geometry.bounding_box.min.elements,
|
110
|
+
max: mesh.geometry.bounding_box.max.elements
|
111
|
+
)
|
112
|
+
# Encode and store in buffers
|
113
|
+
@buffers << {
|
114
|
+
uri: "data:application/octet-stream;base64," + Base64.strict_encode64(data),
|
115
|
+
byteLength: data.length
|
116
|
+
}
|
117
|
+
# Add mesh
|
118
|
+
mesh_index = @meshes.count
|
119
|
+
@meshes << {
|
120
|
+
"primitives" => [
|
121
|
+
{
|
122
|
+
"attributes" => {
|
123
|
+
"POSITION" => vertex_accessor_index
|
124
|
+
},
|
125
|
+
"indices" => face_accessor_index
|
126
|
+
}
|
127
|
+
]
|
128
|
+
}
|
129
|
+
# Add node
|
130
|
+
index = @nodes.count
|
131
|
+
@nodes << {
|
132
|
+
mesh: mesh_index
|
133
|
+
}
|
134
|
+
index
|
135
|
+
end
|
136
|
+
|
137
|
+
def add_buffer_view(buffer:, offset:, length:, target: nil)
|
138
|
+
# Check args
|
139
|
+
raise ArgumentError.new("invalid GPU buffer target: #{target}") unless target.nil? || GPU_BUFFER_TYPES.keys.include?(target)
|
140
|
+
index = @buffer_views.count
|
141
|
+
@buffer_views << {
|
142
|
+
buffer: buffer,
|
143
|
+
byteOffset: offset,
|
144
|
+
byteLength: length,
|
145
|
+
target: GPU_BUFFER_TYPES[target]
|
146
|
+
}
|
147
|
+
index
|
148
|
+
end
|
149
|
+
|
150
|
+
def add_accessor(buffer_view:, component_type:, count:, type:, min:, max:, offset: 0)
|
151
|
+
# Check args
|
152
|
+
raise ArgumentError.new("invalid component type: #{component_type}") unless COMPONENT_TYPES.key?(component_type)
|
153
|
+
raise ArgumentError.new("invalid element type: #{type}") unless ELEMENT_TYPES.include?(type)
|
154
|
+
# Add data
|
155
|
+
index = @accessors.count
|
156
|
+
@accessors << {
|
157
|
+
bufferView: buffer_view,
|
158
|
+
byteOffset: offset,
|
159
|
+
componentType: COMPONENT_TYPES[component_type],
|
160
|
+
count: count,
|
161
|
+
type: type,
|
162
|
+
min: Array(min),
|
163
|
+
max: Array(max)
|
164
|
+
}
|
165
|
+
index
|
166
|
+
end
|
167
|
+
end
|
168
|
+
end
|
data/lib/mittsu/gltf.rb
ADDED
metadata
ADDED
@@ -0,0 +1,149 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: mittsu-gltf
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- James Smith
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2024-12-12 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: mittsu
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0.4'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0.4'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: jbuilder
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '2.13'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '2.13'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rake
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '13.2'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '13.2'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rspec
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '3.13'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '3.13'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: standard
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '1.41'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '1.41'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: rubocop-rspec
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - "~>"
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '3.2'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - "~>"
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '3.2'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: rubocop-rake
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - "~>"
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0.6'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - "~>"
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0.6'
|
111
|
+
description: GLTF file support for Mittsu
|
112
|
+
email:
|
113
|
+
- james@floppy.org.uk
|
114
|
+
executables: []
|
115
|
+
extensions: []
|
116
|
+
extra_rdoc_files: []
|
117
|
+
files:
|
118
|
+
- README.md
|
119
|
+
- Rakefile
|
120
|
+
- lib/mittsu/gltf.rb
|
121
|
+
- lib/mittsu/gltf/exporter.rb
|
122
|
+
- lib/mittsu/gltf/version.rb
|
123
|
+
homepage: https://github.com/manyfold3d/mittsu-gltf
|
124
|
+
licenses:
|
125
|
+
- MIT
|
126
|
+
metadata:
|
127
|
+
homepage_uri: https://github.com/manyfold3d/mittsu-gltf
|
128
|
+
source_code_uri: https://github.com/manyfold3d/mittsu-gltf
|
129
|
+
changelog_uri: https://github.com/manyfold3d/mittsu-gltf/releases
|
130
|
+
post_install_message:
|
131
|
+
rdoc_options: []
|
132
|
+
require_paths:
|
133
|
+
- lib
|
134
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
135
|
+
requirements:
|
136
|
+
- - "~>"
|
137
|
+
- !ruby/object:Gem::Version
|
138
|
+
version: '3.1'
|
139
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
140
|
+
requirements:
|
141
|
+
- - ">="
|
142
|
+
- !ruby/object:Gem::Version
|
143
|
+
version: '0'
|
144
|
+
requirements: []
|
145
|
+
rubygems_version: 3.5.11
|
146
|
+
signing_key:
|
147
|
+
specification_version: 4
|
148
|
+
summary: GLTF file support for Mittsu
|
149
|
+
test_files: []
|