mirror of
https://github.com/whoahq/whoa.git
synced 2026-02-01 00:02:45 +03:00
808 lines
23 KiB
C++
808 lines
23 KiB
C++
#include "model/CM2Shared.hpp"
|
|
#include "async/AsyncFile.hpp"
|
|
#include "gx/Buffer.hpp"
|
|
#include "gx/Shader.hpp"
|
|
#include "gx/Texture.hpp"
|
|
#include "model/CM2Cache.hpp"
|
|
#include "model/CM2Model.hpp"
|
|
#include "model/M2Data.hpp"
|
|
#include "model/M2Init.hpp"
|
|
#include "model/M2Types.hpp"
|
|
#include "util/CStatus.hpp"
|
|
#include "util/SFile.hpp"
|
|
#include <cstring>
|
|
|
|
void CM2Shared::LoadFailedCallback(void* arg) {
|
|
CM2Shared* shared = static_cast<CM2Shared*>(arg);
|
|
|
|
AsyncFileReadDestroyObject(shared->asyncObject);
|
|
shared->asyncObject = nullptr;
|
|
}
|
|
|
|
void CM2Shared::LoadSucceededCallback(void* arg) {
|
|
CM2Shared* shared = static_cast<CM2Shared*>(arg);
|
|
|
|
AsyncFileReadDestroyObject(shared->asyncObject);
|
|
shared->asyncObject = nullptr;
|
|
|
|
uint8_t* base = reinterpret_cast<uint8_t*>(shared->m_data);
|
|
uint32_t size = shared->m_dataSize;
|
|
M2Data& data = *shared->m_data;
|
|
|
|
if (!M2Init(base, size, data)) {
|
|
return;
|
|
}
|
|
|
|
if (!shared->Initialize()) {
|
|
return;
|
|
}
|
|
|
|
// TODO
|
|
// - allocate space for low priority sequence pointers
|
|
|
|
shared->m_m2DataLoaded = 1;
|
|
}
|
|
|
|
void CM2Shared::SkinProfileLoadedCallback(void* arg) {
|
|
CM2Shared* shared = static_cast<CM2Shared*>(arg);
|
|
|
|
shared->FinishLoadingSkinProfile(shared->asyncObject->size);
|
|
|
|
AsyncFileReadDestroyObject(shared->asyncObject);
|
|
shared->asyncObject = nullptr;
|
|
}
|
|
|
|
void CM2Shared::AddRef() {
|
|
// TODO
|
|
}
|
|
|
|
int32_t CM2Shared::CallbackWhenLoaded(CM2Model* model) {
|
|
if (model->m_flags & 0x20) {
|
|
return 1;
|
|
}
|
|
|
|
if (this->m_m2DataLoaded && this->m_skinProfileLoaded) {
|
|
model->InitializeLoaded();
|
|
|
|
return 1;
|
|
}
|
|
|
|
model->LinkToCallbackListTail();
|
|
|
|
return 1;
|
|
}
|
|
|
|
CShaderEffect* CM2Shared::CreateSimpleEffect(uint32_t textureCount, uint16_t shader, uint16_t textureCoordComboIndex) {
|
|
uint32_t combiner[2];
|
|
uint32_t envmap[2];
|
|
|
|
combiner[0] = (shader >> M2COMBINER_STAGE_SHIFT) & M2COMBINER_OP_MASK; // T1 Combiner
|
|
combiner[1] = (shader >> 0) & M2COMBINER_OP_MASK; // T2 Combiner
|
|
envmap[0] = (shader >> M2COMBINER_STAGE_SHIFT) & M2COMBINER_ENVMAP; // T1 Env Mapped
|
|
envmap[1] = (shader >> 0) & M2COMBINER_ENVMAP; // T2 Env Mapped
|
|
|
|
const char* vsName;
|
|
const char* psName;
|
|
|
|
// 1 texture
|
|
if (textureCount == 1) {
|
|
if (envmap[0]) {
|
|
vsName = "Diffuse_Env";
|
|
} else if (this->m_data->textureCoordCombos[textureCoordComboIndex] == 0) {
|
|
vsName = "Diffuse_T1";
|
|
} else {
|
|
vsName = "Diffuse_T2";
|
|
}
|
|
|
|
switch (combiner[0]) {
|
|
case M2COMBINER_OPAQUE:
|
|
psName = "Combiners_Opaque";
|
|
break;
|
|
|
|
case M2COMBINER_MOD:
|
|
psName = "Combiners_Mod";
|
|
break;
|
|
|
|
case M2COMBINER_DECAL:
|
|
psName = "Combiners_Decal";
|
|
break;
|
|
|
|
case M2COMBINER_ADD:
|
|
psName = "Combiners_Add";
|
|
break;
|
|
|
|
case M2COMBINER_MOD2X:
|
|
psName = "Combiners_Mod2x";
|
|
break;
|
|
|
|
case M2COMBINER_FADE:
|
|
psName = "Combiners_Fade";
|
|
break;
|
|
|
|
default:
|
|
psName = "Combiners_Mod";
|
|
break;
|
|
}
|
|
|
|
// 2 textures
|
|
} else {
|
|
if (envmap[0] && envmap[1]) {
|
|
vsName = "Diffuse_Env_Env";
|
|
} else if (envmap[0]) {
|
|
vsName = "Diffuse_Env_T2";
|
|
} else if (envmap[1]) {
|
|
vsName = "Diffuse_T1_Env";
|
|
} else {
|
|
vsName = "Diffuse_T1_T2";
|
|
}
|
|
|
|
switch (combiner[0]) {
|
|
case M2COMBINER_OPAQUE:
|
|
switch (combiner[1]) {
|
|
case M2COMBINER_OPAQUE:
|
|
psName = "Combiners_Opaque_Opaque";
|
|
break;
|
|
|
|
case M2COMBINER_ADD:
|
|
psName = "Combiners_Opaque_Add";
|
|
break;
|
|
|
|
case M2COMBINER_MOD2X:
|
|
psName = "Combiners_Opaque_Mod2x";
|
|
break;
|
|
|
|
case M2COMBINER_MOD2X_NA:
|
|
psName = "Combiners_Opaque_Mod2xNA";
|
|
break;
|
|
|
|
case M2COMBINER_ADD_NA:
|
|
psName = "Combiners_Opaque_AddNA";
|
|
break;
|
|
|
|
default:
|
|
psName = "Combiners_Opaque_Mod";
|
|
break;
|
|
}
|
|
|
|
break;
|
|
|
|
case M2COMBINER_MOD:
|
|
switch (combiner[1]) {
|
|
case M2COMBINER_OPAQUE:
|
|
psName = "Combiners_Mod_Opaque";
|
|
break;
|
|
|
|
case M2COMBINER_MOD:
|
|
psName = "Combiners_Mod_Mod";
|
|
break;
|
|
|
|
case M2COMBINER_ADD:
|
|
psName = "Combiners_Mod_Add";
|
|
break;
|
|
|
|
case M2COMBINER_MOD2X:
|
|
psName = "Combiners_Mod_Mod2x";
|
|
break;
|
|
|
|
case M2COMBINER_MOD2X_NA:
|
|
psName = "Combiners_Mod_Mod2xNA";
|
|
break;
|
|
|
|
case M2COMBINER_ADD_NA:
|
|
psName = "Combiners_Mod_AddNA";
|
|
break;
|
|
|
|
default:
|
|
psName = "Combiners_Mod_Mod";
|
|
break;
|
|
}
|
|
|
|
break;
|
|
|
|
case M2COMBINER_ADD:
|
|
switch (combiner[1]) {
|
|
case M2COMBINER_MOD:
|
|
psName = "Combiners_Add_Mod";
|
|
break;
|
|
|
|
default:
|
|
return nullptr;
|
|
}
|
|
|
|
break;
|
|
|
|
case M2COMBINER_MOD2X:
|
|
switch (combiner[1]) {
|
|
case M2COMBINER_MOD2X:
|
|
psName = "Combiners_Mod2x_Mod2x";
|
|
break;
|
|
|
|
default:
|
|
return nullptr;
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
char effectName[256];
|
|
|
|
// Create effect name for hashing
|
|
strcpy(effectName, vsName);
|
|
strcat(effectName, psName);
|
|
|
|
CShaderEffect* effect = CShaderEffectManager::GetEffect(effectName);
|
|
|
|
if (!effect) {
|
|
effect = CShaderEffectManager::CreateEffect(effectName);
|
|
|
|
effect->InitEffect(vsName, psName);
|
|
|
|
// M2GetCombinerOps (inlined)
|
|
for (int32_t textureIndex = 0; textureIndex < textureCount; textureIndex++) {
|
|
// TODO
|
|
// colorOps[textureIndex] = colorOpTable[combiner[textureIndex]];
|
|
// alphaOps[textureIndex] = alphaOpTable[combiner[textureIndex]];
|
|
}
|
|
|
|
// TODO
|
|
// effect->InitFixedFuncPass(colorOps, alphaOps, textureCount);
|
|
}
|
|
|
|
return effect;
|
|
}
|
|
|
|
int32_t CM2Shared::FinishLoadingSkinProfile(uint32_t size) {
|
|
if (this->m_skinProfileLoaded) {
|
|
return 1;
|
|
}
|
|
|
|
uint8_t* base = reinterpret_cast<uint8_t*>(this->skinProfile);
|
|
M2Data& data = *this->m_data;
|
|
M2SkinProfile& skinProfile = *this->skinProfile;
|
|
|
|
if (!M2Init(base, size, data, skinProfile)) {
|
|
return 0;
|
|
}
|
|
|
|
if (!this->InitializeSkinProfile()) {
|
|
return 0;
|
|
}
|
|
|
|
this->m_skinProfileLoaded = 1;
|
|
|
|
for (auto model = this->m_callbackList; model; model = this->m_callbackList) {
|
|
STORM_ASSERT(model->m_callbackPrev);
|
|
STORM_ASSERT(*model->m_callbackPrev == this->m_callbackList);
|
|
|
|
model->UnlinkFromCallbackList();
|
|
model->InitializeLoaded();
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
CShaderEffect* CM2Shared::GetEffect(M2Batch* batch) {
|
|
CShaderEffect* effect;
|
|
|
|
// Simple effect
|
|
if (!(batch->shader & 0x8000)) {
|
|
effect = this->CreateSimpleEffect(batch->textureCount, batch->shader, batch->textureCoordComboIndex);
|
|
|
|
// Fallback
|
|
// 0000000000010001
|
|
// 1 texture: Combiners_Mod
|
|
// 2 texture: Combiners_Mod_Mod
|
|
if (!effect) {
|
|
effect = this->CreateSimpleEffect(batch->textureCount, 0x11, batch->textureCoordComboIndex);
|
|
}
|
|
|
|
return effect;
|
|
}
|
|
|
|
// Specialized effect
|
|
const char* vsName = nullptr;
|
|
const char* psName = nullptr;
|
|
uint32_t colorOp = 0;
|
|
uint32_t alphaOp = 0;
|
|
|
|
switch (batch->shader & 0x7FFF) {
|
|
case 0:
|
|
return nullptr;
|
|
|
|
case 1:
|
|
vsName = "Diffuse_T1_Env";
|
|
psName = "Combiners_Opaque_Mod2xNA_Alpha";
|
|
colorOp = 0;
|
|
alphaOp = 3;
|
|
|
|
break;
|
|
|
|
case 2:
|
|
vsName = "Diffuse_T1_Env";
|
|
psName = "Combiners_Opaque_AddAlpha";
|
|
colorOp = 0;
|
|
alphaOp = 3;
|
|
|
|
break;
|
|
|
|
case 3:
|
|
vsName = "Diffuse_T1_Env";
|
|
psName = "Combiners_Opaque_AddAlpha_Alpha";
|
|
colorOp = 0;
|
|
alphaOp = 3;
|
|
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
char effectName[256];
|
|
|
|
// Create effect name for hashing
|
|
strcpy(effectName, vsName);
|
|
strcat(effectName, psName);
|
|
|
|
effect = CShaderEffectManager::GetEffect(effectName);
|
|
|
|
if (!effect) {
|
|
effect = CShaderEffectManager::CreateEffect(effectName);
|
|
|
|
effect->InitEffect(vsName, psName);
|
|
|
|
// TODO
|
|
// effect->InitFixedFuncPass(effect, &colorOp, &alphaOp, 1);
|
|
}
|
|
|
|
return effect;
|
|
}
|
|
|
|
int32_t CM2Shared::Initialize() {
|
|
this->skinProfile = nullptr;
|
|
|
|
// TODO
|
|
// implement logic to select skin profile
|
|
|
|
uint32_t profile = this->m_data->numSkinProfiles - 1;
|
|
|
|
if (!this->LoadSkinProfile(profile)) {
|
|
return 0;
|
|
}
|
|
|
|
void* textures = SMemAlloc(sizeof(HTEXTURE) * this->m_data->textures.count, __FILE__, __LINE__, 0x8);
|
|
this->textures = static_cast<HTEXTURE*>(textures);
|
|
|
|
if (!textures) {
|
|
// TODO
|
|
// CM2Model::ErrorSetFileLine(__FILE__, __LINE__);
|
|
// CM2Model::ErrorSet("Failed to allocate texture array: %s", this + 60);
|
|
|
|
return 0;
|
|
}
|
|
|
|
for (int32_t i = 0; i < this->m_data->materials.count; i++) {
|
|
M2Material& material = this->m_data->materials[i];
|
|
if (material.blendMode >= M2BLEND_MOD) {
|
|
material.flags |= 0x1;
|
|
}
|
|
}
|
|
|
|
for (int32_t i = 0; i < this->m_data->textures.count; i++) {
|
|
M2Texture& texture = this->m_data->textures[i];
|
|
|
|
if (texture.filename.count > 1) {
|
|
CGxTexFlags texFlags = CGxTexFlags(GxTex_Linear, 0, 0, 0, 0, 0, 1);
|
|
|
|
int32_t createFlags = 0;
|
|
|
|
if (this->m_flag4) {
|
|
createFlags |= 0x2;
|
|
}
|
|
|
|
if (this->m_flag40) {
|
|
createFlags |= 0x30;
|
|
}
|
|
|
|
CStatus* status = &GetGlobalStatusObj();
|
|
|
|
this->textures[i] = TextureCreate(&texture.filename[0], texFlags, status, createFlags);
|
|
} else {
|
|
static CImVector FRIENDLY_WHITE = { 0xFF, 0xFF, 0xFF, 0xFF };
|
|
this->textures[i] = TextureCreateSolid(FRIENDLY_WHITE);
|
|
}
|
|
}
|
|
|
|
for (int32_t i = 0; i < this->m_data->bones.count; i++) {
|
|
M2CompBone& bone = this->m_data->bones[i];
|
|
|
|
if (bone.flags & (0x200 | 0x80 | 0x40 | 0x20 | 0x10 | 0x8)) {
|
|
// TODO
|
|
}
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
int32_t CM2Shared::InitializeSkinProfile() {
|
|
this->uint194 = this->skinProfile->indices.Count()
|
|
? 65536 / this->skinProfile->indices.Count()
|
|
: 1;
|
|
|
|
for (int32_t i = 0; i < this->skinProfile->skinSections.Count(); i++) {
|
|
uint32_t v6 = this->skinProfile->boneCountMax / this->skinProfile->skinSections[i].boneCount;
|
|
|
|
if (this->uint194 > v6) {
|
|
this->uint194 = v6;
|
|
}
|
|
}
|
|
|
|
if (!this->uint194) {
|
|
this->uint194 = 1;
|
|
}
|
|
|
|
this->uint190 = 1;
|
|
|
|
uint32_t dataSize = sizeof(M2SkinSection) * this->skinProfile->skinSections.Count();
|
|
if (this->skinProfile->indices.Count()) {
|
|
dataSize += sizeof(CShaderEffect*) * this->skinProfile->batches.Count();
|
|
}
|
|
|
|
char* data = static_cast<char*>(SMemAlloc(dataSize, __FILE__, __LINE__, 0x0));
|
|
if (!data) {
|
|
return 0;
|
|
}
|
|
|
|
this->m_skinSections = reinterpret_cast<M2SkinSection*>(data);
|
|
if (this->skinProfile->skinSections.Count()) {
|
|
data += sizeof(M2SkinSection) * this->skinProfile->skinSections.Count();
|
|
memcpy(this->m_skinSections, this->skinProfile->skinSections.Data(), this->skinProfile->skinSections.Count() * sizeof(M2SkinSection));
|
|
}
|
|
|
|
if (this->skinProfile->indices.Count()) {
|
|
this->m_batchShaders = reinterpret_cast<CShaderEffect**>(data);
|
|
memset(this->m_batchShaders, 0, sizeof(CShaderEffect*) * this->skinProfile->batches.Count());
|
|
}
|
|
|
|
this->SubstituteSimpleShaders();
|
|
this->SubstituteSpecializedShaders();
|
|
|
|
if (this->skinProfile->indices.Count()) {
|
|
for (int32_t i = 0; i < this->skinProfile->batches.Count(); i++) {
|
|
this->m_batchShaders[i] = this->GetEffect(&this->skinProfile->batches[i]);
|
|
}
|
|
}
|
|
|
|
if (!(this->m_cache->m_flags & 0x8)) {
|
|
// TODO
|
|
// - non-shader-path vertex logic
|
|
}
|
|
|
|
if (!(this->m_cache->m_flags & 0x8)) {
|
|
for (int32_t i = 0; i < this->skinProfile->batches.Count(); i++) {
|
|
auto& batch = this->skinProfile->batches[i];
|
|
|
|
if (batch.textureCount > 1) {
|
|
this->skinProfile->batches[i - batch.materialLayer].flags |= 0x40;
|
|
}
|
|
}
|
|
|
|
for (int32_t i = 0; i < this->skinProfile->batches.Count(); i++) {
|
|
auto& batch = this->skinProfile->batches[i];
|
|
|
|
if (batch.materialLayer) {
|
|
if (this->skinProfile->batches[i - batch.materialLayer].flags & 0x40) {
|
|
batch.flags |= 0x40;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
int32_t CM2Shared::Load(SFile* file, int32_t a3, CAaBox* a4) {
|
|
// TODO
|
|
// this->dword8 ^= (this->dword8 ^ (4 * (a3 != 0))) & 4;
|
|
|
|
this->m_dataSize = SFile::GetFileSize(file, 0);
|
|
|
|
// TODO use proper allocation function here
|
|
this->m_data = static_cast<M2Data*>(SMemAlloc(this->m_dataSize, __FILE__, __LINE__, 0));
|
|
|
|
if (!this->m_data) {
|
|
return 0;
|
|
}
|
|
|
|
if (a4) {
|
|
this->aaBox154 = *a4;
|
|
}
|
|
|
|
this->asyncObject = AsyncFileReadAllocObject();
|
|
|
|
if (!this->asyncObject) {
|
|
return 0;
|
|
}
|
|
|
|
this->asyncObject->file = file;
|
|
this->asyncObject->buffer = this->m_data;
|
|
this->asyncObject->size = this->m_dataSize;
|
|
this->asyncObject->userArg = this;
|
|
this->asyncObject->userPostloadCallback = &CM2Shared::LoadSucceededCallback;
|
|
this->asyncObject->userFailedCallback = &CM2Shared::LoadFailedCallback;
|
|
this->asyncObject->isRead = 0;
|
|
this->asyncObject->isProcessed = 0;
|
|
|
|
AsyncFileReadObject(this->asyncObject, 0);
|
|
|
|
return 1;
|
|
}
|
|
|
|
int32_t CM2Shared::LoadSkinProfile(uint32_t profile) {
|
|
// TODO
|
|
// the file path logic is in its own function
|
|
|
|
char skinFilePath[STORM_MAX_PATH];
|
|
|
|
strcpy(skinFilePath, this->m_filePath);
|
|
char* v4 = strrchr(skinFilePath, '.');
|
|
if (v4) {
|
|
*v4 = 0;
|
|
}
|
|
sprintf(&skinFilePath[strlen(skinFilePath)], "%02d.skin", profile);
|
|
|
|
SFile* fileptr;
|
|
|
|
if (!SFile::OpenEx(nullptr, skinFilePath, this->m_flag4, &fileptr)) {
|
|
// TODO
|
|
// error handling
|
|
|
|
return 0;
|
|
}
|
|
|
|
uint32_t size = SFile::GetFileSize(fileptr, nullptr);
|
|
|
|
// TODO use proper allocation function here
|
|
this->skinProfile = static_cast<M2SkinProfile*>(SMemAlloc(size, __FILE__, __LINE__, 0));
|
|
|
|
if (!this->skinProfile) {
|
|
SFile::Close(fileptr);
|
|
|
|
return 0;
|
|
}
|
|
|
|
this->asyncObject = AsyncFileReadAllocObject();
|
|
|
|
if (!this->asyncObject) {
|
|
SFile::Close(fileptr);
|
|
delete this->skinProfile;
|
|
|
|
return 0;
|
|
}
|
|
|
|
this->asyncObject->file = fileptr;
|
|
this->asyncObject->buffer = this->skinProfile;
|
|
this->asyncObject->size = size,
|
|
this->asyncObject->userArg = this;
|
|
this->asyncObject->userPostloadCallback = &CM2Shared::SkinProfileLoadedCallback;
|
|
this->asyncObject->userFailedCallback = &CM2Shared::LoadFailedCallback;
|
|
this->asyncObject->isRead = 0;
|
|
this->asyncObject->isProcessed = 0;
|
|
this->asyncObject->priority = 125;
|
|
|
|
AsyncFileReadObject(this->asyncObject, 1);
|
|
|
|
return 1;
|
|
}
|
|
|
|
void CM2Shared::Release() {
|
|
// TODO
|
|
}
|
|
|
|
int32_t CM2Shared::SetIndices() {
|
|
if (!this->m_indexPool) {
|
|
this->m_indexPool = GxPoolCreate(
|
|
GxPoolTarget_Index,
|
|
GxPoolUsage_Dynamic,
|
|
2 * this->uint190 * this->skinProfile->indices.Count(),
|
|
GxPoolHintBit_Unk1,
|
|
this->ext
|
|
);
|
|
|
|
this->m_indexBuf = GxBufCreate(
|
|
this->m_indexPool,
|
|
2,
|
|
this->uint190 * this->skinProfile->indices.Count(),
|
|
0
|
|
);
|
|
|
|
if (!this->m_indexPool || !this->m_indexBuf) {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
if (!this->m_indexBuf->unk1C || !this->m_indexBuf->unk1D) {
|
|
char* indexData = GxBufLock(this->m_indexBuf);
|
|
uint16_t* indexBuf = reinterpret_cast<uint16_t*>(indexData);
|
|
|
|
if (!indexData) {
|
|
return 0;
|
|
}
|
|
|
|
int32_t v10 = (this->m_cache->m_flags & 0x8) != 0
|
|
|| (this->m_data->bones.Count() == 1 && (this->m_cache->m_flags & 0x40) != 0);
|
|
uint32_t v21 = 0;
|
|
|
|
for (int32_t i = 0; i < this->skinProfile->skinSections.Count(); i++) {
|
|
auto& skinSection = this->m_skinSections[i];
|
|
auto indexStart = this->skinProfile->skinSections[i].indexStart;
|
|
auto v25 = v10 ? 0 : -skinSection.vertexStart;
|
|
|
|
for (int32_t j = 0; j < this->uint190; j++) {
|
|
for (int32_t k = 0; k < skinSection.indexCount; k++) {
|
|
indexBuf[k] = this->skinProfile->indices[indexStart + k + v25];
|
|
}
|
|
|
|
indexBuf += skinSection.indexCount;
|
|
|
|
v25 += v10 ? this->skinProfile->vertices.Count() : skinSection.vertexCount;
|
|
}
|
|
|
|
skinSection.indexStart = v21;
|
|
v21 += this->uint190 * skinSection.indexCount;
|
|
}
|
|
|
|
GxBufUnlock(this->m_indexBuf, 0);
|
|
}
|
|
|
|
GxPrimIndexPtr(this->m_indexBuf);
|
|
|
|
return 1;
|
|
}
|
|
|
|
int32_t CM2Shared::SetVertices(uint32_t a2) {
|
|
if (!this->m_vertexPool) {
|
|
this->m_vertexPool = GxPoolCreate(
|
|
GxPoolTarget_Vertex,
|
|
GxPoolUsage_Static,
|
|
sizeof(CGxVertexPBNT2) * this->uint190 * this->skinProfile->vertices.Count(),
|
|
GxPoolHintBit_Unk1,
|
|
this->ext
|
|
);
|
|
|
|
this->m_vertexBuf = GxBufCreate(
|
|
this->m_vertexPool,
|
|
sizeof(CGxVertexPBNT2),
|
|
this->uint190 * this->skinProfile->vertices.Count(),
|
|
0
|
|
);
|
|
|
|
if (!this->m_vertexPool || !this->m_vertexBuf) {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
if (CShaderEffect::s_enableShaders) {
|
|
if (!this->m_vertexBuf->unk1C || !this->m_vertexBuf->unk1D) {
|
|
char* vertexData = GxBufLock(this->m_vertexBuf);
|
|
CGxVertexPBNT2* vertexBuf = reinterpret_cast<CGxVertexPBNT2*>(vertexData);
|
|
|
|
if (!vertexData) {
|
|
return 0;
|
|
}
|
|
|
|
auto v27 = 0;
|
|
for (int32_t i = 0; i < this->uint190; i++) {
|
|
for (int32_t j = 0; j < this->skinProfile->skinSections.Count(); j++) {
|
|
auto& skinSection = this->skinProfile->skinSections[j];
|
|
auto vertexStart = skinSection.vertexStart;
|
|
auto vertexEnd = vertexStart + skinSection.vertexCount;
|
|
|
|
uint32_t v25 = 0x1010101 * i * skinSection.boneCount;
|
|
|
|
if (vertexStart < vertexEnd) {
|
|
for (int32_t k = vertexStart; k < vertexEnd; k++) {
|
|
auto vertex = &this->m_data->vertices[this->skinProfile->vertices[k]];
|
|
memcpy(&vertexBuf[k], vertex, sizeof(CGxVertexPBNT2));
|
|
vertexBuf[k].bi.u = v25 + this->skinProfile->bones[k].u;
|
|
}
|
|
}
|
|
}
|
|
|
|
vertexBuf += this->skinProfile->vertices.Count();
|
|
}
|
|
|
|
GxBufUnlock(this->m_vertexBuf, 0);
|
|
}
|
|
|
|
GxPrimVertexPtr(this->m_vertexBuf, GxVBF_PBNT2);
|
|
|
|
return 1;
|
|
} else {
|
|
// TODO
|
|
// non-shader code path
|
|
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
void CM2Shared::SubstituteSimpleShaders() {
|
|
for (int32_t batchIndex = 0; batchIndex < this->skinProfile->batches.Count(); batchIndex++) {
|
|
auto& batch = this->skinProfile->batches[batchIndex];
|
|
|
|
if (batch.shader & 0x8000) {
|
|
continue;
|
|
}
|
|
|
|
auto& material = this->m_data->materials[batch.materialIndex];
|
|
uint16_t textureCoordComboIndex = batch.textureCoordComboIndex;
|
|
|
|
// M2Data flag 0x8: use combiner combos
|
|
if (this->m_data->flags & 0x8) {
|
|
uint16_t textureCombinerComboIndex = batch.shader;
|
|
|
|
batch.shader = 0;
|
|
|
|
uint16_t shader[2] = { 0, 0 };
|
|
|
|
for (int32_t textureIndex = 0; textureIndex < batch.textureCount; textureIndex++) {
|
|
bool isFirstTexture = textureIndex == 0;
|
|
bool isLastTexture = textureIndex == batch.textureCount - 1;
|
|
|
|
// If this is the first texture and the batch material's blending mode is opaque,
|
|
// override the combiner mode to opaque; otherwise, use the combiner mode from the
|
|
// combiner combos
|
|
uint16_t textureCombiner = isFirstTexture && material.blendMode == M2BLEND_OPAQUE
|
|
? M2COMBINER_OPAQUE
|
|
: this->m_data->textureCombinerCombos[textureCombinerComboIndex + textureIndex];
|
|
|
|
shader[textureIndex] |= textureCombiner;
|
|
|
|
uint16_t textureCoord = this->m_data->textureCoordCombos[textureCoordComboIndex + textureIndex];
|
|
|
|
// If the texture coord is env, set env bit for texture
|
|
if (textureCoord > 2) {
|
|
shader[textureIndex] |= M2COMBINER_ENVMAP;
|
|
}
|
|
|
|
// If this is the last texture and the texture coord is T2, enable bit 15
|
|
if (isLastTexture && textureCoord == 1) {
|
|
batch.shader |= 0x4000;
|
|
}
|
|
}
|
|
|
|
batch.shader |= (shader[0] << M2COMBINER_STAGE_SHIFT) | shader[1];
|
|
} else {
|
|
uint16_t shader = 0;
|
|
|
|
// If the material blend mode is opaque, force the combiner to opaque; otherwise,
|
|
// default combiner to mod
|
|
uint16_t textureCombiner = material.blendMode == M2BLEND_OPAQUE
|
|
? M2COMBINER_OPAQUE
|
|
: M2COMBINER_MOD;
|
|
|
|
shader |= textureCombiner;
|
|
|
|
uint16_t textureCoord = this->m_data->textureCoordCombos[textureCoordComboIndex];
|
|
|
|
// If the texture coord is env, set env bit for texture
|
|
if (textureCoord > 2) {
|
|
shader |= M2COMBINER_ENVMAP;
|
|
}
|
|
|
|
shader <<= M2COMBINER_STAGE_SHIFT;
|
|
|
|
// If the texture coord is T2, enable bit 15
|
|
if (textureCoord == 1) {
|
|
shader |= 0x4000;
|
|
}
|
|
|
|
batch.shader = shader;
|
|
}
|
|
}
|
|
}
|
|
|
|
void CM2Shared::SubstituteSpecializedShaders() {
|
|
// TODO
|
|
}
|