diff --git a/Core/CDrawUtil.cpp b/Core/CDrawUtil.cpp index 7fcc44d7..78e83979 100644 --- a/Core/CDrawUtil.cpp +++ b/Core/CDrawUtil.cpp @@ -28,6 +28,8 @@ CToken CDrawUtil::mDoubleSidedSphereToken; CShader *CDrawUtil::mpColorShader; CShader *CDrawUtil::mpColorShaderLighting; +CShader *CDrawUtil::mpBillboardShader; +CShader *CDrawUtil::mpLightBillboardShader; CShader *CDrawUtil::mpTextureShader; CShader *CDrawUtil::mpCollisionShader; CShader *CDrawUtil::mpTextShader; @@ -35,6 +37,10 @@ CShader *CDrawUtil::mpTextShader; CTexture *CDrawUtil::mpCheckerTexture; CToken CDrawUtil::mCheckerTextureToken; +CTexture *CDrawUtil::mpLightTextures[4]; +CTexture *CDrawUtil::mpLightMasks[4]; +CToken CDrawUtil::mLightTextureTokens[8]; + bool CDrawUtil::mDrawUtilInitialized = false; // ************ PUBLIC ************ @@ -193,6 +199,79 @@ void CDrawUtil::DrawSphere(const CColor &kColor) DrawSphere(false); } +void CDrawUtil::DrawBillboard(CTexture* pTexture, const CVector3f& Position, const CVector2f& Scale /*= CVector2f::skOne*/, const CColor& Tint /*= CColor::skWhite*/) +{ + Init(); + + // Create translation-only model matrix + CGraphics::sMVPBlock.ModelMatrix = CTransform4f::TranslationMatrix(Position).ToMatrix4f(); + CGraphics::UpdateMVPBlock(); + + // Set uniforms + mpBillboardShader->SetCurrent(); + + GLuint ScaleLoc = mpBillboardShader->GetUniformLocation("BillboardScale"); + glUniform2f(ScaleLoc, Scale.x, Scale.y); + + GLuint TintLoc = mpBillboardShader->GetUniformLocation("TintColor"); + CVector4f Tint4f = Tint.ToVector4f(); + glUniform4f(TintLoc, Tint4f.x, Tint4f.y, Tint4f.z, Tint4f.w); + + pTexture->Bind(0); + + // Set other properties + CMaterial::KillCachedMaterial(); + glBlendFunc(GL_ONE, GL_ZERO); + glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); + glDepthMask(GL_TRUE); + + // Draw + DrawSquare(); +} + +void CDrawUtil::DrawLightBillboard(ELightType Type, const CColor& LightColor, const CVector3f& Position, const CVector2f& Scale /*= CVector2f::skOne*/, const CColor& Tint /*= CColor::skWhite*/) +{ + Init(); + + // Create translation-only model matrix + CGraphics::sMVPBlock.ModelMatrix = CTransform4f::TranslationMatrix(Position).ToMatrix4f(); + CGraphics::UpdateMVPBlock(); + + // Set uniforms + mpLightBillboardShader->SetCurrent(); + + GLuint ScaleLoc = mpLightBillboardShader->GetUniformLocation("BillboardScale"); + glUniform2f(ScaleLoc, Scale.x, Scale.y); + + GLuint ColorLoc = mpLightBillboardShader->GetUniformLocation("LightColor"); + CVector4f Color4f = LightColor.ToVector4f(); + glUniform4f(ColorLoc, Color4f.x, Color4f.y, Color4f.z, Color4f.w); + + GLuint TintLoc = mpLightBillboardShader->GetUniformLocation("TintColor"); + CVector4f Tint4f = Tint.ToVector4f(); + glUniform4f(TintLoc, Tint4f.x, Tint4f.y, Tint4f.z, Tint4f.w); + + CTexture *pTexA = GetLightTexture(Type); + CTexture *pTexB = GetLightMask(Type); + pTexA->Bind(0); + pTexB->Bind(1); + + GLuint TextureLoc = mpLightBillboardShader->GetUniformLocation("Texture"); + GLuint MaskLoc = mpLightBillboardShader->GetUniformLocation("LightMask"); + glUniform1i(TextureLoc, 0); + glUniform1i(MaskLoc, 1); + + // Set other properties + CMaterial::KillCachedMaterial(); + glBlendFunc(GL_ONE, GL_ZERO); + glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); + glDepthMask(GL_TRUE); + + // Draw + DrawSquare(); + +} + void CDrawUtil::UseColorShader(const CColor& kColor) { Init(); @@ -258,6 +337,18 @@ void CDrawUtil::LoadCheckerboardTexture(u32 GLTextureUnit) mpCheckerTexture->Bind(GLTextureUnit); } +CTexture* CDrawUtil::GetLightTexture(ELightType Type) +{ + Init(); + return mpLightTextures[Type]; +} + +CTexture* CDrawUtil::GetLightMask(ELightType Type) +{ + Init(); + return mpLightMasks[Type]; +} + CModel* CDrawUtil::GetCubeModel() { Init(); @@ -420,18 +511,36 @@ void CDrawUtil::InitSphere() void CDrawUtil::InitShaders() { Log::Write("Creating shaders"); - mpColorShader = CShader::FromResourceFile("ColorShader"); - mpColorShaderLighting = CShader::FromResourceFile("ColorShaderLighting"); - mpTextureShader = CShader::FromResourceFile("TextureShader"); - mpCollisionShader = CShader::FromResourceFile("CollisionShader"); - mpTextShader = CShader::FromResourceFile("TextShader"); + mpColorShader = CShader::FromResourceFile("ColorShader"); + mpColorShaderLighting = CShader::FromResourceFile("ColorShaderLighting"); + mpBillboardShader = CShader::FromResourceFile("BillboardShader"); + mpLightBillboardShader = CShader::FromResourceFile("LightBillboardShader"); + mpTextureShader = CShader::FromResourceFile("TextureShader"); + mpCollisionShader = CShader::FromResourceFile("CollisionShader"); + mpTextShader = CShader::FromResourceFile("TextShader"); } void CDrawUtil::InitTextures() { - Log::Write("Loading checkerboard texture"); + Log::Write("Loading textures"); mpCheckerTexture = (CTexture*) gResCache.GetResource("../resources/Checkerboard.txtr"); mCheckerTextureToken = CToken(mpCheckerTexture); + + mpLightTextures[0] = (CTexture*) gResCache.GetResource("../resources/LightAmbient.txtr"); + mpLightTextures[1] = (CTexture*) gResCache.GetResource("../resources/LightDirectional.txtr"); + mpLightTextures[2] = (CTexture*) gResCache.GetResource("../resources/LightCustom.txtr"); + mpLightTextures[3] = (CTexture*) gResCache.GetResource("../resources/LightSpot.txtr"); + + mpLightMasks[0] = (CTexture*) gResCache.GetResource("../resources/LightAmbientMask.txtr"); + mpLightMasks[1] = (CTexture*) gResCache.GetResource("../resources/LightDirectionalMask.txtr"); + mpLightMasks[2] = (CTexture*) gResCache.GetResource("../resources/LightCustomMask.txtr"); + mpLightMasks[3] = (CTexture*) gResCache.GetResource("../resources/LightSpotMask.txtr"); + + for (int i = 0; i < 4; i++) + { + mLightTextureTokens[i] = CToken(mpLightTextures[i]); + mLightTextureTokens[i+4] = CToken(mpLightMasks[i]); + } } void CDrawUtil::Shutdown() diff --git a/Core/CDrawUtil.h b/Core/CDrawUtil.h index d9a8d7fd..cc07ab6f 100644 --- a/Core/CDrawUtil.h +++ b/Core/CDrawUtil.h @@ -5,6 +5,7 @@ #include #include #include +#include class CDrawUtil { @@ -37,6 +38,8 @@ class CDrawUtil // Shaders static CShader *mpColorShader; static CShader *mpColorShaderLighting; + static CShader *mpBillboardShader; + static CShader *mpLightBillboardShader; static CShader *mpTextureShader; static CShader *mpCollisionShader; static CShader *mpTextShader; @@ -45,33 +48,50 @@ class CDrawUtil static CTexture *mpCheckerTexture; static CToken mCheckerTextureToken; + static CTexture *mpLightTextures[4]; + static CTexture *mpLightMasks[4]; + static CToken mLightTextureTokens[8]; + // Have all the above members been initialized? static bool mDrawUtilInitialized; public: static void DrawGrid(); + static void DrawSquare(); static void DrawSquare(const CVector2f& TexUL, const CVector2f& TexUR, const CVector2f& TexBR, const CVector2f& TexBL); static void DrawSquare(const float *pTexCoords); + static void DrawLine(const CVector3f& PointA, const CVector3f& PointB); static void DrawLine(const CVector2f& PointA, const CVector2f& PointB); static void DrawLine(const CVector3f& PointA, const CVector3f& PointB, const CColor& LineColor); static void DrawLine(const CVector2f& PointA, const CVector2f& PointB, const CColor& LineColor); + static void DrawCube(); static void DrawCube(const CColor& Color); static void DrawCube(const CVector3f& Position, const CColor& Color); static void DrawShadedCube(const CColor& Color); + static void DrawWireCube(); static void DrawWireCube(const CAABox& AABox, const CColor& Color); + static void DrawSphere(bool DoubleSided = false); static void DrawSphere(const CColor& Color); + + static void DrawBillboard(CTexture* pTexture, const CVector3f& Position, const CVector2f& Scale = CVector2f::skOne, const CColor& Tint = CColor::skWhite); + + static void DrawLightBillboard(ELightType Type, const CColor& LightColor, const CVector3f& Position, const CVector2f& Scale = CVector2f::skOne, const CColor& Tint = CColor::skWhite); + static void UseColorShader(const CColor& Color); static void UseColorShaderLighting(const CColor& Color); static void UseTextureShader(); static void UseTextureShader(const CColor& TintColor); static void UseCollisionShader(); + static CShader* GetTextShader(); static void LoadCheckerboardTexture(u32 GLTextureUnit); + static CTexture* GetLightTexture(ELightType Type); + static CTexture* GetLightMask(ELightType Type); static CModel* GetCubeModel(); private: diff --git a/Resource/cooker/CTemplateWriter.cpp b/Resource/cooker/CTemplateWriter.cpp index b61dfae4..0a8a0c7a 100644 --- a/Resource/cooker/CTemplateWriter.cpp +++ b/Resource/cooker/CTemplateWriter.cpp @@ -269,6 +269,7 @@ void CTemplateWriter::SaveScriptTemplate(CScriptTemplate *pTemp, const TString& { case CScriptTemplate::SEditorAsset::eModel: type = "model"; break; case CScriptTemplate::SEditorAsset::eAnimParams: type = "animparams"; break; + case CScriptTemplate::SEditorAsset::eBillboard: type = "billboard"; break; case CScriptTemplate::SEditorAsset::eCollision: type = "collision"; break; } diff --git a/Resource/factory/CTemplateLoader.cpp b/Resource/factory/CTemplateLoader.cpp index a11377e4..00f8f31c 100644 --- a/Resource/factory/CTemplateLoader.cpp +++ b/Resource/factory/CTemplateLoader.cpp @@ -352,6 +352,8 @@ CScriptTemplate* CTemplateLoader::LoadScriptTemplate(tinyxml2::XMLDocument *pDoc asset.AssetType = CScriptTemplate::SEditorAsset::eAnimParams; else if (strcmp(pAsset->Name(), "model") == 0) asset.AssetType = CScriptTemplate::SEditorAsset::eModel; + else if (strcmp(pAsset->Name(), "billboard") == 0) + asset.AssetType = CScriptTemplate::SEditorAsset::eBillboard; else if (strcmp(pAsset->Name(), "collision") == 0) asset.AssetType = CScriptTemplate::SEditorAsset::eCollision; else diff --git a/Resource/script/CScriptObject.cpp b/Resource/script/CScriptObject.cpp index 8ee8607a..4baa88ef 100644 --- a/Resource/script/CScriptObject.cpp +++ b/Resource/script/CScriptObject.cpp @@ -10,6 +10,7 @@ CScriptObject::CScriptObject(CGameArea *pArea, CScriptLayer *pLayer, CScriptTemp mpProperties = nullptr; mpTemplate->AddObject(this); mpDisplayModel = nullptr; + mpBillboard = nullptr; mpCollision = nullptr; } @@ -37,6 +38,7 @@ void CScriptObject::EvaluateProperties() mpLightParameters = mpTemplate->FindLightParameters(mpProperties); mVolumeShape = mpTemplate->VolumeShape(this); EvaluateDisplayModel(); + EvaluateBillboard(); EvaluateCollisionModel(); } @@ -46,6 +48,12 @@ void CScriptObject::EvaluateDisplayModel() mModelToken = CToken(mpDisplayModel); } +void CScriptObject::EvaluateBillboard() +{ + mpBillboard = mpTemplate->FindBillboardTexture(mpProperties); + mBillboardToken = CToken(mpBillboard); +} + void CScriptObject::EvaluateCollisionModel() { mpCollision = mpTemplate->FindCollision(mpProperties); @@ -198,6 +206,11 @@ CModel* CScriptObject::GetDisplayModel() const return mpDisplayModel; } +CTexture* CScriptObject::GetBillboard() const +{ + return mpBillboard; +} + CCollisionMeshGroup* CScriptObject::GetCollision() const { return mpCollision; diff --git a/Resource/script/CScriptObject.h b/Resource/script/CScriptObject.h index 7e803200..af44a6d4 100644 --- a/Resource/script/CScriptObject.h +++ b/Resource/script/CScriptObject.h @@ -32,8 +32,10 @@ class CScriptObject CBoolProperty *mpActive; CPropertyStruct *mpLightParameters; CModel *mpDisplayModel; + CTexture *mpBillboard; CCollisionMeshGroup *mpCollision; CToken mModelToken; + CToken mBillboardToken; CToken mCollisionToken; EVolumeShape mVolumeShape; @@ -44,6 +46,7 @@ public: void CopyFromTemplate(CScriptTemplate *pTemp, u32 propCount); void EvaluateProperties(); void EvaluateDisplayModel(); + void EvaluateBillboard(); void EvaluateCollisionModel(); CScriptTemplate* Template() const; @@ -73,6 +76,7 @@ public: void SetActive(bool isActive); CPropertyStruct* LightParameters() const; CModel* GetDisplayModel() const; + CTexture* GetBillboard() const; CCollisionMeshGroup* GetCollision() const; EVolumeShape VolumeShape() const; }; diff --git a/Resource/script/CScriptTemplate.cpp b/Resource/script/CScriptTemplate.cpp index aa0a616d..0dcb5c11 100644 --- a/Resource/script/CScriptTemplate.cpp +++ b/Resource/script/CScriptTemplate.cpp @@ -210,10 +210,12 @@ CPropertyStruct* CScriptTemplate::FindLightParameters(CPropertyStruct *pProperti return TFetchProperty(pProperties, mLightParametersIDString); } +// todo: merge these three functions, they have near-identical code CModel* CScriptTemplate::FindDisplayModel(CPropertyStruct *pProperties) { for (auto it = mAssets.begin(); it != mAssets.end(); it++) { + if ((it->AssetType != SEditorAsset::eModel) && (it->AssetType != SEditorAsset::eAnimParams)) continue; CResource *pRes = nullptr; // File @@ -249,10 +251,45 @@ CModel* CScriptTemplate::FindDisplayModel(CPropertyStruct *pProperties) return nullptr; } +CTexture* CScriptTemplate::FindBillboardTexture(CPropertyStruct *pProperties) +{ + for (auto it = mAssets.begin(); it != mAssets.end(); it++) + { + if (it->AssetType != SEditorAsset::eBillboard) continue; + CResource *pRes = nullptr; + + // File + if (it->AssetSource == SEditorAsset::eFile) + { + TString path = "../resources/" + it->AssetLocation; + pRes = gResCache.GetResource(path); + } + + // Property + else + { + CPropertyBase *pProp = pProperties->PropertyByIDString(it->AssetLocation); + + if (pProp->Type() == eFileProperty) + { + CFileProperty *pFile = static_cast(pProp); + pRes = pFile->Get(); + } + } + + // Verify resource exists + is correct type + if (pRes && (pRes->Type() == eTexture)) + return static_cast(pRes); + } + + return nullptr; +} + CCollisionMeshGroup* CScriptTemplate::FindCollision(CPropertyStruct *pProperties) { for (auto it = mAssets.begin(); it != mAssets.end(); it++) { + if (it->AssetType != SEditorAsset::eCollision) continue; CResource *pRes = nullptr; // File diff --git a/Resource/script/CScriptTemplate.h b/Resource/script/CScriptTemplate.h index 454fcdfa..a316cd9c 100644 --- a/Resource/script/CScriptTemplate.h +++ b/Resource/script/CScriptTemplate.h @@ -48,7 +48,7 @@ private: struct SEditorAsset { enum { - eModel, eAnimParams, eCollision + eModel, eAnimParams, eBillboard, eCollision } AssetType; enum { @@ -115,6 +115,7 @@ public: CBoolProperty* FindActive(CPropertyStruct *pProperties); CPropertyStruct* FindLightParameters(CPropertyStruct *pProperties); CModel* FindDisplayModel(CPropertyStruct *pProperties); + CTexture* FindBillboardTexture(CPropertyStruct *pProperties); CCollisionMeshGroup* FindCollision(CPropertyStruct *pProperties); bool HasPosition(); diff --git a/Scene/CLightNode.cpp b/Scene/CLightNode.cpp index 88dd18df..a81432cc 100644 --- a/Scene/CLightNode.cpp +++ b/Scene/CLightNode.cpp @@ -29,27 +29,11 @@ void CLightNode::AddToRenderer(CRenderer *pRenderer, const CFrustumPlanes& frust if (!frustum.BoxInFrustum(AABox())) return; pRenderer->AddOpaqueMesh(this, 0, CAABox(mPosition + 0.5f, mPosition - 0.5f), eDrawMesh); - - if (IsSelected()) - pRenderer->AddOpaqueMesh(this, 0, AABox(), eDrawSelection); } -void CLightNode::Draw(ERenderOptions) +void CLightNode::Draw(ERenderOptions /*Options*/) { - // Not using parameter 1 (ERenderOptions - Options) - glBlendFunc(GL_ONE, GL_ZERO); - glDepthMask(GL_TRUE); - LoadModelMatrix(); - CGraphics::SetDefaultLighting(); - CGraphics::UpdateLightBlock(); - CGraphics::sVertexBlock.COLOR0_Amb = CVector4f(1.f); - CGraphics::sVertexBlock.COLOR0_Mat = CVector4f(1.f); - CGraphics::UpdateVertexBlock(); - - // Force alpha to 0 to prevent issues with bloom - CColor cubeColor = mpLight->GetColor(); - cubeColor.a = 0; - CDrawUtil::DrawShadedCube(cubeColor); + CDrawUtil::DrawLightBillboard(mpLight->GetType(), mpLight->GetColor(), mPosition, mScale.xy() * CVector2f(0.75f), TintColor()); // Below commented-out code is for light radius visualization as a bounding box /*float r = mLight->GetRadius(); @@ -57,10 +41,8 @@ void CLightNode::Draw(ERenderOptions) pRenderer->DrawBoundingBox(mLight->GetColor(), AABB);*/ } -void CLightNode::DrawAsset(ERenderOptions, u32) +void CLightNode::DrawAsset(ERenderOptions /*Options*/, u32 /*asset*/) { - // Not using parameter 1 (ERenderOptions - Options) - // Not using parameter 2 (u32 - asset) } SRayIntersection CLightNode::RayNodeIntersectTest(const CRay& Ray, u32 /*AssetID*/, ERenderOptions options) diff --git a/Scene/CSceneNode.cpp b/Scene/CSceneNode.cpp index df2b76e6..de445927 100644 --- a/Scene/CSceneNode.cpp +++ b/Scene/CSceneNode.cpp @@ -10,6 +10,7 @@ #include u32 CSceneNode::smNumNodes = 0; +CColor CSceneNode::skSelectionTint((u8) 156, 133, 155, 255); CSceneNode::CSceneNode(CSceneManager *pScene, CSceneNode *pParent) { @@ -302,6 +303,12 @@ CSceneManager* CSceneNode::Scene() return mpScene; } +CColor CSceneNode::TintColor() const +{ + // convenience; this is/will be a fairly common operation + return (IsSelected() ? skSelectionTint : CColor::skWhite); +} + CVector3f CSceneNode::LocalPosition() const { return mPosition; diff --git a/Scene/CSceneNode.h b/Scene/CSceneNode.h index 669fa604..61bcb500 100644 --- a/Scene/CSceneNode.h +++ b/Scene/CSceneNode.h @@ -83,6 +83,7 @@ public: TString Name() const; CSceneNode* Parent() const; CSceneManager* Scene(); + CColor TintColor() const; CVector3f LocalPosition() const; CVector3f AbsolutePosition() const; CQuaternion LocalRotation() const; @@ -112,6 +113,7 @@ public: // Static static int NumNodes(); + static CColor skSelectionTint; }; // ************ INLINE FUNCTIONS ************ diff --git a/Scene/CScriptNode.cpp b/Scene/CScriptNode.cpp index 0dfefbaa..9a1e40c2 100644 --- a/Scene/CScriptNode.cpp +++ b/Scene/CScriptNode.cpp @@ -17,6 +17,7 @@ CScriptNode::CScriptNode(CSceneManager *pScene, CSceneNode *pParent, CScriptObje // Evaluate instance mpInstance = pObject; mpActiveModel = nullptr; + mpBillboard = nullptr; mpCollisionNode = new CCollisionNode(pScene, this); mpCollisionNode->SetInheritance(true, true, false); @@ -38,6 +39,9 @@ CScriptNode::CScriptNode(CSceneManager *pScene, CSceneNode *pParent, CScriptObje mpActiveModel = mpInstance->GetDisplayModel(); mModelToken = CToken(mpActiveModel); + mpBillboard = mpInstance->GetBillboard(); + mBillboardToken = CToken(mpBillboard); + mpCollisionNode->SetCollision(mpInstance->GetCollision()); // Create preview volume node @@ -156,24 +160,33 @@ void CScriptNode::Draw(ERenderOptions Options) { if (!mpInstance) return; - // Set lighting - LoadModelMatrix(); - LoadLights(); + // Draw model + if (mpActiveModel) + { + LoadModelMatrix(); + LoadLights(); + mpActiveModel->Draw(Options, 0); + } - // Default to drawing purple box if no model - if (!mpActiveModel) + // Draw billboard + else if (mpBillboard) + { + CDrawUtil::DrawBillboard(mpBillboard, mPosition, mScale.xy() * CVector2f(0.75f), TintColor()); + } + + // If no model or billboard, default to drawing a purple box + else { glBlendFuncSeparate(GL_ONE, GL_ZERO, GL_ZERO, GL_ZERO); glDepthMask(GL_TRUE); LoadModelMatrix(); + LoadLights(); CGraphics::UpdateVertexBlock(); CGraphics::UpdateLightBlock(); CDrawUtil::DrawShadedCube(CColor::skTransparentPurple); return; } - - mpActiveModel->Draw(Options, 0); } void CScriptNode::DrawAsset(ERenderOptions Options, u32 Asset) @@ -195,7 +208,13 @@ void CScriptNode::DrawAsset(ERenderOptions Options, u32 Asset) void CScriptNode::DrawSelection() { glBlendFunc(GL_ONE, GL_ZERO); - CDrawUtil::DrawWireCube(AABox(), CColor::skTransparentWhite); + + // Only draw bounding box for models; billboards get a tint color + if (mpActiveModel || !mpBillboard) + { + LoadModelMatrix(); + CDrawUtil::DrawWireCube(AABox(), CColor::skTransparentWhite); + } if (mpInstance) { diff --git a/Scene/CScriptNode.h b/Scene/CScriptNode.h index fd395f8f..3ea6efbb 100644 --- a/Scene/CScriptNode.h +++ b/Scene/CScriptNode.h @@ -11,7 +11,9 @@ class CScriptNode : public CSceneNode { CScriptObject *mpInstance; CModel *mpActiveModel; + CTexture *mpBillboard; CToken mModelToken; + CToken mBillboardToken; CCollisionNode *mpCollisionNode; bool mHasValidPosition;