@kimap/indoor-positioning-sdk-vue2 5.7.7 → 5.7.8
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 +187 -50
- package/src/core/SceneCore.js +68 -21
package/package.json
CHANGED
package/src/core/KimapSDK.js
CHANGED
|
@@ -82,9 +82,10 @@ KimapSDK.prototype.init = function() {
|
|
|
82
82
|
self.startRenderLoop();
|
|
83
83
|
self._extractWallsFromModel();
|
|
84
84
|
|
|
85
|
-
//
|
|
85
|
+
// 多楼层默认显示第一个楼层(单层模式),同时记录ALL模式间隙
|
|
86
86
|
if (self.isMultiFloor && self.floorGroups && self.floorGroups.length > 0) {
|
|
87
87
|
self.showSingleFloor(0);
|
|
88
|
+
self.showAllFloors(); // 立即恢复到ALL模式(含间隙),初始化后保持ALL状态
|
|
88
89
|
}
|
|
89
90
|
|
|
90
91
|
// 渲染内置楼层选择器 UI
|
|
@@ -607,16 +608,17 @@ KimapSDK.prototype.showSingleFloor = function(floorIndex) {
|
|
|
607
608
|
var self = this;
|
|
608
609
|
this.floorGroups.forEach(function(group, index) {
|
|
609
610
|
if (index === floorIndex) {
|
|
610
|
-
//
|
|
611
|
+
// 贴地 + 清除ALL模式的XZ中心对齐
|
|
611
612
|
group.visible = true;
|
|
612
|
-
group.position.
|
|
613
|
+
group.position.set(0, 0, 0);
|
|
613
614
|
} else {
|
|
614
|
-
// 隐藏其他楼层,同时移到下方避免干扰
|
|
615
615
|
group.visible = false;
|
|
616
616
|
var originalY = self.core.floorOriginalY[index] || 0;
|
|
617
|
-
group.position.y = -originalY - 1;
|
|
617
|
+
group.position.y = -originalY - 1;
|
|
618
|
+
// XZ 保持中心对齐(向下移动后不再占用视觉空间)
|
|
618
619
|
}
|
|
619
620
|
});
|
|
621
|
+
this.currentFloorIndex = floorIndex;
|
|
620
622
|
};
|
|
621
623
|
|
|
622
624
|
/**
|
|
@@ -628,10 +630,10 @@ KimapSDK.prototype.showAllFloors = function() {
|
|
|
628
630
|
var self = this;
|
|
629
631
|
this.floorGroups.forEach(function(group, index) {
|
|
630
632
|
group.visible = true;
|
|
631
|
-
// 恢复原始世界 Y 偏移(含间隙)
|
|
632
633
|
var originalY = self.core.floorOriginalY[index] || 0;
|
|
633
634
|
group.position.y = originalY;
|
|
634
635
|
});
|
|
636
|
+
this.currentFloorIndex = 0;
|
|
635
637
|
};
|
|
636
638
|
|
|
637
639
|
/**
|
|
@@ -888,55 +890,62 @@ KimapSDK.prototype.getConfig = function() {
|
|
|
888
890
|
|
|
889
891
|
KimapSDK.prototype.loadKMData = function() {
|
|
890
892
|
var self = this;
|
|
891
|
-
|
|
893
|
+
|
|
892
894
|
if (!this.dataUrl) {
|
|
893
895
|
console.warn('⚠️ 未配置dataUrl,无法加载3D家具模型');
|
|
894
896
|
return Promise.resolve({ success: false, message: '未配置dataUrl' });
|
|
895
897
|
}
|
|
896
|
-
|
|
898
|
+
|
|
897
899
|
if (!this.config.objUrl) {
|
|
898
900
|
console.warn('⚠️ 未配置objUrl,无法确定kidata文件名');
|
|
899
901
|
return Promise.resolve({ success: false, message: '未配置objUrl' });
|
|
900
902
|
}
|
|
901
|
-
|
|
902
|
-
// 从objUrl提取文件名和目录(支持数组或字符串)
|
|
903
|
+
|
|
903
904
|
var objUrl = this.config.objUrl;
|
|
904
905
|
var isArray = Array.isArray(objUrl);
|
|
905
|
-
|
|
906
|
+
|
|
907
|
+
// 多楼层模式:等 floorGroups 就绪后再加载各层 kidata
|
|
908
|
+
if (isArray) {
|
|
909
|
+
var self2 = this;
|
|
910
|
+
if (self2.floorGroups && self2.floorGroups.length > 0) {
|
|
911
|
+
// 模型已就绪,直接加载
|
|
912
|
+
return self2._loadAllFloorKidatas(objUrl);
|
|
913
|
+
}
|
|
914
|
+
// 模型未就绪,等回调
|
|
915
|
+
return new Promise(function(resolve) {
|
|
916
|
+
var original = self2.core.onFloorModelsLoaded;
|
|
917
|
+
self2.core.onFloorModelsLoaded = function() {
|
|
918
|
+
if (typeof original === 'function') original();
|
|
919
|
+
resolve();
|
|
920
|
+
};
|
|
921
|
+
}).then(function() {
|
|
922
|
+
return self2._loadAllFloorKidatas(objUrl);
|
|
923
|
+
});
|
|
924
|
+
}
|
|
925
|
+
|
|
926
|
+
// 单楼层模式:沿用原有逻辑
|
|
927
|
+
var firstUrl = objUrl;
|
|
906
928
|
var lastSlashIndex = firstUrl.lastIndexOf('/');
|
|
907
929
|
var directory = lastSlashIndex >= 0 ? firstUrl.substring(0, lastSlashIndex) : '';
|
|
908
930
|
var fileName = firstUrl.split('/').pop().replace(/\.(obj|kimap)$/i, '');
|
|
909
931
|
var kidataUrl = directory + '/' + fileName + '.kidata';
|
|
910
|
-
|
|
932
|
+
|
|
911
933
|
return fetch(kidataUrl)
|
|
912
934
|
.then(function(response) {
|
|
913
|
-
if (!response.ok)
|
|
914
|
-
throw new Error('Kidata文件加载失败: ' + response.status);
|
|
915
|
-
}
|
|
935
|
+
if (!response.ok) throw new Error('Kidata文件加载失败: ' + response.status);
|
|
916
936
|
return response.text();
|
|
917
937
|
})
|
|
918
938
|
.then(function(encryptedData) {
|
|
919
|
-
// 解密kidata文件
|
|
920
939
|
var decrypted = self._decryptKidata(encryptedData);
|
|
921
940
|
var kidataObj = JSON.parse(decrypted);
|
|
922
|
-
|
|
923
|
-
// 保存kidata数据供路径规划使用
|
|
924
941
|
self.kidataContent = kidataObj;
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
if (kidataObj.floors && Array.isArray(kidataObj.floors)) {
|
|
928
|
-
// 设置第一个楼层为当前楼层(如果没有其他楼层数据)
|
|
929
|
-
if (!self.projectLayers && kidataObj.floors.length > 0) {
|
|
930
|
-
self.projectLayers = kidataObj.floors[0].layers;
|
|
931
|
-
}
|
|
942
|
+
if (kidataObj.floors && Array.isArray(kidataObj.floors) && kidataObj.floors.length > 0) {
|
|
943
|
+
self.projectLayers = kidataObj.floors[0].layers;
|
|
932
944
|
}
|
|
933
|
-
|
|
934
|
-
// 加载3D家具模型
|
|
935
945
|
return self._loadFurnitureModels(kidataObj);
|
|
936
946
|
})
|
|
937
947
|
.then(function(result) {
|
|
938
948
|
console.log('✅ 家具加载完成:成功 ' + result.loaded + ' 个,失败 ' + result.failed + ' 个');
|
|
939
|
-
|
|
940
949
|
return { success: true, loaded: result.loaded, failed: result.failed };
|
|
941
950
|
})
|
|
942
951
|
.catch(function(error) {
|
|
@@ -945,6 +954,131 @@ KimapSDK.prototype.loadKMData = function() {
|
|
|
945
954
|
});
|
|
946
955
|
};
|
|
947
956
|
|
|
957
|
+
/**
|
|
958
|
+
* 多楼层:为指定楼层加载 kidata(含家具 + 文字)
|
|
959
|
+
* @private
|
|
960
|
+
*/
|
|
961
|
+
KimapSDK.prototype._loadKidataForFloor = function(url, floorIndex) {
|
|
962
|
+
var self = this;
|
|
963
|
+
|
|
964
|
+
var lastSlashIndex = url.lastIndexOf('/');
|
|
965
|
+
var directory = lastSlashIndex >= 0 ? url.substring(0, lastSlashIndex) : '';
|
|
966
|
+
var fileName = url.split('/').pop().replace(/\.(obj|kimap)$/i, '');
|
|
967
|
+
var kidataUrl = directory + '/' + fileName + '.kidata';
|
|
968
|
+
|
|
969
|
+
return fetch(kidataUrl)
|
|
970
|
+
.then(function(response) {
|
|
971
|
+
if (!response.ok) throw new Error('Kidata文件加载失败: ' + response.status);
|
|
972
|
+
return response.text();
|
|
973
|
+
})
|
|
974
|
+
.then(function(encryptedData) {
|
|
975
|
+
var decrypted = self._decryptKidata(encryptedData);
|
|
976
|
+
var kidataObj = JSON.parse(decrypted);
|
|
977
|
+
|
|
978
|
+
// 创建该楼层的家具组(作为 floorGroup 的子级,随楼层移动)
|
|
979
|
+
var floorFurnitureGroup = new THREE.Group();
|
|
980
|
+
floorFurnitureGroup.name = 'furniture_floor_' + floorIndex;
|
|
981
|
+
self.floorGroups[floorIndex].add(floorFurnitureGroup);
|
|
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
|
+
});
|
|
1044
|
+
}
|
|
1045
|
+
});
|
|
1046
|
+
}
|
|
1047
|
+
|
|
1048
|
+
if (textElements.length === 0) return;
|
|
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
|
|
1072
|
+
});
|
|
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
|
+
};
|
|
1081
|
+
|
|
948
1082
|
KimapSDK.prototype._decryptKidata = function(encrypted) {
|
|
949
1083
|
try {
|
|
950
1084
|
// 1. Base64解码
|
|
@@ -964,48 +1098,51 @@ KimapSDK.prototype._decryptKidata = function(encrypted) {
|
|
|
964
1098
|
}
|
|
965
1099
|
};
|
|
966
1100
|
|
|
967
|
-
KimapSDK.prototype._loadFurnitureModels = function(kidataObj) {
|
|
1101
|
+
KimapSDK.prototype._loadFurnitureModels = function(kidataObj, targetGroup) {
|
|
968
1102
|
var self = this;
|
|
969
|
-
|
|
1103
|
+
|
|
970
1104
|
if (!kidataObj.furnitures || kidataObj.furnitures.length === 0) {
|
|
971
1105
|
return Promise.resolve({ loaded: 0, failed: 0 });
|
|
972
1106
|
}
|
|
973
|
-
|
|
1107
|
+
|
|
974
1108
|
// 创建家具组(如果不存在)
|
|
975
1109
|
if (!this.furnitureGroup) {
|
|
976
1110
|
this.furnitureGroup = new THREE.Group();
|
|
977
1111
|
this.furnitureGroup.name = 'furnitures';
|
|
978
1112
|
this.core.scene.add(this.furnitureGroup);
|
|
979
1113
|
}
|
|
980
|
-
|
|
981
|
-
//
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
child.material
|
|
1114
|
+
|
|
1115
|
+
// 多楼层模式:家具加载到指定楼层的子Group(随楼层移动)
|
|
1116
|
+
// 单楼层模式:家具加载到场景家具组(独立存在)
|
|
1117
|
+
var target = targetGroup || this.furnitureGroup;
|
|
1118
|
+
|
|
1119
|
+
// 单楼层模式:清空场景家具组
|
|
1120
|
+
if (!targetGroup) {
|
|
1121
|
+
while (this.furnitureGroup.children.length > 0) {
|
|
1122
|
+
var child = this.furnitureGroup.children[0];
|
|
1123
|
+
this.furnitureGroup.remove(child);
|
|
1124
|
+
if (child.geometry) child.geometry.dispose();
|
|
1125
|
+
if (child.material) {
|
|
1126
|
+
if (Array.isArray(child.material)) {
|
|
1127
|
+
child.material.forEach(function(m) { m.dispose(); });
|
|
1128
|
+
} else {
|
|
1129
|
+
child.material.dispose();
|
|
1130
|
+
}
|
|
993
1131
|
}
|
|
994
1132
|
}
|
|
995
1133
|
}
|
|
996
|
-
|
|
1134
|
+
|
|
997
1135
|
// 优先使用dataUrl,如果不存在则使用serverUrl(兼容旧版本)
|
|
998
1136
|
var serverUrl = kidataObj.dataUrl || kidataObj.serverUrl;
|
|
999
1137
|
var loadPromises = [];
|
|
1000
1138
|
var loadedCount = 0;
|
|
1001
1139
|
var failedCount = 0;
|
|
1002
|
-
|
|
1003
|
-
// 加载每个家具模型
|
|
1140
|
+
|
|
1004
1141
|
kidataObj.furnitures.forEach(function(furniture) {
|
|
1005
1142
|
var promise = self._loadSingleFurniture(furniture, serverUrl)
|
|
1006
1143
|
.then(function(mesh) {
|
|
1007
1144
|
if (mesh) {
|
|
1008
|
-
|
|
1145
|
+
target.add(mesh);
|
|
1009
1146
|
loadedCount++;
|
|
1010
1147
|
} else {
|
|
1011
1148
|
failedCount++;
|
|
@@ -1014,10 +1151,10 @@ KimapSDK.prototype._loadFurnitureModels = function(kidataObj) {
|
|
|
1014
1151
|
.catch(function(error) {
|
|
1015
1152
|
failedCount++;
|
|
1016
1153
|
});
|
|
1017
|
-
|
|
1154
|
+
|
|
1018
1155
|
loadPromises.push(promise);
|
|
1019
1156
|
});
|
|
1020
|
-
|
|
1157
|
+
|
|
1021
1158
|
return Promise.all(loadPromises).then(function() {
|
|
1022
1159
|
return { loaded: loadedCount, failed: failedCount };
|
|
1023
1160
|
});
|
package/src/core/SceneCore.js
CHANGED
|
@@ -62,9 +62,12 @@ function SceneCore(config) {
|
|
|
62
62
|
|
|
63
63
|
// 多楼层支持(不影响单楼层原有渲染路径)
|
|
64
64
|
this.floorGroups = [];
|
|
65
|
+
this.floorFurnitureGroups = []; // 每层独立的家具Group
|
|
66
|
+
this.floorTextGroups = []; // 每层独立的文字Group
|
|
65
67
|
this.floorHeight = 3; // 米
|
|
66
68
|
this.isMultiFloor = Array.isArray(config.objUrl);
|
|
67
69
|
this.floorOriginalY = []; // 每个楼层的原始世界Y偏移(含间隙)
|
|
70
|
+
this.onFloorModelsLoaded = null; // 回调:所有楼层模型加载完毕后触发
|
|
68
71
|
}
|
|
69
72
|
|
|
70
73
|
/**
|
|
@@ -97,6 +100,12 @@ SceneCore.prototype.init = function() {
|
|
|
97
100
|
var OBJLoader = loaders[0];
|
|
98
101
|
var MTLLoader = loaders[1];
|
|
99
102
|
return self.loadMap(OBJLoader, MTLLoader);
|
|
103
|
+
})
|
|
104
|
+
.then(function() {
|
|
105
|
+
// 通知 onFloorModelsLoaded(如果已注册)
|
|
106
|
+
if (typeof self.onFloorModelsLoaded === 'function') {
|
|
107
|
+
self.onFloorModelsLoaded();
|
|
108
|
+
}
|
|
100
109
|
});
|
|
101
110
|
};
|
|
102
111
|
|
|
@@ -268,10 +277,11 @@ SceneCore.prototype.loadMap = function(OBJLoader, MTLLoader) {
|
|
|
268
277
|
};
|
|
269
278
|
|
|
270
279
|
/**
|
|
271
|
-
*
|
|
280
|
+
* 多楼层:依次加载多个模型(两阶段:先加载添加,再统一算中心对齐)
|
|
272
281
|
* @private
|
|
282
|
+
* @param {Function} onAllLoaded - 所有楼层模型加载完毕后的回调(此时 floorGroups 已就绪)
|
|
273
283
|
*/
|
|
274
|
-
SceneCore.prototype._loadMultiFloorMaps = function(OBJLoader, MTLLoader, objUrls) {
|
|
284
|
+
SceneCore.prototype._loadMultiFloorMaps = function(OBJLoader, MTLLoader, objUrls, onAllLoaded) {
|
|
275
285
|
var self = this;
|
|
276
286
|
var floorIndex = 0;
|
|
277
287
|
|
|
@@ -280,8 +290,16 @@ SceneCore.prototype._loadMultiFloorMaps = function(OBJLoader, MTLLoader, objUrls
|
|
|
280
290
|
return self._loadSingleMapWithFloorIndex(OBJLoader, MTLLoader, url, floorIndex++);
|
|
281
291
|
});
|
|
282
292
|
}, Promise.resolve()).then(function() {
|
|
293
|
+
// 阶段二:所有楼层加载完毕后,按联合包围盒中心对齐 XZ
|
|
283
294
|
if (self.floorGroups.length > 1) {
|
|
284
|
-
self.
|
|
295
|
+
self._alignAllFloorsToCenter();
|
|
296
|
+
}
|
|
297
|
+
// 通知 SDK 层:楼层模型已就绪,可以加载各层 kidata 了
|
|
298
|
+
if (typeof onAllLoaded === 'function') {
|
|
299
|
+
onAllLoaded();
|
|
300
|
+
}
|
|
301
|
+
if (typeof self.onFloorModelsLoaded === 'function') {
|
|
302
|
+
self.onFloorModelsLoaded();
|
|
285
303
|
}
|
|
286
304
|
});
|
|
287
305
|
};
|
|
@@ -373,15 +391,6 @@ SceneCore.prototype._loadKimapFileToGroup = function(kimapUrl, themeUrl, objLoad
|
|
|
373
391
|
var object = objLoader.parse(objContent);
|
|
374
392
|
self._processLoadedModelToGroup(object, materials, colorMap, floorGroup, floorIndex);
|
|
375
393
|
|
|
376
|
-
// 提取文字元素(每个楼层都提取,以第一个楼层为准)
|
|
377
|
-
if (!self._textElements || self._textElements.length === 0) {
|
|
378
|
-
var textElements = extractTextElementsFromOBJ(objContent);
|
|
379
|
-
if (textElements && textElements.length > 0) {
|
|
380
|
-
self._textElements = textElements;
|
|
381
|
-
self.renderTextElements(self._textElements);
|
|
382
|
-
}
|
|
383
|
-
}
|
|
384
|
-
|
|
385
394
|
resolve();
|
|
386
395
|
} catch (error) {
|
|
387
396
|
reject(error);
|
|
@@ -418,26 +427,36 @@ SceneCore.prototype._processLoadedModelToGroup = function(object, materials, col
|
|
|
418
427
|
var self = this;
|
|
419
428
|
floorGroup.add(object);
|
|
420
429
|
|
|
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
|
+
|
|
421
437
|
var box = new THREE.Box3().setFromObject(object);
|
|
422
438
|
var boxHeight = box.max.y - box.min.y;
|
|
423
439
|
|
|
440
|
+
// 记录每层的包围盒(供两阶段定位使用)
|
|
441
|
+
if (!self._floorBoxes) self._floorBoxes = [];
|
|
442
|
+
self._floorBoxes[floorIndex] = box;
|
|
443
|
+
|
|
424
444
|
// 逐层累积高度:计算该楼层在 ALL 模式下的世界 Y 偏移
|
|
425
445
|
var floorY = 0;
|
|
426
446
|
for (var i = 0; i < floorIndex; i++) {
|
|
427
|
-
var
|
|
428
|
-
if (
|
|
429
|
-
var prevBox = new THREE.Box3().setFromObject(prevGroup);
|
|
430
|
-
floorY += (prevBox.max.y - prevBox.min.y);
|
|
431
|
-
}
|
|
447
|
+
var prevBox = self._floorBoxes[i];
|
|
448
|
+
if (prevBox) floorY += (prevBox.max.y - prevBox.min.y);
|
|
432
449
|
}
|
|
433
450
|
|
|
434
|
-
//
|
|
451
|
+
// object.position.y = -box.min.y:对象底部(box.min.y)对齐到本地 Y=0
|
|
452
|
+
// 单层模式 floorGroup.position.y=0 → 对象底部世界 Y=0(贴地)
|
|
453
|
+
// ALL 模式 floorGroup.position.y=floorY → 对象底部世界 Y=floorY(堆叠)
|
|
435
454
|
object.position.set(-box.min.x, -box.min.y, -box.min.z);
|
|
436
455
|
|
|
437
|
-
//
|
|
438
|
-
floorGroup.position.
|
|
456
|
+
// 初始 Y 偏移(XZ 后续由 _alignAllFloorsToCenter 统一调整)
|
|
457
|
+
floorGroup.position.set(0, floorY, 0);
|
|
439
458
|
|
|
440
|
-
//
|
|
459
|
+
// 记录原始 Y 偏移(showSingleFloor/showAllFloors 使用)
|
|
441
460
|
self.floorOriginalY[floorIndex] = floorY;
|
|
442
461
|
|
|
443
462
|
// 复用 backup 的材质处理策略,不改渲染风格
|
|
@@ -452,6 +471,34 @@ SceneCore.prototype._processLoadedModelToGroup = function(object, materials, col
|
|
|
452
471
|
});
|
|
453
472
|
};
|
|
454
473
|
|
|
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
|
+
|
|
455
502
|
/**
|
|
456
503
|
* 多楼层:相机看全楼层
|
|
457
504
|
* @private
|