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