받침 중심 정렬 + 부재별 색상 구분
bearing.rs: X 중심, Y 하방향 (-h to 0) 기하학 수정 bridge_scene.rs: 받침 X 오프셋 제거 (girder 정렬) Mesh: colors 필드 추가 + recolor() 메서드 sweep.rs / occt.rs: 기본 콘크리트 색 자동 채움 bridge_scene: 부재별 색상 (거더/슬래브/받침/교대) shader.wgsl: base_color 입력 → 조명 계산에 적용 선택(Selection) 기능은 계획대로 별도 Sprint에 구현. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -7,12 +7,18 @@ pub fn build_bearing_mesh(ir: &BearingIR) -> Result<Mesh, KernelError> {
|
||||
if ir.plan_length <= 0.0 || ir.plan_width <= 0.0 || ir.total_height <= 0.0 {
|
||||
return Err(KernelError::InvalidInput("bearing dimensions must be positive".into()));
|
||||
}
|
||||
// Centred box: plan_length × total_height × plan_width
|
||||
// X = along span (plan_length), Y = height, Z = transverse (plan_width)
|
||||
// Box: X=[-l/2, +l/2] centred, Y=[-h, 0] (top at Y=0 = girder soffit), Z=[0, w]
|
||||
// Caller translates to girder X offset; Y=0 means bearing top sits at soffit.
|
||||
let l = ir.plan_length as f32;
|
||||
let h = ir.total_height as f32;
|
||||
let w = ir.plan_width as f32;
|
||||
Ok(sweep::centred_box(-l/2.0, 0.0, l/2.0, h, w))
|
||||
let profile = vec![
|
||||
[-l * 0.5, -h ],
|
||||
[ l * 0.5, -h ],
|
||||
[ l * 0.5, 0.0],
|
||||
[-l * 0.5, 0.0],
|
||||
];
|
||||
Ok(sweep::sweep_profile_flat(&profile, w))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
@@ -39,6 +39,19 @@ pub struct Mesh {
|
||||
pub indices: Vec<u32>,
|
||||
/// Per-vertex normals (unit vectors).
|
||||
pub normals: Vec<[f32; 3]>,
|
||||
/// Per-vertex base colour [R, G, B] in linear sRGB.
|
||||
/// Default: concrete grey. Set by scene compositor per feature type.
|
||||
pub colors: Vec<[f32; 3]>,
|
||||
}
|
||||
|
||||
/// Default concrete grey (PSC-I standard colour).
|
||||
pub const COLOR_CONCRETE: [f32; 3] = [0.80, 0.76, 0.65];
|
||||
|
||||
impl Mesh {
|
||||
/// Repaint all vertices with a single colour.
|
||||
pub fn recolor(&mut self, c: [f32; 3]) {
|
||||
self.colors = vec![c; self.vertices.len()];
|
||||
}
|
||||
}
|
||||
|
||||
impl Mesh {
|
||||
@@ -127,7 +140,8 @@ impl GeomKernel for StubKernel {
|
||||
0, 1, 5, 0, 5, 4, 3, 7, 6, 3, 6, 2,
|
||||
];
|
||||
let normals = vec![[0.0_f32, 1.0, 0.0]; vertices.len()];
|
||||
Ok(Mesh { vertices, indices, normals })
|
||||
let colors = vec![COLOR_CONCRETE; vertices.len()];
|
||||
Ok(Mesh { vertices, indices, normals, colors })
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -141,7 +141,8 @@ mod inner {
|
||||
.map(|&i| i as u32)
|
||||
.collect();
|
||||
|
||||
Ok(Mesh { vertices, normals, indices })
|
||||
let colors = vec![crate::COLOR_CONCRETE; vertices.len()];
|
||||
Ok(Mesh { vertices, normals, indices, colors })
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -66,7 +66,8 @@ pub fn sweep_profile_flat(profile: &[[f32; 2]], span: f32) -> Mesh {
|
||||
push_tri(cen_b, [profile[i][0],profile[i][1],span], [profile[j][0],profile[j][1],span]);
|
||||
}
|
||||
|
||||
Mesh { vertices, normals, indices }
|
||||
let colors = vec![crate::COLOR_CONCRETE; vertices.len()];
|
||||
Mesh { vertices, normals, indices, colors }
|
||||
}
|
||||
|
||||
// ─── Convenience shapes ───────────────────────────────────────────────────────
|
||||
@@ -115,11 +116,13 @@ pub fn merge_meshes(meshes: Vec<Mesh>) -> Mesh {
|
||||
let mut vertices: Vec<[f32; 3]> = Vec::new();
|
||||
let mut normals: Vec<[f32; 3]> = Vec::new();
|
||||
let mut indices: Vec<u32> = Vec::new();
|
||||
let mut colors: Vec<[f32; 3]> = Vec::new();
|
||||
for m in meshes {
|
||||
let base = vertices.len() as u32;
|
||||
vertices.extend_from_slice(&m.vertices);
|
||||
normals.extend_from_slice(&m.normals);
|
||||
colors.extend_from_slice(&m.colors);
|
||||
indices.extend(m.indices.iter().map(|i| i + base));
|
||||
}
|
||||
Mesh { vertices, normals, indices }
|
||||
Mesh { vertices, normals, indices, colors }
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user