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..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; @@ -10,20 +11,29 @@ 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 { + 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/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/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(); +}; + 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/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 4ee1f52..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) @@ -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,237 @@ 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 bary; + 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); + 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] = 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; + 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; + 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++) { + 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; + } + 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; + } + } + 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; @@ -1742,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/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..6cd8d44 100644 --- a/SA2LevelEditor/src/rendering/Renderer.cpp +++ b/SA2LevelEditor/src/rendering/Renderer.cpp @@ -29,11 +29,16 @@ void EntityRenderer::renderNEW(std::unordered_map* entityList = &entry.second; - + int noTexture = entry.first->getTexture()->getIDs()->size() == 0; for (Entity* entity : (*entityList)) { prepareInstance(entity); - glDrawElements(GL_TRIANGLES, (entry.first)->getRawModel()->getVertexCount(), GL_UNSIGNED_INT, 0); + 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(); } @@ -47,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(); @@ -60,10 +68,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() @@ -72,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 f9a6f99..b51582c 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); @@ -87,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) @@ -111,6 +117,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();