@kimap/indoor-positioning-sdk-vue2 5.2.6 → 5.2.7
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/package.json +1 -1
- package/src/core/KimapSDK.js +524 -54
package/package.json
CHANGED
package/src/core/KimapSDK.js
CHANGED
|
@@ -35,6 +35,10 @@ function KimapSDK(config) {
|
|
|
35
35
|
this.currentFloorId = null;
|
|
36
36
|
this.currentFloor = null;
|
|
37
37
|
|
|
38
|
+
// 楼层工具配置
|
|
39
|
+
this.floorToolsConfig = config.floorTools || { enabled: false };
|
|
40
|
+
this.floorToolsElement = null;
|
|
41
|
+
|
|
38
42
|
// 多楼层相关配置
|
|
39
43
|
this.floorNames = config.floorNames || []; // 楼层名称列表
|
|
40
44
|
this.currentFloorIndex = 0; // 当前楼层索引(0=ALL)
|
|
@@ -84,11 +88,17 @@ KimapSDK.prototype.init = function() {
|
|
|
84
88
|
self.floorGroups = self.core.floorGroups;
|
|
85
89
|
}
|
|
86
90
|
|
|
87
|
-
//
|
|
88
|
-
if (
|
|
91
|
+
// 渲染楼层选择器(使用 floorTools 配置)
|
|
92
|
+
if (this.floorToolsConfig.enabled !== false && (this.isMultiFloor || this.floorNames.length > 0)) {
|
|
89
93
|
self._renderFloorSelector();
|
|
90
94
|
}
|
|
91
95
|
|
|
96
|
+
// 创建科技感地面背景
|
|
97
|
+
self._createTechGround();
|
|
98
|
+
|
|
99
|
+
// 启动道路动画循环
|
|
100
|
+
self._startRoadAnimationLoop();
|
|
101
|
+
|
|
92
102
|
// 加载背景建筑
|
|
93
103
|
if (self.backgroundBuildingsConfig.enabled !== false) {
|
|
94
104
|
self._loadBackgroundBuildings();
|
|
@@ -169,31 +179,57 @@ KimapSDK.prototype.startRenderLoop = function() {
|
|
|
169
179
|
*/
|
|
170
180
|
KimapSDK.prototype._extractWallsFromModel = function() {
|
|
171
181
|
var self = this;
|
|
172
|
-
|
|
173
|
-
if (!self.core
|
|
174
|
-
console.warn('KimapSDK:
|
|
182
|
+
|
|
183
|
+
if (!self.core) {
|
|
184
|
+
console.warn('KimapSDK: SceneCore未初始化,无法提取墙面数据');
|
|
175
185
|
return;
|
|
176
186
|
}
|
|
177
|
-
|
|
187
|
+
|
|
188
|
+
// 优先从 mapModel 提取(单楼层模式)
|
|
189
|
+
if (self.core.mapModel) {
|
|
190
|
+
self._extractWallsFromGroup(self.core.mapModel);
|
|
191
|
+
return;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// 从 floorGroups 提取(多楼层模式)
|
|
195
|
+
if (self.floorGroups && self.floorGroups.length > 0) {
|
|
196
|
+
self.floorGroups.forEach(function(group) {
|
|
197
|
+
self._extractWallsFromGroup(group);
|
|
198
|
+
});
|
|
199
|
+
return;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
console.warn('KimapSDK: 3D模型未加载,无法提取墙面数据');
|
|
203
|
+
};
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* 从Group中提取墙面数据
|
|
207
|
+
* @private
|
|
208
|
+
*/
|
|
209
|
+
KimapSDK.prototype._extractWallsFromGroup = function(group) {
|
|
210
|
+
var THREE = window.THREE || global.THREE;
|
|
211
|
+
if (!THREE) return;
|
|
212
|
+
|
|
178
213
|
var walls = [];
|
|
179
214
|
var floorGroups = {};
|
|
180
|
-
|
|
215
|
+
var self = this;
|
|
216
|
+
|
|
181
217
|
// 遍历场景中的所有对象,查找墙面
|
|
182
|
-
|
|
218
|
+
group.traverse(function(child) {
|
|
183
219
|
if (child.name && child.name.startsWith('Wall_')) {
|
|
184
220
|
var box = new THREE.Box3().setFromObject(child);
|
|
185
221
|
var min = box.min;
|
|
186
222
|
var max = box.max;
|
|
187
|
-
|
|
223
|
+
|
|
188
224
|
var centerX = (min.x + max.x) / 2;
|
|
189
225
|
var centerZ = (min.z + max.z) / 2;
|
|
190
226
|
var width = max.x - min.x;
|
|
191
227
|
var depth = max.z - min.z;
|
|
192
228
|
var height = max.y - min.y;
|
|
193
|
-
|
|
229
|
+
|
|
194
230
|
var isHorizontal = width > depth;
|
|
195
231
|
var thickness = isHorizontal ? depth : width;
|
|
196
|
-
|
|
232
|
+
|
|
197
233
|
var points;
|
|
198
234
|
if (isHorizontal) {
|
|
199
235
|
points = [
|
|
@@ -206,9 +242,9 @@ KimapSDK.prototype._extractWallsFromModel = function() {
|
|
|
206
242
|
{ x: centerX * 100, y: max.z * 100 }
|
|
207
243
|
];
|
|
208
244
|
}
|
|
209
|
-
|
|
245
|
+
|
|
210
246
|
var floorLevel = Math.floor(min.y / 3);
|
|
211
|
-
|
|
247
|
+
|
|
212
248
|
var wallData = {
|
|
213
249
|
type: 'wall',
|
|
214
250
|
id: child.name,
|
|
@@ -219,23 +255,23 @@ KimapSDK.prototype._extractWallsFromModel = function() {
|
|
|
219
255
|
height: height * 1000,
|
|
220
256
|
floorLevel: floorLevel
|
|
221
257
|
};
|
|
222
|
-
|
|
258
|
+
|
|
223
259
|
walls.push(wallData);
|
|
224
|
-
|
|
260
|
+
|
|
225
261
|
if (!floorGroups[floorLevel]) {
|
|
226
262
|
floorGroups[floorLevel] = [];
|
|
227
263
|
}
|
|
228
264
|
floorGroups[floorLevel].push(wallData);
|
|
229
265
|
}
|
|
230
266
|
});
|
|
231
|
-
|
|
267
|
+
|
|
232
268
|
console.log('✅ 从3D模型中提取墙面数据:', walls.length, '个墙面');
|
|
233
|
-
|
|
269
|
+
|
|
234
270
|
var floors = [];
|
|
235
271
|
Object.keys(floorGroups).sort().forEach(function(level) {
|
|
236
272
|
var floorId = 'floor_' + level;
|
|
237
273
|
var floorName = (parseInt(level) + 1) + '楼';
|
|
238
|
-
|
|
274
|
+
|
|
239
275
|
floors.push({
|
|
240
276
|
id: floorId,
|
|
241
277
|
name: floorName,
|
|
@@ -253,10 +289,12 @@ KimapSDK.prototype._extractWallsFromModel = function() {
|
|
|
253
289
|
]
|
|
254
290
|
});
|
|
255
291
|
});
|
|
256
|
-
|
|
257
|
-
if (floors.length > 0) {
|
|
258
|
-
|
|
292
|
+
|
|
293
|
+
if (floors.length > 0 && this.floors.length === 0) {
|
|
294
|
+
this.setFloors(floors);
|
|
259
295
|
console.log('✅ 已自动设置楼层数据(从3D模型提取)');
|
|
296
|
+
} else if (floors.length > 0) {
|
|
297
|
+
console.log('✅ 已提取墙面数据(楼层已设置)');
|
|
260
298
|
} else {
|
|
261
299
|
console.warn('⚠️ 未从3D模型中提取到墙面数据');
|
|
262
300
|
}
|
|
@@ -556,6 +594,11 @@ KimapSDK.prototype._renderFloorSelector = function() {
|
|
|
556
594
|
existingSelector.remove();
|
|
557
595
|
}
|
|
558
596
|
|
|
597
|
+
// 如果楼层工具未启用,不渲染
|
|
598
|
+
if (this.floorToolsConfig.enabled === false) {
|
|
599
|
+
return;
|
|
600
|
+
}
|
|
601
|
+
|
|
559
602
|
// 获取楼层名称列表
|
|
560
603
|
var floorNames = this.floorNames;
|
|
561
604
|
if (!floorNames || floorNames.length === 0) {
|
|
@@ -569,15 +612,33 @@ KimapSDK.prototype._renderFloorSelector = function() {
|
|
|
569
612
|
// 创建选择器容器
|
|
570
613
|
var selector = document.createElement('div');
|
|
571
614
|
selector.id = 'kimap-floor-selector';
|
|
615
|
+
this.floorToolsElement = selector;
|
|
616
|
+
|
|
617
|
+
// 根据配置设置位置
|
|
618
|
+
var position = this.floorToolsConfig.position || 'left-bottom';
|
|
619
|
+
var offsetX = this.floorToolsConfig.offsetX || 20;
|
|
620
|
+
var offsetY = this.floorToolsConfig.offsetY || 20;
|
|
621
|
+
|
|
622
|
+
var positionStyles = {
|
|
623
|
+
'left-top': { top: offsetY + 'px', left: offsetX + 'px', bottom: 'auto', right: 'auto' },
|
|
624
|
+
'left-bottom': { bottom: offsetY + 'px', left: offsetX + 'px', top: 'auto', right: 'auto' },
|
|
625
|
+
'right-top': { top: offsetY + 'px', right: offsetX + 'px', bottom: 'auto', left: 'auto' },
|
|
626
|
+
'right-bottom': { bottom: offsetY + 'px', right: offsetX + 'px', top: 'auto', left: 'auto' }
|
|
627
|
+
};
|
|
628
|
+
|
|
629
|
+
var posStyle = positionStyles[position] || positionStyles['left-bottom'];
|
|
630
|
+
|
|
572
631
|
selector.style.cssText = [
|
|
573
632
|
'position: absolute',
|
|
574
|
-
'bottom: 20px',
|
|
575
|
-
'left: 20px',
|
|
576
633
|
'display: flex',
|
|
577
634
|
'flex-direction: column',
|
|
578
635
|
'gap: 4px',
|
|
579
636
|
'z-index: 1000',
|
|
580
|
-
'font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif'
|
|
637
|
+
'font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif',
|
|
638
|
+
'top: ' + posStyle.top,
|
|
639
|
+
'left: ' + posStyle.left,
|
|
640
|
+
'bottom: ' + posStyle.bottom,
|
|
641
|
+
'right: ' + posStyle.right
|
|
581
642
|
].join(';');
|
|
582
643
|
|
|
583
644
|
// 创建ALL按钮
|
|
@@ -687,16 +748,19 @@ KimapSDK.prototype._onFloorButtonClick = function(index) {
|
|
|
687
748
|
KimapSDK.prototype._showAllFloors = function() {
|
|
688
749
|
var self = this;
|
|
689
750
|
|
|
690
|
-
//
|
|
751
|
+
// 淡出当前楼层(如果有)
|
|
691
752
|
if (this.floorGroups && this.floorGroups.length > 0) {
|
|
692
753
|
this.floorGroups.forEach(function(group) {
|
|
693
754
|
group.visible = true;
|
|
755
|
+
// 淡入楼层
|
|
756
|
+
self._fadeInGroup(group, 400);
|
|
694
757
|
});
|
|
695
758
|
}
|
|
696
759
|
|
|
697
|
-
//
|
|
760
|
+
// 淡入背景建筑
|
|
698
761
|
if (this.backgroundGroup) {
|
|
699
762
|
this.backgroundGroup.visible = true;
|
|
763
|
+
this._fadeInBackgroundBuildings(600);
|
|
700
764
|
}
|
|
701
765
|
|
|
702
766
|
// 隐藏家具(ALL模式不显示家具)
|
|
@@ -704,10 +768,57 @@ KimapSDK.prototype._showAllFloors = function() {
|
|
|
704
768
|
this.furnitureGroup.visible = false;
|
|
705
769
|
}
|
|
706
770
|
|
|
707
|
-
//
|
|
771
|
+
// 调整相机位置到能看到所有楼层(带动画)
|
|
708
772
|
this._zoomToShowAllFloors();
|
|
709
773
|
|
|
710
|
-
console.log('KimapSDK: 已切换到ALL
|
|
774
|
+
console.log('KimapSDK: 已切换到ALL模式');
|
|
775
|
+
};
|
|
776
|
+
|
|
777
|
+
/**
|
|
778
|
+
* 淡入Group
|
|
779
|
+
* @private
|
|
780
|
+
*/
|
|
781
|
+
KimapSDK.prototype._fadeInGroup = function(group, duration) {
|
|
782
|
+
var self = this;
|
|
783
|
+
var startTime = performance.now();
|
|
784
|
+
|
|
785
|
+
// 记录初始透明度
|
|
786
|
+
group.traverse(function(child) {
|
|
787
|
+
if (child.material) {
|
|
788
|
+
if (Array.isArray(child.material)) {
|
|
789
|
+
child.material.forEach(function(mat) {
|
|
790
|
+
if (mat.transparent !== undefined) mat.transparent = true;
|
|
791
|
+
});
|
|
792
|
+
} else if (child.material.transparent !== undefined) {
|
|
793
|
+
child.material.transparent = true;
|
|
794
|
+
}
|
|
795
|
+
}
|
|
796
|
+
});
|
|
797
|
+
|
|
798
|
+
function fadeAnimate() {
|
|
799
|
+
var elapsed = performance.now() - startTime;
|
|
800
|
+
var progress = Math.min(elapsed / duration, 1);
|
|
801
|
+
|
|
802
|
+
group.traverse(function(child) {
|
|
803
|
+
if (child.material) {
|
|
804
|
+
if (Array.isArray(child.material)) {
|
|
805
|
+
child.material.forEach(function(mat) {
|
|
806
|
+
if (mat.opacity !== undefined) {
|
|
807
|
+
mat.opacity = progress;
|
|
808
|
+
}
|
|
809
|
+
});
|
|
810
|
+
} else if (child.material.opacity !== undefined) {
|
|
811
|
+
child.material.opacity = progress;
|
|
812
|
+
}
|
|
813
|
+
}
|
|
814
|
+
});
|
|
815
|
+
|
|
816
|
+
if (progress < 1) {
|
|
817
|
+
requestAnimationFrame(fadeAnimate);
|
|
818
|
+
}
|
|
819
|
+
}
|
|
820
|
+
|
|
821
|
+
fadeAnimate();
|
|
711
822
|
};
|
|
712
823
|
|
|
713
824
|
/**
|
|
@@ -717,10 +828,15 @@ KimapSDK.prototype._showAllFloors = function() {
|
|
|
717
828
|
KimapSDK.prototype._showSingleFloor = function(floorIndex) {
|
|
718
829
|
var self = this;
|
|
719
830
|
|
|
720
|
-
//
|
|
831
|
+
// 淡出所有楼层,然后只显示目标楼层
|
|
721
832
|
if (this.floorGroups && this.floorGroups.length > 0) {
|
|
722
833
|
this.floorGroups.forEach(function(group, index) {
|
|
723
|
-
|
|
834
|
+
if (index === floorIndex) {
|
|
835
|
+
group.visible = true;
|
|
836
|
+
self._fadeInGroup(group, 300);
|
|
837
|
+
} else {
|
|
838
|
+
group.visible = false;
|
|
839
|
+
}
|
|
724
840
|
});
|
|
725
841
|
}
|
|
726
842
|
|
|
@@ -734,14 +850,14 @@ KimapSDK.prototype._showSingleFloor = function(floorIndex) {
|
|
|
734
850
|
this.furnitureGroup.visible = true;
|
|
735
851
|
}
|
|
736
852
|
|
|
737
|
-
//
|
|
853
|
+
// 调整相机到该楼层(带动画)
|
|
738
854
|
this._adjustCameraToFloor(floorIndex);
|
|
739
855
|
|
|
740
856
|
console.log('KimapSDK: 已切换到楼层:', (floorIndex + 1) + 'F');
|
|
741
857
|
};
|
|
742
858
|
|
|
743
859
|
/**
|
|
744
|
-
*
|
|
860
|
+
* 调整相机到能看到所有楼层(带动画)
|
|
745
861
|
* @private
|
|
746
862
|
*/
|
|
747
863
|
KimapSDK.prototype._zoomToShowAllFloors = function() {
|
|
@@ -773,25 +889,87 @@ KimapSDK.prototype._zoomToShowAllFloors = function() {
|
|
|
773
889
|
var maxSize = Math.max(size.x, size.z);
|
|
774
890
|
var distance = maxSize * 2;
|
|
775
891
|
|
|
776
|
-
//
|
|
777
|
-
|
|
892
|
+
// 计算目标位置
|
|
893
|
+
var targetPosition = new THREE.Vector3(
|
|
778
894
|
center.x,
|
|
779
895
|
center.y + distance * 1.5,
|
|
780
896
|
center.z + distance
|
|
781
897
|
);
|
|
782
|
-
this.core.camera.lookAt(center);
|
|
783
898
|
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
899
|
+
// 使用动画移动相机
|
|
900
|
+
var self = this;
|
|
901
|
+
this._animateCamera({
|
|
902
|
+
position: targetPosition,
|
|
903
|
+
lookAt: center
|
|
904
|
+
}, 800, function() {
|
|
905
|
+
console.log('KimapSDK: 已切换到ALL模式(动画完成)');
|
|
906
|
+
});
|
|
907
|
+
|
|
908
|
+
// 淡入背景建筑
|
|
909
|
+
this._fadeInBackgroundBuildings(600);
|
|
788
910
|
} catch (e) {
|
|
789
911
|
console.warn('调整相机到ALL模式失败:', e);
|
|
790
912
|
}
|
|
791
913
|
};
|
|
792
914
|
|
|
793
915
|
/**
|
|
794
|
-
*
|
|
916
|
+
* 淡入背景建筑
|
|
917
|
+
* @private
|
|
918
|
+
*/
|
|
919
|
+
KimapSDK.prototype._fadeInBackgroundBuildings = function(duration) {
|
|
920
|
+
if (!this.backgroundGroup) return;
|
|
921
|
+
|
|
922
|
+
this.backgroundGroup.visible = true;
|
|
923
|
+
|
|
924
|
+
// 设置初始透明度
|
|
925
|
+
this.backgroundGroup.traverse(function(child) {
|
|
926
|
+
if (child.material) {
|
|
927
|
+
if (Array.isArray(child.material)) {
|
|
928
|
+
child.material.forEach(function(mat) {
|
|
929
|
+
if (mat.opacity !== undefined) {
|
|
930
|
+
mat._originalOpacity = mat.opacity;
|
|
931
|
+
mat.opacity = 0;
|
|
932
|
+
}
|
|
933
|
+
});
|
|
934
|
+
} else if (child.material.opacity !== undefined) {
|
|
935
|
+
child.material._originalOpacity = child.material.opacity;
|
|
936
|
+
child.material.opacity = 0;
|
|
937
|
+
}
|
|
938
|
+
}
|
|
939
|
+
});
|
|
940
|
+
|
|
941
|
+
var self = this;
|
|
942
|
+
var startTime = performance.now();
|
|
943
|
+
|
|
944
|
+
function fadeAnimate() {
|
|
945
|
+
var elapsed = performance.now() - startTime;
|
|
946
|
+
var progress = Math.min(elapsed / duration, 1);
|
|
947
|
+
|
|
948
|
+
self.backgroundGroup.traverse(function(child) {
|
|
949
|
+
if (child.material) {
|
|
950
|
+
var originalOpacity = child.material._originalOpacity !== undefined ? child.material._originalOpacity : 1;
|
|
951
|
+
if (Array.isArray(child.material)) {
|
|
952
|
+
child.material.forEach(function(mat) {
|
|
953
|
+
if (mat.opacity !== undefined) {
|
|
954
|
+
mat.opacity = originalOpacity * progress;
|
|
955
|
+
}
|
|
956
|
+
});
|
|
957
|
+
} else if (child.material.opacity !== undefined) {
|
|
958
|
+
child.material.opacity = originalOpacity * progress;
|
|
959
|
+
}
|
|
960
|
+
}
|
|
961
|
+
});
|
|
962
|
+
|
|
963
|
+
if (progress < 1) {
|
|
964
|
+
requestAnimationFrame(fadeAnimate);
|
|
965
|
+
}
|
|
966
|
+
}
|
|
967
|
+
|
|
968
|
+
fadeAnimate();
|
|
969
|
+
};
|
|
970
|
+
|
|
971
|
+
/**
|
|
972
|
+
* 调整相机到指定楼层(带动画)
|
|
795
973
|
* @private
|
|
796
974
|
*/
|
|
797
975
|
KimapSDK.prototype._adjustCameraToFloor = function(floorIndex) {
|
|
@@ -808,17 +986,21 @@ KimapSDK.prototype._adjustCameraToFloor = function(floorIndex) {
|
|
|
808
986
|
var maxSize = Math.max(size.x, size.z);
|
|
809
987
|
var distance = maxSize * 1.5;
|
|
810
988
|
|
|
811
|
-
|
|
989
|
+
// 计算目标位置
|
|
990
|
+
var targetPosition = new THREE.Vector3(
|
|
812
991
|
center.x,
|
|
813
992
|
center.y + distance,
|
|
814
993
|
center.z + distance * 0.5
|
|
815
994
|
);
|
|
816
|
-
this.core.camera.lookAt(center);
|
|
817
995
|
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
996
|
+
// 使用动画移动相机
|
|
997
|
+
var self = this;
|
|
998
|
+
this._animateCamera({
|
|
999
|
+
position: targetPosition,
|
|
1000
|
+
lookAt: center
|
|
1001
|
+
}, 600, function() {
|
|
1002
|
+
console.log('KimapSDK: 已切换到楼层 ' + (floorIndex + 1) + 'F(动画完成)');
|
|
1003
|
+
});
|
|
822
1004
|
} catch (e) {
|
|
823
1005
|
console.warn('调整相机到楼层失败:', e);
|
|
824
1006
|
}
|
|
@@ -926,6 +1108,7 @@ KimapSDK.prototype._createBackgroundBuildings = function(GLTFLoader, models, cou
|
|
|
926
1108
|
|
|
927
1109
|
/**
|
|
928
1110
|
* 创建备用几何体建筑(当没有模型文件时)
|
|
1111
|
+
* 增强版:科技蓝风格,窗户细节
|
|
929
1112
|
* @private
|
|
930
1113
|
*/
|
|
931
1114
|
KimapSDK.prototype._createFallbackBuilding = function(x, z) {
|
|
@@ -933,17 +1116,226 @@ KimapSDK.prototype._createFallbackBuilding = function(x, z) {
|
|
|
933
1116
|
var width = 15 + Math.random() * 25;
|
|
934
1117
|
var depth = 15 + Math.random() * 25;
|
|
935
1118
|
|
|
1119
|
+
var buildingGroup = new THREE.Group();
|
|
1120
|
+
buildingGroup.position.set(x, 0, z);
|
|
1121
|
+
|
|
1122
|
+
// 主楼体 - 科技蓝渐变
|
|
936
1123
|
var geometry = new THREE.BoxGeometry(width, height, depth);
|
|
1124
|
+
|
|
1125
|
+
// 科技蓝基础色
|
|
1126
|
+
var baseColor = new THREE.Color().setHSL(0.6 + Math.random() * 0.05, 0.8, 0.25 + Math.random() * 0.1);
|
|
937
1127
|
var material = new THREE.MeshStandardMaterial({
|
|
938
|
-
color:
|
|
939
|
-
roughness: 0.
|
|
940
|
-
metalness: 0.
|
|
1128
|
+
color: baseColor,
|
|
1129
|
+
roughness: 0.3,
|
|
1130
|
+
metalness: 0.6,
|
|
1131
|
+
emissive: baseColor.clone().multiplyScalar(0.1)
|
|
941
1132
|
});
|
|
942
1133
|
|
|
943
1134
|
var building = new THREE.Mesh(geometry, material);
|
|
944
|
-
building.position.
|
|
1135
|
+
building.position.y = height / 2;
|
|
1136
|
+
buildingGroup.add(building);
|
|
1137
|
+
|
|
1138
|
+
// 添加窗户细节
|
|
1139
|
+
this._addWindowsToBuilding(buildingGroup, width, height, depth);
|
|
1140
|
+
|
|
1141
|
+
// 添加发光边缘
|
|
1142
|
+
this._addGlowingEdges(buildingGroup, width, height, depth);
|
|
1143
|
+
|
|
1144
|
+
return buildingGroup;
|
|
1145
|
+
};
|
|
1146
|
+
|
|
1147
|
+
/**
|
|
1148
|
+
* 为建筑添加窗户细节
|
|
1149
|
+
* @private
|
|
1150
|
+
*/
|
|
1151
|
+
KimapSDK.prototype._addWindowsToBuilding = function(buildingGroup, width, height, depth) {
|
|
1152
|
+
var windowWidth = 1.5;
|
|
1153
|
+
var windowHeight = 2;
|
|
1154
|
+
var windowSpacingX = 3;
|
|
1155
|
+
var windowSpacingY = 4;
|
|
1156
|
+
|
|
1157
|
+
var windowMaterial = new THREE.MeshBasicMaterial({
|
|
1158
|
+
color: new THREE.Color().setHSL(0.58, 0.9, 0.7), // 淡蓝色窗户
|
|
1159
|
+
transparent: true,
|
|
1160
|
+
opacity: 0.8
|
|
1161
|
+
});
|
|
1162
|
+
|
|
1163
|
+
var windowGeometry = new THREE.PlaneGeometry(windowWidth, windowHeight);
|
|
1164
|
+
|
|
1165
|
+
// 前后面窗户
|
|
1166
|
+
for (var y = 3; y < height - 2; y += windowSpacingY) {
|
|
1167
|
+
for (var xOff = -width / 2 + 2; xOff < width / 2 - 1; xOff += windowSpacingX) {
|
|
1168
|
+
// 前面
|
|
1169
|
+
var windowFront = new THREE.Mesh(windowGeometry, windowMaterial.clone());
|
|
1170
|
+
windowFront.position.set(xOff, y, depth / 2 + 0.1);
|
|
1171
|
+
buildingGroup.add(windowFront);
|
|
1172
|
+
|
|
1173
|
+
// 后面
|
|
1174
|
+
var windowBack = new THREE.Mesh(windowGeometry, windowMaterial.clone());
|
|
1175
|
+
windowBack.position.set(xOff, y, -depth / 2 - 0.1);
|
|
1176
|
+
windowBack.rotation.y = Math.PI;
|
|
1177
|
+
buildingGroup.add(windowBack);
|
|
1178
|
+
}
|
|
1179
|
+
}
|
|
1180
|
+
|
|
1181
|
+
// 左右面窗户
|
|
1182
|
+
for (y = 3; y < height - 2; y += windowSpacingY) {
|
|
1183
|
+
for (var zOff = -depth / 2 + 2; zOff < depth / 2 - 1; zOff += windowSpacingX) {
|
|
1184
|
+
// 右面
|
|
1185
|
+
var windowRight = new THREE.Mesh(windowGeometry, windowMaterial.clone());
|
|
1186
|
+
windowRight.position.set(width / 2 + 0.1, y, zOff);
|
|
1187
|
+
windowRight.rotation.y = Math.PI / 2;
|
|
1188
|
+
buildingGroup.add(windowRight);
|
|
1189
|
+
|
|
1190
|
+
// 左面
|
|
1191
|
+
var windowLeft = new THREE.Mesh(windowGeometry, windowMaterial.clone());
|
|
1192
|
+
windowLeft.position.set(-width / 2 - 0.1, y, zOff);
|
|
1193
|
+
windowLeft.rotation.y = -Math.PI / 2;
|
|
1194
|
+
buildingGroup.add(windowLeft);
|
|
1195
|
+
}
|
|
1196
|
+
}
|
|
1197
|
+
};
|
|
1198
|
+
|
|
1199
|
+
/**
|
|
1200
|
+
* 为建筑添加发光边缘
|
|
1201
|
+
* @private
|
|
1202
|
+
*/
|
|
1203
|
+
KimapSDK.prototype._addGlowingEdges = function(buildingGroup, width, height, depth) {
|
|
1204
|
+
var edgeMaterial = new THREE.LineBasicMaterial({
|
|
1205
|
+
color: new THREE.Color().setHSL(0.58, 1, 0.5), // 科技蓝
|
|
1206
|
+
linewidth: 2
|
|
1207
|
+
});
|
|
1208
|
+
|
|
1209
|
+
var edgesGeometry = new THREE.EdgesGeometry(new THREE.BoxGeometry(width, height, depth));
|
|
1210
|
+
var edges = new THREE.LineSegments(edgesGeometry, edgeMaterial);
|
|
1211
|
+
edges.position.y = height / 2;
|
|
1212
|
+
buildingGroup.add(edges);
|
|
1213
|
+
};
|
|
1214
|
+
|
|
1215
|
+
/**
|
|
1216
|
+
* 创建科技感地面背景
|
|
1217
|
+
* @private
|
|
1218
|
+
*/
|
|
1219
|
+
KimapSDK.prototype._createTechGround = function() {
|
|
1220
|
+
if (!this.core) return;
|
|
1221
|
+
|
|
1222
|
+
var groundSize = 500;
|
|
1223
|
+
|
|
1224
|
+
// 创建网格地面
|
|
1225
|
+
var gridHelper = new THREE.GridHelper(groundSize, 50, 0x0066aa, 0x003355);
|
|
1226
|
+
gridHelper.position.y = -0.1;
|
|
1227
|
+
gridHelper.material.opacity = 0.3;
|
|
1228
|
+
gridHelper.material.transparent = true;
|
|
1229
|
+
this.core.scene.add(gridHelper);
|
|
1230
|
+
|
|
1231
|
+
// 创建地面平面
|
|
1232
|
+
var groundGeometry = new THREE.PlaneGeometry(groundSize, groundSize);
|
|
1233
|
+
var groundMaterial = new THREE.MeshBasicMaterial({
|
|
1234
|
+
color: 0x0a1628,
|
|
1235
|
+
transparent: true,
|
|
1236
|
+
opacity: 0.8
|
|
1237
|
+
});
|
|
1238
|
+
var ground = new THREE.Mesh(groundGeometry, groundMaterial);
|
|
1239
|
+
ground.rotation.x = -Math.PI / 2;
|
|
1240
|
+
ground.position.y = -0.2;
|
|
1241
|
+
this.core.scene.add(ground);
|
|
1242
|
+
|
|
1243
|
+
// 创建道路线条
|
|
1244
|
+
this._createRoadLines(groundSize);
|
|
1245
|
+
};
|
|
1246
|
+
|
|
1247
|
+
/**
|
|
1248
|
+
* 创建道路流光线条
|
|
1249
|
+
* @private
|
|
1250
|
+
*/
|
|
1251
|
+
KimapSDK.prototype._createRoadLines = function(size) {
|
|
1252
|
+
var roadGroup = new THREE.Group();
|
|
1253
|
+
roadGroup.name = 'roadLines';
|
|
1254
|
+
this.roadLinesGroup = roadGroup;
|
|
1255
|
+
|
|
1256
|
+
var lineCount = 20;
|
|
1257
|
+
var roadWidth = 8;
|
|
1258
|
+
|
|
1259
|
+
for (var i = 0; i < lineCount; i++) {
|
|
1260
|
+
var angle = Math.random() * Math.PI * 2;
|
|
1261
|
+
var length = size * (0.3 + Math.random() * 0.5);
|
|
1262
|
+
var x = Math.cos(angle) * size * 0.4;
|
|
1263
|
+
var z = Math.sin(angle) * size * 0.4;
|
|
1264
|
+
|
|
1265
|
+
// 创建道路
|
|
1266
|
+
var roadGeometry = new THREE.PlaneGeometry(length, roadWidth);
|
|
1267
|
+
var roadMaterial = new THREE.MeshBasicMaterial({
|
|
1268
|
+
color: 0x112233,
|
|
1269
|
+
transparent: true,
|
|
1270
|
+
opacity: 0.6
|
|
1271
|
+
});
|
|
1272
|
+
var road = new THREE.Mesh(roadGeometry, roadMaterial);
|
|
1273
|
+
road.rotation.x = -Math.PI / 2;
|
|
1274
|
+
road.rotation.z = angle;
|
|
1275
|
+
road.position.set(x, 0, z);
|
|
1276
|
+
roadGroup.add(road);
|
|
1277
|
+
|
|
1278
|
+
// 创建中心虚线
|
|
1279
|
+
var dashCount = Math.floor(length / 5);
|
|
1280
|
+
for (var d = 0; d < dashCount; d++) {
|
|
1281
|
+
var dashGeometry = new THREE.PlaneGeometry(2, 0.3);
|
|
1282
|
+
var dashMaterial = new THREE.MeshBasicMaterial({
|
|
1283
|
+
color: 0x00aaff,
|
|
1284
|
+
transparent: true,
|
|
1285
|
+
opacity: 0.8
|
|
1286
|
+
});
|
|
1287
|
+
var dash = new THREE.Mesh(dashGeometry, dashMaterial);
|
|
1288
|
+
dash.rotation.x = -Math.PI / 2;
|
|
1289
|
+
dash.rotation.z = angle;
|
|
1290
|
+
dash.position.set(
|
|
1291
|
+
x + Math.cos(angle) * (d * 5 - length / 2 + 1),
|
|
1292
|
+
0.05,
|
|
1293
|
+
z + Math.sin(angle) * (d * 5 - length / 2 + 1)
|
|
1294
|
+
);
|
|
1295
|
+
dash.userData.baseOpacity = 0.4 + Math.random() * 0.4;
|
|
1296
|
+
dash.userData.phase = Math.random() * Math.PI * 2;
|
|
1297
|
+
dash.userData.speed = 0.5 + Math.random() * 1.5;
|
|
1298
|
+
roadGroup.add(dash);
|
|
1299
|
+
}
|
|
1300
|
+
}
|
|
1301
|
+
|
|
1302
|
+
this.core.scene.add(roadGroup);
|
|
1303
|
+
};
|
|
1304
|
+
|
|
1305
|
+
/**
|
|
1306
|
+
* 更新道路流光动画(在render loop中调用)
|
|
1307
|
+
* @private
|
|
1308
|
+
*/
|
|
1309
|
+
KimapSDK.prototype._updateRoadAnimation = function(time) {
|
|
1310
|
+
if (!this.roadLinesGroup) return;
|
|
945
1311
|
|
|
946
|
-
|
|
1312
|
+
this.roadLinesGroup.traverse(function(child) {
|
|
1313
|
+
if (child.userData && child.userData.baseOpacity !== undefined) {
|
|
1314
|
+
var pulse = Math.sin(time * child.userData.speed + child.userData.phase) * 0.3 + 0.5;
|
|
1315
|
+
child.material.opacity = child.userData.baseOpacity * pulse;
|
|
1316
|
+
}
|
|
1317
|
+
});
|
|
1318
|
+
};
|
|
1319
|
+
|
|
1320
|
+
/**
|
|
1321
|
+
* 在渲染循环中添加道路动画更新
|
|
1322
|
+
* @private
|
|
1323
|
+
*/
|
|
1324
|
+
KimapSDK.prototype._startRoadAnimationLoop = function() {
|
|
1325
|
+
var self = this;
|
|
1326
|
+
var originalStartRenderLoop = this.startRenderLoop.bind(this);
|
|
1327
|
+
|
|
1328
|
+
this.startRenderLoop = function() {
|
|
1329
|
+
originalStartRenderLoop();
|
|
1330
|
+
|
|
1331
|
+
// 添加道路动画更新
|
|
1332
|
+
var animationLoop = function() {
|
|
1333
|
+
requestAnimationFrame(animationLoop);
|
|
1334
|
+
var time = performance.now() * 0.001;
|
|
1335
|
+
self._updateRoadAnimation(time);
|
|
1336
|
+
};
|
|
1337
|
+
animationLoop();
|
|
1338
|
+
};
|
|
947
1339
|
};
|
|
948
1340
|
|
|
949
1341
|
/**
|
|
@@ -1013,6 +1405,66 @@ KimapSDK.prototype.hasProjectLayers = function() {
|
|
|
1013
1405
|
|
|
1014
1406
|
// ==================== 工具方法 ====================
|
|
1015
1407
|
|
|
1408
|
+
/**
|
|
1409
|
+
* 相机动画状态
|
|
1410
|
+
*/
|
|
1411
|
+
KimapSDK.prototype._cameraAnimating = false;
|
|
1412
|
+
KimapSDK.prototype._cameraAnimationId = null;
|
|
1413
|
+
|
|
1414
|
+
/**
|
|
1415
|
+
* 动画相机移动到目标位置
|
|
1416
|
+
* @param {Object} target - 目标位置 { position, lookAt }
|
|
1417
|
+
* @param {number} duration - 动画持续时间(毫秒)
|
|
1418
|
+
* @param {Function} onComplete - 完成回调
|
|
1419
|
+
* @private
|
|
1420
|
+
*/
|
|
1421
|
+
KimapSDK.prototype._animateCamera = function(target, duration, onComplete) {
|
|
1422
|
+
var self = this;
|
|
1423
|
+
if (!this.core || !this.core.camera) return;
|
|
1424
|
+
|
|
1425
|
+
var startPosition = this.core.camera.position.clone();
|
|
1426
|
+
var startTarget = this.core.controls ? this.core.controls.target.clone() : new THREE.Vector3();
|
|
1427
|
+
|
|
1428
|
+
var endPosition = target.position || startPosition;
|
|
1429
|
+
var endTarget = target.lookAt || startTarget;
|
|
1430
|
+
|
|
1431
|
+
var startTime = performance.now();
|
|
1432
|
+
this._cameraAnimating = true;
|
|
1433
|
+
|
|
1434
|
+
function easeOutCubic(t) {
|
|
1435
|
+
return 1 - Math.pow(1 - t, 3);
|
|
1436
|
+
}
|
|
1437
|
+
|
|
1438
|
+
function animate() {
|
|
1439
|
+
var elapsed = performance.now() - startTime;
|
|
1440
|
+
var progress = Math.min(elapsed / duration, 1);
|
|
1441
|
+
var easedProgress = easeOutCubic(progress);
|
|
1442
|
+
|
|
1443
|
+
// 插值相机位置
|
|
1444
|
+
self.core.camera.position.lerpVectors(startPosition, endPosition, easedProgress);
|
|
1445
|
+
|
|
1446
|
+
// 插值目标点
|
|
1447
|
+
if (self.core.controls) {
|
|
1448
|
+
self.core.controls.target.lerpVectors(startTarget, endTarget, easedProgress);
|
|
1449
|
+
self.core.controls.update();
|
|
1450
|
+
}
|
|
1451
|
+
|
|
1452
|
+
if (progress < 1) {
|
|
1453
|
+
self._cameraAnimationId = requestAnimationFrame(animate);
|
|
1454
|
+
} else {
|
|
1455
|
+
self._cameraAnimating = false;
|
|
1456
|
+
if (onComplete) onComplete();
|
|
1457
|
+
}
|
|
1458
|
+
}
|
|
1459
|
+
|
|
1460
|
+
// 取消之前的动画
|
|
1461
|
+
if (this._cameraAnimationId) {
|
|
1462
|
+
cancelAnimationFrame(this._cameraAnimationId);
|
|
1463
|
+
}
|
|
1464
|
+
|
|
1465
|
+
animate();
|
|
1466
|
+
};
|
|
1467
|
+
|
|
1016
1468
|
KimapSDK.prototype.getScene = function() {
|
|
1017
1469
|
return this.core ? this.core.scene : null;
|
|
1018
1470
|
};
|
|
@@ -1143,19 +1595,37 @@ KimapSDK.prototype.getConfig = function() {
|
|
|
1143
1595
|
|
|
1144
1596
|
KimapSDK.prototype.loadKMData = function() {
|
|
1145
1597
|
var self = this;
|
|
1146
|
-
|
|
1598
|
+
|
|
1147
1599
|
if (!this.dataUrl) {
|
|
1148
1600
|
console.warn('⚠️ 未配置dataUrl,无法加载3D家具模型');
|
|
1149
1601
|
return Promise.resolve({ success: false, message: '未配置dataUrl' });
|
|
1150
1602
|
}
|
|
1151
|
-
|
|
1603
|
+
|
|
1152
1604
|
if (!this.config.objUrl) {
|
|
1153
1605
|
console.warn('⚠️ 未配置objUrl,无法确定kidata文件名');
|
|
1154
1606
|
return Promise.resolve({ success: false, message: '未配置objUrl' });
|
|
1155
1607
|
}
|
|
1156
|
-
|
|
1608
|
+
|
|
1157
1609
|
// 从objUrl提取文件名和目录
|
|
1158
1610
|
var objUrl = this.config.objUrl;
|
|
1611
|
+
|
|
1612
|
+
// 处理 objUrl 可能是数组的情况
|
|
1613
|
+
if (Array.isArray(objUrl)) {
|
|
1614
|
+
// 多楼层模式:使用第一个 URL 或跳过
|
|
1615
|
+
if (objUrl.length === 0) {
|
|
1616
|
+
console.warn('⚠️ objUrl数组为空,无法加载kidata');
|
|
1617
|
+
return Promise.resolve({ success: false, message: 'objUrl数组为空' });
|
|
1618
|
+
}
|
|
1619
|
+
objUrl = objUrl[0];
|
|
1620
|
+
console.log('KimapSDK: 使用第一个地图URL加载kidata:', objUrl);
|
|
1621
|
+
}
|
|
1622
|
+
|
|
1623
|
+
// 确保 objUrl 是字符串
|
|
1624
|
+
if (typeof objUrl !== 'string') {
|
|
1625
|
+
console.warn('⚠️ objUrl类型错误,无法加载kidata');
|
|
1626
|
+
return Promise.resolve({ success: false, message: 'objUrl类型错误' });
|
|
1627
|
+
}
|
|
1628
|
+
|
|
1159
1629
|
var lastSlashIndex = objUrl.lastIndexOf('/');
|
|
1160
1630
|
var directory = lastSlashIndex >= 0 ? objUrl.substring(0, lastSlashIndex) : '';
|
|
1161
1631
|
var fileName = objUrl.split('/').pop().replace(/\.(obj|kimap)$/i, '');
|