Browse Source

progress for a very secret project

vlod 7 months ago
parent
commit
acf11075e6

+ 7 - 2
Pika/core/pikaRuntime/containerManager/containerManager.cpp

@@ -533,17 +533,18 @@ void pika::ContainerManager::update(pika::LoadedDll &loadedDll, pika::PikaWindow
 				}
 				
 
-				auto s = ImGui::GetContentRegionMax();
+				//auto s = ImGui::GetContentRegionMax();
 
 				//todo try set borders here at 0,0, easiest thing to do probably
 				ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.f);
 				//ImGui::Image((void *)c.second.requestedContainerInfo.requestedFBO.texture, s, {0, 1}, {1, 0},
-				//	{1,1,1,1}, {0,0,0,0});
+				//	{1,1,1,1}, {0,0,0,0}
 
 				ImVec2 pos = ImGui::GetCursorScreenPos();
 				ImVec2 maxPos = {ImGui::GetWindowPos().x + ImGui::GetWindowSize().x,
 					ImGui::GetWindowPos().y + ImGui::GetWindowSize().y};
 				unsigned texId = c.second.requestedContainerInfo.requestedFBO.texture;
+				maxPos.y--;
 
 				ImGui::GetWindowDrawList()->AddImage(
 					(void *)texId,
@@ -552,6 +553,10 @@ void pika::ContainerManager::update(pika::LoadedDll &loadedDll, pika::PikaWindow
 					ImVec2(0, 1), ImVec2(1, 0)
 				);
 
+				//todo test
+				ImVec2 s = {maxPos.x - pos.x, maxPos.y - pos.y};
+
+
 				ImGui::PopStyleVar();
 
 				ImGui::End();

+ 1 - 1
Pika/gameplay/containers/isometricGame/isometricGame.cpp

@@ -966,7 +966,7 @@ void IsometricGame::saveData(RequestedContainerInfo &requestedInfo)
 		);
 	}
 
-	auto fileData = data.formatIntoFileData();
+	auto fileData = data.formatIntoFileDataBinary();
 
 	requestedInfo.writeEntireFileBinary(PIKA_RESOURCES_PATH "iso/save.bin", fileData.data(), fileData.size());
 

+ 183 - 0
Pika/gameplay/containers/milk/milk.cpp

@@ -0,0 +1,183 @@
+#include "milk.h"
+
+
+float vertexDataOriginal[] = {
+	//front
+	0.5, 0.5, 0.5,
+	-0.5, 0.5, 0.5,
+	-0.5, -0.5, 0.5,
+	-0.5, -0.5, 0.5,
+	0.5, -0.5, 0.5,
+	0.5, 0.5, 0.5,
+
+	//back
+	-0.5, -0.5, -0.5,
+	-0.5, 0.5, -0.5,
+	0.5, 0.5, -0.5,
+	0.5, 0.5, -0.5,
+	0.5, -0.5, -0.5,
+	-0.5, -0.5, -0.5,
+
+	//top
+	-0.5, 0.5, -0.5,
+	-0.5, 0.5, 0.5,
+	0.5, 0.5, 0.5,
+	0.5, 0.5, 0.5,
+	0.5, 0.5, -0.5,
+	-0.5, 0.5, -0.5,
+
+	//bottom
+	0.5, -0.5, 0.5,
+	-0.5, -0.5, 0.5,
+	-0.5, -0.5, -0.5,
+	-0.5, -0.5, -0.5,
+	0.5, -0.5, -0.5,
+	0.5, -0.5, 0.5,
+
+	//left
+	-0.5, -0.5, 0.5,
+	-0.5, 0.5, 0.5,
+	-0.5, 0.5, -0.5,
+	-0.5, 0.5, -0.5,
+	-0.5, -0.5, -0.5,
+	-0.5, -0.5, 0.5,
+
+	//right
+	0.5, 0.5, -0.5,
+	0.5, 0.5, 0.5,
+	0.5, -0.5, 0.5,
+	0.5, -0.5, 0.5,
+	0.5, -0.5, -0.5,
+	0.5, 0.5, -0.5,
+};
+
+float vertexUVOriginal[] = {
+	//front
+	1, 1,
+	0, 1,
+	0, 0,
+	0, 0,
+	1, 0,
+	1, 1,
+
+	//back
+	0, 0,
+	0, 1,
+	1, 1,
+	1, 1,
+	1, 0,
+	0, 0,
+
+	//top
+	0, 0,
+	0, 1,
+	1, 1,
+	1, 1,
+	1, 0,
+	0, 0,
+
+	//bottom
+	1, 1,
+	0, 1,
+	0, 0,
+	0, 0,
+	1, 0,
+	1, 1,
+
+	//left
+	1, 0,
+	1, 1,
+	0, 1,
+	0, 1,
+	0, 0,
+	1, 0,
+
+	//right
+	0, 1,
+	1, 1,
+	1, 0,
+	1, 0,
+	0, 0,
+	0, 1,
+};
+
+gl3d::Model Milk::createCubeModel(glm::vec3 size)
+{
+
+	std::vector<float> data;
+	data.reserve(2000);
+	for (int face = 0; face < 6; face++)
+	{
+
+
+		glm::vec3 normal = {};
+		glm::vec3 positions[3] = {};
+
+		for (int i = 0; i < 3; i++)
+		{
+			positions[i].x = vertexDataOriginal[i * 3 + face * 3 * 6 + 0];
+			positions[i].y = vertexDataOriginal[i * 3 + face * 3 * 6 + 1];
+			positions[i].z = vertexDataOriginal[i * 3 + face * 3 * 6 + 2];
+		}
+
+		glm::vec3 edge1 = positions[1] - positions[0];
+		glm::vec3 edge2 = positions[2] - positions[0];
+		normal = glm::normalize(glm::cross(edge1, edge2));
+
+		for (int i = 0; i < 6; i++)
+		{
+
+			data.push_back(vertexDataOriginal[i * 3 + face * 3 * 6 + 0] * size.x);
+			data.push_back(vertexDataOriginal[i * 3 + face * 3 * 6 + 1] * size.y);
+			data.push_back(vertexDataOriginal[i * 3 + face * 3 * 6 + 2] * size.z);
+
+			data.push_back(normal.x);
+			data.push_back(normal.y);
+			data.push_back(normal.z);
+
+			if (face == 0)
+			{
+				data.push_back(vertexUVOriginal[i * 2 + face * 2 * 6 + 0] * size.x * 0.25);
+				data.push_back(vertexUVOriginal[i * 2 + face * 2 * 6 + 1] * size.y * 0.25);
+			}
+			else if (face == 1)
+			{
+				data.push_back(vertexUVOriginal[i * 2 + face * 2 * 6 + 0] * size.x * 0.25);
+				data.push_back(vertexUVOriginal[i * 2 + face * 2 * 6 + 1] * size.y * 0.25);
+			}
+			else if (face == 2)
+			{
+				data.push_back(vertexUVOriginal[i * 2 + face * 2 * 6 + 0] * size.x * 0.25);
+				data.push_back(vertexUVOriginal[i * 2 + face * 2 * 6 + 1] * size.z * 0.25);
+			}
+			else if (face == 3)
+			{
+				data.push_back(vertexUVOriginal[i * 2 + face * 2 * 6 + 0] * size.x * 0.25);
+				data.push_back(vertexUVOriginal[i * 2 + face * 2 * 6 + 1] * size.z * 0.25);
+			}
+			else if (face == 4)
+			{
+				data.push_back(vertexUVOriginal[i * 2 + face * 2 * 6 + 0] * size.z * 0.25);
+				data.push_back(vertexUVOriginal[i * 2 + face * 2 * 6 + 1] * size.y * 0.25);
+			}
+			else if (face == 5)
+			{
+				data.push_back(vertexUVOriginal[i * 2 + face * 2 * 6 + 0] * size.z * 0.25);
+				data.push_back(vertexUVOriginal[i * 2 + face * 2 * 6 + 1] * size.y * 0.25);
+			}
+
+
+		}
+	}
+
+	/*
+	position					vec3
+	normals						vec3
+	(optional) texcoords		vec2
+	(optional) joints id		ivec4
+	(optional) joints weights	vec4
+	*/
+
+	return renderer.createModelFromData(floorMaterial, "cube", data.size(), data.data());
+	
+}

+ 293 - 8
Pika/gameplay/containers/milk/milk.h

@@ -8,6 +8,8 @@
 #include <pikaSizes.h>
 #include <imgui_spinner.h>
 #include <engineLibraresSupport/engineGL3DSupport.h>
+#include <safeSave/safeSave.h>
+#include "physics.h"
 
 
 struct Milk: public Container
@@ -31,6 +33,118 @@ struct Milk: public Container
 	gl3d::Model helmetModel;
 	gl3d::Entity helmetEntity;
 	pika::gl3d::General3DEditor editor;
