@mcolabs/threebox-plugin 4.0.0 → 4.0.2
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/threebox.cjs +1 -1
- package/dist/threebox.cjs.map +1 -1
- package/dist/threebox.iife.js +1 -1
- package/dist/threebox.iife.js.map +1 -1
- package/dist/threebox.js +114 -43
- package/dist/threebox.js.map +1 -1
- package/package.json +2 -2
- package/src/Threebox.js +68 -20
- package/src/animation/AnimationManager.js +6 -6
- package/src/camera/CameraSync.js +4 -1
- package/src/objects/effects/BuildingShadows.js +36 -10
- package/src/objects/loadObj.js +54 -9
- package/src/objects/objects.js +3 -2
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mcolabs/threebox-plugin",
|
|
3
|
-
"version": "4.0.
|
|
3
|
+
"version": "4.0.2",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "A Three.js plugin for Mapbox GL JS, using the CustomLayerInterface feature. Provides convenient methods to manage objects in lnglat coordinates, and to synchronize the map and scene cameras.",
|
|
6
6
|
"main": "dist/threebox.cjs",
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
"test": "tests"
|
|
40
40
|
},
|
|
41
41
|
"peerDependencies": {
|
|
42
|
-
"three": ">=0.
|
|
42
|
+
"three": ">=0.182.0"
|
|
43
43
|
},
|
|
44
44
|
"devDependencies": {
|
|
45
45
|
"terser": "^5.44.1",
|
package/src/Threebox.js
CHANGED
|
@@ -113,7 +113,14 @@ Threebox.prototype = {
|
|
|
113
113
|
this.map.on('style.load', function () {
|
|
114
114
|
this.tb.zoomLayers = [];
|
|
115
115
|
//[jscastro] if multiLayer, create a by default layer in the map, so tb.update won't be needed in client side to avoid duplicating calls to render
|
|
116
|
-
if (this.tb.options.multiLayer) this.addLayer({
|
|
116
|
+
if (this.tb.options.multiLayer) this.addLayer({
|
|
117
|
+
id: "threebox_layer",
|
|
118
|
+
type: 'custom',
|
|
119
|
+
renderingMode: '3d',
|
|
120
|
+
map: this,
|
|
121
|
+
onAdd: function (map, gl) { },
|
|
122
|
+
render: function (gl, matrix) { this.map.tb.update(); }
|
|
123
|
+
})
|
|
117
124
|
|
|
118
125
|
this.once('idle', () => {
|
|
119
126
|
this.tb.setObjectsScale();
|
|
@@ -681,36 +688,58 @@ Threebox.prototype = {
|
|
|
681
688
|
// Objects
|
|
682
689
|
sphere: function (options) {
|
|
683
690
|
this.setDefaultView(options, this.options);
|
|
684
|
-
|
|
691
|
+
let obj = sphere(options, this.world);
|
|
692
|
+
obj.threebox = this;
|
|
693
|
+
return obj;
|
|
685
694
|
},
|
|
686
695
|
|
|
687
|
-
line:
|
|
696
|
+
line: function (options) {
|
|
697
|
+
let obj = line(options);
|
|
698
|
+
obj.threebox = this;
|
|
699
|
+
return obj;
|
|
700
|
+
},
|
|
688
701
|
|
|
689
|
-
label:
|
|
702
|
+
label: function (options) {
|
|
703
|
+
let obj = label(options);
|
|
704
|
+
obj.threebox = this;
|
|
705
|
+
return obj;
|
|
706
|
+
},
|
|
690
707
|
|
|
691
|
-
tooltip:
|
|
708
|
+
tooltip: function (options) {
|
|
709
|
+
let obj = tooltip(options);
|
|
710
|
+
obj.threebox = this;
|
|
711
|
+
return obj;
|
|
712
|
+
},
|
|
692
713
|
|
|
693
714
|
tube: function (options) {
|
|
694
715
|
this.setDefaultView(options, this.options);
|
|
695
|
-
|
|
716
|
+
let obj = tube(options, this.world);
|
|
717
|
+
obj.threebox = this;
|
|
718
|
+
return obj;
|
|
696
719
|
},
|
|
697
720
|
|
|
698
721
|
extrusion: function (options) {
|
|
699
722
|
this.setDefaultView(options, this.options);
|
|
700
|
-
|
|
723
|
+
let obj = extrusion(options);
|
|
724
|
+
obj.threebox = this;
|
|
725
|
+
return obj;
|
|
701
726
|
},
|
|
702
727
|
|
|
703
728
|
Object3D: function (options) {
|
|
704
729
|
this.setDefaultView(options, this.options);
|
|
705
|
-
|
|
730
|
+
let obj = Object3D(options);
|
|
731
|
+
obj.threebox = this;
|
|
732
|
+
return obj;
|
|
706
733
|
},
|
|
707
734
|
|
|
708
735
|
loadObj: async function loadObj(options, cb) {
|
|
709
736
|
this.setDefaultView(options, this.options);
|
|
737
|
+
const tb = this; // capture threebox reference for callbacks
|
|
710
738
|
if (options.clone === false) {
|
|
711
739
|
return new Promise(
|
|
712
740
|
async (resolve) => {
|
|
713
741
|
loader(options, cb, async (obj) => {
|
|
742
|
+
obj.threebox = tb;
|
|
714
743
|
resolve(obj);
|
|
715
744
|
});
|
|
716
745
|
});
|
|
@@ -721,7 +750,9 @@ Threebox.prototype = {
|
|
|
721
750
|
if (cache) {
|
|
722
751
|
cache.promise
|
|
723
752
|
.then(obj => {
|
|
724
|
-
|
|
753
|
+
let dupe = obj.duplicate(options);
|
|
754
|
+
dupe.threebox = tb;
|
|
755
|
+
cb(dupe);
|
|
725
756
|
})
|
|
726
757
|
.catch(err => {
|
|
727
758
|
this.objectsCache.delete(options.obj);
|
|
@@ -732,6 +763,7 @@ Threebox.prototype = {
|
|
|
732
763
|
promise: new Promise(
|
|
733
764
|
async (resolve, reject) => {
|
|
734
765
|
loader(options, cb, async (obj) => {
|
|
766
|
+
obj.threebox = tb;
|
|
735
767
|
if (obj.duplicate) {
|
|
736
768
|
resolve(obj.duplicate());
|
|
737
769
|
} else {
|
|
@@ -894,7 +926,7 @@ Threebox.prototype = {
|
|
|
894
926
|
this.labelRenderer.toggleLabels(layerId, visible);
|
|
895
927
|
},
|
|
896
928
|
|
|
897
|
-
update: function () {
|
|
929
|
+
update: function (matrix) {
|
|
898
930
|
|
|
899
931
|
if (this.map.repaint) this.map.repaint = false
|
|
900
932
|
|
|
@@ -907,6 +939,19 @@ Threebox.prototype = {
|
|
|
907
939
|
|
|
908
940
|
// Render the scene and repaint the map
|
|
909
941
|
this.renderer.resetState(); //update threejs r126
|
|
942
|
+
// Reset pixel store params that Mapbox sets - these aren't allowed for 3D texture uploads
|
|
943
|
+
const gl = this.renderer.getContext();
|
|
944
|
+
gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, false);
|
|
945
|
+
gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, false);
|
|
946
|
+
|
|
947
|
+
// Mapbox v3 fix: Re-enable depth testing
|
|
948
|
+
// Mapbox v3 disables depth testing before calling custom layer render
|
|
949
|
+
if (this.mapboxVersion >= 3.0) {
|
|
950
|
+
gl.enable(gl.DEPTH_TEST);
|
|
951
|
+
gl.depthFunc(gl.LESS);
|
|
952
|
+
gl.depthMask(true);
|
|
953
|
+
}
|
|
954
|
+
|
|
910
955
|
this.renderer.render(this.scene, this.camera);
|
|
911
956
|
|
|
912
957
|
// [jscastro] Render any label
|
|
@@ -917,6 +962,7 @@ Threebox.prototype = {
|
|
|
917
962
|
add: function (obj, layerId, sourceId) {
|
|
918
963
|
//[jscastro] remove the tooltip if not enabled
|
|
919
964
|
if (!this.enableTooltips && obj.tooltip) { obj.tooltip.visibility = false };
|
|
965
|
+
obj.threebox = this; // store reference to threebox instance
|
|
920
966
|
this.world.add(obj);
|
|
921
967
|
if (layerId) {
|
|
922
968
|
obj.layer = layerId;
|
|
@@ -1035,8 +1081,9 @@ Threebox.prototype = {
|
|
|
1035
1081
|
|
|
1036
1082
|
this.lights.dirLight.position.set(azSin, azCos, alt);
|
|
1037
1083
|
this.lights.dirLight.position.multiplyScalar(radius);
|
|
1038
|
-
|
|
1039
|
-
this.lights.
|
|
1084
|
+
// Intensities scaled up for physically-based lighting (Three.js r155+)
|
|
1085
|
+
this.lights.dirLight.intensity = Math.max(alt, 0) * 5;
|
|
1086
|
+
this.lights.hemiLight.intensity = Math.max(alt * 1, 0.1) * 3;
|
|
1040
1087
|
//console.log("Intensity:" + this.lights.dirLight.intensity);
|
|
1041
1088
|
this.lights.dirLight.updateMatrixWorld();
|
|
1042
1089
|
this.updateLightHelper();
|
|
@@ -1115,25 +1162,26 @@ Threebox.prototype = {
|
|
|
1115
1162
|
},
|
|
1116
1163
|
|
|
1117
1164
|
defaultLights: function () {
|
|
1118
|
-
|
|
1119
|
-
|
|
1165
|
+
// Light intensities scaled up for physically-based lighting (Three.js r155+)
|
|
1166
|
+
// Original values were designed for legacy lighting mode
|
|
1167
|
+
this.lights.ambientLight = new THREE.AmbientLight(new THREE.Color('hsl(0, 0%, 100%)'), 3);
|
|
1120
1168
|
this.scene.add(this.lights.ambientLight);
|
|
1121
1169
|
|
|
1122
|
-
this.lights.dirLightBack = new THREE.DirectionalLight(new THREE.Color('hsl(0, 0%, 100%)'),
|
|
1170
|
+
this.lights.dirLightBack = new THREE.DirectionalLight(new THREE.Color('hsl(0, 0%, 100%)'), 1);
|
|
1123
1171
|
this.lights.dirLightBack.position.set(30, 100, 100);
|
|
1124
1172
|
this.scene.add(this.lights.dirLightBack);
|
|
1125
1173
|
|
|
1126
|
-
this.lights.dirLight
|
|
1174
|
+
this.lights.dirLight = new THREE.DirectionalLight(new THREE.Color('hsl(0, 0%, 100%)'), 1);
|
|
1127
1175
|
this.lights.dirLight.position.set(-30, 100, -100);
|
|
1128
1176
|
this.scene.add(this.lights.dirLight);
|
|
1129
1177
|
|
|
1130
1178
|
},
|
|
1131
1179
|
|
|
1132
1180
|
realSunlight: function (helper = false) {
|
|
1133
|
-
|
|
1181
|
+
// Light intensities scaled up for physically-based lighting (Three.js r155+)
|
|
1134
1182
|
this.renderer.shadowMap.enabled = true;
|
|
1135
1183
|
//this.renderer.shadowMap.type = THREE.PCFSoftShadowMap;
|
|
1136
|
-
this.lights.dirLight = new THREE.DirectionalLight(0xffffff,
|
|
1184
|
+
this.lights.dirLight = new THREE.DirectionalLight(0xffffff, 5);
|
|
1137
1185
|
this.scene.add(this.lights.dirLight);
|
|
1138
1186
|
if (helper) {
|
|
1139
1187
|
this.lights.dirLightHelper = new THREE.DirectionalLightHelper(this.lights.dirLight, 5);
|
|
@@ -1148,9 +1196,9 @@ Threebox.prototype = {
|
|
|
1148
1196
|
this.lights.dirLight.shadow.camera.bottom = this.lights.dirLight.shadow.camera.left = -d2;
|
|
1149
1197
|
this.lights.dirLight.shadow.camera.near = 1;
|
|
1150
1198
|
this.lights.dirLight.shadow.camera.visible = true;
|
|
1151
|
-
this.lights.dirLight.shadow.camera.far = 400000000;
|
|
1199
|
+
this.lights.dirLight.shadow.camera.far = 400000000;
|
|
1152
1200
|
|
|
1153
|
-
this.lights.hemiLight = new THREE.HemisphereLight(new THREE.Color(0xffffff), new THREE.Color(0xffffff),
|
|
1201
|
+
this.lights.hemiLight = new THREE.HemisphereLight(new THREE.Color(0xffffff), new THREE.Color(0xffffff), 3);
|
|
1154
1202
|
this.lights.hemiLight.color.setHSL(0.661, 0.96, 0.12);
|
|
1155
1203
|
this.lights.hemiLight.groundColor.setHSL(0.11, 0.96, 0.14);
|
|
1156
1204
|
this.lights.hemiLight.position.set(0, 0, 50);
|
|
@@ -133,7 +133,7 @@ AnimationManager.prototype = {
|
|
|
133
133
|
this.animationQueue
|
|
134
134
|
.push(entry);
|
|
135
135
|
|
|
136
|
-
|
|
136
|
+
this.threebox.map.repaint = true;
|
|
137
137
|
}
|
|
138
138
|
|
|
139
139
|
//if no duration set, stop object's existing animations and go to that state immediately
|
|
@@ -183,7 +183,7 @@ AnimationManager.prototype = {
|
|
|
183
183
|
this.animationQueue
|
|
184
184
|
.push(entry);
|
|
185
185
|
|
|
186
|
-
|
|
186
|
+
this.threebox.map.repaint = true;
|
|
187
187
|
|
|
188
188
|
return this;
|
|
189
189
|
};
|
|
@@ -251,7 +251,7 @@ AnimationManager.prototype = {
|
|
|
251
251
|
this.setReceiveShadowFloor();
|
|
252
252
|
|
|
253
253
|
this.updateMatrixWorld();
|
|
254
|
-
|
|
254
|
+
this.threebox.map.repaint = true;
|
|
255
255
|
|
|
256
256
|
//const threeTarget = new THREE.EventDispatcher();
|
|
257
257
|
//threeTarget.dispatchEvent({ type: 'event', detail: { object: this, action: { position: options.position, rotation: options.rotation, scale: options.scale } } });
|
|
@@ -283,7 +283,7 @@ AnimationManager.prototype = {
|
|
|
283
283
|
this.animationQueue
|
|
284
284
|
.push(entry);
|
|
285
285
|
|
|
286
|
-
|
|
286
|
+
this.threebox.map.repaint = true;
|
|
287
287
|
return this;
|
|
288
288
|
}
|
|
289
289
|
}
|
|
@@ -343,7 +343,7 @@ AnimationManager.prototype = {
|
|
|
343
343
|
// Update the animation mixer and render this frame
|
|
344
344
|
obj.mixer.update(0.01);
|
|
345
345
|
}
|
|
346
|
-
|
|
346
|
+
this.threebox.map.repaint = true;
|
|
347
347
|
return this;
|
|
348
348
|
}
|
|
349
349
|
|
|
@@ -458,7 +458,7 @@ AnimationManager.prototype = {
|
|
|
458
458
|
object.isPlaying = true;
|
|
459
459
|
object.animationMethod = requestAnimationFrame(this.update);
|
|
460
460
|
object.mixer.update(object.clock.getDelta());
|
|
461
|
-
|
|
461
|
+
object.threebox.map.repaint = true;
|
|
462
462
|
}
|
|
463
463
|
|
|
464
464
|
}
|
package/src/camera/CameraSync.js
CHANGED
|
@@ -13,11 +13,13 @@ function CameraSync(map, camera, world) {
|
|
|
13
13
|
this.active = true;
|
|
14
14
|
|
|
15
15
|
this.camera.matrixAutoUpdate = false; // We're in charge of the camera now!
|
|
16
|
+
this.camera.matrixWorldAutoUpdate = false; // Three.js r150+: prevent auto world matrix recalculation
|
|
16
17
|
|
|
17
18
|
// Postion and configure the world group so we can scale it appropriately when the camera zooms
|
|
18
19
|
this.world = world || new THREE.Group();
|
|
19
20
|
this.world.position.x = this.world.position.y = ThreeboxConstants.WORLD_SIZE / 2
|
|
20
21
|
this.world.matrixAutoUpdate = false;
|
|
22
|
+
this.world.matrixWorldAutoUpdate = false; // Three.js r150+: prevent auto world matrix recalculation
|
|
21
23
|
|
|
22
24
|
// set up basic camera state
|
|
23
25
|
this.state = {
|
|
@@ -101,7 +103,7 @@ CameraSync.prototype = {
|
|
|
101
103
|
// someday @ansis set further near plane to fix precision for deckgl,so we should fix it to use mapbox-gl v1.3+ correctly
|
|
102
104
|
// https://github.com/mapbox/mapbox-gl-js/commit/5cf6e5f523611bea61dae155db19a7cb19eb825c#diff-5dddfe9d7b5b4413ee54284bc1f7966d
|
|
103
105
|
const nz = (t.height / 50); //min near z as coded by @ansis
|
|
104
|
-
|
|
106
|
+
let nearZ = Math.max(nz * pitchAngle, nz); //on changes in the pitch nz could be too low
|
|
105
107
|
|
|
106
108
|
const h = t.height;
|
|
107
109
|
const w = t.width;
|
|
@@ -118,6 +120,7 @@ CameraSync.prototype = {
|
|
|
118
120
|
let cameraWorldMatrix = this.calcCameraMatrix(t._pitch, t.angle);
|
|
119
121
|
// When terrain layers are included, height of 3D layers must be modified from t_camera.z * worldSize
|
|
120
122
|
if (t.elevation) cameraWorldMatrix.elements[14] = t._camera.position[2] * worldSize;
|
|
123
|
+
|
|
121
124
|
//this.camera.matrixWorld.elements is equivalent to t._camera._transform
|
|
122
125
|
this.camera.matrixWorld.copy(cameraWorldMatrix);
|
|
123
126
|
|
|
@@ -14,9 +14,14 @@ class BuildingShadows {
|
|
|
14
14
|
this.map = map;
|
|
15
15
|
// find layer source
|
|
16
16
|
const sourceName = this.map.getLayer(this.buildingsLayerId).source;
|
|
17
|
-
|
|
17
|
+
// Handle Mapbox v1, v2, and v3 internal API differences for source cache access
|
|
18
|
+
const style = this.map.style;
|
|
19
|
+
this.source = style.sourceCaches?.[sourceName] ||
|
|
20
|
+
style._otherSourceCaches?.[sourceName] ||
|
|
21
|
+
style._sourceCaches?.[sourceName];
|
|
22
|
+
|
|
18
23
|
if (!this.source) {
|
|
19
|
-
console.warn(`Can't find layer ${this.buildingsLayerId}'s source.`);
|
|
24
|
+
console.warn(`BuildingShadows: Can't find layer ${this.buildingsLayerId}'s source.`);
|
|
20
25
|
}
|
|
21
26
|
|
|
22
27
|
// vertex shader of fill-extrusion layer is different in mapbox v1 and v2.
|
|
@@ -30,13 +35,22 @@ class BuildingShadows {
|
|
|
30
35
|
const vertexShader = gl.createShader(gl.VERTEX_SHADER);
|
|
31
36
|
gl.shaderSource(vertexShader, vertexSource);
|
|
32
37
|
gl.compileShader(vertexShader);
|
|
38
|
+
if (!gl.getShaderParameter(vertexShader, gl.COMPILE_STATUS)) {
|
|
39
|
+
console.error('BuildingShadows vertex shader error:', gl.getShaderInfoLog(vertexShader));
|
|
40
|
+
}
|
|
33
41
|
const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
|
|
34
42
|
gl.shaderSource(fragmentShader, fragmentSource);
|
|
35
43
|
gl.compileShader(fragmentShader);
|
|
44
|
+
if (!gl.getShaderParameter(fragmentShader, gl.COMPILE_STATUS)) {
|
|
45
|
+
console.error('BuildingShadows fragment shader error:', gl.getShaderInfoLog(fragmentShader));
|
|
46
|
+
}
|
|
36
47
|
this.program = gl.createProgram();
|
|
37
48
|
gl.attachShader(this.program, vertexShader);
|
|
38
49
|
gl.attachShader(this.program, fragmentShader);
|
|
39
50
|
gl.linkProgram(this.program);
|
|
51
|
+
if (!gl.getProgramParameter(this.program, gl.LINK_STATUS)) {
|
|
52
|
+
console.error('BuildingShadows program link error:', gl.getProgramInfoLog(this.program));
|
|
53
|
+
}
|
|
40
54
|
gl.validateProgram(this.program);
|
|
41
55
|
this.uMatrix = gl.getUniformLocation(this.program, "u_matrix");
|
|
42
56
|
this.uHeightFactor = gl.getUniformLocation(this.program, "u_height_factor");
|
|
@@ -64,19 +78,31 @@ class BuildingShadows {
|
|
|
64
78
|
const pos = this.tb.getSunPosition(this.tb.lightDateTime, [lng, lat]);
|
|
65
79
|
gl.uniform1f(this.uAltitude, (pos.altitude > this.minAltitude ? pos.altitude : 0));
|
|
66
80
|
gl.uniform1f(this.uAzimuth, pos.azimuth + 3 * Math.PI / 2);
|
|
67
|
-
//this.opacity = Math.sin(Math.max(pos.altitude, 0)) * 0.6;
|
|
68
81
|
gl.enable(gl.BLEND);
|
|
69
|
-
//gl.blendFuncSeparate(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA, gl.DST_ALPHA, gl.SRC_ALPHA);
|
|
70
82
|
gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA)
|
|
71
|
-
var ext = gl.getExtension('EXT_blend_minmax');
|
|
72
|
-
//gl.blendEquationSeparate(gl.FUNC_SUBTRACT, ext.MIN_EXT);
|
|
73
|
-
//gl.blendEquation(gl.FUNC_ADD);
|
|
74
83
|
gl.disable(gl.DEPTH_TEST);
|
|
75
84
|
for (const coord of coords) {
|
|
76
85
|
const tile = this.source.getTile(coord);
|
|
77
|
-
|
|
86
|
+
|
|
87
|
+
let bucket = tile.getBucket(buildingsLayer);
|
|
88
|
+
// Mapbox v3 fallback: try direct bucket access by layer ID
|
|
89
|
+
if (!bucket && tile.buckets) {
|
|
90
|
+
bucket = tile.buckets[this.buildingsLayerId];
|
|
91
|
+
}
|
|
78
92
|
if (!bucket) continue;
|
|
79
|
-
|
|
93
|
+
|
|
94
|
+
// Handle Mapbox v3 internal API changes for buffer access
|
|
95
|
+
let heightBuffer, baseBuffer;
|
|
96
|
+
const programConfig = bucket.programConfigurations?.programConfigurations?.[this.buildingsLayerId];
|
|
97
|
+
if (programConfig?._buffers) {
|
|
98
|
+
[heightBuffer, baseBuffer] = programConfig._buffers;
|
|
99
|
+
} else if (programConfig?.getBuffers) {
|
|
100
|
+
const buffers = programConfig.getBuffers();
|
|
101
|
+
heightBuffer = buffers[0];
|
|
102
|
+
baseBuffer = buffers[1];
|
|
103
|
+
} else {
|
|
104
|
+
continue;
|
|
105
|
+
}
|
|
80
106
|
gl.uniformMatrix4fv(this.uMatrix, false, (coord.posMatrix || coord.projMatrix));
|
|
81
107
|
gl.uniform1f(this.uHeightFactor, Math.pow(2, coord.overscaledZ) / tile.tileSize / 8);
|
|
82
108
|
for (const segment of bucket.segments.get()) {
|
|
@@ -160,4 +186,4 @@ class BuildingShadows {
|
|
|
160
186
|
}
|
|
161
187
|
}
|
|
162
188
|
|
|
163
|
-
export default BuildingShadows;
|
|
189
|
+
export default BuildingShadows;
|
package/src/objects/loadObj.js
CHANGED
|
@@ -43,10 +43,16 @@ function loadObj(options, cb, promise) {
|
|
|
43
43
|
break;
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
46
|
+
// Only load MTL if specified, otherwise proceed directly
|
|
47
|
+
if (options.mtl) {
|
|
48
|
+
materialLoader.withCredentials = options.withCredentials;
|
|
49
|
+
materialLoader.load(options.mtl, loadObject, () => (null), error => {
|
|
50
|
+
console.warn("No material file found " + error.stack);
|
|
51
|
+
loadObject(null); // Proceed without materials on error
|
|
52
|
+
});
|
|
53
|
+
} else {
|
|
54
|
+
loadObject(null); // No MTL specified, proceed directly
|
|
55
|
+
}
|
|
50
56
|
|
|
51
57
|
function loadObject(materials) {
|
|
52
58
|
|
|
@@ -80,6 +86,8 @@ function loadObj(options, cb, promise) {
|
|
|
80
86
|
const s = utils.types.scale(options.scale, [1, 1, 1]);
|
|
81
87
|
obj.rotation.set(r[0], r[1], r[2]);
|
|
82
88
|
obj.scale.set(s[0], s[1], s[2]);
|
|
89
|
+
// Fix materials for Three.js r152+ color management and transparency
|
|
90
|
+
// fixMaterials(obj);
|
|
83
91
|
// [jscastro] normalize specular/metalness/shininess from meshes in FBX and GLB model as it would need 5 lights to illuminate them properly
|
|
84
92
|
if (options.normalize) { normalizeSpecular(obj); }
|
|
85
93
|
obj.name = "model";
|
|
@@ -107,7 +115,44 @@ function loadObj(options, cb, promise) {
|
|
|
107
115
|
|
|
108
116
|
};
|
|
109
117
|
|
|
118
|
+
// // Fix materials for Three.js r132+ transparency and r152+ color management
|
|
119
|
+
// function fixMaterials(model) {
|
|
120
|
+
// model.traverse(function (c) {
|
|
121
|
+
// if (c.isMesh) {
|
|
122
|
+
// let materials = Array.isArray(c.material) ? c.material : [c.material];
|
|
123
|
+
// materials.forEach(function (mat) {
|
|
124
|
+
// if (!mat) return;
|
|
125
|
+
|
|
126
|
+
// // Set colorSpace for color textures (Three.js r152+)
|
|
127
|
+
// if (mat.map) mat.map.colorSpace = THREE.SRGBColorSpace;
|
|
128
|
+
// if (mat.emissiveMap) mat.emissiveMap.colorSpace = THREE.SRGBColorSpace;
|
|
129
|
+
|
|
130
|
+
// // Fix GLTF transparency (Three.js r132+ issue)
|
|
131
|
+
// // Since r132, setting transparent=true on GLTF materials doesn't work properly
|
|
132
|
+
// // Need to use custom blending for transparency to display correctly
|
|
133
|
+
// let isTransparent = mat.transparent || mat.opacity < 1 || mat.alphaMap || mat.alphaTest > 0;
|
|
134
|
+
|
|
135
|
+
// // Also check for alpha in the base color texture
|
|
136
|
+
// if (mat.map && mat.map.format === THREE.RGBAFormat) {
|
|
137
|
+
// isTransparent = true;
|
|
138
|
+
// }
|
|
139
|
+
|
|
140
|
+
// if (isTransparent) {
|
|
141
|
+
// mat.transparent = true;
|
|
142
|
+
// mat.depthWrite = false;
|
|
143
|
+
// mat.blending = THREE.CustomBlending;
|
|
144
|
+
// mat.blendSrc = THREE.SrcAlphaFactor;
|
|
145
|
+
// mat.blendDst = THREE.OneMinusSrcAlphaFactor;
|
|
146
|
+
// mat.blendEquation = THREE.AddEquation;
|
|
147
|
+
// mat.needsUpdate = true;
|
|
148
|
+
// }
|
|
149
|
+
// });
|
|
150
|
+
// }
|
|
151
|
+
// });
|
|
152
|
+
// }
|
|
153
|
+
|
|
110
154
|
//[jscastro] some FBX/GLTF models have too much specular effects for mapbox
|
|
155
|
+
// Multipliers adjusted for physically-based lighting (Three.js r155+)
|
|
111
156
|
function normalizeSpecular(model) {
|
|
112
157
|
model.traverse(function (c) {
|
|
113
158
|
|
|
@@ -116,13 +161,13 @@ function loadObj(options, cb, promise) {
|
|
|
116
161
|
let specularColor;
|
|
117
162
|
if (c.material.type == 'MeshStandardMaterial') {
|
|
118
163
|
|
|
119
|
-
if (c.material.metalness) { c.material.metalness *= 0.
|
|
120
|
-
if (c.material.glossiness) { c.material.glossiness *= 0.
|
|
121
|
-
specularColor = new THREE.Color(
|
|
164
|
+
if (c.material.metalness) { c.material.metalness *= 0.3; }
|
|
165
|
+
if (c.material.glossiness) { c.material.glossiness *= 0.5; }
|
|
166
|
+
specularColor = new THREE.Color(0x0c0c0c);
|
|
122
167
|
|
|
123
168
|
} else if (c.material.type == 'MeshPhongMaterial') {
|
|
124
|
-
c.material.shininess = 0.
|
|
125
|
-
specularColor = new THREE.Color(
|
|
169
|
+
c.material.shininess = 0.3;
|
|
170
|
+
specularColor = new THREE.Color(0x141414);
|
|
126
171
|
}
|
|
127
172
|
if (c.material.specular && c.material.specular.isColor) {
|
|
128
173
|
c.material.specular = specularColor;
|
package/src/objects/objects.js
CHANGED
|
@@ -175,7 +175,7 @@ Objects.prototype = {
|
|
|
175
175
|
model.position.add(point); // re-add the offset
|
|
176
176
|
model.rotateOnAxis(axis, theta)
|
|
177
177
|
|
|
178
|
-
|
|
178
|
+
obj.threebox.map.repaint = true;
|
|
179
179
|
}
|
|
180
180
|
|
|
181
181
|
|
|
@@ -818,13 +818,14 @@ Objects.prototype = {
|
|
|
818
818
|
})
|
|
819
819
|
|
|
820
820
|
obj.scaleGroup.remove(o);
|
|
821
|
-
|
|
821
|
+
obj.threebox.map.repaint = true;
|
|
822
822
|
}
|
|
823
823
|
|
|
824
824
|
//[jscastro] clone + assigning all the attributes
|
|
825
825
|
obj.duplicate = function (options) {
|
|
826
826
|
|
|
827
827
|
let dupe = obj.clone(true); //clone the whole threebox object
|
|
828
|
+
dupe.threebox = obj.threebox; // copy threebox reference to the duplicate
|
|
828
829
|
dupe.getObjectByName("model").animations = obj.animations; //we must set this explicitly before addMethods
|
|
829
830
|
if (dupe.userData.feature) {
|
|
830
831
|
if (options && options.feature) dupe.userData.feature = options.feature;
|