fix: v2.10 3D 室外场景窗户陷入墙体+平开扇出框双重修复
- 墙体与窗体 z 轴关系: 墙厚 3→2.4×型深, 外墙面退至窗框外表面之后(窗框外露 30% 型深), 消除整窗埋入墙厚 - 窗洞净空自适应外框面宽(fwMax+0.015), 替代固定 0.05 - 平开扇铰链偏移符号修复(+(cxr-hingeX)), 消除关闭态扇出框/开启态扇悬空 - 草地垫对齐新墙面, 网格降至墙脚 - 新增 verify-wall.mjs 真实浏览器三态截图验证
This commit is contained in:
@@ -321,7 +321,8 @@ function build(drawing: DrawingData) {
|
||||
const hingeLeft = Math.cos(odRad) < 0
|
||||
const hingeX = hingeLeft ? cxr - w / 2 : cxr + w / 2
|
||||
group.position.set(hingeX, cyr, 0)
|
||||
group.children.forEach(child => { child.position.x -= (cxr - hingeX) })
|
||||
// 子元素为面板中心局部坐标: 平移 +(cxr-hingeX) 使 t=0 世界位置不变, 仅将旋转支点移到铰链边
|
||||
group.children.forEach(child => { child.position.x += (cxr - hingeX) })
|
||||
const maxAng = (s.isDoor ? 75 : s.wtf ? 40 : 32) * Math.PI / 180
|
||||
const dirSign = s.openToward === 'inward' ? 1 : -1
|
||||
sashAnims.push({ group, kind: 'casement', baseX: hingeX, slideDir: 0,
|
||||
@@ -344,12 +345,17 @@ function build(drawing: DrawingData) {
|
||||
}
|
||||
|
||||
// ---- 环境场景(室内/室外/影棚)----
|
||||
let gridY = -gh * S / 2 - DEPTH * 2.2
|
||||
if (scene3d.value === 'outdoor') {
|
||||
// 室外: 建筑外墙围绕窗洞 + 草地
|
||||
const hw = gw * S / 2 + 0.05 // 窗洞半宽
|
||||
const hh = gh * S / 2 + 0.05 // 窗洞半高
|
||||
// 室外: 建筑外墙围绕窗洞 + 草地(墙体外墙面退至窗框外表面之后, 窗框外露30%型深, 避免整窗陷入墙体内)
|
||||
const fwMax = Math.max(0.018, ...drawing.sm.map(sh => fwOf(sh, S)))
|
||||
const gap = fwMax + 0.015 // 窗洞净空: 外框面宽 + 安装缝(固定值会遮挡宽面型材)
|
||||
const hw = gw * S / 2 + gap // 窗洞半宽
|
||||
const hh = gh * S / 2 + gap // 窗洞半高
|
||||
const side = 0.55 // 上下墙高度/左右墙宽度
|
||||
const wt = DEPTH * 3
|
||||
const wt = DEPTH * 2.4 // 墙厚(≈240 实墙比例)
|
||||
const wallFront = DEPTH * 0.2 // 外墙面 z
|
||||
const wallZ = wallFront - wt / 2
|
||||
const wallBoxes: number[][] = [
|
||||
[-hw - side, hh, side * 2 + hw * 2, side], // 上
|
||||
[-hw - side, -hh - side, side * 2 + hw * 2, side], // 下
|
||||
@@ -358,15 +364,16 @@ function build(drawing: DrawingData) {
|
||||
]
|
||||
for (const [px, py, pw, ph] of wallBoxes) {
|
||||
const m = new THREE.Mesh(new THREE.BoxGeometry(pw, ph, wt), wallMat)
|
||||
m.position.set(px + pw / 2, py + ph / 2, 0)
|
||||
m.position.set(px + pw / 2, py + ph / 2, wallZ)
|
||||
m.receiveShadow = true
|
||||
m.userData.isWall = true
|
||||
sceneRoot.add(m)
|
||||
}
|
||||
const ground = new THREE.Mesh(new THREE.BoxGeometry(3, 0.02, DEPTH * 8), groundMat)
|
||||
ground.position.set(0, -hh - side - 0.02, DEPTH * 2)
|
||||
ground.position.set(0, -hh - side - 0.02, wallFront + DEPTH * 4)
|
||||
ground.receiveShadow = true
|
||||
sceneRoot.add(ground)
|
||||
gridY = -hh - side - 0.008 // 网格降至墙脚, 避免穿墙
|
||||
} else if (scene3d.value === 'indoor') {
|
||||
// 室内: 窗后整墙 + 室内地板(木色)
|
||||
const wall = new THREE.Mesh(new THREE.BoxGeometry(gw * S + 1.4, gh * S + 1.2, 0.02), wallMat)
|
||||
@@ -392,7 +399,7 @@ function build(drawing: DrawingData) {
|
||||
|
||||
hasOpenable.value = sashAnims.length > 0
|
||||
const grid = new THREE.GridHelper(2.4, 12, 0xc8cdd2, 0xdfe3e8)
|
||||
grid.position.y = -gh * S / 2 - DEPTH * 2.2
|
||||
grid.position.y = gridY
|
||||
scene.add(grid)
|
||||
|
||||
// 阴影相机范围
|
||||
|
||||
Reference in New Issue
Block a user