@configura/babylon-view 2.1.0-alpha.3 → 2.2.0-alpha.0
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/.eslintrc.json +5 -5
- package/LICENSE +201 -201
- package/README.md +1 -1
- package/dist/animation/AnimatableObject.d.ts +8 -8
- package/dist/animation/AnimatableObject.js +3 -3
- package/dist/animation/animator/Animator.d.ts +33 -33
- package/dist/animation/animator/Animator.js +58 -58
- package/dist/animation/animator/AnimatorEasing.d.ts +16 -16
- package/dist/animation/animator/AnimatorEasing.js +31 -31
- package/dist/animation/animator/AnimatorEasingMatrix.d.ts +14 -14
- package/dist/animation/animator/AnimatorEasingMatrix.js +16 -16
- package/dist/animation/animator/AnimatorHighlight.d.ts +16 -16
- package/dist/animation/animator/AnimatorHighlight.js +32 -32
- package/dist/animation/animator/AnimatorPointToPoint.d.ts +8 -8
- package/dist/animation/animator/AnimatorPointToPoint.js +14 -14
- package/dist/animation/animator/AnimatorQueue.d.ts +13 -13
- package/dist/animation/animator/AnimatorQueue.js +57 -57
- package/dist/animation/animator/AnimatorScale.d.ts +8 -8
- package/dist/animation/animator/AnimatorScale.js +13 -13
- package/dist/animation/animator/AnimatorSpin.d.ts +10 -10
- package/dist/animation/animator/AnimatorSpin.js +13 -13
- package/dist/animation/animator/EasingFunctions.d.ts +35 -35
- package/dist/animation/animator/EasingFunctions.js +137 -137
- package/dist/animation/coordinator/Coordinator.d.ts +28 -28
- package/dist/animation/coordinator/Coordinator.js +53 -53
- package/dist/animation/coordinator/CoordinatorDropAndSpin.d.ts +22 -22
- package/dist/animation/coordinator/CoordinatorDropAndSpin.js +138 -138
- package/dist/animation/coordinator/CoordinatorIdentity.d.ts +11 -11
- package/dist/animation/coordinator/CoordinatorIdentity.js +14 -14
- package/dist/animation/coordinator/CoordinatorNodeQueues.d.ts +18 -18
- package/dist/animation/coordinator/CoordinatorNodeQueues.js +50 -50
- package/dist/animation/coordinator/CoordinatorPulse.d.ts +21 -21
- package/dist/animation/coordinator/CoordinatorPulse.js +47 -47
- package/dist/animation/coordinator/CoordinatorPulseBounce.d.ts +14 -14
- package/dist/animation/coordinator/CoordinatorPulseBounce.js +35 -35
- package/dist/animation/coordinator/CoordinatorPulseHighlight.d.ts +13 -13
- package/dist/animation/coordinator/CoordinatorPulseHighlight.js +29 -29
- package/dist/animation/coordinator/CoordinatorPulseInflate.d.ts +14 -14
- package/dist/animation/coordinator/CoordinatorPulseInflate.js +23 -23
- package/dist/camera/CameraCreator.d.ts +5 -5
- package/dist/camera/CameraCreator.js +4 -4
- package/dist/camera/CfgOrbitalCamera.d.ts +76 -76
- package/dist/camera/CfgOrbitalCamera.js +277 -281
- package/dist/camera/CfgOrbitalCameraControlProps.d.ts +14 -14
- package/dist/camera/CfgOrbitalCameraControlProps.js +7 -7
- package/dist/camera/GradingApplier.d.ts +3 -3
- package/dist/camera/GradingApplier.js +48 -48
- package/dist/engine/EngineCreator.d.ts +3 -3
- package/dist/engine/EngineCreator.js +10 -10
- package/dist/geometry/CfgGeometry.d.ts +29 -29
- package/dist/geometry/CfgGeometry.js +146 -146
- package/dist/geometry/CfgMesh.d.ts +10 -10
- package/dist/geometry/CfgMesh.js +38 -38
- package/dist/geometry/geoSplitter.d.ts +8 -8
- package/dist/geometry/geoSplitter.js +192 -192
- package/dist/geometry/stretch/CfgMorphTarget.d.ts +15 -15
- package/dist/geometry/stretch/CfgMorphTarget.js +65 -65
- package/dist/geometry/stretch/CfgStretchData.d.ts +116 -116
- package/dist/geometry/stretch/CfgStretchData.js +347 -350
- package/dist/geometry/stretch/CfgStretchMorphGeometry.d.ts +16 -16
- package/dist/geometry/stretch/CfgStretchMorphGeometry.js +95 -95
- package/dist/index.d.ts +16 -16
- package/dist/index.js +16 -16
- package/dist/io/CfgHistoryToCameraConfConnector.d.ts +31 -31
- package/dist/io/CfgHistoryToCameraConfConnector.js +90 -90
- package/dist/io/CfgIOCameraConfConnector.d.ts +35 -35
- package/dist/io/CfgIOCameraConfConnector.js +81 -81
- package/dist/io/CfgObservableStateToCameraConfConnector.d.ts +10 -10
- package/dist/io/CfgObservableStateToCameraConfConnector.js +11 -11
- package/dist/io/CfgWindowMessageToCameraConfConnector.d.ts +10 -10
- package/dist/io/CfgWindowMessageToCameraConfConnector.js +11 -11
- package/dist/light/CfgDirectionalLight.d.ts +8 -8
- package/dist/light/CfgDirectionalLight.js +18 -18
- package/dist/light/CfgHemisphericLight.d.ts +7 -7
- package/dist/light/CfgHemisphericLight.js +17 -17
- package/dist/light/CfgPointLight.d.ts +8 -8
- package/dist/light/CfgPointLight.js +18 -18
- package/dist/light/DefaultLightRig.d.ts +19 -19
- package/dist/light/DefaultLightRig.js +77 -77
- package/dist/light/LightRigCreator.d.ts +9 -9
- package/dist/light/LightRigCreator.js +3 -3
- package/dist/material/CfgMaterial.d.ts +68 -68
- package/dist/material/CfgMaterial.js +482 -482
- package/dist/material/DummyMaterialCreator.d.ts +4 -4
- package/dist/material/DummyMaterialCreator.js +15 -15
- package/dist/material/material.d.ts +18 -18
- package/dist/material/material.js +128 -128
- package/dist/material/texture.d.ts +14 -14
- package/dist/material/texture.js +306 -306
- package/dist/nodes/CfgContentRootNode.d.ts +19 -19
- package/dist/nodes/CfgContentRootNode.js +75 -75
- package/dist/nodes/CfgDeferredMeshNode.d.ts +55 -55
- package/dist/nodes/CfgDeferredMeshNode.js +378 -378
- package/dist/nodes/CfgProductNode.d.ts +127 -127
- package/dist/nodes/CfgProductNode.js +598 -598
- package/dist/nodes/CfgSymNode.d.ts +50 -50
- package/dist/nodes/CfgSymNode.js +249 -249
- package/dist/nodes/CfgSymRootNode.d.ts +45 -45
- package/dist/nodes/CfgSymRootNode.js +240 -229
- package/dist/nodes/CfgTransformNode.d.ts +33 -33
- package/dist/nodes/CfgTransformNode.js +83 -83
- package/dist/scene/SceneCreator.d.ts +6 -6
- package/dist/scene/SceneCreator.js +22 -22
- package/dist/utilities/CfgBoundingBox.d.ts +21 -21
- package/dist/utilities/CfgBoundingBox.js +81 -81
- package/dist/utilities/anchor/anchor.d.ts +50 -50
- package/dist/utilities/anchor/anchor.js +133 -133
- package/dist/utilities/anchor/anchorMap.d.ts +20 -20
- package/dist/utilities/anchor/anchorMap.js +111 -111
- package/dist/utilities/utilities3D.d.ts +70 -70
- package/dist/utilities/utilities3D.js +259 -265
- package/dist/utilities/utilitiesColor.d.ts +18 -18
- package/dist/utilities/utilitiesColor.js +50 -50
- package/dist/utilities/utilitiesImage.d.ts +6 -6
- package/dist/utilities/utilitiesImage.js +107 -107
- package/dist/utilities/utilitiesSymRootIdentifier.d.ts +7 -7
- package/dist/utilities/utilitiesSymRootIdentifier.js +26 -26
- package/dist/view/BaseView.d.ts +78 -78
- package/dist/view/BaseView.js +303 -303
- package/dist/view/BaseViewConfiguration.d.ts +32 -32
- package/dist/view/BaseViewConfiguration.js +10 -10
- package/dist/view/RenderEnv.d.ts +43 -43
- package/dist/view/RenderEnv.js +7 -7
- package/dist/view/SingleProductDefaultCameraView.d.ts +38 -38
- package/dist/view/SingleProductDefaultCameraView.js +149 -149
- package/dist/view/SingleProductDefaultCameraViewConfiguration.d.ts +44 -44
- package/dist/view/SingleProductDefaultCameraViewConfiguration.js +11 -11
- package/dist/view/SingleProductView.d.ts +44 -44
- package/dist/view/SingleProductView.js +212 -212
- package/dist/view/SingleProductViewConfiguration.d.ts +32 -32
- package/dist/view/SingleProductViewConfiguration.js +19 -19
- package/package.json +12 -7
- package/dist/camera/CfgArcRotateCameraPointersInput.d.ts +0 -27
- package/dist/camera/CfgArcRotateCameraPointersInput.js +0 -266
package/dist/material/texture.js
CHANGED
|
@@ -1,306 +1,306 @@
|
|
|
1
|
-
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2
|
-
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
|
-
return new (P || (P = Promise))(function (resolve, reject) {
|
|
4
|
-
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
5
|
-
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
6
|
-
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
7
|
-
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
|
-
});
|
|
9
|
-
};
|
|
10
|
-
import { Texture } from "@babylonjs/core/Materials/Textures/texture.js";
|
|
11
|
-
import { Vector3 } from "@babylonjs/core/Maths/math.vector.js";
|
|
12
|
-
import { GMaterial3DTexture, } from "@configura/web-core/dist/cm/core3D/GMaterial3D.js";
|
|
13
|
-
import { Bump3D, GMaterialClassic } from "@configura/web-core/dist/cm/core3D/GMaterialClassic.js";
|
|
14
|
-
import { GMaterialPBR } from "@configura/web-core/dist/cm/core3D/GMaterialPBR.js";
|
|
15
|
-
import { CMMIPS_MIMETYPE, extractCmMips, getFileExtension, loadImage, LogObservable, } from "@configura/web-utilities";
|
|
16
|
-
function getImageMimeType(fileExtension) {
|
|
17
|
-
switch (fileExtension.toLowerCase()) {
|
|
18
|
-
case "jpg":
|
|
19
|
-
case "jpeg":
|
|
20
|
-
return "image/jpeg";
|
|
21
|
-
case "png":
|
|
22
|
-
return "image/png";
|
|
23
|
-
case "bmp":
|
|
24
|
-
return "image/bmp";
|
|
25
|
-
case "cmmips":
|
|
26
|
-
return CMMIPS_MIMETYPE;
|
|
27
|
-
default:
|
|
28
|
-
throw Error("unrecognized file extension: " + fileExtension);
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
function symWrapToBabylonWrap(logger, wrap) {
|
|
32
|
-
switch (wrap) {
|
|
33
|
-
case 3 /* clampToEdge */:
|
|
34
|
-
return Texture.CLAMP_ADDRESSMODE;
|
|
35
|
-
case 0 /* repeat */:
|
|
36
|
-
return Texture.WRAP_ADDRESSMODE;
|
|
37
|
-
case 1 /* mirroredRepeat */:
|
|
38
|
-
logger.warn("Mirrored repeat wrapping not fully tested.");
|
|
39
|
-
return Texture.MIRROR_ADDRESSMODE;
|
|
40
|
-
case 2 /* clamp */:
|
|
41
|
-
// From the CmSym specification this sounds like a "clamp to border" with a black
|
|
42
|
-
// or transparent border color, not supported by WebGL or WebGPU.
|
|
43
|
-
throw Error("Clamp wrapping not supported");
|
|
44
|
-
case 4 /* clampToBorder */:
|
|
45
|
-
// Not supported by WebGL or WebGPU
|
|
46
|
-
throw Error("ClampToBorder wrapping not supported");
|
|
47
|
-
default:
|
|
48
|
-
throw Error("wrapping not implemented");
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
const TEXTURE_FILTER_LOOKUP = [
|
|
52
|
-
Texture.NEAREST_NEAREST,
|
|
53
|
-
Texture.LINEAR_NEAREST,
|
|
54
|
-
Texture.NEAREST_LINEAR,
|
|
55
|
-
Texture.LINEAR_LINEAR,
|
|
56
|
-
Texture.NEAREST_NEAREST_MIPNEAREST,
|
|
57
|
-
Texture.LINEAR_NEAREST_MIPNEAREST,
|
|
58
|
-
Texture.NEAREST_LINEAR_MIPNEAREST,
|
|
59
|
-
Texture.LINEAR_LINEAR_MIPNEAREST,
|
|
60
|
-
Texture.NEAREST_NEAREST_MIPLINEAR,
|
|
61
|
-
Texture.LINEAR_NEAREST_MIPLINEAR,
|
|
62
|
-
Texture.NEAREST_LINEAR_MIPLINEAR,
|
|
63
|
-
Texture.LINEAR_LINEAR_MIPLINEAR,
|
|
64
|
-
];
|
|
65
|
-
function symFilterToBabylonFilter(mag, min, mip) {
|
|
66
|
-
let lookup = mip === undefined ? 0 : mip === 1 /* nearest */ ? 4 : 8;
|
|
67
|
-
lookup += min === 1 /* nearest */ ? 0 : 2;
|
|
68
|
-
lookup += mag === 1 /* nearest */ ? 0 : 1;
|
|
69
|
-
return TEXTURE_FILTER_LOOKUP[lookup];
|
|
70
|
-
}
|
|
71
|
-
function loadCachedImage(logger, resourceUrl, renderEnvironment) {
|
|
72
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
73
|
-
const textureImageWithMeta = yield renderEnvironment.textureImageCache.get(resourceUrl, () => __awaiter(this, void 0, void 0, function* () {
|
|
74
|
-
const result = {
|
|
75
|
-
image: undefined,
|
|
76
|
-
logger: new LogObservable(),
|
|
77
|
-
};
|
|
78
|
-
let mimeType = getImageMimeType(getFileExtension(resourceUrl));
|
|
79
|
-
const reader = renderEnvironment.dexManager.readers.get(resourceUrl);
|
|
80
|
-
if (reader === undefined) {
|
|
81
|
-
result.logger.warn("No reader for", resourceUrl);
|
|
82
|
-
return result;
|
|
83
|
-
}
|
|
84
|
-
let bytes = reader.bytes();
|
|
85
|
-
if (mimeType === CMMIPS_MIMETYPE) {
|
|
86
|
-
const mip = extractCmMips(bytes, result.logger); // Max quality
|
|
87
|
-
// const mip = extractCmMips(bytes, 2048); // High quality
|
|
88
|
-
// const mip = extractCmMips(bytes, 256); // Low quality
|
|
89
|
-
if ((mip === null || mip === void 0 ? void 0 : mip.data) === undefined) {
|
|
90
|
-
result.logger.warn("Could not extract image from CmMips", resourceUrl);
|
|
91
|
-
return result;
|
|
92
|
-
}
|
|
93
|
-
mimeType = getImageMimeType(mip.suffix);
|
|
94
|
-
bytes = mip.data;
|
|
95
|
-
}
|
|
96
|
-
const blob = new Blob([bytes], { type: mimeType });
|
|
97
|
-
const blobImageUrl = window.URL.createObjectURL(blob);
|
|
98
|
-
try {
|
|
99
|
-
result.image = yield loadImage(blobImageUrl);
|
|
100
|
-
return result;
|
|
101
|
-
}
|
|
102
|
-
catch (e) {
|
|
103
|
-
result.logger.warnFromCaught(e);
|
|
104
|
-
return result;
|
|
105
|
-
}
|
|
106
|
-
}));
|
|
107
|
-
textureImageWithMeta.logger.accumulated.forEach((p) => logger.addPrebaked(p));
|
|
108
|
-
return textureImageWithMeta.image;
|
|
109
|
-
});
|
|
110
|
-
}
|
|
111
|
-
export function loadTextureFromURL(url, renderEnvironment) {
|
|
112
|
-
const task = renderEnvironment.assetsManager.addTextureTask("(loadTextureFromURL)", url);
|
|
113
|
-
return new Promise((resolve, reject) => {
|
|
114
|
-
task.runTask(renderEnvironment.scene, () => {
|
|
115
|
-
resolve(task.texture);
|
|
116
|
-
}, reject);
|
|
117
|
-
});
|
|
118
|
-
}
|
|
119
|
-
/**
|
|
120
|
-
* Derives a normal map from a supplied height map (aka bump-map). Classic materials in CET can use
|
|
121
|
-
* both height maps and normal maps but Babylon.js only supports normal maps.
|
|
122
|
-
*
|
|
123
|
-
* The derived normal map and the effect from `scale` is carefully crafted to be as similar to how
|
|
124
|
-
* the RedSDK 3D renderer in CET operates and match it's final output. Do not change this code to
|
|
125
|
-
* make it visually "better", the goal is instead to be as similar as possible.
|
|
126
|
-
*/
|
|
127
|
-
function deriveNormalMapFromHeightMap(image, amount, logger) {
|
|
128
|
-
const w = image.width;
|
|
129
|
-
const h = image.height;
|
|
130
|
-
const canvas = document.createElement("canvas");
|
|
131
|
-
canvas.width = w;
|
|
132
|
-
canvas.height = h;
|
|
133
|
-
const context = canvas.getContext("2d");
|
|
134
|
-
if (context !== null) {
|
|
135
|
-
const tick = performance.now();
|
|
136
|
-
context.drawImage(image, 0, 0);
|
|
137
|
-
const imgData = context.getImageData(0, 0, image.width, image.height);
|
|
138
|
-
const data = imgData.data;
|
|
139
|
-
// The scale conversion and default value comes from the CET source code.
|
|
140
|
-
const scale = amount ? 15 * amount : 0.1;
|
|
141
|
-
// Number of components, canvas returns RGBA
|
|
142
|
-
const c = 4;
|
|
143
|
-
// Go through the image and calculate a gray scale version, storing it in the A channel.
|
|
144
|
-
// Since this is legacy support code, we might as well support non-gray scale height maps,
|
|
145
|
-
// which we have seen in the wild. This is also how RedSDK in CET works according to docs.
|
|
146
|
-
for (let i = 0; i < w * h * c; i += c) {
|
|
147
|
-
data[i + 3] = (data[i] + data[i + 1] + data[i + 2]) / 3;
|
|
148
|
-
}
|
|
149
|
-
// Derive the height map to create a normal map, store the X and Y components in the R and
|
|
150
|
-
// G channels respectively.
|
|
151
|
-
//
|
|
152
|
-
// We are assuming that the map will be repeated when displayed since the Material Lab does
|
|
153
|
-
// not expose texture repeats in it's UI, and the default mode is wrapped. This means that
|
|
154
|
-
// edge sampling should wrap around as well.
|
|
155
|
-
for (let i = 0; i < w * h; i++) {
|
|
156
|
-
// The sample kernel:
|
|
157
|
-
// [x][r]
|
|
158
|
-
// [b]
|
|
159
|
-
//
|
|
160
|
-
// Produces results shifted half a pixel diagonally and is very local, but is about the // same as the kernel used in CET's render engine and also OpenGLs derivation extension.
|
|
161
|
-
const offset = i * c + 3; // A channel
|
|
162
|
-
const x = data[offset];
|
|
163
|
-
let r, b;
|
|
164
|
-
if (i % w === w - 1) {
|
|
165
|
-
// End of the row, sample from the beginning of the same row
|
|
166
|
-
r = data[offset - (w - 1) * c];
|
|
167
|
-
}
|
|
168
|
-
else {
|
|
169
|
-
r = data[offset + c];
|
|
170
|
-
}
|
|
171
|
-
if (i >= (h - 1) * w) {
|
|
172
|
-
// Last row, sample from the same column in the first row
|
|
173
|
-
b = data[offset - (h - 1) * w * c];
|
|
174
|
-
}
|
|
175
|
-
else {
|
|
176
|
-
b = data[offset + w * c];
|
|
177
|
-
}
|
|
178
|
-
// y-axis is inverted, OpenGL vs DirectX
|
|
179
|
-
const normal = new Vector3((x - r) * scale, (b - x) * scale, 255).normalize();
|
|
180
|
-
data[offset - 3] = Math.max(0, Math.min(255, normal.x * 128 + 127)); // R, x-axis
|
|
181
|
-
data[offset - 2] = Math.max(0, Math.min(255, normal.y * 128 + 127)); // G, y-axis
|
|
182
|
-
data[offset - 1] = Math.max(0, Math.min(255, normal.z * 128 + 127)); // B, z-axis
|
|
183
|
-
}
|
|
184
|
-
// Set the A channel to 255 to avoid any strange edge case and let it compress better.
|
|
185
|
-
for (let offset = 3; offset < w * h * c; offset += c) {
|
|
186
|
-
data[offset] = 255; // A
|
|
187
|
-
}
|
|
188
|
-
context.putImageData(imgData, 0, 0);
|
|
189
|
-
const convertedImage = new Image();
|
|
190
|
-
convertedImage.src = canvas.toDataURL("image/png");
|
|
191
|
-
logger.info(`Converting height based bump map`, `of size ${w}x${h} took ${Math.round(performance.now() - tick)}ms. For optimal performance, update all classic materials to use normal maps rather than height maps or switch to PBR materials.`);
|
|
192
|
-
return convertedImage;
|
|
193
|
-
}
|
|
194
|
-
return image;
|
|
195
|
-
}
|
|
196
|
-
function isGMaterial3DTexture(v) {
|
|
197
|
-
return v instanceof GMaterial3DTexture;
|
|
198
|
-
}
|
|
199
|
-
function getToImageLoadFunc(logger, renderEnvironment) {
|
|
200
|
-
return function (gm) {
|
|
201
|
-
let im;
|
|
202
|
-
if (gm === undefined || gm.textureUrl === undefined) {
|
|
203
|
-
im = Promise.resolve(undefined);
|
|
204
|
-
}
|
|
205
|
-
else {
|
|
206
|
-
im = loadCachedImage(logger, gm.textureUrl.value, renderEnvironment);
|
|
207
|
-
}
|
|
208
|
-
return { gm, im };
|
|
209
|
-
};
|
|
210
|
-
}
|
|
211
|
-
function getApplyImageConversionsFunc(logger, renderEnvironment) {
|
|
212
|
-
return function (gmv) {
|
|
213
|
-
const { gm } = gmv;
|
|
214
|
-
if (gm instanceof Bump3D && !gm.prepared) {
|
|
215
|
-
/* A non-"prepared" bump texture means that the bump map is actually an old school
|
|
216
|
-
* height map rather than a more modern (and faster) normal map. Babylon.js does not
|
|
217
|
-
* support height maps so we will need to convert it manually.
|
|
218
|
-
* https://forum.babylonjs.com/t/old-school-bump-map-height-map-not-supported/13447
|
|
219
|
-
*
|
|
220
|
-
* Normal map vs Height map:
|
|
221
|
-
* https://docs.unity3d.com/2019.3/Documentation/Manual/StandardShaderMaterialParameterNormalMap.html
|
|
222
|
-
*/
|
|
223
|
-
const textureUrl = gm.textureUrl;
|
|
224
|
-
if (textureUrl !== undefined) {
|
|
225
|
-
const mimeType = getImageMimeType(getFileExtension(textureUrl.value));
|
|
226
|
-
if (mimeType === "image/png" || mimeType === "image/jpeg") {
|
|
227
|
-
// Wrap the pure image load promise with an normal map derive promise
|
|
228
|
-
gmv.im = renderEnvironment.derivedNormalMapCache.get(
|
|
229
|
-
// The derivation depends on gm.amount, include it in the key. See WRD-664.
|
|
230
|
-
`${textureUrl.value}_scale_${gm.amount}`, () => __awaiter(this, void 0, void 0, function* () {
|
|
231
|
-
let image = yield gmv.im;
|
|
232
|
-
if (image !== undefined) {
|
|
233
|
-
image = deriveNormalMapFromHeightMap(image, gm.amount, logger);
|
|
234
|
-
}
|
|
235
|
-
return image;
|
|
236
|
-
}));
|
|
237
|
-
}
|
|
238
|
-
}
|
|
239
|
-
}
|
|
240
|
-
return gmv;
|
|
241
|
-
};
|
|
242
|
-
}
|
|
243
|
-
function getToTextureLoadFunc(renderEnvironment) {
|
|
244
|
-
return function (gmv) {
|
|
245
|
-
const { gm, im } = gmv;
|
|
246
|
-
const tx = (() => __awaiter(this, void 0, void 0, function* () {
|
|
247
|
-
const image = yield im;
|
|
248
|
-
if (image === undefined) {
|
|
249
|
-
return undefined;
|
|
250
|
-
}
|
|
251
|
-
return loadTextureFromURL(image.src, renderEnvironment);
|
|
252
|
-
}))();
|
|
253
|
-
return { gm, tx };
|
|
254
|
-
};
|
|
255
|
-
}
|
|
256
|
-
function getApplyTexturePropertiesFromGMaterialFunc(logger) {
|
|
257
|
-
return function (gmv) {
|
|
258
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
259
|
-
const gm = gmv.gm;
|
|
260
|
-
const tx = yield gmv.tx;
|
|
261
|
-
if (tx !== undefined) {
|
|
262
|
-
tx.wrapU = symWrapToBabylonWrap(logger, gm.wrapU);
|
|
263
|
-
tx.wrapV = symWrapToBabylonWrap(logger, gm.wrapV);
|
|
264
|
-
tx.uScale = gm.uScale;
|
|
265
|
-
tx.vScale = gm.vScale;
|
|
266
|
-
tx.uOffset = gm.uOffset;
|
|
267
|
-
tx.vOffset = gm.vOffset;
|
|
268
|
-
tx.uRotationCenter = 0;
|
|
269
|
-
tx.vRotationCenter = 0;
|
|
270
|
-
tx.wAng = gm.rot;
|
|
271
|
-
tx.updateSamplingMode(symFilterToBabylonFilter(gm.minFilter, gm.magFilter, gm.mipFilter));
|
|
272
|
-
}
|
|
273
|
-
return { gm, tx };
|
|
274
|
-
});
|
|
275
|
-
};
|
|
276
|
-
}
|
|
277
|
-
export function getTextures(logger, renderEnvironment, gm) {
|
|
278
|
-
var _a, _b, _c, _d, _e, _f, _g;
|
|
279
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
280
|
-
let textures = [];
|
|
281
|
-
if (gm instanceof GMaterialClassic) {
|
|
282
|
-
// Ambient is not used in CET.
|
|
283
|
-
// Emissive no longer exist in CET.
|
|
284
|
-
// Specular is rare (and we can currently only handle its general value,
|
|
285
|
-
// not its texture since the MeshStandardMaterial does not accept such a thing.)
|
|
286
|
-
textures = [gm.bump, gm.diffuse, gm.specular, gm.transparency];
|
|
287
|
-
}
|
|
288
|
-
if (gm instanceof GMaterialPBR) {
|
|
289
|
-
textures = [
|
|
290
|
-
(_a = gm.base) === null || _a === void 0 ? void 0 : _a.texture,
|
|
291
|
-
(_b = gm.emissive) === null || _b === void 0 ? void 0 : _b.texture,
|
|
292
|
-
(_c = gm.metallic) === null || _c === void 0 ? void 0 : _c.texture,
|
|
293
|
-
(_d = gm.normal) === null || _d === void 0 ? void 0 : _d.texture,
|
|
294
|
-
(_e = gm.occlusion) === null || _e === void 0 ? void 0 : _e.texture,
|
|
295
|
-
(_f = gm.opacity) === null || _f === void 0 ? void 0 : _f.texture,
|
|
296
|
-
(_g = gm.roughness) === null || _g === void 0 ? void 0 : _g.texture,
|
|
297
|
-
];
|
|
298
|
-
}
|
|
299
|
-
return yield Promise.all(textures
|
|
300
|
-
.filter(isGMaterial3DTexture)
|
|
301
|
-
.map(getToImageLoadFunc(logger, renderEnvironment))
|
|
302
|
-
.map(getApplyImageConversionsFunc(logger, renderEnvironment))
|
|
303
|
-
.map(getToTextureLoadFunc(renderEnvironment))
|
|
304
|
-
.map(getApplyTexturePropertiesFromGMaterialFunc(logger)));
|
|
305
|
-
});
|
|
306
|
-
}
|
|
1
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
4
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
5
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
6
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
7
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
|
+
});
|
|
9
|
+
};
|
|
10
|
+
import { Texture } from "@babylonjs/core/Materials/Textures/texture.js";
|
|
11
|
+
import { Vector3 } from "@babylonjs/core/Maths/math.vector.js";
|
|
12
|
+
import { GMaterial3DTexture, } from "@configura/web-core/dist/cm/core3D/GMaterial3D.js";
|
|
13
|
+
import { Bump3D, GMaterialClassic } from "@configura/web-core/dist/cm/core3D/GMaterialClassic.js";
|
|
14
|
+
import { GMaterialPBR } from "@configura/web-core/dist/cm/core3D/GMaterialPBR.js";
|
|
15
|
+
import { CMMIPS_MIMETYPE, extractCmMips, getFileExtension, loadImage, LogObservable, } from "@configura/web-utilities";
|
|
16
|
+
function getImageMimeType(fileExtension) {
|
|
17
|
+
switch (fileExtension.toLowerCase()) {
|
|
18
|
+
case "jpg":
|
|
19
|
+
case "jpeg":
|
|
20
|
+
return "image/jpeg";
|
|
21
|
+
case "png":
|
|
22
|
+
return "image/png";
|
|
23
|
+
case "bmp":
|
|
24
|
+
return "image/bmp";
|
|
25
|
+
case "cmmips":
|
|
26
|
+
return CMMIPS_MIMETYPE;
|
|
27
|
+
default:
|
|
28
|
+
throw Error("unrecognized file extension: " + fileExtension);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
function symWrapToBabylonWrap(logger, wrap) {
|
|
32
|
+
switch (wrap) {
|
|
33
|
+
case 3 /* clampToEdge */:
|
|
34
|
+
return Texture.CLAMP_ADDRESSMODE;
|
|
35
|
+
case 0 /* repeat */:
|
|
36
|
+
return Texture.WRAP_ADDRESSMODE;
|
|
37
|
+
case 1 /* mirroredRepeat */:
|
|
38
|
+
logger.warn("Mirrored repeat wrapping not fully tested.");
|
|
39
|
+
return Texture.MIRROR_ADDRESSMODE;
|
|
40
|
+
case 2 /* clamp */:
|
|
41
|
+
// From the CmSym specification this sounds like a "clamp to border" with a black
|
|
42
|
+
// or transparent border color, not supported by WebGL or WebGPU.
|
|
43
|
+
throw Error("Clamp wrapping not supported");
|
|
44
|
+
case 4 /* clampToBorder */:
|
|
45
|
+
// Not supported by WebGL or WebGPU
|
|
46
|
+
throw Error("ClampToBorder wrapping not supported");
|
|
47
|
+
default:
|
|
48
|
+
throw Error("wrapping not implemented");
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
const TEXTURE_FILTER_LOOKUP = [
|
|
52
|
+
Texture.NEAREST_NEAREST,
|
|
53
|
+
Texture.LINEAR_NEAREST,
|
|
54
|
+
Texture.NEAREST_LINEAR,
|
|
55
|
+
Texture.LINEAR_LINEAR,
|
|
56
|
+
Texture.NEAREST_NEAREST_MIPNEAREST,
|
|
57
|
+
Texture.LINEAR_NEAREST_MIPNEAREST,
|
|
58
|
+
Texture.NEAREST_LINEAR_MIPNEAREST,
|
|
59
|
+
Texture.LINEAR_LINEAR_MIPNEAREST,
|
|
60
|
+
Texture.NEAREST_NEAREST_MIPLINEAR,
|
|
61
|
+
Texture.LINEAR_NEAREST_MIPLINEAR,
|
|
62
|
+
Texture.NEAREST_LINEAR_MIPLINEAR,
|
|
63
|
+
Texture.LINEAR_LINEAR_MIPLINEAR,
|
|
64
|
+
];
|
|
65
|
+
function symFilterToBabylonFilter(mag, min, mip) {
|
|
66
|
+
let lookup = mip === undefined ? 0 : mip === 1 /* nearest */ ? 4 : 8;
|
|
67
|
+
lookup += min === 1 /* nearest */ ? 0 : 2;
|
|
68
|
+
lookup += mag === 1 /* nearest */ ? 0 : 1;
|
|
69
|
+
return TEXTURE_FILTER_LOOKUP[lookup];
|
|
70
|
+
}
|
|
71
|
+
function loadCachedImage(logger, resourceUrl, renderEnvironment) {
|
|
72
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
73
|
+
const textureImageWithMeta = yield renderEnvironment.textureImageCache.get(resourceUrl, () => __awaiter(this, void 0, void 0, function* () {
|
|
74
|
+
const result = {
|
|
75
|
+
image: undefined,
|
|
76
|
+
logger: new LogObservable(),
|
|
77
|
+
};
|
|
78
|
+
let mimeType = getImageMimeType(getFileExtension(resourceUrl));
|
|
79
|
+
const reader = renderEnvironment.dexManager.readers.get(resourceUrl);
|
|
80
|
+
if (reader === undefined) {
|
|
81
|
+
result.logger.warn("No reader for", resourceUrl);
|
|
82
|
+
return result;
|
|
83
|
+
}
|
|
84
|
+
let bytes = reader.bytes();
|
|
85
|
+
if (mimeType === CMMIPS_MIMETYPE) {
|
|
86
|
+
const mip = extractCmMips(bytes, result.logger); // Max quality
|
|
87
|
+
// const mip = extractCmMips(bytes, 2048); // High quality
|
|
88
|
+
// const mip = extractCmMips(bytes, 256); // Low quality
|
|
89
|
+
if ((mip === null || mip === void 0 ? void 0 : mip.data) === undefined) {
|
|
90
|
+
result.logger.warn("Could not extract image from CmMips", resourceUrl);
|
|
91
|
+
return result;
|
|
92
|
+
}
|
|
93
|
+
mimeType = getImageMimeType(mip.suffix);
|
|
94
|
+
bytes = mip.data;
|
|
95
|
+
}
|
|
96
|
+
const blob = new Blob([bytes], { type: mimeType });
|
|
97
|
+
const blobImageUrl = window.URL.createObjectURL(blob);
|
|
98
|
+
try {
|
|
99
|
+
result.image = yield loadImage(blobImageUrl);
|
|
100
|
+
return result;
|
|
101
|
+
}
|
|
102
|
+
catch (e) {
|
|
103
|
+
result.logger.warnFromCaught(e);
|
|
104
|
+
return result;
|
|
105
|
+
}
|
|
106
|
+
}));
|
|
107
|
+
textureImageWithMeta.logger.accumulated.forEach((p) => logger.addPrebaked(p));
|
|
108
|
+
return textureImageWithMeta.image;
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
export function loadTextureFromURL(url, renderEnvironment) {
|
|
112
|
+
const task = renderEnvironment.assetsManager.addTextureTask("(loadTextureFromURL)", url);
|
|
113
|
+
return new Promise((resolve, reject) => {
|
|
114
|
+
task.runTask(renderEnvironment.scene, () => {
|
|
115
|
+
resolve(task.texture);
|
|
116
|
+
}, reject);
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Derives a normal map from a supplied height map (aka bump-map). Classic materials in CET can use
|
|
121
|
+
* both height maps and normal maps but Babylon.js only supports normal maps.
|
|
122
|
+
*
|
|
123
|
+
* The derived normal map and the effect from `scale` is carefully crafted to be as similar to how
|
|
124
|
+
* the RedSDK 3D renderer in CET operates and match it's final output. Do not change this code to
|
|
125
|
+
* make it visually "better", the goal is instead to be as similar as possible.
|
|
126
|
+
*/
|
|
127
|
+
function deriveNormalMapFromHeightMap(image, amount, logger) {
|
|
128
|
+
const w = image.width;
|
|
129
|
+
const h = image.height;
|
|
130
|
+
const canvas = document.createElement("canvas");
|
|
131
|
+
canvas.width = w;
|
|
132
|
+
canvas.height = h;
|
|
133
|
+
const context = canvas.getContext("2d");
|
|
134
|
+
if (context !== null) {
|
|
135
|
+
const tick = performance.now();
|
|
136
|
+
context.drawImage(image, 0, 0);
|
|
137
|
+
const imgData = context.getImageData(0, 0, image.width, image.height);
|
|
138
|
+
const data = imgData.data;
|
|
139
|
+
// The scale conversion and default value comes from the CET source code.
|
|
140
|
+
const scale = amount ? 15 * amount : 0.1;
|
|
141
|
+
// Number of components, canvas returns RGBA
|
|
142
|
+
const c = 4;
|
|
143
|
+
// Go through the image and calculate a gray scale version, storing it in the A channel.
|
|
144
|
+
// Since this is legacy support code, we might as well support non-gray scale height maps,
|
|
145
|
+
// which we have seen in the wild. This is also how RedSDK in CET works according to docs.
|
|
146
|
+
for (let i = 0; i < w * h * c; i += c) {
|
|
147
|
+
data[i + 3] = (data[i] + data[i + 1] + data[i + 2]) / 3;
|
|
148
|
+
}
|
|
149
|
+
// Derive the height map to create a normal map, store the X and Y components in the R and
|
|
150
|
+
// G channels respectively.
|
|
151
|
+
//
|
|
152
|
+
// We are assuming that the map will be repeated when displayed since the Material Lab does
|
|
153
|
+
// not expose texture repeats in it's UI, and the default mode is wrapped. This means that
|
|
154
|
+
// edge sampling should wrap around as well.
|
|
155
|
+
for (let i = 0; i < w * h; i++) {
|
|
156
|
+
// The sample kernel:
|
|
157
|
+
// [x][r]
|
|
158
|
+
// [b]
|
|
159
|
+
//
|
|
160
|
+
// Produces results shifted half a pixel diagonally and is very local, but is about the // same as the kernel used in CET's render engine and also OpenGLs derivation extension.
|
|
161
|
+
const offset = i * c + 3; // A channel
|
|
162
|
+
const x = data[offset];
|
|
163
|
+
let r, b;
|
|
164
|
+
if (i % w === w - 1) {
|
|
165
|
+
// End of the row, sample from the beginning of the same row
|
|
166
|
+
r = data[offset - (w - 1) * c];
|
|
167
|
+
}
|
|
168
|
+
else {
|
|
169
|
+
r = data[offset + c];
|
|
170
|
+
}
|
|
171
|
+
if (i >= (h - 1) * w) {
|
|
172
|
+
// Last row, sample from the same column in the first row
|
|
173
|
+
b = data[offset - (h - 1) * w * c];
|
|
174
|
+
}
|
|
175
|
+
else {
|
|
176
|
+
b = data[offset + w * c];
|
|
177
|
+
}
|
|
178
|
+
// y-axis is inverted, OpenGL vs DirectX
|
|
179
|
+
const normal = new Vector3((x - r) * scale, (b - x) * scale, 255).normalize();
|
|
180
|
+
data[offset - 3] = Math.max(0, Math.min(255, normal.x * 128 + 127)); // R, x-axis
|
|
181
|
+
data[offset - 2] = Math.max(0, Math.min(255, normal.y * 128 + 127)); // G, y-axis
|
|
182
|
+
data[offset - 1] = Math.max(0, Math.min(255, normal.z * 128 + 127)); // B, z-axis
|
|
183
|
+
}
|
|
184
|
+
// Set the A channel to 255 to avoid any strange edge case and let it compress better.
|
|
185
|
+
for (let offset = 3; offset < w * h * c; offset += c) {
|
|
186
|
+
data[offset] = 255; // A
|
|
187
|
+
}
|
|
188
|
+
context.putImageData(imgData, 0, 0);
|
|
189
|
+
const convertedImage = new Image();
|
|
190
|
+
convertedImage.src = canvas.toDataURL("image/png");
|
|
191
|
+
logger.info(`Converting height based bump map`, `of size ${w}x${h} took ${Math.round(performance.now() - tick)}ms. For optimal performance, update all classic materials to use normal maps rather than height maps or switch to PBR materials.`);
|
|
192
|
+
return convertedImage;
|
|
193
|
+
}
|
|
194
|
+
return image;
|
|
195
|
+
}
|
|
196
|
+
function isGMaterial3DTexture(v) {
|
|
197
|
+
return v instanceof GMaterial3DTexture;
|
|
198
|
+
}
|
|
199
|
+
function getToImageLoadFunc(logger, renderEnvironment) {
|
|
200
|
+
return function (gm) {
|
|
201
|
+
let im;
|
|
202
|
+
if (gm === undefined || gm.textureUrl === undefined) {
|
|
203
|
+
im = Promise.resolve(undefined);
|
|
204
|
+
}
|
|
205
|
+
else {
|
|
206
|
+
im = loadCachedImage(logger, gm.textureUrl.value, renderEnvironment);
|
|
207
|
+
}
|
|
208
|
+
return { gm, im };
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
function getApplyImageConversionsFunc(logger, renderEnvironment) {
|
|
212
|
+
return function (gmv) {
|
|
213
|
+
const { gm } = gmv;
|
|
214
|
+
if (gm instanceof Bump3D && !gm.prepared) {
|
|
215
|
+
/* A non-"prepared" bump texture means that the bump map is actually an old school
|
|
216
|
+
* height map rather than a more modern (and faster) normal map. Babylon.js does not
|
|
217
|
+
* support height maps so we will need to convert it manually.
|
|
218
|
+
* https://forum.babylonjs.com/t/old-school-bump-map-height-map-not-supported/13447
|
|
219
|
+
*
|
|
220
|
+
* Normal map vs Height map:
|
|
221
|
+
* https://docs.unity3d.com/2019.3/Documentation/Manual/StandardShaderMaterialParameterNormalMap.html
|
|
222
|
+
*/
|
|
223
|
+
const textureUrl = gm.textureUrl;
|
|
224
|
+
if (textureUrl !== undefined) {
|
|
225
|
+
const mimeType = getImageMimeType(getFileExtension(textureUrl.value));
|
|
226
|
+
if (mimeType === "image/png" || mimeType === "image/jpeg") {
|
|
227
|
+
// Wrap the pure image load promise with an normal map derive promise
|
|
228
|
+
gmv.im = renderEnvironment.derivedNormalMapCache.get(
|
|
229
|
+
// The derivation depends on gm.amount, include it in the key. See WRD-664.
|
|
230
|
+
`${textureUrl.value}_scale_${gm.amount}`, () => __awaiter(this, void 0, void 0, function* () {
|
|
231
|
+
let image = yield gmv.im;
|
|
232
|
+
if (image !== undefined) {
|
|
233
|
+
image = deriveNormalMapFromHeightMap(image, gm.amount, logger);
|
|
234
|
+
}
|
|
235
|
+
return image;
|
|
236
|
+
}));
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
return gmv;
|
|
241
|
+
};
|
|
242
|
+
}
|
|
243
|
+
function getToTextureLoadFunc(renderEnvironment) {
|
|
244
|
+
return function (gmv) {
|
|
245
|
+
const { gm, im } = gmv;
|
|
246
|
+
const tx = (() => __awaiter(this, void 0, void 0, function* () {
|
|
247
|
+
const image = yield im;
|
|
248
|
+
if (image === undefined) {
|
|
249
|
+
return undefined;
|
|
250
|
+
}
|
|
251
|
+
return loadTextureFromURL(image.src, renderEnvironment);
|
|
252
|
+
}))();
|
|
253
|
+
return { gm, tx };
|
|
254
|
+
};
|
|
255
|
+
}
|
|
256
|
+
function getApplyTexturePropertiesFromGMaterialFunc(logger) {
|
|
257
|
+
return function (gmv) {
|
|
258
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
259
|
+
const gm = gmv.gm;
|
|
260
|
+
const tx = yield gmv.tx;
|
|
261
|
+
if (tx !== undefined) {
|
|
262
|
+
tx.wrapU = symWrapToBabylonWrap(logger, gm.wrapU);
|
|
263
|
+
tx.wrapV = symWrapToBabylonWrap(logger, gm.wrapV);
|
|
264
|
+
tx.uScale = gm.uScale;
|
|
265
|
+
tx.vScale = gm.vScale;
|
|
266
|
+
tx.uOffset = gm.uOffset;
|
|
267
|
+
tx.vOffset = gm.vOffset;
|
|
268
|
+
tx.uRotationCenter = 0;
|
|
269
|
+
tx.vRotationCenter = 0;
|
|
270
|
+
tx.wAng = gm.rot;
|
|
271
|
+
tx.updateSamplingMode(symFilterToBabylonFilter(gm.minFilter, gm.magFilter, gm.mipFilter));
|
|
272
|
+
}
|
|
273
|
+
return { gm, tx };
|
|
274
|
+
});
|
|
275
|
+
};
|
|
276
|
+
}
|
|
277
|
+
export function getTextures(logger, renderEnvironment, gm) {
|
|
278
|
+
var _a, _b, _c, _d, _e, _f, _g;
|
|
279
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
280
|
+
let textures = [];
|
|
281
|
+
if (gm instanceof GMaterialClassic) {
|
|
282
|
+
// Ambient is not used in CET.
|
|
283
|
+
// Emissive no longer exist in CET.
|
|
284
|
+
// Specular is rare (and we can currently only handle its general value,
|
|
285
|
+
// not its texture since the MeshStandardMaterial does not accept such a thing.)
|
|
286
|
+
textures = [gm.bump, gm.diffuse, gm.specular, gm.transparency];
|
|
287
|
+
}
|
|
288
|
+
if (gm instanceof GMaterialPBR) {
|
|
289
|
+
textures = [
|
|
290
|
+
(_a = gm.base) === null || _a === void 0 ? void 0 : _a.texture,
|
|
291
|
+
(_b = gm.emissive) === null || _b === void 0 ? void 0 : _b.texture,
|
|
292
|
+
(_c = gm.metallic) === null || _c === void 0 ? void 0 : _c.texture,
|
|
293
|
+
(_d = gm.normal) === null || _d === void 0 ? void 0 : _d.texture,
|
|
294
|
+
(_e = gm.occlusion) === null || _e === void 0 ? void 0 : _e.texture,
|
|
295
|
+
(_f = gm.opacity) === null || _f === void 0 ? void 0 : _f.texture,
|
|
296
|
+
(_g = gm.roughness) === null || _g === void 0 ? void 0 : _g.texture,
|
|
297
|
+
];
|
|
298
|
+
}
|
|
299
|
+
return yield Promise.all(textures
|
|
300
|
+
.filter(isGMaterial3DTexture)
|
|
301
|
+
.map(getToImageLoadFunc(logger, renderEnvironment))
|
|
302
|
+
.map(getApplyImageConversionsFunc(logger, renderEnvironment))
|
|
303
|
+
.map(getToTextureLoadFunc(renderEnvironment))
|
|
304
|
+
.map(getApplyTexturePropertiesFromGMaterialFunc(logger)));
|
|
305
|
+
});
|
|
306
|
+
}
|