diff options
-rw-r--r-- | Makefile | 10 | ||||
-rw-r--r-- | c2.cpp | 27 | ||||
-rw-r--r-- | configure.lua | 1 | ||||
-rw-r--r-- | editor.cpp | 65 | ||||
-rw-r--r-- | editor.hpp | 6 | ||||
-rw-r--r-- | physics.cpp | 274 | ||||
-rw-r--r-- | physics.hpp | 78 |
7 files changed, 452 insertions, 9 deletions
@@ -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 @@ -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", @@ -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; +} @@ -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 |