Added support for model skinning

This commit is contained in:
parax0 2016-04-27 04:27:57 -06:00
parent c5ff634cd1
commit 98059cedaa
81 changed files with 802 additions and 245 deletions

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -1,6 +1,7 @@
#ifndef ASSERT_H
#define ASSERT_H
#include "Log.h"
#include "TString.h"
#include <cstdlib>
#include <string.h>

View File

@ -47,7 +47,7 @@ void CColor::SetIntegral(u8 _R, u8 _G, u8 _B, u8 _A /*= 255*/)
A = _A / 255.f;
}
void CColor::Write(IOutputStream &rOutput, bool Integral /*= false*/)
void CColor::Write(IOutputStream &rOutput, bool Integral /*= false*/) const
{
if (Integral)
{

View File

@ -16,7 +16,7 @@ public:
CColor(float _R, float _G, float _B, float A = 1.f);
void SetIntegral(u8 RGBA);
void SetIntegral(u8 _R, u8 _G, u8 _B, u8 _A = 255);
void Write(IOutputStream& rOutput, bool Integral = false);
void Write(IOutputStream& rOutput, bool Integral = false) const;
long ToLongRGBA() const;
long ToLongARGB() const;

View File

@ -68,7 +68,8 @@ HEADERS += \
Flags.h \
TString.h \
types.h \
Log.h
Log.h \
Assert.h
# Source Files
SOURCES += \

View File

@ -29,7 +29,9 @@ public:
inline TFlags operator&(u32 Mask) const { return TFlags(FlagEnum(mValue & Mask)); }
inline TFlags operator&(FlagEnum Flag) const { return TFlags(FlagEnum(mValue & Flag)); }
inline bool HasFlag(FlagEnum Flag) const { return ((mValue & Flag) != 0); }
inline bool HasAnyFlags(TFlags Flags) const { return ((mValue & Flags) != 0); }
inline bool HasAllFlags(TFlags Flags) const { return ((mValue & Flags) == Flags); }
};
#define DECLARE_FLAGS(Enum, FlagTypeName) typedef TFlags<Enum> FlagTypeName;

View File

@ -93,6 +93,13 @@ void Warning(const TString& rkMessage)
gErrorLog.push_back(FullMessage);
}
void Fatal(const TString& rkMessage)
{
TString FullMessage = "FATAL ERROR: " + rkMessage;
Write(FullMessage);
abort();
}
void FileWrite(const TString& rkFilename, const TString& rkMessage)
{
Write(rkFilename + " : " + rkMessage);

View File

@ -10,6 +10,7 @@ bool InitLog(const TString& rkFilename);
void Write(const TString& rkMessage);
void Error(const TString& rkMessage);
void Warning(const TString& rkMessage);
void Fatal(const TString& rkMessage);
void FileWrite(const TString& rkFilename, const TString& rkMessage);
void FileWrite(const TString& rkFilename, unsigned long Offset, const TString& rkMessage);
void FileError(const TString& rkFilename, const TString& rkMessage);

View File

@ -191,7 +191,9 @@ HEADERS += \
Scene/CCharacterNode.h \
Resource/CAnimation.h \
Resource/Factory/CAnimationLoader.h \
Render/CBoneTransformData.h
Render/CBoneTransformData.h \
Resource/CSkin.h \
Resource/Factory/CSkinLoader.h
# Source Files
SOURCES += \
@ -274,4 +276,6 @@ SOURCES += \
Resource/Factory/CSkeletonLoader.cpp \
Scene/CCharacterNode.cpp \
Resource/CAnimation.cpp \
Resource/Factory/CAnimationLoader.cpp
Resource/Factory/CAnimationLoader.cpp \
Resource/Factory/CSkinLoader.cpp \
Resource/Model/EVertexAttribute.cpp

View File

@ -157,6 +157,7 @@ bool CShader::LinkShaders()
mVertexBlockIndex = GetUniformBlockIndex("VertexBlock");
mPixelBlockIndex = GetUniformBlockIndex("PixelBlock");
mLightBlockIndex = GetUniformBlockIndex("LightBlock");
mBoneTransformBlockIndex = GetUniformBlockIndex("BoneTransformBlock");
mProgramExists = true;
return true;
@ -193,6 +194,7 @@ void CShader::SetCurrent()
glUniformBlockBinding(mProgram, mVertexBlockIndex, CGraphics::VertexBlockBindingPoint());
glUniformBlockBinding(mProgram, mPixelBlockIndex, CGraphics::PixelBlockBindingPoint());
glUniformBlockBinding(mProgram, mLightBlockIndex, CGraphics::LightBlockBindingPoint());
glUniformBlockBinding(mProgram, mBoneTransformBlockIndex, CGraphics::BoneTransformBlockBindingPoint());
}
}

View File

@ -17,6 +17,7 @@ class CShader
GLuint mVertexBlockIndex;
GLuint mPixelBlockIndex;
GLuint mLightBlockIndex;
GLuint mBoneTransformBlockIndex;
static CShader* spCurrentShader;

View File