+	gl3d::Material floorMaterial;
+
+	Simulator simulator;
+
+	struct Cube
+	{
+		glm::vec3 size = {1,1,1};
+		gl3d::Model model;
+		gl3d::Entity entity;
+		int physicsID = 0;
+	};
+
+	std::vector<Cube> cubes;
+
+	std::vector<Cube> physicsCubes;
+
+
+	gl3d::Model createCubeModel(glm::vec3 size);
+
+
+	void addNewCube(glm::vec3 size, glm::vec3 position, glm::vec3 rotation = {})
+	{
+		Cube c;
+
+		gl3d::Transform t;
+		t.position = position;
+		t.rotation = rotation;
+
+		c.size = size;
+		c.model = createCubeModel(size);
+		c.entity = renderer.createEntity(c.model, t);
+		
+
+		auto box = createBox(position, size);
+		box.mass = INFINITY;
+
+		int id = simulator.getIdAndIncrement();
+
+		simulator.bodies[id] = box;
+		c.physicsID = id;
+
+
+		cubes.push_back(c);
+
+	};
+
+	void addNewCubePhysics(glm::vec3 size, glm::vec3 position, glm::vec3 rotation = {})
+	{
+		Cube c;
+
+		gl3d::Transform t;
+		t.position = position;
+		t.rotation = rotation;
+
+		c.size = size;
+		c.model = createCubeModel(size);
+		c.entity = renderer.createEntity(c.model, t);
+
+
+		auto box = createBox(position, size);
+
+		int id = simulator.getIdAndIncrement();
+
+		simulator.bodies[id] = box;
+		c.physicsID = id;
+
+
+		physicsCubes.push_back(c);
+
+	};
+
+	void modifyCube(int index, glm::vec3 size, bool force = 0)
+	{
+
+		auto &c = cubes[index];
+
+		if (c.size != size || force)
+		{
+			auto transform = renderer.getEntityTransform(c.entity);
+			renderer.deleteEntity(c.entity);
+			renderer.deleteModel(c.model);
+			
+			c.size = size;
+			c.model = createCubeModel(size);
+			c.entity = renderer.createEntity(c.model, transform);
+
+			auto found = simulator.bodies.find(c.physicsID);
+			if (found != simulator.bodies.end()) 
+			{
+				found->second.shape = size;
+			}
+			
+		}
+
+
+	};
+
+	void deleteCube(int index)
+	{
+		if (index < 0 || index >= cubes.size()) { return; }
+
+		auto &c = cubes[index];
+
+		auto found = simulator.bodies.find(c.physicsID);
+		if (found != simulator.bodies.end()) { simulator.bodies.erase(found); }
+		
+		renderer.deleteEntity(c.entity);
+		renderer.deleteModel(c.model);
+
+		cubes.erase(cubes.begin() + index);
+
+	}
 
 	bool create(RequestedContainerInfo &requestedInfo, pika::StaticString<256> commandLineArgument)
 	{
@@ -68,12 +182,13 @@ struct Milk: public Container
 
 		renderer.skyBox = renderer.loadHDRSkyBox(PIKA_RESOURCES_PATH "/skyBoxes/canary_wharf_2k.hdr");
 		renderer.skyBox.color = {0.4,0.4,0.4};
+		floorMaterial = renderer.loadMaterial(PIKA_RESOURCES_PATH "milk/floor/floor.mtl", gl3d::TextureLoadQuality::maxQuality)[0];
 
 
 		helmetModel = renderer.loadModel(PIKA_RESOURCES_PATH "helmet/helmet.obj", gl3d::TextureLoadQuality::maxQuality, 1.f);
 		//helmetModel = renderer.loadModel(PIKA_RESOURCES_PATH "/knight/uploads_files_1950170_Solus_the_knight.gltf", 1.f);
 
-		
+
 		renderer.createDirectionalLight({-1,-0.5,-0.2});
 
 
@@ -82,29 +197,41 @@ struct Milk: public Container
 		t.rotation = {1.5, 0 , 0};
 
 		helmetEntity = renderer.createEntity(helmetModel, t);
+		renderer.setEntityMeshMaterial(helmetEntity, 0, floorMaterial);
+
+		addNewCube({2,1,2}, {0,-1,0});
+
 
 		return true;
 	}
 
+	int selectedCube = -1;
+
 	bool update(pika::Input input, pika::WindowState windowState,
 		RequestedContainerInfo &requestedInfo)
 	{
-		
-		//editor.update(requestedInfo.requestedImguiIds, renderer, input, 5, requestedInfo, {windowState.windowW,windowState.windowH});
-
-
+	
+	#pragma region frame start stuff
 		renderer.setErrorCallback(&errorCallbackCustom, &requestedInfo);
 		renderer.fileOpener.userData = &requestedInfo;
 		renderer.fileOpener.readEntireFileBinaryCallback = readEntireFileBinaryCustom;
 		renderer.fileOpener.readEntireFileCallback = readEntireFileCustom;
 		renderer.fileOpener.fileExistsCallback = defaultFileExistsCustom;
-
-
+		
 		glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
 		glEnable(GL_DEPTH_TEST);
 
 		renderer.updateWindowMetrics(windowState.windowW, windowState.windowH);
 		renderer.camera.aspectRatio = (float)windowState.windowW / windowState.windowH; //todo do this in update
+
+		
+		editor.update(requestedInfo.requestedImguiIds, renderer, input, 5, requestedInfo, {windowState.windowW,windowState.windowH});
+
+		if (input.buttons[pika::Button::Escape].released())
+		{
+			::pika::pikaImgui::removeFocusToCurrentWindow();
+		}
+
 		
 		{
 			static glm::dvec2 lastMousePos = {};
@@ -115,7 +242,7 @@ struct Milk: public Container
 				float speed = 0.8f;
 
 				glm::vec2 delta = lastMousePos - currentMousePos;
-				delta *= speed * input.deltaTime;
+				delta *= speed * 0.01;
 
 				renderer.camera.rotateCamera(delta);
 
@@ -126,8 +253,155 @@ struct Milk: public Container
 				lastMousePos = {input.mouseX, input.mouseY};
 			}
 		}
+	#pragma endregion
+
+		ImGui::PushID(requestedInfo.requestedImguiIds);
+		if (ImGui::Begin("General3DEditor"))
+		{
+			ImGui::Separator();
+			ImGui::Text("Level data");
+			if (ImGui::Button("Save"))
+			{
+				sfs::SafeSafeKeyValueData rez;
+
+				for (int i = 0; i < cubes.size(); i++)
+				{
+					sfs::SafeSafeKeyValueData cube;
+					
+					glm::vec3 size = cubes[i].size;
+					auto transform = renderer.getEntityTransform(cubes[i].entity);
+					glm::vec3 position = transform.position;
+					glm::vec3 rotation = transform.rotation;
+
+					cube.setVec3("size", size.x, size.y, size.z);
+					cube.setVec3("position", position.x, position.y, position.z);
+					cube.setVec3("rotation", rotation.x, rotation.y, rotation.z);
+
+					rez.setKeyValueData(std::to_string(i), cube);
+				}
+
+				auto finalData = rez.formatIntoFileDataBinary();
+
+				requestedInfo.writeEntireFileBinary(PIKA_RESOURCES_PATH "milk/level.bin",
+					finalData.data(), finalData.size());
+			}
+			
+			ImGui::SameLine();
+
+			if (ImGui::Button("Load"))
+			{
+				sfs::SafeSafeKeyValueData loadedData;
+				std::vector<char> binaryData;
+
+				requestedInfo.readEntireFileBinary(PIKA_RESOURCES_PATH "milk/level.bin", binaryData);
+
+				loadedData.loadFromFileData(binaryData.data(), binaryData.size());
+
+				while (cubes.size())
+				{
+					deleteCube(cubes.size() - 1);
+				}
+
+				for (auto &e : loadedData.entries)
+				{
+					sfs::SafeSafeKeyValueData cube;
+
+					if (loadedData.getKeyValueData(e.first, cube) == sfs::noError)
+					{
+						glm::vec3 size = {};
+						glm::vec3 position = {};
+						glm::vec3 rotation = {};
+
+						cube.getVec3("size", size.x, size.y, size.z);
+						cube.getVec3("position", position.x, position.y, position.z);
+						cube.getVec3("rotation", rotation.x, rotation.y, rotation.z);
+
+						addNewCube(size, position, rotation);
+					}
+
+				}
+
+			}
+
+			ImGui::Separator();
+
+			if (ImGui::Button("Recreate"))
+			{
+				for (int i = 0; i < cubes.size(); i++)
+				{
+					modifyCube(i, cubes[i].size, true);
+				}
+			}
+
+			if (ImGui::Button("New"))
+			{
+				addNewCube({1,1,1}, {0,0,0});
+				selectedCube = cubes.size() - 1;
+			}
+
+			if (ImGui::Button("New Physics"))
+			{
+				addNewCubePhysics({1,1,1}, {0,10,0});
+			}
+
+			if (cubes.size())
+			{
+				ImGui::SliderInt("Select Cube:", &selectedCube, -1, cubes.size() - 1);
+
+				if (selectedCube >= 0)
+				{
+					auto s = cubes[selectedCube].size;
+
+					auto transform = renderer.getEntityTransform(cubes[selectedCube].entity);
+
+					ImGui::DragFloat3("Size", &s[0], 0.1);
+					ImGui::DragFloat3("Position", &transform.position[0], 0.1);
+					ImGui::DragFloat3("Rotation", &transform.rotation[0], 0.1);
+
+					renderer.setEntityTransform(cubes[selectedCube].entity, transform);
+
+					modifyCube(selectedCube, s);
+
+					auto found = simulator.bodies.find(cubes[selectedCube].physicsID);
+					if (found != simulator.bodies.end())
+					{
+						found->second.position = transform.position;
+					}
+
+					if (ImGui::Button("Delte"))
+					{
+						deleteCube(selectedCube);
+					}
+				}
+
+			}
+			else
+			{
+				selectedCube = -1;
+			}
+
+
+
+		}
+		ImGui::End();
+		ImGui::PopID();
 		
 
+		simulator.update(input.deltaTime);
+
+
+		for (auto &c : physicsCubes)
+		{
+			auto found = simulator.bodies.find(c.physicsID);
+			if (found != simulator.bodies.end())
+			{
+				auto transform = renderer.getEntityTransform(c.entity);
+				transform.position = found->second.position;
+				renderer.setEntityTransform(c.entity, transform);
+			}
+		}
+
+
 		renderer.render(input.deltaTime);
 
 
