@needle-tools/engine 3.3.0-alpha → 3.5.0-alpha
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +19 -0
- package/dist/needle-engine.js +62620 -61118
- package/dist/needle-engine.min.js +434 -410
- package/dist/needle-engine.umd.cjs +435 -411
- package/lib/engine/api.d.ts +1 -0
- package/lib/engine/api.js +1 -0
- package/lib/engine/api.js.map +1 -1
- package/lib/engine/codegen/register_types.js +50 -2
- package/lib/engine/codegen/register_types.js.map +1 -1
- package/lib/engine/engine_context.d.ts +1 -1
- package/lib/engine/engine_context.js +21 -15
- package/lib/engine/engine_context.js.map +1 -1
- package/lib/engine/engine_context_registry.d.ts +5 -3
- package/lib/engine/engine_context_registry.js +10 -2
- package/lib/engine/engine_context_registry.js.map +1 -1
- package/lib/engine/engine_element.js.map +1 -1
- package/lib/engine/engine_element_loading.js +2 -3
- package/lib/engine/engine_element_loading.js.map +1 -1
- package/lib/engine/engine_gameobject.d.ts +1 -1
- package/lib/engine/engine_gameobject.js +4 -2
- package/lib/engine/engine_gameobject.js.map +1 -1
- package/lib/engine/engine_input.d.ts +2 -2
- package/lib/engine/engine_physics.d.ts +20 -93
- package/lib/engine/engine_physics.js +20 -892
- package/lib/engine/engine_physics.js.map +1 -1
- package/lib/engine/engine_physics.types.js.map +1 -1
- package/lib/engine/engine_physics_rapier.d.ts +103 -0
- package/lib/engine/engine_physics_rapier.js +1003 -0
- package/lib/engine/engine_physics_rapier.js.map +1 -0
- package/lib/engine/engine_three_utils.js +2 -2
- package/lib/engine/engine_three_utils.js.map +1 -1
- package/lib/engine/engine_types.d.ts +50 -1
- package/lib/engine/engine_types.js +8 -0
- package/lib/engine/engine_types.js.map +1 -1
- package/lib/engine-components/Animation.js +4 -0
- package/lib/engine-components/Animation.js.map +1 -1
- package/lib/engine-components/Collider.js +6 -6
- package/lib/engine-components/Collider.js.map +1 -1
- package/lib/engine-components/Joints.js +2 -2
- package/lib/engine-components/Joints.js.map +1 -1
- package/lib/engine-components/RigidBody.d.ts +0 -1
- package/lib/engine-components/RigidBody.js +24 -30
- package/lib/engine-components/RigidBody.js.map +1 -1
- package/lib/engine-components/codegen/components.d.ts +25 -1
- package/lib/engine-components/codegen/components.js +25 -1
- package/lib/engine-components/codegen/components.js.map +1 -1
- package/lib/engine-components/export/usdz/Extension.d.ts +4 -4
- package/lib/engine-components/export/usdz/ThreeUSDZExporter.d.ts +86 -0
- package/lib/engine-components/export/usdz/ThreeUSDZExporter.js +858 -0
- package/lib/engine-components/export/usdz/ThreeUSDZExporter.js.map +1 -0
- package/lib/engine-components/export/usdz/USDZExporter.d.ts +6 -3
- package/lib/engine-components/export/usdz/USDZExporter.js +34 -11
- package/lib/engine-components/export/usdz/USDZExporter.js.map +1 -1
- package/lib/engine-components/export/usdz/extensions/Animation.d.ts +15 -15
- package/lib/engine-components/export/usdz/extensions/Animation.js +24 -29
- package/lib/engine-components/export/usdz/extensions/Animation.js.map +1 -1
- package/lib/engine-components/export/usdz/extensions/DocumentExtension.d.ts +5 -0
- package/lib/engine-components/export/usdz/extensions/DocumentExtension.js +7 -0
- package/lib/engine-components/export/usdz/extensions/DocumentExtension.js.map +1 -0
- package/lib/engine-components/export/usdz/extensions/USDZText.d.ts +47 -0
- package/lib/engine-components/export/usdz/extensions/USDZText.js +114 -0
- package/lib/engine-components/export/usdz/extensions/USDZText.js.map +1 -0
- package/lib/engine-components/export/usdz/extensions/behavior/Actions.d.ts +30 -0
- package/lib/engine-components/export/usdz/extensions/behavior/Actions.js +89 -0
- package/lib/engine-components/export/usdz/extensions/behavior/Actions.js.map +1 -0
- package/lib/engine-components/export/usdz/extensions/behavior/Behaviour.d.ts +23 -0
- package/lib/engine-components/export/usdz/extensions/behavior/Behaviour.js +114 -0
- package/lib/engine-components/export/usdz/extensions/behavior/Behaviour.js.map +1 -0
- package/lib/engine-components/export/usdz/extensions/behavior/BehaviourComponents.d.ts +102 -0
- package/lib/engine-components/export/usdz/extensions/behavior/BehaviourComponents.js +458 -0
- package/lib/engine-components/export/usdz/extensions/behavior/BehaviourComponents.js.map +1 -0
- package/lib/engine-components/export/usdz/extensions/behavior/BehavioursBuilder.d.ts +111 -0
- package/lib/engine-components/export/usdz/extensions/behavior/BehavioursBuilder.js +409 -0
- package/lib/engine-components/export/usdz/extensions/behavior/BehavioursBuilder.js.map +1 -0
- package/lib/engine-components/postprocessing/PostProcessingHandler.js.map +1 -1
- package/lib/engine-components/ui/BaseUIComponent.d.ts +2 -0
- package/lib/engine-components/ui/BaseUIComponent.js +6 -0
- package/lib/engine-components/ui/BaseUIComponent.js.map +1 -1
- package/lib/engine-components/ui/Canvas.d.ts +11 -1
- package/lib/engine-components/ui/Canvas.js +72 -3
- package/lib/engine-components/ui/Canvas.js.map +1 -1
- package/lib/engine-components/ui/Graphic.js.map +1 -1
- package/lib/engine-components/ui/Image.js +4 -4
- package/lib/engine-components/ui/Image.js.map +1 -1
- package/lib/engine-components/ui/Interfaces.d.ts +11 -0
- package/lib/engine-components/ui/Interfaces.js +11 -0
- package/lib/engine-components/ui/Interfaces.js.map +1 -1
- package/lib/engine-components/ui/Layout.d.ts +65 -3
- package/lib/engine-components/ui/Layout.js +304 -3
- package/lib/engine-components/ui/Layout.js.map +1 -1
- package/lib/engine-components/ui/RectTransform.d.ts +8 -7
- package/lib/engine-components/ui/RectTransform.js +66 -36
- package/lib/engine-components/ui/RectTransform.js.map +1 -1
- package/lib/engine-components/utils/LookAt.d.ts +7 -1
- package/lib/engine-components/utils/LookAt.js +43 -6
- package/lib/engine-components/utils/LookAt.js.map +1 -1
- package/lib/engine-components/webxr/WebXRImageTracking.d.ts +4 -3
- package/lib/engine-components/webxr/WebXRImageTracking.js +81 -25
- package/lib/engine-components/webxr/WebXRImageTracking.js.map +1 -1
- package/lib/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/plugins/vite/config.js +2 -1
- package/plugins/vite/defines.js +30 -0
- package/plugins/vite/dependency-watcher.js +173 -0
- package/plugins/vite/editor-connection.js +37 -39
- package/plugins/vite/index.js +5 -1
- package/plugins/vite/reload.js +16 -3
- package/src/engine/api.ts +1 -0
- package/src/engine/codegen/register_types.js +50 -2
- package/src/engine/engine_context.ts +32 -23
- package/src/engine/engine_context_registry.ts +13 -6
- package/src/engine/engine_element.ts +2 -1
- package/src/engine/engine_element_loading.ts +2 -3
- package/src/engine/engine_gameobject.ts +3 -2
- package/src/engine/engine_input.ts +2 -2
- package/src/engine/engine_physics.ts +25 -1020
- package/src/engine/engine_physics.types.ts +1 -3
- package/src/engine/engine_physics_rapier.ts +1127 -0
- package/src/engine/engine_three_utils.ts +2 -2
- package/src/engine/engine_types.ts +66 -4
- package/src/engine-components/Animation.ts +4 -0
- package/src/engine-components/Collider.ts +6 -6
- package/src/engine-components/Joints.ts +2 -2
- package/src/engine-components/RigidBody.ts +24 -31
- package/src/engine-components/codegen/components.ts +25 -1
- package/src/engine-components/export/usdz/Extension.ts +4 -5
- package/src/engine-components/export/usdz/ThreeUSDZExporter.ts +1312 -0
- package/src/engine-components/export/usdz/USDZExporter.ts +39 -17
- package/src/engine-components/export/usdz/extensions/Animation.ts +37 -45
- package/src/engine-components/export/usdz/extensions/DocumentExtension.ts +10 -0
- package/src/engine-components/export/usdz/extensions/USDZText.ts +142 -0
- package/src/engine-components/export/usdz/extensions/behavior/Actions.ts +99 -0
- package/src/engine-components/export/usdz/extensions/behavior/Behaviour.ts +181 -0
- package/src/engine-components/export/usdz/extensions/behavior/BehaviourComponents.ts +545 -0
- package/src/engine-components/export/usdz/extensions/behavior/BehavioursBuilder.ts +459 -0
- package/src/engine-components/postprocessing/PostProcessingHandler.ts +1 -1
- package/src/engine-components/ui/BaseUIComponent.ts +7 -1
- package/src/engine-components/ui/Canvas.ts +80 -5
- package/src/engine-components/ui/Graphic.ts +2 -0
- package/src/engine-components/ui/Image.ts +3 -3
- package/src/engine-components/ui/Interfaces.ts +30 -6
- package/src/engine-components/ui/Layout.ts +303 -4
- package/src/engine-components/ui/RectTransform.ts +67 -41
- package/src/engine-components/utils/LookAt.ts +60 -7
- package/src/engine-components/webxr/WebXRImageTracking.ts +100 -27
- package/lib/engine-components/export/usdz/types.d.ts +0 -34
- package/lib/engine-components/export/usdz/types.js +0 -2
- package/lib/engine-components/export/usdz/types.js.map +0 -1
- package/src/engine-components/export/usdz/types.ts +0 -39
|
@@ -0,0 +1,858 @@
|
|
|
1
|
+
import { PlaneGeometry, Texture, Uniform, PerspectiveCamera, Scene, Mesh, ShaderMaterial, WebGLRenderer, MathUtils, Matrix4, RepeatWrapping, MirroredRepeatWrapping, DoubleSide, sRGBEncoding, MeshPhysicalMaterial, } from 'three';
|
|
2
|
+
import * as fflate from 'three/examples/jsm/libs/fflate.module.js';
|
|
3
|
+
function makeNameSafe(str) {
|
|
4
|
+
str = str.replace(/[^a-zA-Z0-9_]/g, '');
|
|
5
|
+
// if str does not start with a-zA-Z_ add _ to the beginning
|
|
6
|
+
if (!str.match(/^[a-zA-Z_]/))
|
|
7
|
+
str = '_' + str;
|
|
8
|
+
return str;
|
|
9
|
+
}
|
|
10
|
+
class USDObject {
|
|
11
|
+
static USDObject_export_id = 0;
|
|
12
|
+
uuid;
|
|
13
|
+
name;
|
|
14
|
+
matrix;
|
|
15
|
+
_isDynamic;
|
|
16
|
+
get isDynamic() { return this._isDynamic; }
|
|
17
|
+
set isDynamic(value) { this._isDynamic = value; }
|
|
18
|
+
geometry;
|
|
19
|
+
material;
|
|
20
|
+
camera;
|
|
21
|
+
parent;
|
|
22
|
+
children = [];
|
|
23
|
+
_eventListeners;
|
|
24
|
+
mesh;
|
|
25
|
+
static createEmptyParent(object) {
|
|
26
|
+
const emptyParent = new USDObject(MathUtils.generateUUID(), object.name + '_empty_' + (USDObject.USDObject_export_id++), object.matrix);
|
|
27
|
+
const parent = object.parent;
|
|
28
|
+
parent.add(emptyParent);
|
|
29
|
+
emptyParent.add(object);
|
|
30
|
+
emptyParent.isDynamic = true;
|
|
31
|
+
object.matrix = new Matrix4().identity();
|
|
32
|
+
return emptyParent;
|
|
33
|
+
}
|
|
34
|
+
constructor(id, name, matrix, mesh = null, material = null, camera = null) {
|
|
35
|
+
this.uuid = id;
|
|
36
|
+
this.name = makeNameSafe(name);
|
|
37
|
+
this.matrix = matrix;
|
|
38
|
+
this.geometry = mesh;
|
|
39
|
+
this.material = material;
|
|
40
|
+
this.camera = camera;
|
|
41
|
+
this.parent = null;
|
|
42
|
+
this.children = [];
|
|
43
|
+
this._eventListeners = {};
|
|
44
|
+
this._isDynamic = false;
|
|
45
|
+
}
|
|
46
|
+
is(obj) {
|
|
47
|
+
if (!obj)
|
|
48
|
+
return false;
|
|
49
|
+
return this.uuid === obj.uuid;
|
|
50
|
+
}
|
|
51
|
+
isEmpty() {
|
|
52
|
+
return !this.geometry;
|
|
53
|
+
}
|
|
54
|
+
clone() {
|
|
55
|
+
const clone = new USDObject(MathUtils.generateUUID(), this.name, this.matrix, this.mesh, this.material);
|
|
56
|
+
clone.isDynamic = this.isDynamic;
|
|
57
|
+
return clone;
|
|
58
|
+
}
|
|
59
|
+
getPath() {
|
|
60
|
+
let current = this.parent;
|
|
61
|
+
let path = this.name;
|
|
62
|
+
while (current) {
|
|
63
|
+
path = current.name + '/' + path;
|
|
64
|
+
current = current.parent;
|
|
65
|
+
}
|
|
66
|
+
return '</' + path + '>';
|
|
67
|
+
}
|
|
68
|
+
add(child) {
|
|
69
|
+
if (child.parent) {
|
|
70
|
+
child.parent.remove(child);
|
|
71
|
+
}
|
|
72
|
+
child.parent = this;
|
|
73
|
+
this.children.push(child);
|
|
74
|
+
}
|
|
75
|
+
remove(child) {
|
|
76
|
+
const index = this.children.indexOf(child);
|
|
77
|
+
if (index >= 0) {
|
|
78
|
+
if (child.parent === this)
|
|
79
|
+
child.parent = null;
|
|
80
|
+
this.children.splice(index, 1);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
addEventListener(evt, listener) {
|
|
84
|
+
if (!this._eventListeners[evt])
|
|
85
|
+
this._eventListeners[evt] = [];
|
|
86
|
+
this._eventListeners[evt].push(listener);
|
|
87
|
+
}
|
|
88
|
+
removeEventListener(evt, listener) {
|
|
89
|
+
if (!this._eventListeners[evt])
|
|
90
|
+
return;
|
|
91
|
+
const index = this._eventListeners[evt].indexOf(listener);
|
|
92
|
+
if (index >= 0) {
|
|
93
|
+
this._eventListeners[evt].splice(index, 1);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
onSerialize(writer, context) {
|
|
97
|
+
const listeners = this._eventListeners['serialize'];
|
|
98
|
+
if (listeners)
|
|
99
|
+
listeners.forEach(listener => listener(writer, context));
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
class USDDocument extends USDObject {
|
|
103
|
+
stageLength;
|
|
104
|
+
get isDocumentRoot() {
|
|
105
|
+
return true;
|
|
106
|
+
}
|
|
107
|
+
get isDynamic() {
|
|
108
|
+
return false;
|
|
109
|
+
}
|
|
110
|
+
constructor() {
|
|
111
|
+
super(undefined, 'StageRoot', new Matrix4(), null, null, null);
|
|
112
|
+
this.children = [];
|
|
113
|
+
this.stageLength = 200;
|
|
114
|
+
}
|
|
115
|
+
add(child) {
|
|
116
|
+
child.parent = this;
|
|
117
|
+
this.children.push(child);
|
|
118
|
+
}
|
|
119
|
+
remove(child) {
|
|
120
|
+
const index = this.children.indexOf(child);
|
|
121
|
+
if (index >= 0) {
|
|
122
|
+
if (child.parent === this)
|
|
123
|
+
child.parent = null;
|
|
124
|
+
this.children.splice(index, 1);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
traverse(callback, current = null) {
|
|
128
|
+
if (current !== null)
|
|
129
|
+
callback(current);
|
|
130
|
+
else
|
|
131
|
+
current = this;
|
|
132
|
+
if (current.children) {
|
|
133
|
+
for (const child of current.children) {
|
|
134
|
+
this.traverse(callback, child);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
findById(uuid) {
|
|
139
|
+
let found = false;
|
|
140
|
+
function search(current) {
|
|
141
|
+
if (found)
|
|
142
|
+
return;
|
|
143
|
+
if (current.uuid === uuid) {
|
|
144
|
+
found = true;
|
|
145
|
+
return current;
|
|
146
|
+
}
|
|
147
|
+
if (current.children) {
|
|
148
|
+
for (const child of current.children) {
|
|
149
|
+
const res = search(child);
|
|
150
|
+
if (res)
|
|
151
|
+
return res;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
return search(this);
|
|
156
|
+
}
|
|
157
|
+
buildHeader() {
|
|
158
|
+
return `#usda 1.0
|
|
159
|
+
(
|
|
160
|
+
customLayerData = {
|
|
161
|
+
string creator = "Three.js USDZExporter"
|
|
162
|
+
}
|
|
163
|
+
defaultPrim = "${makeNameSafe(this.name)}"
|
|
164
|
+
metersPerUnit = 1
|
|
165
|
+
upAxis = "Y"
|
|
166
|
+
startTimeCode = 0
|
|
167
|
+
endTimeCode = ${this.stageLength}
|
|
168
|
+
timeCodesPerSecond = 60
|
|
169
|
+
framesPerSecond = 60
|
|
170
|
+
)
|
|
171
|
+
`;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
const newLine = '\n';
|
|
175
|
+
class USDWriter {
|
|
176
|
+
str;
|
|
177
|
+
indent;
|
|
178
|
+
constructor() {
|
|
179
|
+
this.str = '';
|
|
180
|
+
this.indent = 0;
|
|
181
|
+
}
|
|
182
|
+
clear() {
|
|
183
|
+
this.str = '';
|
|
184
|
+
this.indent = 0;
|
|
185
|
+
}
|
|
186
|
+
beginBlock(str) {
|
|
187
|
+
str = this.applyIndent(str);
|
|
188
|
+
this.str += str;
|
|
189
|
+
this.str += newLine;
|
|
190
|
+
this.str += this.applyIndent('{');
|
|
191
|
+
this.str += newLine;
|
|
192
|
+
this.indent += 1;
|
|
193
|
+
}
|
|
194
|
+
closeBlock() {
|
|
195
|
+
this.indent -= 1;
|
|
196
|
+
this.str += this.applyIndent('}') + newLine;
|
|
197
|
+
}
|
|
198
|
+
beginArray(str) {
|
|
199
|
+
str = this.applyIndent(str + ' = [');
|
|
200
|
+
this.str += str;
|
|
201
|
+
this.str += newLine;
|
|
202
|
+
this.indent += 1;
|
|
203
|
+
}
|
|
204
|
+
closeArray() {
|
|
205
|
+
this.indent -= 1;
|
|
206
|
+
this.str += this.applyIndent(']') + newLine;
|
|
207
|
+
}
|
|
208
|
+
appendLine(str = '') {
|
|
209
|
+
str = this.applyIndent(str);
|
|
210
|
+
this.str += str;
|
|
211
|
+
this.str += newLine;
|
|
212
|
+
}
|
|
213
|
+
toString() {
|
|
214
|
+
return this.str;
|
|
215
|
+
}
|
|
216
|
+
applyIndent(str) {
|
|
217
|
+
let indents = '';
|
|
218
|
+
for (let i = 0; i < this.indent; i++)
|
|
219
|
+
indents += '\t';
|
|
220
|
+
return indents + str;
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
class USDZExporterContext {
|
|
224
|
+
root;
|
|
225
|
+
exporter;
|
|
226
|
+
extensions;
|
|
227
|
+
materials;
|
|
228
|
+
textures;
|
|
229
|
+
files;
|
|
230
|
+
document;
|
|
231
|
+
output;
|
|
232
|
+
constructor(root, exporter, extensions) {
|
|
233
|
+
this.root = root;
|
|
234
|
+
this.exporter = exporter;
|
|
235
|
+
if (extensions)
|
|
236
|
+
this.extensions = extensions;
|
|
237
|
+
this.materials = {};
|
|
238
|
+
this.textures = {};
|
|
239
|
+
this.files = {};
|
|
240
|
+
this.document = new USDDocument();
|
|
241
|
+
this.output = '';
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
class USDZExporterOptions {
|
|
245
|
+
ar = { anchoring: { type: 'plane' } };
|
|
246
|
+
planeAnchoring = { alignment: 'horizontal' };
|
|
247
|
+
extensions = [];
|
|
248
|
+
}
|
|
249
|
+
class USDZExporter {
|
|
250
|
+
debug;
|
|
251
|
+
sceneAnchoringOptions = {};
|
|
252
|
+
extensions;
|
|
253
|
+
constructor() {
|
|
254
|
+
this.debug = false;
|
|
255
|
+
}
|
|
256
|
+
async parse(scene, options = new USDZExporterOptions()) {
|
|
257
|
+
options = Object.assign(new USDZExporterOptions(), options);
|
|
258
|
+
this.sceneAnchoringOptions = options;
|
|
259
|
+
// @ts-ignore
|
|
260
|
+
const context = new USDZExporterContext(scene, this, options.extensions);
|
|
261
|
+
this.extensions = context.extensions;
|
|
262
|
+
const files = context.files;
|
|
263
|
+
const modelFileName = 'model.usda';
|
|
264
|
+
// model file should be first in USDZ archive so we init it here
|
|
265
|
+
files[modelFileName] = null;
|
|
266
|
+
const materials = context.materials;
|
|
267
|
+
const textures = context.textures;
|
|
268
|
+
invokeAll(context, 'onBeforeBuildDocument');
|
|
269
|
+
traverseVisible(scene, context.document, context);
|
|
270
|
+
invokeAll(context, 'onAfterBuildDocument');
|
|
271
|
+
parseDocument(context);
|
|
272
|
+
invokeAll(context, 'onAfterSerialize');
|
|
273
|
+
context.output += buildMaterials(materials, textures);
|
|
274
|
+
const header = context.document.buildHeader();
|
|
275
|
+
const final = header + '\n' + context.output;
|
|
276
|
+
// full output file
|
|
277
|
+
if (this.debug)
|
|
278
|
+
console.log(final);
|
|
279
|
+
files[modelFileName] = fflate.strToU8(final);
|
|
280
|
+
context.output = '';
|
|
281
|
+
for (const id in textures) {
|
|
282
|
+
let texture = textures[id];
|
|
283
|
+
const isRGBA = texture.format === 1023;
|
|
284
|
+
if (texture.isCompressedTexture) {
|
|
285
|
+
texture = copyTexture(texture);
|
|
286
|
+
}
|
|
287
|
+
// TODO add readback options for textures that don't have texture.image
|
|
288
|
+
const canvas = await imageToCanvas(texture.image);
|
|
289
|
+
if (canvas) {
|
|
290
|
+
const blob = await new Promise(resolve => canvas.toBlob(resolve, isRGBA ? 'image/png' : 'image/jpeg', 1));
|
|
291
|
+
files[`textures/Texture_${id}.${isRGBA ? 'png' : 'jpg'}`] = new Uint8Array(await blob.arrayBuffer());
|
|
292
|
+
}
|
|
293
|
+
else {
|
|
294
|
+
console.warn('Can`t export texture: ', texture);
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
// 64 byte alignment
|
|
298
|
+
// https://github.com/101arrowz/fflate/issues/39#issuecomment-777263109
|
|
299
|
+
let offset = 0;
|
|
300
|
+
for (const filename in files) {
|
|
301
|
+
const file = files[filename];
|
|
302
|
+
const headerSize = 34 + filename.length;
|
|
303
|
+
offset += headerSize;
|
|
304
|
+
const offsetMod64 = offset & 63;
|
|
305
|
+
if (offsetMod64 !== 4) {
|
|
306
|
+
const padLength = 64 - offsetMod64;
|
|
307
|
+
const padding = new Uint8Array(padLength);
|
|
308
|
+
files[filename] = [file, { extra: { 12345: padding } }];
|
|
309
|
+
}
|
|
310
|
+
offset = file.length;
|
|
311
|
+
}
|
|
312
|
+
return fflate.zipSync(files, { level: 0 });
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
function traverseVisible(object, parentModel, context) {
|
|
316
|
+
if (!object.visible)
|
|
317
|
+
return;
|
|
318
|
+
let model = undefined;
|
|
319
|
+
const geometry = object.geometry;
|
|
320
|
+
const material = object.material;
|
|
321
|
+
if (object.isMesh && material && (material.isMeshStandardMaterial || material.isMeshBasicMaterial) && !object.isSkinnedMesh) {
|
|
322
|
+
const name = getObjectId(object);
|
|
323
|
+
model = new USDObject(object.uuid, name, object.matrix, geometry, material);
|
|
324
|
+
}
|
|
325
|
+
else if (object.isCamera) {
|
|
326
|
+
const name = getObjectId(object);
|
|
327
|
+
model = new USDObject(object.uuid, name, object.matrix, undefined, undefined, object);
|
|
328
|
+
}
|
|
329
|
+
else {
|
|
330
|
+
const name = getObjectId(object);
|
|
331
|
+
model = new USDObject(object.uuid, name, object.matrix);
|
|
332
|
+
}
|
|
333
|
+
if (model) {
|
|
334
|
+
if (parentModel) {
|
|
335
|
+
parentModel.add(model);
|
|
336
|
+
}
|
|
337
|
+
parentModel = model;
|
|
338
|
+
if (context.extensions) {
|
|
339
|
+
for (const ext of context.extensions) {
|
|
340
|
+
if (ext.onExportObject)
|
|
341
|
+
ext.onExportObject.call(ext, object, model, context);
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
else {
|
|
346
|
+
const name = getObjectId(object);
|
|
347
|
+
const empty = new USDObject(object.uuid, name, object.matrix);
|
|
348
|
+
if (parentModel) {
|
|
349
|
+
parentModel.add(empty);
|
|
350
|
+
}
|
|
351
|
+
parentModel = empty;
|
|
352
|
+
}
|
|
353
|
+
for (const ch of object.children) {
|
|
354
|
+
traverseVisible(ch, parentModel, context);
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
function parseDocument(context) {
|
|
358
|
+
for (const child of context.document.children) {
|
|
359
|
+
addResources(child, context);
|
|
360
|
+
}
|
|
361
|
+
const writer = new USDWriter();
|
|
362
|
+
writer.beginBlock(`def Xform "${context.document.name}"`);
|
|
363
|
+
writer.beginBlock(`def Scope "Scenes" (
|
|
364
|
+
kind = "sceneLibrary"
|
|
365
|
+
)`);
|
|
366
|
+
writer.beginBlock(`def Xform "Scene" (
|
|
367
|
+
apiSchemas = ["Preliminary_AnchoringAPI"]
|
|
368
|
+
customData = {
|
|
369
|
+
bool preliminary_collidesWithEnvironment = 0
|
|
370
|
+
string sceneName = "Scene"
|
|
371
|
+
}
|
|
372
|
+
sceneName = "Scene"
|
|
373
|
+
)`);
|
|
374
|
+
writer.appendLine(`token preliminary:anchoring:type = "${context.exporter.sceneAnchoringOptions.ar.anchoring.type}"`);
|
|
375
|
+
if (context.exporter.sceneAnchoringOptions.ar.anchoring.type === 'plane')
|
|
376
|
+
writer.appendLine(`token preliminary:planeAnchoring:alignment = "${context.exporter.sceneAnchoringOptions.planeAnchoring.alignment}"`);
|
|
377
|
+
// bit hacky as we don't have a callback here yet. Relies on the fact that the image is named identical in the ImageTracking extension.
|
|
378
|
+
if (context.exporter.sceneAnchoringOptions.ar.anchoring.type === 'image')
|
|
379
|
+
writer.appendLine(`rel preliminary:imageAnchoring:referenceImage = </${context.document.name}/Scenes/Scene/AnchoringReferenceImage>`);
|
|
380
|
+
writer.appendLine();
|
|
381
|
+
for (const child of context.document.children) {
|
|
382
|
+
buildXform(child, writer, context);
|
|
383
|
+
}
|
|
384
|
+
invokeAll(context, 'onAfterHierarchy', writer);
|
|
385
|
+
writer.closeBlock();
|
|
386
|
+
writer.closeBlock();
|
|
387
|
+
writer.closeBlock();
|
|
388
|
+
context.output += writer.toString();
|
|
389
|
+
}
|
|
390
|
+
function addResources(object, context) {
|
|
391
|
+
const geometry = object.geometry;
|
|
392
|
+
let material = object.material;
|
|
393
|
+
if (geometry) {
|
|
394
|
+
if (material.isMeshStandardMaterial) { // || material.isMeshBasicMaterial // TODO convert unlit to lit+emissive
|
|
395
|
+
const geometryFileName = 'geometries/Geometry_' + geometry.id + '.usd';
|
|
396
|
+
if (!(geometryFileName in context.files)) {
|
|
397
|
+
const meshObject = buildMeshObject(geometry);
|
|
398
|
+
context.files[geometryFileName] = buildUSDFileAsString(meshObject, context);
|
|
399
|
+
}
|
|
400
|
+
if (!(material.uuid in context.materials)) {
|
|
401
|
+
context.materials[material.uuid] = material;
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
else {
|
|
405
|
+
console.warn('THREE.USDZExporter: Unsupported material type (USDZ only supports MeshStandardMaterial)', name);
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
for (const ch of object.children) {
|
|
409
|
+
addResources(ch, context);
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
function invokeAll(context, name, writer = null) {
|
|
413
|
+
if (context.extensions) {
|
|
414
|
+
for (const ext of context.extensions) {
|
|
415
|
+
if (typeof ext[name] === 'function')
|
|
416
|
+
ext[name](context, writer);
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
function copyTexture(texture) {
|
|
421
|
+
const geometry = new PlaneGeometry(2, 2, 1, 1);
|
|
422
|
+
const material = new ShaderMaterial({
|
|
423
|
+
uniforms: {
|
|
424
|
+
blitTexture: new Uniform(texture),
|
|
425
|
+
},
|
|
426
|
+
defines: {
|
|
427
|
+
IS_SRGB: texture.encoding == sRGBEncoding,
|
|
428
|
+
},
|
|
429
|
+
vertexShader: `
|
|
430
|
+
varying vec2 vUv;
|
|
431
|
+
void main(){
|
|
432
|
+
vUv = uv;
|
|
433
|
+
vUv.y = 1. - vUv.y;
|
|
434
|
+
gl_Position = vec4(position.xy * 1.0,0.,.999999);
|
|
435
|
+
}`,
|
|
436
|
+
fragmentShader: `
|
|
437
|
+
uniform sampler2D blitTexture;
|
|
438
|
+
varying vec2 vUv;
|
|
439
|
+
|
|
440
|
+
// took from threejs 05fc79cd52b79e8c3e8dec1e7dca72c5c39983a4
|
|
441
|
+
vec4 conv_LinearTosRGB( in vec4 value ) {
|
|
442
|
+
return vec4( mix( pow( value.rgb, vec3( 0.41666 ) ) * 1.055 - vec3( 0.055 ), value.rgb * 12.92, vec3( lessThanEqual( value.rgb, vec3( 0.0031308 ) ) ) ), value.a );
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
void main(){
|
|
446
|
+
gl_FragColor = vec4(vUv.xy, 0, 1);
|
|
447
|
+
|
|
448
|
+
#ifdef IS_SRGB
|
|
449
|
+
gl_FragColor = conv_LinearTosRGB( texture2D( blitTexture, vUv) );
|
|
450
|
+
#else
|
|
451
|
+
gl_FragColor = texture2D( blitTexture, vUv);
|
|
452
|
+
#endif
|
|
453
|
+
}`
|
|
454
|
+
});
|
|
455
|
+
const mesh = new Mesh(geometry, material);
|
|
456
|
+
mesh.frustumCulled = false;
|
|
457
|
+
const cam = new PerspectiveCamera();
|
|
458
|
+
const scene = new Scene();
|
|
459
|
+
scene.add(mesh);
|
|
460
|
+
const renderer = new WebGLRenderer({ antialias: false });
|
|
461
|
+
renderer.setSize(texture.image.width, texture.image.height);
|
|
462
|
+
renderer.clear();
|
|
463
|
+
renderer.render(scene, cam);
|
|
464
|
+
const tex = new Texture(renderer.domElement);
|
|
465
|
+
tex.encoding = texture.encoding;
|
|
466
|
+
return tex;
|
|
467
|
+
}
|
|
468
|
+
function isImageBitmap(image) {
|
|
469
|
+
return (typeof HTMLImageElement !== 'undefined' && image instanceof HTMLImageElement) ||
|
|
470
|
+
(typeof HTMLCanvasElement !== 'undefined' && image instanceof HTMLCanvasElement) ||
|
|
471
|
+
(typeof OffscreenCanvas !== 'undefined' && image instanceof OffscreenCanvas) ||
|
|
472
|
+
(typeof ImageBitmap !== 'undefined' && image instanceof ImageBitmap);
|
|
473
|
+
}
|
|
474
|
+
async function imageToCanvas(image, color = undefined, flipY = false) {
|
|
475
|
+
if (isImageBitmap(image)) {
|
|
476
|
+
const scale = 1024 / Math.max(image.width, image.height);
|
|
477
|
+
const canvas = document.createElement('canvas');
|
|
478
|
+
canvas.width = image.width * Math.min(1, scale);
|
|
479
|
+
canvas.height = image.height * Math.min(1, scale);
|
|
480
|
+
const context = canvas.getContext('2d');
|
|
481
|
+
if (!context)
|
|
482
|
+
throw new Error('Could not get canvas 2D context');
|
|
483
|
+
if (flipY === true) {
|
|
484
|
+
context.translate(0, canvas.height);
|
|
485
|
+
context.scale(1, -1);
|
|
486
|
+
}
|
|
487
|
+
context.drawImage(image, 0, 0, canvas.width, canvas.height);
|
|
488
|
+
// TODO remove, not used anymore
|
|
489
|
+
if (color !== undefined) {
|
|
490
|
+
const hex = parseInt(color, 16);
|
|
491
|
+
const r = (hex >> 16 & 255) / 255;
|
|
492
|
+
const g = (hex >> 8 & 255) / 255;
|
|
493
|
+
const b = (hex & 255) / 255;
|
|
494
|
+
const imagedata = context.getImageData(0, 0, canvas.width, canvas.height);
|
|
495
|
+
const data = imagedata.data;
|
|
496
|
+
for (let i = 0; i < data.length; i += 4) {
|
|
497
|
+
data[i + 0] = data[i + 0] * r;
|
|
498
|
+
data[i + 1] = data[i + 1] * g;
|
|
499
|
+
data[i + 2] = data[i + 2] * b;
|
|
500
|
+
}
|
|
501
|
+
context.putImageData(imagedata, 0, 0);
|
|
502
|
+
}
|
|
503
|
+
return canvas;
|
|
504
|
+
}
|
|
505
|
+
else {
|
|
506
|
+
throw new Error('THREE.USDZExporter: No valid image data found. Unable to process texture.');
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
//
|
|
510
|
+
const PRECISION = 7;
|
|
511
|
+
function buildHeader() {
|
|
512
|
+
return `#usda 1.0
|
|
513
|
+
(
|
|
514
|
+
customLayerData = {
|
|
515
|
+
string creator = "Three.js USDZExporter"
|
|
516
|
+
}
|
|
517
|
+
metersPerUnit = 1
|
|
518
|
+
upAxis = "Y"
|
|
519
|
+
)
|
|
520
|
+
`;
|
|
521
|
+
}
|
|
522
|
+
function buildUSDFileAsString(dataToInsert, _context) {
|
|
523
|
+
let output = buildHeader();
|
|
524
|
+
output += dataToInsert;
|
|
525
|
+
return fflate.strToU8(output);
|
|
526
|
+
}
|
|
527
|
+
function getObjectId(object) {
|
|
528
|
+
return object.name.replace(/[-<>\(\)\[\]§$%&\/\\\=\?\,\;]/g, '') + '_' + object.id;
|
|
529
|
+
}
|
|
530
|
+
// Xform
|
|
531
|
+
export function buildXform(model, writer, context) {
|
|
532
|
+
const matrix = model.matrix;
|
|
533
|
+
const geometry = model.geometry;
|
|
534
|
+
const material = model.material;
|
|
535
|
+
const camera = model.camera;
|
|
536
|
+
const name = model.name;
|
|
537
|
+
const transform = buildMatrix(matrix);
|
|
538
|
+
if (matrix.determinant() < 0) {
|
|
539
|
+
console.warn('THREE.USDZExporter: USDZ does not support negative scales', name);
|
|
540
|
+
}
|
|
541
|
+
if (geometry)
|
|
542
|
+
writer.beginBlock(`def Xform "${name}" (
|
|
543
|
+
prepend references = @./geometries/Geometry_${geometry.id}.usd@</Geometry>
|
|
544
|
+
prepend apiSchemas = ["MaterialBindingAPI"]
|
|
545
|
+
)`);
|
|
546
|
+
else if (camera)
|
|
547
|
+
writer.beginBlock(`def Camera "${name}"`);
|
|
548
|
+
else
|
|
549
|
+
writer.beginBlock(`def Xform "${name}"`);
|
|
550
|
+
if (material)
|
|
551
|
+
writer.appendLine(`rel material:binding = </Materials/Material_${material.id}>`);
|
|
552
|
+
writer.appendLine(`matrix4d xformOp:transform = ${transform}`);
|
|
553
|
+
writer.appendLine('uniform token[] xformOpOrder = ["xformOp:transform"]');
|
|
554
|
+
if (camera) {
|
|
555
|
+
if (camera.isOrthographicCamera) {
|
|
556
|
+
writer.appendLine(`float2 clippingRange = (${camera.near}, ${camera.far})`);
|
|
557
|
+
writer.appendLine(`float horizontalAperture = ${((Math.abs(camera.left) + Math.abs(camera.right)) * 10).toPrecision(PRECISION)}`);
|
|
558
|
+
writer.appendLine(`float verticalAperture = ${((Math.abs(camera.top) + Math.abs(camera.bottom)) * 10).toPrecision(PRECISION)}`);
|
|
559
|
+
writer.appendLine('token projection = "orthographic"');
|
|
560
|
+
}
|
|
561
|
+
else {
|
|
562
|
+
writer.appendLine(`float2 clippingRange = (${camera.near.toPrecision(PRECISION)}, ${camera.far.toPrecision(PRECISION)})`);
|
|
563
|
+
writer.appendLine(`float focalLength = ${camera.getFocalLength().toPrecision(PRECISION)}`);
|
|
564
|
+
writer.appendLine(`float focusDistance = ${camera.focus.toPrecision(PRECISION)}`);
|
|
565
|
+
writer.appendLine(`float horizontalAperture = ${camera.getFilmWidth().toPrecision(PRECISION)}`);
|
|
566
|
+
writer.appendLine('token projection = "perspective"');
|
|
567
|
+
writer.appendLine(`float verticalAperture = ${camera.getFilmHeight().toPrecision(PRECISION)}`);
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
if (model.onSerialize) {
|
|
571
|
+
model.onSerialize(writer, context);
|
|
572
|
+
}
|
|
573
|
+
if (model.children) {
|
|
574
|
+
writer.appendLine();
|
|
575
|
+
for (const ch of model.children) {
|
|
576
|
+
buildXform(ch, writer, context);
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
writer.closeBlock();
|
|
580
|
+
}
|
|
581
|
+
function fn(num) {
|
|
582
|
+
return num.toFixed(10);
|
|
583
|
+
}
|
|
584
|
+
function buildMatrix(matrix) {
|
|
585
|
+
const array = matrix.elements;
|
|
586
|
+
return `( ${buildMatrixRow(array, 0)}, ${buildMatrixRow(array, 4)}, ${buildMatrixRow(array, 8)}, ${buildMatrixRow(array, 12)} )`;
|
|
587
|
+
}
|
|
588
|
+
function buildMatrixRow(array, offset) {
|
|
589
|
+
return `(${fn(array[offset + 0])}, ${fn(array[offset + 1])}, ${fn(array[offset + 2])}, ${fn(array[offset + 3])})`;
|
|
590
|
+
}
|
|
591
|
+
// Mesh
|
|
592
|
+
function buildMeshObject(geometry) {
|
|
593
|
+
const mesh = buildMesh(geometry);
|
|
594
|
+
return `
|
|
595
|
+
def "Geometry"
|
|
596
|
+
{
|
|
597
|
+
${mesh}
|
|
598
|
+
}
|
|
599
|
+
`;
|
|
600
|
+
}
|
|
601
|
+
function buildMesh(geometry) {
|
|
602
|
+
const name = 'Geometry';
|
|
603
|
+
const attributes = geometry.attributes;
|
|
604
|
+
const count = attributes.position.count;
|
|
605
|
+
return `
|
|
606
|
+
def Mesh "${name}"
|
|
607
|
+
{
|
|
608
|
+
int[] faceVertexCounts = [${buildMeshVertexCount(geometry)}]
|
|
609
|
+
int[] faceVertexIndices = [${buildMeshVertexIndices(geometry)}]
|
|
610
|
+
normal3f[] normals = [${buildVector3Array(attributes.normal, count)}] (
|
|
611
|
+
interpolation = "vertex"
|
|
612
|
+
)
|
|
613
|
+
point3f[] points = [${buildVector3Array(attributes.position, count)}]
|
|
614
|
+
${attributes.uv ?
|
|
615
|
+
`texCoord2f[] primvars:st = [${buildVector2Array(attributes.uv, count)}] (
|
|
616
|
+
interpolation = "vertex"
|
|
617
|
+
)` : ''}
|
|
618
|
+
${attributes.uv2 ?
|
|
619
|
+
`texCoord2f[] primvars:st2 = [${buildVector2Array(attributes.uv2, count)}] (
|
|
620
|
+
interpolation = "vertex"
|
|
621
|
+
)` : ''}
|
|
622
|
+
uniform token subdivisionScheme = "none"
|
|
623
|
+
}
|
|
624
|
+
`;
|
|
625
|
+
}
|
|
626
|
+
function buildMeshVertexCount(geometry) {
|
|
627
|
+
const count = geometry.index !== null ? geometry.index.count : geometry.attributes.position.count;
|
|
628
|
+
return Array(count / 3).fill(3).join(', ');
|
|
629
|
+
}
|
|
630
|
+
function buildMeshVertexIndices(geometry) {
|
|
631
|
+
const index = geometry.index;
|
|
632
|
+
const array = [];
|
|
633
|
+
if (index !== null) {
|
|
634
|
+
for (let i = 0; i < index.count; i++) {
|
|
635
|
+
array.push(index.getX(i));
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
else {
|
|
639
|
+
const length = geometry.attributes.position.count;
|
|
640
|
+
for (let i = 0; i < length; i++) {
|
|
641
|
+
array.push(i);
|
|
642
|
+
}
|
|
643
|
+
}
|
|
644
|
+
return array.join(', ');
|
|
645
|
+
}
|
|
646
|
+
function buildVector3Array(attribute, count) {
|
|
647
|
+
if (attribute === undefined) {
|
|
648
|
+
console.warn('USDZExporter: Normals missing.');
|
|
649
|
+
return Array(count).fill('(0, 0, 0)').join(', ');
|
|
650
|
+
}
|
|
651
|
+
const array = [];
|
|
652
|
+
for (let i = 0; i < attribute.count; i++) {
|
|
653
|
+
const x = attribute.getX(i);
|
|
654
|
+
const y = attribute.getY(i);
|
|
655
|
+
const z = attribute.getZ(i);
|
|
656
|
+
array.push(`(${x.toPrecision(PRECISION)}, ${y.toPrecision(PRECISION)}, ${z.toPrecision(PRECISION)})`);
|
|
657
|
+
}
|
|
658
|
+
return array.join(', ');
|
|
659
|
+
}
|
|
660
|
+
function buildVector2Array(attribute, count) {
|
|
661
|
+
if (attribute === undefined) {
|
|
662
|
+
console.warn('USDZExporter: UVs missing.');
|
|
663
|
+
return Array(count).fill('(0, 0)').join(', ');
|
|
664
|
+
}
|
|
665
|
+
const array = [];
|
|
666
|
+
for (let i = 0; i < attribute.count; i++) {
|
|
667
|
+
const x = attribute.getX(i);
|
|
668
|
+
const y = attribute.getY(i);
|
|
669
|
+
array.push(`(${x.toPrecision(PRECISION)}, ${1 - y.toPrecision(PRECISION)})`);
|
|
670
|
+
}
|
|
671
|
+
return array.join(', ');
|
|
672
|
+
}
|
|
673
|
+
// Materials
|
|
674
|
+
function buildMaterials(materials, textures) {
|
|
675
|
+
const array = [];
|
|
676
|
+
for (const uuid in materials) {
|
|
677
|
+
const material = materials[uuid];
|
|
678
|
+
array.push(buildMaterial(material, textures));
|
|
679
|
+
}
|
|
680
|
+
return `def "Materials"
|
|
681
|
+
{
|
|
682
|
+
${array.join('')}
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
`;
|
|
686
|
+
}
|
|
687
|
+
function buildMaterial(material, textures) {
|
|
688
|
+
// https://graphics.pixar.com/usd/docs/UsdPreviewSurface-Proposal.html
|
|
689
|
+
const pad = ' ';
|
|
690
|
+
const inputs = [];
|
|
691
|
+
const samplers = [];
|
|
692
|
+
const exportForQuickLook = true;
|
|
693
|
+
function buildTexture(texture, mapType, color = undefined, opacity = undefined) {
|
|
694
|
+
const id = texture.id + (color ? '_' + color.getHexString() : '') + (opacity ? '_' + opacity : '');
|
|
695
|
+
const isRGBA = texture.format === 1023;
|
|
696
|
+
const wrapS = (texture.wrapS == RepeatWrapping) ? 'repeat' : (texture.wrapS == MirroredRepeatWrapping ? 'mirror' : 'clamp');
|
|
697
|
+
const wrapT = (texture.wrapT == RepeatWrapping) ? 'repeat' : (texture.wrapT == MirroredRepeatWrapping ? 'mirror' : 'clamp');
|
|
698
|
+
const repeat = texture.repeat.clone();
|
|
699
|
+
const offset = texture.offset.clone();
|
|
700
|
+
// texture coordinates start in the opposite corner, need to correct
|
|
701
|
+
offset.y = 1 - offset.y - repeat.y;
|
|
702
|
+
// turns out QuickLook is buggy and interprets texture repeat inverted.
|
|
703
|
+
// Apple Feedback: FB10036297 and FB11442287
|
|
704
|
+
if (exportForQuickLook) {
|
|
705
|
+
offset.x = offset.x / repeat.x;
|
|
706
|
+
offset.y = offset.y / repeat.y;
|
|
707
|
+
}
|
|
708
|
+
textures[id] = texture;
|
|
709
|
+
const uvReader = mapType == 'occlusion' ? 'uvReader_st2' : 'uvReader_st';
|
|
710
|
+
const needsTextureTransform = (repeat.x != 1 || repeat.y != 1 || offset.x != 0 || offset.y != 0);
|
|
711
|
+
const textureTransformInput = `</Materials/Material_${material.id}/${uvReader}.outputs:result>`;
|
|
712
|
+
const textureTransformOutput = `</Materials/Material_${material.id}/Transform2d_${mapType}.outputs:result>`;
|
|
713
|
+
const rawTextureExtra = `(
|
|
714
|
+
colorSpace = "Raw"
|
|
715
|
+
)`;
|
|
716
|
+
const needsTextureScale = mapType !== 'normal' && (color && (color.r !== 1 || color.g !== 1 || color.b !== 1 || opacity !== 1)) || false;
|
|
717
|
+
const needsNormalScaleAndBias = mapType === 'normal';
|
|
718
|
+
const normalScaleValueString = (material.normalScale ? material.normalScale.x * 2 : 2).toFixed(PRECISION);
|
|
719
|
+
return `
|
|
720
|
+
${needsTextureTransform ? `def Shader "Transform2d_${mapType}" (
|
|
721
|
+
sdrMetadata = {
|
|
722
|
+
string role = "math"
|
|
723
|
+
}
|
|
724
|
+
)
|
|
725
|
+
{
|
|
726
|
+
uniform token info:id = "UsdTransform2d"
|
|
727
|
+
float2 inputs:in.connect = ${textureTransformInput}
|
|
728
|
+
float2 inputs:scale = ${buildVector2(repeat)}
|
|
729
|
+
float2 inputs:translation = ${buildVector2(offset)}
|
|
730
|
+
float2 outputs:result
|
|
731
|
+
}
|
|
732
|
+
` : ''}
|
|
733
|
+
def Shader "Texture_${texture.id}_${mapType}"
|
|
734
|
+
{
|
|
735
|
+
uniform token info:id = "UsdUVTexture"
|
|
736
|
+
asset inputs:file = @textures/Texture_${id}.${isRGBA ? 'png' : 'jpg'}@ ${mapType === 'normal' ? rawTextureExtra : ''}
|
|
737
|
+
float2 inputs:st.connect = ${needsTextureTransform ? textureTransformOutput : textureTransformInput}
|
|
738
|
+
${needsTextureScale ? `
|
|
739
|
+
float4 inputs:scale = (${color ? color.r + ', ' + color.g + ', ' + color.b : '1, 1, 1'}, ${opacity ? opacity : '1'})
|
|
740
|
+
` : ``}
|
|
741
|
+
${needsNormalScaleAndBias ? `
|
|
742
|
+
float4 inputs:scale = (${normalScaleValueString}, ${normalScaleValueString}, ${normalScaleValueString}, 1)
|
|
743
|
+
float4 inputs:bias = (-1, -1, -1, 0)
|
|
744
|
+
` : ``}
|
|
745
|
+
token inputs:wrapS = "${wrapS}"
|
|
746
|
+
token inputs:wrapT = "${wrapT}"
|
|
747
|
+
float outputs:r
|
|
748
|
+
float outputs:g
|
|
749
|
+
float outputs:b
|
|
750
|
+
float3 outputs:rgb
|
|
751
|
+
${material.transparent || material.alphaTest > 0.0 ? 'float outputs:a' : ''}
|
|
752
|
+
}`;
|
|
753
|
+
}
|
|
754
|
+
const effectiveOpacity = (material.transparent || material.alphaTest) ? material.opacity : 1;
|
|
755
|
+
if (material.side === DoubleSide) {
|
|
756
|
+
console.warn('THREE.USDZExporter: USDZ does not support double sided materials', material);
|
|
757
|
+
}
|
|
758
|
+
if (material.map !== null) {
|
|
759
|
+
inputs.push(`${pad}color3f inputs:diffuseColor.connect = </Materials/Material_${material.id}/Texture_${material.map.id}_diffuse.outputs:rgb>`);
|
|
760
|
+
if (material.transparent) {
|
|
761
|
+
inputs.push(`${pad}float inputs:opacity.connect = </Materials/Material_${material.id}/Texture_${material.map.id}_diffuse.outputs:a>`);
|
|
762
|
+
}
|
|
763
|
+
else if (material.alphaTest > 0.0) {
|
|
764
|
+
inputs.push(`${pad}float inputs:opacity.connect = </Materials/Material_${material.id}/Texture_${material.map.id}_diffuse.outputs:a>`);
|
|
765
|
+
inputs.push(`${pad}float inputs:opacityThreshold = ${material.alphaTest}`);
|
|
766
|
+
}
|
|
767
|
+
samplers.push(buildTexture(material.map, 'diffuse', material.color, effectiveOpacity));
|
|
768
|
+
}
|
|
769
|
+
else {
|
|
770
|
+
inputs.push(`${pad}color3f inputs:diffuseColor = ${buildColor(material.color)}`);
|
|
771
|
+
}
|
|
772
|
+
if (material.emissiveMap) {
|
|
773
|
+
inputs.push(`${pad}color3f inputs:emissiveColor.connect = </Materials/Material_${material.id}/Texture_${material.emissiveMap.id}_emissive.outputs:rgb>`);
|
|
774
|
+
samplers.push(buildTexture(material.emissiveMap, 'emissive'));
|
|
775
|
+
}
|
|
776
|
+
else if (material.emissive?.getHex() > 0) {
|
|
777
|
+
inputs.push(`${pad}color3f inputs:emissiveColor = ${buildColor(material.emissive)}`);
|
|
778
|
+
}
|
|
779
|
+
else {
|
|
780
|
+
inputs.push(`${pad}color3f inputs:emissiveColor = (0, 0, 0)`);
|
|
781
|
+
}
|
|
782
|
+
if (material.normalMap) {
|
|
783
|
+
inputs.push(`${pad}normal3f inputs:normal.connect = </Materials/Material_${material.id}/Texture_${material.normalMap.id}_normal.outputs:rgb>`);
|
|
784
|
+
samplers.push(buildTexture(material.normalMap, 'normal'));
|
|
785
|
+
}
|
|
786
|
+
if (material.aoMap) {
|
|
787
|
+
inputs.push(`${pad}float inputs:occlusion.connect = </Materials/Material_${material.id}/Texture_${material.aoMap.id}_occlusion.outputs:r>`);
|
|
788
|
+
samplers.push(buildTexture(material.aoMap, 'occlusion'));
|
|
789
|
+
}
|
|
790
|
+
if (material.roughnessMap && material.roughness === 1) {
|
|
791
|
+
inputs.push(`${pad}float inputs:roughness.connect = </Materials/Material_${material.id}/Texture_${material.roughnessMap.id}_roughness.outputs:g>`);
|
|
792
|
+
samplers.push(buildTexture(material.roughnessMap, 'roughness'));
|
|
793
|
+
}
|
|
794
|
+
else {
|
|
795
|
+
inputs.push(`${pad}float inputs:roughness = ${material.roughness}`);
|
|
796
|
+
}
|
|
797
|
+
if (material.metalnessMap && material.metalness === 1) {
|
|
798
|
+
inputs.push(`${pad}float inputs:metallic.connect = </Materials/Material_${material.id}/Texture_${material.metalnessMap.id}_metallic.outputs:b>`);
|
|
799
|
+
samplers.push(buildTexture(material.metalnessMap, 'metallic'));
|
|
800
|
+
}
|
|
801
|
+
else {
|
|
802
|
+
inputs.push(`${pad}float inputs:metallic = ${material.metalness}`);
|
|
803
|
+
}
|
|
804
|
+
if (material.alphaMap) {
|
|
805
|
+
inputs.push(`${pad}float inputs:opacity.connect = </Materials/Material_${material.id}/Texture_${material.alphaMap.id}_opacity.outputs:r>`);
|
|
806
|
+
inputs.push(`${pad}float inputs:opacityThreshold = 0.0001`);
|
|
807
|
+
samplers.push(buildTexture(material.alphaMap, 'opacity'));
|
|
808
|
+
}
|
|
809
|
+
else {
|
|
810
|
+
inputs.push(`${pad}float inputs:opacity = ${effectiveOpacity}`);
|
|
811
|
+
}
|
|
812
|
+
if (material instanceof MeshPhysicalMaterial) {
|
|
813
|
+
inputs.push(`${pad}float inputs:clearcoat = ${material.clearcoat}`);
|
|
814
|
+
inputs.push(`${pad}float inputs:clearcoatRoughness = ${material.clearcoatRoughness}`);
|
|
815
|
+
inputs.push(`${pad}float inputs:ior = ${material.ior}`);
|
|
816
|
+
}
|
|
817
|
+
return `
|
|
818
|
+
def Material "Material_${material.id}"
|
|
819
|
+
{
|
|
820
|
+
def Shader "PreviewSurface"
|
|
821
|
+
{
|
|
822
|
+
uniform token info:id = "UsdPreviewSurface"
|
|
823
|
+
${inputs.join('\n')}
|
|
824
|
+
int inputs:useSpecularWorkflow = 0
|
|
825
|
+
token outputs:surface
|
|
826
|
+
}
|
|
827
|
+
|
|
828
|
+
token outputs:surface.connect = </Materials/Material_${material.id}/PreviewSurface.outputs:surface>
|
|
829
|
+
|
|
830
|
+
def Shader "uvReader_st"
|
|
831
|
+
{
|
|
832
|
+
uniform token info:id = "UsdPrimvarReader_float2"
|
|
833
|
+
token inputs:varname = "st"
|
|
834
|
+
float2 inputs:fallback = (0.0, 0.0)
|
|
835
|
+
float2 outputs:result
|
|
836
|
+
}
|
|
837
|
+
|
|
838
|
+
def Shader "uvReader_st2"
|
|
839
|
+
{
|
|
840
|
+
uniform token info:id = "UsdPrimvarReader_float2"
|
|
841
|
+
token inputs:varname = "st2"
|
|
842
|
+
float2 inputs:fallback = (0.0, 0.0)
|
|
843
|
+
float2 outputs:result
|
|
844
|
+
}
|
|
845
|
+
|
|
846
|
+
${samplers.join('\n')}
|
|
847
|
+
|
|
848
|
+
}
|
|
849
|
+
`;
|
|
850
|
+
}
|
|
851
|
+
function buildColor(color) {
|
|
852
|
+
return `(${color.r}, ${color.g}, ${color.b})`;
|
|
853
|
+
}
|
|
854
|
+
function buildVector2(vector) {
|
|
855
|
+
return `(${vector.x}, ${vector.y})`;
|
|
856
|
+
}
|
|
857
|
+
export { USDZExporter, USDZExporterContext, USDWriter, USDObject, buildMatrix, USDDocument, makeNameSafe as makeNameSafeForUSD, imageToCanvas };
|
|
858
|
+
//# sourceMappingURL=ThreeUSDZExporter.js.map
|