@ -1,12 +1,13 @@
#include "CShaderGenerator.h"
#include <Common/Assert.h>
#include <iostream>
#include <fstream>
#include <sstream>
#include <GL/glew.h>
const TString gkCoordSrc[] = {
"RawPosition.xyz",
"RawNormal.xyz",
"ModelSpacePos.xyz",
"ModelSpaceNormal.xyz",
"0.0, 0.0, 0.0",
"0.0, 0.0, 0.0",
"RawTex0.xy, 1.0",
@ -144,7 +145,7 @@ CShaderGenerator::~CShaderGenerator()
{
}
bool CShaderGenerator::CreateVertexShader(const CMaterial& Mat)
bool CShaderGenerator::CreateVertexShader(const CMaterial& rkMat)
{
std::stringstream ShaderCode;
@ -153,126 +154,188 @@ bool CShaderGenerator::CreateVertexShader(const CMaterial& Mat)
// Input
ShaderCode << "// Input\n";
FVertexDescription VtxDesc = Mat.VtxDesc();
if (VtxDesc & ePosition) ShaderCode << "layout(location = 0) in vec3 RawPosition;\n";
if (VtxDesc & eNormal) ShaderCode << "layout(location = 1) in vec3 RawNormal;\n";
if (VtxDesc & eColor0) ShaderCode << "layout(location = 2) in vec4 RawColor0;\n";
if (VtxDesc & eColor1) ShaderCode << "layout(location = 3) in vec4 RawColor1;\n";
if (VtxDesc & eTex0) ShaderCode << "layout(location = 4) in vec2 RawTex0;\n";
if (VtxDesc & eTex1) ShaderCode << "layout(location = 5) in vec2 RawTex1;\n";
if (VtxDesc & eTex2) ShaderCode << "layout(location = 6) in vec2 RawTex2;\n";
if (VtxDesc & eTex3) ShaderCode << "layout(location = 7) in vec2 RawTex3;\n";
if (VtxDesc & eTex4) ShaderCode << "layout(location = 8) in vec2 RawTex4;\n";
if (VtxDesc & eTex5) ShaderCode << "layout(location = 9) in vec2 RawTex5;\n";
if (VtxDesc & eTex6) ShaderCode << "layout(location = 10) in vec2 RawTex6;\n";
FVertexDescription VtxDesc = rkMat.VtxDesc();
ASSERT(VtxDesc & ePosition);
ShaderCode << "layout(location = 0) in vec3 RawPosition;\n";
if (VtxDesc & eNormal) ShaderCode << "layout(location = 1) in vec3 RawNormal;\n";
if (VtxDesc & eColor0) ShaderCode << "layout(location = 2) in vec4 RawColor0;\n";
if (VtxDesc & eColor1) ShaderCode << "layout(location = 3) in vec4 RawColor1;\n";
if (VtxDesc & eTex0) ShaderCode << "layout(location = 4) in vec2 RawTex0;\n";
if (VtxDesc & eTex1) ShaderCode << "layout(location = 5) in vec2 RawTex1;\n";
if (VtxDesc & eTex2) ShaderCode << "layout(location = 6) in vec2 RawTex2;\n";
if (VtxDesc & eTex3) ShaderCode << "layout(location = 7) in vec2 RawTex3;\n";
if (VtxDesc & eTex4) ShaderCode << "layout(location = 8) in vec2 RawTex4;\n";
if (VtxDesc & eTex5) ShaderCode << "layout(location = 9) in vec2 RawTex5;\n";
if (VtxDesc & eTex6) ShaderCode << "layout(location = 10) in vec2 RawTex6;\n";
if (VtxDesc & eTex7) ShaderCode << "layout(location = 11) in vec2 RawTex7;\n";
if (VtxDesc & eBoneIndices) ShaderCode << "layout(location = 12) in ivec4 BoneIndices;\n";
if (VtxDesc & eBoneWeights) ShaderCode << "layout(location = 13) in vec4 BoneWeights;\n";
ShaderCode << "\n";
// Output
ShaderCode << "// Output\n";
if (VtxDesc & eNormal) ShaderCode << "out vec3 Normal;\n";
if (VtxDesc & eColor0) ShaderCode << "out vec4 Color0;\n";
if (VtxDesc & eColor1) ShaderCode << "out vec4 Color1;\n";
if (VtxDesc & eNormal) ShaderCode << "out vec3 Normal;\n";
if (VtxDesc & eColor0) ShaderCode << "out vec4 Color0;\n";
if (VtxDesc & eColor1) ShaderCode << "out vec4 Color1;\n";
for (u32 iPass = 0; iPass < Mat.PassCount(); iPass++)
if (Mat.Pass(iPass)->TexCoordSource() != 0xFF)
for (u32 iPass = 0; iPass < rkMat.PassCount(); iPass++)
if (rkMat.Pass(iPass)->TexCoordSource() != 0xFF)
ShaderCode << "out vec3 Tex" << iPass << ";\n";
ShaderCode << "out vec4 COLOR0A0;\n"
<< "out vec4 COLOR1A1;\n";
ShaderCode << "\n";
ShaderCode << "out vec4 COLOR0A0;\n"
<< "out vec4 COLOR1A1;\n";
ShaderCode << "\n";
// Uniforms
ShaderCode << "// Uniforms\n"
<< "layout(std140) uniform MVPBlock\n"
<< "{\n"
<< " mat4 ModelMtx;\n"
<< " mat4 ViewMtx;\n"
<< " mat4 ProjMtx;\n"
<< "};\n"
<< "\n"
<< "layout(std140) uniform VertexBlock\n"
<< "{\n"
<< " mat4 TexMtx[10];\n"
<< " mat4 PostMtx[20];\n"
<< " vec4 COLOR0_Amb;\n"
<< " vec4 COLOR0_Mat;\n"
<< " vec4 COLOR1_Amb;\n"
<< " vec4 COLOR1_Mat;\n"
<< "};\n"
<< "\n"
<< "struct GXLight\n"
<< "{\n"
<< " vec4 Position;\n"
<< " vec4 Direction;\n"
<< " vec4 Color;\n"
<< " vec4 DistAtten;\n"
<< " vec4 AngleAtten;\n"
<< "};\n"
<< "layout(std140) uniform LightBlock {\n"
<< " GXLight Lights[8];\n"
<< "};\n"
<< "uniform int NumLights;\n"
<< "\n";
ShaderCode << "// Uniforms\n"
<< "layout(std140) uniform MVPBlock\n"
<< "{\n"
<< " mat4 ModelMtx;\n"
<< " mat4 ViewMtx;\n"
<< " mat4 ProjMtx;\n"
<< "};\n"
<< "\n"
<< "layout(std140) uniform VertexBlock\n"
<< "{\n"
<< " mat4 TexMtx[10];\n"
<< " mat4 PostMtx[20];\n"
<< " vec4 COLOR0_Amb;\n"
<< " vec4 COLOR0_Mat;\n"
<< " vec4 COLOR1_Amb;\n"
<< " vec4 COLOR1_Mat;\n"
<< "};\n"
<< "\n"
<< "struct GXLight\n"
<< "{\n"
<< " vec4 Position;\n"
<< " vec4 Direction;\n"
<< " vec4 Color;\n"
<< " vec4 DistAtten;\n"
<< " vec4 AngleAtten;\n"
<< "};\n"
<< "\n"
<< "layout(std140) uniform LightBlock\n"
<< "{\n"
<< " GXLight Lights[8];\n"
<< "};\n"
<< "uniform int NumLights;\n"
<< "\n";
if (rkMat.Options() & CMaterial::eSkinningEnabled)
{
ShaderCode << "layout(std140) uniform BoneTransformBlock\n"
<< "{\n"
<< " mat4 BoneTransforms[100];\n"
<< " mat4 InverseBindTransforms[100];\n"
<< "};\n"
<< "\n";
}
// Main
ShaderCode << "// Main\n"
<< "void main()\n"
<< "{\n"
<< " mat4 MV = ModelMtx * ViewMtx;\n"
<< " mat4 MVP = MV * ProjMtx;\n";
ShaderCode << "// Main\n"
<< "void main()\n"
<< "{\n"
<< " mat4 MV = ModelMtx * ViewMtx;\n"
<< " mat4 MVP = MV * ProjMtx;\n";
if (VtxDesc & ePosition) ShaderCode << " gl_Position = vec4(RawPosition, 1) * MVP;\n";
if (VtxDesc & eNormal) ShaderCode << " Normal = normalize(RawNormal.xyz * inverse(transpose(mat3(MV))));\n";
if (VtxDesc & eColor0) ShaderCode << " Color0 = RawColor0;\n";
if (VtxDesc & eColor1) ShaderCode << " Color1 = RawColor1;\n";
ShaderCode << "\n";
// Skinning
if (rkMat.Options() & CMaterial::eSkinningEnabled)
{
ShaderCode << " // Skinning\n"
<< " vec3 ModelSpacePos = vec3(0,0,0);\n";
if (VtxDesc & eNormal)
ShaderCode << " vec3 ModelSpaceNormal = vec3(0,0,0);\n";
ShaderCode << " \n"
<< " for (int iBone = 0; iBone < 4; iBone++)\n"
<< " {\n"
<< " int BoneIdx = BoneIndices[iBone];\n"
<< " float Weight = BoneWeights[iBone];\n"
<< " \n"
<< " if (BoneIdx > 0)\n"
<< " {\n"
<< " mat4 BoneSpaceTransform = InverseBindTransforms[BoneIdx] * BoneTransforms[BoneIdx];\n"
<< " ModelSpacePos += vec3(vec4(RawPosition, 1) * BoneSpaceTransform * Weight);\n";
if (VtxDesc & eNormal)
ShaderCode << " ModelSpaceNormal += RawNormal.xyz * inverse(transpose(mat3(BoneSpaceTransform))) * Weight;\n";
ShaderCode << " }\n"
<< " }\n"
<< " \n";
if (VtxDesc & eNormal)
ShaderCode << " ModelSpaceNormal = normalize(ModelSpaceNormal);\n"
<< " \n";
}
else
{
ShaderCode << " vec3 ModelSpacePos = RawPosition;\n";
if (VtxDesc & eNormal)
ShaderCode << " vec3 ModelSpaceNormal = RawNormal.xyz;\n";
ShaderCode << "\n";
}
ShaderCode << " gl_Position = vec4(ModelSpacePos, 1) * MVP;\n";
if (VtxDesc & eNormal)
ShaderCode << " Normal = normalize(ModelSpaceNormal * inverse(transpose(mat3(MV))));\n";
// Per-vertex lighting
ShaderCode << "\n"
<< " // Dynamic Lighting\n";
ShaderCode << "\n"
<< " // Dynamic Lighting\n";
// This bit could do with some cleaning up
// It took a lot of experimentation to get dynamic lights working and I never went back and cleaned it up after
if (Mat.IsLightingEnabled())
if (rkMat.IsLightingEnabled())
{
ShaderCode << " vec4 Illum = vec4(0.0);\n"
<< " vec3 PositionMV = vec3(vec4(RawPosition, 1.0) * MV);\n"
<< " \n"
<< " for (int iLight = 0; iLight < NumLights; iLight++)\n"
<< " {\n"
<< " vec3 LightPosMV = vec3(Lights[iLight].Position * ViewMtx);\n"
<< " vec3 LightDirMV = normalize(Lights[iLight].Direction.xyz * inverse(transpose(mat3(ViewMtx))));\n"
<< " vec3 LightDist = LightPosMV.xyz - PositionMV.xyz;\n"
<< " float DistSquared = dot(LightDist, LightDist);\n"
<< " float Dist = sqrt(DistSquared);\n"
<< " LightDist /= Dist;\n"
<< " vec3 AngleAtten = Lights[iLight].AngleAtten.xyz;\n"
<< " AngleAtten = vec3(AngleAtten.x, AngleAtten.y, AngleAtten.z);\n"
<< " float Atten = max(0, dot(LightDist, LightDirMV.xyz));\n"
<< " Atten = max(0, dot(AngleAtten, vec3(1.0, Atten, Atten * Atten))) / dot(Lights[iLight].DistAtten.xyz, vec3(1.0, Dist, DistSquared));\n"
<< " float DiffuseAtten = max(0, dot(Normal, LightDist));\n"
<< " Illum += (Atten * DiffuseAtten * Lights[iLight].Color);\n"
<< " }\n"
<< " COLOR0A0 = COLOR0_Mat * (Illum + COLOR0_Amb);\n"
<< " COLOR1A1 = COLOR1_Mat * (Illum + COLOR1_Amb);\n"
<< " \n";
ShaderCode << " vec4 Illum = vec4(0.0);\n"
<< " vec3 PositionMV = vec3(vec4(ModelSpacePos, 1.0) * MV);\n"
<< " \n"
<< " for (int iLight = 0; iLight < NumLights; iLight++)\n"
<< " {\n"
<< " vec3 LightPosMV = vec3(Lights[iLight].Position * ViewMtx);\n"
<< " vec3 LightDirMV = normalize(Lights[iLight].Direction.xyz * inverse(transpose(mat3(ViewMtx))));\n"
<< " vec3 LightDist = LightPosMV.xyz - PositionMV.xyz;\n"
<< " float DistSquared = dot(LightDist, LightDist);\n"
<< " float Dist = sqrt(DistSquared);\n"
<< " LightDist /= Dist;\n"
<< " vec3 AngleAtten = Lights[iLight].AngleAtten.xyz;\n"
<< " AngleAtten = vec3(AngleAtten.x, AngleAtten.y, AngleAtten.z);\n"
<< " float Atten = max(0, dot(LightDist, LightDirMV.xyz));\n"
<< " Atten = max(0, dot(AngleAtten, vec3(1.0, Atten, Atten * Atten))) / dot(Lights[iLight].DistAtten.xyz, vec3(1.0, Dist, DistSquared));\n"
<< " float DiffuseAtten = max(0, dot(Normal, LightDist));\n"
<< " Illum += (Atten * DiffuseAtten * Lights[iLight].Color);\n"
<< " }\n"
<< " COLOR0A0 = COLOR0_Mat * (Illum + COLOR0_Amb);\n"
<< " COLOR1A1 = COLOR1_Mat * (Illum + COLOR1_Amb);\n"
<< " \n";
}
else
{
ShaderCode << " COLOR0A0 = COLOR0_Mat;\n"
<< " COLOR1A1 = COLOR1_Mat;\n"
<< "\n";
ShaderCode << " COLOR0A0 = COLOR0_Mat;\n"
<< " COLOR1A1 = COLOR1_Mat;\n"
<< "\n";
}
// Texture coordinate generation
ShaderCode << " \n"
<< " // TexGen\n";
ShaderCode << " \n"
<< " // TexGen\n";
u32 PassCount = Mat.PassCount();
u32 PassCount = rkMat.PassCount();
for (u32 iPass = 0; iPass < PassCount; iPass++)
{
CMaterialPass *pPass = Mat.Pass(iPass);
CMaterialPass *pPass = rkMat.Pass(iPass);
if (pPass->TexCoordSource() == 0xFF) continue;
EUVAnimMode AnimMode = pPass->AnimMode();
@ -288,8 +351,8 @@ bool CShaderGenerator::CreateVertexShader(const CMaterial& Mat)
if ((AnimMode < 2) || (AnimMode > 5))
{
// Normalization + Post-Transform
ShaderCode << " Tex" << iPass << " = normalize(Tex" << iPass << ");\n";
ShaderCode << " Tex" << iPass << " = vec3(vec4(Tex" << iPass << ", 1.0) * PostMtx[" << iPass << "]).xyz;\n";
ShaderCode << " Tex" << iPass << " = normalize(Tex" << iPass << ");\n"
<< " Tex" << iPass << " = vec3(vec4(Tex" << iPass << ", 1.0) * PostMtx[" << iPass << "]).xyz;\n";
}
}
@ -302,22 +365,22 @@ bool CShaderGenerator::CreateVertexShader(const CMaterial& Mat)
return mpShader->CompileVertexSource(ShaderCode.str().c_str());
}
bool CShaderGenerator::CreatePixelShader(const CMaterial& Mat)
bool CShaderGenerator::CreatePixelShader(const CMaterial& rkMat)
{
std::stringstream ShaderCode;
ShaderCode << "#version 330 core\n"
<< "\n";
FVertexDescription VtxDesc = Mat.VtxDesc();
FVertexDescription VtxDesc = rkMat.VtxDesc();
if (VtxDesc & ePosition) ShaderCode << "in vec3 Position;\n";
if (VtxDesc & eNormal) ShaderCode << "in vec3 Normal;\n";
if (VtxDesc & eColor0) ShaderCode << "in vec4 Color0;\n";
if (VtxDesc & eColor1) ShaderCode << "in vec4 Color1;\n";
u32 PassCount = Mat.PassCount();
u32 PassCount = rkMat.PassCount();
for (u32 iPass = 0; iPass < PassCount; iPass++)
if (Mat.Pass(iPass)->TexCoordSource() != 0xFF)
if (rkMat.Pass(iPass)->TexCoordSource() != 0xFF)
ShaderCode << "in vec3 Tex" << iPass << ";\n";
ShaderCode << "in vec4 COLOR0A0;\n"
@ -333,7 +396,7 @@ bool CShaderGenerator::CreatePixelShader(const CMaterial& Mat)
<< "};\n\n";
for (u32 iPass = 0; iPass < PassCount; iPass++)
if (Mat.Pass(iPass)->Texture() != nullptr)
if (rkMat.Pass(iPass)->Texture() != nullptr)
ShaderCode << "uniform sampler2D Texture" << iPass << ";\n";
ShaderCode <<"\n";
@ -351,7 +414,7 @@ bool CShaderGenerator::CreatePixelShader(const CMaterial& Mat)
bool Lightmap = false;
for (u32 iPass = 0; iPass < PassCount; iPass++)
{
const CMaterialPass *pPass = Mat.Pass(iPass);
const CMaterialPass *pPass = rkMat.Pass(iPass);
CFourCC PassType = pPass->Type();
ShaderCode << " // TEV Stage " << iPass << " - " << PassType.ToString() << "\n";
@ -377,7 +440,7 @@ bool CShaderGenerator::CreatePixelShader(const CMaterial& Mat)
// Apply lightmap multiplier
if ( (PassType == "DIFF") ||
(PassType == "CUST" && (Mat.Options() & CMaterial::eLightmap) && iPass == 0) )
(PassType == "CUST" && (rkMat.Options() & CMaterial::eLightmap) && iPass == 0) )
ShaderCode << " * LightmapMultiplier";
ShaderCode << ";\n";
@ -415,9 +478,9 @@ bool CShaderGenerator::CreatePixelShader(const CMaterial& Mat)
ShaderCode << "clamp(TevInD.a + ((1.0 - TevInC.a) * TevInA.a + TevInC.a * TevInB.a), 0.0, 1.0);\n\n";
}
if (Mat.Options() & CMaterial::ePunchthrough)
if (rkMat.Options() & CMaterial::ePunchthrough)
{
if (Mat.Version() < eCorruptionProto)
if (rkMat.Version() < eCorruptionProto)
{
ShaderCode << " if (Prev.a <= 0.25) discard;\n"
<< " else Prev.a = 1.0;\n\n";

View File

@ -45,10 +45,17 @@ public:
Unbind();
}
void Buffer(void *pData)
void Buffer(const void *pkData)
{
Bind();
glBufferSubData(GL_UNIFORM_BUFFER, 0, mBufferSize, pData);
glBufferSubData(GL_UNIFORM_BUFFER, 0, mBufferSize, pkData);
Unbind();
}
void BufferRange(const void *pkData, u32 Offset, u32 Size)
{
Bind();
glBufferSubData(GL_UNIFORM_BUFFER, Offset, Size, pkData);
Unbind();
}

View File

@ -18,7 +18,7 @@ CVertexBuffer::~CVertexBuffer()
CVertexArrayManager::DeleteAllArraysForVBO(this);
if (mBuffered)
glDeleteBuffers(12, mAttribBuffers);
glDeleteBuffers(14, mAttribBuffers);
}
u16 CVertexBuffer::AddVertex(const CVertex& rkVtx)
@ -31,11 +31,18 @@ u16 CVertexBuffer::AddVertex(const CVertex& rkVtx)
if (mVtxDesc & eColor1) mColors[1].push_back(rkVtx.Color[1]);
for (u32 iTex = 0; iTex < 8; iTex++)
if (mVtxDesc & (eTex0 << (iTex * 2))) mTexCoords[iTex].push_back(rkVtx.Tex[iTex]);
if (mVtxDesc & (eTex0 << iTex)) mTexCoords[iTex].push_back(rkVtx.Tex[iTex]);
for (u32 iMtx = 0; iMtx < 8; iMtx++)
if (mVtxDesc & (ePosMtx << iMtx)) mTexCoords[iMtx].push_back(rkVtx.MatrixIndices[iMtx]);
if (mVtxDesc.HasAnyFlags(eBoneIndices | eBoneWeights) && mpSkin)
{
const SVertexWeights& rkWeights = mpSkin->WeightsForVertex(rkVtx.ArrayPosition);
if (mVtxDesc & eBoneIndices) mBoneIndices.push_back(rkWeights.Indices);
if (mVtxDesc & eBoneWeights) mBoneWeights.push_back(rkWeights.Weights);
}
return (mPositions.size() - 1);
}
@ -51,24 +58,39 @@ u16 CVertexBuffer::AddIfUnique(const CVertex& rkVtx, u16 Start)
if (mVtxDesc & ePosition)
if (rkVtx.Position != mPositions[iVert]) Unique = true;
if ((!Unique) && (mVtxDesc & eNormal))
if (!Unique && (mVtxDesc & eNormal))
if (rkVtx.Normal != mNormals[iVert]) Unique = true;
if ((!Unique) && (mVtxDesc & eColor0))
if (!Unique && (mVtxDesc & eColor0))
if (rkVtx.Color[0] != mColors[0][iVert]) Unique = true;
if ((!Unique) && (mVtxDesc & eColor1))
if (!Unique && (mVtxDesc & eColor1))
if (rkVtx.Color[1] != mColors[1][iVert]) Unique = true;
if (!Unique)
for (u32 iTex = 0; iTex < 8; iTex++)
if ((mVtxDesc & (eTex0 << (iTex * 2))))
if ((mVtxDesc & (eTex0 << iTex)))
if (rkVtx.Tex[iTex] != mTexCoords[iTex][iVert])
{
Unique = true;
break;
}
if (!Unique && mpSkin && (mVtxDesc.HasAnyFlags(eBoneIndices | eBoneWeights)))
{
const SVertexWeights& rkWeights = mpSkin->WeightsForVertex(rkVtx.ArrayPosition);
for (u32 iWgt = 0; iWgt < 4; iWgt++)
{
if ( ((mVtxDesc & eBoneIndices) && (rkWeights.Indices[iWgt] != mBoneIndices[iVert][iWgt])) ||
((mVtxDesc & eBoneWeights) && (rkWeights.Weights[iWgt] != mBoneWeights[iVert][iWgt])) )
{
Unique = true;
break;
}
}
}
if (!Unique) return iVert;
}
}
@ -93,14 +115,20 @@ void CVertexBuffer::Reserve(u16 Size)
mColors[1].reserve(ReserveSize);
for (u32 iTex = 0; iTex < 8; iTex++)
if (mVtxDesc & (eTex0 << (iTex * 2)))
if (mVtxDesc & (eTex0 << iTex))
mTexCoords[iTex].reserve(ReserveSize);
if (mVtxDesc & eBoneIndices)
mBoneIndices.reserve(ReserveSize);
if (mVtxDesc & eBoneWeights)
mBoneWeights.reserve(ReserveSize);
}
void CVertexBuffer::Clear()
{
if (mBuffered)
glDeleteBuffers(12, mAttribBuffers);
glDeleteBuffers(14, mAttribBuffers);
mBuffered = false;
mPositions.clear();
@ -110,6 +138,9 @@ void CVertexBuffer::Clear()
for (u32 iTex = 0; iTex < 8; iTex++)
mTexCoords[iTex].clear();
mBoneIndices.clear();
mBoneWeights.clear();
}
void CVertexBuffer::Buffer()
@ -117,16 +148,16 @@ void CVertexBuffer::Buffer()
// Make sure we don't end up with two buffers for the same data...
if (mBuffered)
{
glDeleteBuffers(12, mAttribBuffers);
glDeleteBuffers(14, mAttribBuffers);
mBuffered = false;
}
// Generate buffers
glGenBuffers(12, mAttribBuffers);
glGenBuffers(14, mAttribBuffers);
for (u32 iAttrib = 0; iAttrib < 12; iAttrib++)
for (u32 iAttrib = 0; iAttrib < 14; iAttrib++)
{
int Attrib = (ePosition << (iAttrib * 2));
int Attrib = (ePosition << iAttrib);
bool HasAttrib = ((mVtxDesc & Attrib) != 0);
if (!HasAttrib) continue;
@ -146,13 +177,25 @@ void CVertexBuffer::Buffer()
glBufferData(GL_ARRAY_BUFFER, mColors[Index].size() * sizeof(CColor), mColors[Index].data(), GL_STATIC_DRAW);
}
else
else if (iAttrib < 12)
{
u8 Index = (u8) (iAttrib - 4);
glBindBuffer(GL_ARRAY_BUFFER, mAttribBuffers[iAttrib]);
glBufferData(GL_ARRAY_BUFFER, mTexCoords[Index].size() * sizeof(CVector2f), mTexCoords[Index].data(), GL_STATIC_DRAW);
}
else if (iAttrib == 12)
{
glBindBuffer(GL_ARRAY_BUFFER, mAttribBuffers[iAttrib]);
glBufferData(GL_ARRAY_BUFFER, mBoneIndices.size() * sizeof(TBoneIndices), mBoneIndices.data(), GL_STATIC_DRAW);
}
else if (iAttrib == 13)
{
glBindBuffer(GL_ARRAY_BUFFER, mAttribBuffers[iAttrib]);
glBufferData(GL_ARRAY_BUFFER, mBoneWeights.size() * sizeof(TBoneWeights), mBoneWeights.data(), GL_STATIC_DRAW);
}
}
mBuffered = true;
@ -185,6 +228,12 @@ void CVertexBuffer::SetVertexDesc(FVertexDescription Desc)
mVtxDesc = Desc;
}
void CVertexBuffer::SetSkin(CSkin *pSkin)
{
Clear();
mpSkin = pSkin;
}
u32 CVertexBuffer::Size()
{
return mPositions.size();
@ -196,9 +245,9 @@ GLuint CVertexBuffer::CreateVAO()
glGenVertexArrays(1, &VertexArray);
glBindVertexArray(VertexArray);
for (u32 iAttrib = 0; iAttrib < 12; iAttrib++)
for (u32 iAttrib = 0; iAttrib < 14; iAttrib++)
{
int Attrib = (ePosition << (iAttrib * 2));
int Attrib = (ePosition << iAttrib);
bool HasAttrib = ((mVtxDesc & Attrib) != 0);
if (!HasAttrib) continue;
@ -216,12 +265,26 @@ GLuint CVertexBuffer::CreateVAO()
glEnableVertexAttribArray(iAttrib);
}
else
else if (iAttrib < 12)
{
glBindBuffer(GL_ARRAY_BUFFER, mAttribBuffers[iAttrib]);
glVertexAttribPointer(iAttrib, 2, GL_FLOAT, GL_FALSE, sizeof(CVector2f), (void*) 0);
glEnableVertexAttribArray(iAttrib);
}
else if (iAttrib == 12)
{
glBindBuffer(GL_ARRAY_BUFFER, mAttribBuffers[iAttrib]);
glVertexAttribIPointer(iAttrib, 4, GL_UNSIGNED_INT, sizeof(TBoneIndices), (void*) 0);
glEnableVertexAttribArray(iAttrib);
}
else if (iAttrib == 13)
{
glBindBuffer(GL_ARRAY_BUFFER, mAttribBuffers[iAttrib]);
glVertexAttribPointer(iAttrib, 4, GL_FLOAT, GL_FALSE, sizeof(TBoneWeights), (void*) 0);
glEnableVertexAttribArray(iAttrib);
}
}
glBindVertexArray(0);