@@ -149,6 +423,17 @@ struct Milk: public Container
 		return true;
 	}
 
+
+	void destruct(RequestedContainerInfo &requestedInfo)
+	{
+
+		this->renderer.skyBox.clearTextures();
+		this->renderer.colorCorrectionTexture().clear();
+		this->renderer.clearAllRendererResources();
+
+
+	}
+
 };
 
 //todo flag to clear screen from engine

+ 893 - 0
Pika/gameplay/containers/milk/physics.cpp

@@ -0,0 +1,893 @@
+#include "physics.h"
+
+PhysicsObject createBall(glm::vec3 pos, float r)
+{
+	PhysicsObject ret;
+	ret.type = TYPE_CIRCLE;
+
+	ret.position = pos;
+	ret.shape.x = r;
+	ret.mass = 4.f * 3.141592 * r * r * r / 3.f;
+
+	return ret;
+}
+
+PhysicsObject createBox(glm::vec3 pos, glm::vec3 size)
+{
+	PhysicsObject ret;
+	ret.type = TYPE_BOX;
+
+	ret.position = pos;
+	ret.shape = size;
+	ret.mass = size.x * size.y * size.z;
+
+	return ret;
+}
+
+PhysicsObject createCilindru(glm::vec3 pos, float r, float h)
+{
+	PhysicsObject ret;
+	ret.type = TYPE_CILINDRU;
+
+	ret.position = pos;
+	ret.shape.x = r;
+	ret.shape.y = h;
+	ret.mass = 3.14159 * r * r * h;
+
+	return ret;
+}
+
+void applyDrag(PhysicsObject &object)
+{
+	glm::vec3 dragForce = 0.1f * -object.velocity * glm::abs(object.velocity) / 2.f;
+	float length = glm::length(dragForce);
+	if (length)
+	{
+		if (length > MAX_AIR_DRAG)
+		{
+			dragForce /= length;
+			dragForce *= MAX_AIR_DRAG;
+		}
+
+		object.acceleration += dragForce;
+	}
+}
+
+void Simulator::updateForces(PhysicsObject &object, float deltaTime)
+{
+
+	if (object.mass >= INFINITY || object.mass <= 0)
+	{
+		object.velocity = {};
+		object.acceleration = {};
+		return;
+	}
+
+	object.acceleration = glm::clamp(object.acceleration,
+		glm::vec3(-MAX_ACCELERATION), glm::vec3(MAX_ACCELERATION));
+
+	//Symplectic Euler
+	object.velocity += object.acceleration * deltaTime * 0.5f;
+	object.velocity = glm::clamp(object.velocity, glm::vec3(-MAX_VELOCITY), glm::vec3(MAX_VELOCITY));
+
+	object.position += object.velocity * deltaTime;
+
+	object.velocity += object.acceleration * deltaTime * 0.5f;
+	object.velocity = glm::clamp(object.velocity, glm::vec3(-MAX_VELOCITY), glm::vec3(MAX_VELOCITY));
+
+	if (std::fabs(object.velocity.x) < 0.00001) { object.velocity.x = 0; }
+	if (std::fabs(object.velocity.y) < 0.00001) { object.velocity.y = 0; }
+
+	object.acceleration = {};
+}
+
+void normalizeSafe(glm::vec3 &v)
+{
+	float l = glm::length(v);
+
+	if (l <= 0.00000001)
+	{
+		v = {1,0,0};
+	}
+	else
+	{
+		v /= l;
+	}
+}
+
+bool AABBvsAABB(PhysicsObject &a, PhysicsObject &b, float &penetration, glm::vec3 &normal)
+{
+	// Vector from A to B
+	glm::vec3 n = b.position - a.position;
+
+	auto aMax = a.getMax();
+	auto aMin = a.getMin();
+	auto bMax = b.getMax();
+	auto bMin = b.getMin();
+
+	// Calculate half extents along each axis for each object
+	float a_extent_x = (aMax.x - aMin.x) / 2.0f;
+	float b_extent_x = (bMax.x - bMin.x) / 2.0f;
+	float a_extent_y = (aMax.y - aMin.y) / 2.0f;
+	float b_extent_y = (bMax.y - bMin.y) / 2.0f;
+	float a_extent_z = (aMax.z - aMin.z) / 2.0f;
+	float b_extent_z = (bMax.z - bMin.z) / 2.0f;
+
+	// Calculate overlaps on each axis
+	float x_overlap = a_extent_x + b_extent_x - abs(n.x);
+	float y_overlap = a_extent_y + b_extent_y - abs(n.y);
+	float z_overlap = a_extent_z + b_extent_z - abs(n.z);
+
+	// SAT test on x, y, and z axes
+	if (x_overlap > 0 && y_overlap > 0 && z_overlap > 0)
+	{
+		// Determine the axis of least penetration
+		if (x_overlap < y_overlap && x_overlap < z_overlap)
+		{
+			normal = (n.x < 0) ? glm::vec3(-1, 0, 0) : glm::vec3(1, 0, 0);
+			penetration = x_overlap;
+		}
+		else if (y_overlap < z_overlap)
+		{
+			normal = (n.y < 0) ? glm::vec3(0, -1, 0) : glm::vec3(0, 1, 0);
+			penetration = y_overlap;
+		}
+		else
+		{
+			normal = (n.z < 0) ? glm::vec3(0, 0, -1) : glm::vec3(0, 0, 1);
+			penetration = z_overlap;
+		}
+		return true;
+	}
+
+	return false;
+}
+
+
+bool CirclevsCircle(PhysicsObject &a, PhysicsObject &b,
+	float &penetration,
+	glm::vec3 &normal
+)
+{
+
+
+	float r = a.shape.r + b.shape.r;
+	float rSquared = r * r;
+	float distanceSquared = ((a.position.x - b.position.x) * (a.position.x - b.position.x)
+		+ (a.position.y - b.position.y) * (a.position.y - b.position.y)
+		+ (a.position.z - b.position.z) * (a.position.z - b.position.z));
+
+	bool rez = rSquared > distanceSquared;
+
+	if (rez)
+	{
+		normal = b.position - a.position;
+		normalizeSafe(normal);
+		penetration = r - sqrt(distanceSquared);
+	}
+
+	return rez;
+}
+
+void positionalCorrection(PhysicsObject &A, PhysicsObject &B, glm::vec3 n,
+	float penetrationDepth, float aInverseMass, float bInverseMass)
+{
+
+	const float percent = 0.2; // usually 20% to 80%
+	const float slop = 0.01; // usually 0.01 to 0.1 
+
+	glm::vec3 correction = (glm::max(penetrationDepth - slop, 0.0f) / (aInverseMass + bInverseMass)) * percent * n;
+
+	A.position -= aInverseMass * correction;
+	B.position += bInverseMass * correction;
+};
+
+float pythagoreanSolve(float fA, float fB)
+{
+	return std::sqrt(fA * fA + fB * fB);
+}
+
+void applyFriction(PhysicsObject &A, PhysicsObject &B, glm::vec3 tangent, glm::vec3 rv,
+	float aInverseMass, float bInverseMass, float j)
+{
+	// Solve for magnitude to apply along the friction vector
+	float jt = -glm::dot(rv, tangent);
+	jt = jt / (aInverseMass + bInverseMass);
+
+	// PythagoreanSolve = A^2 + B^2 = C^2, solving for C given A and B
+	// Use to approximate mu given friction coefficients of each body
+	float mu = pythagoreanSolve(A.staticFriction, B.staticFriction);
+
+	// Clamp magnitude of friction and create impulse vector
+	//(Coulomb's Law) Ff<=Fn
+	glm::vec3 frictionImpulse = {};
+	if (abs(jt) < j * mu)
+	{
+		frictionImpulse = jt * tangent;
+	}
+	else
+	{
+		float dynamicFriction = pythagoreanSolve(A.dynamicFriction, B.dynamicFriction);
+		frictionImpulse = -j * tangent * dynamicFriction;
+	}
+
+	// Apply
+	A.velocity -= (aInverseMass)*frictionImpulse;
+	B.velocity += (bInverseMass)*frictionImpulse;
+
+};
+
+
+void impulseResolution(PhysicsObject &A, PhysicsObject &B, glm::vec3 normal,
+	float velAlongNormal, float penetrationDepth)
+{
+
+	//calculate elasticity
+	float e = std::min(A.bouncyness, B.bouncyness);
+	//float e = 0.9;
+
+	float massInverseA = 1.f / A.mass;
+	float massInverseB = 1.f / B.mass;
+
+	if (A.mass == 0 || A.mass == INFINITY) { massInverseA = 0; }
+	if (B.mass == 0 || B.mass == INFINITY) { massInverseB = 0; }
+
+	// Calculate impulse scalar
+	float j = -(1.f + e) * velAlongNormal;
+	j /= massInverseA + massInverseB;
+
+	// Apply impulse
+	glm::vec3 impulse = j * normal;
+	A.velocity -= massInverseA * impulse;
+	B.velocity += massInverseB * impulse;
+
+	positionalCorrection(A, B, normal, penetrationDepth, massInverseA, massInverseB);
+
+	{
+
+		// Re-calculate relative velocity after normal impulse
+		// is applied (impulse from first article, this code comes
+		// directly thereafter in the same resolve function)
+
+		glm::vec3 rv = B.velocity - A.velocity;
+
+		// Solve for the tangent vector
+		glm::vec3 tangent = rv - glm::dot(rv, normal) * normal;
+
+		normalizeSafe(tangent);
+
+		applyFriction(A, B, tangent, rv, massInverseA, massInverseB, j);
+	}
+};
+
+bool AABBvsSphere(PhysicsObject abox, PhysicsObject bsphere, float &penetration, glm::vec3 &normal)
+{
+	// Vector from A to B
+	glm::vec3 n = bsphere.position - abox.position;
+
+	// Closest point on A to center of B
+	glm::vec3 closest = n;
+
+	// Calculate half extents along each axis for the AABB
+	float x_extent = (abox.getMax().x - abox.getMin().x) / 2.0f;
+	float y_extent = (abox.getMax().y - abox.getMin().y) / 2.0f;
+	float z_extent = (abox.getMax().z - abox.getMin().z) / 2.0f;
+
+	// Clamp point to edges of the AABB
+	closest.x = glm::clamp(closest.x, -x_extent, x_extent);
+	closest.y = glm::clamp(closest.y, -y_extent, y_extent);
+	closest.z = glm::clamp(closest.z, -z_extent, z_extent);
+
+	bool inside = false;
+
+	// Check if sphere center is inside the AABB
+	if (n == closest)
+	{
+		inside = true;
+
+		// Clamp to closest extent along the axis with the largest component
+		if (abs(n.x) > abs(n.y) && abs(n.x) > abs(n.z))
+		{
+			closest.x = (closest.x > 0) ? x_extent : -x_extent;
+		}
+		else if (abs(n.y) > abs(n.z))
+		{
+			closest.y = (closest.y > 0) ? y_extent : -y_extent;
+		}
+		else
+		{
+			closest.z = (closest.z > 0) ? z_extent : -z_extent;
+		}
+	}
+
+	// Calculate the vector from the closest point on A to the center of B
+	glm::vec3 normal3D = n - closest;
+	float d = glm::dot(normal3D, normal3D);
+	float r = bsphere.shape.r;
+
+	// Early out if the distance to the closest point is greater than the sphere's radius and the sphere is not inside the AABB
+	if (d > r * r && !inside)
+	{
+		return false;
+	}
+
+	// Calculate the actual distance if needed
+	d = sqrt(d);
+
+	// Set the collision normal and penetration depth
+	if (inside)
+	{
+		normal = -glm::normalize(normal3D);
+		penetration = r - d;
+	}
+	else
+	{
+		normal = glm::normalize(normal3D);
+		penetration = r - d;
+	}
+
+	return true;
+}
+
+
+bool CylindervsCylinder(PhysicsObject a, PhysicsObject b, float &penetration, glm::vec3 &normal)
+{
+	// Vector from A to B
+	glm::vec3 n = b.position - a.position;
+
+	auto aMax = a.getMax();
+	auto aMin = a.getMin();
+	auto bMax = b.getMax();
+	auto bMin = b.getMin();
+
+	// Calculate half extents along each axis for each object
+	float a_extent_x = (aMax.x - aMin.x) / 2.0f;
+	float b_extent_x = (bMax.x - bMin.x) / 2.0f;
+	float a_extent_y = (aMax.y - aMin.y) / 2.0f;
+	float b_extent_y = (bMax.y - bMin.y) / 2.0f;
+	float a_extent_z = (aMax.z - aMin.z) / 2.0f;
+	float b_extent_z = (bMax.z - bMin.z) / 2.0f;
+
+	// Calculate overlaps on each axis
+	float x_overlap = a_extent_x + b_extent_x - abs(n.x);
+	float y_overlap = a_extent_y + b_extent_y - abs(n.y);
+	float z_overlap = a_extent_z + b_extent_z - abs(n.z);
+
+	glm::vec2 distantaXZ(n.x, n.z);
+
+	float r = a.shape.r + b.shape.r;
+	float rSquared = r * r;
+	float distanceSquared = ((a.position.x - b.position.x) * (a.position.x - b.position.x)
+		+ (a.position.z - b.position.z) * (a.position.z - b.position.z));
+	bool overlapXZ = rSquared > distanceSquared;
+
+
+	// SAT test on x, y, and z axes
+	if (y_overlap > 0 && overlapXZ)
+	{
+		float XZdist = std::sqrt(distanceSquared);
+		float xzOverlap = r - XZdist;
+
+		// Determine the axis of least penetration
+		if (y_overlap < xzOverlap)
+		{
+			normal = (n.y < 0) ? glm::vec3(0, -1, 0) : glm::vec3(0, 1, 0);
+			penetration = y_overlap;
+		}
+		else
+		{
+
+			if (distantaXZ.x == 0 && distantaXZ.y == 0)
+			{
+				normal = glm::vec3(-1, 0, 0);
+				penetration = r;
+			}
+			else
+			{
+
+
+				distantaXZ /= XZdist; //normalize
+				normal.x = distantaXZ.x;
+				normal.y = 0;
+				normal.z = distantaXZ.y;
+
+				penetration = xzOverlap;
+			}
+		}
+
+		return true;
+	}
+
+	return false;
+
+}
+
+
+//a is cube
+//b is cylinder
+bool AABBvsCylinder(PhysicsObject a, PhysicsObject b, float &penetration, glm::vec3 &normal)
+{
+	// Vector from A to B
+	glm::vec3 n = b.position - a.position;
+
+	auto aMax = a.getMax();
+	auto aMin = a.getMin();
+	auto bMax = b.getMax();
+	auto bMin = b.getMin();
+
+	// Calculate half extents along each axis for each object
+	float a_extent_x = (aMax.x - aMin.x) / 2.0f;
+	float b_extent_x = (bMax.x - bMin.x) / 2.0f;
+	float a_extent_y = (aMax.y - aMin.y) / 2.0f;
+	float b_extent_y = (bMax.y - bMin.y) / 2.0f;
+	float a_extent_z = (aMax.z - aMin.z) / 2.0f;
+	float b_extent_z = (bMax.z - bMin.z) / 2.0f;
+
+	// Calculate overlaps on each axis
+	float x_overlap = a_extent_x + b_extent_x - abs(n.x);
+	float y_overlap = a_extent_y + b_extent_y - abs(n.y);
+	float z_overlap = a_extent_z + b_extent_z - abs(n.z);
+
+
+	// Closest point on A to center of B
+	glm::vec3 closest = n;
+
+	// Calculate half extents along each axis for the AABB
+	float x_extent = (aMax.x - aMin.x) / 2.0f;
+	float y_extent = (aMax.y - aMin.y) / 2.0f;
+	float z_extent = (aMax.z - aMin.z) / 2.0f;
+
+	// Clamp point to edges of the AABB
+	closest.x = glm::clamp(closest.x, -x_extent, x_extent);
+	closest.y = glm::clamp(closest.y, -y_extent, y_extent);
+	closest.z = glm::clamp(closest.z, -z_extent, z_extent);
+
+	bool inside = false;
+
+	// Check if sphere center is inside the AABB
+	if (n == closest)
+	{
+		inside = true;
+
+		// Clamp to closest extent along the axis with the largest component
+		if (abs(n.x) > abs(n.y) && abs(n.x) > abs(n.z))
+		{
+			closest.x = (closest.x > 0) ? x_extent : -x_extent;
+		}
+		else if (abs(n.y) > abs(n.z))
+		{
+			closest.y = (closest.y > 0) ? y_extent : -y_extent;
+		}
+		else
+		{
+			closest.z = (closest.z > 0) ? z_extent : -z_extent;
+		}
+	}
+
+	glm::vec3 closestWorldPos = closest + a.position;
+
+
+	float distantaClosestPoint = glm::distance(glm::vec3{closestWorldPos.x, 0.f, closestWorldPos.z}, {b.position.x, 0.f, b.position.z});
+	float overlapXZ = b.shape.x - distantaClosestPoint;
+
+	// SAT test on x, y, and z axes
+	if (inside || (y_overlap > 0 && overlapXZ > 0))
+	{
+
+		// Determine the axis of least penetration
+		if (y_overlap < overlapXZ)
+		{
+			normal = (n.y < 0) ? glm::vec3(0, -1, 0) : glm::vec3(0, 1, 0);
+			penetration = y_overlap;
+		}
+		else
+		{
+			//cerc patrat
+
+			// Calculate the vector from the closest point on A to the center of B
+			glm::vec3 normal3D = n - closest; normal3D.y = 0;
+			float d = glm::dot(normal3D, normal3D);
+			float r = b.shape.r;
+
+			if (normal3D.x == 0 && normal3D.z == 0)
+			{
+				normal = -glm::vec3({1,0,0});
+				penetration = r;
+				return true;
+			}
+
+			d = sqrt(d);
+
+			// Set the collision normal and penetration depth
+			if (inside)
+			{
+				normal = -glm::normalize(normal3D);
+				penetration = r - d;
+			}
+			else
+			{
+				normal = glm::normalize(normal3D);
+				penetration = r - d;
+			}
+		}
+
+		return true;
+	}
+
+	return false;
+
+}
+
+
+//a is sphere
+//b is cylinder
+bool SpherevsCylinder(PhysicsObject a, PhysicsObject b, float &penetration, glm::vec3 &normal)
+{
+	// Vector from A to B
+	glm::vec3 n = b.position - a.position;
+
+	auto aMax = a.getMax();
+	auto aMin = a.getMin();
+	auto bMax = b.getMax();
+	auto bMin = b.getMin();
+
+	// Calculate half extents along each axis for each object
+	float a_extent_x = (aMax.x - aMin.x) / 2.0f;
+	float b_extent_x = (bMax.x - bMin.x) / 2.0f;
+	float a_extent_y = (aMax.y - aMin.y) / 2.0f;
+	float b_extent_y = (bMax.y - bMin.y) / 2.0f;
+	float a_extent_z = (aMax.z - aMin.z) / 2.0f;
+	float b_extent_z = (bMax.z - bMin.z) / 2.0f;
+
+	// Calculate overlaps on each axis
+	//float x_overlap = a_extent_x + b_extent_x - abs(n.x);
+	//float y_overlap = a_extent_y + b_extent_y - abs(n.y);
+	//float z_overlap = a_extent_z + b_extent_z - abs(n.z);
+
+	float y_overlap = 0;
+	bool inside = false;
+
+	// Closest point on B to center of A sphere
+	glm::vec3 closest = -n;
+	{
+
+		// Calculate half extents along each axis for the AABB
+		float x_extent = (bMax.x - bMin.x) / 2.0f;
+		float y_extent = (bMax.y - bMin.y) / 2.0f;
+		float z_extent = (bMax.z - bMin.z) / 2.0f;
+
+		// Clamp point to edges of the AABB
+		closest.x = glm::clamp(closest.x, -x_extent, x_extent);
+		closest.y = glm::clamp(closest.y, -y_extent, y_extent);
+		closest.z = glm::clamp(closest.z, -z_extent, z_extent);
+
+
+		// Check if sphere center is inside the AABB
+		if (n == closest)
+		{
+			inside = true;
+
+			// Clamp to closest extent along the axis with the largest component
+			if (abs(n.x) > abs(n.y) && abs(n.x) > abs(n.z))
+			{
+				closest.x = (closest.x > 0) ? x_extent : -x_extent;
+			}
+			else if (abs(n.y) > abs(n.z))
+			{
+				closest.y = (closest.y > 0) ? y_extent : -y_extent;
+			}
+			else
+			{
+				closest.z = (closest.z > 0) ? z_extent : -z_extent;
+			}
+		}
+
+		glm::vec3 closestWorldPos = closest + b.position;
+
+		y_overlap = a.shape.x - std::abs(a.position.y - closestWorldPos.y);
+	}
+
+	float overlapXZ = 0;
+	float r = a.shape.r + b.shape.r;
+	float distanceSquared = 0;
+	{
+		glm::vec2 distantaXZ(n.x, n.z);
+		float rSquared = r * r;
+		float distanceSquared = ((a.position.x - b.position.x) * (a.position.x - b.position.x)
+			+ (a.position.z - b.position.z) * (a.position.z - b.position.z));
+		float XZdist = std::sqrt(distanceSquared);
+		overlapXZ = r - XZdist;
+	}
+
+
+	// SAT test on x, y, and z axes
+	if (inside || (y_overlap > 0 && overlapXZ > 0))
+	{
+
+		// Determine the axis of least penetration
+		if (y_overlap < overlapXZ)
+		{
+			normal = (n.y < 0) ? glm::vec3(0, -1, 0) : glm::vec3(0, 1, 0);
+			penetration = y_overlap;
+
+			//cerc patrat
+
+			//// Calculate the vector from the closest point on A to the center of B
+			//glm::vec3 normal3D = a.position - (b.position + closest);
+			//float d = glm::dot(normal3D, normal3D);
+			//float r = a.shape.r;
+			//
+			//if (normal3D.x == 0 && normal3D.z == 0)
+			//{
+			//	normal = -glm::vec3({1,0,0});
+			//	penetration = r;
+			//	return true;
+			//}
+			//
+			//d = sqrt(d);
+			//
+			//// Set the collision normal and penetration depth
+			//if (inside)
+			//{
+			//	normal = -glm::normalize(normal3D);
+			//	penetration = r - d;
+			//}
+			//else
+			//{
+			//	normal = glm::normalize(normal3D);
+			//	penetration = r - d;
+			//}
+		}
+		else
+		{
+			//cerc cerc pe XZ
+			normal = b.position - a.position;
+			normal.y = 0;
+			normalizeSafe(normal);
+			penetration = overlapXZ;
+
+		}
+
+		return true;
+	}
+
+	return false;
+
+}
+
+
+
+void Simulator::update(float deltaTime)
+{
+
+	size_t bodiesSize = bodies.size();
+	for (auto &body: bodies)
+	{
+		auto &b = body.second;
+
+		//gravity
+		b.acceleration += glm::vec3{0, -9.81, 0};
+
+		applyDrag(b);
+
+		//detect colisions
+		for (auto &body2 : bodies)
+		{
+
+			if (body.first == body2.first) { continue; }
+
+			auto &A = b;
+			auto &B = body2.second;
+
+			if (A.type == TYPE_CIRCLE &&
+				B.type == TYPE_CIRCLE
+				)
+			{
+				glm::vec3 normal = {};
+				float penetration = 0;
+
+				if (CirclevsCircle(
+					A, B,
+					penetration, normal))
+				{
+					glm::vec3 relativeVelocity = B.velocity - A.velocity;
+					float velAlongNormal = glm::dot(relativeVelocity, normal);
+
+					// Do not resolve if velocities are separating
+					if (velAlongNormal > 0)
+					{
+
+					}
+					else
+					{
+						impulseResolution(A, B, normal, velAlongNormal, penetration);
+					}
+				}
+
+			}
+			else
+				if (A.type == TYPE_BOX &&
+					B.type == TYPE_BOX
+					)
+				{
+					glm::vec3 normal = {};
+					float penetration = 0;
+
+					if (AABBvsAABB(
+						A, B,
+						penetration, normal))
+					{
+						glm::vec3 relativeVelocity = B.velocity - A.velocity;
+						float velAlongNormal = glm::dot(relativeVelocity, normal);
+
+						// Do not resolve if velocities are separating
+						if (velAlongNormal > 0)
+						{
+
+						}
+						else
+						{
+							impulseResolution(A, B, normal, velAlongNormal, penetration);
+						}
+					}
+
+				}
+				else
+					if (A.type == TYPE_BOX &&
+						B.type == TYPE_CIRCLE
+						)
+					{
+
+						glm::vec3 normal = {};
+						float penetration = 0;
+
+						if (AABBvsSphere(
+							A, B,
+							penetration, normal))
+						{
+							glm::vec3 relativeVelocity = B.velocity - A.velocity;
+							float velAlongNormal = glm::dot(relativeVelocity, normal);
+
+							// Do not resolve if velocities are separating
+							if (velAlongNormal > 0)
+							{
+
+							}
+							else
+							{
+								impulseResolution(A, B, normal, velAlongNormal, penetration);
+							}
+						}
+					}
+					else
+						if (A.type == TYPE_CILINDRU &&
+							B.type == TYPE_CILINDRU
+							)
+						{
+
+							glm::vec3 normal = {};
+							float penetration = 0;
+
+							if (CylindervsCylinder(
+								A, B,
+								penetration, normal))
+							{
+								glm::vec3 relativeVelocity = B.velocity - A.velocity;
+								float velAlongNormal = glm::dot(relativeVelocity, normal);
+
+								// Do not resolve if velocities are separating
+								if (velAlongNormal > 0)
+								{
+
+								}
+								else
+								{
+									impulseResolution(A, B, normal, velAlongNormal, penetration);
+								}
+							}
+						}
+						else if (A.type == TYPE_BOX && B.type == TYPE_CILINDRU)
+						{
+							glm::vec3 normal = {};
+							float penetration = 0;
+
+							if (AABBvsCylinder(
+								A, B,
+								penetration, normal))
+							{
+								glm::vec3 relativeVelocity = B.velocity - A.velocity;
+								float velAlongNormal = glm::dot(relativeVelocity, normal);
+
+								// Do not resolve if velocities are separating
+								if (velAlongNormal > 0)
+								{
+
+								}
+								else
+								{
+									impulseResolution(A, B, normal, velAlongNormal, penetration);
+								}
+							}
+						}
+						else if (A.type == TYPE_CIRCLE && B.type == TYPE_CILINDRU)
+						{
+							glm::vec3 normal = {};
+							float penetration = 0;
+
+							if (SpherevsCylinder(
+								A, B,
+								penetration, normal))
+							{
+								glm::vec3 relativeVelocity = B.velocity - A.velocity;
+								float velAlongNormal = glm::dot(relativeVelocity, normal);
+
+								// Do not resolve if velocities are separating
+								if (velAlongNormal > 0)
+								{
+
+								}
+								else
+								{
+									impulseResolution(A, B, normal, velAlongNormal, penetration);
+								}
+							}
+						}
+
+
+
+		}
+
+		updateForces(b, deltaTime);
+
+	#pragma region hit walls
+		auto minPos = b.getMin();
+		auto maxPos = b.getMax();
+
+		float boxDown = -boxDimensions.y / 2;
+
+		if (minPos.y < boxDown)
+		{
+			float extra = boxDown - minPos.y;
+			b.position.y += extra;
+			b.velocity.y *= -1;
+		}
+
+		if (maxPos.y > boxDimensions.y / 2)
+		{
+			float extra = maxPos.y - boxDimensions.y / 2;
+			b.position.y -= extra;
+			b.velocity.y *= -1;
+		}
+
+		if (minPos.x < -boxDimensions.x / 2)
+		{
+			float extra = -boxDimensions.x / 2 - minPos.x;
+			b.position.x += extra;
+			b.velocity.x *= -1;
+		}
+
+		if (minPos.z < -boxDimensions.z / 2)
+		{
+			float extra = -boxDimensions.z / 2 - minPos.z;
+			b.position.z += extra;
+			b.velocity.z *= -1;
+		}
+
+		if (maxPos.x > boxDimensions.x / 2)
+		{
+			float extra = maxPos.x - boxDimensions.x / 2;
+			b.position.x -= extra;
+			b.velocity.x *= -1;
+		}
+
+		if (maxPos.z > boxDimensions.z / 2)
+		{
+			float extra = maxPos.z - boxDimensions.z / 2;
+			b.position.z -= extra;
+			b.velocity.z *= -1;
+		}
+	#pragma endregion
+
+
+	}
+}

