#include "CScriptNode.h" #include #include #include #include #include #include #include #include CScriptNode::CScriptNode(CSceneManager *pScene, CSceneNode *pParent, CScriptObject *pObject) : CSceneNode(pScene, pParent) { mpVolumePreviewNode = nullptr; // Evaluate instance mpInstance = pObject; mpActiveModel = nullptr; if (mpInstance) { mpActiveModel = mpInstance->GetDisplayModel(); mPosition = mpInstance->GetPosition(); mRotation = CQuaternion::FromEuler(mpInstance->GetRotation()); mScale = mpInstance->GetScale(); SetName("[" + mpInstance->Template()->TemplateName() + "] " + mpInstance->GetInstanceName()); MarkTransformChanged(); mHasValidPosition = ((mpInstance->GetAttribFlags() & ePositionAttrib) != 0); mHasVolumePreview = ((mpInstance->GetAttribFlags() & eVolumeAttrib) != 0); // Create volume preview node if (mHasVolumePreview) { u32 VolumeShape = mpInstance->GetVolumeShape(); CModel *pVolumeModel = nullptr; if ((VolumeShape == 0) || (VolumeShape == 1)) // Box/OrientedBox pVolumeModel = (CModel*) gResCache.GetResource("../resources/VolumeBox.cmdl"); else if (VolumeShape == 2) // Sphere pVolumeModel = (CModel*) gResCache.GetResource("../resources/VolumeSphere.cmdl"); if (pVolumeModel) { mpVolumePreviewNode = new CModelNode(pScene, this, pVolumeModel); mpVolumePreviewNode->SetInheritance(true, (VolumeShape == 1), false); mpVolumePreviewNode->Scale(mpInstance->GetVolume()); mpVolumePreviewNode->ForceAlphaEnabled(true); } } } else { // Shouldn't ever happen SetName("ScriptNode - NO INSTANCE"); } if (mpActiveModel) mLocalAABox = mpActiveModel->AABox(); else mLocalAABox = CAABox::skOne; } ENodeType CScriptNode::NodeType() { return eScriptNode; } std::string CScriptNode::PrefixedName() const { return "[" + mpInstance->Template()->TemplateName() + "] " + mpInstance->GetInstanceName(); } void CScriptNode::AddToRenderer(CRenderer *pRenderer) { if (!mpInstance) return; if (!mpActiveModel) pRenderer->AddOpaqueMesh(this, 0, AABox(), eDrawMesh); else { if (!mpActiveModel->IsBuffered()) mpActiveModel->BufferGL(); if (!mpActiveModel->HasTransparency(0)) pRenderer->AddOpaqueMesh(this, 0, AABox(), eDrawMesh); else { u32 SubmeshCount = mpActiveModel->GetSurfaceCount(); for (u32 s = 0; s < SubmeshCount; s++) { if (!mpActiveModel->IsSurfaceTransparent(s, 0)) pRenderer->AddOpaqueMesh(this, s, mpActiveModel->GetSurfaceAABox(s).Transformed(Transform()), eDrawAsset); else pRenderer->AddTransparentMesh(this, s, mpActiveModel->GetSurfaceAABox(s).Transformed(Transform()), eDrawAsset); } } } if (IsSelected()) { pRenderer->AddOpaqueMesh(this, 0, AABox(), eDrawSelection); if (mHasVolumePreview) mpVolumePreviewNode->AddToRenderer(pRenderer); } } void CScriptNode::Draw(ERenderOptions Options) { if (!mpInstance) return; // Set lighting LoadModelMatrix(); LoadLights(); if (CGraphics::sLightMode == CGraphics::WorldLighting) CGraphics::sVertexBlock.COLOR0_Amb = CGraphics::sAreaAmbientColor.ToVector4f() * CGraphics::sWorldLightMultiplier; else CGraphics::sVertexBlock.COLOR0_Amb = CGraphics::skDefaultAmbientColor.ToVector4f(); // Default to drawing purple box if no model if (!mpActiveModel) { glBlendFuncSeparate(GL_ONE, GL_ZERO, GL_ZERO, GL_ZERO); glDepthMask(GL_TRUE); LoadModelMatrix(); CGraphics::UpdateVertexBlock(); CGraphics::UpdateLightBlock(); CDrawUtil::DrawShadedCube(CColor::skTransparentPurple); return; } // Set tev color (used rarely) CGraphics::sPixelBlock.TevColor = mpInstance->GetTevColor().ToVector4f(); mpActiveModel->Draw(Options, 0); } void CScriptNode::DrawAsset(ERenderOptions Options, u32 Asset) { if (!mpInstance) return; if (!mpActiveModel) return; if (CGraphics::sLightMode == CGraphics::WorldLighting) CGraphics::sVertexBlock.COLOR0_Amb = CGraphics::sAreaAmbientColor.ToVector4f() * CGraphics::sWorldLightMultiplier; else CGraphics::sVertexBlock.COLOR0_Amb = CGraphics::skDefaultAmbientColor.ToVector4f(); LoadModelMatrix(); LoadLights(); CGraphics::sPixelBlock.TevColor = mpInstance->GetTevColor().ToVector4f(); mpActiveModel->DrawSurface(Options, Asset, 0); } void CScriptNode::DrawSelection() { glBlendFunc(GL_ONE, GL_ZERO); CDrawUtil::DrawWireCube(AABox(), CColor::skTransparentWhite); if (mpInstance) { CGraphics::sMVPBlock.ModelMatrix = CMatrix4f::skIdentity; CGraphics::UpdateMVPBlock(); for (u32 iIn = 0; iIn < mpInstance->NumInLinks(); iIn++) { const SLink& con = mpInstance->InLink(iIn); CScriptNode *pLinkNode = mpScene->ScriptNodeByID(con.ObjectID); if (pLinkNode) CDrawUtil::DrawLine(CenterPoint(), pLinkNode->CenterPoint(), CColor::skTransparentRed); } for (u32 iOut = 0; iOut < mpInstance->NumOutLinks(); iOut++) { const SLink& con = mpInstance->OutLink(iOut); CScriptNode *pLinkNode = mpScene->ScriptNodeByID(con.ObjectID); if (pLinkNode) CDrawUtil::DrawLine(CenterPoint(), pLinkNode->CenterPoint(), CColor::skTransparentGreen); } } } void CScriptNode::RayAABoxIntersectTest(CRayCollisionTester &Tester) { if (!mpInstance) return; const CRay& Ray = Tester.Ray(); std::pair BoxResult = AABox().IntersectsRay(Ray); if (BoxResult.first) { if (mpActiveModel) { for (u32 iSurf = 0; iSurf < mpActiveModel->GetSurfaceCount(); iSurf++) { std::pair SurfResult = mpActiveModel->GetSurfaceAABox(iSurf).Transformed(Transform()).IntersectsRay(Ray); if (SurfResult.first) Tester.AddNode(this, iSurf, SurfResult.second); } } else Tester.AddNode(this, 0, BoxResult.second); } } SRayIntersection CScriptNode::RayNodeIntersectTest(const CRay &Ray, u32 AssetID) { SRayIntersection out; out.pNode = this; out.AssetIndex = AssetID; CRay TransformedRay = Ray.Transformed(Transform().Inverse()); CModel *pModel = (mpActiveModel ? mpActiveModel : CDrawUtil::GetCubeModel()); std::pair Result = pModel->GetSurface(AssetID)->IntersectsRay(TransformedRay); if (Result.first) { out.Hit = true; CVector3f HitPoint = TransformedRay.PointOnRay(Result.second); CVector3f WorldHitPoint = Transform() * HitPoint; out.Distance = Math::Distance(Ray.Origin(), WorldHitPoint); } else out.Hit = false; return out; } bool CScriptNode::IsVisible() const { // Reimplementation of CSceneNode::IsHidden() to allow for layer and template visiblity to be taken into account return (mVisible && mpInstance->Layer()->IsVisible() && mpInstance->Template()->IsVisible()); } CScriptObject* CScriptNode::Object() { return mpInstance; } CModel* CScriptNode::ActiveModel() { return mpActiveModel; } void CScriptNode::GeneratePosition() { if (!mHasValidPosition) { // Default to center of the active area; this is to preven recursion issues CTransform4f& AreaTransform = mpScene->GetActiveArea()->GetTransform(); mPosition = CVector3f(AreaTransform[0][3], AreaTransform[1][3], AreaTransform[2][3]); mHasValidPosition = true; MarkTransformChanged(); // Ideal way to generate the position is to find a spot close to where it's being used. // To do this I check the location of the objects that this one is linked to. u32 NumLinks = mpInstance->NumInLinks() + mpInstance->NumOutLinks(); // In the case of one link, apply an offset so the new position isn't the same place as the object it's linked to if (NumLinks == 1) { const SLink& link = (mpInstance->NumInLinks() > 0 ? mpInstance->InLink(0) : mpInstance->OutLink(0)); CScriptNode *pNode = mpScene->ScriptNodeByID(link.ObjectID); pNode->GeneratePosition(); mPosition = pNode->AbsolutePosition(); mPosition.z += (pNode->AABox().Size().z / 2.f); mPosition.z += (AABox().Size().z / 2.f); mPosition.z += 2.f; } // For two or more links, average out the position of the connected objects. else if (NumLinks >= 2) { CVector3f NewPos = CVector3f::skZero; for (u32 iIn = 0; iIn < mpInstance->NumInLinks(); iIn++) { CScriptNode *pNode = mpScene->ScriptNodeByID(mpInstance->InLink(iIn).ObjectID); if (pNode) { pNode->GeneratePosition(); NewPos += pNode->AABox().Center(); } } for (u32 iOut = 0; iOut < mpInstance->NumOutLinks(); iOut++) { CScriptNode *pNode = mpScene->ScriptNodeByID(mpInstance->OutLink(iOut).ObjectID); if (pNode) { pNode->GeneratePosition(); NewPos += pNode->AABox().Center(); } } mPosition = NewPos / NumLinks; mPosition.x += 2.f; } MarkTransformChanged(); } } bool CScriptNode::HasPreviewVolume() { return mHasVolumePreview; } CAABox CScriptNode::PreviewVolumeAABox() { if (!mHasVolumePreview) return CAABox::skZero; else return mpVolumePreviewNode->AABox(); }