@kimap/indoor-positioning-sdk-vue2 5.8.0 → 5.8.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/package.json +1 -1
- package/src/core/KimapSDK.js +98 -156
- package/src/core/SceneCore.js +78 -68
package/package.json
CHANGED
package/src/core/KimapSDK.js
CHANGED
|
@@ -30,6 +30,7 @@ function KimapSDK(config) {
|
|
|
30
30
|
this.customDOMs = {};
|
|
31
31
|
this.dataUrl = config.dataUrl || '';
|
|
32
32
|
this.furnitureGroup = null;
|
|
33
|
+
this.floorFurnitureGroups = []; // 每个楼层独立的家具组
|
|
33
34
|
this.projectLayers = null;
|
|
34
35
|
this.floors = [];
|
|
35
36
|
this.currentFloorId = null;
|
|
@@ -82,10 +83,9 @@ KimapSDK.prototype.init = function() {
|
|
|
82
83
|
self.startRenderLoop();
|
|
83
84
|
self._extractWallsFromModel();
|
|
84
85
|
|
|
85
|
-
//
|
|
86
|
+
// 多楼层默认显示第一个楼层
|
|
86
87
|
if (self.isMultiFloor && self.floorGroups && self.floorGroups.length > 0) {
|
|
87
88
|
self.showSingleFloor(0);
|
|
88
|
-
self.showAllFloors(); // 立即恢复到ALL模式(含间隙),初始化后保持ALL状态
|
|
89
89
|
}
|
|
90
90
|
|
|
91
91
|
// 渲染内置楼层选择器 UI
|
|
@@ -608,17 +608,16 @@ KimapSDK.prototype.showSingleFloor = function(floorIndex) {
|
|
|
608
608
|
var self = this;
|
|
609
609
|
this.floorGroups.forEach(function(group, index) {
|
|
610
610
|
if (index === floorIndex) {
|
|
611
|
-
//
|
|
611
|
+
// 单层模式:将该楼层移动到贴地位置(y = 0)
|
|
612
612
|
group.visible = true;
|
|
613
|
-
group.position.
|
|
613
|
+
group.position.y = 0;
|
|
614
614
|
} else {
|
|
615
|
+
// 隐藏其他楼层,同时移到下方避免干扰
|
|
615
616
|
group.visible = false;
|
|
616
617
|
var originalY = self.core.floorOriginalY[index] || 0;
|
|
617
|
-
group.position.y = -originalY - 1;
|
|
618
|
-
// XZ 保持中心对齐(向下移动后不再占用视觉空间)
|
|
618
|
+
group.position.y = -originalY - 1; // 移到地面以下
|
|
619
619
|
}
|
|
620
620
|
});
|
|
621
|
-
this.currentFloorIndex = floorIndex;
|
|
622
621
|
};
|
|
623
622
|
|
|
624
623
|
/**
|
|
@@ -630,10 +629,10 @@ KimapSDK.prototype.showAllFloors = function() {
|
|
|
630
629
|
var self = this;
|
|
631
630
|
this.floorGroups.forEach(function(group, index) {
|
|
632
631
|
group.visible = true;
|
|
632
|
+
// 恢复原始世界 Y 偏移(含间隙)
|
|
633
633
|
var originalY = self.core.floorOriginalY[index] || 0;
|
|
634
634
|
group.position.y = originalY;
|
|
635
635
|
});
|
|
636
|
-
this.currentFloorIndex = 0;
|
|
637
636
|
};
|
|
638
637
|
|
|
639
638
|
/**
|
|
@@ -901,33 +900,35 @@ KimapSDK.prototype.loadKMData = function() {
|
|
|
901
900
|
return Promise.resolve({ success: false, message: '未配置objUrl' });
|
|
902
901
|
}
|
|
903
902
|
|
|
904
|
-
var
|
|
905
|
-
var
|
|
906
|
-
|
|
907
|
-
//
|
|
908
|
-
if (
|
|
909
|
-
var
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
return
|
|
903
|
+
var objUrls = this.config.objUrl;
|
|
904
|
+
var isMulti = Array.isArray(objUrls);
|
|
905
|
+
|
|
906
|
+
// 多楼层:每个楼层独立加载自己的 kidata → 家具 → 对应的 floorGroup
|
|
907
|
+
if (isMulti) {
|
|
908
|
+
var floorIndex = 0;
|
|
909
|
+
var totalLoaded = 0;
|
|
910
|
+
var totalFailed = 0;
|
|
911
|
+
|
|
912
|
+
return objUrls.reduce(function(promise, url) {
|
|
913
|
+
return promise.then(function() {
|
|
914
|
+
return self._loadSingleFloorKidata(url, floorIndex++);
|
|
915
|
+
}).then(function(result) {
|
|
916
|
+
totalLoaded += result.loaded;
|
|
917
|
+
totalFailed += result.failed;
|
|
918
|
+
});
|
|
919
|
+
}, Promise.resolve()).then(function() {
|
|
920
|
+
console.log('✅ 多楼层家具加载完成:成功 ' + totalLoaded + ' 个,失败 ' + totalFailed + ' 个');
|
|
921
|
+
return { success: true, loaded: totalLoaded, failed: totalFailed };
|
|
922
|
+
}).catch(function(error) {
|
|
923
|
+
console.error('❌ 多楼层家具加载失败:', error.message);
|
|
924
|
+
return { success: false, error: error.message };
|
|
923
925
|
});
|
|
924
926
|
}
|
|
925
927
|
|
|
926
|
-
//
|
|
927
|
-
var
|
|
928
|
-
var
|
|
929
|
-
var
|
|
930
|
-
var fileName = firstUrl.split('/').pop().replace(/\.(obj|kimap)$/i, '');
|
|
928
|
+
// 单楼层:原逻辑
|
|
929
|
+
var lastSlashIndex = objUrls.lastIndexOf('/');
|
|
930
|
+
var directory = lastSlashIndex >= 0 ? objUrls.substring(0, lastSlashIndex) : '';
|
|
931
|
+
var fileName = objUrls.split('/').pop().replace(/\.(obj|kimap)$/i, '');
|
|
931
932
|
var kidataUrl = directory + '/' + fileName + '.kidata';
|
|
932
933
|
|
|
933
934
|
return fetch(kidataUrl)
|
|
@@ -939,10 +940,14 @@ KimapSDK.prototype.loadKMData = function() {
|
|
|
939
940
|
var decrypted = self._decryptKidata(encryptedData);
|
|
940
941
|
var kidataObj = JSON.parse(decrypted);
|
|
941
942
|
self.kidataContent = kidataObj;
|
|
942
|
-
|
|
943
|
-
|
|
943
|
+
|
|
944
|
+
if (kidataObj.floors && Array.isArray(kidataObj.floors)) {
|
|
945
|
+
if (!self.projectLayers && kidataObj.floors.length > 0) {
|
|
946
|
+
self.projectLayers = kidataObj.floors[0].layers;
|
|
947
|
+
}
|
|
944
948
|
}
|
|
945
|
-
|
|
949
|
+
|
|
950
|
+
return self._loadFurnitureModels(kidataObj, -1); // -1 = 使用全局 furnitureGroup
|
|
946
951
|
})
|
|
947
952
|
.then(function(result) {
|
|
948
953
|
console.log('✅ 家具加载完成:成功 ' + result.loaded + ' 个,失败 ' + result.failed + ' 个');
|
|
@@ -955,15 +960,18 @@ KimapSDK.prototype.loadKMData = function() {
|
|
|
955
960
|
};
|
|
956
961
|
|
|
957
962
|
/**
|
|
958
|
-
*
|
|
963
|
+
* 多楼层专用:加载单个楼层的 kidata → 家具到对应 floorGroup
|
|
964
|
+
* @param {string} floorUrl - 该楼层的模型 URL
|
|
965
|
+
* @param {number} floorIndex - 楼层索引
|
|
966
|
+
* @returns {Promise}
|
|
959
967
|
* @private
|
|
960
968
|
*/
|
|
961
|
-
KimapSDK.prototype.
|
|
969
|
+
KimapSDK.prototype._loadSingleFloorKidata = function(floorUrl, floorIndex) {
|
|
962
970
|
var self = this;
|
|
963
971
|
|
|
964
|
-
var lastSlashIndex =
|
|
965
|
-
var directory = lastSlashIndex >= 0 ?
|
|
966
|
-
var fileName =
|
|
972
|
+
var lastSlashIndex = floorUrl.lastIndexOf('/');
|
|
973
|
+
var directory = lastSlashIndex >= 0 ? floorUrl.substring(0, lastSlashIndex) : '';
|
|
974
|
+
var fileName = floorUrl.split('/').pop().replace(/\.(obj|kimap)$/i, '');
|
|
967
975
|
var kidataUrl = directory + '/' + fileName + '.kidata';
|
|
968
976
|
|
|
969
977
|
return fetch(kidataUrl)
|
|
@@ -975,108 +983,16 @@ KimapSDK.prototype._loadKidataForFloor = function(url, floorIndex) {
|
|
|
975
983
|
var decrypted = self._decryptKidata(encryptedData);
|
|
976
984
|
var kidataObj = JSON.parse(decrypted);
|
|
977
985
|
|
|
978
|
-
//
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
return self._loadFurnitureModels(kidataObj, floorFurnitureGroup)
|
|
985
|
-
.then(function(furnitureResult) {
|
|
986
|
-
// 提取并渲染文字(每层独立)
|
|
987
|
-
self._loadTextForFloor(kidataObj, floorIndex);
|
|
988
|
-
return furnitureResult;
|
|
989
|
-
});
|
|
990
|
-
})
|
|
991
|
-
.then(function(result) {
|
|
992
|
-
return result;
|
|
993
|
-
})
|
|
994
|
-
.catch(function(error) {
|
|
995
|
-
console.error('❌ 楼层' + floorIndex + '加载失败:', error.message);
|
|
996
|
-
return { loaded: 0, failed: 0 };
|
|
997
|
-
});
|
|
998
|
-
};
|
|
999
|
-
|
|
1000
|
-
/**
|
|
1001
|
-
* 多楼层:加载所有楼层的 kidata(家具+文字)
|
|
1002
|
-
* @private
|
|
1003
|
-
*/
|
|
1004
|
-
KimapSDK.prototype._loadAllFloorKidatas = function(objUrlArray) {
|
|
1005
|
-
var self = this;
|
|
1006
|
-
var loadPromises = objUrlArray.map(function(url, index) {
|
|
1007
|
-
return self._loadKidataForFloor(url, index);
|
|
1008
|
-
});
|
|
1009
|
-
return Promise.all(loadPromises)
|
|
1010
|
-
.then(function(results) {
|
|
1011
|
-
var totalLoaded = results.reduce(function(s, r) { return s + (r.loaded || 0); }, 0);
|
|
1012
|
-
var totalFailed = results.reduce(function(s, r) { return s + (r.failed || 0); }, 0);
|
|
1013
|
-
console.log('✅ 全部楼层家具加载完成:成功 ' + totalLoaded + ' 个,失败 ' + totalFailed + ' 个');
|
|
1014
|
-
return { success: true, loaded: totalLoaded, failed: totalFailed };
|
|
1015
|
-
})
|
|
1016
|
-
.catch(function(error) {
|
|
1017
|
-
console.error('❌ 楼层家具加载失败:', error.message);
|
|
1018
|
-
return { success: false, error: error.message };
|
|
1019
|
-
});
|
|
1020
|
-
};
|
|
1021
|
-
|
|
1022
|
-
/**
|
|
1023
|
-
* 多楼层:为指定楼层渲染文字
|
|
1024
|
-
* @private
|
|
1025
|
-
*/
|
|
1026
|
-
KimapSDK.prototype._loadTextForFloor = function(kidataObj, floorIndex) {
|
|
1027
|
-
var self = this;
|
|
1028
|
-
var floorTextGroup = this.core.floorTextGroups[floorIndex];
|
|
1029
|
-
if (!floorTextGroup) return;
|
|
1030
|
-
|
|
1031
|
-
var textElements = [];
|
|
1032
|
-
if (kidataObj.floors && Array.isArray(kidataObj.floors)) {
|
|
1033
|
-
kidataObj.floors.forEach(function(floor) {
|
|
1034
|
-
if (floor.layers) {
|
|
1035
|
-
floor.layers.forEach(function(layer) {
|
|
1036
|
-
if (layer.elements) {
|
|
1037
|
-
layer.elements.forEach(function(element) {
|
|
1038
|
-
if (element.type === 'text') {
|
|
1039
|
-
textElements.push(element);
|
|
1040
|
-
}
|
|
1041
|
-
});
|
|
1042
|
-
}
|
|
1043
|
-
});
|
|
986
|
+
// 保存第一层 kidata 供路径规划使用
|
|
987
|
+
if (floorIndex === 0) {
|
|
988
|
+
self.kidataContent = kidataObj;
|
|
989
|
+
if (kidataObj.floors && Array.isArray(kidataObj.floors) && !self.projectLayers) {
|
|
990
|
+
self.projectLayers = kidataObj.floors[0].layers;
|
|
991
|
+
}
|
|
1044
992
|
}
|
|
1045
|
-
});
|
|
1046
|
-
}
|
|
1047
993
|
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
textElements.forEach(function(textEl) {
|
|
1051
|
-
if (!textEl.points || textEl.points.length === 0) return;
|
|
1052
|
-
var point = textEl.points[0];
|
|
1053
|
-
var x = point.x / 100;
|
|
1054
|
-
var z = point.y / 100;
|
|
1055
|
-
var LOGICAL_WALL_HEIGHT = 3000;
|
|
1056
|
-
var RENDER_WALL_HEIGHT = 0.375;
|
|
1057
|
-
var elevationMm = textEl.elevation || 8500;
|
|
1058
|
-
var y = (elevationMm / LOGICAL_WALL_HEIGHT) * RENDER_WALL_HEIGHT;
|
|
1059
|
-
var TEXT_SCALE_FACTOR = 2.52;
|
|
1060
|
-
|
|
1061
|
-
var sprite = self.core.createTextSprite({
|
|
1062
|
-
content: textEl.content || '',
|
|
1063
|
-
fontSize: (textEl.fontSize || 16) * TEXT_SCALE_FACTOR,
|
|
1064
|
-
color: textEl.color || '#000000',
|
|
1065
|
-
fontFamily: textEl.fontFamily || 'Arial, sans-serif',
|
|
1066
|
-
bold: textEl.bold || false,
|
|
1067
|
-
italic: textEl.italic || false,
|
|
1068
|
-
borderColor: textEl.borderColor || '#ADD8E6',
|
|
1069
|
-
backgroundColor: textEl.backgroundColor || '#FFFFFF',
|
|
1070
|
-
backgroundOpacity: textEl.backgroundOpacity !== undefined ? textEl.backgroundOpacity : 0.8,
|
|
1071
|
-
padding: textEl.padding || 8
|
|
994
|
+
return self._loadFurnitureModels(kidataObj, floorIndex);
|
|
1072
995
|
});
|
|
1073
|
-
|
|
1074
|
-
sprite.position.set(x, y, z);
|
|
1075
|
-
sprite.userData = { type: 'text', elementId: textEl.id };
|
|
1076
|
-
floorTextGroup.add(sprite);
|
|
1077
|
-
});
|
|
1078
|
-
|
|
1079
|
-
console.log('✅ 楼层' + floorIndex + '文字加载完成: ' + textElements.length + ' 个');
|
|
1080
996
|
};
|
|
1081
997
|
|
|
1082
998
|
KimapSDK.prototype._decryptKidata = function(encrypted) {
|
|
@@ -1098,29 +1014,53 @@ KimapSDK.prototype._decryptKidata = function(encrypted) {
|
|
|
1098
1014
|
}
|
|
1099
1015
|
};
|
|
1100
1016
|
|
|
1101
|
-
KimapSDK.prototype._loadFurnitureModels = function(kidataObj,
|
|
1017
|
+
KimapSDK.prototype._loadFurnitureModels = function(kidataObj, floorIndex) {
|
|
1102
1018
|
var self = this;
|
|
1019
|
+
var isMultiFloorMode = floorIndex !== undefined && floorIndex >= 0;
|
|
1103
1020
|
|
|
1104
1021
|
if (!kidataObj.furnitures || kidataObj.furnitures.length === 0) {
|
|
1105
1022
|
return Promise.resolve({ loaded: 0, failed: 0 });
|
|
1106
1023
|
}
|
|
1107
1024
|
|
|
1108
|
-
|
|
1109
|
-
if (!this.furnitureGroup) {
|
|
1110
|
-
this.furnitureGroup = new THREE.Group();
|
|
1111
|
-
this.furnitureGroup.name = 'furnitures';
|
|
1112
|
-
this.core.scene.add(this.furnitureGroup);
|
|
1113
|
-
}
|
|
1025
|
+
var furnitureGroup;
|
|
1114
1026
|
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1027
|
+
if (isMultiFloorMode) {
|
|
1028
|
+
// 多楼层模式:家具放进对应楼层的 furnitureGroup(作为 floorGroup 子级)
|
|
1029
|
+
if (!this.floorFurnitureGroups[floorIndex]) {
|
|
1030
|
+
furnitureGroup = new THREE.Group();
|
|
1031
|
+
furnitureGroup.name = 'furnitures';
|
|
1032
|
+
this.floorFurnitureGroups[floorIndex] = furnitureGroup;
|
|
1033
|
+
var floorGroup = this.core.floorGroups[floorIndex];
|
|
1034
|
+
if (floorGroup) floorGroup.add(furnitureGroup);
|
|
1035
|
+
} else {
|
|
1036
|
+
furnitureGroup = this.floorFurnitureGroups[floorIndex];
|
|
1037
|
+
// 清空现有家具
|
|
1038
|
+
while (furnitureGroup.children.length > 0) {
|
|
1039
|
+
var child = furnitureGroup.children[0];
|
|
1040
|
+
furnitureGroup.remove(child);
|
|
1041
|
+
if (child.geometry) child.geometry.dispose();
|
|
1042
|
+
if (child.material) {
|
|
1043
|
+
if (Array.isArray(child.material)) {
|
|
1044
|
+
child.material.forEach(function(m) { m.dispose(); });
|
|
1045
|
+
} else {
|
|
1046
|
+
child.material.dispose();
|
|
1047
|
+
}
|
|
1048
|
+
}
|
|
1049
|
+
}
|
|
1050
|
+
}
|
|
1051
|
+
} else {
|
|
1052
|
+
// 单楼层模式:家具放进全局 furnitureGroup(直接加到 scene)
|
|
1053
|
+
if (!this.furnitureGroup) {
|
|
1054
|
+
this.furnitureGroup = new THREE.Group();
|
|
1055
|
+
this.furnitureGroup.name = 'furnitures';
|
|
1056
|
+
this.core.scene.add(this.furnitureGroup);
|
|
1057
|
+
}
|
|
1058
|
+
furnitureGroup = this.furnitureGroup;
|
|
1118
1059
|
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
this.furnitureGroup.remove(child);
|
|
1060
|
+
// 清空现有家具
|
|
1061
|
+
while (furnitureGroup.children.length > 0) {
|
|
1062
|
+
var child = furnitureGroup.children[0];
|
|
1063
|
+
furnitureGroup.remove(child);
|
|
1124
1064
|
if (child.geometry) child.geometry.dispose();
|
|
1125
1065
|
if (child.material) {
|
|
1126
1066
|
if (Array.isArray(child.material)) {
|
|
@@ -1138,11 +1078,13 @@ KimapSDK.prototype._loadFurnitureModels = function(kidataObj, targetGroup) {
|
|
|
1138
1078
|
var loadedCount = 0;
|
|
1139
1079
|
var failedCount = 0;
|
|
1140
1080
|
|
|
1081
|
+
// 加载每个家具模型
|
|
1082
|
+
var self2 = this;
|
|
1141
1083
|
kidataObj.furnitures.forEach(function(furniture) {
|
|
1142
|
-
var promise =
|
|
1084
|
+
var promise = self2._loadSingleFurniture(furniture, serverUrl)
|
|
1143
1085
|
.then(function(mesh) {
|
|
1144
1086
|
if (mesh) {
|
|
1145
|
-
|
|
1087
|
+
furnitureGroup.add(mesh);
|
|
1146
1088
|
loadedCount++;
|
|
1147
1089
|
} else {
|
|
1148
1090
|
failedCount++;
|
package/src/core/SceneCore.js
CHANGED
|
@@ -62,12 +62,11 @@ function SceneCore(config) {
|
|
|
62
62
|
|
|
63
63
|
// 多楼层支持(不影响单楼层原有渲染路径)
|
|
64
64
|
this.floorGroups = [];
|
|
65
|
-
this.
|
|
66
|
-
this.
|
|
65
|
+
this.floorTextGroups = []; // 每个楼层的文字组
|
|
66
|
+
this.floorTextElements = []; // 每个楼层的文字数据
|
|
67
67
|
this.floorHeight = 3; // 米
|
|
68
68
|
this.isMultiFloor = Array.isArray(config.objUrl);
|
|
69
69
|
this.floorOriginalY = []; // 每个楼层的原始世界Y偏移(含间隙)
|
|
70
|
-
this.onFloorModelsLoaded = null; // 回调:所有楼层模型加载完毕后触发
|
|
71
70
|
}
|
|
72
71
|
|
|
73
72
|
/**
|
|
@@ -100,12 +99,6 @@ SceneCore.prototype.init = function() {
|
|
|
100
99
|
var OBJLoader = loaders[0];
|
|
101
100
|
var MTLLoader = loaders[1];
|
|
102
101
|
return self.loadMap(OBJLoader, MTLLoader);
|
|
103
|
-
})
|
|
104
|
-
.then(function() {
|
|
105
|
-
// 通知 onFloorModelsLoaded(如果已注册)
|
|
106
|
-
if (typeof self.onFloorModelsLoaded === 'function') {
|
|
107
|
-
self.onFloorModelsLoaded();
|
|
108
|
-
}
|
|
109
102
|
});
|
|
110
103
|
};
|
|
111
104
|
|
|
@@ -277,11 +270,10 @@ SceneCore.prototype.loadMap = function(OBJLoader, MTLLoader) {
|
|
|
277
270
|
};
|
|
278
271
|
|
|
279
272
|
/**
|
|
280
|
-
*
|
|
273
|
+
* 多楼层:依次加载多个模型(新增能力,不改变单楼层逻辑)
|
|
281
274
|
* @private
|
|
282
|
-
* @param {Function} onAllLoaded - 所有楼层模型加载完毕后的回调(此时 floorGroups 已就绪)
|
|
283
275
|
*/
|
|
284
|
-
SceneCore.prototype._loadMultiFloorMaps = function(OBJLoader, MTLLoader, objUrls
|
|
276
|
+
SceneCore.prototype._loadMultiFloorMaps = function(OBJLoader, MTLLoader, objUrls) {
|
|
285
277
|
var self = this;
|
|
286
278
|
var floorIndex = 0;
|
|
287
279
|
|
|
@@ -290,16 +282,8 @@ SceneCore.prototype._loadMultiFloorMaps = function(OBJLoader, MTLLoader, objUrls
|
|
|
290
282
|
return self._loadSingleMapWithFloorIndex(OBJLoader, MTLLoader, url, floorIndex++);
|
|
291
283
|
});
|
|
292
284
|
}, Promise.resolve()).then(function() {
|
|
293
|
-
// 阶段二:所有楼层加载完毕后,按联合包围盒中心对齐 XZ
|
|
294
285
|
if (self.floorGroups.length > 1) {
|
|
295
|
-
self.
|
|
296
|
-
}
|
|
297
|
-
// 通知 SDK 层:楼层模型已就绪,可以加载各层 kidata 了
|
|
298
|
-
if (typeof onAllLoaded === 'function') {
|
|
299
|
-
onAllLoaded();
|
|
300
|
-
}
|
|
301
|
-
if (typeof self.onFloorModelsLoaded === 'function') {
|
|
302
|
-
self.onFloorModelsLoaded();
|
|
286
|
+
self._adjustCameraToAllFloors();
|
|
303
287
|
}
|
|
304
288
|
});
|
|
305
289
|
};
|
|
@@ -391,6 +375,13 @@ SceneCore.prototype._loadKimapFileToGroup = function(kimapUrl, themeUrl, objLoad
|
|
|
391
375
|
var object = objLoader.parse(objContent);
|
|
392
376
|
self._processLoadedModelToGroup(object, materials, colorMap, floorGroup, floorIndex);
|
|
393
377
|
|
|
378
|
+
// 每个楼层从自己的 .kimap 文件中提取并渲染文字
|
|
379
|
+
var textElements = extractTextElementsFromOBJ(objContent);
|
|
380
|
+
if (textElements && textElements.length > 0) {
|
|
381
|
+
self.floorTextElements[floorIndex] = textElements;
|
|
382
|
+
self.renderTextElementsToFloor(floorIndex, textElements);
|
|
383
|
+
}
|
|
384
|
+
|
|
394
385
|
resolve();
|
|
395
386
|
} catch (error) {
|
|
396
387
|
reject(error);
|
|
@@ -427,38 +418,34 @@ SceneCore.prototype._processLoadedModelToGroup = function(object, materials, col
|
|
|
427
418
|
var self = this;
|
|
428
419
|
floorGroup.add(object);
|
|
429
420
|
|
|
430
|
-
// 为该层创建文字组(作为 floorGroup 子级,随楼层移动)
|
|
431
|
-
if (!self.floorTextGroups) self.floorTextGroups = [];
|
|
432
|
-
var floorTextGroup = new THREE.Group();
|
|
433
|
-
floorTextGroup.name = 'texts_floor_' + floorIndex;
|
|
434
|
-
floorGroup.add(floorTextGroup);
|
|
435
|
-
self.floorTextGroups[floorIndex] = floorTextGroup;
|
|
436
|
-
|
|
437
421
|
var box = new THREE.Box3().setFromObject(object);
|
|
438
422
|
var boxHeight = box.max.y - box.min.y;
|
|
439
423
|
|
|
440
|
-
// 记录每层的包围盒(供两阶段定位使用)
|
|
441
|
-
if (!self._floorBoxes) self._floorBoxes = [];
|
|
442
|
-
self._floorBoxes[floorIndex] = box;
|
|
443
|
-
|
|
444
424
|
// 逐层累积高度:计算该楼层在 ALL 模式下的世界 Y 偏移
|
|
445
425
|
var floorY = 0;
|
|
446
426
|
for (var i = 0; i < floorIndex; i++) {
|
|
447
|
-
var
|
|
448
|
-
if (
|
|
427
|
+
var prevGroup = self.floorGroups[i];
|
|
428
|
+
if (prevGroup) {
|
|
429
|
+
var prevBox = new THREE.Box3().setFromObject(prevGroup);
|
|
430
|
+
floorY += (prevBox.max.y - prevBox.min.y);
|
|
431
|
+
}
|
|
449
432
|
}
|
|
450
433
|
|
|
451
|
-
// object
|
|
452
|
-
// 单层模式 floorGroup.position.y=0 → 对象底部世界 Y=0(贴地)
|
|
453
|
-
// ALL 模式 floorGroup.position.y=floorY → 对象底部世界 Y=floorY(堆叠)
|
|
434
|
+
// 所有楼层的 object 底部(box.min.y)对齐到本地 Y=0,统一基准
|
|
454
435
|
object.position.set(-box.min.x, -box.min.y, -box.min.z);
|
|
455
436
|
|
|
456
|
-
//
|
|
457
|
-
floorGroup.position.
|
|
437
|
+
// ALL 模式时 floorGroup.position.y 持有累积偏移(含间隙)
|
|
438
|
+
floorGroup.position.y = floorY;
|
|
458
439
|
|
|
459
|
-
//
|
|
440
|
+
// 记录每个楼层的原始世界 Y 偏移(单层模式时用于将选中层拽回 Y=0)
|
|
460
441
|
self.floorOriginalY[floorIndex] = floorY;
|
|
461
442
|
|
|
443
|
+
// 为该楼层创建独立的文字组
|
|
444
|
+
var textGroup = new THREE.Group();
|
|
445
|
+
textGroup.name = 'texts';
|
|
446
|
+
floorGroup.add(textGroup);
|
|
447
|
+
self.floorTextGroups[floorIndex] = textGroup;
|
|
448
|
+
|
|
462
449
|
// 复用 backup 的材质处理策略,不改渲染风格
|
|
463
450
|
object.traverse(function(child) {
|
|
464
451
|
if (child.name === 'HiddenGroundPlane' || child.name.includes('InteractionLayer')) {
|
|
@@ -471,34 +458,6 @@ SceneCore.prototype._processLoadedModelToGroup = function(object, materials, col
|
|
|
471
458
|
});
|
|
472
459
|
};
|
|
473
460
|
|
|
474
|
-
/**
|
|
475
|
-
* 多楼层:ALL 模式按每个楼层的几何中心对齐 XZ(Y 堆叠不变)
|
|
476
|
-
* object.position = (-box.min.x, -box.min.y, -box.min.z)
|
|
477
|
-
* 所以对象在 group 空间里范围是 [0~width, minY~maxY, 0~depth]
|
|
478
|
-
* 中心 = (width/2, (minY+maxY)/2, depth/2)
|
|
479
|
-
* 令 group.position.x/z = -center,即把中心对齐到世界原点
|
|
480
|
-
* @private
|
|
481
|
-
*/
|
|
482
|
-
SceneCore.prototype._alignAllFloorsToCenter = function() {
|
|
483
|
-
var self = this;
|
|
484
|
-
if (!self._floorBoxes || self._floorBoxes.length === 0) return;
|
|
485
|
-
|
|
486
|
-
self.floorGroups.forEach(function(group, index) {
|
|
487
|
-
var box = self._floorBoxes[index];
|
|
488
|
-
if (!box) return;
|
|
489
|
-
|
|
490
|
-
var boxW = box.max.x - box.min.x;
|
|
491
|
-
var boxD = box.max.z - box.min.z;
|
|
492
|
-
var centerX = boxW / 2;
|
|
493
|
-
var centerZ = boxD / 2;
|
|
494
|
-
|
|
495
|
-
group.position.x = -centerX;
|
|
496
|
-
group.position.z = -centerZ;
|
|
497
|
-
});
|
|
498
|
-
|
|
499
|
-
console.log('[SceneCore] 多楼层按中心对齐 XZ 完成');
|
|
500
|
-
};
|
|
501
|
-
|
|
502
461
|
/**
|
|
503
462
|
* 多楼层:相机看全楼层
|
|
504
463
|
* @private
|
|
@@ -1052,6 +1011,57 @@ SceneCore.prototype.createTextSprite = function(options) {
|
|
|
1052
1011
|
return sprite;
|
|
1053
1012
|
};
|
|
1054
1013
|
|
|
1014
|
+
/**
|
|
1015
|
+
* 多楼层专用:渲染文字到指定楼层的 textGroup
|
|
1016
|
+
*/
|
|
1017
|
+
SceneCore.prototype.renderTextElementsToFloor = function(floorIndex, textElements) {
|
|
1018
|
+
var self = this;
|
|
1019
|
+
|
|
1020
|
+
if (!textElements || textElements.length === 0) return;
|
|
1021
|
+
|
|
1022
|
+
var floorGroup = this.floorGroups[floorIndex];
|
|
1023
|
+
var textGroup = this.floorTextGroups[floorIndex];
|
|
1024
|
+
if (!floorGroup || !textGroup) return;
|
|
1025
|
+
|
|
1026
|
+
// 清空该楼层现有文字
|
|
1027
|
+
while (textGroup.children.length > 0) {
|
|
1028
|
+
var child = textGroup.children[0];
|
|
1029
|
+
textGroup.remove(child);
|
|
1030
|
+
if (child.material && child.material.map) child.material.map.dispose();
|
|
1031
|
+
if (child.material) child.material.dispose();
|
|
1032
|
+
}
|
|
1033
|
+
|
|
1034
|
+
textElements.forEach(function(textEl) {
|
|
1035
|
+
if (!textEl.points || textEl.points.length === 0) return;
|
|
1036
|
+
|
|
1037
|
+
var point = textEl.points[0];
|
|
1038
|
+
var x = point.x / 100;
|
|
1039
|
+
var z = point.y / 100;
|
|
1040
|
+
var LOGICAL_WALL_HEIGHT = 3000;
|
|
1041
|
+
var RENDER_WALL_HEIGHT = 0.375;
|
|
1042
|
+
var elevationMm = textEl.elevation || 8500;
|
|
1043
|
+
var y = (elevationMm / LOGICAL_WALL_HEIGHT) * RENDER_WALL_HEIGHT;
|
|
1044
|
+
var TEXT_SCALE_FACTOR = 2.52;
|
|
1045
|
+
|
|
1046
|
+
var sprite = self.createTextSprite({
|
|
1047
|
+
content: textEl.content || '',
|
|
1048
|
+
fontSize: (textEl.fontSize || 16) * TEXT_SCALE_FACTOR,
|
|
1049
|
+
color: textEl.color || '#000000',
|
|
1050
|
+
fontFamily: textEl.fontFamily || 'Arial, sans-serif',
|
|
1051
|
+
bold: textEl.bold || false,
|
|
1052
|
+
italic: textEl.italic || false,
|
|
1053
|
+
borderColor: textEl.borderColor || '#ADD8E6',
|
|
1054
|
+
backgroundColor: textEl.backgroundColor || '#FFFFFF',
|
|
1055
|
+
backgroundOpacity: textEl.backgroundOpacity !== undefined ? textEl.backgroundOpacity : 0.8,
|
|
1056
|
+
padding: textEl.padding || 8
|
|
1057
|
+
});
|
|
1058
|
+
|
|
1059
|
+
sprite.position.set(x, y, z);
|
|
1060
|
+
sprite.userData = { type: 'text', elementId: textEl.id };
|
|
1061
|
+
textGroup.add(sprite);
|
|
1062
|
+
});
|
|
1063
|
+
};
|
|
1064
|
+
|
|
1055
1065
|
/**
|
|
1056
1066
|
* 渲染文本元素到场景
|
|
1057
1067
|
*/
|