+ 108 - 0
Pika/gameplay/containers/milk/physics.h

@@ -0,0 +1,108 @@
+#pragma once
+#include <glm/glm.hpp>
+#include <vector>
+#include <unordered_map>
+
+constexpr int TYPE_CIRCLE = 0;
+constexpr int TYPE_BOX = 1;
+constexpr int TYPE_CILINDRU = 2;
+
+constexpr static float MAX_VELOCITY = 10'000;
+constexpr static float MAX_ACCELERATION = 10'000;
+constexpr static float MAX_AIR_DRAG = 100;
+
+
+struct alignas(16) PhysicsObject
+{
+	//the position represents the center of the object.
+	// the shape is the width height depth for cube and for circle it is radius.
+
+	glm::vec3 position = {};    // 12 bytes
+
+	glm::vec3 shape = {};
+
+	glm::vec3 velocity = {};
+
+	glm::vec3 acceleration = {};
+
+	float mass = 1;
+	float bouncyness = 0.9;
+	int type = 0;
+	float staticFriction = 0.4;
+
+	float dynamicFriction = 0.3;
+
+
+	glm::vec3 getMin()
+	{
+		if (type == TYPE_CIRCLE)
+		{
+			glm::vec3 rez = position;
+			rez -= glm::vec3(shape.x, shape.x, shape.x);
+			return rez;
+		}
+		else if (type == TYPE_BOX)
+		{
+			glm::vec3 rez = position;
+			rez -= glm::vec3(shape) / 2.f;
+			return rez;
+		}
+		else if (type == TYPE_CILINDRU)
+		{
+			glm::vec3 rez = position;
+			rez -= glm::vec3(shape.x, shape.y / 2.f, shape.x);
+			return rez;
+		}
+	}
+
+	glm::vec3 getMax()
+	{
+		if (type == TYPE_CIRCLE)
+		{
+			glm::vec3 rez = position;
+			rez += glm::vec3(shape.x, shape.x, shape.x);
+			return rez;
+		}
+		else if (type == TYPE_BOX)
+		{
+			glm::vec3 rez = position;
+			rez += glm::vec3(shape) / 2.f;
+			return rez;
+		}if (type == TYPE_CILINDRU)
+		{
+			glm::vec3 rez = position;
+			rez += glm::vec3(shape.x, shape.y / 2.f, shape.x);
+			return rez;
+		}
+	}
+
+};
+
+
+
+PhysicsObject createBall(glm::vec3 pos, float r);
+
+PhysicsObject createBox(glm::vec3 pos, glm::vec3 size);
+
+PhysicsObject createCilindru(glm::vec3 pos, float r, float h);
+
+
+struct Simulator
+{
+	std::unordered_map<int, PhysicsObject> bodies;
+
+	void updateForces(PhysicsObject &object, float deltaTime);
+
+	int id = 1;
+
+	int getIdAndIncrement()
+	{
+		return id++;
+	}
+
+	glm::vec3 boxDimensions = {200, 100, 200};
+
+	void update(float deltaTime);
+
+};
+

