// recursively render meshes for all nodes // lambda-free version bool CompoundMesh::render( ID3D11DeviceContext *deviceContext, VanillaShaderClass *shader, DirectX::CXMMATRIX worldMatrix, DirectX::CXMMATRIX viewMatrix, DirectX::CXMMATRIX projectionMatrix, std::vector<Light> &lights, bool orthoProjection /*= false*/, double animationTick /*= 1.0*/, CompoundMeshNode *node /*= nullptr*/, DirectX::CXMMATRIX parentNodeTransform /*= XMMatrixIdentity()*/ ) { if (!node) { // top-level invocation; do some initialization and culling node = m_root.get(); if (m_animation.loaded()) { double ticksPerSec = m_aiScene->mAnimations[0]->mTicksPerSecond; if (ticksPerSec <= 0) ticksPerSec = 24; animationTick *= ticksPerSec; animationTick += 1; // time starts at 0 but ticks, alas, at 1? while (animationTick > m_animation.maxTick) animationTick -= m_animation.maxTick; updateNodeTransforms(animationTick); } // clip to view frustum BoundingSphere bSphere(m_bSphere); bSphere.Transform(bSphere, worldMatrix); //BoundingOrientedBox bBox; //m_bBox.Transform(bBox, worldMatrix); if (!orthoProjection) { BoundingFrustum frustum(projectionMatrix); // TODO this should be moved somewhere higher up if the engine ever becomes CPU-bound XMStoreFloat4(&frustum.Orientation, XMVectorSet(0, 0, 0, 1)); // identity quaternion frustum.Transform(frustum, XMMatrixInverse(nullptr, viewMatrix)); // move the frustum as though it was an object within the world; probably slow? if (!DirectX::Internal::XMQuaternionIsUnit(XMLoadFloat4(&frustum.Orientation))) { cerr << "Bad frustum quaternion! :( " << XMLoadFloat4(&frustum.Orientation) << endl; XMStoreFloat4(&frustum.Orientation, XMVectorSet(0, 0, 0, 1)); } try { if (!frustum.Intersects(bSphere)) return true; } catch (exception *e) { cerr << e->what() << endl; } } else { // make a bounding box from the orthographic projection matrix (is this weird? perhaps it's weird.) XMMATRIX unproj = XMMatrixInverse(nullptr, projectionMatrix); float x = unproj.r[0].m128_f32[0]; // half the width; for now, assuming projection centered at origin float y = unproj.r[1].m128_f32[1]; // half the height float near_plane = unproj.r[3].m128_f32[2]; float far_plane = unproj.r[2].m128_f32[2] + near_plane; XMFLOAT3 points[2]; points[0].x = -x; points[0].y = -y; points[0].z = near_plane; points[1].x = x; points[1].y = y; points[1].z = max(far_plane,10000); // /fp:fast workaround ಠ_ಠ //points[1].z = far_plane; // XXX XXX XXX Something about this code fails with /fp:fast XXX XXX XXX // one workaround is to set far_plane to 10000; we're probably not culling based on distance anyway so no harm done, at least? // another is, of course, to set /fp:precise ... this costs several FPS on the i7 laptop with the double-torus test scene! BoundingOrientedBox viewBox; viewBox.CreateFromPoints(viewBox, 2, points, sizeof(XMFLOAT3)); XMStoreFloat4(&viewBox.Orientation, XMVectorSet(0, 0, 0, 1)); // identity quaternion... strange that Orientation would be uninitialized though viewBox.Transform(viewBox, XMMatrixInverse(nullptr, viewMatrix)); if (!DirectX::Internal::XMQuaternionIsUnit(XMLoadFloat4(&viewBox.Orientation))) { cerr << "Bad viewBox quaternion! :( " << XMLoadFloat4(&viewBox.Orientation) << endl; XMStoreFloat4(&viewBox.Orientation, XMVectorSet(0, 0, 0, 1)); } // clip to viewbox try { if (!viewBox.Intersects(bSphere)) return true; } catch (exception *e) { cerr << e->what() << endl; } } // tell the vertex shader about the wonderful animation buffer we have for it: if (m_animation.loaded()) { m_animation.updateCurrentBoneKeys(deviceContext, animationTick); } } for (auto mesh = node->meshes.begin(), end = node->meshes.end(); mesh != end; ++mesh) { if (mesh->m_indexBuffer == nullptr || mesh->m_vertexBuffer == nullptr || !mesh->getIndexCount()) { cerr << "strange, empty mesh"; continue; } mesh->setBuffers(deviceContext); // point the GPU at the right geometry data SimpleMesh::Material &mat = mesh->m_material; bool useNormalMap = mat.normalMap.getTexture() ? true : false; bool useSpecularMap = mat.specularMap.getTexture() ? true : false; if (!shader->SetPSMaterial(deviceContext, mat.ambient, mat.diffuse, mat.shininess, mat.specular, useNormalMap, useSpecularMap)) { return false; } XMFLOAT4X4 *offsetMatrix = nullptr; if (m_animation.loaded()) { m_animation.updateBoneTransforms(deviceContext, animationTick, mesh->m_OffsetMatrix, mesh->m_name, [&](std::string nodeName) { return getNodeGlobalTransform(nodeName); } ); } XMMATRIX finalWorldMatrix; if (!m_animation.loaded() && node->name.size() == 0) { finalWorldMatrix = worldMatrix; } else { finalWorldMatrix = XMMatrixMultiply(getNodeGlobalTransform(node->name), worldMatrix); } if (!shader->Render(deviceContext, mesh->getIndexCount(), finalWorldMatrix, viewMatrix, projectionMatrix, mesh->m_material.normalMap.getTexture(), mesh->m_material.specularMap.getTexture(), &lights, mesh->m_material.diffuseTexture.getTexture(), 1, true, m_animation.loaded() ? animationTick : -1)) { return false; } } for (auto i : node->children) { if (!render(deviceContext, shader, worldMatrix, viewMatrix, projectionMatrix, lights, orthoProjection, animationTick, i.get(), XMMatrixIdentity())) return false; } return true; }