collada_scene: add mutable node transforms

This commit is contained in:
Zack Buhman 2026-01-26 23:49:13 -06:00
parent b052718d7e
commit ff5d2da6dc
7 changed files with 250 additions and 122 deletions

View File

@ -285,7 +285,7 @@ def get_node_name_id(node):
return name return name
def render_node_children(state, collada, node_name, nodes): def render_node_children(state, collada, node_name, nodes):
yield f"node const * const node_children_{node_name} = {{" yield f"node const * const node_children_{node_name}[] = {{"
for node in nodes: for node in nodes:
node_name_id = get_node_name_id(node) node_name_id = get_node_name_id(node)
node_name = sanitize_name(state, node_name_id, node) node_name = sanitize_name(state, node_name_id, node)

View File

@ -1,25 +1,65 @@
#pragma once #pragma once
#include "collada_types.hpp" #include "collada_types.hpp"
#include "directxmath/directxmath.h"
namespace collada_scene { namespace collada_scene {
struct scene_state { struct lookat {
const UINT numBuffers = 1; // FIXME XMVECTOR eye;
const UINT strides[1] = {3 * 3 * 4}; // FIXME XMVECTOR at;
const UINT offsets[1] = {0}; // FIXME XMVECTOR up;
ID3D10Buffer * pVertexBuffers[1] = {}; // FIXME
ID3D10Buffer * pIndexBuffer = NULL;
ID3D10InputLayout ** pVertexLayouts = NULL;
HRESULT load_vertex_buffer(LPCWSTR name);
HRESULT load_index_buffer(LPCWSTR name);
HRESULT load_layout(int inputs_index, collada::inputs const& inputs);
HRESULT load_layouts(collada::descriptor const& descriptor);
}; };
HRESULT LoadScene(collada::descriptor const& descriptor, scene_state& state); struct transform {
void Render(collada::descriptor const& descriptor, scene_state const& state); collada::transform_type type;
union {
lookat lookat;
XMMATRIX matrix;
XMVECTOR rotate;
XMVECTOR scale;
XMVECTOR translate;
};
};
struct node_instance {
transform * transforms = NULL;
};
struct scene_state {
const UINT m_numBuffers = 1; // FIXME
const UINT m_strides[1] = {3 * 3 * 4}; // FIXME
const UINT m_offsets[1] = {0}; // FIXME
ID3D10Buffer * m_pVertexBuffers[1] = {}; // FIXME
ID3D10Buffer * m_pIndexBuffer = NULL;
ID3D10InputLayout ** m_pVertexLayouts = NULL;
node_instance * m_nodeInstances = NULL;
collada::descriptor const * m_descriptor;
HRESULT load_scene(collada::descriptor const * const descriptor);
void render();
typedef void (* const apply_node_func_t)(collada::node const * const node,
node_instance * node_instance);
private:
HRESULT load_layouts();
void allocate_node_instances();
node_instance * apply_node_instances1(collada::node const * const node,
node_instance * node_instance,
apply_node_func_t func);
void apply_node_instances(apply_node_func_t func);
void render_geometries(collada::instance_geometry const * const instance_geometries,
int const instance_geometries_count);
};
HRESULT LoadEffect();
HRESULT LoadLayout(collada::inputs const& inputs, ID3D10InputLayout ** ppVertexLayout);
HRESULT LoadVertexBuffer(LPCWSTR name, ID3D10Buffer ** ppVertexBuffer);
HRESULT LoadIndexBuffer(LPCWSTR name, ID3D10Buffer ** ppIndexBuffer);
} }

View File