+ 4 - 1
Pika/gameplay/containers/threedtest.h

@@ -77,10 +77,13 @@ struct ThreeDTest: public Container
 
 
 		gl3d::Transform t;
-		t.position = {0, 0, -3};
+		t.position = {400'000, 0, -4};
 		t.rotation = {1.5, 0 , 0};
 
 		helmetEntity = renderer.createEntity(helmetModel, t);
+	
+		renderer.camera.position.x = 400'000;
+
 
 		return true;
 	}

+ 2 - 2
Pika/resources/logs.txt

@@ -1,2 +1,2 @@
-#2025-03-27 15:00:39: Created container: Milk
-#2025-03-27 15:00:41: Destroyed continer: Milk #1
+#2025-05-10 21:26:43: Created container: Milk
+#2025-05-10 21:27:22: Destroyed continer: Milk #1

+ 5 - 0
Pika/resources/milk/floor/floor.mtl

@@ -0,0 +1,5 @@
+newmtl floorMaterial
+
+map_Kd floor.png
+norm floorN.png
+map_Pr floorR.png

BIN
Pika/resources/milk/floor/floor.png


BIN
Pika/resources/milk/floor/floorN.png


BIN
Pika/resources/milk/floor/floorR.png


BIN
Pika/resources/milk/level.bin


