mirror of
https://github.com/thunderbrewhq/thunderbrew
synced 2025-12-12 19:22:30 +00:00
407 lines
11 KiB
C++
407 lines
11 KiB
C++
#include "ui/CSimpleModel.hpp"
|
|
#include "gx/Camera.hpp"
|
|
#include "gx/Coordinate.hpp"
|
|
#include "gx/Draw.hpp"
|
|
#include "gx/Shader.hpp"
|
|
#include "gx/Transform.hpp"
|
|
#include "math/Utils.hpp"
|
|
#include "model/CM2Shared.hpp"
|
|
#include "model/M2Data.hpp"
|
|
#include "ui/CSimpleModelScript.hpp"
|
|
#include "ui/LoadXML.hpp"
|
|
#include <common/XML.hpp>
|
|
#include <tempest/Math.hpp>
|
|
|
|
int32_t CSimpleModel::s_metatable;
|
|
int32_t CSimpleModel::s_objectType;
|
|
|
|
void CSimpleModel::CreateScriptMetaTable() {
|
|
lua_State* L = FrameScript_GetContext();
|
|
int32_t ref = FrameScript_Object::CreateScriptMetaTable(L, &CSimpleModel::RegisterScriptMethods);
|
|
CSimpleModel::s_metatable = ref;
|
|
}
|
|
|
|
int32_t CSimpleModel::GetObjectType() {
|
|
if (!CSimpleModel::s_objectType) {
|
|
CSimpleModel::s_objectType = ++FrameScript_Object::s_objectTypes;
|
|
}
|
|
|
|
return CSimpleModel::s_objectType;
|
|
}
|
|
|
|
void CSimpleModel::LightingCallback(CM2Model* model, CM2Lighting* lighting, void* userArg) {
|
|
CSimpleModel* simpleModel = static_cast<CSimpleModel*>(userArg);
|
|
|
|
if (simpleModel->m_flags & 0x1) {
|
|
lighting->SetFog(simpleModel->m_fogColor, simpleModel->m_fogNear, simpleModel->m_fogFar);
|
|
}
|
|
|
|
if (simpleModel->m_light.m_visible) {
|
|
lighting->AddLight(&simpleModel->m_light);
|
|
}
|
|
}
|
|
|
|
void CSimpleModel::ModelLoaded(CM2Model* model, void* arg) {
|
|
auto simpleModel = static_cast<CSimpleModel*>(arg);
|
|
simpleModel->OnModelLoaded(model);
|
|
}
|
|
|
|
void CSimpleModel::RegisterScriptMethods(lua_State* L) {
|
|
CSimpleFrame::RegisterScriptMethods(L);
|
|
FrameScript_Object::FillScriptMethodTable(L, SimpleModelMethods, NUM_SIMPLE_MODEL_SCRIPT_METHODS);
|
|
}
|
|
|
|
void CSimpleModel::RenderModel(void* arg) {
|
|
auto simpleModel = static_cast<CSimpleModel*>(arg);
|
|
|
|
if (!simpleModel->m_model) {
|
|
return;
|
|
}
|
|
|
|
CRect viewRect;
|
|
|
|
if (!simpleModel->GetRect(&viewRect)) {
|
|
return;
|
|
}
|
|
|
|
C44Matrix proj;
|
|
GxXformProjection(proj);
|
|
|
|
C44Matrix view;
|
|
GxXformView(view);
|
|
|
|
float minX, maxX, minY, maxY, minZ, maxZ;
|
|
GxXformViewport(minX, maxX, minY, maxY, minZ, maxZ);
|
|
|
|
C3Vector cameraPos;
|
|
C3Vector cameraTarg;
|
|
|
|
if (simpleModel->m_camera) {
|
|
DataMgrGetCoord(simpleModel->m_camera, 7, &cameraPos);
|
|
DataMgrGetCoord(simpleModel->m_camera, 8, &cameraTarg);
|
|
CameraSetupWorldProjection(simpleModel->m_camera, viewRect, 0x0);
|
|
} else {
|
|
C2Vector v19 = { viewRect.minX, viewRect.minY };
|
|
CameraSetupScreenProjection(viewRect, v19, 0.0f, 1);
|
|
}
|
|
|
|
DDCToNDC(viewRect.minX, viewRect.minY, &viewRect.minX, &viewRect.minY);
|
|
DDCToNDC(viewRect.maxX, viewRect.maxY, &viewRect.maxX, &viewRect.maxY);
|
|
|
|
viewRect.minX = std::max(0.0f, std::min(viewRect.minX, 1.0f));
|
|
viewRect.maxX = std::max(0.0f, std::min(viewRect.maxX, 1.0f));
|
|
viewRect.minY = std::max(0.0f, std::min(viewRect.minY, 1.0f));
|
|
viewRect.maxY = std::max(0.0f, std::min(viewRect.maxY, 1.0f));
|
|
|
|
// Handle zero width or zero height
|
|
if (AreEqual(viewRect.minX, viewRect.maxX, WHOA_EPSILON_1) || AreEqual(viewRect.minY, viewRect.maxY, WHOA_EPSILON_1)) {
|
|
GxXformSetProjection(proj);
|
|
GxXformSetView(view);
|
|
GxXformSetViewport(minX, maxX, minY, maxY, minZ, maxZ);
|
|
|
|
return;
|
|
}
|
|
|
|
float v11 = viewRect.minX * maxX;
|
|
float v12 = viewRect.maxX * maxX;
|
|
float v13 = viewRect.minY * maxY;
|
|
float v14 = viewRect.maxY * maxY;
|
|
|
|
GxXformSetViewport(v11, v12, v13, v14, 0.0f, 1.0f);
|
|
CImVector clearColor = { 0x00, 0x00, 0x00, 0xFF };
|
|
GxSceneClear(0x2, clearColor);
|
|
CShaderEffect::UpdateProjMatrix();
|
|
|
|
simpleModel->UpdateModel();
|
|
|
|
if (simpleModel->m_pendingCameraIndex == -1u) {
|
|
simpleModel->GetScene()->Animate(cameraPos);
|
|
simpleModel->GetScene()->Draw(M2PASS_0);
|
|
simpleModel->GetScene()->Draw(M2PASS_1);
|
|
}
|
|
|
|
GxXformSetProjection(proj);
|
|
GxXformSetView(view);
|
|
GxXformSetViewport(minX, maxX, minY, maxY, minZ, maxZ);
|
|
CShaderEffect::UpdateProjMatrix();
|
|
}
|
|
|
|
CSimpleModel::CSimpleModel(CSimpleFrame* parent) : CSimpleFrame(parent) {
|
|
this->m_light.SetVisible(0);
|
|
this->m_light.SetLightType(M2LIGHT_1);
|
|
this->m_light.m_ambColor = { 1.0f, 1.0f, 1.0f };
|
|
this->m_light.m_dirColor = { 1.0f, 1.0f, 1.0f };
|
|
|
|
this->m_fogColor = { 0xFF, 0xFF, 0xFF, 0xFF };
|
|
}
|
|
|
|
CM2Scene* CSimpleModel::GetScene() {
|
|
if (!this->m_scene) {
|
|
this->m_scene = M2CreateScene();
|
|
}
|
|
|
|
return this->m_scene;
|
|
}
|
|
|
|
FrameScript_Object::ScriptIx* CSimpleModel::GetScriptByName(const char* name, ScriptData& data) {
|
|
auto parentScript = CSimpleFrame::GetScriptByName(name, data);
|
|
|
|
if (parentScript) {
|
|
return parentScript;
|
|
}
|
|
|
|
if (!SStrCmpI(name, "OnUpdateModel", STORM_MAX_STR)) {
|
|
return &this->m_onUpdateModel;
|
|
}
|
|
|
|
if (!SStrCmpI(name, "OnAnimFinished", STORM_MAX_STR)) {
|
|
return &this->m_onAnimFinished;
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
int32_t CSimpleModel::GetScriptMetaTable() {
|
|
return CSimpleModel::s_metatable;
|
|
}
|
|
|
|
bool CSimpleModel::IsA(int32_t type) {
|
|
return type == CSimpleModel::s_objectType
|
|
|| type == CSimpleFrame::s_objectType
|
|
|| type == CScriptRegion::s_objectType
|
|
|| type == CScriptObject::s_objectType;
|
|
}
|
|
|
|
void CSimpleModel::LoadXML(XMLNode* node, CStatus* status) {
|
|
CSimpleFrame::LoadXML(node, status);
|
|
|
|
const char* fileAttr = node->GetAttributeByName("file");
|
|
if (fileAttr && *fileAttr) {
|
|
this->SetModel(fileAttr);
|
|
|
|
if (!this->m_model) {
|
|
status->Add(
|
|
STATUS_WARNING,
|
|
"Frame %s: Bad model file: %s",
|
|
this->GetDisplayName(),
|
|
fileAttr
|
|
);
|
|
}
|
|
}
|
|
|
|
const char* scaleAttr = node->GetAttributeByName("scale");
|
|
if (scaleAttr && *scaleAttr) {
|
|
float scale = SStrToFloat(scaleAttr);
|
|
|
|
if (scale <= 0.0f) {
|
|
status->Add(
|
|
STATUS_WARNING,
|
|
"Frame %s: Invalid model scale: %s",
|
|
this->GetDisplayName(),
|
|
scaleAttr
|
|
);
|
|
} else {
|
|
this->SetScale(scale);
|
|
}
|
|
}
|
|
|
|
const char* fogNearAttr = node->GetAttributeByName("fogNear");
|
|
if (fogNearAttr && *fogNearAttr) {
|
|
float fogNear = SStrToFloat(fogNearAttr);
|
|
|
|
if (fogNear < 0.0f) {
|
|
fogNear = 0.0f;
|
|
}
|
|
|
|
this->SetFogNear(fogNear);
|
|
}
|
|
|
|
const char* fogFarAttr = node->GetAttributeByName("fogFar");
|
|
if (fogFarAttr && *fogFarAttr) {
|
|
float fogFar = SStrToFloat(fogFarAttr);
|
|
|
|
if (fogFar < 0.0f) {
|
|
fogFar = 0.0f;
|
|
}
|
|
|
|
this->SetFogFar(fogFar);
|
|
}
|
|
|
|
for (XMLNode* child = node->m_child; child; child = child->m_next) {
|
|
if (!SStrCmpI(child->GetName(), "FogColor", STORM_MAX_STR)) {
|
|
CImVector color;
|
|
LoadXML_Color(child, color);
|
|
this->SetFogColor(color);
|
|
}
|
|
}
|
|
}
|
|
|
|
void CSimpleModel::OnFrameRender(CRenderBatch* batch, uint32_t layer) {
|
|
// TODO
|
|
}
|
|
|
|
void CSimpleModel::OnLayerUpdate(float elapsedSec) {
|
|
CSimpleFrame::OnLayerUpdate(elapsedSec);
|
|
|
|
if (this->m_model && this->m_visible && this->m_pendingCameraIndex == -1u) {
|
|
this->m_model->SetAnimating(1);
|
|
this->m_model->SetVisible(1);
|
|
|
|
if (this->m_model->m_attachParent) {
|
|
this->m_model->m_flag20000 = 1;
|
|
} else {
|
|
this->m_model->m_flag10000 = 1;
|
|
}
|
|
}
|
|
|
|
uint32_t advance = CMath::fuint(elapsedSec * 1000.f);
|
|
this->GetScene()->AdvanceTime(advance);
|
|
}
|
|
|
|
void CSimpleModel::OnModelLoaded(CM2Model* model) {
|
|
model->SetAnimating(1);
|
|
|
|
C3Vector cameraPos;
|
|
model->m_scene->Animate(cameraPos);
|
|
|
|
if (this->m_pendingCameraIndex != -1u) {
|
|
this->SetCameraByIndex(this->m_pendingCameraIndex);
|
|
} else if (this->m_pendingCameraId != -1u) {
|
|
this->SetCameraByID(this->m_pendingCameraId);
|
|
}
|
|
|
|
CAaBox bounds;
|
|
this->m_bounds = model->GetBoundingBox(bounds);
|
|
|
|
this->Resize(0);
|
|
}
|
|
|
|
void CSimpleModel::SetCamera(HCAMERA camera) {
|
|
if (camera) {
|
|
camera = HandleDuplicate(camera);
|
|
}
|
|
|
|
if (this->m_camera) {
|
|
HandleClose(this->m_camera);
|
|
}
|
|
|
|
this->m_camera = camera;
|
|
this->m_pendingCameraIndex = -1u;
|
|
this->m_pendingCameraId = -1u;
|
|
}
|
|
|
|
void CSimpleModel::SetCameraByID(uint32_t id) {
|
|
// TODO
|
|
}
|
|
|
|
void CSimpleModel::SetCameraByIndex(uint32_t index) {
|
|
if (!this->m_model || !this->m_model->IsLoaded(0, 0)) {
|
|
this->m_pendingCameraIndex = index;
|
|
|
|
return;
|
|
}
|
|
|
|
if (!this->m_model->m_loaded) {
|
|
this->m_model->WaitForLoad(nullptr);
|
|
}
|
|
|
|
if (index >= this->m_model->m_shared->m_data->cameras.Count()) {
|
|
if (this->m_camera) {
|
|
HandleClose(this->m_camera);
|
|
}
|
|
|
|
this->m_camera = nullptr;
|
|
this->m_pendingCameraIndex = -1u;
|
|
this->m_pendingCameraId = -1u;
|
|
|
|
return;
|
|
}
|
|
|
|
HCAMERA camera = this->m_model->GetCameraByIndex(index);
|
|
this->SetCamera(camera);
|
|
}
|
|
|
|
void CSimpleModel::SetFogColor(CImVector& fogColor) {
|
|
this->m_fogColor = fogColor;
|
|
this->m_flags |= 0x1;
|
|
}
|
|
|
|
void CSimpleModel::SetFogFar(float fogFar) {
|
|
this->m_fogFar = fogFar;
|
|
}
|
|
|
|
void CSimpleModel::SetFogNear(float fogNear) {
|
|
this->m_fogNear = fogNear;
|
|
}
|
|
|
|
void CSimpleModel::SetModel(const char* sourcefile) {
|
|
CM2Model* model = nullptr;
|
|
|
|
if (sourcefile && *sourcefile) {
|
|
if (!this->m_scene) {
|
|
this->m_scene = M2CreateScene();
|
|
}
|
|
|
|
model = this->m_scene->CreateModel(sourcefile, 1);
|
|
}
|
|
|
|
this->SetModel(model);
|
|
|
|
if (model) {
|
|
model->Release();
|
|
}
|
|
}
|
|
|
|
void CSimpleModel::SetModel(CM2Model* model) {
|
|
if (model) {
|
|
model->m_refCount++;
|
|
}
|
|
|
|
if (this->m_model) {
|
|
this->m_model->Release();
|
|
}
|
|
|
|
this->m_model = model;
|
|
|
|
if (model) {
|
|
// TODO
|
|
this->m_model->SetLightingCallback(&CSimpleModel::LightingCallback, this);
|
|
this->m_model->SetLoadedCallback(&CSimpleModel::ModelLoaded, this);
|
|
}
|
|
|
|
this->NotifyDrawLayerChanged(DRAWLAYER_ARTWORK);
|
|
}
|
|
|
|
void CSimpleModel::SetScale(float scale) {
|
|
this->m_scale = scale;
|
|
}
|
|
|
|
void CSimpleModel::SetSequence(uint32_t sequenceId) {
|
|
if (this->m_model) {
|
|
this->m_model->SetBoneSequence(-1, sequenceId, -1, 0, 1.0f, 0, 1);
|
|
}
|
|
}
|
|
|
|
int32_t CSimpleModel::SetSequenceTime(uint32_t sequence, int32_t time) {
|
|
if (this->m_model) {
|
|
this->m_model->SetBoneSequence(-1, sequence, -1, time, 1.0f, 0, 1);
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
void CSimpleModel::UpdateModel() {
|
|
if (this->m_onUpdateModel.luaRef) {
|
|
this->RunScript(this->m_onUpdateModel, 0, nullptr);
|
|
}
|
|
|
|
C3Vector position = {
|
|
this->m_position.x * this->m_layoutScale,
|
|
this->m_position.y * this->m_layoutScale,
|
|
this->m_position.z * this->m_layoutScale
|
|
};
|
|
|
|
float scale = NDCToDDCHeight(1.0f) * 1.6666666 * (this->m_scale * this->m_layoutScale);
|
|
|
|
this->m_model->SetWorldTransform(position, this->m_facing, scale);
|
|
}
|