diff --git a/ToyEngine/ToyEngine.vcxproj b/ToyEngine/ToyEngine.vcxproj
index dced617..a72d952 100644
--- a/ToyEngine/ToyEngine.vcxproj
+++ b/ToyEngine/ToyEngine.vcxproj
@@ -31,6 +31,7 @@
+
@@ -53,6 +54,7 @@
+
diff --git a/ToyEngine/ToyEngine.vcxproj.filters b/ToyEngine/ToyEngine.vcxproj.filters
index 161e55d..688fc93 100644
--- a/ToyEngine/ToyEngine.vcxproj.filters
+++ b/ToyEngine/ToyEngine.vcxproj.filters
@@ -72,6 +72,9 @@
头文件
+
+ 头文件
+
@@ -131,6 +134,9 @@
源文件
+
+ 源文件
+
diff --git a/ToyEngine/src/DemoWorld.cpp b/ToyEngine/src/DemoWorld.cpp
index c469d66..2ab9f11 100644
--- a/ToyEngine/src/DemoWorld.cpp
+++ b/ToyEngine/src/DemoWorld.cpp
@@ -114,7 +114,7 @@ void DemoWorld::rendererTick(float deltaTime)
World::rendererTick(deltaTime);
}
-void DemoWorld::mouseCallback(GLFWwindow* window, double xpos, double ypos)
+void DemoWorld::cursorPosCallback(GLFWwindow* window, double xpos, double ypos)
{
static bool firstMouse = true;
static float lastX;
diff --git a/ToyEngine/src/DemoWorld.h b/ToyEngine/src/DemoWorld.h
index a9d0128..05888e1 100644
--- a/ToyEngine/src/DemoWorld.h
+++ b/ToyEngine/src/DemoWorld.h
@@ -7,7 +7,7 @@ public:
DemoWorld();
void logicalTick(float deltaTime) override;
void rendererTick(float deltaTime) override;
- virtual void mouseCallback(GLFWwindow* window, double xpos, double ypos) override;
+ virtual void cursorPosCallback(GLFWwindow* window, double xpos, double ypos) override;
virtual void scrollCallback(GLFWwindow* window, double xoffset, double yoffset) override;
virtual void processInput(GLFWwindow* window, float deltaTime) override;
diff --git a/ToyEngine/src/IblUtils.cpp b/ToyEngine/src/IblUtils.cpp
index 5d5695c..45f9272 100644
--- a/ToyEngine/src/IblUtils.cpp
+++ b/ToyEngine/src/IblUtils.cpp
@@ -243,7 +243,7 @@ GLuint IblUtils::generateCubemap(GladGLContext* gl, GLuint captureFBO, GLuint ca
// ---------------------------------
stbi_set_flip_vertically_on_load(true);
int width, height, nrComponents;
- float* data = stbi_loadf("HDRI/clarens_midday_4k.hdr", &width, &height, &nrComponents, 0);
+ float* data = stbi_loadf(hdrPath, &width, &height, &nrComponents, 0);
unsigned int hdrTexture = 0;
if (data)
{
diff --git a/ToyEngine/src/IblUtils.h b/ToyEngine/src/IblUtils.h
index c91a6ad..53063b8 100644
--- a/ToyEngine/src/IblUtils.h
+++ b/ToyEngine/src/IblUtils.h
@@ -7,6 +7,7 @@ struct GladGLContext;
class IblUtils
{
public:
+ static constexpr char hdrPath[] = "HDRI/clarens_midday_4k.hdr";
static constexpr int cubemapSize = 1024;
static constexpr int irradianceMapSize = 32;
static constexpr int prefilterMapSize = 128;
diff --git a/ToyEngine/src/MainWindow.cpp b/ToyEngine/src/MainWindow.cpp
index b3110d7..4656b24 100644
--- a/ToyEngine/src/MainWindow.cpp
+++ b/ToyEngine/src/MainWindow.cpp
@@ -1,6 +1,7 @@
#include "MainWindow.h"
#include "Particle.h"
#include "DemoWorld.h"
+#include "ParticleWorld.h"
#include "RenderingSystem.h"
@@ -23,7 +24,10 @@ int MainWindow::exec()
RenderingSystem::instance().framebufferSizeCallback(window, width, height);
});
glfwSetCursorPosCallback(window, [](GLFWwindow* window, double xpos, double ypos) {
- MainWindow::instance().mouseCallback(window, xpos, ypos);
+ MainWindow::instance().cursorPosCallback(window, xpos, ypos);
+ });
+ glfwSetMouseButtonCallback(window, [](GLFWwindow* window, int button, int action, int mods) {
+ MainWindow::instance().mouseButtonCallback(window, button, action, mods);
});
glfwSetScrollCallback(window, [](GLFWwindow* window, double xoffset, double yoffset) {
MainWindow::instance().scrollCallback(window, xoffset, yoffset);
@@ -43,7 +47,7 @@ int MainWindow::exec()
}
- world = std::make_unique();
+ world = std::make_unique();
auto [minPos, maxPos] = world->getAABB();
std::cout << std::format("{},{},{} {},{},{}\n", minPos.x, minPos.y, minPos.z, maxPos.x, maxPos.y, maxPos.z);
@@ -113,9 +117,14 @@ void MainWindow::processInput(GLFWwindow* window, float deltaTime)
world->processInput(window, deltaTime);
}
-void MainWindow::mouseCallback(GLFWwindow* window, double xpos, double ypos)
+void MainWindow::cursorPosCallback(GLFWwindow* window, double xpos, double ypos)
{
- world->mouseCallback(window, xpos, ypos);
+ world->cursorPosCallback(window, xpos, ypos);
+}
+
+void MainWindow::mouseButtonCallback(GLFWwindow* window, int button, int action, int mods)
+{
+ world->mouseButtonCallback(window, button, action, mods);
}
void MainWindow::scrollCallback(GLFWwindow* window, double xoffset, double yoffset)
diff --git a/ToyEngine/src/MainWindow.h b/ToyEngine/src/MainWindow.h
index 4bd891e..85ca002 100644
--- a/ToyEngine/src/MainWindow.h
+++ b/ToyEngine/src/MainWindow.h
@@ -15,7 +15,8 @@ public:
private:
MainWindow();
- void mouseCallback(GLFWwindow* window, double xpos, double ypos);
+ void cursorPosCallback(GLFWwindow* window, double xpos, double ypos);
+ void mouseButtonCallback(GLFWwindow* window, int button, int action, int mods);
void scrollCallback(GLFWwindow* window, double xoffset, double yoffset);
void keyCallback(GLFWwindow* window, int key, int scancode, int action, int mods);
void processInput(GLFWwindow* window, float deltaTime);
diff --git a/ToyEngine/src/ParticleWorld.cpp b/ToyEngine/src/ParticleWorld.cpp
new file mode 100644
index 0000000..b0d7d57
--- /dev/null
+++ b/ToyEngine/src/ParticleWorld.cpp
@@ -0,0 +1,149 @@
+#include "ParticleWorld.h"
+#include "Particle.h"
+#include "RenderingSystem.h"
+
+ParticleWorld::ParticleWorld()
+{
+ light.lightDirection = glm::normalize(glm::vec3((cos(lightPitch) * cos(lightYaw)), (sin(lightPitch)), (cos(lightPitch) * sin(lightYaw))));
+ light.radiance = 15.f * lightColor;
+
+ {
+ auto p0 = std::make_shared(1);
+ p0->setPosition({ 0,1,0 });
+ p0->setFixed(true);
+
+ auto p1 = std::make_shared(1);
+ p1->setPosition({ 0,0,0 });
+ p1->setEnableGravity(true);
+
+ addActor(p0);
+ addActor(p1);
+
+ physicsManager.addSpring(p0, p1, 2, 5, 0.1);
+ }
+
+ for (int i = 0, particleCount = 5; i < particleCount; i++)
+ {
+ auto particle = std::make_shared(1);
+ particle->setPosition({ 5, 0, glm::mix(-3.1f,2.f,(double)i / (particleCount - 1)) });
+ if (i == 0 || i == particleCount - 1)
+ particle->setFixed(true);
+ else
+ particle->setEnableGravity(true);
+
+ particles.push_back(particle);
+ addActor(particle);
+
+ if (i != 0)
+ {
+ float ks = 10, kd = 0.1;
+ physicsManager.addRubberBand(particles[i - 1], particle, 0.01, ks, kd);
+ }
+ }
+
+ {
+ planet = std::make_shared(100);
+ planet->setPosition({ -10,0,0 });
+ planet->setFixed(true);
+
+ auto p1 = std::make_shared(1);
+ p1->setPosition({ -15,0,0 });
+ p1->setSpeed(glm::vec3(0, 0, 1) * glm::sqrt(G * planet->getMass() / glm::distance(planet->getPosition(), p1->getPosition())));
+
+ addActor(planet);
+ addActor(p1);
+
+ physicsManager.addGravitation(planet, p1, G);
+ }
+}
+
+void ParticleWorld::logicalTick(float deltaTime)
+{
+ World::logicalTick(deltaTime);
+}
+
+void ParticleWorld::rendererTick(float deltaTime)
+{
+ World::rendererTick(deltaTime);
+
+ for (auto iter = particles.begin(); iter != particles.end(); )
+ {
+ if (glm::length2((*iter)->getPosition()) > 1e4)
+ {
+ removeActor(*iter);
+ iter = particles.erase(iter);
+ }
+ else iter++;
+ }
+
+ if (addNewParticle)
+ {
+ addNewParticle = false;
+
+ auto p = std::make_shared(1);
+ p->setPosition(camera.Position + glm::vec3(0, -0.3, 0));
+ p->setSpeed(5.f * glm::normalize(camera.Front));
+ addActor(p);
+ physicsManager.addGravitation(planet, p, G);
+ particles.push_back(p);
+ }
+}
+
+void ParticleWorld::cursorPosCallback(GLFWwindow* window, double xpos, double ypos)
+{
+ static bool firstMouse = true;
+ static float lastX;
+ static float lastY;
+
+ float xoffset = xpos - lastX;
+ float yoffset = lastY - ypos;
+
+ lastX = xpos;
+ lastY = ypos;
+
+ if (firstMouse)
+ {
+ firstMouse = false;
+ return;
+ }
+
+ if (glfwGetMouseButton(window, GLFW_MOUSE_BUTTON_RIGHT) == GLFW_PRESS)
+ camera.processMouseMovement(xoffset, yoffset);
+}
+
+void ParticleWorld::mouseButtonCallback(GLFWwindow* window, int button, int action, int mods)
+{
+ if (button == GLFW_MOUSE_BUTTON_LEFT && action == GLFW_PRESS)
+ addNewParticle = true;
+}
+
+void ParticleWorld::scrollCallback(GLFWwindow* window, double xoffset, double yoffset)
+{
+ if (glfwGetKey(window, GLFW_KEY_LEFT_CONTROL) == GLFW_PRESS)
+ lightPitch = glm::mod(lightPitch + yoffset / 100., glm::pi());
+ else if (glfwGetKey(window, GLFW_KEY_LEFT_ALT) == GLFW_PRESS)
+ lightYaw = glm::mod(lightYaw + yoffset / 100., 2 * glm::pi());
+ else if (glfwGetKey(window, GLFW_KEY_R) == GLFW_PRESS)
+ light.radiance = glm::clamp((light.radiance / lightColor).x + (float)yoffset, 0.f, 100.f) * lightColor;
+ else if (glfwGetKey(window, GLFW_KEY_E) == GLFW_PRESS)
+ RenderingSystem::instance().exposure = glm::clamp(RenderingSystem::instance().exposure + (float)yoffset * 0.1f, 0.f, 2.f);
+ else camera.processMouseScroll(yoffset);
+
+ light.lightDirection = glm::normalize(glm::vec3((cos(lightPitch) * cos(lightYaw)), (sin(lightPitch)), (cos(lightPitch) * sin(lightYaw))));
+}
+
+void ParticleWorld::processInput(GLFWwindow* window, float deltaTime)
+{
+ if (glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS)
+ camera.processKeyboard(FORWARD, deltaTime);
+ if (glfwGetKey(window, GLFW_KEY_S) == GLFW_PRESS)
+ camera.processKeyboard(BACKWARD, deltaTime);
+ if (glfwGetKey(window, GLFW_KEY_A) == GLFW_PRESS)
+ camera.processKeyboard(LEFT, deltaTime);
+ if (glfwGetKey(window, GLFW_KEY_D) == GLFW_PRESS)
+ camera.processKeyboard(RIGHT, deltaTime);
+ if (glfwGetKey(window, GLFW_KEY_SPACE) == GLFW_PRESS)
+ camera.processKeyboard(UP, deltaTime);
+ if (glfwGetKey(window, GLFW_KEY_LEFT_SHIFT) == GLFW_PRESS)
+ camera.processKeyboard(DOWN, deltaTime);
+}
diff --git a/ToyEngine/src/ParticleWorld.h b/ToyEngine/src/ParticleWorld.h
new file mode 100644
index 0000000..051b6c4
--- /dev/null
+++ b/ToyEngine/src/ParticleWorld.h
@@ -0,0 +1,25 @@
+#pragma once
+#include "World.h"
+class ParticleWorld : public World
+{
+public:
+ ParticleWorld();
+ void logicalTick(float deltaTime) override;
+ void rendererTick(float deltaTime) override;
+ virtual void cursorPosCallback(GLFWwindow* window, double xpos, double ypos) override;
+ virtual void mouseButtonCallback(GLFWwindow* window, int button, int action, int mods) override;
+ virtual void scrollCallback(GLFWwindow* window, double xoffset, double yoffset) override;
+ virtual void processInput(GLFWwindow* window, float deltaTime) override;
+
+private:
+ std::vector> particles;
+ std::shared_ptr planet;
+ float G = 1;
+
+ glm::vec3 lightColor = glm::normalize(glm::vec3(0.7529, 0.7450, 0.6784));
+ float lightYaw = glm::radians(80.f);
+ float lightPitch = glm::radians(105.f);
+
+ bool addNewParticle = false;
+};
+
diff --git a/ToyEngine/src/PhysicsManager.cpp b/ToyEngine/src/PhysicsManager.cpp
index 996f040..50cc659 100644
--- a/ToyEngine/src/PhysicsManager.cpp
+++ b/ToyEngine/src/PhysicsManager.cpp
@@ -5,7 +5,11 @@ void PhysicsManager::tick(float deltaTime)
for (auto iter = constraints.begin(); iter < constraints.end(); )
{
if (iter->p1.expired() || iter->p2.expired())
+ {
+ if (!iter->p1.expired()) iter->p1.lock()->removeForce(iter->f1);
+ if (!iter->p2.expired()) iter->p2.lock()->removeForce(iter->f2);
iter = constraints.erase(iter);
+ }
else
{
iter->f1->value = iter->solver(*iter->p1.lock(), *iter->p2.lock());
@@ -29,3 +33,33 @@ void PhysicsManager::addSpring(std::shared_ptr p1, std::shared_ptr p1, std::shared_ptr p2, float length, float ks, float kd)
+{
+ auto f1 = std::make_shared(glm::vec3(0));
+ auto f2 = std::make_shared(glm::vec3(0));
+ p1->addForce(f1);
+ p2->addForce(f2);
+ constraints.emplace_back(p1, p2, [length, ks, kd](Particle& p1, Particle& p2) {
+ auto d = glm::distance(p1.getPosition(), p2.getPosition());
+ float dt = 1.f;
+ auto vd = glm::distance(p1.getPosition() + p1.getSpeed() * dt, p2.getPosition() + p2.getSpeed() * dt) / dt;
+ if (d <= length) return glm::vec3(0);
+ return -(ks * (d - length) + kd * vd) * (p1.getPosition() - p2.getPosition()) / d;
+ }, f1, f2);
+}
+
+void PhysicsManager::addGravitation(std::shared_ptr p1, std::shared_ptr p2, float G)
+{
+ auto f1 = std::make_shared(glm::vec3(0));
+ auto f2 = std::make_shared(glm::vec3(0));
+ p1->addForce(f1);
+ p2->addForce(f2);
+ constraints.emplace_back(p1, p2, [G](Particle& p1, Particle& p2) {
+ auto D = p2.getPosition() - p1.getPosition();
+ auto d2 = glm::length2(D);
+ auto d = glm::sqrt(d2);
+ if (d <= 1e-5) return glm::vec3(0);
+ return G * p1.getMass() * p2.getMass() / d2 * D / d;
+ }, f1, f2);
+}
diff --git a/ToyEngine/src/PhysicsManager.h b/ToyEngine/src/PhysicsManager.h
index 3190172..3c4aa1d 100644
--- a/ToyEngine/src/PhysicsManager.h
+++ b/ToyEngine/src/PhysicsManager.h
@@ -15,6 +15,8 @@ class PhysicsManager
public:
void tick(float deltaTime);
void addSpring(std::shared_ptr p1, std::shared_ptr p2, float length, float ks, float kd);
+ void addRubberBand(std::shared_ptr p1, std::shared_ptr p2, float length, float ks, float kd);
+ void addGravitation(std::shared_ptr p1, std::shared_ptr p2, float G = 6.67430e-11f);
private:
std::vector constraints;
diff --git a/ToyEngine/src/World.cpp b/ToyEngine/src/World.cpp
index 87b5959..4f66902 100644
--- a/ToyEngine/src/World.cpp
+++ b/ToyEngine/src/World.cpp
@@ -27,7 +27,12 @@ void World::draw(const RenderPassContext& context, Shader& shader)
void World::addActor(std::shared_ptr actor)
{
- actors.push_back(actor);
+ actors.insert(actor);
+}
+
+void World::removeActor(std::shared_ptr actor)
+{
+ actors.erase(actor);
}
std::pair World::getAABB()
@@ -41,4 +46,4 @@ std::pair World::getAABB()
maxPos = glm::max(maxPos, max);
}
return { minPos, maxPos };
-}
\ No newline at end of file
+}
diff --git a/ToyEngine/src/World.h b/ToyEngine/src/World.h
index f2bec0c..4c4340a 100644
--- a/ToyEngine/src/World.h
+++ b/ToyEngine/src/World.h
@@ -11,15 +11,17 @@ class World
public:
Camera camera;
Light light;
- std::vector> actors;
+ std::set> actors;
World();
virtual void logicalTick(float deltaTime);
virtual void rendererTick(float deltaTime);
void draw(const RenderPassContext& context, Shader& shader);
void addActor(std::shared_ptr actor);
+ void removeActor(std::shared_ptr actor);
std::pair getAABB();
- virtual void mouseCallback(GLFWwindow* window, double xpos, double ypos) {};
+ virtual void cursorPosCallback(GLFWwindow* window, double xpos, double ypos) {};
+ virtual void mouseButtonCallback(GLFWwindow* window, int button, int action, int mods) {};
virtual void scrollCallback(GLFWwindow* window, double xoffset, double yoffset) {};
virtual void keyCallback(GLFWwindow* window, int key, int scancode, int action, int mods) {};
virtual void processInput(GLFWwindow* window, float deltaTime) {};