+ 240 - 29
Pika/thirdparty/safeSafe/include/safeSave/safeSave.h

@@ -2,7 +2,7 @@
 //do not remove this notice
 //(c) Luta Vlad
 // 
-// safeSave 1.0.0
+// safeSave 1.0.4
 // 
 ///////////////////////////////////////////
 
@@ -10,6 +10,7 @@
 #include <fstream>
 #include <vector>
 #include <unordered_map>
+#include <initializer_list>
 
 #ifdef _MSC_VER
 #pragma warning( disable : 26812 )
@@ -17,7 +18,7 @@
 
 namespace sfs
 {
-	enum Errors : int
+	enum Errors: int
 	{
 		noError = 0,
 		couldNotOpenFinle,
@@ -29,17 +30,18 @@ namespace sfs
 		entryNotFound,
 		entryHasDifferentDataType,
 		couldNotParseData,
-		fileSizeNotBigEnough,	//files with checksum have a check sum in them so if the file is smaller than the checksum size it is corupted
+		fileSizeNotBigEnough,
 	};
-	
-	const char* getErrorString(Errors e);
-	
+
+	const char *getErrorString(Errors e);
+
 	//can return error: couldNotOpenFinle
-	Errors readEntireFile(std::vector<char>& data, const char* name);
-	
+	Errors readEntireFile(std::vector<char> &data, const char *name);
+
 	//reades the content of a file (size bytes), if shouldMatchSize is false will read the entire fill untill size bytes are read or the entire file was read
 	//can return error: couldNotOpenFinle, fileSizeDitNotMatch
-	Errors readEntireFile(void* data, size_t size, const char* name, bool shouldMatchSize, int *bytesRead = nullptr);
+	Errors readEntireFile(void *data, size_t size, const char *name, bool shouldMatchSize,
+		int *bytesRead = nullptr);
 
 	//gets the file size
 	//can return error: couldNotOpenFinle
@@ -47,7 +49,7 @@ namespace sfs
 
 	//reades the entire content of the data to a file and uses checkSum
 	//can return error: couldNotOpenFinle, fileSizeDitNotMatch, checkSumFailed
-	Errors readEntireFileWithCheckSum(void* data, size_t size, const char* name);
+	Errors readEntireFileWithCheckSum(void *data, size_t size, const char *name);
 
 	//reades the entire content of the data to a file and uses checkSum
 	//can return error: couldNotOpenFinle, fileSizeNotBigEnough
@@ -55,34 +57,43 @@ namespace sfs
 
 	//writes the entire content of the data to a file and uses checkSum
 	//can return error: couldNotOpenFinle
-	Errors writeEntireFileWithCheckSum(const void* data, size_t size, const char* name);
+	Errors writeEntireFileWithCheckSum(const void *data, size_t size, const char *name);
 
 	//writes the entire content of the data to a file
 	//can return error: couldNotOpenFinle
-	Errors writeEntireFile(const std::vector<char>& data, const char* name);
-	
+	Errors writeEntireFile(const std::vector<char> &data, const char *name);
+
 	//writes the entire content of the data to a file
 	//can return error: couldNotOpenFinle
-	Errors writeEntireFile(const void*data, size_t size, const char* name);
+	Errors writeEntireFile(const void *data, size_t size, const char *name);
 
 	//saved the data with a check sum and a backup
+	// It will also use a temporary file to make sure the saving is safe.
 	//can return error: couldNotOpenFinle, 
 	//	couldNotMakeBackup (if reportnotMakingBackupAsAnError is true, but will still save the first file)
-	Errors safeSave(const void* data, size_t size, const char* nameWithoutExtension, bool reportnotMakingBackupAsAnError);
+	Errors safeSave(const void *data, size_t size, const char *nameWithoutExtension, bool reportnotMakingBackupAsAnError);
+
+
+	//saved the data with a check sum. It will use a temporary file to make sure the saving is safe.
+	//can return error: couldNotOpenFinle
+	Errors safeSaveNoBackup(const void *data, size_t size, const char *nameWithoutExtension);
+
 
 	//loads the data that was saved using safeSave
 	//can return error: couldNotOpenFinle, fileSizeDitNotMatch, checkSumFailed, 
-	//	readBackup (if reportLoadingBackupAsAnError but data will still be loaded with the backup)
-	Errors safeLoad(void* data, size_t size, const char* nameWithoutExtension, bool reportLoadingBackupAsAnError);
+	//	readBackup (if reportLoadingBackupAsAnError but data will still be loaded with the backup)'
+	// is checkSumFailed is returned, the data was still read!
+	Errors safeLoad(void *data, size_t size, const char *nameWithoutExtension, bool reportLoadingBackupAsAnError);
 
 	//loads the data that was saved using safeSave and stored as a SafeSafeKeyValueData structure
 	//can return error: couldNotOpenFinle, checkSumFailed, fileSizeNotBigEnough
 	//	readBackup (if reportLoadingBackupAsAnError but data will still be loaded with the backup)
-	Errors safeLoad(std::vector<char> &data, const char *nameWithoutExtension, bool reportLoadingBackupAsAnError);
+	Errors safeLoad(std::vector<char> &data, const char *nameWithoutExtension,
+		bool reportLoadingBackupAsAnError);
 
 	//same as safeLoad but only loads the backup file.
 	//can return error: couldNotOpenFinle, fileSizeDitNotMatch, checkSumFailed
-	Errors safeLoadBackup(void* data, size_t size, const char* nameWithoutExtension);
+	Errors safeLoadBackup(void *data, size_t size, const char *nameWithoutExtension);
 
 
 	//used to save data and read it
@@ -98,20 +109,114 @@ namespace sfs
 				float_type,
 				bool_type,
 				string_type,
+				uint64_type,
+				int64_type,
+				keyValueData_type,
+
+				char_type,
+				uchar_type,
+				uint_type,
+				double_type,
+
+				vec2_type,
+				vec3_type,
+				vec4_type,
+
+				ivec2_type,
+				ivec3_type,
+				ivec4_type,
 			};
 
 			std::vector<char> data;
 			char type = 0;
 			union Primitives
 			{
+				std::uint64_t uint64Data = 0;
+				std::int64_t int64Data;
 				std::int32_t intData;
 				float floatData;
 				bool boolData;
+				char charData;
+				unsigned char uCharData;
+				unsigned int uintData;
+				double doubleData;
+
+				struct Vec
+				{
+					float x;
+					float y;
+					float z;
+					float w;
+				}vec;
+
+				struct IVec
+				{
+					int x;
+					int y;
+					int z;
+					int w;
+				}ivec;
+
 			}primitives;
