use concinnity_core::bake;
use concinnity_core::gfx::mesh_payload::Vertex;
use concinnity_core::gfx::render_types::{AreaLightData, FALLBACK_TEXTURE_COUNT};
use concinnity_core::render::backend_init::{MediaPayloads, SceneData};
use concinnity_core::render::error::{RenderError, RenderResult};
use concinnity_core::render::ltc;
use windows::Win32::Graphics::Direct3D12::*;
use super::InitGpu;
use crate::directx::allocator::PooledTexture;
use crate::directx::com;
use crate::directx::context::{
AreaLightState, DxDescriptors, DxGeometry, DxSceneAssets, FRAMES, align256,
};
use crate::directx::draw::upload_static_records;
use crate::directx::texture::*;
pub(super) fn build_scene_assets(
gpu: &InitGpu<'_>,
descriptors: &DxDescriptors,
media: &MediaPayloads<'_>,
area_lights: &[AreaLightData],
world: &SceneData<'_>,
) -> RenderResult<DxSceneAssets> {
let hw = gpu.hw;
let layout = &descriptors.layout;
let area_light = build_area_light(gpu, descriptors, area_lights)?;
let env_map = if let Some(bytes) = media.env_map_bytes {
let view = bake::environment_map::deserialize(bytes)
.map_err(|e| RenderError::Other(format!("EnvironmentMap payload malformed: {e}")))?;
upload_environment_map(
&hw.alloc,
crate::directx::texture::EnvironmentMapPayload {
irradiance_face: view.irradiance_face,
irradiance_bytes: view.irradiance_bytes,
prefilter_face: view.prefilter_face,
mip_bytes: &view.prefilter_mip_bytes,
},
crate::directx::texture::EnvironmentMapDescriptors {
irr_srv_cpu: descriptors.slot_cpu(1),
irr_srv_gpu: descriptors.slot_gpu(1),
pre_srv_cpu: descriptors.slot_cpu(2),
pre_srv_gpu: descriptors.slot_gpu(2),
},
)?
} else {
let irradiance = create_fallback_cubemap(
&hw.alloc,
[0.05, 0.05, 0.05, 1.0],
descriptors.slot_cpu(1),
descriptors.slot_gpu(1),
)?;
let prefilter = create_fallback_cubemap(
&hw.alloc,
[0.05, 0.05, 0.05, 1.0],
descriptors.slot_cpu(2),
descriptors.slot_gpu(2),
)?;
EnvironmentMapTextures {
irradiance,
prefilter,
prefilter_mip_count: 0,
}
};
let gpu_textures: Vec<PooledTexture> = if media.textures.is_empty() {
vec![create_fallback_white_resource(&hw.alloc)?]
} else {
media
.textures
.iter()
.enumerate()
.map(|(i, image)| {
upload_texture_image(&hw.alloc, image)
.map_err(|e| e.context(format!("texture[{i}]")))
})
.collect::<Result<Vec<_>, _>>()?
};
let gpu_fallbacks: Vec<PooledTexture> = vec![
create_fallback_flat_normal_resource(&hw.alloc)?,
create_fallback_white_resource(&hw.alloc)?,
];
let flat_albedo_count = gpu_textures.len();
debug_assert_eq!(gpu_fallbacks.len(), FALLBACK_TEXTURE_COUNT);
debug_assert_eq!(
flat_albedo_count + gpu_fallbacks.len(),
descriptors.flat_pool_len
);
for f in 0..FRAMES {
let copy_base = layout.flat_pool_base_slot + f * descriptors.flat_pool_len;
for (k, tex) in gpu_textures.iter().enumerate() {
write_texture_srv(&hw.device, tex, descriptors.slot_cpu(copy_base + k));
}
for (k, tex) in gpu_fallbacks.iter().enumerate() {
write_texture_srv(
&hw.device,
tex,
descriptors.slot_cpu(copy_base + flat_albedo_count + k),
);
}
}
let color_lut = if let Some(bytes) = media.color_lut_bytes {
let (size, data) = bake::color_lut::deserialize(bytes)
.map_err(|e| RenderError::Other(format!("ColorLut payload malformed: {e}")))?;
upload_color_lut(
&hw.alloc,
size,
data,
descriptors.slot_cpu(layout.lut_srv_slot),
descriptors.slot_gpu(layout.lut_srv_slot),
)?
} else {
create_fallback_color_lut(
&hw.alloc,
descriptors.slot_cpu(layout.lut_srv_slot),
descriptors.slot_gpu(layout.lut_srv_slot),
)?
};
let geometry = build_geometry(gpu, world)?;
Ok(DxSceneAssets {
env_map,
color_lut,
area_light,
geometry,
textures: gpu_textures,
_fallback_textures: gpu_fallbacks,
})
}
fn build_area_light(
gpu: &InitGpu<'_>,
descriptors: &DxDescriptors,
area_lights: &[AreaLightData],
) -> RenderResult<AreaLightState> {
let hw = gpu.hw;
let ltc_srv_base_slot = descriptors.layout.ltc_srv_base_slot;
let area_light_data = if area_lights.is_empty() {
vec![AreaLightData::ZERO]
} else {
area_lights.to_vec()
};
let area_light_buffer = {
let size = align256((area_light_data.len() * size_of::<AreaLightData>()) as u64);
let buf = hw.alloc.alloc_buffer(
size,
D3D12_HEAP_TYPE_UPLOAD,
D3D12_RESOURCE_STATE_GENERIC_READ,
)?;
upload_static_records(&buf, &area_light_data, "area-light")?;
buf
};
let ltc_size = ltc::LTC_LUT_SIZE as u32;
let ltc_matrix_texture = upload_float_lut(
&hw.alloc,
ltc_size,
4,
ltc::matrix_texels(),
descriptors.slot_cpu(ltc_srv_base_slot),
descriptors.slot_gpu(ltc_srv_base_slot),
)?;
let ltc_magnitude_texture = upload_float_lut(
&hw.alloc,
ltc_size,
2,
ltc::magnitude_texels(),
descriptors.slot_cpu(ltc_srv_base_slot + 1),
descriptors.slot_gpu(ltc_srv_base_slot + 1),
)?;
Ok(AreaLightState {
buffer: area_light_buffer,
ltc_matrix: ltc_matrix_texture,
ltc_magnitude: ltc_magnitude_texture,
ltc_table_gpu: descriptors.slot_gpu(ltc_srv_base_slot),
})
}
fn build_geometry(gpu: &InitGpu<'_>, world: &SceneData<'_>) -> RenderResult<DxGeometry> {
let hw = gpu.hw;
let vert_bytes_raw = bytemuck::cast_slice(world.vertices);
let idx_bytes_raw = bytemuck::cast_slice(world.indices);
let vertex_buffer = upload_buffer(
&hw.alloc,
vert_bytes_raw,
D3D12_RESOURCE_STATE_VERTEX_AND_CONSTANT_BUFFER,
)?;
let index_buffer = upload_buffer(&hw.alloc, idx_bytes_raw, D3D12_RESOURCE_STATE_INDEX_BUFFER)?;
let vertex_buffer_view = D3D12_VERTEX_BUFFER_VIEW {
BufferLocation: com::gpu_va(&vertex_buffer),
SizeInBytes: vert_bytes_raw.len().max(4) as u32,
StrideInBytes: std::mem::size_of::<Vertex>() as u32,
};
let index_buffer_view = D3D12_INDEX_BUFFER_VIEW {
BufferLocation: com::gpu_va(&index_buffer),
SizeInBytes: idx_bytes_raw.len().max(4) as u32,
Format: windows::Win32::Graphics::Dxgi::Common::DXGI_FORMAT_R32_UINT,
};
Ok(DxGeometry {
vertex_buffer,
index_buffer,
vertex_buffer_view,
index_buffer_view,
})
}