summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile10
-rw-r--r--c2.cpp27
-rw-r--r--configure.lua1
-rw-r--r--editor.cpp65
-rw-r--r--editor.hpp6
-rw-r--r--physics.cpp274
-rw-r--r--physics.hpp78
7 files changed, 452 insertions, 9 deletions
diff --git a/Makefile b/Makefile
index 8761134..15182e1 100644
--- a/Makefile
+++ b/Makefile
@@ -56,6 +56,8 @@ maths.o: maths.cpp
g++ -std=c++20 $(opt_com) $(cflags) -c maths.cpp -o maths.o
model.o: model.cpp
g++ -std=c++20 $(opt_com) $(cflags) -c model.cpp -o model.o
+physics.o: physics.cpp
+ g++ -std=c++20 $(opt_com) $(cflags) -c physics.cpp -o physics.o
pipeline.o: pipeline.cpp
g++ -std=c++20 $(opt_com) $(cflags) -c pipeline.cpp -o pipeline.o
scene.o: scene.cpp
@@ -66,8 +68,8 @@ video.o: video.cpp
g++ -std=c++20 $(opt_com) $(cflags) -c video.cpp -o video.o
world.o: world.cpp
g++ -std=c++20 $(opt_com) $(cflags) -c world.cpp -o world.o
-c2: app.o asset.o c2.o camera.o debugdraw.o editor.o maths.o model.o pipeline.o scene.o ui.o video.o world.o libqstd.a
- g++ $(opt_lnk) $(lflags) -o c2 app.o asset.o c2.o camera.o debugdraw.o editor.o maths.o model.o pipeline.o scene.o ui.o video.o world.o libqstd.a -lX11 -lm
+c2: app.o asset.o c2.o camera.o debugdraw.o editor.o maths.o model.o physics.o pipeline.o scene.o ui.o video.o world.o libqstd.a
+ g++ $(opt_lnk) $(lflags) -o c2 app.o asset.o c2.o camera.o debugdraw.o editor.o maths.o model.o physics.o pipeline.o scene.o ui.o video.o world.o libqstd.a -lX11 -lm
c2: pack
@@ -135,10 +137,10 @@ pack: packer data/debug.csh data/mip_spec.csh data/sky.csh data/surface.csh data
data:
mkdir -p data
--include qstd/memory.d qstd/plat.d qstd/str.d qstd/pack.d cfg/cfgparse.d sc/sc.d sc/includer.d app.d asset.d c2.d camera.d debugdraw.d editor.d maths.d model.d pipeline.d scene.d ui.d video.d world.d convtexture.d convmodel.d convmaterial.d packer.d
+-include qstd/memory.d qstd/plat.d qstd/str.d qstd/pack.d cfg/cfgparse.d sc/sc.d sc/includer.d app.d asset.d c2.d camera.d debugdraw.d editor.d maths.d model.d physics.d pipeline.d scene.d ui.d video.d world.d convtexture.d convmodel.d convmaterial.d packer.d
clean:
- rm -f qstd/memory.o qstd/plat.o qstd/str.o qstd/pack.o cfg/cfgparse.o sc/sc.o sc/includer.o app.o asset.o c2.o camera.o debugdraw.o editor.o maths.o model.o pipeline.o scene.o ui.o video.o world.o convtexture.o convmodel.o convmaterial.o packer.o qstd/memory.d qstd/plat.d qstd/str.d qstd/pack.d cfg/cfgparse.d sc/sc.d sc/includer.d app.d asset.d c2.d camera.d debugdraw.d editor.d maths.d model.d pipeline.d scene.d ui.d video.d world.d convtexture.d convmodel.d convmaterial.d packer.d data/debug.csh data/mip_spec.csh data/sky.csh data/surface.csh data/surface_depthonly.csh data/tonemap.csh data/triangle.csh data/ui.csh data/monkey.mdl data/22.tex data/kita.tex data/brick_albedo.tex data/brick_ao.tex data/brick_normal.tex data/sky.tex data/bricks.mat data/plastic.mat
+ rm -f qstd/memory.o qstd/plat.o qstd/str.o qstd/pack.o cfg/cfgparse.o sc/sc.o sc/includer.o app.o asset.o c2.o camera.o debugdraw.o editor.o maths.o model.o physics.o pipeline.o scene.o ui.o video.o world.o convtexture.o convmodel.o convmaterial.o packer.o qstd/memory.d qstd/plat.d qstd/str.d qstd/pack.d cfg/cfgparse.d sc/sc.d sc/includer.d app.d asset.d c2.d camera.d debugdraw.d editor.d maths.d model.d physics.d pipeline.d scene.d ui.d video.d world.d convtexture.d convmodel.d convmaterial.d packer.d data/debug.csh data/mip_spec.csh data/sky.csh data/surface.csh data/surface_depthonly.csh data/tonemap.csh data/triangle.csh data/ui.csh data/monkey.mdl data/22.tex data/kita.tex data/brick_albedo.tex data/brick_ao.tex data/brick_normal.tex data/sky.tex data/bricks.mat data/plastic.mat
rm -f shadercompiler
rmdir data
rm -f c2
diff --git a/c2.cpp b/c2.cpp
index 58f3831..be27f60 100644
--- a/c2.cpp
+++ b/c2.cpp
@@ -3,6 +3,7 @@
#include "debugdraw.hpp"
#include "editor.hpp"
#include "model.hpp"
+#include "physics.hpp"
#include "scene.hpp"
#include "ui.hpp"
#include "video.hpp"
@@ -632,10 +633,11 @@ struct C2 : public App {
Asset_Arena assets;
Device* dev;
Shader* shader, * ui_shader;
+ Collider* box_col, * floor_col;
Texture* texture;
Texture* texture2;
Texture_Id default_texture;
- Entity_Id monkey, monkey2;
+ Entity_Id monkey, monkey2, box, floor;
Model_Scene scene;
Orbit_Cam camera;
Fullscreen_Quad quad;
@@ -736,12 +738,32 @@ struct C2 : public App {
(Model*)assets.load("monkey.mdl")
);
}
+ {
+ box_col = make_box(&asset_arena, v3f(1.0f, 1.0f, 1.0f));
+ floor_col = make_box(&asset_arena, v3f(10.0f, 0.1f, 10.0f));
+ box = world->create_entity();
+ auto [trans, rb] = world->add<Transform, Rigidbody>(box);
+ trans.mat = m4f::identity();
+ rb.init(box_col, v3f(0.0f, 5.0f, 0.0f), quat::identity(), 1.0f);
+ rb.add_force(v3f(0.4f, -1.0f, 0.0f), v3f(0.0f, 0.1f, 0.0f));
+
+ floor = world->create_entity();
+ auto [transf, rbf] = world->add<Transform, Rigidbody>(floor);
+ transf.mat = m4f::identity();
+ rbf.init(floor_col, v3f(0.0f), quat::identity(), 0.0f);
+ }
}
void on_update() override {
+ float phys_dt = dt;
+ Editor_Settings& es = editor_settings();
Arena frame_arena;
init_arena(&frame_arena, per_frame, per_frame_memory_size);
+ if (es.pause_physics)
+ phys_dt = 0.0f;
+ physics_update(*world, &frame_arena, phys_dt);
+
dev->begin_frame();
lr.begin(w, h);
@@ -930,6 +952,9 @@ struct C2 : public App {
editor_draw(lr);
+ if (es.debug_physics)
+ physics_debug(*world, lr);
+
ctx.debug_push("debug");
pb.begin_rp();
pb.rp_target(dev->get_backbuffer(), Clear_Mode::restore);
diff --git a/configure.lua b/configure.lua
index 3d4a748..7f126ac 100644
--- a/configure.lua
+++ b/configure.lua
@@ -8,6 +8,7 @@ config = {
"editor",
"maths",
"model",
+ "physics",
"pipeline",
"scene",
"ui",
diff --git a/editor.cpp b/editor.cpp
index 5b96db6..698b5ff 100644
--- a/editor.cpp
+++ b/editor.cpp
@@ -5,11 +5,16 @@
#include "ui.hpp"
#include "world.hpp"
+extern "C" {
+#include "memory.h"
+}
+
static struct {
+ Editor_Settings settings;
UI* ui;
UI::Toolbar* toolbar;
- UI::Button* mat_btn, * ent_btn;
- UI::Modal* mat_win, * ent_win;
+ UI::Button* mat_btn, * ent_btn, * phy_btn;
+ UI::Modal* mat_win, * ent_win, * phy_win;
UI::Label* mat_name;
UI::Slider* metalness_input;
UI::Slider* roughness_input;
@@ -131,8 +136,7 @@ static int mat_btn_handler(UI::Element* e, const UI::Message& m) {
return 0;
}
-static int ent_win_handler(UI::Element* e, const UI::Message& m) {
- (void)e;
+static int ent_win_handler(UI::Element* e, const UI::Message& m) { (void)e;
if (m.type == UI::Message::Type::destroy) {
editor.ent_btn->enable();
editor.ent_win = 0;
@@ -141,6 +145,7 @@ static int ent_win_handler(UI::Element* e, const UI::Message& m) {
}
static int ent_btn_handler(UI::Element* e, const UI::Message& m) {
+ (void)e;
if (m.type == UI::Message::Type::click) {
auto ui = editor.ui;
editor.ent_win = ui->create_element<UI::Modal>(
@@ -167,6 +172,47 @@ static int ent_btn_handler(UI::Element* e, const UI::Message& m) {
return 0;
}
+static int phy_win_handler(UI::Element* e, const UI::Message& m) { (void)e;
+ if (m.type == UI::Message::Type::destroy) {
+ editor.phy_btn->enable();
+ editor.phy_win = 0;
+ }
+ return 0;
+}
+
+static int phy_btn_handler(UI::Element* e, const UI::Message& m) {
+ (void)e;
+ if (m.type == UI::Message::Type::click) {
+ auto ui = editor.ui;
+ editor.phy_win = ui->create_element<UI::Modal>(
+ editor.ui->root,
+ "Physics Debugger"
+ );
+ auto cont = editor.phy_win->contents;
+ editor.phy_win->handler = phy_win_handler;
+ editor.phy_btn->disable();
+ auto cdebug = ui->create_element<UI::Checkbox>(cont, "Debug Draw");
+ cdebug->handler = [](UI::Element* e, const UI::Message& m) {
+ if (m.type == UI::Message::Type::checkbox_changed) {
+ UI::Checkbox* ch = (UI::Checkbox*)e;
+ editor.settings.debug_physics = ch->val;
+ }
+ return 0;
+ };
+ cdebug->set_val(editor.settings.debug_physics);
+ auto cpause = ui->create_element<UI::Checkbox>(cont, "Pause");
+ cpause->set_val(editor.settings.pause_physics);
+ cpause->handler = [](UI::Element* e, const UI::Message& m) {
+ if (m.type == UI::Message::Type::checkbox_changed) {
+ UI::Checkbox* ch = (UI::Checkbox*)e;
+ editor.settings.pause_physics = ch->val;
+ }
+ return 0;
+ };
+ }
+ return 0;
+}
+
void init_editor(UI* ui, World* world) {
editor.ui = ui;
editor.toolbar = ui->create_element<UI::Toolbar>(ui->root);
@@ -180,9 +226,16 @@ void init_editor(UI* ui, World* world) {
"Entity Debugger"
);
editor.ent_btn->handler = ent_btn_handler;
+ editor.phy_btn = ui->create_element<UI::Button>(
+ editor.toolbar,
+ "Physics Debugger"
+ );
+ editor.phy_btn->handler = phy_btn_handler;
editor.mat_win = 0;
editor.ent_win = 0;
+ editor.phy_win = 0;
editor.world = world;
+ zero(&editor.settings, sizeof editor.settings);
}
void deinit_editor() {
@@ -215,3 +268,7 @@ void editor_draw(Line_Renderer& lr) {
);
}
}
+
+Editor_Settings& editor_settings() {
+ return editor.settings;
+}
diff --git a/editor.hpp b/editor.hpp
index a809d6d..77c7e9e 100644
--- a/editor.hpp
+++ b/editor.hpp
@@ -7,9 +7,15 @@ struct UI;
struct Model_Instance;
struct Line_Renderer;
+struct Editor_Settings {
+ bool pause_physics;
+ bool debug_physics;
+};
+
void init_editor(UI* ui, World* w);
void deinit_editor();
void editor_on_select(Entity_Id e, int m);
void editor_draw(Line_Renderer& lr);
+Editor_Settings& editor_settings();
#endif
diff --git a/physics.cpp b/physics.cpp
new file mode 100644
index 0000000..993b587
--- /dev/null
+++ b/physics.cpp
@@ -0,0 +1,274 @@
+#include "debugdraw.hpp"
+#include "physics.hpp"
+#include "scene.hpp"
+#include "world.hpp"
+
+extern "C" {
+#include "memory.h"
+#include "str.h"
+}
+
+#include <new>
+
+void Collider::debug_render(
+ Line_Renderer& lr,
+ const m4f& t,
+ const v3f& col
+) {
+ int i, c = face_count;
+ for (i = 0; i < c; i++) {
+ Face& f = faces[i];
+ uint16_t prev = f.verts[0];
+ v3f avg = (t * v4f(verts[prev], 1.0f)).xyz();
+ int j;
+ lr.colour(col);
+ for (j = 1; j < f.vert_count; j++) {
+ uint16_t idx = f.verts[j];
+ v3f from = (t * v4f(verts[prev], 1.0f)).xyz();
+ v3f to = (t * v4f(verts[idx], 1.0f)).xyz();
+ lr.add_line(from, to);
+ avg += to;
+ prev = idx;
+ }
+ lr.add_line(
+ (t * v4f(verts[prev], 1.0f)).xyz(),
+ (t * v4f(verts[f.verts[0]], 1.0f)).xyz()
+ );
+ avg /= (float)f.vert_count;
+ lr.colour(v3f(0.3f, 1.0f, 1.0f));
+ lr.add_arrow(avg, avg + (t * v4f(f.normal, 0.0f)).xyz());
+ }
+}
+
+Int_Collider* Collider::transform(const m4f& t, Arena* a) const {
+ int i;
+ Int_Collider* r = (Int_Collider*)arena_alloc(a, sizeof *r);
+ r->faces = (v3f*)arena_alloc(a, face_count * sizeof *r->faces);
+ r->verts = (v3f*)arena_alloc(a, vert_count * sizeof *r->verts);
+ for (i = 0; i < face_count; i++)
+ r->faces[i] = (t * v4f(faces[i].normal, 0.0f)).xyz();
+ for (i = 0; i < vert_count; i++)
+ r->verts[i] = (t * v4f(verts[i], 1.0f)).xyz();
+ return r;
+}
+
+Collider* make_box(Arena* a, const v3f& size) {
+ int i;
+ v3f he = size * 0.5f;
+ v3f* verts;
+ Collider::Face* faces;
+ Collider* c = (Collider*)arena_alloc(a, sizeof *c);
+ new(c) Collider();
+ c->vert_count = 8;
+ c->face_count = 6;
+ verts = (v3f*)arena_alloc(
+ a,
+ sizeof *c->verts * c->vert_count
+ );
+ faces = (Collider::Face*)arena_alloc(
+ a,
+ sizeof *c->faces * c->face_count
+ );
+ for (i = 0; i < c->face_count; i++) {
+ Collider::Face& f = faces[i];
+ f.vert_count = 4;
+ f.verts = (uint16_t*)arena_alloc(
+ a,
+ sizeof *f.verts * f.vert_count
+ );
+ }
+ c->verts = verts;
+ c->faces = faces;
+ verts[0] = he;
+ verts[1] = v3f( he.x, he.y, -he.z);
+ verts[2] = v3f(-he.x, he.y, -he.z);
+ verts[3] = v3f(-he.x, he.y, he.z);
+ verts[4] = v3f( he.x, -he.y, he.z);
+ verts[5] = v3f( he.x, -he.y, -he.z);
+ verts[6] = -he;
+ verts[7] = v3f(-he.x, -he.y, he.z);
+ faces[0].normal = v3f( 1.0f, 0.0f, 0.0f);
+ faces[0].verts[0] = 0;
+ faces[0].verts[1] = 1;
+ faces[0].verts[2] = 5;
+ faces[0].verts[3] = 4;
+ faces[1].normal = v3f(-1.0f, 0.0f, 0.0f);
+ faces[1].verts[0] = 2;
+ faces[1].verts[1] = 3;
+ faces[1].verts[2] = 7;
+ faces[1].verts[3] = 6;
+ faces[2].normal = v3f( 0.0f, 1.0f, 0.0f);
+ faces[2].verts[0] = 2;
+ faces[2].verts[1] = 3;
+ faces[2].verts[2] = 0;
+ faces[2].verts[3] = 1;
+ faces[3].normal = v3f( 0.0f, -1.0f, 0.0f);
+ faces[3].verts[0] = 6;
+ faces[3].verts[1] = 7;
+ faces[3].verts[2] = 4;
+ faces[3].verts[3] = 5;
+ faces[4].normal = v3f( 0.0f, 0.0f, 1.0f);
+ faces[4].verts[0] = 4;
+ faces[4].verts[1] = 7;
+ faces[4].verts[2] = 3;
+ faces[4].verts[3] = 0;
+ faces[5].normal = v3f( 0.0f, 0.0f, -1.0f);
+ faces[5].verts[0] = 6;
+ faces[5].verts[1] = 5;
+ faces[5].verts[2] = 1;
+ faces[5].verts[3] = 2;
+ c->name = dup_string(a, "<box>");
+ c->loader = 0;
+ {
+ v3f i, hl2 = he * he;
+ float a = 1.0f / 12.0f;
+ i.x = a * (hl2.z + hl2.y);
+ i.y = a * (hl2.x + hl2.z);
+ i.z = a * (hl2.x + hl2.y);
+ c->moment = m3f(
+ v3f(i.x, 0.0f, 0.0f),
+ v3f(0.0f, i.y, 0.0f),
+ v3f(0.0f, 0.0f, i.z)
+ ).inverse();
+ }
+ return c;
+}
+
+void Rigidbody::init(
+ Collider* c,
+ const v3f& p,
+ const v4f& r,
+ float mass
+) {
+ col = c;
+ set_pos(p);
+ set_rot(r);
+ set_mass(mass);
+ force = v3f(0.0f);
+ torque = v3f(0.0f);
+ vel = v3f(0.0f);
+ avel = v3f(0.0f);
+ set_mass(mass);
+}
+
+void Rigidbody::set_pos(const v3f& p) {
+ pos = p;
+ prev_pos = p;
+}
+
+void Rigidbody::set_rot(const v4f& r) {
+ rot = r;
+ prev_rot = r;
+}
+
+void Rigidbody::set_mass(float m) {
+ if (m > 0.000001f)
+ inv_mass = 1.0f / m;
+ else
+ inv_mass = 0.0f;
+}
+
+void Rigidbody::add_force(const v3f& f) {
+ force += f;
+}
+
+void Rigidbody::add_force(const v3f& f, const v3f& p) {
+ force += f;
+ add_torque(v3f::cross(f, p));
+}
+
+void Rigidbody::add_impulse(const v3f& f) {
+ vel += f * inv_mass;
+}
+
+void Rigidbody::add_impulse(const v3f& f, const v3f& p) {
+ vel += f * inv_mass;
+ avel += v3f::cross(f, p) * inv_mass;
+}
+
+void Rigidbody::add_torque(const v3f& t) {
+ torque += t;
+}
+
+void physics_tick(World& w, float ts) {
+ for (auto v : w.view<Rigidbody>()) {
+ auto& rb = v.get<Rigidbody>();
+ v3f accel = rb.inv_mass * rb.force;
+ v3f aaccel = rb.col->moment * rb.torque;
+ rb.prev_pos = rb.pos;
+ rb.prev_rot = rb.rot;
+ rb.vel += accel * ts;
+ rb.avel += aaccel * ts;
+ rb.pos += rb.vel * ts;
+ rb.rot = quat::normalised(rb.rot + quat::mul<false>(
+ quat::scale(v4f(rb.avel * 0.5f, 0.0f), ts),
+ rb.rot
+ ));
+ }
+}
+
+bool Int_Collider::collide(const Int_Collider& other) const {
+ return false;
+}
+
+struct Collision_Engine {
+ Int_Collider* bodies;
+ Arena* arena;
+ void init(World& w, Arena* a) {
+ arena = a;
+ bodies = 0;
+ for (auto v : w.view<Transform, Rigidbody>()) {
+ auto& t = v.get<Transform>();
+ auto& r = v.get<Rigidbody>();
+ Int_Collider* b = r.col->transform(t.mat, a);
+ b->rb = &r;
+ r.colliding = 0;
+ b->next = bodies;
+ bodies = b;
+ }
+ }
+ void detect() {
+ Int_Collider* a;
+ for (a = bodies; a; a = a->next) {
+ Int_Collider* b;
+ for (b = a->next; b; b = b->next) {
+ if (a->collide(*b)) {
+ a->rb->colliding = 1;
+ b->rb->colliding = 1;
+ }
+ }
+ }
+ }
+};
+
+void physics_update(World& w, Arena* a, float ts) {
+ Collision_Engine ce;
+ ce.init(w, a);
+ ce.detect();
+ physics_tick(w, ts);
+ for (auto v : w.view<Transform, Rigidbody>()) {
+ auto& t = v.get<Transform>();
+ auto& r = v.get<Rigidbody>();
+ t.mat =
+ m4f::translate(m4f::identity(), r.pos) *
+ m4f::rotate(m4f::identity(), r.rot);
+ }
+}
+
+void physics_debug(World& w, Line_Renderer& r) {
+ for (auto v : w.view<Transform, Rigidbody>()) {
+ Transform& t = v.get<Transform>();
+ Rigidbody& rb = v.get<Rigidbody>();
+ Collider* c = rb.col;
+ v3f col = rb.colliding?
+ v3f(1.0f, 0.0f, 0.0f):
+ v3f(0.3f, 1.0f, 0.3f);
+ c->debug_render(r, t.mat, col);
+ r.colour(v3f(1.0f, 0.3f, 0.3f));
+ r.add_arrow(rb.pos, rb.pos + rb.vel);
+ r.colour(v3f(0.3f, 0.3f, 1.0f));
+ r.add_arrow(rb.pos, rb.pos + rb.avel);
+ r.colour(v3f(1.0f, 1.0f, 0.3f));
+ r.add_arrow(rb.pos, rb.pos + rb.torque);
+ }
+}
diff --git a/physics.hpp b/physics.hpp
new file mode 100644
index 0000000..6e6b259
--- /dev/null
+++ b/physics.hpp
@@ -0,0 +1,78 @@
+#ifndef physics_hpp
+#define physics_hpp
+
+#include "asset.hpp"
+#include "maths.hpp"
+
+#include <tuple>
+
+struct Arena;
+struct Line_Renderer;
+struct World;
+
+struct Manifold {
+ static constexpr int max_contacts = 16;
+ v3f contacts[max_contacts];
+ int contact_count;
+ v3f normal;
+};
+
+struct Rigidbody;
+struct Int_Collider {
+ Rigidbody* rb;
+ Int_Collider* next;
+ int vert_count;
+ int face_count;
+ v3f* verts;
+ v3f* faces;
+
+ bool collide(const Int_Collider& other) const;
+};
+
+struct Collider : Asset {
+ struct Face {
+ v3f normal;
+ int vert_count;
+ uint16_t* verts;
+ };
+ int face_count;
+ int vert_count;
+ Face* faces;
+ v3f* verts;
+ m3f moment;
+
+ void debug_render(
+ Line_Renderer& lr,
+ const m4f& t,
+ const v3f& c
+ );
+ Int_Collider* transform(const m4f& m, Arena* a) const;
+};
+
+struct Rigidbody {
+ v3f prev_pos, pos;
+ v3f vel, avel;
+ v3f force;
+ v3f torque;
+ v4f prev_rot, rot;
+ float inv_mass;
+ Collider* col;
+ int colliding;
+
+ void init(Collider* c, const v3f& p, const v4f& r, float mass);
+ void set_pos(const v3f& p);
+ void set_rot(const v4f& r);
+ void set_mass(float m);
+ void add_force(const v3f& f);
+ void add_force(const v3f& f, const v3f& p);
+ void add_impulse(const v3f& f);
+ void add_impulse(const v3f& f, const v3f& p);
+ void add_torque(const v3f& t);
+};
+
+Collider* make_box(Arena* a, const v3f& size);
+
+void physics_update(World& w, Arena* a, float ts);
+void physics_debug(World& w, Line_Renderer& lr);
+
+#endif