+
+			bool operator== (const Entry &other) const;
+
+			bool operator!= (const Entry &other) const
+			{
+				return !(*this == other);
+			}
+
+			// Implicit constructors for primitive types
+			Entry() = default;
+			Entry(int v) { type = int_type; primitives.intData = v; }
+			Entry(float v) { type = float_type; primitives.floatData = v; }
+			Entry(bool v) { type = bool_type; primitives.boolData = v; }
+			Entry(std::int64_t v) { type = int64_type; primitives.int64Data = v; }
+			Entry(std::uint64_t v) { type = uint64_type; primitives.uint64Data = v; }
+			Entry(const std::string &v)
+			{
+				type = string_type;
+				data.assign(v.begin(), v.end());
+			}
+			Entry(const char *v): Entry(std::string(v)) {}
+
+			Entry(char c) { type = char_type; primitives.charData = c; }
+			Entry(unsigned char c) { type = uchar_type; primitives.uCharData = c; }
+			Entry(unsigned int i) { type = uint_type; primitives.uintData = i; }
+			Entry(double d) { type = double_type; primitives.doubleData = d; }
+
+
+			Entry(float x, float y) { type = vec2_type; primitives.vec = {}; primitives.vec.x = x; primitives.vec.y = y; }
+			Entry(float x, float y, float z) { type = vec3_type; primitives.vec = {}; primitives.vec.x = x; primitives.vec.y = y; primitives.vec.y = z; }
+			Entry(float x, float y, float z, float w) { type = vec4_type; primitives.vec = {}; primitives.vec.x = x; primitives.vec.y = y; primitives.vec.y = z; primitives.vec.w = w; }
+
+			Entry(int x, int y) { type = ivec2_type; primitives.ivec = {}; primitives.ivec.x = x; primitives.ivec.y = y; }
+			Entry(int x, int y, int z) { type = ivec3_type; primitives.ivec = {}; primitives.ivec.x = x; primitives.ivec.y = y; primitives.ivec.y = z; }
+			Entry(int x, int y, int z, int w) { type = ivec4_type; primitives.ivec = {}; primitives.ivec.x = x; primitives.ivec.y = y; primitives.ivec.y = z; primitives.ivec.w = w; }
+
+
 		};
 
