mirror of
https://github.com/thunderbrewhq/thunderbrew
synced 2025-04-16 10:04:42 +03:00
543 lines
17 KiB
C++
543 lines
17 KiB
C++
#include "model/CM2SceneRender.hpp"
|
|
#include "gx/Draw.hpp"
|
|
#include "gx/RenderState.hpp"
|
|
#include "gx/Shader.hpp"
|
|
#include "gx/Texture.hpp"
|
|
#include "gx/Transform.hpp"
|
|
#include "model/CM2Cache.hpp"
|
|
#include "model/CM2Model.hpp"
|
|
#include "model/CM2Shared.hpp"
|
|
#include "model/M2Types.hpp"
|
|
#include <tempest/Math.hpp>
|
|
|
|
C44Matrix CM2SceneRender::s_identity;
|
|
|
|
int32_t CM2SceneRender::s_fogModeList[M2BLEND_COUNT] = {
|
|
1, // M2BLEND_OPAQUE
|
|
1, // M2BLEND_ALPHA_KEY
|
|
1, // M2BLEND_ALPHA
|
|
2, // M2BLEND_NO_ALPHA_ADD
|
|
2, // M2BLEND_ADD
|
|
3, // M2BLEND_MOD
|
|
4 // M2BLEND_MOD_2X
|
|
};
|
|
|
|
EGxBlend CM2SceneRender::s_gxBlend[M2PASS_COUNT][M2BLEND_COUNT] = {
|
|
// M2PASS_0
|
|
{
|
|
GxBlend_Opaque, // M2BLEND_OPAQUE
|
|
GxBlend_AlphaKey, // M2BLEND_ALPHA_KEY
|
|
GxBlend_Alpha, // M2BLEND_ALPHA
|
|
GxBlend_NoAlphaAdd, // M2BLEND_NO_ALPHA_ADD
|
|
GxBlend_Add, // M2BLEND_ADD
|
|
GxBlend_Mod, // M2BLEND_MOD
|
|
GxBlend_Mod2x // M2BLEND_MOD_2X
|
|
},
|
|
|
|
// M2PASS_1
|
|
{
|
|
GxBlend_Alpha, // M2BLEND_OPAQUE
|
|
GxBlend_Alpha, // M2BLEND_ALPHA_KEY
|
|
GxBlend_Alpha, // M2BLEND_ALPHA
|
|
GxBlend_NoAlphaAdd, // M2BLEND_NO_ALPHA_ADD
|
|
GxBlend_Add, // M2BLEND_ADD
|
|
GxBlend_Mod, // M2BLEND_MOD
|
|
GxBlend_Mod2x // M2BLEND_MOD_2X
|
|
},
|
|
|
|
// M2PASS_2
|
|
{
|
|
GxBlend_Alpha, // M2BLEND_OPAQUE
|
|
GxBlend_Alpha, // M2BLEND_ALPHA_KEY
|
|
GxBlend_Alpha, // M2BLEND_ALPHA
|
|
GxBlend_NoAlphaAdd, // M2BLEND_NO_ALPHA_ADD
|
|
GxBlend_Add, // M2BLEND_ADD
|
|
GxBlend_Mod, // M2BLEND_MOD
|
|
GxBlend_Mod2x // M2BLEND_MOD_2X
|
|
}
|
|
};
|
|
|
|
int32_t CM2SceneRender::s_shadedList[M2BLEND_COUNT] = {
|
|
1, // M2BLEND_OPAQUE
|
|
1, // M2BLEND_ALPHA_KEY
|
|
1, // M2BLEND_ALPHA
|
|
1, // M2BLEND_NO_ALPHA_ADD
|
|
1, // M2BLEND_ADD
|
|
0, // M2BLEND_MOD
|
|
0 // M2BLEND_MOD_2X
|
|
};
|
|
|
|
void CM2SceneRender::Draw(M2PASS pass, M2Element* elements, uint32_t* a4, uint32_t a5) {
|
|
if (!a5) {
|
|
return;
|
|
}
|
|
|
|
GxRsPush();
|
|
|
|
C44Matrix savedView;
|
|
GxXformView(savedView);
|
|
|
|
for (int32_t xf = GxXform_Tex0; xf <= GxXform_World; xf++) {
|
|
GxXformPush(static_cast<EGxXform>(xf));
|
|
}
|
|
|
|
GxXformSetView(CM2SceneRender::s_identity);
|
|
GxXformSet(GxXform_World, CM2SceneRender::s_identity);
|
|
|
|
GxRsSet(GxRs_DepthFunc, 0);
|
|
GxRsSet(GxRs_PolygonOffset, 0);
|
|
GxRsSet(GxRs_Lighting, 0);
|
|
GxRsSet(GxRs_Fog, 0);
|
|
GxRsSet(GxRs_MatSpecularExp, 0.0f);
|
|
|
|
if (CShaderEffect::s_enableShaders) {
|
|
C44Matrix projNative;
|
|
GxXformProjNative(projNative);
|
|
this->matrix0 = projNative.Inverse(projNative.Determinant()).Transpose();
|
|
|
|
CShaderEffect::UpdateProjMatrix();
|
|
|
|
// TODO
|
|
// CShadowCache::SetShadowMapGenericGlobal();
|
|
}
|
|
|
|
this->m_curPass = pass;
|
|
|
|
for (int32_t i = 0; i < a5; i++) {
|
|
uint32_t index = a4[i];
|
|
auto element = &elements[index];
|
|
|
|
if (element->type == 2 || element->type == 4 || !element->model->m_flag2000) {
|
|
this->m_curElement = element;
|
|
this->m_curType = element->type;
|
|
this->m_curModel = element->model;
|
|
this->m_curShared = element->model->m_shared;
|
|
this->m_curShaded = 1;
|
|
this->m_curFogMode = 1;
|
|
this->m_curBatch = nullptr;
|
|
this->m_curSkinSection = nullptr;
|
|
this->m_curLighting = element->model->m_currentLighting;
|
|
// TODO
|
|
// this->m_curMaterial = this->dwordB8;
|
|
this->m_data = element->model->m_shared->m_data;
|
|
|
|
// TODO
|
|
// this->m_cache->LinkToSharedUpdateList(this->m_curShared);
|
|
|
|
switch (this->m_curElement->type) {
|
|
case 0: {
|
|
this->DrawBatch();
|
|
break;
|
|
}
|
|
|
|
case 1: {
|
|
this->DrawBatchProj();
|
|
break;
|
|
}
|
|
|
|
case 2: {
|
|
this->DrawBatchDoodad(elements, &a4[i]);
|
|
// TODO
|
|
// i += this->m_curElement->dword1C - 1;
|
|
break;
|
|
}
|
|
|
|
case 3: {
|
|
this->DrawRibbon();
|
|
break;
|
|
}
|
|
|
|
case 4: {
|
|
i += this->DrawParticle(i, elements, a4, a5);
|
|
break;
|
|
}
|
|
|
|
case 5: {
|
|
this->DrawCallback();
|
|
break;
|
|
}
|
|
|
|
default:
|
|
continue;
|
|
}
|
|
|
|
this->m_prevElement = this->m_curElement;
|
|
this->m_prevType = this->m_curType;
|
|
this->m_prevModel = this->m_curModel;
|
|
this->m_prevShared = this->m_curShared;
|
|
this->m_curLighting = this->m_prevLighting; // TODO investigate (maybe bug?)
|
|
this->m_prevShaded = this->m_curShaded;
|
|
this->m_prevFogMode = this->m_curFogMode;
|
|
this->m_prevBatch = this->m_curBatch;
|
|
this->m_prevSkinSection = this->m_curSkinSection;
|
|
this->m_prevMaterial = this->m_curMaterial;
|
|
}
|
|
}
|
|
|
|
for (int32_t xf = GxXform_Tex0; xf <= GxXform_World; xf++) {
|
|
GxXformPop(static_cast<EGxXform>(xf));
|
|
}
|
|
|
|
GxXformSetView(savedView);
|
|
|
|
GxRsPop();
|
|
GxRsSet(GxRs_Fog, 0);
|
|
}
|
|
|
|
void CM2SceneRender::DrawBatch() {
|
|
auto element = this->m_curElement;
|
|
|
|
this->m_curBatch = element->batch;
|
|
this->m_curSkinSection = element->skinSection;
|
|
this->m_curMaterial = &this->m_data->materials[element->batch->materialIndex];
|
|
|
|
element->effect->SetCurrent();
|
|
this->SetupLighting();
|
|
this->SetupMaterial();
|
|
this->SetupTextures();
|
|
|
|
if (
|
|
CShaderEffect::s_enableShaders
|
|
&& (
|
|
this->m_curType != this->m_prevType
|
|
|| this->m_curModel != this->m_prevModel
|
|
|| this->m_curSkinSection->boneComboIndex != this->m_prevSkinSection->boneComboIndex
|
|
)
|
|
) {
|
|
C4Vector* constants = reinterpret_cast<C4Vector*>(GxShaderConstantsLock(GxSh_Vertex));
|
|
|
|
for (int32_t i = 0; i < this->m_curSkinSection->boneCount; i++) {
|
|
auto& boneMatrix = this->m_curModel->m_boneMatrices[this->m_data->boneCombos[this->m_curSkinSection->boneComboIndex + i]];
|
|
|
|
constants[31 + (i * 3) + 0] = { boneMatrix.a0, boneMatrix.b0, boneMatrix.c0, boneMatrix.d0 };
|
|
constants[31 + (i * 3) + 1] = { boneMatrix.a1, boneMatrix.b1, boneMatrix.c1, boneMatrix.d1 };
|
|
constants[31 + (i * 3) + 2] = { boneMatrix.a2, boneMatrix.b2, boneMatrix.c2, boneMatrix.d2 };
|
|
}
|
|
|
|
GxShaderConstantsUnlock(GxSh_Vertex, 31, this->m_curSkinSection->boneCount * 3);
|
|
}
|
|
|
|
if (this->m_curElement->flags & 0x4) {
|
|
if (
|
|
this->m_curType != this->m_prevType
|
|
|| this->m_curModel != this->m_prevModel
|
|
) {
|
|
this->m_curModel->SetIndices();
|
|
}
|
|
} else if (
|
|
this->m_curType != this->m_prevType
|
|
|| this->m_curShared != this->m_prevShared
|
|
|| this->m_prevElement->flags & 0x4
|
|
) {
|
|
this->m_curShared->SetIndices();
|
|
}
|
|
|
|
int32_t v9 = this->m_curModel->m_shared->m_data->bones.count == 1 && this->m_cache->m_flags & 0x40;
|
|
this->SetBatchVertices(v9);
|
|
|
|
CShaderEffect::SetShaders(this->m_curElement->vertexPermute, this->m_curElement->pixelPermute);
|
|
|
|
if (CShaderEffect::s_enableShaders) {
|
|
auto skinSection = this->m_curSkinSection;
|
|
|
|
CGxBatch batch;
|
|
|
|
batch.m_primType = GxPrim_Triangles;
|
|
batch.m_start = skinSection->indexStart;
|
|
batch.m_count = skinSection->indexCount;
|
|
batch.m_minIndex = skinSection->vertexStart;
|
|
batch.m_maxIndex = skinSection->vertexStart + skinSection->vertexCount - 1;
|
|
|
|
GxDraw(&batch, 1);
|
|
} else if (v9) {
|
|
// TODO
|
|
} else {
|
|
// TODO
|
|
}
|
|
}
|
|
|
|
void CM2SceneRender::DrawBatchDoodad(M2Element* elements, uint32_t* a3) {
|
|
// TODO
|
|
}
|
|
|
|
void CM2SceneRender::DrawBatchProj() {
|
|
// TODO
|
|
}
|
|
|
|
void CM2SceneRender::DrawCallback() {
|
|
// TODO
|
|
}
|
|
|
|
int32_t CM2SceneRender::DrawParticle(uint32_t a2, M2Element* elements, uint32_t* a4, uint32_t a5) {
|
|
// TODO
|
|
return 0;
|
|
}
|
|
|
|
void CM2SceneRender::DrawRibbon() {
|
|
// TODO
|
|
}
|
|
|
|
void CM2SceneRender::SetBatchVertices(int32_t a2) {
|
|
if (CShaderEffect::s_enableShaders) {
|
|
if (this->m_curType != this->m_prevType || this->m_curShared != this->m_prevShared) {
|
|
this->m_curShared->SetVertices(0);
|
|
}
|
|
} else {
|
|
// TODO
|
|
// - non-shader code path
|
|
}
|
|
}
|
|
|
|
void CM2SceneRender::SetupLighting() {
|
|
if (this->m_curMaterial->flags & 0x1) {
|
|
this->m_curShaded = 0;
|
|
} else {
|
|
this->m_curShaded = CM2SceneRender::s_shadedList[this->m_curMaterial->blendMode];
|
|
}
|
|
|
|
CShaderEffect::SetLocalLighting(this->m_curLighting, this->m_curShaded, 0);
|
|
|
|
// TODO
|
|
// dwordD43010 = this->m_curElement->dword3C;
|
|
|
|
if ((this->m_curMaterial->flags & 0x2) || this->m_curLighting->m_fogScale <= 0.0f) {
|
|
this->m_curFogMode = 0;
|
|
} else {
|
|
this->m_curFogMode = CM2SceneRender::s_fogModeList[this->m_curMaterial->blendMode];
|
|
}
|
|
|
|
if (this->m_curFogMode == 0) {
|
|
CShaderEffect::SetFogEnabled(0);
|
|
} else {
|
|
CImVector fogColor;
|
|
|
|
switch (this->m_curFogMode) {
|
|
case 1: {
|
|
float x = this->m_curLighting->m_fogColor.x;
|
|
float y = this->m_curLighting->m_fogColor.y;
|
|
float z = this->m_curLighting->m_fogColor.z;
|
|
|
|
fogColor.b = z <= 0.0f ? 0x00 : z >= 1.0f ? 0xFF : CMath::fuint_n(z * 255.0f);
|
|
fogColor.g = y <= 0.0f ? 0x00 : y >= 1.0f ? 0xFF : CMath::fuint_n(y * 255.0f);
|
|
fogColor.r = x <= 0.0f ? 0x00 : x >= 1.0f ? 0xFF : CMath::fuint_n(x * 255.0f);
|
|
fogColor.a = 0xFF;
|
|
|
|
break;
|
|
}
|
|
|
|
case 2: {
|
|
fogColor = { 0x00, 0x00, 0x00, 0x00 };
|
|
break;
|
|
}
|
|
|
|
case 3: {
|
|
fogColor = { 0xFF, 0xFF, 0xFF, 0x00 };
|
|
break;
|
|
}
|
|
|
|
case 4: {
|
|
fogColor = { 0x80, 0x80, 0x80, 0x00 };
|
|
break;
|
|
}
|
|
}
|
|
|
|
CShaderEffect::SetFogParams(
|
|
this->m_curLighting->m_fogStart,
|
|
this->m_curLighting->m_fogEnd,
|
|
this->m_curLighting->m_fogDensity,
|
|
fogColor
|
|
);
|
|
|
|
CShaderEffect::SetFogEnabled(1);
|
|
}
|
|
|
|
if (
|
|
this->m_curType != this->m_prevType
|
|
|| this->m_curModel != this->m_prevModel
|
|
|| this->m_curLighting != this->m_prevLighting
|
|
|| ((this->m_curElement->flags & 0x2) != (this->m_prevElement->flags & 0x2))
|
|
) {
|
|
if (this->m_curElement->flags & 0x2) {
|
|
// TODO
|
|
// - enable clip plane mask for liquid plane
|
|
} else {
|
|
GxRsSet(GxRs_ClipPlaneMask, 0);
|
|
}
|
|
}
|
|
}
|
|
|
|
void CM2SceneRender::SetupMaterial() {
|
|
if (
|
|
this->m_curType != this->m_prevType
|
|
|| this->m_curMaterial != this->m_prevMaterial
|
|
|| (this->m_curElement->flags & 0x1) != (this->m_prevElement->flags & 0x1)
|
|
|| this->m_curElement->alpha != this->m_prevElement->alpha
|
|
) {
|
|
EGxBlend blendingMode = this->m_curElement->flags & 0x1
|
|
? GxBlend_AlphaKey
|
|
: CM2SceneRender::s_gxBlend[this->m_curPass][this->m_curMaterial->blendMode];
|
|
|
|
int32_t colorWrite = (this->m_curElement->flags & 0x1)
|
|
? 0
|
|
: 15;
|
|
|
|
GxRsSet(GxRs_ColorWrite, colorWrite);
|
|
GxRsSet(GxRs_BlendingMode, blendingMode);
|
|
|
|
float alphaRef;
|
|
if (this->m_curMaterial->blendMode == 0) {
|
|
alphaRef = 0.0f;
|
|
} else if (this->m_curMaterial->blendMode == 1) {
|
|
alphaRef = this->m_curElement->alpha * 0.87843138f;
|
|
} else {
|
|
alphaRef = 0.0039215689f;
|
|
}
|
|
|
|
CShaderEffect::SetAlphaRef(alphaRef);
|
|
}
|
|
|
|
if (
|
|
this->m_curType != this->m_prevType
|
|
|| this->m_curMaterial != this->m_prevMaterial
|
|
) {
|
|
int32_t culling = (this->m_curMaterial->flags & 0x4) == 0;
|
|
GxRsSet(GxRs_Culling, culling);
|
|
|
|
int32_t depthTest = (this->m_curMaterial->flags & 0x8) == 0;
|
|
GxRsSet(GxRs_DepthTest, depthTest);
|
|
|
|
int32_t depthWrite = (this->m_curMaterial->flags & 0x10) == 0;
|
|
GxRsSet(GxRs_DepthWrite, depthWrite);
|
|
}
|
|
|
|
if (!CShaderEffect::s_enableShaders) {
|
|
// TODO
|
|
}
|
|
|
|
if (this->m_curElement->type > 2) {
|
|
if (
|
|
this->m_curType != this->m_prevType
|
|
|| this->m_curElement->alpha != this->m_prevElement->alpha
|
|
) {
|
|
C4Vector diffuse = { 1.0f, 1.0f, 1.0f, this->m_curElement->alpha };
|
|
CShaderEffect::SetDiffuse(diffuse);
|
|
|
|
C4Vector emissive = { 0.0f, 0.0f, 0.0f, 0.0f };
|
|
CShaderEffect::SetEmissive(emissive);
|
|
}
|
|
} else if (
|
|
this->m_curType != this->m_prevType
|
|
|| this->m_curShared != this->m_prevShared
|
|
|| this->m_curShaded != this->m_prevShaded
|
|
|| this->m_curMaterial->blendMode != this->m_prevMaterial->blendMode
|
|
|| this->m_prevBatch == nullptr
|
|
|| this->m_curBatch->colorIndex != this->m_prevBatch->colorIndex
|
|
|| this->m_prevElement->alpha != this->m_curElement->alpha
|
|
|| this->m_curModel->m_currentDiffuse != this->m_prevModel->m_currentDiffuse
|
|
|| this->m_curModel->m_currentEmissive != this->m_prevModel->m_currentEmissive
|
|
) {
|
|
if (this->m_curMaterial->blendMode == M2BLEND_MOD) {
|
|
C4Vector diffuse = { 0.0f, 0.0f, 0.0f, this->m_curElement->alpha };
|
|
CShaderEffect::SetDiffuse(diffuse);
|
|
|
|
C4Vector emissive = { 1.0f, 1.0f, 1.0f, 0.0f };
|
|
CShaderEffect::SetEmissive(emissive);
|
|
} else if (this->m_curMaterial->blendMode == M2BLEND_MOD_2X) {
|
|
C4Vector diffuse = { 0.0f, 0.0f, 0.0f, this->m_curElement->alpha };
|
|
CShaderEffect::SetDiffuse(diffuse);
|
|
|
|
C4Vector emissive = { 0.5f, 0.5f, 0.5f, 0.0f };
|
|
CShaderEffect::SetEmissive(emissive);
|
|
} else {
|
|
auto modelDiffuse = this->m_curModel->m_currentDiffuse;
|
|
auto modelEmissive = this->m_curModel->m_currentEmissive;
|
|
|
|
if (this->m_curBatch->colorIndex < this->m_data->colors.Count()) {
|
|
auto& modelColor = this->m_curModel->m_colors[this->m_curBatch->colorIndex];
|
|
|
|
modelDiffuse.x *= modelColor.colorTrack.currentValue.x;
|
|
modelDiffuse.y *= modelColor.colorTrack.currentValue.y;
|
|
modelDiffuse.z *= modelColor.colorTrack.currentValue.z;
|
|
}
|
|
|
|
if (!this->m_curShaded) {
|
|
modelEmissive.x += modelDiffuse.x;
|
|
modelEmissive.y += modelDiffuse.y;
|
|
modelEmissive.z += modelDiffuse.z;
|
|
|
|
modelDiffuse.x = 0.0f;
|
|
modelDiffuse.y = 0.0f;
|
|
modelDiffuse.z = 0.0f;
|
|
}
|
|
|
|
C4Vector diffuse = { modelDiffuse.x, modelDiffuse.y, modelDiffuse.z, this->m_curElement->alpha };
|
|
CShaderEffect::SetDiffuse(diffuse);
|
|
|
|
C4Vector emissive = { modelEmissive.x, modelEmissive.y, modelEmissive.z, 0.0f };
|
|
CShaderEffect::SetEmissive(emissive);
|
|
}
|
|
}
|
|
}
|
|
|
|
void CM2SceneRender::SetupTextures() {
|
|
if (this->m_curType > 2) {
|
|
for (int32_t i = 0; i < 2; i++) {
|
|
GxRsSet(static_cast<EGxRenderState>(GxRs_Texture0 + i), 0);
|
|
CShaderEffect::SetTexMtx_Identity(i);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
uint32_t textureCount = this->m_curBatch->textureCount;
|
|
int32_t v19 = 1;
|
|
|
|
// TODO
|
|
// - override texture count in certain cases
|
|
|
|
if (!(this->m_curBatch->shader & 0x4000) || textureCount != 1) {
|
|
v19 = 0;
|
|
}
|
|
|
|
for (int32_t i = 0; i < 2; i++) {
|
|
if (i >= textureCount) {
|
|
GxRsSet(static_cast<EGxRenderState>(GxRs_Texture0 + i), static_cast<CGxTex*>(nullptr));
|
|
continue;
|
|
}
|
|
|
|
auto textureIndex = this->m_data->textureCombos[this->m_curBatch->textureComboIndex + i];
|
|
auto textureHandle = textureIndex < this->m_data->textures.Count()
|
|
? this->m_curModel->m_textures[textureIndex]
|
|
: nullptr;
|
|
auto texture = textureHandle
|
|
? TextureGetGxTex(textureHandle, 1, nullptr)
|
|
: nullptr;
|
|
|
|
if (texture) {
|
|
uint16_t textureFlags = this->m_data->textures[textureIndex].flags;
|
|
|
|
EGxTexWrapMode wrapU = textureFlags & 0x1 ? GxTex_Wrap : GxTex_Clamp;
|
|
EGxTexWrapMode wrapV = textureFlags & 0x2 ? GxTex_Wrap : GxTex_Clamp;
|
|
|
|
GxTexSetWrap(texture, wrapU, wrapV);
|
|
}
|
|
|
|
GxRsSet(static_cast<EGxRenderState>(GxRs_Texture0 + i), texture);
|
|
|
|
auto textureTransformIndex = this->m_data->textureTransformCombos[this->m_curBatch->textureTransformComboIndex + i];
|
|
auto stageShift = M2COMBINER_STAGE_SHIFT * (2 - (i + 1));
|
|
|
|
uint32_t v21 = v19 == 0 ? i : 1;
|
|
|
|
if (this->m_curBatch->shader & 0x8000 || !((this->m_curBatch->shader >> stageShift) & M2COMBINER_ENVMAP)) {
|
|
if (textureTransformIndex >= this->m_data->textureTransforms.Count()) {
|
|
CShaderEffect::SetTexMtx_Identity(v21);
|
|
} else {
|
|
CShaderEffect::SetTexMtx(this->m_curModel->m_textureMatrices[textureTransformIndex], v21);
|
|
}
|
|
} else {
|
|
CShaderEffect::SetTexMtx_SphereMap(v21);
|
|
}
|
|
}
|
|
}
|