View File

@ -1,6 +1,8 @@
#ifndef CVERTEXBUFFER_H
#define CVERTEXBUFFER_H
#include "Core/Resource/CSkin.h"
#include "Core/Resource/TResPtr.h"
#include "Core/Resource/Model/CVertex.h"
#include "Core/Resource/Model/EVertexAttribute.h"
#include <vector>
@ -8,13 +10,16 @@
class CVertexBuffer
{
FVertexDescription mVtxDesc; // Flags that indicate what vertex attributes are enabled on this vertex buffer
GLuint mAttribBuffers[12]; // Separate GL buffer for each attribute to allow not tracking unused attribs. No support for matrix indices currently.
std::vector<CVector3f> mPositions; // Vector of vertex positions
std::vector<CVector3f> mNormals; // Vector of vertex normals
std::vector<CColor> mColors[2]; // Vectors of vertex colors
std::vector<CVector2f> mTexCoords[8]; // Vectors of texture coordinates
bool mBuffered; // Bool value that indicates whether the attributes have been buffered.
FVertexDescription mVtxDesc; // Flags that indicate what vertex attributes are enabled on this vertex buffer
GLuint mAttribBuffers[14]; // Separate GL buffer for each attribute to allow not tracking unused attribs. No support for matrix indices currently.
TResPtr<CSkin> mpSkin; // Skin for skinned models. Null on unskinned models;
std::vector<CVector3f> mPositions; // Vector of vertex positions
std::vector<CVector3f> mNormals; // Vector of vertex normals
std::vector<CColor> mColors[2]; // Vectors of vertex colors
std::vector<CVector2f> mTexCoords[8]; // Vectors of texture coordinates
std::vector<TBoneIndices> mBoneIndices; // Vectors of bone indices
std::vector<TBoneWeights> mBoneWeights; // Vectors of bone weights
bool mBuffered; // Bool value that indicates whether the attributes have been buffered.
public:
CVertexBuffer();
@ -30,6 +35,7 @@ public:
bool IsBuffered();
FVertexDescription VertexDesc();
void SetVertexDesc(FVertexDescription Desc);
void SetSkin(CSkin *pSkin);
u32 Size();
GLuint CreateVAO();
};

