@configura/babylon-view 1.3.0-alpha.1 → 1.3.0-alpha.5
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/dist/animation/coordinator/CoordinatorDropAndSpin.js +1 -1
- package/dist/animation/coordinator/CoordinatorPulseBounce.js +1 -1
- package/dist/animation/coordinator/CoordinatorPulseHighlight.js +1 -1
- package/dist/animation/coordinator/CoordinatorPulseInflate.js +1 -1
- package/dist/camera/CfgArcRotateCameraPointersInput.d.ts +16 -0
- package/dist/camera/CfgArcRotateCameraPointersInput.js +17 -15
- package/dist/camera/CfgOrbitalCamera.d.ts +5 -1
- package/dist/camera/CfgOrbitalCamera.js +24 -7
- package/dist/engine/EngineCreator.js +1 -1
- package/dist/geometry/CfgGeometry.d.ts +22 -5
- package/dist/geometry/CfgGeometry.js +131 -102
- package/dist/geometry/CfgMesh.d.ts +4 -1
- package/dist/geometry/CfgMesh.js +32 -2
- package/dist/geometry/stretch/CfgMorphTarget.d.ts +16 -0
- package/dist/geometry/stretch/CfgMorphTarget.js +65 -0
- package/dist/geometry/stretch/CfgStretchData.d.ts +115 -0
- package/dist/geometry/stretch/CfgStretchData.js +340 -0
- package/dist/geometry/stretch/CfgStretchMorphGeometry.d.ts +17 -0
- package/dist/geometry/stretch/CfgStretchMorphGeometry.js +95 -0
- package/dist/material/CfgMaterial.d.ts +19 -4
- package/dist/material/CfgMaterial.js +38 -30
- package/dist/material/material.js +3 -3
- package/dist/material/texture.js +10 -8
- package/dist/nodes/CfgDeferredMeshNode.d.ts +8 -1
- package/dist/nodes/CfgDeferredMeshNode.js +48 -18
- package/dist/nodes/CfgProductNode.d.ts +68 -3
- package/dist/nodes/CfgProductNode.js +130 -38
- package/dist/nodes/CfgSymNode.d.ts +14 -6
- package/dist/nodes/CfgSymNode.js +50 -17
- package/dist/nodes/CfgSymRootNode.d.ts +18 -6
- package/dist/nodes/CfgSymRootNode.js +62 -17
- package/dist/nodes/CfgTransformNode.d.ts +4 -0
- package/dist/nodes/CfgTransformNode.js +4 -2
- package/dist/utilities/CfgBoundingBox.d.ts +5 -0
- package/dist/utilities/CfgBoundingBox.js +18 -1
- package/dist/utilities/anchor/anchor.d.ts +52 -0
- package/dist/utilities/anchor/anchor.js +136 -0
- package/dist/utilities/anchor/anchorMap.d.ts +21 -0
- package/dist/utilities/anchor/anchorMap.js +111 -0
- package/dist/utilities/utilities3D.d.ts +44 -0
- package/dist/utilities/utilities3D.js +97 -19
- package/dist/utilities/utilitiesSymRootIdentifier.d.ts +3 -1
- package/dist/utilities/utilitiesSymRootIdentifier.js +10 -4
- package/dist/view/BaseView.d.ts +9 -1
- package/dist/view/BaseView.js +16 -10
- package/dist/view/RenderEnv.d.ts +6 -1
- package/dist/view/SingleProductDefaultCameraView.js +2 -1
- package/dist/view/SingleProductDefaultCameraViewConfiguration.d.ts +2 -0
- package/dist/view/SingleProductView.d.ts +8 -1
- package/dist/view/SingleProductView.js +1 -0
- package/package.json +5 -5
|
@@ -36,7 +36,7 @@ export class CoordinatorDropAndSpin extends CoordinatorNodeQueues {
|
|
|
36
36
|
}
|
|
37
37
|
static getCoordinatorCreator(config) {
|
|
38
38
|
return (view, phaseObservable) => {
|
|
39
|
-
return new
|
|
39
|
+
return new this(view, phaseObservable, config || {});
|
|
40
40
|
};
|
|
41
41
|
}
|
|
42
42
|
destroy() {
|
|
@@ -15,7 +15,7 @@ const defaultConfig = Object.assign(Object.assign({}, baseDefaultConfig), { heig
|
|
|
15
15
|
export class CoordinatorPulseBounce extends CoordinatorPulse {
|
|
16
16
|
static getCoordinatorCreator(config) {
|
|
17
17
|
return (view, phaseObservable) => {
|
|
18
|
-
return new
|
|
18
|
+
return new this(view, phaseObservable, config || {});
|
|
19
19
|
};
|
|
20
20
|
}
|
|
21
21
|
constructor(view, phaseObservable, config) {
|
|
@@ -14,7 +14,7 @@ const defaultConfig = Object.assign(Object.assign({}, baseDefaultConfig), animat
|
|
|
14
14
|
export class CoordinatorPulseHighlight extends CoordinatorPulse {
|
|
15
15
|
static getCoordinatorCreator(config) {
|
|
16
16
|
return (view, phaseObservable) => {
|
|
17
|
-
return new
|
|
17
|
+
return new this(view, phaseObservable, config || {});
|
|
18
18
|
};
|
|
19
19
|
}
|
|
20
20
|
constructor(view, phaseObservable, config) {
|
|
@@ -14,7 +14,7 @@ const defaultConfig = Object.assign(Object.assign({}, baseDefaultConfig), { size
|
|
|
14
14
|
export class CoordinatorPulseInflate extends CoordinatorPulse {
|
|
15
15
|
static getCoordinatorCreator(config) {
|
|
16
16
|
return (view, phaseObservable) => {
|
|
17
|
-
return new
|
|
17
|
+
return new this(view, phaseObservable, config || {});
|
|
18
18
|
};
|
|
19
19
|
}
|
|
20
20
|
constructor(view, phaseObservable, config) {
|
|
@@ -1,5 +1,21 @@
|
|
|
1
1
|
import { ArcRotateCameraPointersInput } from "@babylonjs/core/Cameras/Inputs/arcRotateCameraPointersInput.js";
|
|
2
2
|
import "@babylonjs/core/Engines/Extensions/engine.webVR.js";
|
|
3
|
+
/**
|
|
4
|
+
* This class exists to workaround a severe touch input issue that exists on at least iOS 13.4 in
|
|
5
|
+
* Babylon release 4.1.0 and possibly later.
|
|
6
|
+
*
|
|
7
|
+
* The main workaround is marked with "--- START OF WORKAROUND ---" in the source below.
|
|
8
|
+
*
|
|
9
|
+
* Most of the code below is copied from the Babylon source class BaseCameraPointersInput.ts,
|
|
10
|
+
* which ArcRotateCameraPointersInput extends. Some additional private support code from
|
|
11
|
+
* BaseCameraPointersInput is also included and suffixed with "CfgPatch".
|
|
12
|
+
*
|
|
13
|
+
* @warning: DO NOT do any other changes to this file! They WILL be lost when this workaround is no
|
|
14
|
+
* longer needed.
|
|
15
|
+
*
|
|
16
|
+
* @todo Every time the babylon module is updated to a new release, test if the workaround is still
|
|
17
|
+
* needed. If not, simply remove this file and update CfgOrbitalCamera.ts to not use it.
|
|
18
|
+
*/
|
|
3
19
|
export declare class CfgArcRotateCameraPointersInput extends ArcRotateCameraPointersInput {
|
|
4
20
|
attachControl(noPreventDefault?: boolean): void;
|
|
5
21
|
detachControl(ignored?: any): void;
|
|
@@ -1,21 +1,23 @@
|
|
|
1
|
-
/// This class exists to workaround a severe touch input issue that exists on at least iOS 13.4 in
|
|
2
|
-
/// Babylon release 4.1.0 and possibly later.
|
|
3
|
-
///
|
|
4
|
-
/// The main workaround is marked with "--- START OF WORKAROUND ---" in the source below.
|
|
5
|
-
///
|
|
6
|
-
/// Most of the code below is copied from the Babylon source class BaseCameraPointersInput.ts,
|
|
7
|
-
/// which ArcRotateCameraPointersInput extends. Some additional private support code from
|
|
8
|
-
/// BaseCameraPointersInput is also included and suffixed with "CfgPatch".
|
|
9
|
-
///
|
|
10
|
-
/// WARNING: DO NOT do any other changes to this file! They WILL be lost when this workaround is no
|
|
11
|
-
/// longer needed.
|
|
12
|
-
///
|
|
13
|
-
/// TODO: Every time the babylon module is updated to a new release, test if the workaround is still
|
|
14
|
-
/// needed. If not, simply remove this file and update CfgOrbitalCamera.ts to not use it.
|
|
15
1
|
import { ArcRotateCameraPointersInput } from "@babylonjs/core/Cameras/Inputs/arcRotateCameraPointersInput.js";
|
|
16
2
|
import "@babylonjs/core/Engines/Extensions/engine.webVR.js"; // isInVRExclusivePointerMode
|
|
17
|
-
import { PointerEventTypes } from "@babylonjs/core/Events/pointerEvents.js";
|
|
3
|
+
import { PointerEventTypes, } from "@babylonjs/core/Events/pointerEvents.js";
|
|
18
4
|
import { Tools } from "@babylonjs/core/Misc/tools.js";
|
|
5
|
+
/**
|
|
6
|
+
* This class exists to workaround a severe touch input issue that exists on at least iOS 13.4 in
|
|
7
|
+
* Babylon release 4.1.0 and possibly later.
|
|
8
|
+
*
|
|
9
|
+
* The main workaround is marked with "--- START OF WORKAROUND ---" in the source below.
|
|
10
|
+
*
|
|
11
|
+
* Most of the code below is copied from the Babylon source class BaseCameraPointersInput.ts,
|
|
12
|
+
* which ArcRotateCameraPointersInput extends. Some additional private support code from
|
|
13
|
+
* BaseCameraPointersInput is also included and suffixed with "CfgPatch".
|
|
14
|
+
*
|
|
15
|
+
* @warning: DO NOT do any other changes to this file! They WILL be lost when this workaround is no
|
|
16
|
+
* longer needed.
|
|
17
|
+
*
|
|
18
|
+
* @todo Every time the babylon module is updated to a new release, test if the workaround is still
|
|
19
|
+
* needed. If not, simply remove this file and update CfgOrbitalCamera.ts to not use it.
|
|
20
|
+
*/
|
|
19
21
|
export class CfgArcRotateCameraPointersInput extends ArcRotateCameraPointersInput {
|
|
20
22
|
constructor() {
|
|
21
23
|
super(...arguments);
|
|
@@ -41,7 +41,11 @@ export declare class CfgOrbitalCamera extends WorkaroundCfgOrbitalCamera {
|
|
|
41
41
|
private _currentMinDistanceToFitModelInView;
|
|
42
42
|
private _orbitalCameraControlObservable;
|
|
43
43
|
private _previousOrbitalCameraControlProps;
|
|
44
|
-
|
|
44
|
+
_disableSubFloorCam: boolean;
|
|
45
|
+
set disableSubFloorCam(v: boolean);
|
|
46
|
+
_lowestAllowedCameraHeight: number | undefined;
|
|
47
|
+
set lowestAllowedCameraHeight(v: number | undefined);
|
|
48
|
+
get lowestAllowedCameraHeight(): number | undefined;
|
|
45
49
|
disableAutomaticSizing: boolean;
|
|
46
50
|
disableZoom: boolean;
|
|
47
51
|
private _externalControlHasHappened;
|
|
@@ -29,7 +29,8 @@ export class CfgOrbitalCamera extends WorkaroundCfgOrbitalCamera {
|
|
|
29
29
|
this._canvas = _canvas;
|
|
30
30
|
this._currentBoundingBox = new CfgBoundingBox();
|
|
31
31
|
this._latestSignificantChangeBoundingBox = new CfgBoundingBox();
|
|
32
|
-
this.
|
|
32
|
+
this._disableSubFloorCam = false;
|
|
33
|
+
this._lowestAllowedCameraHeight = undefined;
|
|
33
34
|
this.disableAutomaticSizing = false;
|
|
34
35
|
this.disableZoom = false;
|
|
35
36
|
this._externalControlHasHappened = false;
|
|
@@ -64,6 +65,24 @@ export class CfgOrbitalCamera extends WorkaroundCfgOrbitalCamera {
|
|
|
64
65
|
this.onViewMatrixChangedObservable.removeCallback(this._boundNotifyCameraControlListeners);
|
|
65
66
|
super.dispose();
|
|
66
67
|
}
|
|
68
|
+
set disableSubFloorCam(v) {
|
|
69
|
+
this._disableSubFloorCam = v;
|
|
70
|
+
}
|
|
71
|
+
set lowestAllowedCameraHeight(v) {
|
|
72
|
+
this._lowestAllowedCameraHeight = v;
|
|
73
|
+
}
|
|
74
|
+
get lowestAllowedCameraHeight() {
|
|
75
|
+
if (this._lowestAllowedCameraHeight !== undefined) {
|
|
76
|
+
if (this._disableSubFloorCam) {
|
|
77
|
+
return Math.max(0, this._lowestAllowedCameraHeight);
|
|
78
|
+
}
|
|
79
|
+
return this._lowestAllowedCameraHeight;
|
|
80
|
+
}
|
|
81
|
+
if (this._disableSubFloorCam) {
|
|
82
|
+
return 0;
|
|
83
|
+
}
|
|
84
|
+
return undefined;
|
|
85
|
+
}
|
|
67
86
|
forceRadius(r) {
|
|
68
87
|
this._radius = r;
|
|
69
88
|
}
|
|
@@ -188,10 +207,6 @@ export class CfgOrbitalCamera extends WorkaroundCfgOrbitalCamera {
|
|
|
188
207
|
this.maxZ = cameraDistance + graceDistance;
|
|
189
208
|
}
|
|
190
209
|
_applyNoSneakPeeking() {
|
|
191
|
-
if (!this.disableSubFloorCam) {
|
|
192
|
-
this.upperBetaLimit = MAX_UPPER_BETA_LIMIT;
|
|
193
|
-
return;
|
|
194
|
-
}
|
|
195
210
|
//It might be impolite to look at a Cfg models underside
|
|
196
211
|
const noPeekLimit = this._calculateNoPeekingLimit();
|
|
197
212
|
if (noPeekLimit === undefined || !isFinite(noPeekLimit)) {
|
|
@@ -222,10 +237,12 @@ export class CfgOrbitalCamera extends WorkaroundCfgOrbitalCamera {
|
|
|
222
237
|
}
|
|
223
238
|
_calculateNoPeekingLimit() {
|
|
224
239
|
const bb = this._currentBoundingBox;
|
|
225
|
-
if (bb.isEmpty) {
|
|
240
|
+
if (bb.isEmpty || this.lowestAllowedCameraHeight === undefined) {
|
|
226
241
|
return undefined;
|
|
227
242
|
}
|
|
228
|
-
const bottomDepth = bb.minimum.y -
|
|
243
|
+
const bottomDepth = bb.minimum.y -
|
|
244
|
+
this.target.y +
|
|
245
|
+
(bb.maximum.y - bb.minimum.y) * this.lowestAllowedCameraHeight;
|
|
229
246
|
const cameraDistance = this.radius;
|
|
230
247
|
const lowestAngleUnderHorizonRad = Math.asin(bottomDepth / cameraDistance);
|
|
231
248
|
return lowestAngleUnderHorizonRad;
|
|
@@ -4,7 +4,7 @@ export function getDefaultEngine(canvas, width, height) {
|
|
|
4
4
|
// see WEB-8493 for additional details. It should be safe to apply to all browsers.
|
|
5
5
|
//
|
|
6
6
|
// TODO: Remove this workaround when upgrading to Babylon.js 5.0
|
|
7
|
-
const engine = new Engine(canvas, true, { xrCompatible: false }, true);
|
|
7
|
+
const engine = new Engine(canvas, true, { xrCompatible: false, preserveDrawingBuffer: true }, true);
|
|
8
8
|
engine.setSize(Math.floor(width), Math.floor(height));
|
|
9
9
|
return engine;
|
|
10
10
|
}
|
|
@@ -1,13 +1,30 @@
|
|
|
1
1
|
import { Geometry } from "@babylonjs/core/Meshes/geometry.js";
|
|
2
|
-
import { Mesh } from "@babylonjs/core/Meshes/mesh.js";
|
|
3
2
|
import { Scene } from "@babylonjs/core/scene.js";
|
|
4
3
|
import { ATriMeshF } from "@configura/web-core/dist/cm/core3D/ATriMeshF.js";
|
|
5
|
-
import {
|
|
4
|
+
import { UVMapper } from "@configura/web-core/dist/cm/core3D/uvmapper/UVMapper";
|
|
5
|
+
import { Logger, LogObservable, LogProducer } from "@configura/web-utilities";
|
|
6
|
+
import { CfgStretchData } from "./stretch/CfgStretchData.js";
|
|
7
|
+
import { CfgMorphTwiceStretchedGeometry } from "./stretch/CfgStretchMorphGeometry.js";
|
|
8
|
+
export declare function makeIndexMap(indices: Uint32Array | number[]): {
|
|
9
|
+
indexMap: Map<number, number>;
|
|
10
|
+
packedIndices: Uint32Array;
|
|
11
|
+
};
|
|
6
12
|
export declare class CfgGeometry extends Geometry implements LogProducer {
|
|
7
|
-
private
|
|
13
|
+
private readonly _ind;
|
|
14
|
+
private readonly _pos;
|
|
15
|
+
private readonly _uvs;
|
|
16
|
+
private readonly _nor;
|
|
17
|
+
private readonly _tan;
|
|
18
|
+
private readonly _col;
|
|
19
|
+
readonly morphGeometries: CfgMorphTwiceStretchedGeometry[];
|
|
8
20
|
logger: LogObservable;
|
|
9
|
-
|
|
21
|
+
static fromATriMeshF(logger: Logger, id: string, mesh: ATriMeshF, scene: Scene, stretchDatas: CfgStretchData[], uvMapper: UVMapper | undefined): CfgGeometry;
|
|
22
|
+
private constructor();
|
|
23
|
+
shallowClone(): CfgGeometry;
|
|
24
|
+
/**
|
|
25
|
+
* Returns a new CfgGeometry containing a subset of this CfgGeometry based on the given
|
|
26
|
+
* indices.
|
|
27
|
+
*/
|
|
10
28
|
cloneWithSubset(indices: Uint32Array | number[]): CfgGeometry;
|
|
11
|
-
releaseForMesh(mesh: Mesh, shouldDispose?: boolean | undefined): void;
|
|
12
29
|
}
|
|
13
30
|
//# sourceMappingURL=CfgGeometry.d.ts.map
|
|
@@ -1,117 +1,146 @@
|
|
|
1
1
|
import { Geometry } from "@babylonjs/core/Meshes/geometry.js";
|
|
2
2
|
import { VertexData } from "@babylonjs/core/Meshes/mesh.vertexData.js";
|
|
3
3
|
import { LogObservable } from "@configura/web-utilities";
|
|
4
|
+
import { CfgMorphTwiceStretchedGeometry } from "./stretch/CfgStretchMorphGeometry.js";
|
|
5
|
+
export function makeIndexMap(indices) {
|
|
6
|
+
// Loop through all old index values, creating an index map between each unique old
|
|
7
|
+
// index value and their corresponding new index into the shorter arrays.
|
|
8
|
+
const indicesLength = indices.length;
|
|
9
|
+
const packedIndices = new Uint32Array(indicesLength);
|
|
10
|
+
const indexMap = new Map();
|
|
11
|
+
for (let i = 0; i < indicesLength; i++) {
|
|
12
|
+
const index = indices[i];
|
|
13
|
+
let newIndex = indexMap.get(index);
|
|
14
|
+
if (newIndex === undefined) {
|
|
15
|
+
newIndex = indexMap.size;
|
|
16
|
+
indexMap.set(index, newIndex);
|
|
17
|
+
}
|
|
18
|
+
packedIndices[i] = newIndex;
|
|
19
|
+
}
|
|
20
|
+
return { indexMap, packedIndices };
|
|
21
|
+
}
|
|
4
22
|
export class CfgGeometry extends Geometry {
|
|
5
|
-
constructor(id,
|
|
23
|
+
constructor(id, scene, _ind, _pos, _uvs, _nor, _tan, _col, morphGeometries) {
|
|
6
24
|
super(id, scene);
|
|
25
|
+
this._ind = _ind;
|
|
26
|
+
this._pos = _pos;
|
|
27
|
+
this._uvs = _uvs;
|
|
28
|
+
this._nor = _nor;
|
|
29
|
+
this._tan = _tan;
|
|
30
|
+
this._col = _col;
|
|
31
|
+
this.morphGeometries = morphGeometries;
|
|
7
32
|
this.logger = new LogObservable();
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
33
|
+
const vertexData = new VertexData();
|
|
34
|
+
vertexData.indices = _ind;
|
|
35
|
+
vertexData.positions = _pos;
|
|
36
|
+
vertexData.uvs = _uvs;
|
|
37
|
+
vertexData.normals = _nor;
|
|
38
|
+
vertexData.tangents = _tan;
|
|
39
|
+
vertexData.colors = _col;
|
|
40
|
+
this.setAllVerticesData(vertexData, false);
|
|
41
|
+
}
|
|
42
|
+
static fromATriMeshF(logger, id, mesh, scene, stretchDatas, uvMapper) {
|
|
43
|
+
const positions = mesh.vertices;
|
|
44
|
+
const normals = 0 < mesh.normals.length ? mesh.normals : undefined;
|
|
45
|
+
const meshUvs = mesh.uvs;
|
|
19
46
|
let uvs;
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
ind = mesh.indices;
|
|
25
|
-
pos = mesh.vertices;
|
|
26
|
-
uvs = hasUVs ? mesh.uvs : null;
|
|
27
|
-
nor = hasNor ? mesh.normals : null;
|
|
28
|
-
tan = hasTan ? mesh.tangents : null;
|
|
29
|
-
col = hasCol ? mesh.colors : null;
|
|
47
|
+
if (meshUvs.length > 0) {
|
|
48
|
+
// Always use pre-baked UVs from CET if they exist.
|
|
49
|
+
// See comment on UVMapper.createUVCoordinates for more details.
|
|
50
|
+
uvs = meshUvs;
|
|
30
51
|
}
|
|
31
|
-
else {
|
|
32
|
-
const
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
// The length of the indexMap tells us how many unique index value we found.
|
|
51
|
-
const count = indexMap.size;
|
|
52
|
-
console.log(`Creating CfgGeometry subset (${num / 3}/${mesh.indices.length / 3} triangles, ${count} unique indices)`);
|
|
53
|
-
pos = new Float32Array(count * 3);
|
|
54
|
-
uvs = hasUVs ? new Float32Array(count * 2) : null;
|
|
55
|
-
nor = hasNor ? new Float32Array(count * 3) : null;
|
|
56
|
-
tan = hasTan ? new Float32Array(count * 4) : null;
|
|
57
|
-
col = hasCol ? new Float32Array(count * 4) : null;
|
|
58
|
-
// Use the index map to move only the referenced used values from all the attributes
|
|
59
|
-
// over to new smaller arrays.
|
|
60
|
-
//
|
|
61
|
-
// A note about the code: The code below is "unrolled" by design since it can be a lot
|
|
62
|
-
// of indexes to process. A more compact version with an inner loop of 0..3 with some
|
|
63
|
-
// additional if checks turned out to be up to 30 times slower in Chrome (1.5ms vs
|
|
64
|
-
// 40ms) for the "repack" call when loading and splitting the forklift model.
|
|
65
|
-
for (const [index, newIndex] of indexMap) {
|
|
66
|
-
pos[newIndex * 3] = mesh.vertices[index * 3];
|
|
67
|
-
pos[newIndex * 3 + 1] = mesh.vertices[index * 3 + 1];
|
|
68
|
-
pos[newIndex * 3 + 2] = mesh.vertices[index * 3 + 2];
|
|
69
|
-
if (uvs) {
|
|
70
|
-
uvs[newIndex * 2] = mesh.uvs[index * 2];
|
|
71
|
-
uvs[newIndex * 2 + 1] = mesh.uvs[index * 2 + 1];
|
|
72
|
-
}
|
|
73
|
-
if (nor) {
|
|
74
|
-
nor[newIndex * 3] = mesh.normals[index * 3];
|
|
75
|
-
nor[newIndex * 3 + 1] = mesh.normals[index * 3 + 1];
|
|
76
|
-
nor[newIndex * 3 + 2] = mesh.normals[index * 3 + 2];
|
|
77
|
-
}
|
|
78
|
-
if (tan) {
|
|
79
|
-
tan[newIndex * 4] = mesh.tangents[index * 4];
|
|
80
|
-
tan[newIndex * 4 + 1] = mesh.tangents[index * 4 + 1];
|
|
81
|
-
tan[newIndex * 4 + 2] = mesh.tangents[index * 4 + 2];
|
|
82
|
-
tan[newIndex * 4 + 3] = mesh.tangents[index * 4 + 3];
|
|
83
|
-
}
|
|
84
|
-
if (col) {
|
|
85
|
-
col[newIndex * 4] = mesh.colors[index * 4];
|
|
86
|
-
col[newIndex * 4 + 1] = mesh.colors[index * 4 + 1];
|
|
87
|
-
col[newIndex * 4 + 2] = mesh.colors[index * 4 + 2];
|
|
88
|
-
col[newIndex * 4 + 3] = mesh.colors[index * 4 + 3];
|
|
89
|
-
}
|
|
52
|
+
else if (uvMapper !== undefined && normals !== undefined) {
|
|
53
|
+
const min = [
|
|
54
|
+
Number.POSITIVE_INFINITY,
|
|
55
|
+
Number.POSITIVE_INFINITY,
|
|
56
|
+
Number.POSITIVE_INFINITY,
|
|
57
|
+
];
|
|
58
|
+
const max = [
|
|
59
|
+
Number.NEGATIVE_INFINITY,
|
|
60
|
+
Number.NEGATIVE_INFINITY,
|
|
61
|
+
Number.NEGATIVE_INFINITY,
|
|
62
|
+
];
|
|
63
|
+
// Calculate the bounds for each axis based on vertex positions
|
|
64
|
+
let p = 0;
|
|
65
|
+
let axis = 0;
|
|
66
|
+
for (let i = 0; i < positions.length; i++) {
|
|
67
|
+
p = positions[i];
|
|
68
|
+
axis = i % 3; // x, y, z
|
|
69
|
+
min[axis] = Math.min(min[axis], p);
|
|
70
|
+
max[axis] = Math.max(max[axis], p);
|
|
90
71
|
}
|
|
72
|
+
const size = max.map((m, axis) => m - min[axis]);
|
|
73
|
+
uvs = uvMapper.createUVCoordinates(logger, positions, {
|
|
74
|
+
min,
|
|
75
|
+
max,
|
|
76
|
+
size,
|
|
77
|
+
}, normals);
|
|
91
78
|
}
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
vertexData.normals = nor;
|
|
97
|
-
vertexData.tangents = tan;
|
|
98
|
-
vertexData.colors = col;
|
|
99
|
-
this.setAllVerticesData(vertexData, false);
|
|
79
|
+
else if (uvMapper !== undefined) {
|
|
80
|
+
logger.warn("Mesh uses UV-mapper without mesh normals which is not supported");
|
|
81
|
+
}
|
|
82
|
+
return new this(id, scene, mesh.indices, positions, uvs !== undefined && uvs.length > 0 ? uvs : null, normals || null, mesh.tangents.length > 0 ? mesh.tangents : null, mesh.colors.length > 0 ? mesh.colors : null, stretchDatas.map((stretchData) => CfgMorphTwiceStretchedGeometry.make(logger, uvMapper, stretchData, positions, normals, uvs)));
|
|
100
83
|
}
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
return new CfgGeometry(this.id + ` (subset ${indices.length})`, this._aTriMesh, this.getScene(), indices);
|
|
84
|
+
shallowClone() {
|
|
85
|
+
return new CfgGeometry(this.id, this.getScene(), this._ind, this._pos, this._uvs, this._nor, this._tan, this._col, this.morphGeometries);
|
|
104
86
|
}
|
|
105
|
-
|
|
106
|
-
*
|
|
107
|
-
*
|
|
108
|
-
*
|
|
109
|
-
* IIRC we are using a geometry cache which is probably holding on firmly to this instance
|
|
110
|
-
* and reusing it for the next time we need the same mesh. If that cache is ever cleared,
|
|
111
|
-
* it should also dispose the geometry inside it.
|
|
112
|
-
*
|
|
87
|
+
/**
|
|
88
|
+
* Returns a new CfgGeometry containing a subset of this CfgGeometry based on the given
|
|
89
|
+
* indices.
|
|
113
90
|
*/
|
|
114
|
-
|
|
115
|
-
|
|
91
|
+
cloneWithSubset(indices) {
|
|
92
|
+
const thisInd = this._ind;
|
|
93
|
+
const thisPos = this._pos;
|
|
94
|
+
const thisUvs = this._uvs;
|
|
95
|
+
const thisNor = this._nor;
|
|
96
|
+
const thisCol = this._col;
|
|
97
|
+
const thisTan = this._tan;
|
|
98
|
+
const indicesLength = indices.length;
|
|
99
|
+
// Babylon will use all vertex positions in the Geometry (not only the ones referenced
|
|
100
|
+
// by the indices) to determine the bounding box. As such, we need to go through the
|
|
101
|
+
// the new subset of indices and only keep the references values in each attribute.
|
|
102
|
+
const { indexMap, packedIndices: ind } = makeIndexMap(indices);
|
|
103
|
+
// The length of the indexMap tells us how many unique index value we found.
|
|
104
|
+
const count = indexMap.size;
|
|
105
|
+
console.log(`Creating CfgGeometry subset (${indicesLength / 3}/${thisInd.length / 3} triangles, ${count} unique indices)`);
|
|
106
|
+
const pos = new Float32Array(count * 3);
|
|
107
|
+
const uvs = thisUvs && new Float32Array(count * 2);
|
|
108
|
+
const nor = thisNor && new Float32Array(count * 3);
|
|
109
|
+
const tan = thisTan && new Float32Array(count * 4);
|
|
110
|
+
const col = thisCol && new Float32Array(count * 4);
|
|
111
|
+
// Use the index map to move only the referenced used values from all the attributes
|
|
112
|
+
// over to new smaller arrays.
|
|
113
|
+
//
|
|
114
|
+
// A note about the code: The code below is "unrolled" by design since it can be a lot
|
|
115
|
+
// of indexes to process. A more compact version with an inner loop of 0..3 with some
|
|
116
|
+
// additional if checks turned out to be up to 30 times slower in Chrome (1.5ms vs
|
|
117
|
+
// 40ms) for the "repack" call when loading and splitting the forklift model.
|
|
118
|
+
for (const [oldIndex, newIndex] of indexMap) {
|
|
119
|
+
pos[newIndex * 3] = thisPos[oldIndex * 3];
|
|
120
|
+
pos[newIndex * 3 + 1] = thisPos[oldIndex * 3 + 1];
|
|
121
|
+
pos[newIndex * 3 + 2] = thisPos[oldIndex * 3 + 2];
|
|
122
|
+
if (uvs !== null && thisUvs !== null) {
|
|
123
|
+
uvs[newIndex * 2] = thisUvs[oldIndex * 2];
|
|
124
|
+
uvs[newIndex * 2 + 1] = thisUvs[oldIndex * 2 + 1];
|
|
125
|
+
}
|
|
126
|
+
if (nor !== null && thisNor !== null) {
|
|
127
|
+
nor[newIndex * 3] = thisNor[oldIndex * 3];
|
|
128
|
+
nor[newIndex * 3 + 1] = thisNor[oldIndex * 3 + 1];
|
|
129
|
+
nor[newIndex * 3 + 2] = thisNor[oldIndex * 3 + 2];
|
|
130
|
+
}
|
|
131
|
+
if (tan !== null && thisTan !== null) {
|
|
132
|
+
tan[newIndex * 4] = thisTan[oldIndex * 4];
|
|
133
|
+
tan[newIndex * 4 + 1] = thisTan[oldIndex * 4 + 1];
|
|
134
|
+
tan[newIndex * 4 + 2] = thisTan[oldIndex * 4 + 2];
|
|
135
|
+
tan[newIndex * 4 + 3] = thisTan[oldIndex * 4 + 3];
|
|
136
|
+
}
|
|
137
|
+
if (col !== null && thisCol !== null) {
|
|
138
|
+
col[newIndex * 4] = thisCol[oldIndex * 4];
|
|
139
|
+
col[newIndex * 4 + 1] = thisCol[oldIndex * 4 + 1];
|
|
140
|
+
col[newIndex * 4 + 2] = thisCol[oldIndex * 4 + 2];
|
|
141
|
+
col[newIndex * 4 + 3] = thisCol[oldIndex * 4 + 3];
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
return new CfgGeometry(this.id + ` (subset ${indices.length})`, this.getScene(), ind, pos, uvs, nor, tan, col, this.morphGeometries.map((morphTarget) => morphTarget.cloneWithSubset(indices)));
|
|
116
145
|
}
|
|
117
146
|
}
|
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
import { PBRMaterial } from "@babylonjs/core/Materials/PBR/pbrMaterial.js";
|
|
2
2
|
import { Mesh } from "@babylonjs/core/Meshes/mesh.js";
|
|
3
|
+
import { CfgProductNode } from "../nodes/CfgProductNode.js";
|
|
3
4
|
import { RenderEnv } from "../view/RenderEnv.js";
|
|
4
5
|
import { CfgGeometry } from "./CfgGeometry.js";
|
|
5
6
|
export declare class CfgMesh extends Mesh {
|
|
6
|
-
|
|
7
|
+
private readonly _morphTargets;
|
|
8
|
+
constructor(name: string, renderEnvironment: RenderEnv, cfgProductNodeParent: CfgProductNode, geometry: CfgGeometry, material: PBRMaterial);
|
|
9
|
+
refreshStretch(): void;
|
|
7
10
|
}
|
|
8
11
|
//# sourceMappingURL=CfgMesh.d.ts.map
|
package/dist/geometry/CfgMesh.js
CHANGED
|
@@ -1,8 +1,38 @@
|
|
|
1
1
|
import { Mesh } from "@babylonjs/core/Meshes/mesh.js";
|
|
2
|
+
import { MorphTargetManager } from "@babylonjs/core/Morph/morphTargetManager.js";
|
|
3
|
+
import { CfgMorphTarget } from "./stretch/CfgMorphTarget.js";
|
|
2
4
|
export class CfgMesh extends Mesh {
|
|
3
|
-
constructor(name, renderEnvironment, geometry, material) {
|
|
5
|
+
constructor(name, renderEnvironment, cfgProductNodeParent, geometry, material) {
|
|
4
6
|
super(name, renderEnvironment.scene);
|
|
5
|
-
|
|
7
|
+
this._morphTargets = [];
|
|
8
|
+
// Cloning is a hack to handle two things: the Geometry subclass getting automatically
|
|
9
|
+
// disposed when the last associated mesh is disposed, for example when changing options
|
|
10
|
+
// back and forth. This way every CfgMesh has it's own instance so we do not need to worry
|
|
11
|
+
// about it being disposed.
|
|
12
|
+
// The other issue being handled is the a short circuit in Babylon between CfgMeshes when
|
|
13
|
+
// using stretch. Meshes using the same CfgGeometry are affected when one of them is unloaded.
|
|
14
|
+
// IIRC we are using a geometry cache which is probably holding on firmly to this instance
|
|
15
|
+
// and reusing it for the next time we need the same mesh. If that cache is ever cleared,
|
|
16
|
+
// it should also dispose the geometry inside it.
|
|
17
|
+
const cloneGeometry = geometry.shallowClone();
|
|
18
|
+
cloneGeometry.applyToMesh(this);
|
|
6
19
|
this.material = material;
|
|
20
|
+
const morphGeometries = cloneGeometry.morphGeometries;
|
|
21
|
+
if (morphGeometries.length !== 0) {
|
|
22
|
+
const manager = new MorphTargetManager();
|
|
23
|
+
manager.enableNormalMorphing = false;
|
|
24
|
+
manager.enableTangentMorphing = false;
|
|
25
|
+
this.morphTargetManager = manager;
|
|
26
|
+
for (const morphGeometry of morphGeometries) {
|
|
27
|
+
const morphTarget = new CfgMorphTarget(morphGeometry, cfgProductNodeParent);
|
|
28
|
+
this._morphTargets.push(morphTarget);
|
|
29
|
+
manager.addTarget(morphTarget);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
refreshStretch() {
|
|
34
|
+
for (const morphTarget of this._morphTargets) {
|
|
35
|
+
morphTarget.refreshStretch();
|
|
36
|
+
}
|
|
7
37
|
}
|
|
8
38
|
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { MorphTarget } from "@babylonjs/core/Morph/morphTarget.js";
|
|
2
|
+
import { CfgProductNode } from "../../nodes/CfgProductNode.js";
|
|
3
|
+
import { CfgStretchData } from "./CfgStretchData.js";
|
|
4
|
+
import { CfgMorphTwiceStretchedGeometry } from "./CfgStretchMorphGeometry.js";
|
|
5
|
+
/**
|
|
6
|
+
* Contains geometry which when added to the original geometry will move the vertices to where
|
|
7
|
+
* they should be when the stretchReference length doubles.
|
|
8
|
+
*/
|
|
9
|
+
export declare class CfgMorphTarget extends MorphTarget {
|
|
10
|
+
private _morphGeometry;
|
|
11
|
+
readonly cfgProductNodeParent: CfgProductNode;
|
|
12
|
+
constructor(_morphGeometry: CfgMorphTwiceStretchedGeometry, cfgProductNodeParent: CfgProductNode);
|
|
13
|
+
get stretchData(): CfgStretchData;
|
|
14
|
+
refreshStretch(): void;
|
|
15
|
+
}
|
|
16
|
+
//# sourceMappingURL=CfgMorphTarget.d.ts.map
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { MorphTarget } from "@babylonjs/core/Morph/morphTarget.js";
|
|
2
|
+
/**
|
|
3
|
+
* Contains geometry which when added to the original geometry will move the vertices to where
|
|
4
|
+
* they should be when the stretchReference length doubles.
|
|
5
|
+
*/
|
|
6
|
+
export class CfgMorphTarget extends MorphTarget {
|
|
7
|
+
constructor(_morphGeometry, cfgProductNodeParent) {
|
|
8
|
+
super(_morphGeometry.id);
|
|
9
|
+
this._morphGeometry = _morphGeometry;
|
|
10
|
+
this.cfgProductNodeParent = cfgProductNodeParent;
|
|
11
|
+
const { pos, uvs } = _morphGeometry;
|
|
12
|
+
this.setPositions(pos);
|
|
13
|
+
if (uvs !== undefined) {
|
|
14
|
+
this.setUVs(uvs);
|
|
15
|
+
}
|
|
16
|
+
this.refreshStretch();
|
|
17
|
+
}
|
|
18
|
+
get stretchData() {
|
|
19
|
+
return this._morphGeometry.stretchData;
|
|
20
|
+
}
|
|
21
|
+
refreshStretch() {
|
|
22
|
+
const stretchReferenceLengthsByMeasureParamCode = this.cfgProductNodeParent.product.configuration._internal
|
|
23
|
+
.stretchReferenceLengthsByMeasureParamCode;
|
|
24
|
+
const referenceLength = stretchReferenceLengthsByMeasureParamCode.get(this.stretchData.measureParam);
|
|
25
|
+
const influence = referenceLength === undefined
|
|
26
|
+
? 0
|
|
27
|
+
: this.stretchData.calculateInfluenceGivenTwiceMorphTarget(referenceLength.current);
|
|
28
|
+
if (this.influence === influence) {
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
/* WORKAROUND for render failure
|
|
32
|
+
*
|
|
33
|
+
* Setting an influence to 0 (zero) will actually remove the MorphTarget from the list of
|
|
34
|
+
* "active" MorphTargets in the MorphTargetManager in Babylon.js.
|
|
35
|
+
*
|
|
36
|
+
* This is probably an performance optimization since it means less calculations has to be
|
|
37
|
+
* done the shaders. Another reason could be a workaround for a technical limitation in
|
|
38
|
+
* WebGL1 that limits the maximum number of MorphTargets in Babylon.js:
|
|
39
|
+
* https://doc.babylonjs.com/divingDeeper/mesh/morphTargets#limitations
|
|
40
|
+
*
|
|
41
|
+
* For some reason this causes problems for us, at least with the combination of Windows 10
|
|
42
|
+
* + Chromium + Babylon 4.2, in where disabling a previous active MorphTarget (i.e. setting
|
|
43
|
+
* influence to 0) will cause the first frame in BaseView.BrowserTick to fail to render,
|
|
44
|
+
* ending up an empty frame with the following error in the console:
|
|
45
|
+
*
|
|
46
|
+
* WebGL: INVALID_OPERATION: drawElements: no buffer is bound to enabled attribute
|
|
47
|
+
* ThinEngine.drawElementsType @ thinEngine.ts:1995
|
|
48
|
+
* Mesh._draw @ mesh.ts:1542
|
|
49
|
+
* Mesh._processRendering @ mesh.ts:1740
|
|
50
|
+
* Mesh.render @ mesh.ts:1930
|
|
51
|
+
* [...]
|
|
52
|
+
*
|
|
53
|
+
* The theory we have is that the change of active MorphTargets needs some time or other
|
|
54
|
+
* event to properly settle in, perhaps due to it changing the shaders. Digging in the
|
|
55
|
+
* Babylon.js source code has not reveled anything useful so far.
|
|
56
|
+
*
|
|
57
|
+
* As a workaround, set the influence to Number.MIN_VALUE (smallest non-zero value) instead
|
|
58
|
+
* of 0. This keeps it the actual influence as zero in practice, but keeps the MorphTarget
|
|
59
|
+
* active and no longer triggers the render failure.
|
|
60
|
+
*
|
|
61
|
+
* TODO: Revisit this when we switch to a new major release of Babylon.js, 5.0 is next.
|
|
62
|
+
*/
|
|
63
|
+
this.influence = influence === 0 ? Number.MIN_VALUE : influence;
|
|
64
|
+
}
|
|
65
|
+
}
|