From c2e35001816414a6c3f4258c56469505f3538331 Mon Sep 17 00:00:00 2001 From: Tenzit Date: Fri, 1 May 2026 17:04:15 -0600 Subject: [PATCH 1/3] Add wireframe mesh drawing Yoinks the model directly from game memory and draws it using wireframe because there's no good way right now to just draw the thing cleanly like how most of the other things are drawn --- SA2LevelEditor/SA2LevelEditor.vcxproj | 2 + SA2LevelEditor/SA2LevelEditor.vcxproj.filters | 4 + .../res/Shaders/entity/fragmentShader.txt | 23 +- SA2LevelEditor/src/collision/collisionmodel.h | 32 +++ SA2LevelEditor/src/loading/LevelLoader.cpp | 2 + SA2LevelEditor/src/loading/objLoader.cpp | 224 +++++++++++++++++- SA2LevelEditor/src/loading/objLoader.h | 4 + SA2LevelEditor/src/main/Main.cpp | 12 +- SA2LevelEditor/src/main/main.h | 2 + SA2LevelEditor/src/models/TexturedModel.cpp | 9 +- SA2LevelEditor/src/rendering/Renderer.cpp | 24 +- SA2LevelEditor/src/shaders/ShaderProgram.cpp | 6 + SA2LevelEditor/src/shaders/shaderprogram.h | 5 + 13 files changed, 324 insertions(+), 25 deletions(-) diff --git a/SA2LevelEditor/SA2LevelEditor.vcxproj b/SA2LevelEditor/SA2LevelEditor.vcxproj index 6eec386..480d07e 100644 --- a/SA2LevelEditor/SA2LevelEditor.vcxproj +++ b/SA2LevelEditor/SA2LevelEditor.vcxproj @@ -152,6 +152,7 @@ + @@ -268,6 +269,7 @@ + diff --git a/SA2LevelEditor/SA2LevelEditor.vcxproj.filters b/SA2LevelEditor/SA2LevelEditor.vcxproj.filters index dccf2f9..41e1855 100644 --- a/SA2LevelEditor/SA2LevelEditor.vcxproj.filters +++ b/SA2LevelEditor/SA2LevelEditor.vcxproj.filters @@ -410,6 +410,9 @@ Source Files\entities\LevelSpecific\WeaponsBed + + Source Files + @@ -752,6 +755,7 @@ Source Files\entities\LevelSpecific\WeaponsBed + diff --git a/SA2LevelEditor/res/Shaders/entity/fragmentShader.txt b/SA2LevelEditor/res/Shaders/entity/fragmentShader.txt index c69157f..704051b 100644 --- a/SA2LevelEditor/res/Shaders/entity/fragmentShader.txt +++ b/SA2LevelEditor/res/Shaders/entity/fragmentShader.txt @@ -10,20 +10,25 @@ uniform sampler2D textureSampler2; uniform int hasTransparency; uniform vec3 baseColour; uniform float mixFactor; +uniform int hasTexture; void main(void) { - vec4 rawTextureColour = mix(texture(textureSampler, pass_textureCoords), texture(textureSampler2, pass_textureCoords), mixFactor); - rawTextureColour.rgb *= baseColour*color; + if (hasTexture != 0) { + vec4 rawTextureColour = mix(texture(textureSampler, pass_textureCoords), texture(textureSampler2, pass_textureCoords), mixFactor); + rawTextureColour.rgb *= baseColour*color; - if (hasTransparency == 0) - { - if (rawTextureColour.a < 0.9) + if (hasTransparency == 0) { - discard; + if (rawTextureColour.a < 0.9) + { + discard; + } + rawTextureColour.a = 1; } - rawTextureColour.a = 1; - } - out_Color = rawTextureColour; + out_Color = rawTextureColour; + } else { + out_Color = vec4(baseColour*color,1.0); + } } diff --git a/SA2LevelEditor/src/collision/collisionmodel.h b/SA2LevelEditor/src/collision/collisionmodel.h index 8be1ad0..a4b8afd 100644 --- a/SA2LevelEditor/src/collision/collisionmodel.h +++ b/SA2LevelEditor/src/collision/collisionmodel.h @@ -7,7 +7,39 @@ class SA2Object; #include #include +#include "../toolbox/vector.h" + +struct PCMeshset { + uint16_t typeAndMaterialID; + uint16_t numMeshes; + int16_t* __ptr32 meshes; + uint32_t* __ptr32 attrA; + Vector3f* __ptr32 normals; + uint32_t* __ptr32 vertexColor; + uint32_t* __ptr32 vertexUV; +}; + +struct PCMeshModel { + Vector3f* __ptr32 points; + Vector3f* __ptr32 normals; + uint32_t numPoints; + struct PCMeshset* __ptr32 meshsets; + uint32_t materials; + uint16_t numMeshsets; + uint16_t numMaterials; + Vector3f center; + float radius; +}; +struct PCMeshObject { + uint32_t evalFlags; + struct PCMeshModel* __ptr32 model; + Vector3f pos; + int32_t ang[3]; + Vector3f scale; + struct PCMeshObject* __ptr32 child; + struct PCMeshObject* __ptr32 sibling; +}; class CollisionModel { diff --git a/SA2LevelEditor/src/loading/LevelLoader.cpp b/SA2LevelEditor/src/loading/LevelLoader.cpp index 96d511e..9c849c4 100644 --- a/SA2LevelEditor/src/loading/LevelLoader.cpp +++ b/SA2LevelEditor/src/loading/LevelLoader.cpp @@ -81,6 +81,7 @@ #include "../entities/GlobalObjects/soapsw.h" #include "../entities/GlobalObjects/tjumpdai.h" #include "../entities/LevelSpecific/PyramidCave/torchcup.h" +#include "../entities/LevelSpecific/PyramidCave/KEYDOOR.h" #include "../entities/LevelSpecific/PyramidCave/snakestatue.h" #include "../entities/LevelSpecific/PyramidCave/sneakrail.h" #include "../entities/LevelSpecific/CosmicWall/cw_stage.h" @@ -1025,6 +1026,7 @@ SA2Object* LevelLoader::newSA2Object(int levelID, int objectID, char data[32], b else if (o == "EMERALD F") {return new EMERALD_F (data, useDefaultValues);} else if (o == "SPIDERWEB") {return new SPIDERWEB (data, useDefaultValues);} else if (o == "TORCHCUP") {return new TORCHCUP (data, useDefaultValues);} + else if (o == "KEYDOOR") {return new KEYDOOR (data, useDefaultValues);} else if (o == "SNAKESTATUE") {return new SNAKESTATUE (data, useDefaultValues);} else if (o == "SNEAKRAIL") {return new SNEAKRAIL (data, useDefaultValues);} else if (o == "SCHBOX") {return new SCHBOX (data, useDefaultValues);} diff --git a/SA2LevelEditor/src/loading/objLoader.cpp b/SA2LevelEditor/src/loading/objLoader.cpp index 4ee1f52..1c5d5e4 100644 --- a/SA2LevelEditor/src/loading/objLoader.cpp +++ b/SA2LevelEditor/src/loading/objLoader.cpp @@ -1632,7 +1632,7 @@ CollisionModel* loadBinaryColCollisionModel(std::string filePath, std::string fi CollisionModel* loadBinaryVclCollisionModel(std::string filePath, std::string fileName) { FILE* file = nullptr; - int err = fopen_s(&file, (filePath+fileName).c_str(), "rb"); + int err = fopen_s(&file, (filePath + fileName).c_str(), "rb"); if (file == nullptr || err != 0) { //std::fprintf(stderr, "Error: Cannot load file '%s'\n", (filePath + fileName).c_str()); @@ -1646,18 +1646,18 @@ CollisionModel* loadBinaryVclCollisionModel(std::string filePath, std::string fi char fileType[4]; fread(fileType, sizeof(char), 4, file); - if (fileType[0] != 'v' || + if (fileType[0] != 'v' || fileType[1] != 'c' || fileType[2] != 'l' || fileType[3] != 0) { - std::fprintf(stdout, "Error: File '%s' is not a valid .binvcl file\n", (filePath+fileName).c_str()); + std::fprintf(stdout, "Error: File '%s' is not a valid .binvcl file\n", (filePath + fileName).c_str()); return nullptr; } CollisionModel* model = new CollisionModel; INCR_NEW("CollisionModel") - std::string line; + std::string line; std::vector vertexList; int mtllibLength; @@ -1710,11 +1710,11 @@ CollisionModel* loadBinaryVclCollisionModel(std::string filePath, std::string fi fread(&f[0], sizeof(int), 6, file); - Vector3f v1 = vertexList[f[0]-1]; - Vector3f v2 = vertexList[f[2]-1]; - Vector3f v3 = vertexList[f[4]-1]; + Vector3f v1 = vertexList[f[0] - 1]; + Vector3f v2 = vertexList[f[2] - 1]; + Vector3f v3 = vertexList[f[4] - 1]; - model->triangles.push_back(new Triangle3D(&v1, &v2, &v3)); INCR_NEW ("Triangle3D") + model->triangles.push_back(new Triangle3D(&v1, &v2, &v3)); INCR_NEW("Triangle3D") } } @@ -1725,6 +1725,214 @@ CollisionModel* loadBinaryVclCollisionModel(std::string filePath, std::string fi return model; } +int loadObjModelFromPCGame(std::list* models, struct PCMeshObject* objectPtr) +{ + if (models->size() > 0) + { + return 1; + } + + + HANDLE handle = Global::getSA2Handle(); + + std::vector vertices; + std::vector normals; + std::vector texCoords; + std::vector colors; + std::vector rawModels; + SIZE_T numberBytesRead; + + struct PCMeshObject object; + if (!ReadProcessMemory(handle, objectPtr, &object, sizeof(struct PCMeshObject), &numberBytesRead) || \ + numberBytesRead != sizeof(struct PCMeshObject)) { + return -1; + } + + struct PCMeshModel model; + if (!ReadProcessMemory(handle, object.model, &model, sizeof(struct PCMeshModel), &numberBytesRead) || \ + numberBytesRead != sizeof(struct PCMeshModel)) { + return -1; + } + + vertices.resize(3 * model.numPoints); + if (!ReadProcessMemory(handle, model.points, vertices.data(), model.numPoints * 3 * sizeof(float), &numberBytesRead) || \ + numberBytesRead != model.numPoints * 3 * sizeof(float)) { + return -1; + } + normals.resize(3 * model.numPoints); + if (!ReadProcessMemory(handle, model.normals, normals.data(), model.numPoints * 3 * sizeof(float), &numberBytesRead) || \ + numberBytesRead != model.numPoints * 3 * sizeof(float)) { + return -1; + } + + texCoords.resize(2 * model.numPoints); + colors.resize(3 * model.numPoints); + for (uint32_t i = 0; i < model.numPoints; i++) { + texCoords[i*2]= 1.0f; // u + texCoords[i*2+1] = 1.0f; // v + colors[i * 3] = 1.0f; + colors[i * 3 + 1] = 0.0f; + colors[i * 3 + 2] = 0.0f; + } + + std::vector meshsets; + meshsets.resize(model.numMeshsets); + + if (!ReadProcessMemory(handle, model.meshsets, meshsets.data(), model.numMeshsets * sizeof(struct PCMeshset), &numberBytesRead) || \ + numberBytesRead != model.numMeshsets * sizeof(struct PCMeshset)) { + return -1; + } + + for (auto meshset : meshsets) { + std::vector indices; + std::vector meshVertexLists; + switch (meshset.typeAndMaterialID & 0xC000) + { + case 0xC000: + { + int16_t listSize = 0; + for (int i = 0; i < meshset.numMeshes; i++) { + int16_t tmpListSize = 0; + uint32_t offsetShorts = i + listSize; + if (!ReadProcessMemory(handle, meshset.meshes + offsetShorts, &tmpListSize, sizeof(int16_t), &numberBytesRead) || \ + numberBytesRead != sizeof(int16_t)) { + return -1; + } + tmpListSize = tmpListSize & 0x7fff; + listSize += tmpListSize; + } + meshVertexLists.resize(listSize + meshset.numMeshes); + if (!ReadProcessMemory(handle, meshset.meshes, meshVertexLists.data(), (listSize + meshset.numMeshes) * sizeof(int16_t), &numberBytesRead) || \ + numberBytesRead != (listSize + meshset.numMeshes) * sizeof(int16_t)) { + return -1; + } + uint32_t offsetCount = 0; + for (uint32_t i = 0; i < meshset.numMeshes; i++) { + // meshVertexLists[offsetCount] will always be at least 3 + // as it's the number of vertices + uint32_t numVerts = meshVertexLists[offsetCount] & 0x7fff; + bool rightWinding = meshVertexLists[offsetCount] & 0x8000; + for (uint32_t j = 3; j <= numVerts; j++) { + if (rightWinding) { + indices.emplace_back(meshVertexLists[offsetCount + j - 1]); + indices.emplace_back(meshVertexLists[offsetCount + j - 2]); + indices.emplace_back(meshVertexLists[offsetCount + j]); + } + else { + indices.emplace_back(meshVertexLists[offsetCount + j - 2]); + indices.emplace_back(meshVertexLists[offsetCount + j - 1]); + indices.emplace_back(meshVertexLists[offsetCount + j]); + } + rightWinding = !rightWinding; + } + offsetCount += (numVerts + 1); + } + rawModels.push_back(Loader::loadToVAO(&vertices, &texCoords, &normals, &colors, &indices)); + } + default: + continue; + } + } + //std::vector texID; + //texID.push_back(Loader::loadTexture("res/Models/LevelObjects/PyramidCave/SNAKESTATUE/collision.png")); + //ModelTexture tex(&texID); + for (RawModel rawModel : rawModels) { + TexturedModel* tm = new TexturedModel(&rawModel, nullptr); INCR_NEW("TexturedModel") + models->push_back(tm); + } + return 1; +} + + +CollisionModel* loadCollisionModelFromPCGame(PCMeshObject* objectPtr) +{ + HANDLE handle = Global::getSA2Handle(); + CollisionModel* collisionModel = new CollisionModel; INCR_NEW("CollisionModel") + std::vector vertices; + SIZE_T numberBytesRead; + + struct PCMeshObject object; + if (!ReadProcessMemory(handle, objectPtr, &object, sizeof(struct PCMeshObject), &numberBytesRead) || \ + numberBytesRead != sizeof(struct PCMeshObject)) { + return nullptr; + } + + struct PCMeshModel model; + if (!ReadProcessMemory(handle, object.model, &model, sizeof(struct PCMeshModel), &numberBytesRead) || \ + numberBytesRead != sizeof(struct PCMeshModel)) { + return nullptr; + } + + vertices.resize(model.numPoints); + if (!ReadProcessMemory(handle, model.points, vertices.data(), model.numPoints * sizeof(Vector3f), &numberBytesRead) || \ + numberBytesRead != model.numPoints * sizeof(Vector3f)) { + return nullptr; + } + + std::vector meshsets; + meshsets.resize(model.numMeshsets); + + if (!ReadProcessMemory(handle, model.meshsets, meshsets.data(), model.numMeshsets * sizeof(struct PCMeshset), &numberBytesRead) || \ + numberBytesRead != model.numMeshsets * sizeof(struct PCMeshset)) { + return nullptr; + } + + for (auto meshset : meshsets) { + std::vector meshVertexLists; + switch (meshset.typeAndMaterialID & 0xC000) + { + case 0xC000: + { + int32_t listSize = 0; + for (int i = 0; i < meshset.numMeshes; i++) { + int16_t tmpListSize = 0; + uint32_t offsetShorts = i + listSize; + if (!ReadProcessMemory(handle, meshset.meshes + offsetShorts, &tmpListSize, sizeof(int16_t), &numberBytesRead) || \ + numberBytesRead != sizeof(int16_t)) { + return nullptr; + } + // MSB is used to indicate winding of triangles in strip + tmpListSize = tmpListSize & 0x7fff; + listSize += tmpListSize; + } + meshVertexLists.resize(listSize + meshset.numMeshes); + if (!ReadProcessMemory(handle, meshset.meshes, meshVertexLists.data(), (listSize + meshset.numMeshes) * sizeof(int16_t), &numberBytesRead) || \ + numberBytesRead != (listSize + meshset.numMeshes) * sizeof(int16_t)) { + return nullptr; + } + uint32_t offsetCount = 0; + for (uint32_t i = 0; i < meshset.numMeshes; i++) { + // meshVertexLists[offsetCount] will always be at least 3 + // as it's the number of vertices + uint32_t numVerts = meshVertexLists[offsetCount] & 0x7fff; + bool rightWinding = meshVertexLists[offsetCount] & 0x8000; + for (uint32_t j = 3; j <= numVerts; j++) { + Vector3f v1, v2, v3; + if (rightWinding) { + v2 = vertices[meshVertexLists[offsetCount + j - 1]]; + v1 = vertices[meshVertexLists[offsetCount + j - 2]]; + v3 = vertices[meshVertexLists[offsetCount + j]]; + } + else { + v1 = vertices[meshVertexLists[offsetCount + j - 2]]; + v2 = vertices[meshVertexLists[offsetCount + j - 1]]; + v3 = vertices[meshVertexLists[offsetCount + j]]; + } + collisionModel->triangles.push_back(new Triangle3D(&v1, &v2, &v3)); INCR_NEW("Triangle3D") + rightWinding = !rightWinding; + } + offsetCount += (numVerts + 1); + } + break; + } + default: + return nullptr; + } + } + collisionModel->generateMinMaxValues(); + return collisionModel; +} + CollisionModel* loadBinaryObjCollisionModel(std::string filePath, std::string fileName) { FILE* file = nullptr; diff --git a/SA2LevelEditor/src/loading/objLoader.h b/SA2LevelEditor/src/loading/objLoader.h index e1d27bc..5a037f5 100644 --- a/SA2LevelEditor/src/loading/objLoader.h +++ b/SA2LevelEditor/src/loading/objLoader.h @@ -6,6 +6,7 @@ class CollisionModel; #include #include +#include "../collision/collisionmodel.h" //Attempts to load a model as either an OBJ or binary format. //Checks for binary file first, then tries OBJ. @@ -47,4 +48,7 @@ CollisionModel* loadBinaryObjCollisionModel(std::string filePath, std::string fi //The CollisionModel returned must be deleted later. CollisionModel* loadBinaryVclCollisionModel(std::string filePath, std::string fileName); + +int loadObjModelFromPCGame(std::list* models, struct PCMeshObject* objectPtr); +CollisionModel* loadCollisionModelFromPCGame(struct PCMeshObject* objectPtr); #endif diff --git a/SA2LevelEditor/src/main/Main.cpp b/SA2LevelEditor/src/main/Main.cpp index 71b3860..f9e406c 100644 --- a/SA2LevelEditor/src/main/Main.cpp +++ b/SA2LevelEditor/src/main/Main.cpp @@ -97,6 +97,7 @@ #include "../entities/GlobalObjects/soapsw.h" #include "../entities/GlobalObjects/tjumpdai.h" #include "../entities/LevelSpecific/PyramidCave/torchcup.h" +#include "../entities/LevelSpecific/PyramidCave/KEYDOOR.h" #include "../entities/LevelSpecific/PyramidCave/snakestatue.h" #include "../entities/LevelSpecific/PyramidCave/sneakrail.h" #include "../entities/LevelSpecific/CosmicWall/cw_stage.h" @@ -1195,6 +1196,11 @@ DWORD sa2PID = NULL; HANDLE sa2Handle = NULL; float timeUntilNextProcessAttach = ATTACH_DELAY; +HANDLE Global::getSA2Handle() +{ + return sa2Handle; +} + DWORD getPIDByName(const char* processName) { PROCESSENTRY32 pe32 = {0}; @@ -1302,6 +1308,10 @@ void Global::attemptAttachToSA2() DolphinBase::m_ARAMAccessible = false; } } + else if (sa2Handle != NULL && Global::sa2Type == Global::SA2Type::Steam) { + KEYDOOR::loadStaticModels(); + } + } } else @@ -2077,4 +2087,4 @@ void Global::deleteGhost(short slot) } Global::userGhosts[slot] = nullptr; -} +} \ No newline at end of file diff --git a/SA2LevelEditor/src/main/main.h b/SA2LevelEditor/src/main/main.h index 5c8ee47..1db19ba 100644 --- a/SA2LevelEditor/src/main/main.h +++ b/SA2LevelEditor/src/main/main.h @@ -250,5 +250,7 @@ class Global static void addTransparentEntity(Entity* entityToAdd); static void deleteTransparentEntity(Entity* entityToDelete); static void deleteAllTransparentEntites(); + + static HANDLE getSA2Handle(); }; #endif diff --git a/SA2LevelEditor/src/models/TexturedModel.cpp b/SA2LevelEditor/src/models/TexturedModel.cpp index 799a64e..39cfe26 100644 --- a/SA2LevelEditor/src/models/TexturedModel.cpp +++ b/SA2LevelEditor/src/models/TexturedModel.cpp @@ -26,8 +26,13 @@ TexturedModel::TexturedModel(RawModel* model, ModelTexture* texture) } //Copy over the ModelTexture data - this->texture = ModelTexture(texture); - this->texture.addMeToAnimationsSetIfNeeded(); + if (texture) { + this->texture = ModelTexture(texture); + this->texture.addMeToAnimationsSetIfNeeded(); + } + else { + this->texture = ModelTexture(); + } } RawModel* TexturedModel::getRawModel() diff --git a/SA2LevelEditor/src/rendering/Renderer.cpp b/SA2LevelEditor/src/rendering/Renderer.cpp index 55fb194..91e642e 100644 --- a/SA2LevelEditor/src/rendering/Renderer.cpp +++ b/SA2LevelEditor/src/rendering/Renderer.cpp @@ -29,12 +29,19 @@ void EntityRenderer::renderNEW(std::unordered_map* entityList = &entry.second; - + int noTexture = entry.first->getTexture()->getIDs()->size() == 0; + if (noTexture) { + glLineWidth(2.0); + glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); + } for (Entity* entity : (*entityList)) { prepareInstance(entity); glDrawElements(GL_TRIANGLES, (entry.first)->getRawModel()->getVertexCount(), GL_UNSIGNED_INT, 0); } + if (noTexture) { + glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); + } unbindTexturedModel(); } } @@ -60,10 +67,17 @@ void EntityRenderer::prepareTexturedModel(TexturedModel* model) shader->loadTransparency(texture->hasTransparency); shader->loadTextureOffsets(clockTime * (texture->scrollX), clockTime * (texture->scrollY)); shader->loadMixFactor(texture->mixFactor()); - glActiveTexture(GL_TEXTURE0); - glBindTexture(GL_TEXTURE_2D, texture->getID()); - glActiveTexture(GL_TEXTURE1); - glBindTexture(GL_TEXTURE_2D, texture->getID2()); + if (texture->getIDs()->size() > 0) { + shader->loadHasTexture(1); + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, texture->getID()); + glActiveTexture(GL_TEXTURE1); + glBindTexture(GL_TEXTURE_2D, texture->getID2()); + + } + else { + shader->loadHasTexture(0); + } } void EntityRenderer::unbindTexturedModel() diff --git a/SA2LevelEditor/src/shaders/ShaderProgram.cpp b/SA2LevelEditor/src/shaders/ShaderProgram.cpp index f9a6f99..beb4758 100644 --- a/SA2LevelEditor/src/shaders/ShaderProgram.cpp +++ b/SA2LevelEditor/src/shaders/ShaderProgram.cpp @@ -70,6 +70,11 @@ void ShaderProgram::loadTransparency(int transparency) loadInt(location_hasTransparency, transparency); } +void ShaderProgram::loadHasTexture(int hasTexture) +{ + loadInt(location_hasTexture, hasTexture); +} + void ShaderProgram::loadBaseColour(Vector3f* baseColour) { loadVector(location_baseColour, baseColour); @@ -111,6 +116,7 @@ void ShaderProgram::getAllUniformLocations() location_clipPlaneBehind = getUniformLocation("clipPlaneBehind"); location_mixFactor = getUniformLocation("mixFactor"); location_textureSampler2 = getUniformLocation("textureSampler2"); + location_hasTexture = getUniformLocation("hasTexture"); } int ShaderProgram::getUniformLocation(const char* uniformName) diff --git a/SA2LevelEditor/src/shaders/shaderprogram.h b/SA2LevelEditor/src/shaders/shaderprogram.h index 368f0ff..2a17482 100644 --- a/SA2LevelEditor/src/shaders/shaderprogram.h +++ b/SA2LevelEditor/src/shaders/shaderprogram.h @@ -26,6 +26,7 @@ class ShaderProgram int location_clipPlaneBehind; int location_mixFactor; int location_textureSampler2; + int location_hasTexture; public: ShaderProgram(const char* vertexFilename, const char* fragmentFilename); @@ -52,8 +53,12 @@ class ShaderProgram void loadMixFactor(float factor); + void loadHasTexture(int hasTexture); + void connectTextureUnits(); + + protected: void bindAttributes(); From 833dad8caf6b56af0b23466f2891cab7df247a79 Mon Sep 17 00:00:00 2001 From: Tenzit Date: Sun, 7 Jun 2026 11:48:11 -0600 Subject: [PATCH 2/3] Replace wireframe drawer with triangle drawer Will automatically draw with this for memory objects if there's no texture on the object --- .../res/Shaders/entity/fragmentShader.txt | 7 +- .../res/Shaders/entity/vertexShader.txt | 3 + SA2LevelEditor/src/loading/Loader.cpp | 31 ++- SA2LevelEditor/src/loading/loader.h | 9 +- SA2LevelEditor/src/loading/objLoader.cpp | 239 ++++++++++-------- SA2LevelEditor/src/rendering/Renderer.cpp | 18 +- SA2LevelEditor/src/shaders/ShaderProgram.cpp | 1 + 7 files changed, 185 insertions(+), 123 deletions(-) diff --git a/SA2LevelEditor/res/Shaders/entity/fragmentShader.txt b/SA2LevelEditor/res/Shaders/entity/fragmentShader.txt index 704051b..c007f3e 100644 --- a/SA2LevelEditor/res/Shaders/entity/fragmentShader.txt +++ b/SA2LevelEditor/res/Shaders/entity/fragmentShader.txt @@ -2,6 +2,7 @@ in vec2 pass_textureCoords; in vec3 color; +in vec3 vBary; out vec4 out_Color; @@ -29,6 +30,10 @@ void main(void) out_Color = rawTextureColour; } else { - out_Color = vec4(baseColour*color,1.0); + float edge = min(vBary.x, min(vBary.y, vBary.z)); + if (edge < 0.01) + out_Color = vec4(0.0, 0.0, 0.0, 1.0); + else + out_Color = vec4(baseColour*color,1.0); } } diff --git a/SA2LevelEditor/res/Shaders/entity/vertexShader.txt b/SA2LevelEditor/res/Shaders/entity/vertexShader.txt index b6eeae7..283a820 100644 --- a/SA2LevelEditor/res/Shaders/entity/vertexShader.txt +++ b/SA2LevelEditor/res/Shaders/entity/vertexShader.txt @@ -4,9 +4,11 @@ in vec3 position; in vec2 textureCoords; in vec3 normal; in vec3 vertexColor; +in vec3 bary; out vec2 pass_textureCoords; out vec3 color; +out vec3 vBary; uniform mat4 transformationMatrix; uniform mat4 projectionMatrix; @@ -30,4 +32,5 @@ void main(void) color = vertexColor; color *= 0.75 + (normal.y/4); + vBary = bary; } diff --git a/SA2LevelEditor/src/loading/Loader.cpp b/SA2LevelEditor/src/loading/Loader.cpp index 974fdd5..f8fc6c9 100644 --- a/SA2LevelEditor/src/loading/Loader.cpp +++ b/SA2LevelEditor/src/loading/Loader.cpp @@ -23,11 +23,12 @@ std::unordered_map Loader::texIdToFilename; int Loader::vaoNumber = 0; int Loader::vboNumber = 0; -RawModel Loader::loadToVAO(std::vector* positions, - std::vector* textureCoords, - std::vector* normals, - std::vector* vertexColors, - std::vector* indicies) +RawModel Loader::loadToVAO(std::vector* positions, + std::vector* textureCoords, + std::vector* normals, + std::vector* vertexColors, + std::vector* indicies, + std::vector* bary) { GLuint vaoID = createVAO(); std::list vboIDs; @@ -37,12 +38,32 @@ RawModel Loader::loadToVAO(std::vector* positions, vboIDs.push_back(storeDataInAttributeList(1, 2, textureCoords)); vboIDs.push_back(storeDataInAttributeList(2, 3, normals)); vboIDs.push_back(storeDataInAttributeList(3, 3, vertexColors)); + if (bary != nullptr) { + vboIDs.push_back(storeDataInAttributeList(4, 3, bary)); + } + else { + std::vector tmpBary(positions->size(), 0.0f); + vboIDs.push_back(storeDataInAttributeList(4, 3, &tmpBary)); + } unbindVAO(); return RawModel(vaoID, (int)indicies->size(), &vboIDs); } +RawModel Loader::loadToVAO( + std::vector* positions, std::vector* normals, std::vector* bary, std::vector* indices) +{ + GLuint vaoID = createVAO(); + std::list vboIDs; + + vboIDs.push_back(bindIndiciesBuffer(indices)); + vboIDs.push_back(storeDataInAttributeList(0, 3, positions)); + vboIDs.push_back(storeDataInAttributeList(2, 3, normals)); + vboIDs.push_back(storeDataInAttributeList(4, 3, bary)); + return RawModel(vaoID, (int)indices->size(), &vboIDs); +} + //for gui RawModel Loader::loadToVAO(std::vector* positions, int dimensions) { diff --git a/SA2LevelEditor/src/loading/loader.h b/SA2LevelEditor/src/loading/loader.h index a307fac..c12dc61 100644 --- a/SA2LevelEditor/src/loading/loader.h +++ b/SA2LevelEditor/src/loading/loader.h @@ -43,7 +43,14 @@ class Loader std::vector* textureCoords, std::vector* normals, std::vector* vertexColors, - std::vector* indices); + std::vector* indices, + std::vector* bary=nullptr); + + static RawModel loadToVAO( + std::vector* positions, + std::vector* normals, + std::vector* bary, + std::vector* indices); //for gui static RawModel loadToVAO(std::vector* positions, int dimensions); diff --git a/SA2LevelEditor/src/loading/objLoader.cpp b/SA2LevelEditor/src/loading/objLoader.cpp index 1c5d5e4..7f0d236 100644 --- a/SA2LevelEditor/src/loading/objLoader.cpp +++ b/SA2LevelEditor/src/loading/objLoader.cpp @@ -28,19 +28,19 @@ void processVertexBinary(int, int, int, std::vector* vertices, std::vector* indices); -void dealWithAlreadyProcessedVertex(Vertex*, - int, - int, - std::vector*, +void dealWithAlreadyProcessedVertex(Vertex*, + int, + int, + std::vector*, std::vector*); void removeUnusedVertices(std::vector* vertices); void convertDataToArrays( - std::vector* vertices, + std::vector* vertices, std::vector* textures, - std::vector* normals, - std::vector* verticesArray, + std::vector* normals, + std::vector* verticesArray, std::vector* texturesArray, std::vector* normalsArray, std::vector* colorsArray); @@ -54,7 +54,7 @@ std::vector textureNamesList; int loadModel(std::list* models, std::string filePath, std::string fileName) { int attemptBinaryOBJ = loadBinaryObjModel(models, filePath, fileName+".binobj"); - + if (attemptBinaryOBJ == -1) { int attemptBinaryVCL = loadBinaryVclModel(models, filePath, fileName+".binvcl"); @@ -65,8 +65,8 @@ int loadModel(std::list* models, std::string filePath, std::stri if (attemptOBJ == -1) { - std::fprintf(stderr, "Error: Cannot load model from file\n'%s' or\n'%s' or\n'%s'\n", - ((filePath + fileName) + ".binobj").c_str(), + std::fprintf(stderr, "Error: Cannot load model from file\n'%s' or\n'%s' or\n'%s'\n", + ((filePath + fileName) + ".binobj").c_str(), ((filePath + fileName) + ".binvcl").c_str(), ((filePath + fileName) + ".obj").c_str()); } @@ -83,7 +83,7 @@ int loadModel(std::list* models, std::string filePath, std::stri CollisionModel* loadCollisionModel(std::string filePath, std::string fileName) { CollisionModel* attemptBinaryCol = loadBinaryColCollisionModel(filePath, fileName+".bincol"); - + if (attemptBinaryCol == nullptr) { CollisionModel* attemptBinaryOBJ = loadBinaryObjCollisionModel(filePath, fileName+".binobj"); @@ -98,8 +98,8 @@ CollisionModel* loadCollisionModel(std::string filePath, std::string fileName) if (attemptObj == nullptr) { - std::fprintf(stderr, "Error: Cannot load collision from file \n'%s' or\n'%s' or\n'%s' or \n'%s'\n", - ((filePath + fileName) + ".bincol").c_str(), + std::fprintf(stderr, "Error: Cannot load collision from file \n'%s' or\n'%s' or\n'%s' or \n'%s'\n", + ((filePath + fileName) + ".bincol").c_str(), ((filePath + fileName) + ".binobj").c_str(), ((filePath + fileName) + ".binvcl").c_str(), ((filePath + fileName) + ".obj").c_str()); @@ -143,7 +143,7 @@ int loadBinaryObjModel(std::list* models, std::string filePath, char fileType[4]; fread(fileType, sizeof(char), 4, file); - if (fileType[0] != 'o' || + if (fileType[0] != 'o' || fileType[1] != 'b' || fileType[2] != 'j' || fileType[3] != 0) @@ -322,7 +322,7 @@ int loadBinaryVclModel(std::list* models, std::string filePath, char fileType[4]; fread(fileType, sizeof(char), 4, file); - if (fileType[0] != 'v' || + if (fileType[0] != 'v' || fileType[1] != 'c' || fileType[2] != 'l' || fileType[3] != 0) @@ -367,10 +367,10 @@ int loadBinaryVclModel(std::list* models, std::string filePath, //new float t[3]; fread(t, sizeof(float), 3, file); - + Vector3f vertex(t[0], t[1], t[2]); Vertex* newVertex = new Vertex((int)vertices.size(), &vertex); INCR_NEW("Vertex"); - + unsigned char c[3]; fread(c, sizeof(unsigned char), 3, file); float red = ((float)c[0])/255.0f; @@ -1041,7 +1041,7 @@ int loadBinaryObjModelWithMTL(std::list* models, std::string fil char fileType[4]; fread(fileType, sizeof(char), 4, file); - if (fileType[0] != 'o' || + if (fileType[0] != 'o' || fileType[1] != 'b' || fileType[2] != 'j' || fileType[3] != 0) @@ -1273,10 +1273,10 @@ void dealWithAlreadyProcessedVertex( void convertDataToArrays( - std::vector* vertices, + std::vector* vertices, std::vector* textures, - std::vector* normals, - std::vector* verticesArray, + std::vector* normals, + std::vector* verticesArray, std::vector* texturesArray, std::vector* normalsArray, std::vector* colorsArray) @@ -1473,7 +1473,7 @@ CollisionModel* loadBinaryColCollisionModel(std::string filePath, std::string fi char fileType[4]; fread(fileType, sizeof(char), 4, file); - if (fileType[0] != 'c' || + if (fileType[0] != 'c' || fileType[1] != 'o' || fileType[2] != 'l' || fileType[3] != 0) @@ -1734,11 +1734,12 @@ int loadObjModelFromPCGame(std::list* models, struct PCMeshObjec HANDLE handle = Global::getSA2Handle(); - + std::vector vertices; std::vector normals; std::vector texCoords; std::vector colors; + std::vector bary; std::vector rawModels; SIZE_T numberBytesRead; @@ -1767,12 +1768,16 @@ int loadObjModelFromPCGame(std::list* models, struct PCMeshObjec texCoords.resize(2 * model.numPoints); colors.resize(3 * model.numPoints); + bary.resize(3 * model.numPoints); for (uint32_t i = 0; i < model.numPoints; i++) { texCoords[i*2]= 1.0f; // u texCoords[i*2+1] = 1.0f; // v - colors[i * 3] = 1.0f; - colors[i * 3 + 1] = 0.0f; - colors[i * 3 + 2] = 0.0f; + colors[i * 3] = 0.8f; + colors[i * 3 + 1] = 0.8f; + colors[i * 3 + 2] = 0.8f; + bary[i * 3] = 0.0f; + bary[i * 3 + 1] = 0.0f; + bary[i * 3 + 2] = 0.0f; } std::vector meshsets; @@ -1786,56 +1791,74 @@ int loadObjModelFromPCGame(std::list* models, struct PCMeshObjec for (auto meshset : meshsets) { std::vector indices; std::vector meshVertexLists; - switch (meshset.typeAndMaterialID & 0xC000) - { - case 0xC000: - { - int16_t listSize = 0; - for (int i = 0; i < meshset.numMeshes; i++) { - int16_t tmpListSize = 0; - uint32_t offsetShorts = i + listSize; - if (!ReadProcessMemory(handle, meshset.meshes + offsetShorts, &tmpListSize, sizeof(int16_t), &numberBytesRead) || \ - numberBytesRead != sizeof(int16_t)) { - return -1; - } - tmpListSize = tmpListSize & 0x7fff; - listSize += tmpListSize; - } - meshVertexLists.resize(listSize + meshset.numMeshes); - if (!ReadProcessMemory(handle, meshset.meshes, meshVertexLists.data(), (listSize + meshset.numMeshes) * sizeof(int16_t), &numberBytesRead) || \ - numberBytesRead != (listSize + meshset.numMeshes) * sizeof(int16_t)) { - return -1; - } - uint32_t offsetCount = 0; - for (uint32_t i = 0; i < meshset.numMeshes; i++) { - // meshVertexLists[offsetCount] will always be at least 3 - // as it's the number of vertices - uint32_t numVerts = meshVertexLists[offsetCount] & 0x7fff; - bool rightWinding = meshVertexLists[offsetCount] & 0x8000; + switch (meshset.typeAndMaterialID & 0xC000) + { + case 0xC000: + { + int16_t listSize = 0; + for (int i = 0; i < meshset.numMeshes; i++) { + int16_t tmpListSize = 0; + uint32_t offsetShorts = i + listSize; + if (!ReadProcessMemory(handle, meshset.meshes + offsetShorts, &tmpListSize, sizeof(int16_t), &numberBytesRead) || \ + numberBytesRead != sizeof(int16_t)) { + return -1; + } + tmpListSize = tmpListSize & 0x7fff; + listSize += tmpListSize; + } + meshVertexLists.resize(listSize + meshset.numMeshes); + if (!ReadProcessMemory(handle, meshset.meshes, meshVertexLists.data(), (listSize + meshset.numMeshes) * sizeof(int16_t), &numberBytesRead) || \ + numberBytesRead != (listSize + meshset.numMeshes) * sizeof(int16_t)) { + return -1; + } + uint32_t offsetCount = 0; + for (uint32_t i = 0; i < meshset.numMeshes; i++) { + // meshVertexLists[offsetCount] will always be at least 3 + // as it's the number of vertices + uint32_t numVerts = meshVertexLists[offsetCount] & 0x7fff; + bool rightWinding = meshVertexLists[offsetCount] & 0x8000; + indices.clear(); + indices.shrink_to_fit(); + if (rightWinding) { + indices.emplace_back(meshVertexLists[offsetCount + 2]); + indices.emplace_back(meshVertexLists[offsetCount + 1]); + } + else { + indices.emplace_back(meshVertexLists[offsetCount + 1]); + indices.emplace_back(meshVertexLists[offsetCount + 2]); + } for (uint32_t j = 3; j <= numVerts; j++) { - if (rightWinding) { - indices.emplace_back(meshVertexLists[offsetCount + j - 1]); - indices.emplace_back(meshVertexLists[offsetCount + j - 2]); - indices.emplace_back(meshVertexLists[offsetCount + j]); + indices.emplace_back(meshVertexLists[offsetCount + j]); + } + offsetCount += (numVerts + 1); + std::unordered_set seen; + std::vector indicesSlice; + int startIdx = 0; + for (int j = 0; j < indices.size(); j++) { + int index = indices[j]; + if (seen.find(index) != seen.end()) { + indicesSlice = std::vector(indices.begin() + startIdx, indices.begin() + j); + rawModels.push_back(Loader::loadToVAO(&vertices, &texCoords, &normals, &colors, &indicesSlice, &bary)); + + seen.clear(); + seen.insert(indices[j - 1]); + seen.insert(indices[j - 2]); + startIdx = j - 2; } - else { - indices.emplace_back(meshVertexLists[offsetCount + j - 2]); - indices.emplace_back(meshVertexLists[offsetCount + j - 1]); - indices.emplace_back(meshVertexLists[offsetCount + j]); - } - rightWinding = !rightWinding; - } - offsetCount += (numVerts + 1); - } - rawModels.push_back(Loader::loadToVAO(&vertices, &texCoords, &normals, &colors, &indices)); - } - default: - continue; + seen.insert(index); + bary[3 * index + 0] = 1.0f * ((j + 0) % 3 == 0); + bary[3 * index + 1] = 1.0f * ((j + 1) % 3 == 0); + bary[3 * index + 2] = 1.0f * ((j + 2) % 3 == 0); + } + indicesSlice = std::vector(indices.begin() + startIdx, indices.end()); + rawModels.push_back(Loader::loadToVAO(&vertices, &texCoords, &normals, &colors, &indicesSlice, &bary)); + } + } + + default: + continue; } } - //std::vector texID; - //texID.push_back(Loader::loadTexture("res/Models/LevelObjects/PyramidCave/SNAKESTATUE/collision.png")); - //ModelTexture tex(&texID); for (RawModel rawModel : rawModels) { TexturedModel* tm = new TexturedModel(&rawModel, nullptr); INCR_NEW("TexturedModel") models->push_back(tm); @@ -1879,33 +1902,33 @@ CollisionModel* loadCollisionModelFromPCGame(PCMeshObject* objectPtr) for (auto meshset : meshsets) { std::vector meshVertexLists; - switch (meshset.typeAndMaterialID & 0xC000) - { - case 0xC000: - { - int32_t listSize = 0; - for (int i = 0; i < meshset.numMeshes; i++) { - int16_t tmpListSize = 0; - uint32_t offsetShorts = i + listSize; - if (!ReadProcessMemory(handle, meshset.meshes + offsetShorts, &tmpListSize, sizeof(int16_t), &numberBytesRead) || \ - numberBytesRead != sizeof(int16_t)) { - return nullptr; - } - // MSB is used to indicate winding of triangles in strip - tmpListSize = tmpListSize & 0x7fff; - listSize += tmpListSize; - } - meshVertexLists.resize(listSize + meshset.numMeshes); - if (!ReadProcessMemory(handle, meshset.meshes, meshVertexLists.data(), (listSize + meshset.numMeshes) * sizeof(int16_t), &numberBytesRead) || \ - numberBytesRead != (listSize + meshset.numMeshes) * sizeof(int16_t)) { - return nullptr; - } - uint32_t offsetCount = 0; - for (uint32_t i = 0; i < meshset.numMeshes; i++) { - // meshVertexLists[offsetCount] will always be at least 3 - // as it's the number of vertices - uint32_t numVerts = meshVertexLists[offsetCount] & 0x7fff; - bool rightWinding = meshVertexLists[offsetCount] & 0x8000; + switch (meshset.typeAndMaterialID & 0xC000) + { + case 0xC000: + { + int32_t listSize = 0; + for (int i = 0; i < meshset.numMeshes; i++) { + int16_t tmpListSize = 0; + uint32_t offsetShorts = i + listSize; + if (!ReadProcessMemory(handle, meshset.meshes + offsetShorts, &tmpListSize, sizeof(int16_t), &numberBytesRead) || \ + numberBytesRead != sizeof(int16_t)) { + return nullptr; + } + // MSB is used to indicate winding of triangles in strip + tmpListSize = tmpListSize & 0x7fff; + listSize += tmpListSize; + } + meshVertexLists.resize(listSize + meshset.numMeshes); + if (!ReadProcessMemory(handle, meshset.meshes, meshVertexLists.data(), (listSize + meshset.numMeshes) * sizeof(int16_t), &numberBytesRead) || \ + numberBytesRead != (listSize + meshset.numMeshes) * sizeof(int16_t)) { + return nullptr; + } + uint32_t offsetCount = 0; + for (uint32_t i = 0; i < meshset.numMeshes; i++) { + // meshVertexLists[offsetCount] will always be at least 3 + // as it's the number of vertices + uint32_t numVerts = meshVertexLists[offsetCount] & 0x7fff; + bool rightWinding = meshVertexLists[offsetCount] & 0x8000; for (uint32_t j = 3; j <= numVerts; j++) { Vector3f v1, v2, v3; if (rightWinding) { @@ -1918,15 +1941,15 @@ CollisionModel* loadCollisionModelFromPCGame(PCMeshObject* objectPtr) v2 = vertices[meshVertexLists[offsetCount + j - 1]]; v3 = vertices[meshVertexLists[offsetCount + j]]; } - collisionModel->triangles.push_back(new Triangle3D(&v1, &v2, &v3)); INCR_NEW("Triangle3D") - rightWinding = !rightWinding; - } - offsetCount += (numVerts + 1); - } - break; - } + collisionModel->triangles.push_back(new Triangle3D(&v1, &v2, &v3)); INCR_NEW("Triangle3D") + rightWinding = !rightWinding; + } + offsetCount += (numVerts + 1); + } + break; + } default: - return nullptr; + return nullptr; } } collisionModel->generateMinMaxValues(); @@ -1950,7 +1973,7 @@ CollisionModel* loadBinaryObjCollisionModel(std::string filePath, std::string fi char fileType[4]; fread(fileType, sizeof(char), 4, file); - if (fileType[0] != 'o' || + if (fileType[0] != 'o' || fileType[1] != 'b' || fileType[2] != 'j' || fileType[3] != 0) diff --git a/SA2LevelEditor/src/rendering/Renderer.cpp b/SA2LevelEditor/src/rendering/Renderer.cpp index 91e642e..6cd8d44 100644 --- a/SA2LevelEditor/src/rendering/Renderer.cpp +++ b/SA2LevelEditor/src/rendering/Renderer.cpp @@ -30,17 +30,15 @@ void EntityRenderer::renderNEW(std::unordered_map* entityList = &entry.second; int noTexture = entry.first->getTexture()->getIDs()->size() == 0; - if (noTexture) { - glLineWidth(2.0); - glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); - } for (Entity* entity : (*entityList)) { prepareInstance(entity); - glDrawElements(GL_TRIANGLES, (entry.first)->getRawModel()->getVertexCount(), GL_UNSIGNED_INT, 0); - } - if (noTexture) { - glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); + if (noTexture) { + glDrawElements(GL_TRIANGLE_STRIP, (entry.first)->getRawModel()->getVertexCount(), GL_UNSIGNED_INT, 0); + } + else { + glDrawElements(GL_TRIANGLES, (entry.first)->getRawModel()->getVertexCount(), GL_UNSIGNED_INT, 0); + } } unbindTexturedModel(); } @@ -54,8 +52,11 @@ void EntityRenderer::prepareTexturedModel(TexturedModel* model) glEnableVertexAttribArray(1); glEnableVertexAttribArray(2); glEnableVertexAttribArray(3); + glEnableVertexAttribArray(4); ModelTexture* texture = model->getTexture(); + int noTexture = texture->getIDs()->size() == 0; + //if (texture->getHasTransparency() != 0) { //Master_disableCulling(); @@ -86,6 +87,7 @@ void EntityRenderer::unbindTexturedModel() glDisableVertexAttribArray(1); glDisableVertexAttribArray(2); glDisableVertexAttribArray(3); + glDisableVertexAttribArray(4); glBindVertexArray(0); } diff --git a/SA2LevelEditor/src/shaders/ShaderProgram.cpp b/SA2LevelEditor/src/shaders/ShaderProgram.cpp index beb4758..b51582c 100644 --- a/SA2LevelEditor/src/shaders/ShaderProgram.cpp +++ b/SA2LevelEditor/src/shaders/ShaderProgram.cpp @@ -92,6 +92,7 @@ void ShaderProgram::bindAttributes() bindAttribute(1, "textureCoords"); bindAttribute(2, "normal"); bindAttribute(3, "vertexColor"); + bindAttribute(4, "bary"); } void ShaderProgram::bindAttribute(int attribute, const char* variableName) From dfa266927aacdc8d8c05e288161a403f9b701f56 Mon Sep 17 00:00:00 2001 From: Tenzit Date: Sun, 7 Jun 2026 11:53:13 -0600 Subject: [PATCH 3/3] Add Pyramid Cave KEYDOOR by reading memory in PC version --- .../LevelSpecific/PyramidCave/KEYDOOR.cpp | 427 ++++++++++++++++++ .../LevelSpecific/PyramidCave/KEYDOOR.h | 37 ++ 2 files changed, 464 insertions(+) create mode 100644 SA2LevelEditor/src/entities/LevelSpecific/PyramidCave/KEYDOOR.cpp create mode 100644 SA2LevelEditor/src/entities/LevelSpecific/PyramidCave/KEYDOOR.h diff --git a/SA2LevelEditor/src/entities/LevelSpecific/PyramidCave/KEYDOOR.cpp b/SA2LevelEditor/src/entities/LevelSpecific/PyramidCave/KEYDOOR.cpp new file mode 100644 index 0000000..0b331a1 --- /dev/null +++ b/SA2LevelEditor/src/entities/LevelSpecific/PyramidCave/KEYDOOR.cpp @@ -0,0 +1,427 @@ +#include "KEYDOOR.h" +#include +#include +#include + +#include "../../entity.h" +#include "../../dummy.h" +#include "../../../toolbox/vector.h" +#include "../../../models/texturedmodel.h" +#include "../../../loading/objLoader.h" +#include "../../../loading/levelloader.h" +#include "../../../main/main.h" +#include "../../../collision/collisionmodel.h" +#include "../../../collision/collisionchecker.h" +#include "../../../toolbox/maths.h" + +#include + +std::list KEYDOOR::models; +CollisionModel* KEYDOOR::cmBase; + +KEYDOOR::KEYDOOR() +{ + +} + +void KEYDOOR::cleanUp() +{ + if (collideModelTransformed != nullptr) + { + CollisionChecker::deleteCollideModel(collideModelTransformed); + collideModelTransformed = nullptr; + } +} + +KEYDOOR::KEYDOOR(char data[32], bool useDefaultValues) +{ + std::memcpy(rawData, data, 32); + + ID = data[1]; + + signed short rX; + signed short rY; + signed short rZ; + + char* ptr = (char*)(&rX); + memset(ptr, data[3], 1); + memset(ptr + 1, data[2], 1); + + ptr = (char*)(&rY); + memset(ptr, data[5], 1); + memset(ptr + 1, data[4], 1); + + ptr = (char*)(&rZ); + memset(ptr, data[7], 1); + memset(ptr + 1, data[6], 1); + + rotationX = (int)rX; + rotationY = (int)rY; + rotationZ = (int)rZ; + + char* x = (char*)&position.x; + x[3] = data[8]; + x[2] = data[9]; + x[1] = data[10]; + x[0] = data[11]; + + char* y = (char*)&position.y; + y[3] = data[12]; + y[2] = data[13]; + y[1] = data[14]; + y[0] = data[15]; + + char* z = (char*)&position.z; + z[3] = data[16]; + z[2] = data[17]; + z[1] = data[18]; + z[0] = data[19]; + + char* v1 = (char*)&scaleX; + v1[3] = data[20]; + v1[2] = data[21]; + v1[1] = data[22]; + v1[0] = data[23]; + + char* v2 = (char*)&scaleY; + v2[3] = data[24]; + v2[2] = data[25]; + v2[1] = data[26]; + v2[0] = data[27]; + + char* v3 = (char*)&scaleZ; + v3[3] = data[28]; + v3[2] = data[29]; + v3[1] = data[30]; + v3[0] = data[31]; + + scaleX = scaleX + 1.0f; + scaleY = scaleX; + scaleZ = scaleX; + + if (useDefaultValues) + { + scaleX = 1.0f; + scaleY = 1.0f; + scaleZ = 1.0f; + } + + rotationX = 0; + rotationZ = 0; + + visible = true; + baseColour.set(1, 1, 1); + updateTransformationMatrixZXY(); + + collideModelOriginal = KEYDOOR::cmBase; + collideModelTransformed = KEYDOOR::cmBase->duplicateMe(); + collideModelTransformed->parent = this; + CollisionChecker::addCollideModel(collideModelTransformed); + + updateTransformationMatrixYXZ(); + updateCollisionModelYXZ(); +} + +bool KEYDOOR::isSA2Object() +{ + return true; +} + +void KEYDOOR::step() +{ + if (Global::selectedSA2Object == this) + { + baseColour.set(1.75f, 1.75f, 1.75f); + } + else + { + baseColour.set(1.0f, 1.0f, 1.0f); + } +} + +std::list* KEYDOOR::getModels() +{ + return &KEYDOOR::models; +} + +void KEYDOOR::loadStaticModels() +{ + if (KEYDOOR::models.size() > 0) + { + return; + } + +#ifdef DEV_MODE + std::fprintf(stdout, "Loading KEYDOOR static models...\n"); +#endif + loadObjModelFromPCGame(&KEYDOOR::models, (struct PCMeshObject *)0xabbbf0); + KEYDOOR::cmBase = loadCollisionModelFromPCGame((struct PCMeshObject *)0xabbbf0); +} + +void KEYDOOR::deleteStaticModels() +{ +#ifdef DEV_MODE + std::fprintf(stdout, "Deleting KEYDOOR static models...\n"); +#endif + Entity::deleteModels(&KEYDOOR::models); + Entity::deleteCollisionModel(&KEYDOOR::cmBase); +} + +void KEYDOOR::updateValue(int btnIndex) +{ + char buf[128]; + GetWindowTextA(Global::windowValues[btnIndex], buf, 128); + std::string text = buf; + + switch (btnIndex) + { + case 0: + { + try + { + //we are going to change into a new object. + int newid = std::stoi(text); + + if (newid != ID) + { + char data[32] = { 0 }; + data[1] = (char)newid; + + data[8] = *(((char*)&position.x) + 3); + data[9] = *(((char*)&position.x) + 2); + data[10] = *(((char*)&position.x) + 1); + data[11] = *(((char*)&position.x) + 0); + data[12] = *(((char*)&position.y) + 3); + data[13] = *(((char*)&position.y) + 2); + data[14] = *(((char*)&position.y) + 1); + data[15] = *(((char*)&position.y) + 0); + data[16] = *(((char*)&position.z) + 3); + data[17] = *(((char*)&position.z) + 2); + data[18] = *(((char*)&position.z) + 1); + data[19] = *(((char*)&position.z) + 0); + + SA2Object* newObject = LevelLoader::newSA2Object(Global::levelID, newid, data, true); + if (newObject != nullptr) + { + newObject->lvlLineNum = lvlLineNum; + Global::addEntity(newObject); + Global::selectedSA2Object = newObject; + newObject->updateEditorWindows(); + Global::redrawWindow = true; + cleanUp(); + Global::deleteEntity(this); + } + } + } + catch (...) {} + break; + } + + case 2: + { + try + { + float newX = std::stof(text); + position.x = newX; + SetWindowTextA(Global::windowValues[2], std::to_string(position.x).c_str()); + break; + } + catch (...) { break; } + } + + case 3: + { + try + { + float newY = std::stof(text); + position.y = newY; + SetWindowTextA(Global::windowValues[3], std::to_string(position.y).c_str()); + break; + } + catch (...) { break; } + } + + case 4: + { + try + { + float newZ = std::stof(text); + position.z = newZ; + SetWindowTextA(Global::windowValues[4], std::to_string(position.z).c_str()); + break; + } + catch (...) { break; } + } + + case 5: + { + try + { + int newRotX = std::stoi(text); + rotationX = newRotX; + SetWindowTextA(Global::windowValues[5], std::to_string(rotationX).c_str()); + break; + } + catch (...) { break; } + } + + case 6: + { + try + { + int newRotY = std::stoi(text); + rotationY = newRotY; + SetWindowTextA(Global::windowValues[6], std::to_string(rotationY).c_str()); + break; + } + catch (...) { break; } + } + + case 7: + { + try + { + int newRotZ = std::stoi(text); + rotationZ = newRotZ; + SetWindowTextA(Global::windowValues[7], std::to_string(rotationZ).c_str()); + break; + } + catch (...) { break; } + } + + case 8: + { + try + { + float newScale = std::stof(text); + scaleX = newScale; + scaleY = newScale; + scaleZ = newScale; + SetWindowTextA(Global::windowValues[8], std::to_string(newScale).c_str()); + break; + } + catch (...) { break; } + } + + //case 9: + //{ + // try + // { + // float newScale = std::stof(text); + // scaleY = newScale; + // SetWindowTextA(Global::windowValues[9], std::to_string(newScale).c_str()); + // break; + // } + // catch (...) { break; } + //} + + default: break; + } + + updateTransformationMatrixZXY(); + updateCollisionModelZXY(); + Global::redrawWindow = true; +} + +void KEYDOOR::updateEditorWindows() +{ + SetWindowTextA(Global::windowLabels[0], "ID"); + SetWindowTextA(Global::windowLabels[1], "Name"); + SetWindowTextA(Global::windowLabels[2], "Position X"); + SetWindowTextA(Global::windowLabels[3], "Position Y"); + SetWindowTextA(Global::windowLabels[4], "Position Z"); + SetWindowTextA(Global::windowLabels[5], "Rotation X"); + SetWindowTextA(Global::windowLabels[6], "Rotation Y"); + SetWindowTextA(Global::windowLabels[7], "Rotation Z"); + SetWindowTextA(Global::windowLabels[8], "Scale?"); + SetWindowTextA(Global::windowLabels[9], "Unknown"); + SetWindowTextA(Global::windowLabels[10], "Side"); + + SetWindowTextA(Global::windowValues[0], std::to_string(ID).c_str()); + SetWindowTextA(Global::windowValues[1], "KEYDOOR"); + SetWindowTextA(Global::windowValues[2], std::to_string(position.x).c_str()); + SetWindowTextA(Global::windowValues[3], std::to_string(position.y).c_str()); + SetWindowTextA(Global::windowValues[4], std::to_string(position.z).c_str()); + SetWindowTextA(Global::windowValues[5], std::to_string(rotationX).c_str()); + SetWindowTextA(Global::windowValues[6], std::to_string(rotationY).c_str()); + SetWindowTextA(Global::windowValues[7], std::to_string(rotationZ).c_str()); + SetWindowTextA(Global::windowValues[8], std::to_string(scaleX).c_str()); + SetWindowTextA(Global::windowValues[9], std::to_string(scaleY).c_str()); + + SendMessageA(Global::windowValues[0], EM_SETREADONLY, 0, 0); + SendMessageA(Global::windowValues[1], EM_SETREADONLY, 1, 0); + SendMessageA(Global::windowValues[2], EM_SETREADONLY, 0, 0); + SendMessageA(Global::windowValues[3], EM_SETREADONLY, 0, 0); + SendMessageA(Global::windowValues[4], EM_SETREADONLY, 0, 0); + SendMessageA(Global::windowValues[5], EM_SETREADONLY, 0, 0); + SendMessageA(Global::windowValues[6], EM_SETREADONLY, 0, 0); + SendMessageA(Global::windowValues[7], EM_SETREADONLY, 0, 0); + SendMessageA(Global::windowValues[8], EM_SETREADONLY, 0, 0); + SendMessageA(Global::windowValues[9], EM_SETREADONLY, 1, 0); + SendMessageA(Global::windowValues[10], EM_SETREADONLY, 1, 0); + + SetWindowTextA(Global::windowDescriptions[0], ""); + SetWindowTextA(Global::windowDescriptions[1], ""); + SetWindowTextA(Global::windowDescriptions[2], ""); + SetWindowTextA(Global::windowDescriptions[3], ""); + SetWindowTextA(Global::windowDescriptions[4], ""); + SetWindowTextA(Global::windowDescriptions[5], ""); + SetWindowTextA(Global::windowDescriptions[6], ""); + SetWindowTextA(Global::windowDescriptions[7], ""); + SetWindowTextA(Global::windowDescriptions[8], ""); + SetWindowTextA(Global::windowDescriptions[9], "Not sure"); + + updateTransformationMatrixZXY(); + updateCollisionModelZXY(); +} + +void KEYDOOR::fillData(char data[32]) +{ + data[1] = (char)ID; + + data[2] = (char)((rotationX >> 8) & 0xFF); + data[3] = (char)((rotationX >> 0) & 0xFF); + data[4] = (char)((rotationY >> 8) & 0xFF); + data[5] = (char)((rotationY >> 0) & 0xFF); + data[6] = (char)((rotationZ >> 8) & 0xFF); + data[7] = (char)((rotationZ >> 0) & 0xFF); + + char* ptr = (char*)(&position.x); + data[8] = (char)(*(ptr + 3)); + data[9] = (char)(*(ptr + 2)); + data[10] = (char)(*(ptr + 1)); + data[11] = (char)(*(ptr + 0)); + + ptr = (char*)(&position.y); + data[12] = (char)(*(ptr + 3)); + data[13] = (char)(*(ptr + 2)); + data[14] = (char)(*(ptr + 1)); + data[15] = (char)(*(ptr + 0)); + + ptr = (char*)(&position.z); + data[16] = (char)(*(ptr + 3)); + data[17] = (char)(*(ptr + 2)); + data[18] = (char)(*(ptr + 1)); + data[19] = (char)(*(ptr + 0)); + + float var1 = scaleX - 1.0f; + ptr = (char*)(&var1); + data[20] = (char)(*(ptr + 3)); + data[21] = (char)(*(ptr + 2)); + data[22] = (char)(*(ptr + 1)); + data[23] = (char)(*(ptr + 0)); + + float var2 = scaleY - 1.0f; + ptr = (char*)(&var2); + data[24] = (char)(*(ptr + 3)); + data[25] = (char)(*(ptr + 2)); + data[26] = (char)(*(ptr + 1)); + data[27] = (char)(*(ptr + 0)); + + float var3 = 0.0f; + ptr = (char*)(&var3); + data[28] = (char)(*(ptr + 3)); + data[29] = (char)(*(ptr + 2)); + data[30] = (char)(*(ptr + 1)); + data[31] = (char)(*(ptr + 0)); +} diff --git a/SA2LevelEditor/src/entities/LevelSpecific/PyramidCave/KEYDOOR.h b/SA2LevelEditor/src/entities/LevelSpecific/PyramidCave/KEYDOOR.h new file mode 100644 index 0000000..b246c95 --- /dev/null +++ b/SA2LevelEditor/src/entities/LevelSpecific/PyramidCave/KEYDOOR.h @@ -0,0 +1,37 @@ +#pragma once + +class CollisionModel; + +#include +#include +#include "../../entity.h" +#include "../../sa2object.h" + +class KEYDOOR : public SA2Object +{ +private: + static std::list models; + static CollisionModel* cmBase; +public: + KEYDOOR(); + KEYDOOR(char data[32], bool useDefaultValues); + + void step(); + + void cleanUp(); + + void updateEditorWindows(); + + void updateValue(int btnIndex); + + void fillData(char data[32]); + + bool isSA2Object(); + + std::list* getModels(); + + static void loadStaticModels(); + + static void deleteStaticModels(); +}; +