@ -102,6 +102,7 @@ namespace collada {
MATRIX, MATRIX,
ROTATE, ROTATE,
SCALE, SCALE,
SKEW,
TRANSLATE, TRANSLATE,
}; };
@ -287,11 +288,12 @@ namespace collada {
channel const * const * const channels; channel const * const * const channels;
int const channels_count; int const channels_count;
node const * const nodes; node const * const * const nodes;
int const nodes_count; int const nodes_count;
}; };
struct descriptor { struct descriptor {
// these are only the top-level nodes; nodes may have children
node const * const * const nodes; node const * const * const nodes;
int const nodes_count; int const nodes_count;

9
include/new.hpp Normal file
View File

@ -0,0 +1,9 @@
#pragma once
#include <stdlib.h>
template <typename T>
T * New(int elements)
{
return (T *)malloc((sizeof (T)) * elements);
}

View File

@ -510,7 +510,7 @@ geometry const * const geometries[] = {
&geometry_geom_plane001, &geometry_geom_plane001,
}; };
node const * const node_children_node_environmentambientlight = { node const * const node_children_node_environmentambientlight[] = {
}; };
transform const transforms_node_environmentambientlight[] = { transform const transforms_node_environmentambientlight[] = {
@ -537,7 +537,7 @@ node const node_node_environmentambientlight = {
.nodes_count = 0, .nodes_count = 0,
}; };
node const * const node_children_node_cube = { node const * const node_children_node_cube[] = {
}; };
transform const transforms_node_cube[] = { transform const transforms_node_cube[] = {
@ -603,7 +603,7 @@ node const node_node_cube = {
.nodes_count = 0, .nodes_count = 0,
}; };
node const * const node_children_node_cylinder001 = { node const * const node_children_node_cylinder001[] = {
}; };
transform const transforms_node_cylinder001[] = { transform const transforms_node_cylinder001[] = {
@ -643,7 +643,7 @@ node const node_node_cylinder001 = {
.nodes_count = 0, .nodes_count = 0,
}; };
node const * const node_children_node_plane001 = { node const * const node_children_node_plane001[] = {
}; };
transform const transforms_node_plane001[] = { transform const transforms_node_plane001[] = {

View File

@ -6,6 +6,7 @@
#include "print.hpp" #include "print.hpp"
#include "collada_scene.hpp" #include "collada_scene.hpp"
#include "new.hpp"
extern ID3D10Device * g_pd3dDevice; extern ID3D10Device * g_pd3dDevice;
extern XMMATRIX g_View; extern XMMATRIX g_View;
@ -51,65 +52,126 @@ namespace collada_scene {
} }
} }
HRESULT scene_state::load_vertex_buffer(LPCWSTR name) HRESULT scene_state::load_layouts()
{ {
HRESULT hr; HRESULT hr;
HRSRC hRes = FindResource(NULL, name, RT_RCDATA);
if (hRes == NULL) {
printW(L"FindResource %s\n", name);
return E_FAIL;
}
DWORD dwResSize = SizeofResource(NULL, hRes);
void * pData = LockResource(LoadResource(NULL, hRes));
D3D10_BUFFER_DESC bd; m_pVertexLayouts = New<ID3D10InputLayout *>(m_descriptor->inputs_list_count);
D3D10_SUBRESOURCE_DATA initData;
bd.Usage = D3D10_USAGE_IMMUTABLE; for (int i = 0; i < m_descriptor->inputs_list_count; i++) {
bd.ByteWidth = dwResSize; hr = LoadLayout(m_descriptor->inputs_list[i], &m_pVertexLayouts[i]);
bd.BindFlags = D3D10_BIND_VERTEX_BUFFER; if (FAILED(hr))
bd.CPUAccessFlags = 0;
bd.MiscFlags = 0;
initData.pSysMem = pData;
hr = g_pd3dDevice->CreateBuffer(&bd, &initData, &pVertexBuffers[0]);
if (FAILED(hr)) {
print("CreateBuffer: D3D10_BIND_VERTEX_BUFFER\n");
return hr; return hr;
} }
return S_OK; return S_OK;
} }
HRESULT scene_state::load_index_buffer(LPCWSTR name) static int count_nodes(node const * const node)
{
int count = 1;
for (int i = 0; i < node->nodes_count; i++) {
count += count_nodes(&node->nodes[i]);
}
return count;
}
static void initialize_node_transforms(node const * const node,
node_instance * node_instance)
{
for (int i = 0; i < node->transforms_count; i++) {
transform& transform = node_instance->transforms[i];
transform.type = node->transforms[i].type;
switch (transform.type) {
case transform_type::LOOKAT:
transform.lookat.eye = XMLoadFloat3((XMFLOAT3 *)&node->transforms[i].lookat.eye);
transform.lookat.at = XMLoadFloat3((XMFLOAT3 *)&node->transforms[i].lookat.at);
transform.lookat.up = XMLoadFloat3((XMFLOAT3 *)&node->transforms[i].lookat.up);
break;
case transform_type::MATRIX:
transform.matrix = XMLoadFloat4x4((XMFLOAT4X4 *)&node->transforms[i].matrix);
break;
case transform_type::ROTATE:
transform.rotate = XMLoadFloat4((XMFLOAT4 *)&node->transforms[i].rotate);
break;
case transform_type::SCALE:
transform.scale = XMLoadFloat3((XMFLOAT3 *)&node->transforms[i].scale);
break;
case transform_type::TRANSLATE:
transform.translate = XMLoadFloat3((XMFLOAT3 *)&node->transforms[i].translate);
break;
default:
assert(false);
}
}
}
static void allocate_node_instance(node const * const node,
node_instance * node_instance)
{
node_instance->transforms = New<transform>(node->transforms_count);
initialize_node_transforms(node, node_instance);
}
node_instance * scene_state::apply_node_instances1(node const * const node,
node_instance * node_instance,
apply_node_func_t func)
{
func(node, node_instance);
node_instance = &node_instance[1];
for (int i = 0; i < node->nodes_count; i++) {
node_instance = apply_node_instances1(&node->nodes[i], node_instance, func);
}
return node_instance;
}
void scene_state::apply_node_instances(apply_node_func_t func)
{
node_instance * node_instance = m_nodeInstances;
for (int i = 0; i < m_descriptor->nodes_count; i++) {
node_instance = apply_node_instances1(m_descriptor->nodes[i], node_instance, func);
}
}
void scene_state::allocate_node_instances()
{
int count = 0;
for (int i = 0; i < m_descriptor->nodes_count; i++) {
count += count_nodes(m_descriptor->nodes[i]);
}
m_nodeInstances = New<node_instance>(count);
apply_node_instances(allocate_node_instance);
}
HRESULT scene_state::load_scene(collada::descriptor const * const descriptor)
{ {
HRESULT hr; HRESULT hr;
HRSRC hRes = FindResource(NULL, name, RT_RCDATA);
if (hRes == NULL) { m_descriptor = descriptor;
printW(L"FindResource %s\n", name);
// layouts
hr = load_layouts();
if (FAILED(hr))
return E_FAIL; return E_FAIL;
}
DWORD dwResSize = SizeofResource(NULL, hRes);
void * pData = LockResource(LoadResource(NULL, hRes));
D3D10_BUFFER_DESC bd; hr = LoadVertexBuffer(L"RES_MODELS_CURVE_INTERPOLATION_VTX", &m_pVertexBuffers[0]);
D3D10_SUBRESOURCE_DATA initData; if (FAILED(hr))
return E_FAIL;
bd.Usage = D3D10_USAGE_IMMUTABLE; hr = LoadIndexBuffer(L"RES_MODELS_CURVE_INTERPOLATION_IDX", &m_pIndexBuffer);
bd.ByteWidth = dwResSize; if (FAILED(hr))
bd.BindFlags = D3D10_BIND_INDEX_BUFFER; return E_FAIL;
bd.CPUAccessFlags = 0;
bd.MiscFlags = 0; allocate_node_instances();
initData.pSysMem = pData;
hr = g_pd3dDevice->CreateBuffer(&bd, &initData, &pIndexBuffer);
if (FAILED(hr)) {
print("CreateBuffer: D3D10_BIND_VERTEX_BUFFER\n");
return hr;
}
return S_OK; return S_OK;
} }
HRESULT scene_state::load_layout(int inputs_index, inputs const& inputs) HRESULT LoadLayout(inputs const& inputs, ID3D10InputLayout ** ppVertexLayout)
{ {
HRESULT hr; HRESULT hr;
@ -134,7 +196,7 @@ namespace collada_scene {
hr = g_pd3dDevice->CreateInputLayout(layout, inputs.elements_count, hr = g_pd3dDevice->CreateInputLayout(layout, inputs.elements_count,
passDesc.pIAInputSignature, passDesc.pIAInputSignature,
passDesc.IAInputSignatureSize, passDesc.IAInputSignatureSize,
&pVertexLayouts[inputs_index]); ppVertexLayout);
if (FAILED(hr)) { if (FAILED(hr)) {
print("CreateInputLayout\n"); print("CreateInputLayout\n");
return hr; return hr;
@ -143,28 +205,6 @@ namespace collada_scene {
return S_OK; return S_OK;
} }
template <typename T>
T New(int elements)
{
return (T)malloc((sizeof (T)) * elements);
}
HRESULT scene_state::load_layouts(descriptor const& descriptor)
{
HRESULT hr;
//this->pVertexLayouts = new ID3D10InputLayout *[descriptor.inputs_list_count];
this->pVertexLayouts = New<ID3D10InputLayout **>(descriptor.inputs_list_count);
for (int i = 0; i < descriptor.inputs_list_count; i++) {
hr = load_layout(i, descriptor.inputs_list[i]);
if (FAILED(hr))
return hr;
}
return S_OK;
}
HRESULT LoadEffect() HRESULT LoadEffect()
{ {
HRESULT hr; HRESULT hr;
@ -203,41 +243,74 @@ namespace collada_scene {
return S_OK; return S_OK;
} }
HRESULT LoadScene(descriptor const& descriptor, scene_state& state) HRESULT LoadVertexBuffer(LPCWSTR name, ID3D10Buffer ** ppVertexBuffer)
{ {
HRESULT hr; HRESULT hr;
HRSRC hRes = FindResource(NULL, name, RT_RCDATA);
// effects/techniques if (hRes == NULL) {
hr = LoadEffect(); printW(L"FindResource %s\n", name);
if (FAILED(hr))
return E_FAIL; return E_FAIL;
}
DWORD dwResSize = SizeofResource(NULL, hRes);
void * pData = LockResource(LoadResource(NULL, hRes));
// layouts D3D10_BUFFER_DESC bd;
hr = state.load_layouts(descriptor); D3D10_SUBRESOURCE_DATA initData;
if (FAILED(hr))
return E_FAIL;
hr = state.load_vertex_buffer(L"RES_MODELS_CURVE_INTERPOLATION_VTX"); bd.Usage = D3D10_USAGE_IMMUTABLE;
if (FAILED(hr)) bd.ByteWidth = dwResSize;
return E_FAIL; bd.BindFlags = D3D10_BIND_VERTEX_BUFFER;
bd.CPUAccessFlags = 0;
hr = state.load_index_buffer(L"RES_MODELS_CURVE_INTERPOLATION_IDX"); bd.MiscFlags = 0;
if (FAILED(hr)) initData.pSysMem = pData;
return E_FAIL; hr = g_pd3dDevice->CreateBuffer(&bd, &initData, ppVertexBuffer);
if (FAILED(hr)) {
print("CreateBuffer: D3D10_BIND_VERTEX_BUFFER\n");
return hr;
}
return S_OK; return S_OK;
} }
static inline XMMATRIX transform_matrix(transform const& transform) HRESULT LoadIndexBuffer(LPCWSTR name, ID3D10Buffer ** ppIndexBuffer)
{
HRESULT hr;
HRSRC hRes = FindResource(NULL, name, RT_RCDATA);
if (hRes == NULL) {
printW(L"FindResource %s\n", name);
return E_FAIL;
}
DWORD dwResSize = SizeofResource(NULL, hRes);
void * pData = LockResource(LoadResource(NULL, hRes));
D3D10_BUFFER_DESC bd;
D3D10_SUBRESOURCE_DATA initData;
bd.Usage = D3D10_USAGE_IMMUTABLE;
bd.ByteWidth = dwResSize;
bd.BindFlags = D3D10_BIND_INDEX_BUFFER;
bd.CPUAccessFlags = 0;
bd.MiscFlags = 0;
initData.pSysMem = pData;
hr = g_pd3dDevice->CreateBuffer(&bd, &initData, ppIndexBuffer);
if (FAILED(hr)) {
print("CreateBuffer: D3D10_BIND_VERTEX_BUFFER\n");
return hr;
}
return S_OK;
}
static inline XMMATRIX TransformMatrix(transform const& transform)
{ {
switch (transform.type) { switch (transform.type) {
case transform_type::TRANSLATE: case transform_type::TRANSLATE:
return XMMatrixTranslationFromVector(XMLoadFloat3((XMFLOAT3 *)&transform.translate.x)); return XMMatrixTranslationFromVector(transform.translate);
case transform_type::ROTATE: case transform_type::ROTATE:
return XMMatrixRotationNormal(XMLoadFloat3((XMFLOAT3 *)&transform.rotate.x), return XMMatrixRotationNormal(transform.rotate,
XMConvertToRadians(transform.rotate.w)); XMConvertToRadians(XMVectorGetW(transform.rotate)));
case transform_type::SCALE: case transform_type::SCALE:
return XMMatrixScalingFromVector(XMLoadFloat3((XMFLOAT3 *)&transform.scale.x)); return XMMatrixScalingFromVector(transform.scale);
default: default:
assert(false); assert(false);
break; break;
@ -274,8 +347,7 @@ namespace collada_scene {
} }
} }
void RenderGeometries(scene_state const& state, void scene_state::render_geometries(instance_geometry const * const instance_geometries,
instance_geometry const * const instance_geometries,
int const instance_geometries_count) int const instance_geometries_count)
{ {
for (int i = 0; i < instance_geometries_count; i++) { for (int i = 0; i < instance_geometries_count; i++) {
@ -284,8 +356,8 @@ namespace collada_scene {
UINT strides[1] = { 3 * 3 * 4 }; UINT strides[1] = { 3 * 3 * 4 };
UINT offsets[1] = { (UINT)mesh.vertex_buffer_offset }; UINT offsets[1] = { (UINT)mesh.vertex_buffer_offset };
g_pd3dDevice->IASetVertexBuffers(0, state.numBuffers, state.pVertexBuffers, strides, offsets); g_pd3dDevice->IASetVertexBuffers(0, m_numBuffers, m_pVertexBuffers, strides, offsets);
g_pd3dDevice->IASetIndexBuffer(state.pIndexBuffer, DXGI_FORMAT_R32_UINT, mesh.index_buffer_offset); g_pd3dDevice->IASetIndexBuffer(m_pIndexBuffer, DXGI_FORMAT_R32_UINT, mesh.index_buffer_offset);
D3D10_TECHNIQUE_DESC techDesc; D3D10_TECHNIQUE_DESC techDesc;
g_pTechniqueBlinn->GetDesc(&techDesc); g_pTechniqueBlinn->GetDesc(&techDesc);
@ -296,33 +368,34 @@ namespace collada_scene {
SetMaterial(*instance_material.material->effect); SetMaterial(*instance_material.material->effect);
g_pTechniqueBlinn->GetPassByIndex(0)->Apply(0); g_pTechniqueBlinn->GetPassByIndex(0)->Apply(0);
g_pd3dDevice->IASetInputLayout(state.pVertexLayouts[triangles.inputs_index]); g_pd3dDevice->IASetInputLayout(m_pVertexLayouts[triangles.inputs_index]);
g_pd3dDevice->DrawIndexed(triangles.count * 3, triangles.index_offset, 0); g_pd3dDevice->DrawIndexed(triangles.count * 3, triangles.index_offset, 0);
} }
} }
} }
void Render(descriptor const& descriptor, scene_state const& state) void scene_state::render()
{ {
g_pViewVariable->SetMatrix((float *)&g_View); g_pViewVariable->SetMatrix((float *)&g_View);
g_pProjectionVariable->SetMatrix((float *)&g_Projection); g_pProjectionVariable->SetMatrix((float *)&g_Projection);
g_pd3dDevice->IASetPrimitiveTopology(D3D10_PRIMITIVE_TOPOLOGY_TRIANGLELIST); g_pd3dDevice->IASetPrimitiveTopology(D3D10_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
for (int i = 0; i < descriptor.nodes_count; i++) { for (int i = 0; i < m_descriptor->nodes_count; i++) {
node const& node = *descriptor.nodes[i]; node const& node = *m_descriptor->nodes[i];
assert(node.nodes_count == 0);
if (node.type != node_type::NODE) if (node.type != node_type::NODE)
continue; continue;
node_instance const& node_instance = m_nodeInstances[i];
XMMATRIX World = XMMatrixIdentity(); XMMATRIX World = XMMatrixIdentity();
// build the world transform // build the world transform
for (int j = 0; j < node.transforms_count; j++) { for (int j = 0; j < node.transforms_count; j++) {
transform const& transform = node.transforms[j]; World = TransformMatrix(node_instance.transforms[j]) * World;
World = transform_matrix(transform) * World;
} }
g_pWorldVariable->SetMatrix((float *)&World); g_pWorldVariable->SetMatrix((float *)&World);
RenderGeometries(state, node.instance_geometries, node.instance_geometries_count); render_geometries(node.instance_geometries, node.instance_geometries_count);
} }
} }
} }

View File

@ -204,8 +204,12 @@ int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLi
return 0; return 0;
} }
if (FAILED(collada_scene::LoadScene(curve_interpolation::descriptor, g_SceneState))) { if (FAILED(collada_scene::LoadEffect())) {
print("collada::LoadScene\n"); print("collada_scene::LoadEffect\n");
}
if (FAILED(g_SceneState.load_scene(&curve_interpolation::descriptor))) {
print("g_SceneState::load_scene\n");
return 0; return 0;
} }
@ -1839,7 +1843,7 @@ void Render(float t, float dt)
//collada::Render(t); //collada::Render(t);
collada_scene::Render(curve_interpolation::descriptor, g_SceneState); g_SceneState.render();
RenderFont(dt); RenderFont(dt);