diff --git a/CMakeLists.txt b/CMakeLists.txt index 70e42d20..e668f624 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -14,7 +14,7 @@ endif() if (EMSCRIPTEN) add_compile_options(-pthread -gsource-map) - add_link_options(-sUSE_WEBGL2=1 -sMIN_WEBGL_VERSION=2 -sALLOW_MEMORY_GROWTH=1 -sMAXIMUM_MEMORY=2gb -sUSE_PTHREADS=1 -sPROXY_TO_PTHREAD=1 -sOFFSCREENCANVAS_SUPPORT=1 -sPTHREAD_POOL_SIZE_STRICT=0 -sFORCE_FILESYSTEM=1 -sWASMFS=1 -sEXIT_RUNTIME=1 -sABORT_ON_WASM_EXCEPTIONS=1 -gsource-map) + add_link_options(-sUSE_WEBGL2=1 -sMIN_WEBGL_VERSION=2 -sALLOW_MEMORY_GROWTH=1 -sINITIAL_MEMORY=128mb -sMAXIMUM_MEMORY=2gb -sUSE_PTHREADS=1 -sPROXY_TO_PTHREAD=1 -sOFFSCREENCANVAS_SUPPORT=1 -sPTHREAD_POOL_SIZE_STRICT=0 -sFORCE_FILESYSTEM=1 -sWASMFS=1 -sEXIT_RUNTIME=1 -sABORT_ON_WASM_EXCEPTIONS=1 -gsource-map) set(SDL_PTHREADS ON CACHE BOOL "Enable SDL pthreads" FORCE) find_program(LLVM_OBJCOPY_BIN NAMES llvm-objcopy HINTS "${EMSCRIPTEN_ROOT_PATH}/../bin" REQUIRED) set(ISLE_EMSCRIPTEN_VERSION_DIR "${CMAKE_BINARY_DIR}/generated") diff --git a/LEGO1/lego/legoomni/include/act2actor.h b/LEGO1/lego/legoomni/include/act2actor.h index 4d1ac98e..a3b5381b 100644 --- a/LEGO1/lego/legoomni/include/act2actor.h +++ b/LEGO1/lego/legoomni/include/act2actor.h @@ -39,6 +39,7 @@ class Act2Actor : public LegoAnimActor { } // vtable+0x68 void Animate(float p_time) override; // vtable+0x70 + void ClearMaps() override; // vtable+0x84 MxResult HitActor(LegoPathActor*, MxBool) override; // vtable+0x94 MxResult CalculateSpline() override; // vtable+0x9c MxS32 NextTargetLocation() override; // vtable+0xa0 diff --git a/LEGO1/lego/legoomni/include/act3actors.h b/LEGO1/lego/legoomni/include/act3actors.h index 99c02776..1d72e5ff 100644 --- a/LEGO1/lego/legoomni/include/act3actors.h +++ b/LEGO1/lego/legoomni/include/act3actors.h @@ -19,6 +19,7 @@ class Act3Shark : public LegoAnimActor { void ParseAction(char*) override; // vtable+0x20 void Animate(float p_time) override; // vtable+0x70 + void ClearMaps() override; // vtable+0x84 // LegoAnimActor vtable virtual MxResult EatPizza(Act3Ammo* p_ammo); // vtable+0x10 @@ -104,6 +105,7 @@ class Act3Cop : public Act3Actor { void ParseAction(char* p_extra) override; // vtable+0x20 void Animate(float p_time) override; // vtable+0x70 + void ClearMaps() override; // vtable+0x84 MxResult HitActor(LegoPathActor*, MxBool) override; // vtable+0x94 MxResult CalculateSpline() override; // vtable+0x9c @@ -137,6 +139,7 @@ class Act3Brickster : public Act3Actor { void ParseAction(char* p_extra) override; // vtable+0x20 void Animate(float p_time) override; // vtable+0x70 + void ClearMaps() override; // vtable+0x84 MxResult HitActor(LegoPathActor* p_actor, MxBool p_bool) override; // vtable+0x94 void SwitchBoundary( LegoPathBoundary*& p_boundary, diff --git a/LEGO1/lego/legoomni/include/legoanimactor.h b/LEGO1/lego/legoomni/include/legoanimactor.h index d774a897..a5eb36b2 100644 --- a/LEGO1/lego/legoomni/include/legoanimactor.h +++ b/LEGO1/lego/legoomni/include/legoanimactor.h @@ -9,6 +9,8 @@ class LegoAnim; // SIZE 0x20 struct LegoAnimActorStruct { LegoAnimActorStruct(float p_worldSpeed, LegoAnim* p_AnimTreePtr, LegoROI** p_roiMap, MxU32 p_numROIs); + LegoAnimActorStruct(const LegoAnimActorStruct& p_other); + LegoAnimActorStruct& operator=(const LegoAnimActorStruct&) = delete; ~LegoAnimActorStruct(); float GetDuration(); diff --git a/LEGO1/lego/legoomni/include/legoextraactor.h b/LEGO1/lego/legoomni/include/legoextraactor.h index bebcbcec..dfed6ab9 100644 --- a/LEGO1/lego/legoomni/include/legoextraactor.h +++ b/LEGO1/lego/legoomni/include/legoextraactor.h @@ -49,6 +49,7 @@ class LegoExtraActor : public virtual LegoAnimActor { ) override; // vtable+0x6c void Animate(float p_time) override; // vtable+0x70 void ApplyTransform(Matrix4& p_transform) override; // vtable+0x74 + void ClearMaps() override; // vtable+0x84 MxU32 StepState(float p_time, Matrix4& p_matrix) override; // vtable+0x90 MxResult HitActor(LegoPathActor* p_actor, MxBool p_bool) override; // vtable+0x94 MxResult CalculateSpline() override; // vtable+0x9c diff --git a/LEGO1/lego/legoomni/include/legoracers.h b/LEGO1/lego/legoomni/include/legoracers.h index 748fb5dc..cff4c71f 100644 --- a/LEGO1/lego/legoomni/include/legoracers.h +++ b/LEGO1/lego/legoomni/include/legoracers.h @@ -160,6 +160,7 @@ class LegoRaceCar : public LegoCarRaceActor, public LegoRaceMap { } // vtable+0x6c void Animate(float p_time) override; // vtable+0x70 + void ClearMaps() override; // vtable+0x84 MxResult HitActor(LegoPathActor* p_actor, MxBool p_bool) override; // vtable+0x94 // FUNCTION: LEGO1 0x10014560 diff --git a/LEGO1/lego/legoomni/src/actors/act2actor.cpp b/LEGO1/lego/legoomni/src/actors/act2actor.cpp index 8fb630ee..b78da1b6 100644 --- a/LEGO1/lego/legoomni/src/actors/act2actor.cpp +++ b/LEGO1/lego/legoomni/src/actors/act2actor.cpp @@ -877,3 +877,9 @@ LegoEntity* Act2Actor::GetNextEntity(MxBool* p_isBuilding) return result; } + +void Act2Actor::ClearMaps() +{ + m_shootAnim = NULL; + LegoAnimActor::ClearMaps(); +} diff --git a/LEGO1/lego/legoomni/src/actors/act3actors.cpp b/LEGO1/lego/legoomni/src/actors/act3actors.cpp index d5c8dd62..3ab7de4a 100644 --- a/LEGO1/lego/legoomni/src/actors/act3actors.cpp +++ b/LEGO1/lego/legoomni/src/actors/act3actors.cpp @@ -1231,3 +1231,22 @@ void Act3Shark::ParseAction(char* p_extra) m_unk0x38->SetVisibility(FALSE); m_world->PlaceActor(this); } + +void Act3Cop::ClearMaps() +{ + m_eatAnim = NULL; + Act3Actor::ClearMaps(); +} + +void Act3Brickster::ClearMaps() +{ + m_shootAnim = NULL; + Act3Actor::ClearMaps(); +} + +void Act3Shark::ClearMaps() +{ + m_unk0x34 = NULL; + m_unk0x38 = NULL; + LegoAnimActor::ClearMaps(); +} diff --git a/LEGO1/lego/legoomni/src/common/legoanimationmanager.cpp b/LEGO1/lego/legoomni/src/common/legoanimationmanager.cpp index 0238dc6c..30036ee4 100644 --- a/LEGO1/lego/legoomni/src/common/legoanimationmanager.cpp +++ b/LEGO1/lego/legoomni/src/common/legoanimationmanager.cpp @@ -437,11 +437,14 @@ void LegoAnimationManager::Suspend() LegoROI* roi = m_extras[i].m_roi; if (roi != NULL) { - LegoPathActor* actor = CharacterManager()->GetExtraActor(roi->GetName()); + LegoExtraActor* actor = CharacterManager()->GetExtraActor(roi->GetName()); - if (actor != NULL && actor->GetController() != NULL) { - actor->GetController()->RemoveActor(actor); - actor->SetController(NULL); + if (actor != NULL) { + if (actor->GetController() != NULL) { + actor->GetController()->RemoveActor(actor); + actor->SetController(NULL); + } + actor->ClearMaps(); } CharacterManager()->ReleaseActor(roi); @@ -466,6 +469,18 @@ void LegoAnimationManager::Suspend() m_extras[i].m_speed = -1.0f; } + // Catch dormant actors too: despawn paths null m_extras[i].m_roi + // without ClearMaps, leaving stale m_AnimTreePtr behind. + for (i = 0; i < (MxS32) CharacterManager()->GetNumActors(); i++) { + const char* name = CharacterManager()->GetActorName(i); + if (name != NULL) { + LegoExtraActor* extraActor = CharacterManager()->GetExtraActor(name); + if (extraActor != NULL) { + extraActor->ClearMaps(); + } + } + } + m_unk0x18 = 0; m_unk0x1a = FALSE; m_enableCamAnims = FALSE; diff --git a/LEGO1/lego/legoomni/src/paths/legoanimactor.cpp b/LEGO1/lego/legoomni/src/paths/legoanimactor.cpp index 9ca28921..8b168575 100644 --- a/LEGO1/lego/legoomni/src/paths/legoanimactor.cpp +++ b/LEGO1/lego/legoomni/src/paths/legoanimactor.cpp @@ -22,13 +22,39 @@ LegoAnimActorStruct::LegoAnimActorStruct( { m_worldSpeed = p_worldSpeed; m_AnimTreePtr = p_AnimTreePtr; - m_roiMap = p_roiMap; m_numROIs = p_numROIs; + // Deep copy: source array's owner may outlive or rebuild it independently. + m_roiMap = new LegoROI*[p_numROIs + 1]; + memcpy(m_roiMap, p_roiMap, (p_numROIs + 1) * sizeof(LegoROI*)); + for (MxU32 i = 0; i <= p_numROIs; i++) { + if (m_roiMap[i]) { + m_roiMap[i]->RegisterSlotRef(&m_roiMap[i]); + } + } +} + +LegoAnimActorStruct::LegoAnimActorStruct(const LegoAnimActorStruct& p_other) + : m_worldSpeed(p_other.m_worldSpeed), m_AnimTreePtr(p_other.m_AnimTreePtr), m_numROIs(p_other.m_numROIs), + m_unk0x10(p_other.m_unk0x10) +{ + m_roiMap = new LegoROI*[m_numROIs + 1]; + memcpy(m_roiMap, p_other.m_roiMap, (m_numROIs + 1) * sizeof(LegoROI*)); + for (MxU32 i = 0; i <= m_numROIs; i++) { + if (m_roiMap[i]) { + m_roiMap[i]->RegisterSlotRef(&m_roiMap[i]); + } + } } // FUNCTION: LEGO1 0x1001c0a0 LegoAnimActorStruct::~LegoAnimActorStruct() { + for (MxU32 i = 0; i <= m_numROIs; i++) { + if (m_roiMap[i]) { + m_roiMap[i]->UnregisterSlotRef(&m_roiMap[i]); + } + } + delete[] m_roiMap; for (MxU16 i = 0; i < m_unk0x10.size(); i++) { delete m_unk0x10[i]; } diff --git a/LEGO1/lego/legoomni/src/paths/legoextraactor.cpp b/LEGO1/lego/legoomni/src/paths/legoextraactor.cpp index 7e01f24b..34d597d5 100644 --- a/LEGO1/lego/legoomni/src/paths/legoextraactor.cpp +++ b/LEGO1/lego/legoomni/src/paths/legoextraactor.cpp @@ -536,3 +536,12 @@ inline MxU32 LegoExtraActor::CheckPresenterAndActorIntersections( return 0; } + +void LegoExtraActor::ClearMaps() +{ + delete m_assAnim; + m_assAnim = NULL; + delete m_disAnim; + m_disAnim = NULL; + LegoAnimActor::ClearMaps(); +} diff --git a/LEGO1/lego/legoomni/src/race/legoracers.cpp b/LEGO1/lego/legoomni/src/race/legoracers.cpp index 148d9c2f..dfba512b 100644 --- a/LEGO1/lego/legoomni/src/race/legoracers.cpp +++ b/LEGO1/lego/legoomni/src/race/legoracers.cpp @@ -736,3 +736,10 @@ MxResult LegoJetski::HitActor(LegoPathActor* p_actor, MxBool p_bool) return SUCCESS; } + +void LegoRaceCar::ClearMaps() +{ + m_skelKick1Anim = NULL; + m_skelKick2Anim = NULL; + LegoAnimActor::ClearMaps(); +} diff --git a/LEGO1/lego/legoomni/src/video/legoanimpresenter.cpp b/LEGO1/lego/legoomni/src/video/legoanimpresenter.cpp index 8eb902cd..88a77c14 100644 --- a/LEGO1/lego/legoomni/src/video/legoanimpresenter.cpp +++ b/LEGO1/lego/legoomni/src/video/legoanimpresenter.cpp @@ -86,6 +86,11 @@ void LegoAnimPresenter::Destroy(MxBool p_fromDestructor) } if (m_roiMap != NULL) { + for (MxU32 i = 0; i <= m_roiMapSize; i++) { + if (m_roiMap[i]) { + m_roiMap[i]->UnregisterSlotRef(&m_roiMap[i]); + } + } delete[] m_roiMap; } @@ -126,6 +131,11 @@ void LegoAnimPresenter::Destroy(MxBool p_fromDestructor) } if (m_ptAtCamROI != NULL) { + for (MxS32 i = 0; i < m_ptAtCamCount; i++) { + if (m_ptAtCamROI[i]) { + m_ptAtCamROI[i]->UnregisterSlotRef(&m_ptAtCamROI[i]); + } + } delete[] m_ptAtCamROI; } @@ -415,12 +425,22 @@ void LegoAnimPresenter::BuildROIMap() LegoAnimStructMap anims; if (m_ptAtCamROI != NULL) { + for (MxS32 i = 0; i < m_ptAtCamCount; i++) { + if (m_ptAtCamROI[i]) { + m_ptAtCamROI[i]->UnregisterSlotRef(&m_ptAtCamROI[i]); + } + } memset(m_ptAtCamROI, 0, m_ptAtCamCount * sizeof(*m_ptAtCamROI)); } UpdateStructMapAndROIIndex(anims, m_anim->GetRoot(), NULL); if (m_roiMap != NULL) { + for (MxU32 i = 0; i <= m_roiMapSize; i++) { + if (m_roiMap[i]) { + m_roiMap[i]->UnregisterSlotRef(&m_roiMap[i]); + } + } delete[] m_roiMap; m_roiMapSize = 0; } @@ -432,12 +452,14 @@ void LegoAnimPresenter::BuildROIMap() for (LegoAnimStructMap::iterator it = anims.begin(); it != anims.end();) { MxU32 index = (*it).second.m_index; m_roiMap[index] = (*it).second.m_roi; + m_roiMap[index]->RegisterSlotRef(&m_roiMap[index]); if (m_roiMap[index]->GetName() != NULL) { for (MxS32 i = 0; i < m_ptAtCamCount; i++) { if (m_ptAtCamROI[i] == NULL && m_ptAtCamNames[i] != NULL) { if (!SDL_strcasecmp(m_ptAtCamNames[i], m_roiMap[index]->GetName())) { m_ptAtCamROI[i] = m_roiMap[index]; + m_ptAtCamROI[i]->RegisterSlotRef(&m_ptAtCamROI[i]); break; } } @@ -1025,6 +1047,11 @@ void LegoAnimPresenter::ParseExtra() } if (m_ptAtCamROI != NULL) { + for (MxS32 i = 0; i < m_ptAtCamCount; i++) { + if (m_ptAtCamROI[i]) { + m_ptAtCamROI[i]->UnregisterSlotRef(&m_ptAtCamROI[i]); + } + } delete[] m_ptAtCamROI; m_ptAtCamROI = NULL; } @@ -1447,6 +1474,13 @@ void LegoLocomotionAnimPresenter::CreateROIAndBuildMap(LegoAnimActor* p_actor, M if (m_roiMap != NULL) { m_roiMapList->Append(m_roiMap); p_actor->CreateAnimActorStruct(m_anim, p_worldSpeed, m_roiMap, m_roiMapSize); + + for (MxU32 i = 0; i <= m_roiMapSize; i++) { + if (m_roiMap[i]) { + m_roiMap[i]->UnregisterSlotRef(&m_roiMap[i]); + } + } + m_roiMap = NULL; } diff --git a/LEGO1/lego/sources/roi/legoroi.cpp b/LEGO1/lego/sources/roi/legoroi.cpp index e7848eda..cbf05348 100644 --- a/LEGO1/lego/sources/roi/legoroi.cpp +++ b/LEGO1/lego/sources/roi/legoroi.cpp @@ -96,6 +96,9 @@ LegoROI::LegoROI(Tgl::Renderer* p_renderer, ViewLODList* p_lodList) : ViewROI(p_ // FUNCTION: BETA10 0x10189a42 LegoROI::~LegoROI() { + for (LegoROI** slot : m_slotRefs) { + *slot = NULL; + } if (comp) { CompoundObject::iterator iterator; diff --git a/LEGO1/lego/sources/roi/legoroi.h b/LEGO1/lego/sources/roi/legoroi.h index b30e319a..a85f22a6 100644 --- a/LEGO1/lego/sources/roi/legoroi.h +++ b/LEGO1/lego/sources/roi/legoroi.h @@ -5,6 +5,9 @@ #include "misc/legotypes.h" #include "viewmanager/viewroi.h" +#include +#include + typedef unsigned char (*ColorOverride)(const char*, char*, unsigned int); typedef unsigned char (*TextureHandler)(const char*, unsigned char*, unsigned int); @@ -99,6 +102,15 @@ class LegoROI : public ViewROI { void SetBoundingSphere(const BoundingSphere& p_sphere) { m_sphere = m_world_bounding_sphere = p_sphere; } void SetBoundingBox(const BoundingBox& p_box) { m_bounding_box = p_box; } + void RegisterSlotRef(LegoROI** p_slot) { m_slotRefs.push_back(p_slot); } + void UnregisterSlotRef(LegoROI** p_slot) + { + std::vector::iterator it = std::find(m_slotRefs.begin(), m_slotRefs.end(), p_slot); + if (it != m_slotRefs.end()) { + m_slotRefs.erase(it); + } + } + // SYNTHETIC: LEGO1 0x100a82b0 // SYNTHETIC: BETA10 0x1018c490 // LegoROI::`scalar deleting destructor' @@ -108,6 +120,7 @@ class LegoROI : public ViewROI { BoundingSphere m_sphere; // 0xe8 LegoBool m_sharedLodList; // 0x100 LegoEntity* m_entity; // 0x104 + std::vector m_slotRefs; }; // VTABLE: LEGO1 0x100dbea8