+		SafeSafeKeyValueData() = default;
+
+		SafeSafeKeyValueData(std::initializer_list<std::pair<std::string, Entry>> init)
+		{
+			for (const auto &pair : init)
+			{
+				entries[pair.first] = pair.second;
+			}
+		}
+
 		std::unordered_map<std::string, Entry> entries;
 
+		bool operator== (const SafeSafeKeyValueData &other) const;
+
+		bool operator!= (const SafeSafeKeyValueData &other) const
+		{
+			return !(*this == other);
+		}
+
+
 		//returns true if entry exists
 		bool entryExists(std::string at);
 
@@ -121,9 +226,16 @@ namespace sfs
 		//can return error: warningEntryAlreadyExists, if so it will overwrite data
 		Errors setRawData(std::string at, void *data, size_t size);
 
-		//it gives you a pointer to the data and the size, so it is like a view
+		//this can be usefull to hold nested key value data objects in one another!
+		//can return error: warningEntryAlreadyExists, if so it will overwrite data
+		Errors setKeyValueData(std::string at, const SafeSafeKeyValueData &data);
+
+		//won't change data if failed
+		//can return error: entryNotFound, entryHasDifferentDataType, couldNotParseData
+		Errors getKeyValueData(std::string at, SafeSafeKeyValueData &data);
+
 		//can return error: entryNotFound, entryHasDifferentDataType
-		Errors getRawDataPointer(std::string at, void* &data, size_t &size);
+		Errors getRawDataPointer(std::string at, void *&data, size_t &size);
 
 		//won't change i if failed
 		//can return error: entryNotFound, entryHasDifferentDataType
@@ -132,10 +244,97 @@ namespace sfs
 		//can return error: warningEntryAlreadyExists, if so it will overwrite data
 		Errors setInt(std::string at, int32_t i);
 
+		//can return error: warningEntryAlreadyExists, if so it will overwrite data
+		Errors setChar(std::string at, char c);
+
+		//can return error: warningEntryAlreadyExists, if so it will overwrite data
+		Errors setUCHar(std::string at, unsigned char c);
+
+		//can return error: warningEntryAlreadyExists, if so it will overwrite data
+		Errors setUInt(std::string at, uint32_t u);
+
+		//can return error: warningEntryAlreadyExists, if so it will overwrite data
+		Errors setDouble(std::string at, double d);
+
+		//can return error: warningEntryAlreadyExists, if so it will overwrite data
+		Errors setVec2(std::string at, float x, float y);
+
+		//can return error: warningEntryAlreadyExists, if so it will overwrite data
+		Errors setVec3(std::string at, float x, float y, float z);
+
+		//can return error: warningEntryAlreadyExists, if so it will overwrite data
+		Errors setVec4(std::string at, float x, float y, float z, float w);
+
+		//can return error: warningEntryAlreadyExists, if so it will overwrite data
+		Errors setIVec2(std::string at, int x, int y);
+
+		//can return error: warningEntryAlreadyExists, if so it will overwrite data
+		Errors setIVec3(std::string at, int x, int y, int z);
+
+		//can return error: warningEntryAlreadyExists, if so it will overwrite data
+		Errors setIVec4(std::string at, int x, int y, int z, int w);
+
+
+		//won't change the variable if failed
+		//can return error: entryNotFound, entryHasDifferentDataType
+		Errors getVec2(std::string at, float &x, float &y);
+
+		//won't change the variable if failed
+		//can return error: entryNotFound, entryHasDifferentDataType
+		Errors getVec3(std::string at, float &x, float &y, float &z);
+
+		//won't change the variable if failed
+		//can return error: entryNotFound, entryHasDifferentDataType
+		Errors getVec4(std::string at, float &x, float &y, float &z, float &w);
+
+		//won't change the variable if failed
+		//can return error: entryNotFound, entryHasDifferentDataType
+		Errors getIVec2(std::string at, int &x, int &y);
+
+		//won't change the variable if failed
+		//can return error: entryNotFound, entryHasDifferentDataType
+		Errors getIVec3(std::string at, int &x, int &y, int &z);
+
+		//won't change the variable if failed
+		//can return error: entryNotFound, entryHasDifferentDataType
+		Errors getIVec4(std::string at, int &x, int &y, int &z, int &w);
+
+
+		//can return error: warningEntryAlreadyExists, if so it will overwrite data
+		Errors setuInt64(std::string at, uint64_t i);
+
+		//won't change i if failed
+		//can return error: entryNotFound, entryHasDifferentDataType
+		Errors getuInt64(std::string at, uint64_t &i);
+
+		//can return error: warningEntryAlreadyExists, if so it will overwrite data
+		Errors setInt64(std::string at, int64_t i);
+
+		//won't change i if failed
+		//can return error: entryNotFound, entryHasDifferentDataType
+		Errors getInt64(std::string at, int64_t &i);
+
 		//won't change f if failed
 		//can return error: entryNotFound, entryHasDifferentDataType
 		Errors getFloat(std::string at, float &f);
 
+
+		//won't change c if failed
+		//can return error: entryNotFound, entryHasDifferentDataType
+		Errors getChar(std::string at, char &c);
+
+		//won't change c if failed
+		//can return error: entryNotFound, entryHasDifferentDataType
+		Errors getUChar(std::string at, unsigned char &c);
+
+		//won't change u if failed
+		//can return error: entryNotFound, entryHasDifferentDataType
+		Errors getUInt(std::string at, unsigned int &u);
+
+		//won't change d if failed
+		//can return error: entryNotFound, entryHasDifferentDataType
+		Errors getDobule(std::string at, double &d);
+
 		//can return error: warningEntryAlreadyExists, if so it will overwrite data
 		Errors setFloat(std::string at, float f);
 
@@ -153,17 +352,29 @@ namespace sfs
 		//can return error: entryNotFound, entryHasDifferentDataType
 		Errors getString(std::string at, std::string &s);
 
-		std::vector<char> formatIntoFileData();
+		std::vector<char> formatIntoFileDataBinary() const;
+
+		//not finished yet
+		//std::vector<char> formatIntoFileDataTextBased();
+
 
 		//can return error: couldNotParseData
 		Errors loadFromFileData(char *data, size_t size);
 	};
-	
+
 	//saved the data stored as a SafeSafeKeyValueData structure in a binary format with a check sum and a backup
+	//Uses a temporary file to make sure the saving is safe
 	//can return error: couldNotOpenFinle, 
 	//	couldNotMakeBackup (if reportnotMakingBackupAsAnError is true, but will still save the first file)
 	Errors safeSave(SafeSafeKeyValueData &data, const char *nameWithoutExtension, bool reportnotMakingBackupAsAnError);
 
+
+	//saved the data stored as a SafeSafeKeyValueData structure in a binary format with a check.
+	//Uses a temporary file to make sure the saving is safe
+	//can return error: couldNotOpenFinle, 
+	Errors safeSaveNoBackup(SafeSafeKeyValueData &data, const char *nameWithoutExtension);
+
+
 	//loads the data that was saved using safeSave and stored as a SafeSafeKeyValueData structure
 	//can return error: couldNotOpenFinle, fileSizeNotBigEnough, checkSumFailed, couldNotParseData
 	//	readBackup (if reportLoadingBackupAsAnError but data will still be loaded from the backup)
@@ -175,12 +386,12 @@ namespace sfs
 
 	struct FileMapping
 	{
-		void* pointer = {};
+		void *pointer = {};
 		size_t size = 0;
 		struct
 		{
-			void* fileHandle = 0;
-			void* fileMapping = 0;
+			void *fileHandle = 0;
+			void *fileMapping = 0;
 		}internal = {};
 	};
 
@@ -188,7 +399,7 @@ namespace sfs
 
 	struct FileMapping
 	{
-		void* pointer = {};
+		void *pointer = {};
 		size_t size = 0;
 		struct
 		{
@@ -200,8 +411,8 @@ namespace sfs
 
 	//a file mapping maps the content of a file to ram
 	//can return error: couldNotOpenFinle
-	Errors openFileMapping(FileMapping& fileMapping, const char* name, size_t size, bool createIfNotExisting);
+	Errors openFileMapping(FileMapping &fileMapping, const char *name, size_t size, bool createIfNotExisting);
 
-	void closeFileMapping(FileMapping& fileMapping);
+	void closeFileMapping(FileMapping &fileMapping);
 
 };

File diff suppressed because it is too large
+ 901 - 161
Pika/thirdparty/safeSafe/src/safeSave.cpp


Some files were not shown because too many files changed in this diff