#include "CDrawUtil.h" #include "CGraphics.h" #include "Core/GameProject/CResourceStore.h" #include #include // ************ PUBLIC ************ void CDrawUtil::DrawGrid(CColor LineColor, CColor BoldLineColor) { Init(); mGridVertices->Bind(); CGraphics::sMVPBlock.ModelMatrix = CMatrix4f::skIdentity; CGraphics::UpdateMVPBlock(); glBlendFunc(GL_ONE, GL_ZERO); glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); glDepthMask(GL_TRUE); glLineWidth(1.0f); LineColor.A = 0.f; UseColorShader(LineColor); mGridIndices.DrawElements(0, mGridIndices.GetSize() - 4); glLineWidth(1.5f); BoldLineColor.A = 0.f; UseColorShader(BoldLineColor); mGridIndices.DrawElements(mGridIndices.GetSize() - 4, 4); } void CDrawUtil::DrawSquare() { // Overload with default tex coords CVector2f TexCoords[4] = { CVector2f(0.f, 1.f), CVector2f(1.f, 1.f), CVector2f(1.f, 0.f), CVector2f(0.f, 0.f) }; DrawSquare(&TexCoords[0].X); } void CDrawUtil::DrawSquare(const CVector2f& TexUL, const CVector2f& TexUR, const CVector2f& TexBR, const CVector2f& TexBL) { // Overload with tex coords specified via parameters // I don't think that parameters are guaranteed to be contiguous in memory, so: CVector2f TexCoords[4] = { TexUL, TexUR, TexBR, TexBL }; DrawSquare(&TexCoords[0].X); } void CDrawUtil::DrawSquare(const float *pTexCoords) { Init(); // Set tex coords for (uint32 iTex = 0; iTex < 8; iTex++) { EVertexAttribute TexAttrib = (EVertexAttribute) ((uint) (EVertexAttribute::Tex0) << iTex); mSquareVertices->BufferAttrib(TexAttrib, pTexCoords); } // Draw mSquareVertices->Bind(); mSquareIndices.DrawElements(); mSquareVertices->Unbind(); } void CDrawUtil::DrawLine(const CVector3f& PointA, const CVector3f& PointB) { DrawLine(PointA, PointB, CColor::White()); } void CDrawUtil::DrawLine(const CVector2f& PointA, const CVector2f& PointB) { // Overload for 2D lines DrawLine(CVector3f(PointA.X, PointA.Y, 0.f), CVector3f(PointB.X, PointB.Y, 0.f), CColor::White()); } void CDrawUtil::DrawLine(const CVector3f& PointA, const CVector3f& PointB, const CColor& LineColor) { Init(); // Copy vec3s into an array to ensure they are adjacent in memory CVector3f Points[2] = { PointA, PointB }; mLineVertices->BufferAttrib(EVertexAttribute::Position, Points); // Draw UseColorShader(LineColor); mLineVertices->Bind(); mLineIndices.DrawElements(); mLineVertices->Unbind(); } void CDrawUtil::DrawLine(const CVector2f& PointA, const CVector2f& PointB, const CColor& LineColor) { // Overload for 2D lines DrawLine(CVector3f(PointA.X, PointA.Y, 0.f), CVector3f(PointB.X, PointB.Y, 0.f), LineColor); } void CDrawUtil::DrawCube() { Init(); mpCubeModel->Draw(ERenderOption::NoMaterialSetup, 0); } void CDrawUtil::DrawCube(const CColor& Color) { Init(); UseColorShader(Color); DrawCube(); } void CDrawUtil::DrawCube(const CVector3f& Position, const CColor& Color) { CGraphics::sMVPBlock.ModelMatrix = CTransform4f::TranslationMatrix(Position); CGraphics::UpdateMVPBlock(); UseColorShader(Color); DrawCube(); } void CDrawUtil::DrawShadedCube(const CColor& Color) { Init(); UseColorShaderLighting(Color); DrawCube(); } void CDrawUtil::DrawWireCube() { Init(); glLineWidth(1.f); mWireCubeVertices->Bind(); mWireCubeIndices.DrawElements(); mWireCubeVertices->Unbind(); } void CDrawUtil::DrawWireCube(const CAABox& kAABox, const CColor& kColor) { Init(); // Calculate model matrix CTransform4f Transform; Transform.Scale(kAABox.Size()); Transform.Translate(kAABox.Center()); CGraphics::sMVPBlock.ModelMatrix = Transform; CGraphics::UpdateMVPBlock(); UseColorShader(kColor); DrawWireCube(); } void CDrawUtil::DrawSphere(bool DoubleSided) { Init(); if (!DoubleSided) mpSphereModel->Draw(ERenderOption::NoMaterialSetup, 0); else mpDoubleSidedSphereModel->Draw(ERenderOption::NoMaterialSetup, 0); } void CDrawUtil::DrawSphere(const CColor &kColor) { Init(); UseColorShader(kColor); DrawSphere(false); } void CDrawUtil::DrawWireSphere(const CVector3f& Position, float Radius, const CColor& Color /*= CColor::skWhite*/) { Init(); // Create model matrix CTransform4f Transform; Transform.Scale(Radius); Transform.Translate(Position); CGraphics::sMVPBlock.ModelMatrix = Transform; CGraphics::UpdateMVPBlock(); // Set other render params UseColorShader(Color); CMaterial::KillCachedMaterial(); glBlendFunc(GL_ONE, GL_ZERO); glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); glDepthMask(GL_TRUE); // Draw mpWireSphereModel->Draw(ERenderOption::NoMaterialSetup, 0); } 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); CGraphics::UpdateMVPBlock(); // Set uniforms mpBillboardShader->SetCurrent(); static GLuint ScaleLoc = mpBillboardShader->GetUniformLocation("BillboardScale"); glUniform2f(ScaleLoc, Scale.X, Scale.Y); static GLuint TintLoc = mpBillboardShader->GetUniformLocation("TintColor"); glUniform4f(TintLoc, Tint.R, Tint.G, Tint.B, Tint.A); 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); CGraphics::UpdateMVPBlock(); // Set uniforms mpLightBillboardShader->SetCurrent(); static GLuint ScaleLoc = mpLightBillboardShader->GetUniformLocation("BillboardScale"); glUniform2f(ScaleLoc, Scale.X, Scale.Y); static GLuint ColorLoc = mpLightBillboardShader->GetUniformLocation("LightColor"); glUniform4f(ColorLoc, LightColor.R, LightColor.G, LightColor.B, LightColor.A); static GLuint TintLoc = mpLightBillboardShader->GetUniformLocation("TintColor"); glUniform4f(TintLoc, Tint.R, Tint.G, Tint.B, Tint.A); CTexture *pTexA = GetLightTexture(Type); CTexture *pTexB = GetLightMask(Type); pTexA->Bind(0); pTexB->Bind(1); static GLuint TextureLoc = mpLightBillboardShader->GetUniformLocation("Texture"); static 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(); mpColorShader->SetCurrent(); static GLuint ColorLoc = mpColorShader->GetUniformLocation("ColorIn"); glUniform4f(ColorLoc, kColor.R, kColor.G, kColor.B, kColor.A); CMaterial::KillCachedMaterial(); } void CDrawUtil::UseColorShaderLighting(const CColor& kColor) { Init(); mpColorShaderLighting->SetCurrent(); static GLuint NumLightsLoc = mpColorShaderLighting->GetUniformLocation("NumLights"); glUniform1i(NumLightsLoc, CGraphics::sNumLights); static GLuint ColorLoc = mpColorShaderLighting->GetUniformLocation("ColorIn"); glUniform4f(ColorLoc, kColor.R, kColor.G, kColor.B, kColor.A); CMaterial::KillCachedMaterial(); } void CDrawUtil::UseTextureShader() { UseTextureShader(CColor::White()); } void CDrawUtil::UseTextureShader(const CColor& TintColor) { Init(); mpTextureShader->SetCurrent(); static GLuint TintColorLoc = mpTextureShader->GetUniformLocation("TintColor"); glUniform4f(TintColorLoc, TintColor.R, TintColor.G, TintColor.B, TintColor.A); CMaterial::KillCachedMaterial(); } void CDrawUtil::UseCollisionShader(bool IsFloor, bool IsUnstandable, const CColor& TintColor /*= CColor::skWhite*/) { Init(); mpCollisionShader->SetCurrent(); // Force blend mode to opaque + set alpha to 0 to ensure collision geometry isn't bloomed glBlendFuncSeparate(GL_ONE, GL_ZERO, GL_ZERO, GL_ZERO); static GLuint TintColorLoc = mpCollisionShader->GetUniformLocation("TintColor"); glUniform4f(TintColorLoc, TintColor.R, TintColor.G, TintColor.B, TintColor.A); static GLuint IsFloorLoc = mpCollisionShader->GetUniformLocation("IsFloor"); glUniform1i(IsFloorLoc, IsFloor ? 1 : 0); static GLuint IsUnstandableLoc = mpCollisionShader->GetUniformLocation("IsUnstandable"); glUniform1i(IsUnstandableLoc, IsUnstandable ? 1 : 0); CMaterial::KillCachedMaterial(); } CShader* CDrawUtil::GetTextShader() { Init(); return mpTextShader.get(); } void CDrawUtil::LoadCheckerboardTexture(uint32 GLTextureUnit) { Init(); mpCheckerTexture->Bind(GLTextureUnit); } CTexture* CDrawUtil::GetLightTexture(ELightType Type) { Init(); return mpLightTextures[static_cast(Type)]; } CTexture* CDrawUtil::GetLightMask(ELightType Type) { Init(); return mpLightMasks[static_cast(Type)]; } CModel* CDrawUtil::GetCubeModel() { Init(); return mpCubeModel; } // ************ PRIVATE ************ CDrawUtil::CDrawUtil() { } void CDrawUtil::Init() { if (!mDrawUtilInitialized) { debugf("Initializing CDrawUtil"); InitGrid(); InitSquare(); InitLine(); InitCube(); InitWireCube(); InitSphere(); InitWireSphere(); InitShaders(); InitTextures(); mDrawUtilInitialized = true; } } void CDrawUtil::InitGrid() { debugf("Creating grid"); const int kGridSize = 501; // must be odd const float kGridSpacing = 1.f; const int MinIdx = (kGridSize - 1) / -2; const int MaxIdx = (kGridSize - 1) / 2; mGridVertices.emplace(); mGridVertices->SetVertexDesc(EVertexAttribute::Position); mGridVertices->Reserve(static_cast(kGridSize * 4)); for (int32 i = MinIdx; i <= MaxIdx; i++) { if (i == 0) continue; mGridVertices->AddVertex(CVector3f(MinIdx * kGridSpacing, i * kGridSpacing, 0.0f)); mGridVertices->AddVertex(CVector3f(MaxIdx * kGridSpacing, i * kGridSpacing, 0.0f)); mGridVertices->AddVertex(CVector3f(i * kGridSpacing, MinIdx * kGridSpacing, 0.0f)); mGridVertices->AddVertex(CVector3f(i * kGridSpacing, MaxIdx * kGridSpacing, 0.0f)); } mGridVertices->AddVertex(CVector3f(MinIdx * kGridSpacing, 0, 0.0f)); mGridVertices->AddVertex(CVector3f(MaxIdx * kGridSpacing, 0, 0.0f)); mGridVertices->AddVertex(CVector3f(0, MinIdx * kGridSpacing, 0.0f)); mGridVertices->AddVertex(CVector3f(0, MaxIdx * kGridSpacing, 0.0f)); const auto NumIndices = static_cast(kGridSize * 4); mGridIndices.Reserve(NumIndices); for (uint16 i = 0; i < NumIndices; i++) mGridIndices.AddIndex(i); mGridIndices.SetPrimitiveType(GL_LINES); } void CDrawUtil::InitSquare() { debugf("Creating square"); mSquareVertices.emplace(); mSquareVertices->SetActiveAttribs(EVertexAttribute::Position | EVertexAttribute::Normal | EVertexAttribute::Tex0 | EVertexAttribute::Tex1 | EVertexAttribute::Tex2 | EVertexAttribute::Tex3 | EVertexAttribute::Tex4 | EVertexAttribute::Tex5 | EVertexAttribute::Tex6 | EVertexAttribute::Tex7); mSquareVertices->SetVertexCount(4); static constexpr std::array SquareVertices{ CVector3f(-1.f, 1.f, 0.f), CVector3f( 1.f, 1.f, 0.f), CVector3f( 1.f, -1.f, 0.f), CVector3f(-1.f, -1.f, 0.f) }; static constexpr std::array SquareNormals{ CVector3f(0.f, 0.f, 1.f), CVector3f(0.f, 0.f, 1.f), CVector3f(0.f, 0.f, 1.f), CVector3f(0.f, 0.f, 1.f) }; static constexpr std::array SquareTexCoords{ CVector2f(0.f, 1.f), CVector2f(1.f, 1.f), CVector2f(1.f, 0.f), CVector2f(0.f, 0.f) }; mSquareVertices->BufferAttrib(EVertexAttribute::Position, SquareVertices.data()); mSquareVertices->BufferAttrib(EVertexAttribute::Normal, SquareNormals.data()); for (uint32 iTex = 0; iTex < 8; iTex++) { const auto Attrib = static_cast(EVertexAttribute::Tex0 << iTex); mSquareVertices->BufferAttrib(Attrib, SquareTexCoords.data()); } mSquareIndices.Reserve(4); mSquareIndices.SetPrimitiveType(GL_TRIANGLE_STRIP); mSquareIndices.AddIndex(3); mSquareIndices.AddIndex(2); mSquareIndices.AddIndex(0); mSquareIndices.AddIndex(1); } void CDrawUtil::InitLine() { debugf("Creating line"); mLineVertices.emplace(); mLineVertices->SetActiveAttribs(EVertexAttribute::Position); mLineVertices->SetVertexCount(2); mLineIndices.Reserve(2); mLineIndices.SetPrimitiveType(GL_LINES); mLineIndices.AddIndex(0); mLineIndices.AddIndex(1); } void CDrawUtil::InitCube() { debugf("Creating cube"); mpCubeModel = gpEditorStore->LoadResource("Cube.CMDL"); } void CDrawUtil::InitWireCube() { debugf("Creating wire cube"); mWireCubeVertices.emplace(); mWireCubeVertices->SetVertexDesc(EVertexAttribute::Position); mWireCubeVertices->Reserve(8); mWireCubeVertices->AddVertex(CVector3f(-0.5f, -0.5f, -0.5f)); mWireCubeVertices->AddVertex(CVector3f(-0.5f, 0.5f, -0.5f)); mWireCubeVertices->AddVertex(CVector3f( 0.5f, 0.5f, -0.5f)); mWireCubeVertices->AddVertex(CVector3f( 0.5f, -0.5f, -0.5f)); mWireCubeVertices->AddVertex(CVector3f(-0.5f, -0.5f, 0.5f)); mWireCubeVertices->AddVertex(CVector3f( 0.5f, -0.5f, 0.5f)); mWireCubeVertices->AddVertex(CVector3f( 0.5f, 0.5f, 0.5f)); mWireCubeVertices->AddVertex(CVector3f(-0.5f, 0.5f, 0.5f)); static constexpr std::array Indices{ 0, 1, 1, 2, 2, 3, 3, 0, 4, 5, 5, 6, 6, 7, 7, 4, 0, 4, 1, 7, 2, 6, 3, 5 }; mWireCubeIndices.AddIndices(Indices.data(), Indices.size()); mWireCubeIndices.SetPrimitiveType(GL_LINES); } void CDrawUtil::InitSphere() { debugf("Creating sphere"); mpSphereModel = gpEditorStore->LoadResource("Sphere.CMDL"); mpDoubleSidedSphereModel = gpEditorStore->LoadResource("SphereDoubleSided.CMDL"); } void CDrawUtil::InitWireSphere() { debugf("Creating wire sphere"); mpWireSphereModel = gpEditorStore->LoadResource("WireSphere.CMDL"); } void CDrawUtil::InitShaders() { debugf("Creating shaders"); 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() { debugf("Loading textures"); mpCheckerTexture = gpEditorStore->LoadResource("Checkerboard.TXTR"); mpLightTextures[0] = gpEditorStore->LoadResource("LightAmbient.TXTR"); mpLightTextures[1] = gpEditorStore->LoadResource("LightDirectional.TXTR"); mpLightTextures[2] = gpEditorStore->LoadResource("LightCustom.TXTR"); mpLightTextures[3] = gpEditorStore->LoadResource("LightSpot.TXTR"); mpLightMasks[0] = gpEditorStore->LoadResource("LightAmbientMask.TXTR"); mpLightMasks[1] = gpEditorStore->LoadResource("LightDirectionalMask.TXTR"); mpLightMasks[2] = gpEditorStore->LoadResource("LightCustomMask.TXTR"); mpLightMasks[3] = gpEditorStore->LoadResource("LightSpotMask.TXTR"); } void CDrawUtil::Shutdown() { if (!mDrawUtilInitialized) return; debugf("Shutting down"); mGridVertices.reset(); mSquareVertices.reset(); mLineVertices.reset(); mWireCubeVertices.reset(); mpColorShader.reset(); mpColorShaderLighting.reset(); mpTextureShader.reset(); mpCollisionShader.reset(); mpTextShader.reset(); mDrawUtilInitialized = false; }