diff --git a/README.md b/README.md index 1af95ca..349715f 100644 --- a/README.md +++ b/README.md @@ -8,4 +8,6 @@ - raylib - boost::asio - imgui -- rlimgui \ No newline at end of file +- rlimgui + +Note: different backend support is planned for Vulkan and whatnot. raylib's raymath component will still be necessary for physics maths. \ No newline at end of file diff --git a/gameenv/data/sys/collisionparams.xml b/gameenv/data/sys/collisionparams.xml new file mode 100644 index 0000000..e1923d3 --- /dev/null +++ b/gameenv/data/sys/collisionparams.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/gameenv/data/sys/cvardefs.cvars b/gameenv/data/sys/cvardefs.cvars index a6b022a..8a2f156 100644 --- a/gameenv/data/sys/cvardefs.cvars +++ b/gameenv/data/sys/cvardefs.cvars @@ -1,4 +1,5 @@ math_3d_floor_collision_work false +math_3d_collision_work false vid_2d_background false vid_3d_renderer true vid_3d_grid false diff --git a/mpfw/MPFW_Physics.cpp b/mpfw/MPFW_Physics.cpp new file mode 100644 index 0000000..c04916a --- /dev/null +++ b/mpfw/MPFW_Physics.cpp @@ -0,0 +1,150 @@ +#include "MPFW_Physics.h" +#include +#include +#include +#include + + +// simple aabb +bool __collides(MPFW::Physics::Box a, MPFW::Physics::Box b) { + return + a.min.x <= b.max.x && + a.max.x >= b.min.x && + a.min.y <= b.max.y && + a.max.y >= b.min.y && + a.min.z <= b.max.z && + a.max.z >= b.min.z; +} + + +MPFW::Physics::Box MPFW::Physics::GenerateBoundingBoxPolygon(Polygon poly) +{ + Polygon polyCopy = poly; + std::vector x; + std::vector y; + std::vector z; + + for (int i = 0; i < polyCopy.size(); i++) { + x.push_back(polyCopy[i].x); + y.push_back(polyCopy[i].y); + z.push_back(polyCopy[i].z); + } + + std::sort(x.begin(), x.end()); + std::sort(y.begin(), y.end()); + std::sort(z.begin(), z.end()); + + Box retval; + retval.min.x = x[0]; + retval.min.y = y[0]; + retval.min.z = z[0]; + + retval.max.x = x[x.size()-1]; + retval.max.y = y[y.size()-1]; + retval.max.z = z[z.size()-1]; + return retval; +} + +std::mutex collSetMutex; + +void PolygonCalcThread(MPFW::Physics::CollisionSet* collSet, std::vector pStructs, int v) { + for (int p = 0; p < pStructs.size(); p++) { + if (collSet->collisionFaces.size() - 1 != p) { + collSet->collisionFaces = std::vector(0); + p = 0; + } + if (__collides(MPFW::Physics::GenerateBoundingBoxPolygon(pStructs[p].polygon), collSet->size)) { + std::lock_guard guard(collSetMutex); + collSet->collisionFaces.push_back(pStructs[p]); + } + std::print("v:{},p:{}\n", v, p); + } +} + +MPFW::Physics::CollisionMap MPFW::Physics::GenerateCollisionMap(std::vector polygons, std::vector totalVertices, CollisionMapGenParams cmgp) +{ + std::vector polygonsCopy = polygons; + std::vector polygonCalculationsThreads; + std::vector pStructs(polygons.size()); + + for (int cFCount = 0; cFCount < polygons.size(); cFCount++) { + CollisionFace cface; + cface.polygon = polygonsCopy[cFCount]; // assuming polygonsCopy == polygons, because it should be. + + + + // calculating normals (if polygon has 3 vertices or more) + if(cface.polygon.size() >= 3){ + + // according to: https://titan.csit.rmit.edu.au/~e20068/teaching/i3dg&a/2012/normals/normals.xhtml + + Vector3 a, b; + + a = Vector3Subtract(cface.polygon[1], cface.polygon[0]); + + b = Vector3Subtract(cface.polygon[2], cface.polygon[0]); + cface.normal = Vector3Normalize(Vector3CrossProduct(a,b)); // does that actually work? I can't believe it would + cface.normalCalculated = true; + + } + + + pStructs[cFCount] = cface; + + } + + Box mapBoundingBox = GenerateBoundingBoxPolygon(totalVertices); + + Vector3 sizeOfMap = mapBoundingBox.max - mapBoundingBox.min; + Vector3 sizeOfVoxel = sizeOfMap / cmgp.voxelLateralCount; + + CollisionMap retval; + int vCount = 0; + for (int z = 0; z < cmgp.voxelLateralCount; z++) { + for (int y = 0; y < cmgp.voxelLateralCount; y++) { + for (int x = 0; x < cmgp.voxelLateralCount; x++) { + CollisionSet cs; + + cs.size.min = + { + mapBoundingBox.min.x + sizeOfVoxel.x * x, + mapBoundingBox.min.y + sizeOfVoxel.y * y, + mapBoundingBox.min.z + sizeOfVoxel.z * z + }; + + cs.size.max = + { + mapBoundingBox.min.x + sizeOfVoxel.x * (x + 1), + mapBoundingBox.min.y + sizeOfVoxel.y * (y + 1), + mapBoundingBox.min.z + sizeOfVoxel.z * (z + 1) + }; + cs.collisionFaces = std::vector(0); + retval.collisionSets.push_back(cs); + // polygonCalculationsThreads.push_back(std::thread(PolygonCalcThread, &retval.collisionSets[vCount], pStructs, vCount)); + + for(int p = 0; p < pStructs.size(); p++){ + if (__collides(MPFW::Physics::GenerateBoundingBoxPolygon(pStructs[p].polygon), retval.collisionSets[vCount].size)) { + // std::lock_guard guard(collSetMutex); + retval.collisionSets[vCount].collisionFaces.push_back(pStructs[p]); + } + std::print("v:{},p:{}\n", vCount, p); + } + vCount++; + + + } + } + } + + + /*for (int i = 0; i < polygonCalculationsThreads.size(); i++) { + if(polygonCalculationsThreads[i].joinable())polygonCalculationsThreads[i].join(); + }*/ + + + return retval; + + + + +} diff --git a/mpfw/MPFW_Physics.h b/mpfw/MPFW_Physics.h new file mode 100644 index 0000000..4dcf5ba --- /dev/null +++ b/mpfw/MPFW_Physics.h @@ -0,0 +1,34 @@ +#pragma once +#include +#include +#include + +namespace MPFW { + namespace Physics { + struct Box { + Vector3 min, max; + }; + using Polygon = std::vector; + struct CollisionFace { + Polygon polygon; + Vector3 normal; + bool normalCalculated = false; + }; + + struct CollisionSet { + Box size; + std::vector collisionFaces; + }; + + struct CollisionMapGenParams { + int voxelLateralCount = 3; + }; + + // collision maps subdivide the maps for collisions + struct CollisionMap { + std::vector collisionSets; + }; + Box GenerateBoundingBoxPolygon(Polygon poly); + CollisionMap GenerateCollisionMap(std::vector polygons, std::vector totalVertices, CollisionMapGenParams cmgp); + } +} \ No newline at end of file diff --git a/mpfw/MPFW_Quake.cpp b/mpfw/MPFW_Quake.cpp index ae803ba..cf8fbb9 100644 --- a/mpfw/MPFW_Quake.cpp +++ b/mpfw/MPFW_Quake.cpp @@ -22,9 +22,9 @@ void MPFW::Quake::Maps::MapFile::LoadBSPMap(std::string path) sstr << fileHandle.rdbuf(); // as fast as possible buffer = sstr.str(); // 3 LINES [WR SPEEDRUN] - // operating on files as a string or a char array is simpler - // than writing some fancy << fuckery (even though it is nice - // to see some << fuckery work without any moving parts) + // operating on files as a string or a char array is simpler + // than writing some fancy << fuckery (even though it is nice + // to see some << fuckery work without any moving parts) } else { @@ -35,15 +35,15 @@ void MPFW::Quake::Maps::MapFile::LoadBSPMap(std::string path) } fileHandle.close(); // close it as soon as we can get file loaded - // as to not disturb other programs + // as to not disturb other programs - - // we can now start the parsing - // hehehe >:3 + +// we can now start the parsing +// hehehe >:3 - // if file is smaller than header +// if file is smaller than header if (buffer.size() < 124) { std::print("Bad file content size.\n"); throw std::runtime_error("File is smaller than header. Can't parse."); @@ -58,7 +58,7 @@ void MPFW::Quake::Maps::MapFile::LoadBSPMap(std::string path) data.header.version += buffer[3] << 24; - + std::vector vecdir; @@ -67,7 +67,7 @@ void MPFW::Quake::Maps::MapFile::LoadBSPMap(std::string path) for (int i = 4; i < 123; i += 8) { - DirectoryEntry nd{0,0}; // new directory; + DirectoryEntry nd{ 0,0 }; // new directory; nd.offset = Utils::Strings::StringIndexToInteger_4b_le(buffer, i); nd.size = Utils::Strings::StringIndexToInteger_4b_le(buffer, i + 4); @@ -105,20 +105,20 @@ void MPFW::Quake::Maps::MapFile::LoadBSPMap(std::string path) - // Loading Models - // we're loading models first because that's - // what the spec does <3 + // Loading Models + // we're loading models first because that's + // what the spec does <3 ds = Debug::MODELS; #ifdef MPFW_QUAKE_SLOW_LOADING data.models.resize(data.header.models.size / 64); - + modelsCDBG = 0; for (int i = 0; i < data.header.models.size / 64; i++) { int base = data.header.models.offset + 64 * i; - - + + qModel qm; @@ -150,7 +150,7 @@ void MPFW::Quake::Maps::MapFile::LoadBSPMap(std::string path) // std::print("Model {}/{}\n", i, data.header.models.size / 64); - + } #else std::string modelsStr = Utils::Strings::IterativeStringExcerpt(buffer, data.header.models.offset, data.header.models.size); @@ -199,7 +199,7 @@ void MPFW::Quake::Maps::MapFile::LoadBSPMap(std::string path) int base = data.header.edges.offset + (4 * i); Edge e; e.vertex0 = Utils::Strings::StringIndexToInteger_2b_le(buffer, base); - e.vertex1 = Utils::Strings::StringIndexToInteger_2b_le(buffer, base+2); + e.vertex1 = Utils::Strings::StringIndexToInteger_2b_le(buffer, base + 2); data.edges[i] = e; edgesCDBG++; } @@ -258,12 +258,12 @@ void MPFW::Quake::Maps::MapFile::LoadBSPMap(std::string path) #endif - + // parsing faces ds = Debug::FACES; - + #ifdef MPFW_QUAKE_SLOW_LOADING @@ -276,9 +276,9 @@ void MPFW::Quake::Maps::MapFile::LoadBSPMap(std::string path) f.side = Utils::Strings::StringIndexToInteger_2b_le(buffer, base + 2); f.ledgeId = (signed long)(Utils::Strings::StringIndexToInteger_4b_le(buffer, base + 4)); - + f.ledgeNum = Utils::Strings::StringIndexToInteger_2b_le(buffer, base + 8); - + f.texinfoId = Utils::Strings::StringIndexToInteger_2b_le(buffer, base + 10); @@ -324,19 +324,19 @@ void MPFW::Quake::Maps::MapFile::LoadBSPMap(std::string path) std::vector texOffsets; // std::memcpy(texOffsets.data(), Utils::Strings::IterativeStringExcerpt(buffer, data.header.miptex.offset + 4, data.header.miptex.size - 4).data(), data.header.miptex.size - 4); for (int i = 0; i < cmh.numtex; i++) { - texOffsets.push_back(Utils::Strings::StringIndexToInteger_4b_le(buffer, data.header.miptex.offset + 4 + 4*i)); + texOffsets.push_back(Utils::Strings::StringIndexToInteger_4b_le(buffer, data.header.miptex.offset + 4 + 4 * i)); } for (int i = 0; i < cmh.numtex; i++) { unsigned int base = static_cast(texOffsets[i]) + (unsigned)data.header.miptex.offset; - // this shit is ¤ undocumented! ¤ - // fuck the unofficial quake specs + // this shit is ¤ undocumented! ¤ + // fuck the unofficial quake specs if (texOffsets[i] == -1) { - // is a texoffset of 0xFFFFFFFF a reference to the fact that - // it starts right after the mipheader? probably not, but it - // makes the texture loading code happy so... :shrug: + // is a texoffset of 0xFFFFFFFF a reference to the fact that + // it starts right after the mipheader? probably not, but it + // makes the texture loading code happy so... :shrug: base = (unsigned)data.header.miptex.offset + 4 + texOffsets.size() * 4; } @@ -368,7 +368,7 @@ void MPFW::Quake::Maps::MapFile::LoadBSPMap(std::string path) UnloadImage(img); data.textures.push_back(mt); - + } data.renderFaces.clear(); @@ -389,6 +389,8 @@ void MPFW::Quake::Maps::MapFile::LoadBSPMap(std::string path) data.header.planes.size ); + std::vector collPolygons; + std::vector totalRlVec; for (int i = 0; i < data.faces.size(); i++) { CalculatedFace cface; @@ -419,6 +421,7 @@ void MPFW::Quake::Maps::MapFile::LoadBSPMap(std::string path) ] )); } + totalRlVec.push_back(cface.vertices[cface.vertices.size() - 1]); // push back the last vertex for collision } @@ -427,9 +430,9 @@ void MPFW::Quake::Maps::MapFile::LoadBSPMap(std::string path) cface.glTextureWidth = data.textures[data.texInfo[data.faces[i].texinfoId].textureId].width; cface.glTextureHeight = data.textures[data.texInfo[data.faces[i].texinfoId].textureId].height; - + collPolygons.push_back(cface.vertices); for (int j = 0; j < cface.vertices.size(); j++) { - + cface.texCoords.push_back(Vector2( (Vector3DotProduct(MPFW::Quake::Maps::RLVec2qVec(cface.vertices[j]), data.texInfo[data.faces[i].texinfoId].vectorS) + data.texInfo[data.faces[i].texinfoId].distS) / data.textures[data.texInfo[data.faces[i].texinfoId].textureId].width, (Vector3DotProduct(MPFW::Quake::Maps::RLVec2qVec(cface.vertices[j]), data.texInfo[data.faces[i].texinfoId].vectorT) + data.texInfo[data.faces[i].texinfoId].distT) / data.textures[data.texInfo[data.faces[i].texinfoId].textureId].height @@ -464,7 +467,7 @@ void MPFW::Quake::Maps::MapFile::LoadBSPMap(std::string path) planeType == 0 || planeType == 3 ? 0 : planeType == 1 || planeType == 4 ? 1 : 2; - + int lmw, lmh; switch (valueForgotten) { @@ -494,7 +497,7 @@ void MPFW::Quake::Maps::MapFile::LoadBSPMap(std::string path) cface.hasLightMap = true; for (int y = 0; y < lmh; y++) { for (int x = 0; x < lmw; x++) { - unsigned char lightValue = data.lightmap[y*lmw+x + lmbase]; + unsigned char lightValue = data.lightmap[y * lmw + x + lmbase]; unsigned char invertedLightValue = 255 - lightValue; // std::cout << "light value : " << (int)invertedLightValue << "\n"; ImageDrawPixel(&lightmap, x, y, { @@ -502,7 +505,7 @@ void MPFW::Quake::Maps::MapFile::LoadBSPMap(std::string path) 0, 0, invertedLightValue - }); + }); } } @@ -526,7 +529,13 @@ void MPFW::Quake::Maps::MapFile::LoadBSPMap(std::string path) } + + + Physics::CollisionMapGenParams cmgp; + *collMap = Physics::GenerateCollisionMap(collPolygons, totalRlVec, cmgp); + + diff --git a/mpfw/MPFW_Quake.h b/mpfw/MPFW_Quake.h index f1a3568..241acbe 100644 --- a/mpfw/MPFW_Quake.h +++ b/mpfw/MPFW_Quake.h @@ -6,6 +6,7 @@ #include #include #include +#include "MPFW_Physics.h" namespace MPFW { namespace Quake { @@ -195,7 +196,7 @@ namespace MPFW { Debug::DebugState ds = Debug::NONE; MapData data; Palette* pal; - + Physics::CollisionMap* collMap; int modelsCDBG = 0; int verticesCDBG = 0; int edgesCDBG = 0; diff --git a/mpfw/main.cpp b/mpfw/main.cpp index 26ce394..bd02b2a 100644 --- a/mpfw/main.cpp +++ b/mpfw/main.cpp @@ -13,6 +13,7 @@ #include "MPFW_UI.h" #include #include +#include "MPFW_Physics.h" // turn quake miptex into rl texture Texture2D RLMT_QUAKE(MPFW::Quake::Maps::rlMipTex rlmt) { @@ -185,6 +186,13 @@ int main() { map.pal = new MPFW::Quake::Maps::Palette("data/palette.lmp"); + + MPFW::Physics::CollisionMap collisionMap; // map of the collisions + + map.collMap = &collisionMap; + + + SetConfigFlags(FLAG_WINDOW_RESIZABLE); InitWindow(1280, 720, TextFormat("mpfw")); @@ -193,6 +201,8 @@ int main() { DisableCursor(); + + MPFW::UI::UIRenderer uiRenderer; MPFW::Console::CommandHandlerResources chr; chr.mapQuake = ↦ @@ -292,12 +302,35 @@ int main() { velocity.y -= 10 * GetFrameTime(); } + if(chr.cvars["math_3d_floor_collision_work"] == "true"){ + for (int b = 0; b < collisionMap.collisionSets.size(); b++) { + BoundingBox bb; + bb.min = collisionMap.collisionSets[b].size.min; + bb.max = collisionMap.collisionSets[b].size.max; + if (CheckCollisionBoxSphere(bb, camera.position, 10)) { + MPFW::Physics::CollisionSet cs = collisionMap.collisionSets[b]; + for (int i = 0; i < cs.collisionFaces.size(); i++) { + for (int t = 2; t < cs.collisionFaces[i].polygon.size(); t++) { + Vector3 a = cs.collisionFaces[i].polygon[0]; + Vector3 b = cs.collisionFaces[i].polygon[t]; + Vector3 c = cs.collisionFaces[i].polygon[t - 1]; + Ray r; + r.position = camera.position; + r.direction = { 0,-1,0 }; + RayCollision coll = GetRayCollisionTriangle(r, a, b, c); + } + } + } + + } + } + velocity *= {GetFrameTime(), 1, GetFrameTime()}; camera.position += velocity; camera.target = camera.position + rotation; - + } @@ -389,6 +422,12 @@ int main() { } + } + for (int i = 0; i < collisionMap.collisionSets.size(); i++) { + BoundingBox b; b.min = collisionMap.collisionSets[i].size.min; + b.max = collisionMap.collisionSets[i].size.max; + + DrawBoundingBox(b, RED); } EndMode3D(); } diff --git a/mpfw/mpfw.vcxproj b/mpfw/mpfw.vcxproj index fbdcbeb..648a78c 100644 --- a/mpfw/mpfw.vcxproj +++ b/mpfw/mpfw.vcxproj @@ -149,6 +149,7 @@ + @@ -160,6 +161,7 @@ + diff --git a/mpfw/mpfw.vcxproj.filters b/mpfw/mpfw.vcxproj.filters index 4e17a98..532c02a 100644 --- a/mpfw/mpfw.vcxproj.filters +++ b/mpfw/mpfw.vcxproj.filters @@ -66,6 +66,9 @@ Backends + + Source Files + @@ -95,5 +98,8 @@ Backends + + Header Files + \ No newline at end of file