View File

@ -16,7 +16,7 @@ public:
inline void ResizeToSkeleton(CSkeleton *pSkel) { mBoneMatrices.resize(pSkel ? pSkel->MaxBoneID() + 1 : 0); }
inline CTransform4f& BoneMatrix(u32 BoneID) { return mBoneMatrices[BoneID]; }
inline const CTransform4f& BoneMatrix(u32 BoneID) const { return mBoneMatrices[BoneID]; }
inline void* Data() { return mBoneMatrices.data(); }
inline const void* Data() const { return mBoneMatrices.data(); }
inline u32 DataSize() const { return mBoneMatrices.size() * sizeof(CTransform4f); }
inline CTransform4f& operator[](u32 BoneIndex) { return BoneMatrix(BoneIndex); }
inline const CTransform4f& operator[](u32 BoneIndex) const { return BoneMatrix(BoneIndex); }

View File

@ -8,10 +8,13 @@ CUniformBuffer* CGraphics::mpMVPBlockBuffer;
CUniformBuffer* CGraphics::mpVertexBlockBuffer;
CUniformBuffer* CGraphics::mpPixelBlockBuffer;
CUniformBuffer* CGraphics::mpLightBlockBuffer;
CUniformBuffer* CGraphics::mpBoneTransformBuffer;
u32 CGraphics::mContextIndices = 0;
u32 CGraphics::mActiveContext = -1;
bool CGraphics::mInitialized = false;
std::vector<CVertexArrayManager*> CGraphics::mVAMs;
bool CGraphics::mIdentityBoneTransforms = false;
const CSkeleton *CGraphics::mpkCurrentSkeleton = nullptr;
CGraphics::SMVPBlock CGraphics::sMVPBlock;
CGraphics::SVertexBlock CGraphics::sVertexBlock;
@ -44,6 +47,7 @@ void CGraphics::Initialize()
mpVertexBlockBuffer = new CUniformBuffer(sizeof(sVertexBlock));
mpPixelBlockBuffer = new CUniformBuffer(sizeof(sPixelBlock));
mpLightBlockBuffer = new CUniformBuffer(sizeof(sLightBlock));
mpBoneTransformBuffer = new CUniformBuffer(sizeof(CTransform4f) * 200);
sLightMode = eWorldLighting;
sNumLights = 0;
@ -55,6 +59,8 @@ void CGraphics::Initialize()
mpVertexBlockBuffer->BindBase(1);
mpPixelBlockBuffer->BindBase(2);
mpLightBlockBuffer->BindBase(3);
mpBoneTransformBuffer->BindBase(4);
LoadIdentityBoneTransforms();
}
void CGraphics::Shutdown()
@ -66,6 +72,7 @@ void CGraphics::Shutdown()
delete mpVertexBlockBuffer;
delete mpPixelBlockBuffer;
delete mpLightBlockBuffer;
delete mpBoneTransformBuffer;
mInitialized = false;
}
}
@ -110,6 +117,11 @@ GLuint CGraphics::LightBlockBindingPoint()
return 3;
}
GLuint CGraphics::BoneTransformBlockBindingPoint()
{
return 4;
}
u32 CGraphics::GetContextIndex()
{
for (u32 iCon = 0; iCon < 32; iCon++)
@ -178,3 +190,31 @@ void CGraphics::SetIdentityMVP()
sMVPBlock.ViewMatrix = CMatrix4f::skIdentity;
sMVPBlock.ProjectionMatrix = CMatrix4f::skIdentity;
}
void CGraphics::LoadBoneTransforms(const CBoneTransformData& rkData)
{
mpBoneTransformBuffer->BufferRange(rkData.Data(), 0, rkData.DataSize());
mIdentityBoneTransforms = false;
}
void CGraphics::LoadBoneInverseBindTransforms(CSkeleton *pSkel)
{
if (mpkCurrentSkeleton != pSkel)
{
mpBoneTransformBuffer->BufferRange(pSkel->InverseBindMatricesData(), sizeof(CTransform4f) * 100, pSkel->InverseBindMatricesSize());
mpkCurrentSkeleton = pSkel;
mIdentityBoneTransforms = false;
}
}
void CGraphics::LoadIdentityBoneTransforms()
{
static CTransform4f IdentityTransforms[200];
if (!mIdentityBoneTransforms)
{
mpBoneTransformBuffer->Buffer(&IdentityTransforms);
mpkCurrentSkeleton = nullptr;
mIdentityBoneTransforms = true;
}
}

View File

@ -1,6 +1,7 @@
#ifndef CGRAPHICS_H
#define CGRAPHICS_H
#include "CBoneTransformData.h"
#include "Core/OpenGL/CUniformBuffer.h"
#include "Core/OpenGL/CVertexArrayManager.h"
#include "Core/Resource/CLight.h"
@ -23,11 +24,15 @@ class CGraphics
static CUniformBuffer *mpVertexBlockBuffer;
static CUniformBuffer *mpPixelBlockBuffer;
static CUniformBuffer *mpLightBlockBuffer;
static CUniformBuffer *mpBoneTransformBuffer;
static u32 mContextIndices;
static u32 mActiveContext;
static bool mInitialized;
static std::vector<CVertexArrayManager*> mVAMs;
static bool mIdentityBoneTransforms;
static const CSkeleton *mpkCurrentSkeleton;
public:
// SMVPBlock
struct SMVPBlock
@ -95,6 +100,7 @@ public:
static GLuint VertexBlockBindingPoint();
static GLuint PixelBlockBindingPoint();
static GLuint LightBlockBindingPoint();
static GLuint BoneTransformBlockBindingPoint();
static u32 GetContextIndex();
static u32 GetActiveContext();
static void ReleaseContext(u32 Index);
@ -102,6 +108,9 @@ public:
static void SetDefaultLighting();
static void SetupAmbientColor();
static void SetIdentityMVP();
static void LoadBoneTransforms(const CBoneTransformData& rkData);
static void LoadBoneInverseBindTransforms(CSkeleton *pSkel);
static void LoadIdentityBoneTransforms();
};
#endif // CGRAPHICS_H

View File

@ -5,6 +5,7 @@
#include "CAnimation.h"
#include "CResource.h"
#include "CSkeleton.h"
#include "CSkin.h"
#include "Core/Resource/Model/CModel.h"
#include <Common/types.h>
@ -20,7 +21,7 @@ class CAnimSet : public CResource
{
TString Name;
TResPtr<CModel> pModel;
u32 SkinID;
TResPtr<CSkin> pSkin;
TResPtr<CSkeleton> pSkeleton;
SNode() { pModel = nullptr; }
@ -40,6 +41,7 @@ public:
u32 NumNodes() const { return mNodes.size(); }
TString NodeName(u32 Index) { if (Index >= mNodes.size()) Index = 0; return mNodes[Index].Name; }
CModel* NodeModel(u32 Index) { if (Index >= mNodes.size()) Index = 0; return mNodes[Index].pModel; }
CSkin* NodeSkin(u32 Index) { if (Index >= mNodes.size()) Index = 0; return mNodes[Index].pSkin; }
CSkeleton* NodeSkeleton(u32 Index) { if (Index >= mNodes.size()) Index = 0; return mNodes[Index].pSkeleton; }
u32 NumAnims() const { return mAnims.size(); }

View File

@ -37,7 +37,8 @@ public:
eLightmap = 0x800,
eShortTexCoord = 0x2000,
eAllMP1Settings = 0x2FF8,
eDrawWhiteAmbientDKCR = 0x80000
eDrawWhiteAmbientDKCR = 0x80000,
eSkinningEnabled = 0x80000000
};
DECLARE_FLAGS(EMaterialOption, FMaterialOptions)
@ -100,12 +101,12 @@ public:
inline CMaterialPass* Pass(u32 PassIndex) const { return mPasses[PassIndex]; }
inline void SetName(const TString& rkName) { mName = rkName; }
inline void SetOptions(FMaterialOptions Options) { mOptions = Options; mRecalcHash = true; }
inline void SetVertexDescription(FVertexDescription Desc) { mVtxDesc = Desc; mRecalcHash = true; }
inline void SetOptions(FMaterialOptions Options) { mOptions = Options; Update(); }
inline void SetVertexDescription(FVertexDescription Desc) { mVtxDesc = Desc; Update(); }
inline void SetBlendMode(GLenum SrcFac, GLenum DstFac) { mBlendSrcFac = SrcFac; mBlendDstFac = DstFac; mRecalcHash = true; }
inline void SetKonst(CColor& Konst, u32 KIndex) { mKonstColors[KIndex] = Konst; mRecalcHash = true; }
inline void SetKonst(CColor& Konst, u32 KIndex) { mKonstColors[KIndex] = Konst; Update(); }
inline void SetIndTexture(CTexture *pTex) { mpIndirectTexture = pTex; }
inline void SetLightingEnabled(bool Enabled) { mLightingEnabled = Enabled; mRecalcHash = true; }
inline void SetLightingEnabled(bool Enabled) { mLightingEnabled = Enabled; Update(); }
// Static
inline static void KillCachedMaterial() { sCurrentMaterial = 0; }

