#include #include #include #include "directxmath/directxmath.h" #include "print.hpp" #include "collada_scene.hpp" #include "new.hpp" extern ID3D10Device * g_pd3dDevice; extern XMVECTOR g_Eye; extern XMMATRIX g_View; extern XMMATRIX g_Projection; namespace collada_scene { using namespace collada; ID3D10Effect * g_pEffect = NULL; ID3D10EffectTechnique * g_pTechniqueBlinn = NULL; ID3D10EffectMatrixVariable * g_pWorldVariable = NULL; ID3D10EffectMatrixVariable * g_pViewVariable = NULL; ID3D10EffectMatrixVariable * g_pProjectionVariable = NULL; ID3D10EffectVectorVariable * g_pViewEyeVariable = NULL; ID3D10EffectVectorVariable * g_pLightPosVariable = NULL; ID3D10EffectVectorVariable * g_pLightDirVariable = NULL; ID3D10EffectVectorVariable * g_pLightColorVariable = NULL; ID3D10EffectVectorVariable * g_pEmissionVariable = NULL; ID3D10EffectVectorVariable * g_pAmbientVariable = NULL; ID3D10EffectVectorVariable * g_pDiffuseVariable = NULL; ID3D10EffectVectorVariable * g_pSpecularVariable = NULL; ID3D10EffectScalarVariable * g_pShininessVariable = NULL; static inline DXGI_FORMAT dxgi_format(input_format format) { switch (format) { case input_format::FLOAT1: return DXGI_FORMAT_R32_FLOAT; case input_format::FLOAT2: return DXGI_FORMAT_R32G32_FLOAT; case input_format::FLOAT3: return DXGI_FORMAT_R32G32B32_FLOAT; case input_format::FLOAT4: return DXGI_FORMAT_R32G32B32A32_FLOAT; default: assert(false); } } static inline int format_size(input_format format) { switch (format) { case input_format::FLOAT1: return 1 * 4; case input_format::FLOAT2: return 2 * 4; case input_format::FLOAT3: return 3 * 4; case input_format::FLOAT4: return 4 * 4; default: assert(false); } } HRESULT scene_state::load_layouts() { HRESULT hr; m_pVertexLayouts = New(m_descriptor->inputs_list_count); for (int i = 0; i < m_descriptor->inputs_list_count; i++) { hr = LoadLayout(m_descriptor->inputs_list[i], &m_pVertexLayouts[i]); if (FAILED(hr)) return hr; } return S_OK; } void scene_state::update_light_instance_vectors(light const& light, node_instance const& node_instance, int light_index) { const XMVECTOR position = XMVectorSet(0, 0, 0, 1); const XMVECTOR direction = XMVectorSet(0, 0, -1, 0); XMVECTOR light_position = XMVector3Transform(position, node_instance.world); XMVECTOR light_direction = XMVector3TransformNormal(direction, node_instance.world); XMVECTOR light_color = XMLoadFloat3((XMFLOAT3 *)&light.color); XMStoreFloat4(&m_lightPositions[light_index], light_position); XMStoreFloat4(&m_lightDirections[light_index], light_direction); XMStoreFloat4(&m_lightColors[light_index], light_color); } void scene_state::update_light_instances() { int light_index = 0; for (int i = 0; i < m_descriptor->nodes_count; i++) { node const& node = *m_descriptor->nodes[i]; node_instance const& node_instance = m_nodeInstances[i]; for (int j = 0; j < node.instance_lights_count; j++) { light const& light = *node.instance_lights[j].light; update_light_instance_vectors(light, node_instance, light_index); light_index += 1; } } } void scene_state::allocate_light_instances() { // count light instances int count = 0; for (int i = 0; i < m_descriptor->nodes_count; i++) { count += m_descriptor->nodes[i]->instance_lights_count; } // allocate m_lightInstancesCount = count; m_lightPositions = New(m_lightInstancesCount); m_lightDirections = New(m_lightInstancesCount); m_lightColors = New(m_lightInstancesCount); } static void initialize_node_transforms(node const * const node, node_instance * const 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.vector = XMLoadFloat4((XMFLOAT4 *)&node->transforms[i].rotate); break; case transform_type::SCALE: transform.vector = XMLoadFloat3((XMFLOAT3*)&node->transforms[i].scale); break; case transform_type::TRANSLATE: transform.vector = XMLoadFloat3((XMFLOAT3*)&node->transforms[i].translate); break; default: assert(false); } } } void scene_state::allocate_node_instance(node const * const node, node_instance * const node_instance) { node_instance->transforms = New(node->transforms_count); initialize_node_transforms(node, node_instance); } void scene_state::allocate_node_instances() { m_nodeInstances = New(m_descriptor->nodes_count); for (int i = 0; i < m_descriptor->nodes_count; i++) { allocate_node_instance(m_descriptor->nodes[i], &m_nodeInstances[i]); } } HRESULT scene_state::load_scene(collada::descriptor const * const descriptor) { HRESULT hr; m_descriptor = descriptor; // layouts hr = load_layouts(); if (FAILED(hr)) return E_FAIL; hr = LoadVertexBuffer(L"RES_MODELS_CURVE_INTERPOLATION_VTX", &m_pVertexBuffers[0]); if (FAILED(hr)) return E_FAIL; hr = LoadIndexBuffer(L"RES_MODELS_CURVE_INTERPOLATION_IDX", &m_pIndexBuffer); if (FAILED(hr)) return E_FAIL; allocate_node_instances(); allocate_light_instances(); return S_OK; } HRESULT LoadLayout(inputs const& inputs, ID3D10InputLayout ** ppVertexLayout) { HRESULT hr; D3D10_INPUT_ELEMENT_DESC layout[inputs.elements_count]; assert(inputs.elements_count == 3); int byte_offset = 0; for (int i = 0; i < inputs.elements_count; i++) { layout[i].SemanticName = inputs.elements[i].semantic; layout[i].SemanticIndex = inputs.elements[i].semantic_index; layout[i].Format = dxgi_format(inputs.elements[i].format); layout[i].InputSlot = 0; layout[i].AlignedByteOffset = byte_offset; layout[i].InputSlotClass = D3D10_INPUT_PER_VERTEX_DATA; layout[i].InstanceDataStepRate = 0; byte_offset += format_size(inputs.elements[i].format); } D3D10_PASS_DESC passDesc; g_pTechniqueBlinn->GetPassByIndex(0)->GetDesc(&passDesc); hr = g_pd3dDevice->CreateInputLayout(layout, inputs.elements_count, passDesc.pIAInputSignature, passDesc.IAInputSignatureSize, ppVertexLayout); if (FAILED(hr)) { print("CreateInputLayout\n"); return hr; } return S_OK; } HRESULT LoadEffect() { HRESULT hr; HRSRC hRes = FindResource(NULL, L"RES_COLLADA_SCENE_FXO", RT_RCDATA); if (hRes == NULL) { print("FindResource RES_COLLADA_SCENE_FXO\n"); return E_FAIL; } DWORD dwResSize = SizeofResource(NULL, hRes); HGLOBAL hData = LoadResource(NULL, hRes); void * pData = LockResource(hData); hr = D3D10CreateEffectFromMemory(pData, dwResSize, 0, g_pd3dDevice, NULL, &g_pEffect ); if (FAILED(hr)) { print("D3D10CreateEffectFromMemory\n"); return hr; } g_pTechniqueBlinn = g_pEffect->GetTechniqueByName("Blinn"); g_pWorldVariable = g_pEffect->GetVariableByName("World")->AsMatrix(); g_pViewVariable = g_pEffect->GetVariableByName("View")->AsMatrix(); g_pProjectionVariable = g_pEffect->GetVariableByName("Projection")->AsMatrix(); g_pViewEyeVariable = g_pEffect->GetVariableByName("ViewEye")->AsVector(); g_pLightPosVariable = g_pEffect->GetVariableByName("LightPos")->AsVector(); g_pLightDirVariable = g_pEffect->GetVariableByName("LightDir")->AsVector(); g_pLightColorVariable = g_pEffect->GetVariableByName("LightColor")->AsVector(); g_pEmissionVariable = g_pEffect->GetVariableByName("Emission")->AsVector(); g_pAmbientVariable = g_pEffect->GetVariableByName("Ambient")->AsVector(); g_pDiffuseVariable = g_pEffect->GetVariableByName("Diffuse")->AsVector(); g_pSpecularVariable = g_pEffect->GetVariableByName("Specular")->AsVector(); g_pShininessVariable = g_pEffect->GetVariableByName("Shininess")->AsScalar(); return S_OK; } HRESULT LoadVertexBuffer(LPCWSTR name, ID3D10Buffer ** ppVertexBuffer) { 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_VERTEX_BUFFER; bd.CPUAccessFlags = 0; bd.MiscFlags = 0; initData.pSysMem = pData; hr = g_pd3dDevice->CreateBuffer(&bd, &initData, ppVertexBuffer); if (FAILED(hr)) { print("CreateBuffer: D3D10_BIND_VERTEX_BUFFER\n"); return hr; } return S_OK; } 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) { case transform_type::TRANSLATE: return XMMatrixTranslationFromVector(transform.vector); case transform_type::ROTATE: return XMMatrixRotationNormal(transform.vector, XMConvertToRadians(XMVectorGetW(transform.vector))); case transform_type::SCALE: return XMMatrixScalingFromVector(transform.vector); default: assert(false); break; } } static inline void SetMaterial(effect const& effect) { switch (effect.type) { case effect_type::BLINN: g_pEmissionVariable->SetFloatVector((float *)&effect.blinn.emission.color); g_pAmbientVariable->SetFloatVector((float *)&effect.blinn.ambient.color); g_pDiffuseVariable->SetFloatVector((float *)&effect.blinn.diffuse.color); g_pSpecularVariable->SetFloatVector((float *)&effect.blinn.specular.color); g_pShininessVariable->SetFloat(effect.blinn.shininess); break; case effect_type::LAMBERT: g_pEmissionVariable->SetFloatVector((float *)&effect.lambert.emission.color); g_pAmbientVariable->SetFloatVector((float *)&effect.lambert.ambient.color); g_pDiffuseVariable->SetFloatVector((float *)&effect.lambert.diffuse.color); break; case effect_type::PHONG: g_pEmissionVariable->SetFloatVector((float *)&effect.phong.emission.color); g_pAmbientVariable->SetFloatVector((float *)&effect.phong.ambient.color); g_pDiffuseVariable->SetFloatVector((float *)&effect.phong.diffuse.color); g_pSpecularVariable->SetFloatVector((float *)&effect.phong.specular.color); g_pShininessVariable->SetFloat(effect.phong.shininess); break; case effect_type::CONSTANT: g_pEmissionVariable->SetFloatVector((float *)&effect.constant.color); break; default: break; } } void scene_state::render_geometries(instance_geometry const * const instance_geometries, int const instance_geometries_count) { for (int i = 0; i < instance_geometries_count; i++) { instance_geometry const &instance_geometry = instance_geometries[i]; mesh const& mesh = instance_geometry.geometry->mesh; UINT strides[1] = { 3 * 3 * 4 }; UINT offsets[1] = { (UINT)mesh.vertex_buffer_offset }; g_pd3dDevice->IASetVertexBuffers(0, m_numBuffers, m_pVertexBuffers, strides, offsets); g_pd3dDevice->IASetIndexBuffer(m_pIndexBuffer, DXGI_FORMAT_R32_UINT, mesh.index_buffer_offset); D3D10_TECHNIQUE_DESC techDesc; g_pTechniqueBlinn->GetDesc(&techDesc); for (int j = 0; j < instance_geometry.instance_materials_count; j++) { instance_material const& instance_material = instance_geometry.instance_materials[j]; triangles const& triangles = mesh.triangles[instance_material.element_index]; SetMaterial(*instance_material.material->effect); g_pTechniqueBlinn->GetPassByIndex(0)->Apply(0); g_pd3dDevice->IASetInputLayout(m_pVertexLayouts[triangles.inputs_index]); g_pd3dDevice->DrawIndexed(triangles.count * 3, triangles.index_offset, 0); } } } static inline float fract(float f) { return f - floorf(f); } static inline float loop(float f, float n) { return fract(f / n) * n; } static inline int find_frame_ix(source const& source, float t) { for (int i = 0; i < source.count - 1; i++) { if (source.float_array[i] <= t && source.float_array[i+1] > t) { return i; } } return -1; } static inline float linear_interpolate_iv(source const& source, int frame_ix, float t) { float prev = source.float_array[(frame_ix+0) * source.stride]; float next = source.float_array[(frame_ix+1) * source.stride]; return (t - prev) / (next - prev); } static inline float linear_interpolate_value(source const& source, int frame_ix, int parameter_ix, float iv) { float prev = source.float_array[(frame_ix+0) * source.stride + parameter_ix]; float next = source.float_array[(frame_ix+1) * source.stride + parameter_ix]; return prev + iv * (next - prev); } static inline float pow3(float f) { return f * f * f; } static inline float pow2(float f) { return f * f; } static inline XMVECTOR bezier(XMVECTOR p0, XMVECTOR c0, XMVECTOR c1, XMVECTOR p1, float s) { return p0 * pow3(1 - s) + 3 * c0 * s * pow2(1 - s) + 3 * c1 * pow2(s) * (1 - s) + p1 * pow3(s); } static inline float bezier_binary_search(XMVECTOR p0, XMVECTOR c0, XMVECTOR c1, XMVECTOR p1, float want) { float low = 0.0f; float high = 1.0f; int iterations = 0; while (iterations < 20) { iterations += 1; float s = (high + low) * 0.5f; XMVECTOR bs = bezier(p0, c0, c1, p1, s); float t = XMVectorGetX(bs); const float epsilon = 0.001f; if (fabsf(t - want) < epsilon) { return XMVectorGetY(bs); } if (t > want) { high = s; } else { low = s; } } print("%f %f\n", XMVectorGetX(p0), XMVectorGetY(p0)); print("%f %f\n", XMVectorGetX(c0), XMVectorGetY(c0)); print("%f %f\n", XMVectorGetX(c1), XMVectorGetY(c1)); print("%f %f\n", XMVectorGetX(p1), XMVectorGetY(p1)); assert(false); } static inline XMFLOAT2 const * tangent_index(source const& source, int frame_ix, int parameter_ix) { int ix = frame_ix * source.stride + parameter_ix * 2; return (XMFLOAT2 const *)&source.float_array[ix]; } static float bezier_sampler(sampler const * const sampler, int frame_ix, int parameter_ix, float t) { /* P0 is (INPUT[i] , OUTPUT[i]) C0 (or T0) is (OUT_TANGENT[i][0] , OUT_TANGENT[i][1]) C1 (or T1) is (IN_TANGENT[i+1][0], IN_TANGENT[i+1][1]) P1 is (INPUT[i+1], OUTPUT[i+1]) */ float frame0_input = sampler->input.float_array[frame_ix+0]; float frame1_input = sampler->input.float_array[frame_ix+1]; float frame0_output = sampler->output.float_array[(frame_ix+0) * sampler->output.stride + parameter_ix]; float frame1_output = sampler->output.float_array[(frame_ix+1) * sampler->output.stride + parameter_ix]; XMVECTOR p0 = XMVectorSet(frame0_input, frame0_output, 0, 0); XMVECTOR c0 = XMLoadFloat2(tangent_index(sampler->out_tangent, frame_ix + 0, parameter_ix)); XMVECTOR c1 = XMLoadFloat2(tangent_index(sampler->in_tangent, frame_ix + 1, parameter_ix)); XMVECTOR p1 = XMVectorSet(frame1_input, frame1_output, 0, 0); return bezier_binary_search(p0, c0, c1, p1, t); } static void apply_transform_target(transform& transform, enum target_attribute channel_target_attribute, float value) { switch (transform.type) { case transform_type::TRANSLATE: __attribute__((fallthrough)); case transform_type::SCALE: switch (channel_target_attribute) { case target_attribute::X: transform.vector = XMVectorSetX(transform.vector, value); return; case target_attribute::Y: transform.vector = XMVectorSetY(transform.vector, value); return; case target_attribute::Z: transform.vector = XMVectorSetZ(transform.vector, value); return; default: assert(false); } case transform_type::ROTATE: switch (channel_target_attribute) { case target_attribute::X: transform.vector = XMVectorSetX(transform.vector, value); return; case target_attribute::Y: transform.vector = XMVectorSetY(transform.vector, value); return; case target_attribute::Z: transform.vector = XMVectorSetZ(transform.vector, value); return; case target_attribute::ANGLE: transform.vector = XMVectorSetW(transform.vector, value); return; default: assert(false); } default: assert(false); break; } } static enum target_attribute const rotate_target_attributes[] = { target_attribute::X, target_attribute::Y, target_attribute::Z, target_attribute::ANGLE, }; static enum target_attribute const translate_scale_target_attributes[] = { target_attribute::X, target_attribute::Y, target_attribute::Z, }; static void animate_channel_segment(channel const& channel, transform& transform, int frame_ix, float t) { enum target_attribute const * target_attributes = &channel.target_attribute; int target_attributes_count = 1; if (channel.target_attribute == target_attribute::ALL) { switch (transform.type) { case transform_type::TRANSLATE: __attribute__((fallthrough)); case transform_type::SCALE: target_attributes = translate_scale_target_attributes; target_attributes_count = 3; break; case transform_type::ROTATE: target_attributes = rotate_target_attributes; target_attributes_count = 4; break; default: assert(false); break; } } for (int parameter_ix = 0; parameter_ix < target_attributes_count; parameter_ix++) { enum collada::interpolation interpolation = channel.source_sampler->interpolation.interpolation_array[frame_ix]; float value; if (interpolation == interpolation::BEZIER) { value = bezier_sampler(channel.source_sampler, frame_ix, parameter_ix, t); } else { float iv = linear_interpolate_iv(channel.source_sampler->input, frame_ix, t); value = linear_interpolate_value(channel.source_sampler->output, frame_ix, parameter_ix, iv); } apply_transform_target(transform, target_attributes[parameter_ix], value); } } static void animate_node(node const& node, node_instance& node_instance, float t) { for (int i = 0; i < node.channels_count; i++) { channel const& channel = *node.channels[i]; transform& transform = node_instance.transforms[channel.target_transform_index]; int frame_ix = find_frame_ix(channel.source_sampler->input, t); assert(frame_ix >= 0); // animation is missing a key frame animate_channel_segment(channel, transform, frame_ix, t); } } void scene_state::node_world_transform(node const& node, node_instance& node_instance) { // build the node's world transform matrix if (node.parent_index >= 0) node_instance.world = m_nodeInstances[node.parent_index].world; else node_instance.world = XMMatrixIdentity(); for (int j = 0; j < node.transforms_count; j++) { node_instance.world = TransformMatrix(node_instance.transforms[j]) * node_instance.world; } } void scene_state::update(float t) { t = loop(t / 2.0f, 3.333333f); // animate and update all world transforms for (int i = 0; i < m_descriptor->nodes_count; i++) { node const& node = *m_descriptor->nodes[i]; node_instance& node_instance = m_nodeInstances[i]; animate_node(node, node_instance, t); node_world_transform(node, node_instance); } // update all lights (after world transforms are computed) update_light_instances(); } static int min(int a, int b) { return a < b ? a : b; } void scene_state::render() { XMFLOAT4 eye; XMFLOAT4X4 projection; XMFLOAT4X4 view; XMStoreFloat4(&eye, g_Eye); XMStoreFloat4x4(&view, g_View); XMStoreFloat4x4(&projection, g_Projection); g_pViewVariable->SetMatrix((float *)&view); g_pProjectionVariable->SetMatrix((float *)&projection); g_pViewEyeVariable->SetFloatVector((float *)&eye); int lights = min(2, m_lightInstancesCount); g_pLightPosVariable->SetFloatVectorArray((float *)m_lightPositions, 0, lights); g_pLightDirVariable->SetFloatVectorArray((float *)m_lightDirections, 0, lights); g_pLightColorVariable->SetFloatVectorArray((float *)m_lightColors, 0, lights); g_pd3dDevice->IASetPrimitiveTopology(D3D10_PRIMITIVE_TOPOLOGY_TRIANGLELIST); for (int i = 0; i < m_descriptor->nodes_count; i++) { node const& node = *m_descriptor->nodes[i]; node_instance& node_instance = m_nodeInstances[i]; // joints aren't rendered if (node.type != node_type::NODE) continue; g_pWorldVariable->SetMatrix((float *)&node_instance.world); render_geometries(node.instance_geometries, node.instance_geometries_count); } } }