예제 #1
0
// 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;
}