View File

@ -8,6 +8,7 @@
#include "Core/Resource/Factory/CPoiToWorldLoader.h"
#include "Core/Resource/Factory/CScanLoader.h"
#include "Core/Resource/Factory/CSkeletonLoader.h"
#include "Core/Resource/Factory/CSkinLoader.h"
#include "Core/Resource/Factory/CStringLoader.h"
#include "Core/Resource/Factory/CTextureDecoder.h"
#include "Core/Resource/Factory/CWorldLoader.h"
@ -153,6 +154,7 @@ CResource* CResCache::GetResource(CUniqueID ResID, CFourCC Type)
else if (Type == "EGMC") pRes = CPoiToWorldLoader::LoadEGMC(Mem);
else if (Type == "CINF") pRes = CSkeletonLoader::LoadCINF(Mem);
else if (Type == "ANIM") pRes = CAnimationLoader::LoadANIM(Mem);
else if (Type == "CSKR") pRes = CSkinLoader::LoadCSKR(Mem);
else SupportedFormat = false;
// Log errors
@ -209,6 +211,7 @@ CResource* CResCache::GetResource(const TString& rkResPath)
else if (Type == "EGMC") pRes = CPoiToWorldLoader::LoadEGMC(File);
else if (Type == "CINF") pRes = CSkeletonLoader::LoadCINF(File);
else if (Type == "ANIM") pRes = CAnimationLoader::LoadANIM(File);
else if (Type == "CSKR") pRes = CSkinLoader::LoadCSKR(File);
else SupportedFormat = false;
if (!pRes) pRes = new CResource(); // Default for unsupported formats

View File

@ -10,7 +10,34 @@
#include <Math/CVector3f.h>
class CBoneTransformData;
class CSkeleton;
class CBone;
class CSkeleton : public CResource
{
DECLARE_RESOURCE_TYPE(eSkeleton)
friend class CSkeletonLoader;
CBone *mpRootBone;
std::vector<CBone*> mBones;
std::vector<CTransform4f> mInvBindMatrices;
static const float skSphereRadius;
public:
CSkeleton();
~CSkeleton();
void UpdateTransform(CBoneTransformData& rData, CAnimation *pAnim, float Time, bool AnchorRoot);
CBone* BoneByID(u32 BoneID) const;
u32 MaxBoneID() const;
void Draw(FRenderOptions Options, const CBoneTransformData& rkData);
std::pair<s32,float> RayIntersect(const CRay& rkRay, const CBoneTransformData& rkData);
inline u32 NumBones() const { return mBones.size(); }
inline const CTransform4f& BoneInverseBindMatrix(u32 BoneID) const { return mInvBindMatrices[BoneID]; }
inline const void* InverseBindMatricesData() const { return mInvBindMatrices.data(); }
inline u32 InverseBindMatricesSize() const { return mInvBindMatrices.size() * sizeof(CTransform4f); }
};
class CBone
{
@ -29,36 +56,15 @@ public:
bool IsRoot() const;
// Accessors
inline CSkeleton* Skeleton() const { return mpSkeleton; }
inline CBone* Parent() const { return mpParent; }
inline u32 NumChildren() const { return mChildren.size(); }
inline CBone* ChildByIndex(u32 Index) const { return mChildren[Index]; }
inline u32 ID() const { return mID; }
inline CVector3f Position() const { return mPosition; }
inline TString Name() const { return mName; }
};
class CSkeleton : public CResource
{
DECLARE_RESOURCE_TYPE(eSkeleton)
friend class CSkeletonLoader;
CBone *mpRootBone;
std::vector<CBone*> mBones;
static const float skSphereRadius;
public:
CSkeleton();
~CSkeleton();
void UpdateTransform(CBoneTransformData& rData, CAnimation *pAnim, float Time, bool AnchorRoot);
CBone* BoneByID(u32 BoneID) const;
u32 MaxBoneID() const;
void Draw(FRenderOptions Options, const CBoneTransformData& rkData);
std::pair<s32,float> RayIntersect(const CRay& rkRay, const CBoneTransformData& rkData);
inline u32 NumBones() const { return mBones.size(); }
inline CSkeleton* Skeleton() const { return mpSkeleton; }
inline CBone* Parent() const { return mpParent; }
inline u32 NumChildren() const { return mChildren.size(); }
inline CBone* ChildByIndex(u32 Index) const { return mChildren[Index]; }
inline u32 ID() const { return mID; }
inline CVector3f Position() const { return mPosition; }
inline CVector3f AbsolutePosition() const { return mPosition + (mpParent ? mpParent->AbsolutePosition() : CVector3f::skZero); }
inline TString Name() const { return mName; }
inline const CTransform4f& InverseBindMtx() const { return mpSkeleton->BoneInverseBindMatrix(mID); }
};
#endif // CSKELETON_H

51
src/Core/Resource/CSkin.h Normal file
View File

@ -0,0 +1,51 @@
#ifndef CSKIN_H
#define CSKIN_H
#include "CResource.h"
#include "Core/Resource/Model/CVertex.h"
struct SVertexWeights
{
TBoneIndices Indices;
TBoneWeights Weights;
};
class CSkin : public CResource
{
DECLARE_RESOURCE_TYPE(eSkin)
friend class CSkinLoader;
struct SVertGroup
{
SVertexWeights Weights;
u32 NumVertices;
};
std::vector<SVertGroup> mVertGroups;
u32 mSkinnedVertexCount;
public:
CSkin() {}
const SVertexWeights& WeightsForVertex(u32 VertIdx)
{
static const SVertexWeights skNullWeights = {
{ 0, 0, 0, 0 },
{ 0.f, 0.f, 0.f, 0.f }
};
u32 Index = 0;
for (u32 iGrp = 0; iGrp < mVertGroups.size(); iGrp++)
{
if (VertIdx < Index + mVertGroups[iGrp].NumVertices)
return mVertGroups[iGrp].Weights;
Index += mVertGroups[iGrp].NumVertices;
}
return skNullWeights;
}
};
#endif // CSKIN_H

View File

@ -6,6 +6,32 @@ CMaterialCooker::CMaterialCooker()
{
}
u32 CMaterialCooker::ConvertFromVertexDescription(FVertexDescription VtxDesc)
{
u32 Flags = 0;
if (VtxDesc & ePosition) Flags |= 0x00000003;
if (VtxDesc & eNormal) Flags |= 0x0000000C;
if (VtxDesc & eColor0) Flags |= 0x00000030;
if (VtxDesc & eColor1) Flags |= 0x000000C0;
if (VtxDesc & eTex0) Flags |= 0x00000300;
if (VtxDesc & eTex1) Flags |= 0x00000C00;
if (VtxDesc & eTex2) Flags |= 0x00003000;
if (VtxDesc & eTex3) Flags |= 0x0000C000;
if (VtxDesc & eTex4) Flags |= 0x00030000;
if (VtxDesc & eTex5) Flags |= 0x000C0000;
if (VtxDesc & eTex6) Flags |= 0x00300000;
if (VtxDesc & eTex7) Flags |= 0x00C00000;
if (VtxDesc & ePosMtx) Flags |= 0x01000000;
if (VtxDesc & eTex0Mtx) Flags |= 0x02000000;
if (VtxDesc & eTex1Mtx) Flags |= 0x04000000;
if (VtxDesc & eTex2Mtx) Flags |= 0x08000000;
if (VtxDesc & eTex3Mtx) Flags |= 0x10000000;
if (VtxDesc & eTex4Mtx) Flags |= 0x20000000;
if (VtxDesc & eTex5Mtx) Flags |= 0x40000000;
if (VtxDesc & eTex6Mtx) Flags |= 0x80000000;
return Flags;
}
void CMaterialCooker::WriteMatSetPrime(IOutputStream& rOut)
{
// Gather texture list from the materials before starting
@ -150,12 +176,12 @@ void CMaterialCooker::WriteMaterialPrime(IOutputStream& rOut)
rOut.WriteLong(TexIndices[iTex]);
// Vertex description
FVertexDescription Desc = mpMat->VtxDesc();
u32 VtxFlags = ConvertFromVertexDescription( mpMat->VtxDesc() );
if (mVersion < eEchoes)
Desc &= 0x00FFFFFF;
VtxFlags &= 0x00FFFFFF;
rOut.WriteLong(Desc);
rOut.WriteLong(VtxFlags);
// Echoes unknowns
if (mVersion == eEchoes)

View File

@ -14,6 +14,7 @@ class CMaterialCooker
std::vector<u64> mMaterialHashes;
CMaterialCooker();
u32 ConvertFromVertexDescription(FVertexDescription VtxDesc);
void WriteMatSetPrime(IOutputStream& rOut);
void WriteMatSetCorruption(IOutputStream& rOut);
void WriteMaterialPrime(IOutputStream& rOut);

View File

@ -63,7 +63,7 @@ void CModelCooker::WriteModelPrime(IOutputStream& rOut)
// Header
rOut.WriteLong(0xDEADBABE);
rOut.WriteLong(GetCMDLVersion(mVersion));
rOut.WriteLong(5);
rOut.WriteLong(mpModel->IsSkinned() ? 6 : 5);
mpModel->mAABox.Write(rOut);
u32 NumSections = mNumMatSets + mNumSurfaces + 6;
@ -115,7 +115,7 @@ void CModelCooker::WriteModelPrime(IOutputStream& rOut)
// Float UV coordinates
for (u32 iTexSlot = 0; iTexSlot < 8; iTexSlot++)
{
bool HasTexSlot = (mVtxAttribs & (eTex0 << (iTexSlot * 2))) != 0;
bool HasTexSlot = (mVtxAttribs & (eTex0 << iTexSlot)) != 0;
if (HasTexSlot)
{
for (u32 iTex = 0; iTex < mNumVertices; iTex++)
@ -156,7 +156,7 @@ void CModelCooker::WriteModelPrime(IOutputStream& rOut)
rOut.WriteToBoundary(32, 0);
u32 PrimTableStart = rOut.Tell();
FVertexDescription MatAttribs = mpModel->GetMaterialBySurface(0, iSurf)->VtxDesc();
FVertexDescription VtxAttribs = mpModel->GetMaterialBySurface(0, iSurf)->VtxDesc();
for (u32 iPrim = 0; iPrim < pSurface->Primitives.size(); iPrim++)
{
@ -171,28 +171,28 @@ void CModelCooker::WriteModelPrime(IOutputStream& rOut)
if (mVersion == eEchoes)
{
for (u32 iMtxAttribs = 0; iMtxAttribs < 8; iMtxAttribs++)
if (MatAttribs & (ePosMtx << iMtxAttribs))
if (VtxAttribs & (ePosMtx << iMtxAttribs))
rOut.WriteByte(pVert->MatrixIndices[iMtxAttribs]);
}
u16 VertexIndex = (u16) pVert->ArrayPosition;
if (MatAttribs & ePosition)
if (VtxAttribs & ePosition)
rOut.WriteShort(VertexIndex);
if (MatAttribs & eNormal)
if (VtxAttribs & eNormal)
rOut.WriteShort(VertexIndex);
if (MatAttribs & eColor0)
if (VtxAttribs & eColor0)
rOut.WriteShort(VertexIndex);
if (MatAttribs & eColor1)
if (VtxAttribs & eColor1)
rOut.WriteShort(VertexIndex);
u16 TexOffset = 0;
for (u32 iTex = 0; iTex < 8; iTex++)
{
if (MatAttribs & (eTex0 << (iTex * 2)))
if (VtxAttribs & (eTex0 << iTex))
{
rOut.WriteShort(VertexIndex + TexOffset);
TexOffset += (u16) mNumVertices;

View File

@ -220,12 +220,16 @@ CAnimSet* CAnimSetLoader::LoadANCS(IInputStream& rANCS)
if (iNode == 0) Loader.mVersion = (Unknown1 == 0xA) ? eEchoes : ePrime; // Best version indicator we know of unfortunately
pNode->Name = rANCS.ReadString();
pNode->pModel = gResCache.GetResource(rANCS.ReadLong(), "CMDL");
pNode->SkinID = rANCS.ReadLong();
if (Loader.mVersion == ePrime)
if (Loader.mVersion <= ePrime)
{
pNode->pSkin = gResCache.GetResource(rANCS.ReadLong(), "CSKR");
pNode->pSkeleton = gResCache.GetResource(rANCS.ReadLong(), "CINF");
pNode->pModel->SetSkin(pNode->pSkin);
}
else
rANCS.Seek(0x4, SEEK_CUR);
rANCS.Seek(0x8, SEEK_CUR);
// Unfortunately that's all that's actually supported at the moment. Hope to expand later.
// Since there's no size value I have to actually read the rest of the node to reach the next one

View File

@ -15,6 +15,32 @@ CMaterialLoader::~CMaterialLoader()
{
}
FVertexDescription CMaterialLoader::ConvertToVertexDescription(u32 VertexFlags)
{
FVertexDescription Desc;
if (VertexFlags & 0x00000003) Desc |= ePosition;
if (VertexFlags & 0x0000000C) Desc |= eNormal;
if (VertexFlags & 0x00000030) Desc |= eColor0;
if (VertexFlags & 0x000000C0) Desc |= eColor1;
if (VertexFlags & 0x00000300) Desc |= eTex0;
if (VertexFlags & 0x00000C00) Desc |= eTex1;
if (VertexFlags & 0x00003000) Desc |= eTex2;
if (VertexFlags & 0x0000C000) Desc |= eTex3;
if (VertexFlags & 0x00030000) Desc |= eTex4;
if (VertexFlags & 0x000C0000) Desc |= eTex5;
if (VertexFlags & 0x00300000) Desc |= eTex6;
if (VertexFlags & 0x00C00000) Desc |= eTex7;
if (VertexFlags & 0x01000000) Desc |= ePosMtx;
if (VertexFlags & 0x02000000) Desc |= eTex0Mtx;
if (VertexFlags & 0x04000000) Desc |= eTex1Mtx;
if (VertexFlags & 0x08000000) Desc |= eTex2Mtx;
if (VertexFlags & 0x10000000) Desc |= eTex3Mtx;
if (VertexFlags & 0x20000000) Desc |= eTex4Mtx;
if (VertexFlags & 0x40000000) Desc |= eTex5Mtx;
if (VertexFlags & 0x80000000) Desc |= eTex6Mtx;
return Desc;
}
void CMaterialLoader::ReadPrimeMatSet()
{
// Textures
@ -63,7 +89,7 @@ CMaterial* CMaterialLoader::ReadPrimeMaterial()
}
// Vertex description
pMat->mVtxDesc = (FVertexDescription) mpFile->ReadLong();
pMat->mVtxDesc = ConvertToVertexDescription( mpFile->ReadLong() );
// Unknowns
if (mVersion >= eEchoesDemo)
@ -267,7 +293,7 @@ CMaterial* CMaterialLoader::ReadCorruptionMaterial()
mHas0x400 = ((Flags & 0x400) != 0);
mpFile->Seek(0x8, SEEK_CUR); // Don't know what any of this is
pMat->mVtxDesc = (FVertexDescription) mpFile->ReadLong();
pMat->mVtxDesc = ConvertToVertexDescription( mpFile->ReadLong() );
mpFile->Seek(0xC, SEEK_CUR);
// Initialize all KColors to white

View File

@ -25,6 +25,7 @@ class CMaterialLoader
CMaterialLoader();
~CMaterialLoader();
FVertexDescription ConvertToVertexDescription(u32 VertexFlags);
// Load Functions
void ReadPrimeMatSet();

View File

@ -168,7 +168,7 @@ SSurface* CModelLoader::LoadSurface(IInputStream& rModel)
// Color
for (u32 iClr = 0; iClr < 2; iClr++)
if (VtxDesc & (eColor0 << (iClr * 2)))
if (VtxDesc & (eColor0 << iClr))
Vtx.Color[iClr] = mColors[rModel.ReadShort() & 0xFFFF];
// Tex Coords - these are done a bit differently in DKCR than in the Prime series
@ -185,7 +185,7 @@ SSurface* CModelLoader::LoadSurface(IInputStream& rModel)
// Tex1-7
for (u32 iTex = 1; iTex < 7; iTex++)
if (VtxDesc & (eTex0 << (iTex * 2)))
if (VtxDesc & (eTex0 << iTex))
Vtx.Tex[iTex] = mTex0[rModel.ReadShort() & 0xFFFF];
}
@ -194,7 +194,7 @@ SSurface* CModelLoader::LoadSurface(IInputStream& rModel)
// Tex0-7
for (u32 iTex = 0; iTex < 7; iTex++)
{
if (VtxDesc & (eTex0 << iTex * 2))
if (VtxDesc & (eTex0 << iTex))
{
if (!mSurfaceUsingTex1)
Vtx.Tex[iTex] = mTex0[rModel.ReadShort() & 0xFFFF];
@ -289,7 +289,7 @@ SSurface* CModelLoader::LoadAssimpMesh(const aiMesh *pkMesh, CMaterialSet *pSet)
if (pkMesh->HasNormals()) Desc |= eNormal;
for (u32 iUV = 0; iUV < pkMesh->GetNumUVChannels(); iUV++)
Desc |= (eTex0 << (iUV * 2));
Desc |= (eTex0 << iUV);
pMat->SetVertexDescription(Desc);
@ -401,6 +401,7 @@ CModel* CModelLoader::LoadCMDL(IInputStream& rCMDL)
BlockCount = rCMDL.ReadLong();
MatSetCount = rCMDL.ReadLong();
if (Flags & 0x1) Loader.mFlags |= eSkinnedModel;
if (Flags & 0x2) Loader.mFlags |= eShortNormals;
if (Flags & 0x4) Loader.mFlags |= eHasTex1;
}
@ -459,9 +460,21 @@ CModel* CModelLoader::LoadCMDL(IInputStream& rCMDL)
// Materials
Loader.mMaterials.resize(MatSetCount);
for (u32 iMat = 0; iMat < MatSetCount; iMat++)
for (u32 iSet = 0; iSet < MatSetCount; iSet++)
{
Loader.mMaterials[iMat] = CMaterialLoader::LoadMaterialSet(rCMDL, Loader.mVersion);
Loader.mMaterials[iSet] = CMaterialLoader::LoadMaterialSet(rCMDL, Loader.mVersion);
// Toggle skinning on materials
if (Loader.mFlags.HasAnyFlags(eSkinnedModel))
{
for (u32 iMat = 0; iMat < Loader.mMaterials[iSet]->NumMaterials(); iMat++)
{
CMaterial *pMat = Loader.mMaterials[iSet]->MaterialByIndex(iMat);
CMaterial::FMaterialOptions Options = pMat->Options();
pMat->SetOptions(Options | CMaterial::eSkinningEnabled);
pMat->SetVertexDescription(pMat->VtxDesc() | FVertexDescription(eBoneIndices | eBoneWeights));
}
}
if (Loader.mVersion < eCorruptionProto)
Loader.mpSectionMgr->ToNextSection();

View File

@ -20,7 +20,8 @@ public:
eShortPositions = 0x1,
eShortNormals = 0x2,
eHasTex1 = 0x4,
eHasVisGroups = 0x8
eHasVisGroups = 0x8,
eSkinnedModel = 0x10
};
DECLARE_FLAGS(EModelFlag, FModelFlags)

View File

@ -12,11 +12,23 @@ void CSkeletonLoader::SetLocalBoneCoords(CBone *pBone)
pBone->mPosition -= pBone->mpParent->mPosition;
}
void CSkeletonLoader::CalculateBoneInverseBindMatrices()
{
mpSkeleton->mInvBindMatrices.resize(mpSkeleton->MaxBoneID() + 1);
for (u32 iBone = 0; iBone < mpSkeleton->mBones.size(); iBone++)
{
CBone *pBone = mpSkeleton->mBones[iBone];
mpSkeleton->mInvBindMatrices[pBone->ID()] = CTransform4f::TranslationMatrix(-pBone->AbsolutePosition());
}
}
// ************ STATIC ************
CSkeleton* CSkeletonLoader::LoadCINF(IInputStream& rCINF)
{
CSkeletonLoader Loader;
CSkeleton *pSkel = new CSkeleton();
Loader.mpSkeleton = pSkel;
u32 NumBones = rCINF.ReadLong();
pSkel->mBones.reserve(NumBones);
@ -78,6 +90,7 @@ CSkeleton* CSkeletonLoader::LoadCINF(IInputStream& rCINF)
}
Loader.SetLocalBoneCoords(pSkel->mpRootBone);
Loader.CalculateBoneInverseBindMatrices();
// Skip bone ID array
u32 NumBoneIDs = rCINF.ReadLong();

View File

@ -12,6 +12,7 @@ class CSkeletonLoader
CSkeletonLoader() {}
void SetLocalBoneCoords(CBone *pBone);
void CalculateBoneInverseBindMatrices();
public:
static CSkeleton* LoadCINF(IInputStream& rCINF);

View File

@ -0,0 +1,29 @@
#include "CSkinLoader.h"
#include <Common/Assert.h>
// ************ STATIC ************
CSkin* CSkinLoader::LoadCSKR(IInputStream& rCSKR)
{
if (!rCSKR.IsValid()) return nullptr;
u32 NumVertexGroups = rCSKR.ReadLong();
CSkin *pSkin = new CSkin();
pSkin->mVertGroups.resize(NumVertexGroups);
for (u32 iGrp = 0; iGrp < NumVertexGroups; iGrp++)
{
CSkin::SVertGroup& rGroup = pSkin->mVertGroups[iGrp];
u32 NumWeights = rCSKR.ReadLong();
ASSERT(NumWeights <= 4);
for (u32 iWgt = 0; iWgt < NumWeights; iWgt++)
{
rGroup.Weights.Indices[iWgt] = (u8) rCSKR.ReadLong();
rGroup.Weights.Weights[iWgt] = rCSKR.ReadFloat();
}
rGroup.NumVertices = rCSKR.ReadLong();
}
return pSkin;
}

View File

@ -0,0 +1,14 @@
#ifndef CSKINLOADER_H
#define CSKINLOADER_H
#include "Core/Resource/CSkin.h"
#include "Core/Resource/TResPtr.h"
class CSkinLoader
{
CSkinLoader() {}
public:
static CSkin* LoadCSKR(IInputStream& rCSKR);
};
#endif // CSKINLOADER_H

View File

@ -160,6 +160,46 @@ void CModel::DrawWireframe(FRenderOptions Options, CColor WireColor /*= CColor::
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
}
void CModel::SetSkin(CSkin *pSkin)
{
if (mpSkin != pSkin)
{
const FVertexDescription kBoneFlags = (eBoneIndices | eBoneWeights);
mpSkin = pSkin;
mVBO.SetSkin(pSkin);
ClearGLBuffer();
if (pSkin && !mVBO.VertexDesc().HasAllFlags(kBoneFlags))
mVBO.SetVertexDesc(mVBO.VertexDesc() | kBoneFlags);
else if (!pSkin && mVBO.VertexDesc().HasAnyFlags(kBoneFlags))
mVBO.SetVertexDesc(mVBO.VertexDesc() & ~kBoneFlags);
for (u32 iSet = 0; iSet < mMaterialSets.size(); iSet++)
{
CMaterialSet *pSet = mMaterialSets[iSet];
for (u32 iMat = 0; iMat < pSet->NumMaterials(); iMat++)
{
CMaterial *pMat = pSet->MaterialByIndex(iMat);
FVertexDescription VtxDesc = pMat->VtxDesc();
if (pSkin && !VtxDesc.HasAllFlags(kBoneFlags))
{
VtxDesc |= kBoneFlags;
pMat->SetVertexDescription(VtxDesc);
}
else if (!pSkin && VtxDesc.HasAnyFlags(kBoneFlags))
{
VtxDesc &= ~kBoneFlags;
pMat->SetVertexDescription(VtxDesc);
}
}
}
}
}
u32 CModel::GetMatSetCount()
{
return mMaterialSets.size();

View File

@ -4,6 +4,7 @@
#include "CBasicModel.h"
#include "SSurface.h"
#include "Core/Resource/CMaterialSet.h"
#include "Core/Resource/CSkin.h"
#include "Core/OpenGL/CIndexBuffer.h"
#include "Core/OpenGL/GLCommon.h"
#include "Core/Render/FRenderOptions.h"
@ -13,6 +14,7 @@ class CModel : public CBasicModel
friend class CModelLoader;
friend class CModelCooker;
TResPtr<CSkin> mpSkin;
std::vector<CMaterialSet*> mMaterialSets;
std::vector<std::vector<CIndexBuffer>> mSurfaceIndexBuffers;
bool mHasOwnMaterials;
@ -28,6 +30,7 @@ public:
void Draw(FRenderOptions Options, u32 MatSet);
void DrawSurface(FRenderOptions Options, u32 Surface, u32 MatSet);
void DrawWireframe(FRenderOptions Options, CColor WireColor = CColor::skWhite);
void SetSkin(CSkin *pSkin);
u32 GetMatSetCount();
u32 GetMatCount();
@ -37,6 +40,8 @@ public:
bool HasTransparency(u32 MatSet);
bool IsSurfaceTransparent(u32 Surface, u32 MatSet);
bool IsSkinned() const { return (mpSkin != nullptr); }
private:
CIndexBuffer* InternalGetIBO(u32 Surface, EGXPrimitiveType Primitive);
};

View File

@ -4,6 +4,10 @@
#include <Common/CColor.h>
#include <Math/CVector2f.h>
#include <Math/CVector3f.h>
#include <array>
typedef std::array<u32, 4> TBoneIndices;
typedef std::array<float, 4> TBoneWeights;
class CVertex
{
@ -14,6 +18,8 @@ public:
CVector3f Normal;
CColor Color[2];
CVector2f Tex[8];
TBoneIndices BoneIndices;
TBoneWeights BoneWeights;
u8 MatrixIndices[8];
CVertex() {}
@ -35,7 +41,9 @@ public:
(Tex[4] == rkOther.Tex[4]) &&
(Tex[5] == rkOther.Tex[5]) &&
(Tex[6] == rkOther.Tex[6]) &&
(Tex[7] == rkOther.Tex[7]));
(Tex[7] == rkOther.Tex[7]) &&
(BoneIndices == rkOther.BoneIndices) &&
(BoneWeights == rkOther.BoneWeights));
}
};

View File

@ -0,0 +1,32 @@
#include "EVertexAttribute.h"
#include <Common/Log.h>
const u32 gkNumVertexAttribs = 22;
u32 VertexAttributeSize(EVertexAttribute Attrib)
{
switch (Attrib)
{
case ePosition:
case eNormal:
return 0x0C;
case eColor0:
case eColor1:
case eBoneWeights:
return 0x10;
case eTex0:
case eTex1:
case eTex2:
case eTex3:
case eTex4:
case eTex5:
case eTex6:
case eTex7:
return 0x08;
case eBoneIndices:
return 0x04;
default:
Log::Error("AttributeSize(): Unknown vertex attribute: " + TString::FromInt32(Attrib, 0, 10));
return 0x00;
}
}

View File

@ -6,28 +6,33 @@
enum EVertexAttribute
{
eNoAttributes = 0x0,
ePosition = 0x3,
eNormal = 0xC,
eColor0 = 0x30,
eColor1 = 0xC0,
eTex0 = 0x300,
eTex1 = 0xC00,
eTex2 = 0x3000,
eTex3 = 0xC000,
eTex4 = 0x30000,
eTex5 = 0xC0000,
eTex6 = 0x300000,
eTex7 = 0xC00000,
ePosMtx = 0x1000000,
eTex0Mtx = 0x2000000,
eTex1Mtx = 0x4000000,
eTex2Mtx = 0x8000000,
eTex3Mtx = 0x10000000,
eTex4Mtx = 0x20000000,
eTex5Mtx = 0x40000000,
eTex6Mtx = 0x80000000
ePosition = 0x1,
eNormal = 0x2,
eColor0 = 0x4,
eColor1 = 0x8,
eTex0 = 0x10,
eTex1 = 0x20,
eTex2 = 0x40,
eTex3 = 0x80,
eTex4 = 0x100,
eTex5 = 0x200,
eTex6 = 0x400,
eTex7 = 0x800,
eBoneIndices = 0x1000,
eBoneWeights = 0x2000,
ePosMtx = 0x4000,
eTex0Mtx = 0x8000,
eTex1Mtx = 0x10000,
eTex2Mtx = 0x20000,
eTex3Mtx = 0x40000,
eTex4Mtx = 0x80000,
eTex5Mtx = 0x100000,
eTex6Mtx = 0x200000
};
DECLARE_FLAGS(EVertexAttribute, FVertexDescription)
extern const u32 gkNumVertexAttribs;
u32 VertexAttributeSize(EVertexAttribute Attrib);
#endif // EVERTEXATTRIBUTE

View File

@ -23,22 +23,64 @@ void CCharacterNode::PostLoad()
}
}
void CCharacterNode::AddToRenderer(CRenderer *pRenderer, const SViewInfo& /*rkViewInfo*/)
void CCharacterNode::AddToRenderer(CRenderer *pRenderer, const SViewInfo& rkViewInfo)
{
if (!mpCharacter) return;
// todo: frustum check. Currently don't have a means of pulling the AABox for the
// current animation so this isn't in yet.
if (mpCharacter->NodeSkeleton(mActiveCharSet))
CModel *pModel = mpCharacter->NodeModel(mActiveCharSet);
CSkeleton *pSkel = mpCharacter->NodeSkeleton(mActiveCharSet);
if (pModel)
{
pRenderer->AddOpaqueMesh(this, 0, AABox(), eDrawMesh);
if (!pModel->HasTransparency(0))
pRenderer->AddOpaqueMesh(this, -1, AABox(), eDrawMesh);
else
AddSurfacesToRenderer(pRenderer, pModel, 0, rkViewInfo, false);
}
if (pSkel)
{
CAnimation *pAnim = mpCharacter->Animation(mActiveAnim);
pSkel->UpdateTransform(mTransformData, pAnim, mAnimTime, false);
if (rkViewInfo.ShowFlags.HasFlag(eShowSkeletons))
pRenderer->AddOpaqueMesh(this, -2, AABox(), eDrawMesh);
}
}
void CCharacterNode::Draw(FRenderOptions Options, int /*ComponentIndex*/, const SViewInfo& /*rkViewInfo*/)
void CCharacterNode::Draw(FRenderOptions Options, int ComponentIndex, const SViewInfo& rkViewInfo)
{
CSkeleton *pSkel = mpCharacter->NodeSkeleton(mActiveCharSet);
CAnimation *pAnim = mpCharacter->Animation(mActiveAnim);
pSkel->UpdateTransform(mTransformData, pAnim, mAnimTime, false);
pSkel->Draw(Options, mTransformData);
// Draw skeleton
if (ComponentIndex == -2)
{
pSkel->Draw(Options, mTransformData);
}
// Draw mesh
else
{
// Set lighting
CGraphics::SetDefaultLighting();
CGraphics::UpdateLightBlock();
CGraphics::sVertexBlock.COLOR0_Amb = CGraphics::skDefaultAmbientColor;
CGraphics::sPixelBlock.LightmapMultiplier = 1.f;
CGraphics::sPixelBlock.TevColor = CColor::skWhite;
CGraphics::sPixelBlock.TintColor = TintColor(rkViewInfo);
// Draw surface OR draw entire model
CGraphics::LoadBoneTransforms(mTransformData);
CGraphics::LoadBoneInverseBindTransforms(pSkel);
CModel *pModel = mpCharacter->NodeModel(mActiveCharSet);
if (ComponentIndex < 0)
pModel->Draw(Options, 0);
else
pModel->DrawSurface(Options, ComponentIndex, 0);
}
}
SRayIntersection CCharacterNode::RayNodeIntersectTest(const CRay& rkRay, u32 /*AssetID*/, const SViewInfo& /*rkViewInfo*/)

View File

@ -83,6 +83,9 @@ void CModelNode::Draw(FRenderOptions Options, int ComponentIndex, const SViewInf
CGraphics::sPixelBlock.TintColor = TintColor(rkViewInfo);
LoadModelMatrix();
if (mpModel->IsSkinned())
CGraphics::LoadIdentityBoneTransforms();
if (ComponentIndex < 0)
mpModel->Draw(Options, mActiveMatSet);
else

View File

@ -224,7 +224,7 @@ void CSceneNode::DrawRotationArrow() const
spArrowModel->Draw(eNoRenderOptions, 0);
}
void CSceneNode::AddSurfacesToRenderer(CRenderer *pRenderer, CModel *pModel, u32 MatSet, const SViewInfo& rkViewInfo)
void CSceneNode::AddSurfacesToRenderer(CRenderer *pRenderer, CModel *pModel, u32 MatSet, const SViewInfo& rkViewInfo, bool DoFrustumTest /*= true*/)
{
u32 SurfaceCount = pModel->GetSurfaceCount();
@ -232,7 +232,7 @@ void CSceneNode::AddSurfacesToRenderer(CRenderer *pRenderer, CModel *pModel, u32
{
CAABox TransformedBox = pModel->GetSurfaceAABox(iSurf).Transformed(Transform());
if (rkViewInfo.ViewFrustum.BoxInFrustum(TransformedBox))
if (!DoFrustumTest || rkViewInfo.ViewFrustum.BoxInFrustum(TransformedBox))
{
if (!pModel->IsSurfaceTransparent(iSurf, MatSet))
pRenderer->AddOpaqueMesh(this, (int) iSurf, TransformedBox, eDrawMesh);

View File

@ -79,7 +79,7 @@ public:
void LoadLights(const SViewInfo& rkViewInfo);
void DrawBoundingBox() const;
void DrawRotationArrow() const;
void AddSurfacesToRenderer(CRenderer *pRenderer, CModel *pModel, u32 MatSet, const SViewInfo& rkViewInfo);
void AddSurfacesToRenderer(CRenderer *pRenderer, CModel *pModel, u32 MatSet, const SViewInfo& rkViewInfo, bool DoFrustumTest = true);
// Transform
void Translate(const CVector3f& rkTranslation, ETransformSpace TransformSpace);

View File

@ -14,7 +14,8 @@ enum EShowFlag
eShowObjects = 0x18,
eShowLights = 0x20,
eShowSky = 0x40,
eShowAll = 0x7F
eShowSkeletons = 0x80,
eShowAll = 0xFFFFFFFF
};
DECLARE_FLAGS(EShowFlag, FShowFlags)

View File

@ -178,6 +178,14 @@ void CBasicViewport::contextMenuEvent(QContextMenuEvent *pEvent)
pEvent->ignore();
}
void CBasicViewport::SetShowFlag(EShowFlag Flag, bool Visible)
{
if (Visible)
mViewInfo.ShowFlags |= Flag;
else
mViewInfo.ShowFlags &= ~Flag;
}
void CBasicViewport::SetGameMode(bool Enabled)
{
mViewInfo.GameMode = Enabled;

View File

@ -52,6 +52,7 @@ public:
void focusOutEvent(QFocusEvent *pEvent);
void contextMenuEvent(QContextMenuEvent *pEvent);
void SetShowFlag(EShowFlag Flag, bool Visible);
void SetGameMode(bool Enabled);
void SetCursorState(const QCursor& rkCursor);
void SetCursorVisible(bool Visible);

View File

@ -42,14 +42,6 @@ void CSceneViewport::SetScene(INodeEditor *pEditor, CScene *pScene)
mpScene = pScene;
}
void CSceneViewport::SetShowFlag(EShowFlag Flag, bool Visible)
{
if (Visible)
mViewInfo.ShowFlags |= Flag;
else
mViewInfo.ShowFlags &= ~Flag;
}
void CSceneViewport::SetShowWorld(bool Visible)
{
if (mRenderingMergedWorld)

View File

@ -47,7 +47,6 @@ public:
CSceneViewport(QWidget *pParent = 0);
~CSceneViewport();
void SetScene(INodeEditor *pEditor, CScene *pScene);
void SetShowFlag(EShowFlag Flag, bool Visible);
void SetShowWorld(bool Visible);
void SetRenderMergedWorld(bool RenderMerged);
FShowFlags ShowFlags() const;

View File

@ -39,6 +39,7 @@ CCharacterEditor::CCharacterEditor(QWidget *parent)
connect(ui->Viewport, SIGNAL(HoverBoneChanged(u32)), this, SLOT(HoverBoneChanged(u32)));
connect(ui->ActionOpen, SIGNAL(triggered()), this, SLOT(Open()));
connect(ui->ActionShowSkeleton, SIGNAL(toggled(bool)), this, SLOT(ToggleSkeletonVisible(bool)));
connect(mpCharComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(SetActiveCharacterIndex(int)));
connect(mpAnimComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(SetActiveAnimation(int)));
@ -143,6 +144,11 @@ void CCharacterEditor::Open()
gResCache.Clean();
}
void CCharacterEditor::ToggleSkeletonVisible(bool Visible)
{
ui->Viewport->SetShowFlag(eShowSkeletons, Visible);
}
void CCharacterEditor::RefreshViewport()
{
UpdateAnimTime();
@ -171,7 +177,7 @@ void CCharacterEditor::SetActiveAnimation(int AnimIndex)
mLastAnimUpdate = CTimer::GlobalTime();
ui->AnimSlider->blockSignals(true);
ui->AnimSlider->setMaximum((int) (mpSet->Animation(AnimIndex)->Duration() * 1000));
ui->AnimSlider->setMaximum((int) (CurrentAnimation() ? CurrentAnimation()->Duration() * 1000 : 0));
ui->AnimSlider->blockSignals(false);
SetAnimTime(0.f);

View File

@ -28,6 +28,7 @@ class CCharacterEditor : public QMainWindow
TResPtr<CAnimSet> mpSet;
u32 mCurrentChar;
u32 mCurrentAnim;
bool mShowSkeleton;
// Playback Controls
double mLastAnimUpdate;
@ -44,6 +45,7 @@ public:
public slots:
void Open();
void ToggleSkeletonVisible(bool Visible);
void RefreshViewport();
void HoverBoneChanged(u32 BoneID);
void SetActiveCharacterIndex(int CharIndex);

View File

@ -154,6 +154,8 @@
<bool>false</bool>
</attribute>
<addaction name="ActionOpen"/>
<addaction name="separator"/>
<addaction name="ActionShowSkeleton"/>
</widget>
<action name="ActionOpen">
<property name="icon">
@ -170,6 +172,17 @@
<string>Ctrl+O</string>
</property>
</action>
<action name="ActionShowSkeleton">
<property name="checkable">
<bool>true</bool>
</property>
<property name="text">
<string>Show Skeleton</string>
</property>
<property name="toolTip">
<string>Show Skeleton</string>
</property>
</action>
</widget>
<customwidgets>
<customwidget>

View File

@ -9,6 +9,7 @@ CCharacterEditorViewport::CCharacterEditorViewport(QWidget *pParent /*= 0*/)
mpRenderer->SetClearColor(CColor(0.3f, 0.3f, 0.3f));
mpRenderer->ToggleGrid(true);
mViewInfo.ShowFlags = eShowNone; // The mesh doesn't check any show flags so this just disables the skeleton.
mViewInfo.pRenderer = mpRenderer;
mViewInfo.pScene = nullptr;
mViewInfo.GameMode = false;

View File

@ -22,7 +22,7 @@ CVector2f::CVector2f(IInputStream& rInput)
Y = rInput.ReadFloat();
}
void CVector2f::Write(IOutputStream& rOutput)
void CVector2f::Write(IOutputStream& rOutput) const
{
rOutput.WriteFloat(X);
rOutput.WriteFloat(Y);

View File

@ -12,7 +12,7 @@ public:
CVector2f(float XY);
CVector2f(float _X, float _Y);
CVector2f(IInputStream& rInput);
void Write(IOutputStream& rOutput);
void Write(IOutputStream& rOutput) const;
float Magnitude() const;
float SquaredMagnitude() const;

View File

@ -29,7 +29,7 @@ CVector3f::CVector3f(IInputStream& rInput)
Z = rInput.ReadFloat();
}
void CVector3f::Write(IOutputStream& rOutput)
void CVector3f::Write(IOutputStream& rOutput) const
{
rOutput.WriteFloat(X);
rOutput.WriteFloat(Y);

View File

@ -19,7 +19,7 @@ public:
CVector3f(float XYZ);
CVector3f(float _X, float _Y, float _Z);
CVector3f(IInputStream& rInput);
void Write(IOutputStream& rOutput);
void Write(IOutputStream& rOutput) const;
// Swizzle
CVector2f XY();

View File

@ -37,20 +37,20 @@ float RadiansToDegrees(float Rad)
return Rad * 180.f / skPi;
}
std::pair<bool,float> RayPlaneIntersection(const CRay& rkRay, const CPlane& plane)
std::pair<bool,float> RayPlaneIntersection(const CRay& rkRay, const CPlane& rkPlane)
{
// Code based on ray/plane intersect code from Ogre
// https://bitbucket.org/sinbad/ogre/src/197116fd2ac62c57cdeed1666f9866c3dddd4289/OgreMain/src/OgreMath.cpp?at=default#OgreMath.cpp-350
// Are ray and plane parallel?
float Denom = plane.Normal().Dot(rkRay.Direction());
float Denom = rkPlane.Normal().Dot(rkRay.Direction());
if (Abs(Denom) < FLT_EPSILON)
return std::pair<bool,float>(false, 0.f);
// Not parallel
float nom = plane.Normal().Dot(rkRay.Origin()) + plane.Dist();
float t = -(nom / Denom);
float Nom = rkPlane.Normal().Dot(rkRay.Origin()) + rkPlane.Dist();
float t = -(Nom / Denom);
return std::pair<bool,float>(t >= 0.f, t);
}