Commit c2535f0f authored by TheNumbat's avatar TheNumbat
Browse files

upstream changes

parent 8b8ee1f1
#include "rig.h" #include "rig.h"
#include "manager.h"
#include "../scene/renderer.h" #include "../scene/renderer.h"
#include "manager.h"
namespace Gui { namespace Gui {
bool Rig::keydown(Widgets& widgets, Undo& undo, SDL_Keysym key) { bool Rig::keydown(Widgets &widgets, Undo &undo, SDL_Keysym key) {
if(!my_obj) return false; if (!my_obj)
return false;
#ifdef __APPLE__ #ifdef __APPLE__
if(key.sym == SDLK_BACKSPACE && key.mod & KMOD_GUI) { if (key.sym == SDLK_BACKSPACE && key.mod & KMOD_GUI) {
#else #else
if(key.sym == SDLK_DELETE && selected) { if (key.sym == SDLK_DELETE && selected) {
#endif #endif
undo.del_bone(my_obj->id(), selected); undo.del_bone(my_obj->id(), selected);
selected = nullptr; selected = nullptr;
return true; return true;
} }
return false; return false;
} }
void Rig::render(Scene_Maybe obj_opt, Widgets& widgets, Camera& cam) { void Rig::render(Scene_Maybe obj_opt, Widgets &widgets, Camera &cam) {
if(!obj_opt.has_value()) return; if (!obj_opt.has_value())
return;
Scene_Item& item = obj_opt.value();
if(!item.is<Scene_Object>()) return; Scene_Item &item = obj_opt.value();
if (!item.is<Scene_Object>())
Scene_Object& obj = item.get<Scene_Object>(); return;
if(obj.opt.shape_type != PT::Shape_Type::none) return;
Scene_Object &obj = item.get<Scene_Object>();
if(my_obj != &obj) { if (obj.opt.shape_type != PT::Shape_Type::none)
my_obj = &obj; return;
selected = nullptr;
} if (my_obj != &obj) {
if(my_obj->rig_dirty) { my_obj = &obj;
mesh_bvh.build(obj.mesh()); selected = nullptr;
my_obj->rig_dirty = false; }
} if (my_obj->rig_dirty) {
mesh_bvh.build(obj.mesh());
Mat4 view = cam.get_view(); my_obj->rig_dirty = false;
}
Mat4 view = cam.get_view();
obj.render(view, false, false, false, false); obj.render(view, false, false, false, false);
obj.armature.render(view, selected, root_selected, false); obj.armature.render(view, selected, root_selected, false);
if(selected || root_selected) { if (selected || root_selected) {
widgets.active = Widget_Type::move; widgets.active = Widget_Type::move;
Vec3 pos; Vec3 pos;
if(selected) pos = obj.armature.end_of(selected); if (selected)
else pos = obj.armature.base(); pos = obj.armature.end_of(selected);
else
float scale = std::min((cam.pos() - pos).norm() / 5.5f, 10.0f); pos = obj.armature.base();
widgets.render(view, pos, scale);
} float scale = std::min((cam.pos() - pos).norm() / 5.5f, 10.0f);
widgets.render(view, pos, scale);
}
} }
void Rig::invalidate(Joint* j) { void Rig::invalidate(Joint *j) {
if(selected == j) selected = nullptr; if (selected == j)
if(new_joint == j) new_joint = nullptr; selected = nullptr;
if (new_joint == j)
new_joint = nullptr;
} }
void Rig::end_transform(Widgets& widgets, Undo& undo, Scene_Object& obj) { void Rig::end_transform(Widgets &widgets, Undo &undo, Scene_Object &obj) {
if(root_selected) undo.move_root(obj.id(), old_ext); if (root_selected)
else undo.move_bone(obj.id(), selected, old_ext); undo.move_root(obj.id(), old_ext);
obj.set_skel_dirty(); else
undo.move_bone(obj.id(), selected, old_ext);
obj.set_skel_dirty();
} }
void Rig::apply_transform(Widgets& widgets) { void Rig::apply_transform(Widgets &widgets) {
if(root_selected) { if (root_selected) {
my_obj->armature.base() = widgets.apply_action(Pose::moved(old_pos)).pos; my_obj->armature.base() = widgets.apply_action(Pose::moved(old_pos)).pos;
my_obj->set_skel_dirty(); my_obj->set_skel_dirty();
} else if(selected) { } else if (selected) {
Vec3 new_pos = widgets.apply_action(Pose::moved(old_pos)).pos; Vec3 new_pos = widgets.apply_action(Pose::moved(old_pos)).pos;
selected->extent = new_pos - old_base; selected->extent = new_pos - old_base;
my_obj->set_skel_dirty(); my_obj->set_skel_dirty();
} }
} }
Vec3 Rig::selected_pos() { Vec3 Rig::selected_pos() {
if(root_selected) { if (root_selected) {
return my_obj->armature.base(); return my_obj->armature.base();
} else { } else {
assert(selected); assert(selected);
return my_obj->armature.end_of(selected); return my_obj->armature.end_of(selected);
} }
} }
void Rig::select(Scene& scene, Widgets& widgets, Undo& undo, Scene_ID id, Vec3 cam, Vec2 spos, Vec3 dir) { void Rig::select(Scene &scene, Widgets &widgets, Undo &undo, Scene_ID id, Vec3 cam, Vec2 spos,
Vec3 dir) {
if(!my_obj) return;
if (!my_obj)
return;
if(creating_bone) { if (creating_bone) {
undo.add_bone(my_obj->id(), new_joint); undo.add_bone(my_obj->id(), new_joint);
selected = new_joint; selected = new_joint;
new_joint = nullptr; new_joint = nullptr;
creating_bone = false; creating_bone = false;
root_selected = false; root_selected = false;
} else if(widgets.want_drag()) { } else if (widgets.want_drag()) {
if(root_selected) { if (root_selected) {
old_pos = my_obj->armature.base(); old_pos = my_obj->armature.base();
} else { } else {
assert(selected); assert(selected);
old_pos = my_obj->armature.end_of(selected); old_pos = my_obj->armature.end_of(selected);
old_base = my_obj->armature.base_of(selected); old_base = my_obj->armature.base_of(selected);
old_ext = selected->extent; old_ext = selected->extent;
} }
widgets.start_drag(old_pos, cam, spos, dir); widgets.start_drag(old_pos, cam, spos, dir);
} else if (!id || id >= n_Widget_IDs) { } else if (!id || id >= n_Widget_IDs) {
selected = my_obj->armature.get_joint(id); selected = my_obj->armature.get_joint(id);
root_selected = my_obj->armature.is_root_id(id); root_selected = my_obj->armature.is_root_id(id);
} }
} }
void Rig::clear() { void Rig::clear() {
my_obj = nullptr; my_obj = nullptr;
selected = nullptr; selected = nullptr;
} }
void Rig::clear_select() { void Rig::clear_select() { selected = nullptr; }
selected = nullptr;
}
void Rig::hover(Vec3 cam, Vec2 spos, Vec3 dir) { void Rig::hover(Vec3 cam, Vec2 spos, Vec3 dir) {
if(creating_bone) { if (creating_bone) {
assert(new_joint); assert(new_joint);
assert(my_obj); assert(my_obj);
Ray f(cam, dir); Ray f(cam, dir);
PT::Trace hit1 = mesh_bvh.hit(f); PT::Trace hit1 = mesh_bvh.hit(f);
if(!hit1.hit) return; if (!hit1.hit)
return;
Ray s(hit1.position + dir * EPS_F, dir); Ray s(hit1.position + dir * EPS_F, dir);
PT::Trace hit2 = mesh_bvh.hit(s); PT::Trace hit2 = mesh_bvh.hit(s);
Vec3 pos = hit1.position; Vec3 pos = hit1.position;
if(hit2.hit) pos = 0.5f * (hit1.position + hit2.position); if (hit2.hit)
pos = 0.5f * (hit1.position + hit2.position);
new_joint->extent = pos - old_base; new_joint->extent = pos - old_base;
my_obj->set_skel_dirty(); my_obj->set_skel_dirty();
} }
} }
Mode Rig::UIsidebar(Manager& manager, Undo& undo, Widgets& widgets, Scene_Maybe obj_opt) { Mode Rig::UIsidebar(Manager &manager, Undo &undo, Widgets &widgets, Scene_Maybe obj_opt) {
if(!my_obj) return Mode::rig; if (!my_obj)
return Mode::rig;
if(!obj_opt.has_value()) return Mode::rig;
if (!obj_opt.has_value())
Scene_Item& item = obj_opt.value(); return Mode::rig;
if(!item.is<Scene_Object>()) return Mode::rig;
Scene_Item &item = obj_opt.value();
Scene_Object& obj = item.get<Scene_Object>(); if (!item.is<Scene_Object>())
if(obj.opt.shape_type != PT::Shape_Type::none) return Mode::rig; return Mode::rig;
if(my_obj != &obj) { Scene_Object &obj = item.get<Scene_Object>();
my_obj = &obj; if (obj.opt.shape_type != PT::Shape_Type::none)
selected = nullptr; return Mode::rig;
}
if(my_obj->rig_dirty) { if (my_obj != &obj) {
mesh_bvh.build(obj.mesh()); my_obj = &obj;
my_obj->rig_dirty = false; selected = nullptr;
} }
if (my_obj->rig_dirty) {
ImGui::Text("Edit Skeleton"); mesh_bvh.build(obj.mesh());
my_obj->rig_dirty = false;
if(creating_bone) { }
if(ImGui::Button("Cancel")) {
creating_bone = false; ImGui::Text("Edit Skeleton");
my_obj->armature.erase(new_joint);
new_joint = nullptr; if (creating_bone) {
my_obj->set_skel_dirty(); if (ImGui::Button("Cancel")) {
} creating_bone = false;
} else if(ImGui::Button("New Bone")) { my_obj->armature.erase(new_joint);
new_joint = nullptr;
creating_bone = true; my_obj->set_skel_dirty();
}
if(!selected || root_selected) { } else if (ImGui::Button("New Bone")) {
new_joint = my_obj->armature.add_root(Vec3{0.0f});
old_base = my_obj->armature.base(); creating_bone = true;
} else {
new_joint = my_obj->armature.add_child(selected, Vec3{0.0f}); if (!selected || root_selected) {
old_base = my_obj->armature.end_of(selected); new_joint = my_obj->armature.add_root(Vec3{0.0f});
} old_base = my_obj->armature.base();
my_obj->set_skel_dirty(); } else {
} new_joint = my_obj->armature.add_child(selected, Vec3{0.0f});
old_base = my_obj->armature.end_of(selected);
if(selected) { }
my_obj->set_skel_dirty();
ImGui::Separator(); }
ImGui::Text("Edit Bone");
if (selected) {
ImGui::DragFloat3("Extent", selected->extent.data, 0.1f);
ImGui::DragFloat("Radius", &selected->radius, 0.05f, 0.0f, std::numeric_limits<float>::max()); ImGui::Separator();
ImGui::Text("Edit Bone");
ImGui::DragFloat3("Pose", selected->pose.data, 0.1f);
ImGui::DragFloat3("Extent", selected->extent.data, 0.1f);
if(ImGui::Button("Delete [del]")) { ImGui::DragFloat("Radius", &selected->radius, 0.05f, 0.0f,
undo.del_bone(my_obj->id(), selected); std::numeric_limits<float>::max());
selected = nullptr;
} ImGui::DragFloat3("Pose", selected->pose.data, 0.1f);
}
if (ImGui::Button("Delete [del]")) {
undo.del_bone(my_obj->id(), selected);
selected = nullptr;
}
}
return Mode::rig; return Mode::rig;
} }
} } // namespace Gui
#pragma once #pragma once
#include <SDL2/SDL.h>
#include "widgets.h"
#include "../rays/tri_mesh.h" #include "../rays/tri_mesh.h"
#include "widgets.h"
#include <SDL2/SDL.h>
namespace Gui { namespace Gui {
...@@ -12,31 +12,32 @@ class Manager; ...@@ -12,31 +12,32 @@ class Manager;
class Rig { class Rig {
public: public:
bool keydown(Widgets& widgets, Undo& undo, SDL_Keysym key); bool keydown(Widgets &widgets, Undo &undo, SDL_Keysym key);
void select(Scene& scene, Widgets& widgets, Undo& undo, Scene_ID id, Vec3 cam, Vec2 spos, Vec3 dir); void select(Scene &scene, Widgets &widgets, Undo &undo, Scene_ID id, Vec3 cam, Vec2 spos,
void hover(Vec3 cam, Vec2 spos, Vec3 dir); Vec3 dir);
void hover(Vec3 cam, Vec2 spos, Vec3 dir);
void end_transform(Widgets& widgets, Undo& undo, Scene_Object& obj); void end_transform(Widgets &widgets, Undo &undo, Scene_Object &obj);
void apply_transform(Widgets& widgets); void apply_transform(Widgets &widgets);
Vec3 selected_pos(); Vec3 selected_pos();
void clear_select(); void clear_select();
void clear(); void clear();
void invalidate(Joint* j); void invalidate(Joint *j);
void render(Scene_Maybe obj_opt, Widgets& widgets, Camera& cam); void render(Scene_Maybe obj_opt, Widgets &widgets, Camera &cam);
Mode UIsidebar(Manager& manager, Undo& undo, Widgets& widgets, Scene_Maybe obj); Mode UIsidebar(Manager &manager, Undo &undo, Widgets &widgets, Scene_Maybe obj);
private: private:
bool creating_bone = false; bool creating_bone = false;
bool root_selected = false; bool root_selected = false;
Vec3 old_pos, old_base, old_ext; Vec3 old_pos, old_base, old_ext;
Scene_Object* my_obj = nullptr; Scene_Object *my_obj = nullptr;
Joint* selected = nullptr; Joint *selected = nullptr;
Joint* new_joint = nullptr; Joint *new_joint = nullptr;
PT::Tri_Mesh mesh_bvh; PT::Tri_Mesh mesh_bvh;
}; };
} } // namespace Gui
#include <imgui/imgui.h> #include <imgui/imgui.h>
#include <iomanip>
#include <iostream>
#include <nfd/nfd.h> #include <nfd/nfd.h>
#include <sf_libs/stb_image_write.h> #include <sf_libs/stb_image_write.h>
#include <iostream>
#include <iomanip>
#include <sstream> #include <sstream>
#include "animate.h"
#include "manager.h" #include "manager.h"
#include "widgets.h" #include "widgets.h"
#include "animate.h"
#include "../geometry/util.h"
#include "../platform/platform.h" #include "../platform/platform.h"
#include "../scene/renderer.h" #include "../scene/renderer.h"
#include "../geometry/util.h"
namespace Gui { namespace Gui {
Widgets::Widgets() : lines(1.0f) { Widgets::Widgets() : lines(1.0f) {
x_mov = Scene_Object((Scene_ID)Widget_IDs::x_mov, Pose::rotated(Vec3{0.0f, 0.0f, -90.0f}), Util::arrow_mesh(0.03f, 0.075f, 1.0f)); x_mov = Scene_Object((Scene_ID)Widget_IDs::x_mov, Pose::rotated(Vec3{0.0f, 0.0f, -90.0f}),
y_mov = Scene_Object((Scene_ID)Widget_IDs::y_mov, {}, Util::arrow_mesh(0.03f, 0.075f, 1.0f)); Util::arrow_mesh(0.03f, 0.075f, 1.0f));
z_mov = Scene_Object((Scene_ID)Widget_IDs::z_mov, Pose::rotated(Vec3{90.0f, 0.0f, 0.0f}), Util::arrow_mesh(0.03f, 0.075f, 1.0f)); y_mov = Scene_Object((Scene_ID)Widget_IDs::y_mov, {}, Util::arrow_mesh(0.03f, 0.075f, 1.0f));
z_mov = Scene_Object((Scene_ID)Widget_IDs::z_mov, Pose::rotated(Vec3{90.0f, 0.0f, 0.0f}),
xy_mov = Scene_Object((Scene_ID)Widget_IDs::xy_mov, Pose::rotated(Vec3{-90.0f, 0.0f, 0.0f}), Util::square_mesh(0.1f)); Util::arrow_mesh(0.03f, 0.075f, 1.0f));
yz_mov = Scene_Object((Scene_ID)Widget_IDs::yz_mov, Pose::rotated(Vec3{0.0f, 0.0f, -90.0f}), Util::square_mesh(0.1f));
xz_mov = Scene_Object((Scene_ID)Widget_IDs::xz_mov, {}, Util::square_mesh(0.1f)); xy_mov = Scene_Object((Scene_ID)Widget_IDs::xy_mov, Pose::rotated(Vec3{-90.0f, 0.0f, 0.0f}),
Util::square_mesh(0.1f));
x_rot = Scene_Object((Scene_ID)Widget_IDs::x_rot, Pose::rotated(Vec3{0.0f, 0.0f, -90.0f}), Util::torus_mesh(0.975f, 1.0f)); yz_mov = Scene_Object((Scene_ID)Widget_IDs::yz_mov, Pose::rotated(Vec3{0.0f, 0.0f, -90.0f}),
y_rot = Scene_Object((Scene_ID)Widget_IDs::y_rot, {}, Util::torus_mesh(0.975f, 1.0f)); Util::square_mesh(0.1f));
z_rot = Scene_Object((Scene_ID)Widget_IDs::z_rot, Pose::rotated(Vec3{90.0f, 0.0f, 0.0f}), Util::torus_mesh(0.975f, 1.0f)); xz_mov = Scene_Object((Scene_ID)Widget_IDs::xz_mov, {}, Util::square_mesh(0.1f));
x_scl = Scene_Object((Scene_ID)Widget_IDs::x_scl, Pose::rotated(Vec3{0.0f, 0.0f, -90.0f}), Util::scale_mesh()); x_rot = Scene_Object((Scene_ID)Widget_IDs::x_rot, Pose::rotated(Vec3{0.0f, 0.0f, -90.0f}),
y_scl = Scene_Object((Scene_ID)Widget_IDs::y_scl, {}, Util::scale_mesh()); Util::torus_mesh(0.975f, 1.0f));
z_scl = Scene_Object((Scene_ID)Widget_IDs::z_scl, Pose::rotated(Vec3{90.0f, 0.0f, 0.0f}), Util::scale_mesh()); y_rot = Scene_Object((Scene_ID)Widget_IDs::y_rot, {}, Util::torus_mesh(0.975f, 1.0f));
z_rot = Scene_Object((Scene_ID)Widget_IDs::z_rot, Pose::rotated(Vec3{90.0f, 0.0f, 0.0f}),
#define setcolor(o,c) o.material.opt.albedo = Spectrum((c).x,(c).y,(c).z); Util::torus_mesh(0.975f, 1.0f));
setcolor(x_mov, Color::red);
setcolor(y_mov, Color::green); x_scl = Scene_Object((Scene_ID)Widget_IDs::x_scl, Pose::rotated(Vec3{0.0f, 0.0f, -90.0f}),
setcolor(z_mov, Color::blue); Util::scale_mesh());
setcolor(xy_mov, Color::blue); y_scl = Scene_Object((Scene_ID)Widget_IDs::y_scl, {}, Util::scale_mesh());
setcolor(yz_mov, Color::red); z_scl = Scene_Object((Scene_ID)Widget_IDs::z_scl, Pose::rotated(Vec3{90.0f, 0.0f, 0.0f}),
setcolor(xz_mov, Color::green); Util::scale_mesh());
setcolor(x_rot, Color::red);
setcolor(y_rot, Color::green); #define setcolor(o, c) o.material.opt.albedo = Spectrum((c).x, (c).y, (c).z);
setcolor(z_rot, Color::blue); setcolor(x_mov, Color::red);
setcolor(x_scl, Color::red); setcolor(y_mov, Color::green);
setcolor(y_scl, Color::green); setcolor(z_mov, Color::blue);
setcolor(z_scl, Color::blue); setcolor(xy_mov, Color::blue);
setcolor(yz_mov, Color::red);
setcolor(xz_mov, Color::green);
setcolor(x_rot, Color::red);
setcolor(y_rot, Color::green);
setcolor(z_rot, Color::blue);
setcolor(x_scl, Color::red);
setcolor(y_scl, Color::green);
setcolor(z_scl, Color::blue);
#undef setcolor #undef setcolor
} }
void Widgets::generate_lines(Vec3 pos) { void Widgets::generate_lines(Vec3 pos) {
auto add_axis = [&](int axis) { auto add_axis = [&](int axis) {
Vec3 start = pos; start[axis] -= 10000.0f; Vec3 start = pos;
Vec3 end = pos; end[axis] += 10000.0f; start[axis] -= 10000.0f;
Vec3 color = Color::axis((Axis)axis); Vec3 end = pos;
lines.add(start, end, color); end[axis] += 10000.0f;
}; Vec3 color = Color::axis((Axis)axis);
if(drag_plane) { lines.add(start, end, color);
add_axis(((int)axis + 1) % 3); };
add_axis(((int)axis + 2) % 3); if (drag_plane) {
} else { add_axis(((int)axis + 1) % 3);
add_axis((int)axis); add_axis(((int)axis + 2) % 3);
} } else {
add_axis((int)axis);
}
} }
bool Widgets::action_button(Widget_Type act, std::string name, bool wrap) { bool Widgets::action_button(Widget_Type act, std::string name, bool wrap) {
bool is_active = act == active; bool is_active = act == active;
if(is_active) ImGui::PushStyleColor(ImGuiCol_Button, ImGui::GetColorU32(ImGuiCol_ButtonActive)); if (is_active)
bool clicked = wrap ? Manager::wrap_button(name) : ImGui::Button(name.c_str()); ImGui::PushStyleColor(ImGuiCol_Button, ImGui::GetColorU32(ImGuiCol_ButtonActive));
if(is_active) ImGui::PopStyleColor(); bool clicked = wrap ? Manager::wrap_button(name) : ImGui::Button(name.c_str());
if(clicked) active = act; if (is_active)
return clicked; ImGui::PopStyleColor();
if (clicked)
active = act;
return clicked;
}; };
void Widgets::render(const Mat4& view, Vec3 pos, float scl) { void Widgets::render(const Mat4 &view, Vec3 pos, float scl) {
Renderer& r = Renderer::get();
r.reset_depth();
Vec3 scale(scl);
r.lines(lines, view, Mat4::I, 0.5f);
if(dragging && (active == Widget_Type::move || active == Widget_Type::scale)) return;
if(active == Widget_Type::move) {
x_mov.pose.scale = scale;
x_mov.pose.pos = pos + Vec3(0.15f * scl, 0.0f, 0.0f);
x_mov.render(view, true);
y_mov.pose.scale = scale;
y_mov.pose.pos = pos + Vec3(0.0f, 0.15f * scl, 0.0f);
y_mov.render(view, true);
z_mov.pose.scale = scale;
z_mov.pose.pos = pos + Vec3(0.0f, 0.0f, 0.15f * scl);
z_mov.render(view, true);
xy_mov.pose.scale = scale;
xy_mov.pose.pos = pos + Vec3(0.45f * scl, 0.45f * scl, 0.0f);
xy_mov.render(view, true);
yz_mov.pose.scale = scale;
yz_mov.pose.pos = pos + Vec3(0.0f, 0.45f * scl, 0.45f * scl);
yz_mov.render(view, true);
xz_mov.pose.scale = scale;
xz_mov.pose.pos = pos + Vec3(0.45f * scl, 0.0f, 0.45f * scl);
xz_mov.render(view, true);
} else if(active == Widget_Type::rotate) {
if(!dragging || axis == Axis::X) {
x_rot.pose.scale = scale;
x_rot.pose.pos = pos;
x_rot.render(view, true);
}
if(!dragging || axis == Axis::Y) {
y_rot.pose.scale = scale;
y_rot.pose.pos = pos;
y_rot.render(view, true);
}
if(!dragging || axis == Axis::Z) {
z_rot.pose.scale = scale;
z_rot.pose.pos = pos;
z_rot.render(view, true);
}
} else if(active == Widget_Type::scale) {
x_scl.pose.scale = scale;
x_scl.pose.pos = pos + Vec3(0.15f * scl, 0.0f, 0.0f);
x_scl.render(view, true);
y_scl.pose.scale = scale;
y_scl.pose.pos = pos + Vec3(0.0f, 0.15f * scl, 0.0f);
y_scl.render(view, true);
z_scl.pose.scale = scale;
z_scl.pose.pos = pos + Vec3(0.0f, 0.0f, 0.15f * scl);
z_scl.render(view, true);
}
}
Pose Widgets::apply_action(const Pose& pose) { Renderer &r = Renderer::get();
r.reset_depth();
Pose result = pose;
Vec3 vaxis; Vec3 scale(scl);
vaxis[(int)axis] = 1.0f; r.lines(lines, view, Mat4::I, 0.5f);
switch(active) { if (dragging && (active == Widget_Type::move || active == Widget_Type::scale))
case Widget_Type::move: { return;
result.pos = pose.pos + drag_end - drag_start;
} break; if (active == Widget_Type::move) {
case Widget_Type::rotate: {
Quat rot = Quat::axis_angle(vaxis, drag_end[(int)axis]); x_mov.pose.scale = scale;
Quat combined = rot * pose.rotation_quat(); x_mov.pose.pos = pos + Vec3(0.15f * scl, 0.0f, 0.0f);
result.euler = combined.to_euler(); x_mov.render(view, true);
} break;
case Widget_Type::scale: { y_mov.pose.scale = scale;
result.scale = Vec3{1.0f}; y_mov.pose.pos = pos + Vec3(0.0f, 0.15f * scl, 0.0f);
result.scale[(int)axis] = drag_end[(int)axis]; y_mov.render(view, true);
Mat4 rot = pose.rotation_mat();
Mat4 trans = Mat4::transpose(rot) * Mat4::scale(result.scale) * rot * Mat4::scale(pose.scale); z_mov.pose.scale = scale;
result.scale = Vec3(trans[0][0], trans[1][1], trans[2][2]); z_mov.pose.pos = pos + Vec3(0.0f, 0.0f, 0.15f * scl);
} break; z_mov.render(view, true);
case Widget_Type::bevel: {
Vec2 off = bevel_start - bevel_end; xy_mov.pose.scale = scale;
result.pos = 2.0f * Vec3(off.x, -off.y, 0.0f); xy_mov.pose.pos = pos + Vec3(0.45f * scl, 0.45f * scl, 0.0f);
} break; xy_mov.render(view, true);
default: assert(false);
} yz_mov.pose.scale = scale;
yz_mov.pose.pos = pos + Vec3(0.0f, 0.45f * scl, 0.45f * scl);
return result; yz_mov.render(view, true);
}
xz_mov.pose.scale = scale;
xz_mov.pose.pos = pos + Vec3(0.45f * scl, 0.0f, 0.45f * scl);
xz_mov.render(view, true);
} else if (active == Widget_Type::rotate) {
if (!dragging || axis == Axis::X) {
x_rot.pose.scale = scale;
x_rot.pose.pos = pos;
x_rot.render(view, true);
}
if (!dragging || axis == Axis::Y) {
y_rot.pose.scale = scale;
y_rot.pose.pos = pos;
y_rot.render(view, true);
}
if (!dragging || axis == Axis::Z) {
z_rot.pose.scale = scale;
z_rot.pose.pos = pos;
z_rot.render(view, true);
}
bool Widgets::to_axis(Vec3 obj_pos, Vec3 cam_pos, Vec3 dir, Vec3& hit) { } else if (active == Widget_Type::scale) {
Vec3 axis1; axis1[(int)axis] = 1.0f; x_scl.pose.scale = scale;
Vec3 axis2; axis2[((int)axis + 1) % 3] = 1.0f; x_scl.pose.pos = pos + Vec3(0.15f * scl, 0.0f, 0.0f);
Vec3 axis3; axis3[((int)axis + 2) % 3] = 1.0f; x_scl.render(view, true);
Line select(cam_pos, dir); y_scl.pose.scale = scale;
Line target(obj_pos, axis1); y_scl.pose.pos = pos + Vec3(0.0f, 0.15f * scl, 0.0f);
Plane l(obj_pos, axis2); y_scl.render(view, true);
Plane r(obj_pos, axis3);
z_scl.pose.scale = scale;
Vec3 hit1, hit2; z_scl.pose.pos = pos + Vec3(0.0f, 0.0f, 0.15f * scl);
bool hl = l.hit(select, hit1); z_scl.render(view, true);
bool hr = r.hit(select, hit2); }
if(!hl && !hr) return false;
else if(!hl) hit = hit2;
else if(!hr) hit = hit1;
else hit = (hit1 - cam_pos).norm() > (hit2 - cam_pos).norm() ? hit2 : hit1;
hit = target.closest(hit);
return hit.valid();
} }
bool Widgets::to_plane(Vec3 obj_pos, Vec3 cam_pos, Vec3 dir, Vec3 norm, Vec3& hit) { Pose Widgets::apply_action(const Pose &pose) {
Pose result = pose;
Vec3 vaxis;
vaxis[(int)axis] = 1.0f;
switch (active) {
case Widget_Type::move: {
result.pos = pose.pos + drag_end - drag_start;
} break;
case Widget_Type::rotate: {
Quat rot = Quat::axis_angle(vaxis, drag_end[(int)axis]);
Quat combined = rot * pose.rotation_quat();
result.euler = combined.to_euler();
} break;
case Widget_Type::scale: {
result.scale = Vec3{1.0f};
result.scale[(int)axis] = drag_end[(int)axis];
Mat4 rot = pose.rotation_mat();
Mat4 trans =
Mat4::transpose(rot) * Mat4::scale(result.scale) * rot * Mat4::scale(pose.scale);
result.scale = Vec3(trans[0][0], trans[1][1], trans[2][2]);
} break;
case Widget_Type::bevel: {
Vec2 off = bevel_start - bevel_end;
result.pos = 2.0f * Vec3(off.x, -off.y, 0.0f);
} break;
default:
assert(false);
}
Line look(cam_pos, dir); return result;
Plane p(obj_pos, norm);
return p.hit(look, hit);
} }
bool Widgets::is_dragging() { bool Widgets::to_axis(Vec3 obj_pos, Vec3 cam_pos, Vec3 dir, Vec3 &hit) {
return dragging;
Vec3 axis1;
axis1[(int)axis] = 1.0f;
Vec3 axis2;
axis2[((int)axis + 1) % 3] = 1.0f;
Vec3 axis3;
axis3[((int)axis + 2) % 3] = 1.0f;
Line select(cam_pos, dir);
Line target(obj_pos, axis1);
Plane l(obj_pos, axis2);
Plane r(obj_pos, axis3);
Vec3 hit1, hit2;
bool hl = l.hit(select, hit1);
bool hr = r.hit(select, hit2);
if (!hl && !hr)
return false;
else if (!hl)
hit = hit2;
else if (!hr)
hit = hit1;
else
hit = (hit1 - cam_pos).norm() > (hit2 - cam_pos).norm() ? hit2 : hit1;
hit = target.closest(hit);
return hit.valid();
} }
bool Widgets::want_drag() { bool Widgets::to_plane(Vec3 obj_pos, Vec3 cam_pos, Vec3 dir, Vec3 norm, Vec3 &hit) {
return start_dragging;
Line look(cam_pos, dir);
Plane p(obj_pos, norm);
return p.hit(look, hit);
} }
bool Widgets::is_dragging() { return dragging; }
bool Widgets::want_drag() { return start_dragging; }
void Widgets::set_dragging(bool drag, bool plane) { void Widgets::set_dragging(bool drag, bool plane) {
dragging = drag; dragging = drag;
drag_plane = plane; drag_plane = plane;
} }
void Widgets::start_drag(Vec3 pos, Vec3 cam, Vec2 spos, Vec3 dir) { void Widgets::start_drag(Vec3 pos, Vec3 cam, Vec2 spos, Vec3 dir) {
start_dragging = false;
dragging = true;
Vec3 hit; start_dragging = false;
Vec3 norm; norm[(int)axis] = 1.0f; dragging = true;
Vec3 hit;
Vec3 norm;
norm[(int)axis] = 1.0f;
if(active == Widget_Type::rotate) { if (active == Widget_Type::rotate) {
if(to_plane(pos, cam, dir, norm, hit)) { if (to_plane(pos, cam, dir, norm, hit)) {
drag_start = (hit - pos).unit(); drag_start = (hit - pos).unit();
drag_end = Vec3{0.0f}; drag_end = Vec3{0.0f};
} }
} else { } else {
bool good; bool good;
if(drag_plane) good = to_plane(pos, cam, dir, norm, hit); if (drag_plane)
else good = to_axis(pos, cam, dir, hit); good = to_plane(pos, cam, dir, norm, hit);
else
good = to_axis(pos, cam, dir, hit);
if(!good) return; if (!good)
return;
if(active == Widget_Type::bevel) { if (active == Widget_Type::bevel) {
bevel_start = bevel_end = spos; bevel_start = bevel_end = spos;
} if(active == Widget_Type::move) { }
drag_start = drag_end = hit; if (active == Widget_Type::move) {
} else { drag_start = drag_end = hit;
drag_start = hit; } else {
drag_end = Vec3{1.0f}; drag_start = hit;
} drag_end = Vec3{1.0f};
}
if(active != Widget_Type::bevel) if (active != Widget_Type::bevel)
generate_lines(pos); generate_lines(pos);
} }
} }
void Widgets::end_drag() { void Widgets::end_drag() {
lines.clear(); lines.clear();
drag_start = drag_end = {}; drag_start = drag_end = {};
bevel_start = bevel_end = {}; bevel_start = bevel_end = {};
dragging = false; dragging = false;
drag_plane = false; drag_plane = false;
} }
void Widgets::drag_to(Vec3 pos, Vec3 cam, Vec2 spos, Vec3 dir, bool scale_invert) { void Widgets::drag_to(Vec3 pos, Vec3 cam, Vec2 spos, Vec3 dir, bool scale_invert) {
Vec3 hit; Vec3 hit;
Vec3 norm; norm[(int)axis] = 1.0f; Vec3 norm;
norm[(int)axis] = 1.0f;
if (active == Widget_Type::bevel) {
if(active == Widget_Type::bevel) { bevel_end = spos;
bevel_end = spos;
} else if(active == Widget_Type::rotate) { } else if (active == Widget_Type::rotate) {
if(!to_plane(pos, cam, dir, norm, hit)) return;
Vec3 ang = (hit - pos).unit(); if (!to_plane(pos, cam, dir, norm, hit))
float sgn = sign(cross(drag_start, ang)[(int)axis]); return;
drag_end = Vec3{};
drag_end[(int)axis] = sgn * Degrees(std::acos(dot(drag_start, ang)));
} else { Vec3 ang = (hit - pos).unit();
float sgn = sign(cross(drag_start, ang)[(int)axis]);
drag_end = Vec3{};
drag_end[(int)axis] = sgn * Degrees(std::acos(dot(drag_start, ang)));
} else {
bool good; bool good;
if(drag_plane) good = to_plane(pos, cam, dir, norm, hit);
else good = to_axis(pos, cam, dir, hit);
if(!good) return; if (drag_plane)
good = to_plane(pos, cam, dir, norm, hit);
else
good = to_axis(pos, cam, dir, hit);
if(active == Widget_Type::move) { if (!good)
drag_end = hit; return;
} else if(active == Widget_Type::scale) {
drag_end = Vec3{1.0f};
drag_end[(int)axis] = (hit - pos).norm() / (drag_start - pos).norm();
} else assert(false);
}
if(scale_invert && active == Widget_Type::scale) { if (active == Widget_Type::move) {
drag_end[(int)axis] *= sign(dot(hit - pos, drag_start - pos)); drag_end = hit;
} } else if (active == Widget_Type::scale) {
drag_end = Vec3{1.0f};
drag_end[(int)axis] = (hit - pos).norm() / (drag_start - pos).norm();
} else
assert(false);
}
if (scale_invert && active == Widget_Type::scale) {
drag_end[(int)axis] *= sign(dot(hit - pos, drag_start - pos));
}
} }
void Widgets::select(Scene_ID id) { void Widgets::select(Scene_ID id) {
start_dragging = true; start_dragging = true;
drag_plane = false; drag_plane = false;
switch(id) { switch (id) {
case (Scene_ID)Widget_IDs::x_mov: { case (Scene_ID)Widget_IDs::x_mov: {
active = Widget_Type::move; active = Widget_Type::move;
axis = Axis::X; axis = Axis::X;
} break; } break;
case (Scene_ID)Widget_IDs::y_mov: { case (Scene_ID)Widget_IDs::y_mov: {
active = Widget_Type::move; active = Widget_Type::move;
axis = Axis::Y; axis = Axis::Y;
} break; } break;
case (Scene_ID)Widget_IDs::z_mov: { case (Scene_ID)Widget_IDs::z_mov: {
active = Widget_Type::move; active = Widget_Type::move;
axis = Axis::Z; axis = Axis::Z;
} break; } break;
case (Scene_ID)Widget_IDs::xy_mov: { case (Scene_ID)Widget_IDs::xy_mov: {
active = Widget_Type::move; active = Widget_Type::move;
axis = Axis::Z; axis = Axis::Z;
drag_plane = true; drag_plane = true;
} break; } break;
case (Scene_ID)Widget_IDs::yz_mov: { case (Scene_ID)Widget_IDs::yz_mov: {
active = Widget_Type::move; active = Widget_Type::move;
axis = Axis::X; axis = Axis::X;
drag_plane = true; drag_plane = true;
} break; } break;
case (Scene_ID)Widget_IDs::xz_mov: { case (Scene_ID)Widget_IDs::xz_mov: {
active = Widget_Type::move; active = Widget_Type::move;
axis = Axis::Y; axis = Axis::Y;
drag_plane = true; drag_plane = true;
} break; } break;
case (Scene_ID)Widget_IDs::x_rot: { case (Scene_ID)Widget_IDs::x_rot: {
active = Widget_Type::rotate; active = Widget_Type::rotate;
axis = Axis::X; axis = Axis::X;
} break; } break;
case (Scene_ID)Widget_IDs::y_rot: { case (Scene_ID)Widget_IDs::y_rot: {
active = Widget_Type::rotate; active = Widget_Type::rotate;
axis = Axis::Y; axis = Axis::Y;
} break; } break;
case (Scene_ID)Widget_IDs::z_rot: { case (Scene_ID)Widget_IDs::z_rot: {
active = Widget_Type::rotate; active = Widget_Type::rotate;
axis = Axis::Z; axis = Axis::Z;
} break; } break;
case (Scene_ID)Widget_IDs::x_scl: { case (Scene_ID)Widget_IDs::x_scl: {
active = Widget_Type::scale; active = Widget_Type::scale;
axis = Axis::X; axis = Axis::X;
} break; } break;
case (Scene_ID)Widget_IDs::y_scl: { case (Scene_ID)Widget_IDs::y_scl: {
active = Widget_Type::scale; active = Widget_Type::scale;
axis = Axis::Y; axis = Axis::Y;
} break; } break;
case (Scene_ID)Widget_IDs::z_scl: { case (Scene_ID)Widget_IDs::z_scl: {
active = Widget_Type::scale; active = Widget_Type::scale;
axis = Axis::Z; axis = Axis::Z;
} break; } break;
default: { default: {
start_dragging = false; start_dragging = false;
} break; } break;
} }
} }
void Widget_Camera::ar(Camera& user_cam, float _ar) { void Widget_Camera::ar(Camera &user_cam, float _ar) {
cam_ar = _ar; cam_ar = _ar;
update_cameras(user_cam); update_cameras(user_cam);
} }
bool Widget_Camera::UI(Undo& undo, Camera& user_cam) { bool Widget_Camera::UI(Undo &undo, Camera &user_cam) {
bool update_cam = false; bool update_cam = false;
bool do_undo = false; bool do_undo = false;
static Camera old = render_cam; static Camera old = render_cam;
static float old_ar, old_fov; static float old_ar, old_fov;
ImGui::Text("Camera Settings"); ImGui::Text("Camera Settings");
if(moving_camera) { if (moving_camera) {
if(ImGui::Button("Confirm Move")) { if (ImGui::Button("Confirm Move")) {
moving_camera = false; moving_camera = false;
old = render_cam; old = render_cam;
render_cam = user_cam; render_cam = user_cam;
user_cam.set_ar(screen_dim); user_cam.set_ar(screen_dim);
user_cam.set_fov(90.0f); user_cam.set_fov(90.0f);
update_cam = true; update_cam = true;
do_undo = true; do_undo = true;
} }
ImGui::SameLine(); ImGui::SameLine();
if(ImGui::Button("Cancel Move")) { if (ImGui::Button("Cancel Move")) {
moving_camera = false; moving_camera = false;
user_cam = saved_cam; user_cam = saved_cam;
user_cam.set_ar(screen_dim); user_cam.set_ar(screen_dim);
user_cam.set_fov(90.0f); user_cam.set_fov(90.0f);
} }
} else { } else {
if(ImGui::Button("Free Move")) { if (ImGui::Button("Free Move")) {
moving_camera = true; moving_camera = true;
user_cam = render_cam; user_cam = render_cam;
saved_cam = render_cam; saved_cam = render_cam;
} }
ImGui::SameLine(); ImGui::SameLine();
if(ImGui::Button("Move to View")) { if (ImGui::Button("Move to View")) {
old = render_cam; old = render_cam;
render_cam = user_cam; render_cam = user_cam;
update_cam = true; update_cam = true;
do_undo = true; do_undo = true;
cam_fov = user_cam.get_fov(); cam_fov = user_cam.get_fov();
cam_ar = user_cam.get_ar(); cam_ar = user_cam.get_ar();
} }
} }
if(ImGui::Button("Reset")) { if (ImGui::Button("Reset")) {
old = render_cam; old = render_cam;
render_cam.reset(); render_cam.reset();
update_cam = true; update_cam = true;
do_undo = true; do_undo = true;
undo.update_camera(*this, old); undo.update_camera(*this, old);
} }
update_cam |= ImGui::DragFloat("Aspect Ratio", &cam_ar, 0.1f, 0.1f, 10.0f, "%.2f"); update_cam |= ImGui::DragFloat("Aspect Ratio", &cam_ar, 0.1f, 0.1f, 10.0f, "%.2f");
if(ImGui::IsItemActivated()) { if (ImGui::IsItemActivated()) {
old = render_cam; old = render_cam;
old_ar = cam_ar; old_ar = cam_ar;
} }
if(ImGui::IsItemDeactivated() && old_ar != cam_ar) do_undo = true; if (ImGui::IsItemDeactivated() && old_ar != cam_ar)
do_undo = true;
update_cam |= ImGui::DragFloat("FOV", &cam_fov, 1.0f, 10.0f, 160.0f, "%.2f"); update_cam |= ImGui::DragFloat("FOV", &cam_fov, 1.0f, 10.0f, 160.0f, "%.2f");
if(ImGui::IsItemActivated()) { if (ImGui::IsItemActivated()) {
old = render_cam; old = render_cam;
old_fov = cam_fov; old_fov = cam_fov;
} }
if(ImGui::IsItemDeactivated() && old_fov != cam_fov) do_undo = true; if (ImGui::IsItemDeactivated() && old_fov != cam_fov)
do_undo = true;
cam_ar = clamp(cam_ar, 0.1f, 10.0f); cam_ar = clamp(cam_ar, 0.1f, 10.0f);
cam_fov = clamp(cam_fov, 10.0f, 160.0f); cam_fov = clamp(cam_fov, 10.0f, 160.0f);
if(update_cam) if (update_cam)
update_cameras(user_cam); update_cameras(user_cam);
if(do_undo) if (do_undo)
undo.update_camera(*this, old); undo.update_camera(*this, old);
return update_cam; return update_cam;
} }
void Widget_Camera::update_cameras(Camera& user_cam) { void Widget_Camera::update_cameras(Camera &user_cam) {
render_cam.set_ar(cam_ar); render_cam.set_ar(cam_ar);
render_cam.set_fov(cam_fov); render_cam.set_fov(cam_fov);
if(moving_camera) { if (moving_camera) {
user_cam.set_ar(cam_ar); user_cam.set_ar(cam_ar);
user_cam.set_fov(cam_fov); user_cam.set_fov(cam_fov);
} }
...@@ -462,16 +493,17 @@ void Widget_Camera::update_cameras(Camera& user_cam) { ...@@ -462,16 +493,17 @@ void Widget_Camera::update_cameras(Camera& user_cam) {
} }
void Widget_Camera::load(Vec3 center, Vec3 pos, float ar, float vfov) { void Widget_Camera::load(Vec3 center, Vec3 pos, float ar, float vfov) {
render_cam.look_at(center, pos); render_cam.look_at(center, pos);
render_cam.set_ar(ar); render_cam.set_ar(ar);
render_cam.set_fov(vfov); render_cam.set_fov(vfov);
cam_fov = vfov; cam_fov = vfov;
cam_ar = ar; cam_ar = ar;
generate_cage(); generate_cage();
} }
void Widget_Camera::render(const Mat4& view) { void Widget_Camera::render(const Mat4 &view) {
if(!moving_camera) Renderer::get().lines(cam_cage, view); if (!moving_camera)
Renderer::get().lines(cam_cage, view);
} }
void Widget_Camera::generate_cage() { void Widget_Camera::generate_cage() {
...@@ -481,7 +513,7 @@ void Widget_Camera::generate_cage() { ...@@ -481,7 +513,7 @@ void Widget_Camera::generate_cage() {
float fov = render_cam.get_fov(); float fov = render_cam.get_fov();
float h = 2.0f * std::tan(Radians(fov) / 2.0f); float h = 2.0f * std::tan(Radians(fov) / 2.0f);
float w = ar * h; float w = ar * h;
Vec3 pos = render_cam.pos(); Vec3 pos = render_cam.pos();
Mat4 iview = render_cam.get_view().inverse(); Mat4 iview = render_cam.get_view().inverse();
...@@ -500,44 +532,43 @@ void Widget_Camera::generate_cage() { ...@@ -500,44 +532,43 @@ void Widget_Camera::generate_cage() {
cam_cage.add(br, bl, Gui::Color::black); cam_cage.add(br, bl, Gui::Color::black);
} }
Widget_Render::Widget_Render(Vec2 dim) Widget_Render::Widget_Render(Vec2 dim) : pathtracer(*this, dim) {
: pathtracer(*this, dim) {
out_w = (size_t)dim.x / 2; out_w = (size_t)dim.x / 2;
out_h = (size_t)dim.y / 2; out_h = (size_t)dim.y / 2;
} }
void Widget_Render::open() { void Widget_Render::open() {
render_window = true; render_window = true;
render_window_focus = true; render_window_focus = true;
} }
void Widget_Render::log_ray(const Ray& ray, float t, Spectrum color) { void Widget_Render::log_ray(const Ray &ray, float t, Spectrum color) {
std::lock_guard<std::mutex> lock(log_mut); std::lock_guard<std::mutex> lock(log_mut);
ray_log.add(ray.point, ray.at(t), Vec3(color.r, color.g, color.b)); ray_log.add(ray.point, ray.at(t), Vec3(color.r, color.g, color.b));
} }
void Widget_Render::begin(Scene& scene, Widget_Camera& cam, Camera& user_cam) { void Widget_Render::begin(Scene &scene, Widget_Camera &cam, Camera &user_cam) {
if(render_window_focus) { if (render_window_focus) {
ImGui::SetNextWindowFocus(); ImGui::SetNextWindowFocus();
render_window_focus = false; render_window_focus = false;
} }
ImGui::Begin("Render Image", &render_window, ImGuiWindowFlags_NoCollapse); ImGui::Begin("Render Image", &render_window, ImGuiWindowFlags_NoCollapse);
static const char* method_names[] = {"Rasterize", "Path Trace"}; static const char *method_names[] = {"Rasterize", "Path Trace"};
ImGui::Combo("Method", &method, method_names, 2); ImGui::Combo("Method", &method, method_names, 2);
ImGui::InputInt("Width", &out_w, 1, 100); ImGui::InputInt("Width", &out_w, 1, 100);
ImGui::InputInt("Height", &out_h, 1, 100); ImGui::InputInt("Height", &out_h, 1, 100);
ImGui::InputInt("Samples", &out_samples, 1, 100); ImGui::InputInt("Samples", &out_samples, 1, 100);
if(method == 1) { if (method == 1) {
ImGui::InputInt("Area Light Samples", &out_area_samples, 1, 100); ImGui::InputInt("Area Light Samples", &out_area_samples, 1, 100);
ImGui::InputInt("Max Ray Depth", &out_depth, 1, 32); ImGui::InputInt("Max Ray Depth", &out_depth, 1, 32);
ImGui::SliderFloat("Exposure", &exposure, 0.01f, 10.0f, "%.2f", 2.5f); ImGui::SliderFloat("Exposure", &exposure, 0.01f, 10.0f, "%.2f", 2.5f);
} else { } else {
out_samples = std::min(out_samples, 32); out_samples = std::min(out_samples, 32);
} }
out_w = std::max(1, out_w); out_w = std::max(1, out_w);
out_h = std::max(1, out_h); out_h = std::max(1, out_h);
...@@ -545,151 +576,157 @@ void Widget_Render::begin(Scene& scene, Widget_Camera& cam, Camera& user_cam) { ...@@ -545,151 +576,157 @@ void Widget_Render::begin(Scene& scene, Widget_Camera& cam, Camera& user_cam) {
out_area_samples = std::max(1, out_area_samples); out_area_samples = std::max(1, out_area_samples);
out_depth = std::max(1, out_depth); out_depth = std::max(1, out_depth);
if(ImGui::Button("Set Width via AR")) { if (ImGui::Button("Set Width via AR")) {
out_w = (size_t)std::ceil(cam.get_ar() * out_h); out_w = (size_t)std::ceil(cam.get_ar() * out_h);
} }
ImGui::SameLine(); ImGui::SameLine();
if(ImGui::Button("Set AR via W/H")) { if (ImGui::Button("Set AR via W/H")) {
cam.ar(user_cam, (float)out_w / (float)out_h); cam.ar(user_cam, (float)out_w / (float)out_h);
} }
} }
std::string Widget_Render::step(Animate& animate, Scene& scene) { std::string Widget_Render::step(Animate &animate, Scene &scene) {
if(animating) { if (animating) {
if(next_frame == max_frame) { if (next_frame == max_frame) {
animating = false; animating = false;
return {}; return {};
} }
if(folder.empty()) { if (folder.empty()) {
animating = false; animating = false;
return "No output folder!"; return "No output folder!";
} }
Camera cam = animate.set_time(scene, (float)next_frame); Camera cam = animate.set_time(scene, (float)next_frame);
if(method == 0) { if (method == 0) {
std::vector<unsigned char> data; std::vector<unsigned char> data;
Renderer::get().save(scene, cam, out_w, out_h, out_samples); Renderer::get().save(scene, cam, out_w, out_h, out_samples);
Renderer::get().saved(data); Renderer::get().saved(data);
std::stringstream str; std::stringstream str;
str << std::setfill('0') << std::setw(4) << next_frame; str << std::setfill('0') << std::setw(4) << next_frame;
std::string path = folder + "\\" + str.str() + ".png"; std::string path = folder + "\\" + str.str() + ".png";
stbi_flip_vertically_on_write(true); stbi_flip_vertically_on_write(true);
if(!stbi_write_png(path.c_str(), (int)out_w, (int)out_h, 4, data.data(), (int)out_w * 4)) { if (!stbi_write_png(path.c_str(), (int)out_w, (int)out_h, 4, data.data(),
animating = false; (int)out_w * 4)) {
return "Failed to write output!"; animating = false;
} return "Failed to write output!";
}
next_frame++;
} else { next_frame++;
} else {
if(init) {
pathtracer.begin_render(scene, cam); if (init) {
init = false; pathtracer.begin_render(scene, cam);
} init = false;
}
if(!pathtracer.in_progress()) {
std::vector<unsigned char> data; if (!pathtracer.in_progress()) {
std::vector<unsigned char> data;
pathtracer.get_output().tonemap_to(data, exposure);
std::stringstream str; pathtracer.get_output().tonemap_to(data, exposure);
str << std::setfill('0') << std::setw(4) << next_frame; std::stringstream str;
std::string path = folder + "\\" + str.str() + ".png"; str << std::setfill('0') << std::setw(4) << next_frame;
std::string path = folder + "\\" + str.str() + ".png";
stbi_flip_vertically_on_write(false);
if(!stbi_write_png(path.c_str(), (int)out_w, (int)out_h, 4, data.data(), (int)out_w * 4)) { stbi_flip_vertically_on_write(false);
animating = false; if (!stbi_write_png(path.c_str(), (int)out_w, (int)out_h, 4, data.data(),
return "Failed to write output!"; (int)out_w * 4)) {
} animating = false;
return "Failed to write output!";
pathtracer.begin_render(scene, cam, true); }
next_frame++;
} pathtracer.begin_render(scene, cam, true);
} next_frame++;
} }
return {}; }
}
return {};
} }
void Widget_Render::animate(Scene& scene, Widget_Camera& cam, Camera& user_cam, int last_frame) { void Widget_Render::animate(Scene &scene, Widget_Camera &cam, Camera &user_cam, int last_frame) {
if(!render_window) return; if (!render_window)
return;
begin(scene, cam, user_cam); begin(scene, cam, user_cam);
if(ImGui::Button("Output Folder")) { if (ImGui::Button("Output Folder")) {
char* path = nullptr; char *path = nullptr;
NFD_OpenDirectoryDialog(nullptr, nullptr, &path); NFD_OpenDirectoryDialog(nullptr, nullptr, &path);
if(path) { if (path) {
Platform::strcpy(output_path, path, sizeof(output_path)); Platform::strcpy(output_path, path, sizeof(output_path));
free(path); free(path);
} }
} }
ImGui::SameLine(); ImGui::SameLine();
ImGui::InputText("##path", output_path, sizeof(output_path)); ImGui::InputText("##path", output_path, sizeof(output_path));
ImGui::Separator(); ImGui::Separator();
ImGui::Text("Render"); ImGui::Text("Render");
if(animating) { if (animating) {
if(ImGui::Button("Cancel")) { if (ImGui::Button("Cancel")) {
pathtracer.cancel(); pathtracer.cancel();
animating = false; animating = false;
} }
ImGui::SameLine(); ImGui::SameLine();
if(method == 1) { if (method == 1) {
ImGui::ProgressBar(((float)next_frame + pathtracer.progress()) / (max_frame + 1)); ImGui::ProgressBar(((float)next_frame + pathtracer.progress()) / (max_frame + 1));
} else { } else {
ImGui::ProgressBar((float)next_frame / (max_frame + 1)); ImGui::ProgressBar((float)next_frame / (max_frame + 1));
} }
} else { } else {
if(ImGui::Button("Start Render")) { if (ImGui::Button("Start Render")) {
animating = true; animating = true;
max_frame = last_frame; max_frame = last_frame;
next_frame = 0; next_frame = 0;
folder = std::string(output_path); folder = std::string(output_path);
if(method == 1) { if (method == 1) {
init = true; init = true;
ray_log.clear(); ray_log.clear();
pathtracer.set_sizes(out_w, out_h, out_samples, out_area_samples, out_depth); pathtracer.set_sizes(out_w, out_h, out_samples, out_area_samples, out_depth);
} }
} }
} }
float avail = ImGui::GetContentRegionAvail().x; float avail = ImGui::GetContentRegionAvail().x;
float w = std::min(avail, (float)out_w); float w = std::min(avail, (float)out_w);
float h = (w / out_w) * out_h; float h = (w / out_w) * out_h;
if(method == 1) { if (method == 1) {
ImGui::Image((ImTextureID)(long long)pathtracer.get_output_texture(exposure).get_id(), {w, h}); ImGui::Image((ImTextureID)(long long)pathtracer.get_output_texture(exposure).get_id(),
} else { {w, h});
ImGui::Image((ImTextureID)(long long)Renderer::get().saved(), {w, h}, {0.0f, 1.0f}, {1.0f, 0.0f}); } else {
} ImGui::Image((ImTextureID)(long long)Renderer::get().saved(), {w, h}, {0.0f, 1.0f},
{1.0f, 0.0f});
}
ImGui::End(); ImGui::End();
} }
bool Widget_Render::UI(Scene& scene, Widget_Camera& cam, Camera& user_cam, std::string& err) { bool Widget_Render::UI(Scene &scene, Widget_Camera &cam, Camera &user_cam, std::string &err) {
bool ret = false; bool ret = false;
if(!render_window) return ret; if (!render_window)
return ret;
begin(scene, cam, user_cam); begin(scene, cam, user_cam);
ImGui::Separator(); ImGui::Separator();
ImGui::Text("Render"); ImGui::Text("Render");
if(pathtracer.in_progress()) { if (pathtracer.in_progress()) {
if(ImGui::Button("Cancel")) { if (ImGui::Button("Cancel")) {
pathtracer.cancel(); pathtracer.cancel();
has_rendered = false; has_rendered = false;
} }
...@@ -699,40 +736,40 @@ bool Widget_Render::UI(Scene& scene, Widget_Camera& cam, Camera& user_cam, std:: ...@@ -699,40 +736,40 @@ bool Widget_Render::UI(Scene& scene, Widget_Camera& cam, Camera& user_cam, std::
} else { } else {
if(ImGui::Button("Start Render")) { if (ImGui::Button("Start Render")) {
if(method == 1) { if (method == 1) {
has_rendered = true; has_rendered = true;
ret = true; ret = true;
ray_log.clear(); ray_log.clear();
pathtracer.set_sizes(out_w, out_h, out_samples, out_area_samples, out_depth); pathtracer.set_sizes(out_w, out_h, out_samples, out_area_samples, out_depth);
pathtracer.begin_render(scene, cam.get()); pathtracer.begin_render(scene, cam.get());
} else { } else {
Renderer::get().save(scene, cam.get(), out_w, out_h, out_samples); Renderer::get().save(scene, cam.get(), out_w, out_h, out_samples);
} }
} }
} }
ImGui::SameLine(); ImGui::SameLine();
if(ImGui::Button("Save Image")) { if (ImGui::Button("Save Image")) {
char* path = nullptr; char *path = nullptr;
NFD_SaveDialog("png", nullptr, &path); NFD_SaveDialog("png", nullptr, &path);
if(path) { if (path) {
std::vector<unsigned char> data; std::vector<unsigned char> data;
if(method == 1) { if (method == 1) {
pathtracer.get_output().tonemap_to(data, exposure); pathtracer.get_output().tonemap_to(data, exposure);
stbi_flip_vertically_on_write(false); stbi_flip_vertically_on_write(false);
} else { } else {
Renderer::get().saved(data); Renderer::get().saved(data);
stbi_flip_vertically_on_write(true); stbi_flip_vertically_on_write(true);
} }
if(!stbi_write_png(path, (int)out_w, (int)out_h, 4, data.data(), (int)out_w * 4)) { if (!stbi_write_png(path, (int)out_w, (int)out_h, 4, data.data(), (int)out_w * 4)) {
err = "Failed to write png!"; err = "Failed to write png!";
} }
free(path); free(path);
} }
} }
...@@ -740,23 +777,26 @@ bool Widget_Render::UI(Scene& scene, Widget_Camera& cam, Camera& user_cam, std:: ...@@ -740,23 +777,26 @@ bool Widget_Render::UI(Scene& scene, Widget_Camera& cam, Camera& user_cam, std::
float w = std::min(avail, (float)out_w); float w = std::min(avail, (float)out_w);
float h = (w / out_w) * out_h; float h = (w / out_w) * out_h;
if(method == 1) { if (method == 1) {
ImGui::Image((ImTextureID)(long long)pathtracer.get_output_texture(exposure).get_id(), {w, h}); ImGui::Image((ImTextureID)(long long)pathtracer.get_output_texture(exposure).get_id(),
{w, h});
if(!pathtracer.in_progress() && has_rendered) { if (!pathtracer.in_progress() && has_rendered) {
auto [build, render] = pathtracer.completion_time(); auto [build, render] = pathtracer.completion_time();
ImGui::Text("Scene built in %.2fs, rendered in %.2fs.", build, render); ImGui::Text("Scene built in %.2fs, rendered in %.2fs.", build, render);
} }
} else { } else {
ImGui::Image((ImTextureID)(long long)Renderer::get().saved(), {w, h}, {0.0f, 1.0f}, {1.0f, 0.0f}); ImGui::Image((ImTextureID)(long long)Renderer::get().saved(), {w, h}, {0.0f, 1.0f},
} {1.0f, 0.0f});
}
ImGui::End(); ImGui::End();
return ret; return ret;
} }
std::string Widget_Render::headless(Animate& animate, Scene& scene, const Camera& cam, std::string output, std::string Widget_Render::headless(Animate &animate, Scene &scene, const Camera &cam,
bool a, int w, int h, int s, int ls, int d, float exp) { std::string output, bool a, int w, int h, int s, int ls, int d,
float exp) {
info("Render settings:"); info("Render settings:");
info("\twidth: %d", w); info("\twidth: %d", w);
...@@ -767,66 +807,70 @@ std::string Widget_Render::headless(Animate& animate, Scene& scene, const Camera ...@@ -767,66 +807,70 @@ std::string Widget_Render::headless(Animate& animate, Scene& scene, const Camera
info("\texposure: %f", exp); info("\texposure: %f", exp);
info("\trender threads: %u", std::thread::hardware_concurrency()); info("\trender threads: %u", std::thread::hardware_concurrency());
out_w = w; out_w = w;
out_h = h; out_h = h;
pathtracer.set_sizes(w, h, s, ls, d); pathtracer.set_sizes(w, h, s, ls, d);
auto print_progress = [](float f) { auto print_progress = [](float f) {
std::cout << "Progress: ["; std::cout << "Progress: [";
int width = std::min(Platform::console_width() - 30, 50); int width = std::min(Platform::console_width() - 30, 50);
if(width) { if (width) {
int bar = (int)(width * f); int bar = (int)(width * f);
for(int i = 0; i < bar; i++) std::cout << "-"; for (int i = 0; i < bar; i++)
for(int i = bar; i < width; i++) std::cout << " "; std::cout << "-";
std::cout << "] "; for (int i = bar; i < width; i++)
} std::cout << " ";
std::cout << "] ";
float percent = 100.0f * f; }
if(percent < 10.0f) std::cout << "0";
std::cout << percent << "%\r"; float percent = 100.0f * f;
std::cout.flush(); if (percent < 10.0f)
}; std::cout << "0";
std::cout << percent << "%\r";
std::cout << std::fixed << std::setw(2) << std::setprecision(2) << std::setfill('0'); std::cout.flush();
if(a) { };
method = 1; std::cout << std::fixed << std::setw(2) << std::setprecision(2) << std::setfill('0');
init = true; if (a) {
animating = true;
max_frame = animate.n_frames(); method = 1;
next_frame = 0; init = true;
folder = output; animating = true;
while(next_frame < max_frame) { max_frame = animate.n_frames();
std::string err = step(animate, scene); next_frame = 0;
if(!err.empty()) return err; folder = output;
print_progress(((float)next_frame + pathtracer.progress()) / (max_frame + 1)); while (next_frame < max_frame) {
std::this_thread::sleep_for(std::chrono::milliseconds(250)); std::string err = step(animate, scene);
} if (!err.empty())
std::cout << std::endl; return err;
print_progress(((float)next_frame + pathtracer.progress()) / (max_frame + 1));
} else { std::this_thread::sleep_for(std::chrono::milliseconds(250));
}
pathtracer.begin_render(scene, cam); std::cout << std::endl;
while(pathtracer.in_progress()) {
print_progress(pathtracer.progress()); } else {
std::this_thread::sleep_for(std::chrono::milliseconds(250));
} pathtracer.begin_render(scene, cam);
std::cout << std::endl; while (pathtracer.in_progress()) {
print_progress(pathtracer.progress());
std::vector<unsigned char> data; std::this_thread::sleep_for(std::chrono::milliseconds(250));
pathtracer.get_output().tonemap_to(data, exp); }
if(!stbi_write_png(output.c_str(), w, h, 4, data.data(), w * 4)) { std::cout << std::endl;
return "Failed to write output!";
} std::vector<unsigned char> data;
} pathtracer.get_output().tonemap_to(data, exp);
if (!stbi_write_png(output.c_str(), w, h, 4, data.data(), w * 4)) {
return "Failed to write output!";
}
}
return {}; return {};
} }
void Widget_Render::render_log(const Mat4& view) const { void Widget_Render::render_log(const Mat4 &view) const {
std::lock_guard<std::mutex> lock(log_mut); std::lock_guard<std::mutex> lock(log_mut);
Renderer::get().lines(ray_log, view); Renderer::get().lines(ray_log, view);
} }
} } // namespace Gui
...@@ -2,8 +2,8 @@ ...@@ -2,8 +2,8 @@
#pragma once #pragma once
#include "../lib/mathlib.h" #include "../lib/mathlib.h"
#include "../scene/scene.h"
#include "../rays/pathtracer.h" #include "../rays/pathtracer.h"
#include "../scene/scene.h"
class Undo; class Undo;
...@@ -11,44 +11,45 @@ namespace Gui { ...@@ -11,44 +11,45 @@ namespace Gui {
class Animate; class Animate;
enum class Axis { enum class Axis { X, Y, Z };
X, Y, Z
};
enum class Widget_Type { enum class Widget_Type { move, rotate, scale, bevel, count };
move,
rotate,
scale,
bevel,
count
};
static const int n_Widget_Types = (int)Widget_Type::count; static const int n_Widget_Types = (int)Widget_Type::count;
enum class Widget_IDs : Scene_ID { enum class Widget_IDs : Scene_ID {
none, none,
x_mov, y_mov, z_mov, x_mov,
xy_mov, yz_mov, xz_mov, y_mov,
x_rot, y_rot, z_rot, z_mov,
x_scl, y_scl, z_scl, xy_mov,
count yz_mov,
xz_mov,
x_rot,
y_rot,
z_rot,
x_scl,
y_scl,
z_scl,
count
}; };
static const int n_Widget_IDs = (int)Widget_IDs::count; static const int n_Widget_IDs = (int)Widget_IDs::count;
class Widget_Camera { class Widget_Camera {
public: public:
Widget_Camera(Vec2 screen_dim) : Widget_Camera(Vec2 screen_dim)
screen_dim(screen_dim), render_cam(screen_dim), saved_cam(screen_dim) : screen_dim(screen_dim), render_cam(screen_dim), saved_cam(screen_dim) {
{ generate_cage(); } generate_cage();
}
bool UI(Undo& undo, Camera& user_cam);
void render(const Mat4& view); bool UI(Undo &undo, Camera &user_cam);
void render(const Mat4 &view);
void load(Vec3 center, Vec3 pos, float ar, float vfov);
const Camera& get() const { return render_cam; } void load(Vec3 center, Vec3 pos, float ar, float vfov);
void ar(Camera& user_cam, float _ar); const Camera &get() const { return render_cam; }
float get_ar() const { return cam_ar; } void ar(Camera &user_cam, float _ar);
bool moving() const { return moving_camera; } float get_ar() const { return cam_ar; }
void dim(Vec2 d) { screen_dim = d; } bool moving() const { return moving_camera; }
void dim(Vec2 d) { screen_dim = d; }
private: private:
float cam_fov = 90.0f, cam_ar = 1.7778f; float cam_fov = 90.0f, cam_ar = 1.7778f;
...@@ -57,89 +58,89 @@ private: ...@@ -57,89 +58,89 @@ private:
Camera render_cam, saved_cam; Camera render_cam, saved_cam;
GL::Lines cam_cage; GL::Lines cam_cage;
void update_cameras(Camera& user_cam); void update_cameras(Camera &user_cam);
void generate_cage(); void generate_cage();
}; };
class Widget_Render { class Widget_Render {
public: public:
Widget_Render(Vec2 dim); Widget_Render(Vec2 dim);
void open(); void open();
bool UI(Scene& scene, Widget_Camera& cam, Camera& user_cam, std::string& err); bool UI(Scene &scene, Widget_Camera &cam, Camera &user_cam, std::string &err);
void animate(Scene& scene, Widget_Camera& cam, Camera& user_cam, int max_frame); void animate(Scene &scene, Widget_Camera &cam, Camera &user_cam, int max_frame);
std::string step(Animate& animate, Scene& scene); std::string step(Animate &animate, Scene &scene);
std::string headless(Animate& animate, Scene& scene, const Camera& cam, std::string output, std::string headless(Animate &animate, Scene &scene, const Camera &cam, std::string output,
bool a, int w, int h, int s, int ls, int d, float exp); bool a, int w, int h, int s, int ls, int d, float exp);
void log_ray(const Ray& ray, float t, Spectrum color = Spectrum{1.0f}); void log_ray(const Ray &ray, float t, Spectrum color = Spectrum{1.0f});
void render_log(const Mat4& view) const; void render_log(const Mat4 &view) const;
PT::Pathtracer& tracer() { return pathtracer; } PT::Pathtracer &tracer() { return pathtracer; }
bool rendered() const { return has_rendered; } bool rendered() const { return has_rendered; }
std::pair<float,float> completion_time() const { return pathtracer.completion_time(); } std::pair<float, float> completion_time() const { return pathtracer.completion_time(); }
bool in_progress() const { return pathtracer.in_progress(); } bool in_progress() const { return pathtracer.in_progress(); }
float wh_ar() const { return (float)out_w / (float)out_h; } float wh_ar() const { return (float)out_w / (float)out_h; }
private: private:
void begin(Scene& scene, Widget_Camera& cam, Camera& user_cam); void begin(Scene &scene, Widget_Camera &cam, Camera &user_cam);
mutable std::mutex log_mut; mutable std::mutex log_mut;
GL::Lines ray_log; GL::Lines ray_log;
int out_w, out_h, out_samples = 32, out_area_samples = 8, out_depth = 4; int out_w, out_h, out_samples = 32, out_area_samples = 8, out_depth = 4;
float exposure = 1.0f; float exposure = 1.0f;
bool has_rendered = false; bool has_rendered = false;
bool render_window = false, render_window_focus = false; bool render_window = false, render_window_focus = false;
int method = 1; int method = 1;
bool animating = false, init = false; bool animating = false, init = false;
int next_frame = 0, max_frame = 0; int next_frame = 0, max_frame = 0;
char output_path[256] = {}; char output_path[256] = {};
std::string folder; std::string folder;
PT::Pathtracer pathtracer; PT::Pathtracer pathtracer;
}; };
class Widgets { class Widgets {
public: public:
Widgets(); Widgets();
Widget_Type active = Widget_Type::move; Widget_Type active = Widget_Type::move;
void end_drag(); void end_drag();
Pose apply_action(const Pose& pose); Pose apply_action(const Pose &pose);
void start_drag(Vec3 pos, Vec3 cam, Vec2 spos, Vec3 dir); void start_drag(Vec3 pos, Vec3 cam, Vec2 spos, Vec3 dir);
void drag_to(Vec3 pos, Vec3 cam, Vec2 spos, Vec3 dir, bool scale_invert); void drag_to(Vec3 pos, Vec3 cam, Vec2 spos, Vec3 dir, bool scale_invert);
void select(Scene_ID id); void select(Scene_ID id);
void render(const Mat4& view, Vec3 pos, float scl); void render(const Mat4 &view, Vec3 pos, float scl);
bool action_button(Widget_Type act, std::string name, bool wrap = true); bool action_button(Widget_Type act, std::string name, bool wrap = true);
bool want_drag(); bool want_drag();
bool is_dragging(); bool is_dragging();
void set_dragging(bool dragging, bool plane); void set_dragging(bool dragging, bool plane);
private: private:
void generate_lines(Vec3 pos); void generate_lines(Vec3 pos);
bool to_axis(Vec3 obj_pos, Vec3 cam_pos, Vec3 dir, Vec3& hit); bool to_axis(Vec3 obj_pos, Vec3 cam_pos, Vec3 dir, Vec3 &hit);
bool to_plane(Vec3 obj_pos, Vec3 cam_pos, Vec3 dir, Vec3 norm, Vec3& hit); bool to_plane(Vec3 obj_pos, Vec3 cam_pos, Vec3 dir, Vec3 norm, Vec3 &hit);
// interface data // interface data
Axis axis = Axis::X; Axis axis = Axis::X;
Vec3 drag_start, drag_end; Vec3 drag_start, drag_end;
Vec2 bevel_start, bevel_end; Vec2 bevel_start, bevel_end;
bool dragging = false, drag_plane = false; bool dragging = false, drag_plane = false;
bool start_dragging = false; bool start_dragging = false;
// render data // render data
GL::Lines lines; GL::Lines lines;
Scene_Object x_mov, y_mov, z_mov; Scene_Object x_mov, y_mov, z_mov;
Scene_Object xy_mov, yz_mov, xz_mov; Scene_Object xy_mov, yz_mov, xz_mov;
Scene_Object x_rot, y_rot, z_rot; Scene_Object x_rot, y_rot, z_rot;
Scene_Object x_scl, z_scl, y_scl; Scene_Object x_scl, z_scl, y_scl;
}; };
} } // namespace Gui
#pragma once #pragma once
#include <cmath>
#include <vector>
#include <algorithm> #include <algorithm>
#include <ostream>
#include <cfloat> #include <cfloat>
#include <cmath>
#include <ostream>
#include <vector>
#include "vec2.h"
#include "vec3.h"
#include "mat4.h" #include "mat4.h"
#include "ray.h" #include "ray.h"
#include "vec2.h"
#include "vec3.h"
struct BBox { struct BBox {
/// Default min is max float value, default max is negative max float value /// Default min is max float value, default max is negative max float value
BBox() : BBox() : min(FLT_MAX), max(-FLT_MAX) {}
min(FLT_MAX),
max(-FLT_MAX) {
}
/// Set minimum and maximum extent /// Set minimum and maximum extent
explicit BBox(Vec3 min, Vec3 max) : explicit BBox(Vec3 min, Vec3 max) : min(min), max(max) {}
min(min),
max(max) {
}
BBox(const BBox& src) = default;
~BBox() = default;
BBox operator=(BBox v) { BBox(const BBox &) = default;
min = v.min; BBox &operator=(const BBox &) = default;
max = v.max; ~BBox() = default;
return *this;
}
/// Rest min to max float, max to negative max float /// Rest min to max float, max to negative max float
void reset() { void reset() {
...@@ -42,7 +32,7 @@ struct BBox { ...@@ -42,7 +32,7 @@ struct BBox {
/// Expand bounding box to include point /// Expand bounding box to include point
void enclose(Vec3 point) { void enclose(Vec3 point) {
min = hmin(min, point); min = hmin(min, point);
max = hmax(max, point); max = hmax(max, point);
} }
void enclose(BBox box) { void enclose(BBox box) {
min = hmin(min, box.min); min = hmin(min, box.min);
...@@ -50,35 +40,32 @@ struct BBox { ...@@ -50,35 +40,32 @@ struct BBox {
} }
/// Get center point of box /// Get center point of box
Vec3 center() const { Vec3 center() const { return (min + max) * 0.5f; }
return (min + max) * 0.5f;
}
// Check whether box has no volume // Check whether box has no volume
bool empty() const { bool empty() const { return min.x > max.x || min.y > max.y || min.z > max.z; }
return min.x > max.x || min.y > max.y || min.z > max.z;
}
/// Get surface area of the box /// Get surface area of the box
float surface_area() const { float surface_area() const {
if(empty()) return 0.0f; if (empty())
return 0.0f;
Vec3 extent = max - min; Vec3 extent = max - min;
return 2.0f * (extent.x * extent.z + extent.x * extent.y + extent.y * extent.z); return 2.0f * (extent.x * extent.z + extent.x * extent.y + extent.y * extent.z);
} }
/// Transform box by a matrix /// Transform box by a matrix
void transform(const Mat4& trans) { void transform(const Mat4 &trans) {
Vec3 amin = min, amax = max; Vec3 amin = min, amax = max;
min = max = trans[3].xyz(); min = max = trans[3].xyz();
for(int i = 0; i < 3; i++) { for (int i = 0; i < 3; i++) {
for(int j = 0; j < 3; j++) { for (int j = 0; j < 3; j++) {
float a = trans[j][i] * amin[j]; float a = trans[j][i] * amin[j];
float b = trans[j][i] * amax[j]; float b = trans[j][i] * amax[j];
if(a < b) { if (a < b) {
min[i] += a; min[i] += a;
max[i] += b; max[i] += b;
} else { } else {
min[i] += b; min[i] += b;
max[i] += a; max[i] += a;
} }
} }
...@@ -86,7 +73,7 @@ struct BBox { ...@@ -86,7 +73,7 @@ struct BBox {
} }
// TODO (PathTracer): see student/bbox.cpp // TODO (PathTracer): see student/bbox.cpp
bool hit(const Ray& ray, Vec2& times) const; bool hit(const Ray &ray, Vec2 &times) const;
/// Get the eight corner points of the bounding box /// Get the eight corner points of the bounding box
std::vector<Vec3> corners() const { std::vector<Vec3> corners() const {
...@@ -102,17 +89,17 @@ struct BBox { ...@@ -102,17 +89,17 @@ struct BBox {
return ret; return ret;
} }
/// Given a screen transformation (projection), calculate screen-space ([-1,1]x[-1,1]) /// Given a screen transformation (projection), calculate screen-space ([-1,1]x[-1,1])
/// bounds that will always contain the bounding box on screen /// bounds that will always contain the bounding box on screen
void screen_rect(const Mat4& transform, Vec2& min_out, Vec2& max_out) const { void screen_rect(const Mat4 &transform, Vec2 &min_out, Vec2 &max_out) const {
min_out = Vec2(FLT_MAX); min_out = Vec2(FLT_MAX);
max_out = Vec2(-FLT_MAX); max_out = Vec2(-FLT_MAX);
auto c = corners(); auto c = corners();
bool partially_behind = false, all_behind = true; bool partially_behind = false, all_behind = true;
for(auto& v : c) { for (auto &v : c) {
Vec3 p = transform * v; Vec3 p = transform * v;
if(p.z < 0) { if (p.z < 0) {
partially_behind = true; partially_behind = true;
} else { } else {
all_behind = false; all_behind = false;
...@@ -121,10 +108,10 @@ struct BBox { ...@@ -121,10 +108,10 @@ struct BBox {
max_out = hmax(max_out, Vec2(p.x, p.y)); max_out = hmax(max_out, Vec2(p.x, p.y));
} }
if(partially_behind && !all_behind) { if (partially_behind && !all_behind) {
min_out = Vec2(-1.0f, -1.0f); min_out = Vec2(-1.0f, -1.0f);
max_out = Vec2(1.0f, 1.0f); max_out = Vec2(1.0f, 1.0f);
} else if(all_behind) { } else if (all_behind) {
min_out = Vec2(0.0f, 0.0f); min_out = Vec2(0.0f, 0.0f);
max_out = Vec2(0.0f, 0.0f); max_out = Vec2(0.0f, 0.0f);
} }
...@@ -133,8 +120,7 @@ struct BBox { ...@@ -133,8 +120,7 @@ struct BBox {
Vec3 min, max; Vec3 min, max;
}; };
inline std::ostream& operator<<(std::ostream& out, BBox b) { inline std::ostream &operator<<(std::ostream &out, BBox b) {
out << "BBox{" << b.min << "," << b.max << "}"; out << "BBox{" << b.min << "," << b.max << "}";
return out; return out;
} }
#pragma once #pragma once
#include <cmath>
#include <algorithm> #include <algorithm>
#include <cmath>
#include <ostream> #include <ostream>
#include "vec3.h" #include "vec3.h"
struct Line { struct Line {
Line() { Line() {}
} /// Create line from point and unit direction
/// Create line from point and unit direction explicit Line(Vec3 point, Vec3 dir) : point(point), dir(dir.unit()) {}
explicit Line(Vec3 point, Vec3 dir) :
point(point), Line(const Line &) = default;
dir(dir.unit()) { Line &operator=(const Line &) = default;
} ~Line() = default;
Line(const Line& src) = default;
~Line() = default; /// Get point on line at time t
Vec3 at(float t) const { return point + t * dir; }
Line operator=(Line v) {
point = v.point; /// Get closest point on line to pt
dir = v.dir; Vec3 closest(Vec3 pt) const {
return *this; Vec3 p = pt - point;
} float t = dot(p, dir);
return at(t);
/// Get point on line at time t }
Vec3 at(float t) const {
return point + t * dir; /// Get closest point on line to another line.
} /// Returns false if the closest point is 'backward' relative to the line's direction
bool closest(Line other, Vec3 &pt) const {
/// Get closest point on line to pt
Vec3 closest(Vec3 pt) const {
Vec3 p = pt - point;
float t = dot(p, dir);
return at(t);
}
/// Get closest point on line to another line.
/// Returns false if the closest point is 'backward' relative to the line's direction
bool closest(Line other, Vec3& pt) const {
Vec3 p0 = point - other.point; Vec3 p0 = point - other.point;
float a = dot(dir, other.dir); float a = dot(dir, other.dir);
float b = dot(dir, p0); float b = dot(dir, p0);
float c = dot(other.dir, p0); float c = dot(other.dir, p0);
float t0 = (a*c - b) / (1.0f - a*a); float t0 = (a * c - b) / (1.0f - a * a);
float t1 = (c - a*b) / (1.0f - a*a); float t1 = (c - a * b) / (1.0f - a * a);
pt = at(t0); pt = at(t0);
return t1 >= 0.0f; return t1 >= 0.0f;
} }
Vec3 point, dir; Vec3 point, dir;
}; };
inline std::ostream& operator<<(std::ostream& out, Line l) { inline std::ostream &operator<<(std::ostream &out, Line l) {
out << "Line{" << l.point << "," << l.dir << "}"; out << "Line{" << l.point << "," << l.dir << "}";
return out; return out;
} }
#pragma once #pragma once
#include <cstdio>
#include <cstdarg> #include <cstdarg>
#include <cstdio>
#include <mutex> #include <mutex>
#include <string> #include <string>
inline std::mutex printf_lock; inline std::mutex printf_lock;
inline void log(std::string fmt, ...) { inline void log(std::string fmt, ...) {
std::lock_guard<std::mutex> lock(printf_lock); std::lock_guard<std::mutex> lock(printf_lock);
va_list args; va_list args;
va_start(args, fmt); va_start(args, fmt);
vprintf(fmt.c_str(), args); vprintf(fmt.c_str(), args);
va_end(args); va_end(args);
fflush(stdout); fflush(stdout);
} }
inline std::string last_file(std::string path) { inline std::string last_file(std::string path) {
size_t p = path.find_last_of("\\/") + 1; size_t p = path.find_last_of("\\/") + 1;
return path.substr(p, path.size() - p); return path.substr(p, path.size() - p);
} }
/// Log informational message /// Log informational message
#define info(fmt, ...) (void)( \ #define info(fmt, ...) \
log("%s:%u [info] " fmt "\n", last_file(__FILE__).c_str(), __LINE__, ##__VA_ARGS__)) (void)(log("%s:%u [info] " fmt "\n", last_file(__FILE__).c_str(), __LINE__, ##__VA_ARGS__))
/// Log warning (red) /// Log warning (red)
#define warn(fmt, ...) (void)( \ #define warn(fmt, ...) \
log("\033[0;31m%s:%u [warn] " fmt "\033[0m\n", last_file(__FILE__).c_str(), __LINE__, ##__VA_ARGS__)) (void)(log("\033[0;31m%s:%u [warn] " fmt "\033[0m\n", last_file(__FILE__).c_str(), __LINE__, \
##__VA_ARGS__))
/// Log fatal error and exit program /// Log fatal error and exit program
#define die(fmt, ...) (void)( \ #define die(fmt, ...) \
log("\033[0;31m%s:%u [fatal] " fmt "\033[0m\n", last_file(__FILE__).c_str(), __LINE__, ##__VA_ARGS__), \ (void)(log("\033[0;31m%s:%u [fatal] " fmt "\033[0m\n", last_file(__FILE__).c_str(), __LINE__, \
std::exit(__LINE__)); ##__VA_ARGS__), \
std::exit(__LINE__));
#ifdef _MSC_VER #ifdef _MSC_VER
#define DEBUG_BREAK __debugbreak() #define DEBUG_BREAK __debugbreak()
...@@ -45,10 +47,10 @@ inline std::string last_file(std::string path) { ...@@ -45,10 +47,10 @@ inline std::string last_file(std::string path) {
#error Unsupported compiler. #error Unsupported compiler.
#endif #endif
#define fail_assert(msg, file, line) (void)( \ #define fail_assert(msg, file, line) \
log("\033[1;31m%s:%u [ASSERT] " msg "\033[0m\n", file, line), DEBUG_BREAK, std::exit(__LINE__), 0) (void)(log("\033[1;31m%s:%u [ASSERT] " msg "\033[0m\n", file, line), DEBUG_BREAK, \
std::exit(__LINE__), 0)
#undef assert #undef assert
#define assert(expr) (void)( \ #define assert(expr) \
(!!(expr)) || \ (void)((!!(expr)) || (fail_assert(#expr, last_file(__FILE__).c_str(), __LINE__), 0))
(fail_assert(#expr, last_file(__FILE__).c_str(), __LINE__), 0))
#pragma once #pragma once
#include <cmath>
#include <algorithm> #include <algorithm>
#include <cmath>
#include <ostream> #include <ostream>
#include "log.h" #include "log.h"
...@@ -10,435 +10,465 @@ ...@@ -10,435 +10,465 @@
struct Mat4 { struct Mat4 {
/// Identity matrix /// Identity matrix
static const Mat4 I; static const Mat4 I;
/// Zero matrix /// Zero matrix
static const Mat4 Zero; static const Mat4 Zero;
/// Return transposed matrix /// Return transposed matrix
static Mat4 transpose(const Mat4& m); static Mat4 transpose(const Mat4 &m);
/// Return inverse matrix (will be NaN if m is not invertible) /// Return inverse matrix (will be NaN if m is not invertible)
static Mat4 inverse(const Mat4& m); static Mat4 inverse(const Mat4 &m);
/// Return transformation matrix for given translation vector /// Return transformation matrix for given translation vector
static Mat4 translate(Vec3 t); static Mat4 translate(Vec3 t);
/// Return transformation matrix for given angle (degrees) and axis /// Return transformation matrix for given angle (degrees) and axis
static Mat4 rotate(float t, Vec3 axis); static Mat4 rotate(float t, Vec3 axis);
/// Return transformation matrix for given XYZ Euler angle rotation /// Return transformation matrix for given XYZ Euler angle rotation
static Mat4 euler(Vec3 angles); static Mat4 euler(Vec3 angles);
/// Return transformation matrix that rotates the Y axis to dir /// Return transformation matrix that rotates the Y axis to dir
static Mat4 rotate_to(Vec3 dir); static Mat4 rotate_to(Vec3 dir);
/// Return transformation matrix that rotates the -Z axis to dir /// Return transformation matrix that rotates the -Z axis to dir
static Mat4 rotate_z_to(Vec3 dir); static Mat4 rotate_z_to(Vec3 dir);
/// Return transformation matrix for given scale factors /// Return transformation matrix for given scale factors
static Mat4 scale(Vec3 s); static Mat4 scale(Vec3 s);
/// Return transformation matrix with given axes /// Return transformation matrix with given axes
static Mat4 axes(Vec3 x, Vec3 y, Vec3 z); static Mat4 axes(Vec3 x, Vec3 y, Vec3 z);
/// Return transformation matrix for viewing a scene from $pos looking at $at, /// Return transformation matrix for viewing a scene from $pos looking at $at,
/// where straight up is defined as $up /// where straight up is defined as $up
static Mat4 look_at(Vec3 pos, Vec3 at, Vec3 up = Vec3{0.0f, 1.0f, 0.0f}); static Mat4 look_at(Vec3 pos, Vec3 at, Vec3 up = Vec3{0.0f, 1.0f, 0.0f});
/// Return orthogonal projection matrix with given left, right, bottom, top, /// Return orthogonal projection matrix with given left, right, bottom, top,
/// near, and far planes. /// near, and far planes.
static Mat4 ortho(float l, float r, float b, float t, float n, float f); static Mat4 ortho(float l, float r, float b, float t, float n, float f);
/// Return perspective projection matrix with given field of view, aspect ratio, /// Return perspective projection matrix with given field of view, aspect ratio,
/// and near plane. The far plane is assumed to be at infinity. This projection /// and near plane. The far plane is assumed to be at infinity. This projection
/// also outputs n/z for better precision with floating point depth buffers, so we use /// also outputs n/z for better precision with floating point depth buffers, so we use
/// a depth mapping where 0 is the far plane (infinity) and 1 is the near plane, and /// a depth mapping where 0 is the far plane (infinity) and 1 is the near plane, and
/// an object is closer if is depth is greater. /// an object is closer if is depth is greater.
static Mat4 project(float fov, float ar, float n); static Mat4 project(float fov, float ar, float n);
Mat4() { Mat4() { *this = I; }
*this = I; explicit Mat4(Vec4 x, Vec4 y, Vec4 z, Vec4 w) {
} cols[0] = x;
explicit Mat4(Vec4 x, Vec4 y, Vec4 z, Vec4 w) { cols[1] = y;
cols[0] = x; cols[2] = z;
cols[1] = y; cols[3] = w;
cols[2] = z; }
cols[3] = w;
} Mat4(const Mat4 &) = default;
Mat4(const Mat4& src) { Mat4 &operator=(const Mat4 &) = default;
for(int i = 0; i < 4; i++) ~Mat4() = default;
cols[i] = src.cols[i];
} Vec4 &operator[](int idx) {
~Mat4() = default; assert(idx >= 0 && idx <= 3);
return cols[idx];
Mat4 operator=(const Mat4& m) { }
for(int i = 0; i < 4; i++) Vec4 operator[](int idx) const {
cols[i] = m.cols[i]; assert(idx >= 0 && idx <= 3);
return *this; return cols[idx];
} }
Vec4& operator[](int idx) { Mat4 operator+=(const Mat4 &m) {
assert(idx >= 0 && idx <= 3); for (int i = 0; i < 4; i++)
return cols[idx]; cols[i] += m.cols[i];
} return *this;
Vec4 operator[](int idx) const { }
assert(idx >= 0 && idx <= 3); Mat4 operator-=(const Mat4 &m) {
return cols[idx]; for (int i = 0; i < 4; i++)
} cols[i] -= m.cols[i];
return *this;
Mat4 operator+=(const Mat4& m) { }
for(int i = 0; i < 4; i++)
cols[i] += m.cols[i]; Mat4 operator+=(float s) {
return *this; for (int i = 0; i < 4; i++)
} cols[i] += s;
Mat4 operator-=(const Mat4& m) { return *this;
for(int i = 0; i < 4; i++) }
cols[i] -= m.cols[i]; Mat4 operator-=(float s) {
return *this; for (int i = 0; i < 4; i++)
} cols[i] -= s;
return *this;
Mat4 operator+=(float s) { }
for(int i = 0; i < 4; i++) Mat4 operator*=(float s) {
cols[i] += s; for (int i = 0; i < 4; i++)
return *this; cols[i] *= s;
} return *this;
Mat4 operator-=(float s) { }
for(int i = 0; i < 4; i++) Mat4 operator/=(float s) {
cols[i] -= s; for (int i = 0; i < 4; i++)
return *this; cols[i] /= s;
} return *this;
Mat4 operator*=(float s) { }
for(int i = 0; i < 4; i++)
cols[i] *= s; Mat4 operator+(const Mat4 &m) const {
return *this; Mat4 r;
} for (int i = 0; i < 4; i++)
Mat4 operator/=(float s) { r.cols[i] = cols[i] + m.cols[i];
for(int i = 0; i < 4; i++) return r;
cols[i] /= s; }
return *this; Mat4 operator-(const Mat4 &m) const {
} Mat4 r;
for (int i = 0; i < 4; i++)
r.cols[i] = cols[i] - m.cols[i];
Mat4 operator+(const Mat4& m) const { return r;
Mat4 r; }
for(int i = 0; i < 4; i++)
r.cols[i] = cols[i] + m.cols[i]; Mat4 operator+(float s) const {
return r; Mat4 r;
} for (int i = 0; i < 4; i++)
Mat4 operator-(const Mat4& m) const { r.cols[i] = cols[i] + s;
Mat4 r; return r;
for(int i = 0; i < 4; i++) }
r.cols[i] = cols[i] - m.cols[i]; Mat4 operator-(float s) const {
return r; Mat4 r;
} for (int i = 0; i < 4; i++)
r.cols[i] = cols[i] - s;
Mat4 operator+(float s) const { return r;
Mat4 r; }
for(int i = 0; i < 4; i++) Mat4 operator*(float s) const {
r.cols[i] = cols[i] + s; Mat4 r;
return r; for (int i = 0; i < 4; i++)
} r.cols[i] = cols[i] * s;
Mat4 operator-(float s) const { return r;
Mat4 r; }
for(int i = 0; i < 4; i++) Mat4 operator/(float s) const {
r.cols[i] = cols[i] - s; Mat4 r;
return r; for (int i = 0; i < 4; i++)
} r.cols[i] = cols[i] / s;
Mat4 operator*(float s) const { return r;
Mat4 r; }
for(int i = 0; i < 4; i++)
r.cols[i] = cols[i] * s; Mat4 operator*=(const Mat4 &v) {
return r; *this = *this * v;
} return *this;
Mat4 operator/(float s) const { }
Mat4 r; Mat4 operator*(const Mat4 &m) const {
for(int i = 0; i < 4; i++) Mat4 ret;
r.cols[i] = cols[i] / s; for (int i = 0; i < 4; i++) {
return r; for (int j = 0; j < 4; j++) {
} ret[i][j] = 0.0f;
for (int k = 0; k < 4; k++) {
Mat4 operator*=(const Mat4& v) { ret[i][j] += m[i][k] * cols[k][j];
*this = *this * v; }
return *this; }
} }
Mat4 operator*(const Mat4& m) const { return ret;
Mat4 ret; }
for(int i = 0; i < 4; i++) {
for(int j = 0; j < 4; j++) { Vec4 operator*(Vec4 v) const {
ret[i][j] = 0.0f; return v[0] * cols[0] + v[1] * cols[1] + v[2] * cols[2] + v[3] * cols[3];
for(int k = 0; k < 4; k++) { }
ret[i][j] += m[i][k] * cols[k][j];
} /// Expands v to Vec4(v, 1.0), multiplies, and projects back to 3D
} Vec3 operator*(Vec3 v) const { return operator*(Vec4(v, 1.0f)).project(); }
} /// Expands v to Vec4(v, 0.0), multiplies, and projects back to 3D
return ret; Vec3 rotate(Vec3 v) const { return operator*(Vec4(v, 0.0f)).xyz(); }
}
/// Converts rotation (orthonormal 3x3) matrix to equivalent Euler angles
Vec4 operator*(Vec4 v) const { Vec3 to_euler() const {
return v[0] * cols[0] + v[1] * cols[1] +
v[2] * cols[2] + v[3] * cols[3]; bool single = true;
} static const float singularity[] = {1.0f, 0.0f, 0.0f, 0.0f, 0.0f, -1.0f,
0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0};
/// Expands v to Vec4(v, 1.0), multiplies, and projects back to 3D for (int i = 0; i < 12 && single; i++) {
Vec3 operator*(Vec3 v) const { single = single && std::abs(data[i] - singularity[i]) < 16.0f * FLT_EPSILON;
return operator*(Vec4(v, 1.0f)).project(); }
} if (single)
/// Expands v to Vec4(v, 0.0), multiplies, and projects back to 3D return Vec3{0.0f, 0.0f, 180.0f};
Vec3 rotate(Vec3 v) const {
return operator*(Vec4(v, 0.0f)).xyz(); Vec3 eul1, eul2;
}
float cy = std::hypotf(cols[0][0], cols[0][1]);
/// Converts rotation (orthonormal 3x3) matrix to equivalent Euler angles if (cy > 16.0f * FLT_EPSILON) {
Vec3 to_euler() const { eul1[0] = std::atan2(cols[1][2], cols[2][2]);
eul1[1] = std::atan2(-cols[0][2], cy);
bool single = true; eul1[2] = std::atan2(cols[0][1], cols[0][0]);
static const float singularity[] =
{1.0f,0.0f,0.0f,0.0f,0.0f,-1.0f,0.0f,0.0f,0.0f,0.0f,1.0f,0.0}; eul2[0] = std::atan2(-cols[1][2], -cols[2][2]);
for(int i = 0; i < 12 && single; i++) { eul2[1] = std::atan2(-cols[0][2], -cy);
single = single && std::abs(data[i] - singularity[i]) < 16.0f * FLT_EPSILON; eul2[2] = std::atan2(-cols[0][1], -cols[0][0]);
} } else {
if(single) return Vec3{0.0f, 0.0f, 180.0f}; eul1[0] = std::atan2(-cols[2][1], cols[1][1]);
eul1[1] = std::atan2(-cols[0][2], cy);
Vec3 eul1, eul2; eul1[2] = 0;
eul2 = eul1;
float cy = std::hypotf(cols[0][0], cols[0][1]); }
if (cy > 16.0f * FLT_EPSILON) { float d1 = std::abs(eul1[0]) + std::abs(eul1[1]) + std::abs(eul1[2]);
eul1[0] = std::atan2(cols[1][2], cols[2][2]); float d2 = std::abs(eul2[0]) + std::abs(eul2[1]) + std::abs(eul2[2]);
eul1[1] = std::atan2(-cols[0][2], cy); if (d1 > d2)
eul1[2] = std::atan2(cols[0][1], cols[0][0]); return Degrees(eul2);
else
eul2[0] = std::atan2(-cols[1][2], -cols[2][2]); return Degrees(eul1);
eul2[1] = std::atan2(-cols[0][2], -cy); }
eul2[2] = std::atan2(-cols[0][1], -cols[0][0]);
} else { /// Returns matrix transpose
eul1[0] = std::atan2(-cols[2][1], cols[1][1]); Mat4 T() const { return transpose(*this); }
eul1[1] = std::atan2(-cols[0][2], cy); /// Returns matrix inverse (will be NaN if m is not invertible)
eul1[2] = 0; Mat4 inverse() const { return inverse(*this); }
eul2 = eul1;
} /// Returns determinant (brute force).
float d1 = std::abs(eul1[0]) + std::abs(eul1[1]) + std::abs(eul1[2]); float det() const {
float d2 = std::abs(eul2[0]) + std::abs(eul2[1]) + std::abs(eul2[2]); return cols[0][3] * cols[1][2] * cols[2][1] * cols[3][0] -
if (d1 > d2) return Degrees(eul2); cols[0][2] * cols[1][3] * cols[2][1] * cols[3][0] -
else return Degrees(eul1); cols[0][3] * cols[1][1] * cols[2][2] * cols[3][0] +
} cols[0][1] * cols[1][3] * cols[2][2] * cols[3][0] +
cols[0][2] * cols[1][1] * cols[2][3] * cols[3][0] -
/// Returns matrix transpose cols[0][1] * cols[1][2] * cols[2][3] * cols[3][0] -
Mat4 T() const { cols[0][3] * cols[1][2] * cols[2][0] * cols[3][1] +
return transpose(*this); cols[0][2] * cols[1][3] * cols[2][0] * cols[3][1] +
} cols[0][3] * cols[1][0] * cols[2][2] * cols[3][1] -
/// Returns matrix inverse (will be NaN if m is not invertible) cols[0][0] * cols[1][3] * cols[2][2] * cols[3][1] -
Mat4 inverse() const { cols[0][2] * cols[1][0] * cols[2][3] * cols[3][1] +
return inverse(*this); cols[0][0] * cols[1][2] * cols[2][3] * cols[3][1] +
} cols[0][3] * cols[1][1] * cols[2][0] * cols[3][2] -
cols[0][1] * cols[1][3] * cols[2][0] * cols[3][2] -
/// Returns determinant (brute force). cols[0][3] * cols[1][0] * cols[2][1] * cols[3][2] +
float det() const { cols[0][0] * cols[1][3] * cols[2][1] * cols[3][2] +
return cols[0][3]*cols[1][2]*cols[2][1]*cols[3][0] - cols[0][2]*cols[1][3]*cols[2][1]*cols[3][0] - cols[0][1] * cols[1][0] * cols[2][3] * cols[3][2] -
cols[0][3]*cols[1][1]*cols[2][2]*cols[3][0] + cols[0][1]*cols[1][3]*cols[2][2]*cols[3][0] + cols[0][0] * cols[1][1] * cols[2][3] * cols[3][2] -
cols[0][2]*cols[1][1]*cols[2][3]*cols[3][0] - cols[0][1]*cols[1][2]*cols[2][3]*cols[3][0] - cols[0][2] * cols[1][1] * cols[2][0] * cols[3][3] +
cols[0][3]*cols[1][2]*cols[2][0]*cols[3][1] + cols[0][2]*cols[1][3]*cols[2][0]*cols[3][1] + cols[0][1] * cols[1][2] * cols[2][0] * cols[3][3] +
cols[0][3]*cols[1][0]*cols[2][2]*cols[3][1] - cols[0][0]*cols[1][3]*cols[2][2]*cols[3][1] - cols[0][2] * cols[1][0] * cols[2][1] * cols[3][3] -
cols[0][2]*cols[1][0]*cols[2][3]*cols[3][1] + cols[0][0]*cols[1][2]*cols[2][3]*cols[3][1] + cols[0][0] * cols[1][2] * cols[2][1] * cols[3][3] -
cols[0][3]*cols[1][1]*cols[2][0]*cols[3][2] - cols[0][1]*cols[1][3]*cols[2][0]*cols[3][2] - cols[0][1] * cols[1][0] * cols[2][2] * cols[3][3] +
cols[0][3]*cols[1][0]*cols[2][1]*cols[3][2] + cols[0][0]*cols[1][3]*cols[2][1]*cols[3][2] + cols[0][0] * cols[1][1] * cols[2][2] * cols[3][3];
cols[0][1]*cols[1][0]*cols[2][3]*cols[3][2] - cols[0][0]*cols[1][1]*cols[2][3]*cols[3][2] - }
cols[0][2]*cols[1][1]*cols[2][0]*cols[3][3] + cols[0][1]*cols[1][2]*cols[2][0]*cols[3][3] +
cols[0][2]*cols[1][0]*cols[2][1]*cols[3][3] - cols[0][0]*cols[1][2]*cols[2][1]*cols[3][3] - union {
cols[0][1]*cols[1][0]*cols[2][2]*cols[3][3] + cols[0][0]*cols[1][1]*cols[2][2]*cols[3][3]; Vec4 cols[4];
} float data[16] = {};
};
union {
Vec4 cols[4];
float data[16] = {};
};
}; };
inline bool operator==(const Mat4& l, const Mat4& r) { inline bool operator==(const Mat4 &l, const Mat4 &r) {
for(int i = 0; i < 16; i++) if(l.data[i] != r.data[i]) return false; for (int i = 0; i < 16; i++)
return true; if (l.data[i] != r.data[i])
return false;
return true;
} }
inline bool operator!=(const Mat4& l, const Mat4& r) { inline bool operator!=(const Mat4 &l, const Mat4 &r) {
for(int i = 0; i < 16; i++) if(l.data[i] != r.data[i]) return true; for (int i = 0; i < 16; i++)
return false; if (l.data[i] != r.data[i])
return true;
return false;
} }
inline Mat4 operator+(float s, const Mat4& m) { inline Mat4 operator+(float s, const Mat4 &m) {
Mat4 r; Mat4 r;
for(int i = 0; i < 4; i++) for (int i = 0; i < 4; i++)
r.cols[i] = m.cols[i] + s; r.cols[i] = m.cols[i] + s;
return r; return r;
} }
inline Mat4 operator-(float s, const Mat4& m) { inline Mat4 operator-(float s, const Mat4 &m) {
Mat4 r; Mat4 r;
for(int i = 0; i < 4; i++) for (int i = 0; i < 4; i++)
r.cols[i] = m.cols[i] - s; r.cols[i] = m.cols[i] - s;
return r; return r;
} }
inline Mat4 operator*(float s, const Mat4& m) { inline Mat4 operator*(float s, const Mat4 &m) {
Mat4 r; Mat4 r;
for(int i = 0; i < 4; i++) for (int i = 0; i < 4; i++)
r.cols[i] = m.cols[i] * s; r.cols[i] = m.cols[i] * s;
return r; return r;
} }
inline Mat4 operator/(float s, const Mat4& m) { inline Mat4 operator/(float s, const Mat4 &m) {
Mat4 r; Mat4 r;
for(int i = 0; i < 4; i++) for (int i = 0; i < 4; i++)
r.cols[i] = m.cols[i] / s; r.cols[i] = m.cols[i] / s;
return r; return r;
} }
const inline Mat4 Mat4::I = Mat4{Vec4{1.0f, 0.0f, 0.0f, 0.0f}, const inline Mat4 Mat4::I = Mat4{Vec4{1.0f, 0.0f, 0.0f, 0.0f}, Vec4{0.0f, 1.0f, 0.0f, 0.0f},
Vec4{0.0f, 1.0f, 0.0f, 0.0f}, Vec4{0.0f, 0.0f, 1.0f, 0.0f}, Vec4{0.0f, 0.0f, 0.0f, 1.0f}};
Vec4{0.0f, 0.0f, 1.0f, 0.0f}, const inline Mat4 Mat4::Zero = Mat4{Vec4{0.0f, 0.0f, 0.0f, 0.0f}, Vec4{0.0f, 0.0f, 0.0f, 0.0f},
Vec4{0.0f, 0.0f, 0.0f, 1.0f}}; Vec4{0.0f, 0.0f, 0.0f, 0.0f}, Vec4{0.0f, 0.0f, 0.0f, 0.0f}};
const inline Mat4 Mat4::Zero = Mat4{Vec4{0.0f, 0.0f, 0.0f, 0.0f},
Vec4{0.0f, 0.0f, 0.0f, 0.0f},
Vec4{0.0f, 0.0f, 0.0f, 0.0f},
Vec4{0.0f, 0.0f, 0.0f, 0.0f}};
inline Mat4 outer(Vec4 u, Vec4 v) { inline Mat4 outer(Vec4 u, Vec4 v) {
Mat4 B; Mat4 B;
for(int i = 0; i < 4; i++) for (int i = 0; i < 4; i++)
for(int j = 0; j < 4; j++) for (int j = 0; j < 4; j++)
B[i][j] = u[i]*v[j]; B[i][j] = u[i] * v[j];
return B; return B;
} }
inline Mat4 Mat4::transpose(const Mat4& m) { inline Mat4 Mat4::transpose(const Mat4 &m) {
Mat4 r; Mat4 r;
for(int i = 0; i < 4; i++) { for (int i = 0; i < 4; i++) {
for(int j = 0; j < 4; j++) { for (int j = 0; j < 4; j++) {
r[i][j] = m[j][i]; r[i][j] = m[j][i];
} }
} }
return r; return r;
} }
inline Mat4 Mat4::inverse(const Mat4& m) { inline Mat4 Mat4::inverse(const Mat4 &m) {
Mat4 r; Mat4 r;
r[0][0] = m[1][2]*m[2][3]*m[3][1] - m[1][3]*m[2][2]*m[3][1] + m[1][3]*m[2][1]*m[3][2] - m[1][1]*m[2][3]*m[3][2] - m[1][2]*m[2][1]*m[3][3] + m[1][1]*m[2][2]*m[3][3]; r[0][0] = m[1][2] * m[2][3] * m[3][1] - m[1][3] * m[2][2] * m[3][1] +
r[0][1] = m[0][3]*m[2][2]*m[3][1] - m[0][2]*m[2][3]*m[3][1] - m[0][3]*m[2][1]*m[3][2] + m[0][1]*m[2][3]*m[3][2] + m[0][2]*m[2][1]*m[3][3] - m[0][1]*m[2][2]*m[3][3]; m[1][3] * m[2][1] * m[3][2] - m[1][1] * m[2][3] * m[3][2] -
r[0][2] = m[0][2]*m[1][3]*m[3][1] - m[0][3]*m[1][2]*m[3][1] + m[0][3]*m[1][1]*m[3][2] - m[0][1]*m[1][3]*m[3][2] - m[0][2]*m[1][1]*m[3][3] + m[0][1]*m[1][2]*m[3][3]; m[1][2] * m[2][1] * m[3][3] + m[1][1] * m[2][2] * m[3][3];
r[0][3] = m[0][3]*m[1][2]*m[2][1] - m[0][2]*m[1][3]*m[2][1] - m[0][3]*m[1][1]*m[2][2] + m[0][1]*m[1][3]*m[2][2] + m[0][2]*m[1][1]*m[2][3] - m[0][1]*m[1][2]*m[2][3]; r[0][1] = m[0][3] * m[2][2] * m[3][1] - m[0][2] * m[2][3] * m[3][1] -
r[1][0] = m[1][3]*m[2][2]*m[3][0] - m[1][2]*m[2][3]*m[3][0] - m[1][3]*m[2][0]*m[3][2] + m[1][0]*m[2][3]*m[3][2] + m[1][2]*m[2][0]*m[3][3] - m[1][0]*m[2][2]*m[3][3]; m[0][3] * m[2][1] * m[3][2] + m[0][1] * m[2][3] * m[3][2] +
r[1][1] = m[0][2]*m[2][3]*m[3][0] - m[0][3]*m[2][2]*m[3][0] + m[0][3]*m[2][0]*m[3][2] - m[0][0]*m[2][3]*m[3][2] - m[0][2]*m[2][0]*m[3][3] + m[0][0]*m[2][2]*m[3][3]; m[0][2] * m[2][1] * m[3][3] - m[0][1] * m[2][2] * m[3][3];
r[1][2] = m[0][3]*m[1][2]*m[3][0] - m[0][2]*m[1][3]*m[3][0] - m[0][3]*m[1][0]*m[3][2] + m[0][0]*m[1][3]*m[3][2] + m[0][2]*m[1][0]*m[3][3] - m[0][0]*m[1][2]*m[3][3]; r[0][2] = m[0][2] * m[1][3] * m[3][1] - m[0][3] * m[1][2] * m[3][1] +
r[1][3] = m[0][2]*m[1][3]*m[2][0] - m[0][3]*m[1][2]*m[2][0] + m[0][3]*m[1][0]*m[2][2] - m[0][0]*m[1][3]*m[2][2] - m[0][2]*m[1][0]*m[2][3] + m[0][0]*m[1][2]*m[2][3]; m[0][3] * m[1][1] * m[3][2] - m[0][1] * m[1][3] * m[3][2] -
r[2][0] = m[1][1]*m[2][3]*m[3][0] - m[1][3]*m[2][1]*m[3][0] + m[1][3]*m[2][0]*m[3][1] - m[1][0]*m[2][3]*m[3][1] - m[1][1]*m[2][0]*m[3][3] + m[1][0]*m[2][1]*m[3][3]; m[0][2] * m[1][1] * m[3][3] + m[0][1] * m[1][2] * m[3][3];
r[2][1] = m[0][3]*m[2][1]*m[3][0] - m[0][1]*m[2][3]*m[3][0] - m[0][3]*m[2][0]*m[3][1] + m[0][0]*m[2][3]*m[3][1] + m[0][1]*m[2][0]*m[3][3] - m[0][0]*m[2][1]*m[3][3]; r[0][3] = m[0][3] * m[1][2] * m[2][1] - m[0][2] * m[1][3] * m[2][1] -
r[2][2] = m[0][1]*m[1][3]*m[3][0] - m[0][3]*m[1][1]*m[3][0] + m[0][3]*m[1][0]*m[3][1] - m[0][0]*m[1][3]*m[3][1] - m[0][1]*m[1][0]*m[3][3] + m[0][0]*m[1][1]*m[3][3]; m[0][3] * m[1][1] * m[2][2] + m[0][1] * m[1][3] * m[2][2] +
r[2][3] = m[0][3]*m[1][1]*m[2][0] - m[0][1]*m[1][3]*m[2][0] - m[0][3]*m[1][0]*m[2][1] + m[0][0]*m[1][3]*m[2][1] + m[0][1]*m[1][0]*m[2][3] - m[0][0]*m[1][1]*m[2][3]; m[0][2] * m[1][1] * m[2][3] - m[0][1] * m[1][2] * m[2][3];
r[3][0] = m[1][2]*m[2][1]*m[3][0] - m[1][1]*m[2][2]*m[3][0] - m[1][2]*m[2][0]*m[3][1] + m[1][0]*m[2][2]*m[3][1] + m[1][1]*m[2][0]*m[3][2] - m[1][0]*m[2][1]*m[3][2]; r[1][0] = m[1][3] * m[2][2] * m[3][0] - m[1][2] * m[2][3] * m[3][0] -
r[3][1] = m[0][1]*m[2][2]*m[3][0] - m[0][2]*m[2][1]*m[3][0] + m[0][2]*m[2][0]*m[3][1] - m[0][0]*m[2][2]*m[3][1] - m[0][1]*m[2][0]*m[3][2] + m[0][0]*m[2][1]*m[3][2]; m[1][3] * m[2][0] * m[3][2] + m[1][0] * m[2][3] * m[3][2] +
r[3][2] = m[0][2]*m[1][1]*m[3][0] - m[0][1]*m[1][2]*m[3][0] - m[0][2]*m[1][0]*m[3][1] + m[0][0]*m[1][2]*m[3][1] + m[0][1]*m[1][0]*m[3][2] - m[0][0]*m[1][1]*m[3][2]; m[1][2] * m[2][0] * m[3][3] - m[1][0] * m[2][2] * m[3][3];
r[3][3] = m[0][1]*m[1][2]*m[2][0] - m[0][2]*m[1][1]*m[2][0] + m[0][2]*m[1][0]*m[2][1] - m[0][0]*m[1][2]*m[2][1] - m[0][1]*m[1][0]*m[2][2] + m[0][0]*m[1][1]*m[2][2]; r[1][1] = m[0][2] * m[2][3] * m[3][0] - m[0][3] * m[2][2] * m[3][0] +
r /= m.det(); m[0][3] * m[2][0] * m[3][2] - m[0][0] * m[2][3] * m[3][2] -
return r; m[0][2] * m[2][0] * m[3][3] + m[0][0] * m[2][2] * m[3][3];
r[1][2] = m[0][3] * m[1][2] * m[3][0] - m[0][2] * m[1][3] * m[3][0] -
m[0][3] * m[1][0] * m[3][2] + m[0][0] * m[1][3] * m[3][2] +
m[0][2] * m[1][0] * m[3][3] - m[0][0] * m[1][2] * m[3][3];
r[1][3] = m[0][2] * m[1][3] * m[2][0] - m[0][3] * m[1][2] * m[2][0] +
m[0][3] * m[1][0] * m[2][2] - m[0][0] * m[1][3] * m[2][2] -
m[0][2] * m[1][0] * m[2][3] + m[0][0] * m[1][2] * m[2][3];
r[2][0] = m[1][1] * m[2][3] * m[3][0] - m[1][3] * m[2][1] * m[3][0] +
m[1][3] * m[2][0] * m[3][1] - m[1][0] * m[2][3] * m[3][1] -
m[1][1] * m[2][0] * m[3][3] + m[1][0] * m[2][1] * m[3][3];
r[2][1] = m[0][3] * m[2][1] * m[3][0] - m[0][1] * m[2][3] * m[3][0] -
m[0][3] * m[2][0] * m[3][1] + m[0][0] * m[2][3] * m[3][1] +
m[0][1] * m[2][0] * m[3][3] - m[0][0] * m[2][1] * m[3][3];
r[2][2] = m[0][1] * m[1][3] * m[3][0] - m[0][3] * m[1][1] * m[3][0] +
m[0][3] * m[1][0] * m[3][1] - m[0][0] * m[1][3] * m[3][1] -
m[0][1] * m[1][0] * m[3][3] + m[0][0] * m[1][1] * m[3][3];
r[2][3] = m[0][3] * m[1][1] * m[2][0] - m[0][1] * m[1][3] * m[2][0] -
m[0][3] * m[1][0] * m[2][1] + m[0][0] * m[1][3] * m[2][1] +
m[0][1] * m[1][0] * m[2][3] - m[0][0] * m[1][1] * m[2][3];
r[3][0] = m[1][2] * m[2][1] * m[3][0] - m[1][1] * m[2][2] * m[3][0] -
m[1][2] * m[2][0] * m[3][1] + m[1][0] * m[2][2] * m[3][1] +
m[1][1] * m[2][0] * m[3][2] - m[1][0] * m[2][1] * m[3][2];
r[3][1] = m[0][1] * m[2][2] * m[3][0] - m[0][2] * m[2][1] * m[3][0] +
m[0][2] * m[2][0] * m[3][1] - m[0][0] * m[2][2] * m[3][1] -
m[0][1] * m[2][0] * m[3][2] + m[0][0] * m[2][1] * m[3][2];
r[3][2] = m[0][2] * m[1][1] * m[3][0] - m[0][1] * m[1][2] * m[3][0] -
m[0][2] * m[1][0] * m[3][1] + m[0][0] * m[1][2] * m[3][1] +
m[0][1] * m[1][0] * m[3][2] - m[0][0] * m[1][1] * m[3][2];
r[3][3] = m[0][1] * m[1][2] * m[2][0] - m[0][2] * m[1][1] * m[2][0] +
m[0][2] * m[1][0] * m[2][1] - m[0][0] * m[1][2] * m[2][1] -
m[0][1] * m[1][0] * m[2][2] + m[0][0] * m[1][1] * m[2][2];
r /= m.det();
return r;
} }
inline Mat4 Mat4::rotate_to(Vec3 dir) { inline Mat4 Mat4::rotate_to(Vec3 dir) {
dir.normalize(); dir.normalize();
if(dir.y == 1.0f) return Mat4::I; if (dir.y == 1.0f)
else if(dir.y == -1.0f) return Mat4{Vec4{1.0f,0.0f,0.0f,0.0f},Vec4{0.0f,-1.0f,0.0f,0.0f}, return Mat4::I;
Vec4{0.0f,0.0f,1.0f,0.0},Vec4{0.0f,0.0f,0.0f,1.0f}}; else if (dir.y == -1.0f)
else { return Mat4{Vec4{1.0f, 0.0f, 0.0f, 0.0f}, Vec4{0.0f, -1.0f, 0.0f, 0.0f},
Vec3 x = cross(dir, Vec3{0.0f, 1.0f, 0.0f}).unit(); Vec4{0.0f, 0.0f, 1.0f, 0.0}, Vec4{0.0f, 0.0f, 0.0f, 1.0f}};
Vec3 z = cross(x, dir).unit(); else {
return Mat4{Vec4{x,0.0f},Vec4{dir,0.0f},Vec4{z,0.0f},Vec4{0.0f,0.0f,0.0f,1.0f}}; Vec3 x = cross(dir, Vec3{0.0f, 1.0f, 0.0f}).unit();
} Vec3 z = cross(x, dir).unit();
return Mat4{Vec4{x, 0.0f}, Vec4{dir, 0.0f}, Vec4{z, 0.0f}, Vec4{0.0f, 0.0f, 0.0f, 1.0f}};
}
} }
inline Mat4 Mat4::rotate_z_to(Vec3 dir) { inline Mat4 Mat4::rotate_z_to(Vec3 dir) {
Mat4 y = rotate_to(dir); Mat4 y = rotate_to(dir);
Vec4 _y = y[1]; Vec4 _y = y[1];
Vec4 _z = y[2]; Vec4 _z = y[2];
y[1] = _z; y[1] = _z;
y[2] = -_y; y[2] = -_y;
return y; return y;
} }
inline Mat4 Mat4::axes(Vec3 x, Vec3 y, Vec3 z) { inline Mat4 Mat4::axes(Vec3 x, Vec3 y, Vec3 z) {
return Mat4{Vec4{x, 0.0f}, Vec4{y, 0.0f}, Vec4{z, 0.0f}, Vec4{0.0f, 0.0f, 0.0f, 1.0f}}; return Mat4{Vec4{x, 0.0f}, Vec4{y, 0.0f}, Vec4{z, 0.0f}, Vec4{0.0f, 0.0f, 0.0f, 1.0f}};
} }
inline Mat4 Mat4::translate(Vec3 t) { inline Mat4 Mat4::translate(Vec3 t) {
Mat4 r; Mat4 r;
r[3] = Vec4(t, 1.0f); r[3] = Vec4(t, 1.0f);
return r; return r;
} }
inline Mat4 Mat4::euler(Vec3 angles) { inline Mat4 Mat4::euler(Vec3 angles) {
return Mat4::rotate(angles.z, Vec3{0.0f, 0.0f, 1.0f}) * return Mat4::rotate(angles.z, Vec3{0.0f, 0.0f, 1.0f}) *
Mat4::rotate(angles.y, Vec3{0.0f, 1.0f, 0.0f}) * Mat4::rotate(angles.y, Vec3{0.0f, 1.0f, 0.0f}) *
Mat4::rotate(angles.x, Vec3{1.0f, 0.0f, 0.0f}); Mat4::rotate(angles.x, Vec3{1.0f, 0.0f, 0.0f});
} }
inline Mat4 Mat4::rotate(float t, Vec3 axis) { inline Mat4 Mat4::rotate(float t, Vec3 axis) {
Mat4 ret; Mat4 ret;
float c = std::cos(Radians(t)); float c = std::cos(Radians(t));
float s = std::sin(Radians(t)); float s = std::sin(Radians(t));
axis.normalize(); axis.normalize();
Vec3 temp = axis * (1.0f - c); Vec3 temp = axis * (1.0f - c);
ret[0][0] = c + temp[0] * axis[0]; ret[0][0] = c + temp[0] * axis[0];
ret[0][1] = temp[0] * axis[1] + s * axis[2]; ret[0][1] = temp[0] * axis[1] + s * axis[2];
ret[0][2] = temp[0] * axis[2] - s * axis[1]; ret[0][2] = temp[0] * axis[2] - s * axis[1];
ret[1][0] = temp[1] * axis[0] - s * axis[2]; ret[1][0] = temp[1] * axis[0] - s * axis[2];
ret[1][1] = c + temp[1] * axis[1]; ret[1][1] = c + temp[1] * axis[1];
ret[1][2] = temp[1] * axis[2] + s * axis[0]; ret[1][2] = temp[1] * axis[2] + s * axis[0];
ret[2][0] = temp[2] * axis[0] + s * axis[1]; ret[2][0] = temp[2] * axis[0] + s * axis[1];
ret[2][1] = temp[2] * axis[1] - s * axis[0]; ret[2][1] = temp[2] * axis[1] - s * axis[0];
ret[2][2] = c + temp[2] * axis[2]; ret[2][2] = c + temp[2] * axis[2];
return ret; return ret;
} }
inline Mat4 Mat4::scale(Vec3 s) { inline Mat4 Mat4::scale(Vec3 s) {
Mat4 r; Mat4 r;
r[0][0] = s.x; r[0][0] = s.x;
r[1][1] = s.y; r[1][1] = s.y;
r[2][2] = s.z; r[2][2] = s.z;
return r; return r;
} }
inline Mat4 Mat4::ortho(float l, float r, float b, float t, float n, float f) { inline Mat4 Mat4::ortho(float l, float r, float b, float t, float n, float f) {
Mat4 rs; Mat4 rs;
rs[0][0] = 2.0f / (r - l); rs[0][0] = 2.0f / (r - l);
rs[1][1] = 2.0f / (t - b); rs[1][1] = 2.0f / (t - b);
rs[2][2] = 2.0f / (n - f); rs[2][2] = 2.0f / (n - f);
rs[3][0] = (-l - r) / (r - l); rs[3][0] = (-l - r) / (r - l);
rs[3][1] = (-b - t) / (t - b); rs[3][1] = (-b - t) / (t - b);
rs[3][2] = - n / (f - n); rs[3][2] = -n / (f - n);
return rs; return rs;
} }
inline Mat4 Mat4::project(float fov, float ar, float n) { inline Mat4 Mat4::project(float fov, float ar, float n) {
float f = 1.0f / std::tan(Radians(fov) / 2.0f); float f = 1.0f / std::tan(Radians(fov) / 2.0f);
Mat4 r; Mat4 r;
r[0][0] = f / ar; r[0][0] = f / ar;
r[1][1] = f; r[1][1] = f;
r[2][2] = 0.0f; r[2][2] = 0.0f;
r[3][3] = 0.0f; r[3][3] = 0.0f;
r[3][2] = n; r[3][2] = n;
r[2][3] = -1.0f; r[2][3] = -1.0f;
return r; return r;
} }
inline Mat4 Mat4::look_at(Vec3 pos, Vec3 at, Vec3 up) { inline Mat4 Mat4::look_at(Vec3 pos, Vec3 at, Vec3 up) {
Mat4 r = Mat4::Zero; Mat4 r = Mat4::Zero;
Vec3 F = (at - pos).unit(); Vec3 F = (at - pos).unit();
Vec3 S = cross(F, up).unit(); Vec3 S = cross(F, up).unit();
Vec3 U = cross(S, F).unit(); Vec3 U = cross(S, F).unit();
r[0][0] = S.x; r[0][0] = S.x;
r[0][1] = U.x; r[0][1] = U.x;
r[0][2] = -F.x; r[0][2] = -F.x;
r[1][0] = S.y; r[1][0] = S.y;
r[1][1] = U.y; r[1][1] = U.y;
r[1][2] = -F.y; r[1][2] = -F.y;
r[2][0] = S.z; r[2][0] = S.z;
r[2][1] = U.z; r[2][1] = U.z;
r[2][2] = -F.z; r[2][2] = -F.z;
r[3][0] = -dot(S, pos); r[3][0] = -dot(S, pos);
r[3][1] = -dot(U, pos); r[3][1] = -dot(U, pos);
r[3][2] = dot(F, pos); r[3][2] = dot(F, pos);
r[3][3] = 1.0f; r[3][3] = 1.0f;
return r; return r;
} }
inline std::ostream& operator<<(std::ostream& out, Mat4 m) { inline std::ostream &operator<<(std::ostream &out, Mat4 m) {
out << "{" << m[0] << "," << m[1] << "," << m[2] << "," << m[3] << "}"; out << "{" << m[0] << "," << m[1] << "," << m[2] << "," << m[3] << "}";
return out; return out;
} }
#pragma once #pragma once
#include <cmath>
#include <algorithm> #include <algorithm>
#include <cmath>
template<class... Ts> struct overloaded : Ts... { using Ts::operator()...; }; template <class... Ts> struct overloaded : Ts... { using Ts::operator()...; };
template<class... Ts> overloaded(Ts...) -> overloaded<Ts...>; template <class... Ts> overloaded(Ts...) -> overloaded<Ts...>;
#include "line.h"
#include "plane.h"
#include "vec2.h" #include "vec2.h"
#include "vec3.h" #include "vec3.h"
#include "vec4.h" #include "vec4.h"
#include "line.h"
#include "plane.h"
#define EPS_F 0.00001f #define EPS_F 0.00001f
#define PI_F 3.14159265358979323846264338327950288f #define PI_F 3.14159265358979323846264338327950288f
#define Radians(v) ((v) * (PI_F / 180.0f)) #define Radians(v) ((v) * (PI_F / 180.0f))
#define Degrees(v) ((v) * (180.0f / PI_F)) #define Degrees(v) ((v) * (180.0f / PI_F))
template<typename T> template <typename T> inline T clamp(T x, T min, T max) { return std::min(std::max(x, min), max); }
inline T clamp(T x, T min, T max) { template <> inline Vec2 clamp(Vec2 v, Vec2 min, Vec2 max) {
return std::min(std::max(x,min),max); return Vec2(clamp(v.x, min.x, max.x), clamp(v.y, min.y, max.y));
}
template<>
inline Vec2 clamp(Vec2 v, Vec2 min, Vec2 max) {
return Vec2(clamp(v.x, min.x, max.x), clamp(v.y, min.y, max.y));
}
template<>
inline Vec3 clamp(Vec3 v, Vec3 min, Vec3 max) {
return Vec3(clamp(v.x, min.x, max.x), clamp(v.y, min.y, max.y), clamp(v.z, min.z, max.z));
} }
template<> template <> inline Vec3 clamp(Vec3 v, Vec3 min, Vec3 max) {
inline Vec4 clamp(Vec4 v, Vec4 min, Vec4 max) { return Vec3(clamp(v.x, min.x, max.x), clamp(v.y, min.y, max.y), clamp(v.z, min.z, max.z));
return Vec4(clamp(v.x, min.x, max.x), clamp(v.y, min.y, max.y), clamp(v.z, min.z, max.z), clamp(v.w, min.w, max.w));
} }
template<typename T> template <> inline Vec4 clamp(Vec4 v, Vec4 min, Vec4 max) {
T lerp(T start, T end, float t) { return Vec4(clamp(v.x, min.x, max.x), clamp(v.y, min.y, max.y), clamp(v.z, min.z, max.z),
return start + (end - start) * t; clamp(v.w, min.w, max.w));
}
inline float sign(float x) {
return x > 0.0f ? 1.0f : x < 0.0f ? -1.0f : 0.0f;
}
inline float frac(float x) {
return x - (long long)x;
} }
template <typename T> T lerp(T start, T end, float t) { return start + (end - start) * t; }
inline float sign(float x) { return x > 0.0f ? 1.0f : x < 0.0f ? -1.0f : 0.0f; }
inline float frac(float x) { return x - (long long)x; }
inline float smoothstep(float e0, float e1, float x) { inline float smoothstep(float e0, float e1, float x) {
float t = clamp((x - e0) / (e1 - e0), 0.0f, 1.0f); float t = clamp((x - e0) / (e1 - e0), 0.0f, 1.0f);
return t * t * (3.0f - 2.0f * t); return t * t * (3.0f - 2.0f * t);
} }
#include "bbox.h" #include "bbox.h"
#include "mat4.h" #include "mat4.h"
#include "quat.h" #include "quat.h"
#include "ray.h" #include "ray.h"
#pragma once #pragma once
#include <cmath>
#include <algorithm> #include <algorithm>
#include <cmath>
#include <ostream> #include <ostream>
#include "line.h" #include "line.h"
...@@ -10,11 +10,10 @@ ...@@ -10,11 +10,10 @@
struct Plane { struct Plane {
Plane() = default; Plane() = default;
/// Create plane from (a,b,c,d) /// Create plane from (a,b,c,d)
explicit Plane(Vec4 p) : explicit Plane(Vec4 p) : p(p) {}
p(p) {
}
/// Create plane from point and unit normal /// Create plane from point and unit normal
explicit Plane(Vec3 point, Vec3 n) { explicit Plane(Vec3 point, Vec3 n) {
p.x = n.x; p.x = n.x;
...@@ -22,17 +21,14 @@ struct Plane { ...@@ -22,17 +21,14 @@ struct Plane {
p.z = n.z; p.z = n.z;
p.w = dot(point, n.unit()); p.w = dot(point, n.unit());
} }
Plane(const Plane& src) = default;
~Plane() = default;
Plane operator=(Plane v) { Plane(const Plane &) = default;
p = v.p; Plane &operator=(const Plane &) = default;
return *this; ~Plane() = default;
}
/// Calculate intersection point between plane and line. /// Calculate intersection point between plane and line.
/// Returns false if the hit point is 'backward' along the line relative to pt.dir /// Returns false if the hit point is 'backward' along the line relative to pt.dir
bool hit(Line line, Vec3& pt) const { bool hit(Line line, Vec3 &pt) const {
Vec3 n = p.xyz(); Vec3 n = p.xyz();
float t = (p.w - dot(line.point, n)) / dot(line.dir, n); float t = (p.w - dot(line.point, n)) / dot(line.dir, n);
pt = line.at(t); pt = line.at(t);
...@@ -42,7 +38,7 @@ struct Plane { ...@@ -42,7 +38,7 @@ struct Plane {
Vec4 p; Vec4 p;
}; };
inline std::ostream& operator<<(std::ostream& out, Plane v) { inline std::ostream &operator<<(std::ostream &out, Plane v) {
out << "Plane" << v.p; out << "Plane" << v.p;
return out; return out;
} }
#pragma once #pragma once
#include <cmath>
#include <algorithm> #include <algorithm>
#include <cmath>
#include "log.h" #include "log.h"
#include "mat4.h"
#include "vec3.h" #include "vec3.h"
#include "vec4.h" #include "vec4.h"
#include "mat4.h"
struct Quat { struct Quat {
Quat() { Quat() {
x = 0.0f; x = 0.0f;
y = 0.0f; y = 0.0f;
z = 0.0f; z = 0.0f;
w = 1.0f; w = 1.0f;
} }
explicit Quat(float _x, float _y, float _z, float _w) { explicit Quat(float _x, float _y, float _z, float _w) {
x = _x; x = _x;
y = _y; y = _y;
z = _z; z = _z;
w = _w; w = _w;
} }
explicit Quat(Vec3 complex, float real) { explicit Quat(Vec3 complex, float real) {
x = complex.x; x = complex.x;
y = complex.y; y = complex.y;
z = complex.z; z = complex.z;
w = real; w = real;
} }
explicit Quat(const Vec4& src) { explicit Quat(const Vec4 &src) {
x = src.x; x = src.x;
y = src.y; y = src.y;
z = src.z; z = src.z;
w = src.w; w = src.w;
} }
Quat(const Quat& src) {
x = src.x; Quat(const Quat &) = default;
y = src.y; Quat &operator=(const Quat &) = default;
z = src.z; ~Quat() = default;
w = src.w;
} /// Create unit quaternion representing given axis-angle rotation
~Quat() = default; static Quat axis_angle(Vec3 axis, float angle) {
axis.normalize();
/// Create unit quaternion representing given axis-angle rotation angle = Radians(angle) / 2.0f;
static Quat axis_angle(Vec3 axis, float angle) { float sin = std::sin(angle);
axis.normalize(); float x = sin * axis.x;
angle = Radians(angle) / 2.0f; float y = sin * axis.y;
float sin = std::sin(angle); float z = sin * axis.z;
float x = sin * axis.x; float w = std::cos(angle);
float y = sin * axis.y; return Quat(x, y, z, w).unit();
float z = sin * axis.z; }
float w = std::cos(angle);
return Quat(x,y,z,w).unit(); /// Create unit quaternion representing given euler angles (XYZ)
} static Quat euler(Vec3 angles) {
if (angles == Vec3{0.0f, 0.0f, 180.0f} || angles == Vec3{180.0f, 0.0f, 0.0f})
/// Create unit quaternion representing given euler angles (XYZ) return Quat{0.0f, 0.0f, -1.0f, 0.0f};
static Quat euler(Vec3 angles) { float c1 = std::cos(Radians(angles[2] * 0.5f));
if(angles == Vec3{0.0f,0.0f,180.0f} || angles == Vec3{180.0f,0.0f,0.0f}) float c2 = std::cos(Radians(angles[1] * 0.5f));
return Quat{0.0f,0.0f,-1.0f,0.0f}; float c3 = std::cos(Radians(angles[0] * 0.5f));
float c1 = std::cos(Radians(angles[2] * 0.5f)); float s1 = std::sin(Radians(angles[2] * 0.5f));
float c2 = std::cos(Radians(angles[1] * 0.5f)); float s2 = std::sin(Radians(angles[1] * 0.5f));
float c3 = std::cos(Radians(angles[0] * 0.5f)); float s3 = std::sin(Radians(angles[0] * 0.5f));
float s1 = std::sin(Radians(angles[2] * 0.5f)); float x = c1 * c2 * s3 - s1 * s2 * c3;
float s2 = std::sin(Radians(angles[1] * 0.5f)); float y = c1 * s2 * c3 + s1 * c2 * s3;
float s3 = std::sin(Radians(angles[0] * 0.5f)); float z = s1 * c2 * c3 - c1 * s2 * s3;
float x = c1*c2*s3 - s1*s2*c3; float w = c1 * c2 * c3 + s1 * s2 * s3;
float y = c1*s2*c3 + s1*c2*s3; return Quat(x, y, z, w);
float z = s1*c2*c3 - c1*s2*s3; }
float w = c1*c2*c3 + s1*s2*s3;
return Quat(x,y,z,w); float &operator[](int idx) {
} assert(idx >= 0 && idx <= 3);
return data[idx];
Quat operator=(const Quat& v) { }
x = v.x; float operator[](int idx) const {
y = v.y; assert(idx >= 0 && idx <= 3);
z = v.z; return data[idx];
w = v.w; }
return *this;
} Quat conjugate() const { return Quat(-x, -y, -z, w); }
Quat inverse() const { return conjugate().unit(); }
float& operator[](int idx) { Vec3 complex() const { return Vec3(x, y, z); }
assert(idx >= 0 && idx <= 3); float real() const { return w; }
return data[idx];
} float norm_squared() const { return x * x + y * y + z * z + w * w; }
float operator[](int idx) const { float norm() const { return std::sqrt(norm_squared()); }
assert(idx >= 0 && idx <= 3); Quat unit() const {
return data[idx]; float n = norm();
} return Quat(x / n, y / n, z / n, w / n);
}
Quat conjugate() const {
return Quat(-x, -y, -z, w); Quat operator*(const Quat &r) const {
} return Quat(y * r.z - z * r.y + x * r.w + w * r.x, z * r.x - x * r.z + y * r.w + w * r.y,
Quat inverse() const { x * r.y - y * r.x + z * r.w + w * r.z, w * r.w - x * r.x - y * r.y - z * r.z);
return conjugate().unit(); }
}
Vec3 complex() const { Quat operator+(const Quat &r) const { return Quat(x + r.x, y + r.y, z + r.z, w + r.w); }
return Vec3(x,y,z); Quat operator-(const Quat &r) const { return Quat(x - r.x, y - r.y, z - r.z, w - r.w); }
}
float real() const { void scale(float f) {
return w; x *= f;
} y *= f;
z *= f;
float norm_squared() const { w *= f;
return x * x + y * y + z * z + w * w; }
} void make_log() {
float norm() const { float a0 = w;
return std::sqrt(norm_squared()); w = 0.0f;
} if (std::abs(a0) < 1.0f) {
Quat unit() const { float angle = std::acos(a0);
float n = norm(); float sin_angle = std::sin(angle);
return Quat(x / n, y / n, z / n, w / n); if (std::abs(sin_angle) > EPS_F) {
} float coeff = angle / sin_angle;
x *= coeff;
Quat operator*(const Quat& r) const { y *= coeff;
return Quat(y*r.z - z*r.y + x*r.w + w*r.x, z *= coeff;
z*r.x - x*r.z + y*r.w + w*r.y, }
x*r.y - y*r.x + z*r.w + w*r.z, }
w*r.w - x*r.x - y*r.y - z*r.z); }
} void make_exp() {
float angle = std::sqrt(x * x + y * y + z * z);
Quat operator+(const Quat& r) const { float sin_angle = std::sin(angle);
return Quat(x+r.x,y+r.y,z+r.z,w+r.w); w = std::cos(angle);
} if (sin_angle > EPS_F) {
Quat operator-(const Quat& r) const { float coeff = sin_angle / angle;
return Quat(x-r.x,y-r.y,z-r.z,w-r.w); x *= coeff;
} y *= coeff;
z *= coeff;
void scale(float f) { }
x *= f; }
y *= f;
z *= f; /// Convert quaternion to equivalent euler angle rotation (XYZ)
w *= f; Vec3 to_euler() const { return unit().to_mat().to_euler(); }
}
void make_log() { /// Convert quaternion to equivalent rotation matrix (orthonormal, 3x3)
float a0 = w; Mat4 to_mat() const {
w = 0.0f; return Mat4{
if(std::abs(a0) < 1.0f) { Vec4{1 - 2 * y * y - 2 * z * z, 2 * x * y + 2 * z * w, 2 * x * z - 2 * y * w, 0.0f},
float angle = std::acos(a0); Vec4{2 * x * y - 2 * z * w, 1 - 2 * x * x - 2 * z * z, 2 * y * z + 2 * x * w, 0.0f},
float sin_angle = std::sin(angle); Vec4{2 * x * z + 2 * y * w, 2 * y * z - 2 * x * w, 1 - 2 * x * x - 2 * y * y, 0.0f},
if(std::abs(sin_angle) > EPS_F) { Vec4{0.0f, 0.0f, 0.0f, 1.0f}};
float coeff = angle / sin_angle; }
x *= coeff;
y *= coeff; /// Apply rotation to given vector
z *= coeff; Vec3 rotate(Vec3 v) const { return (((*this) * Quat(v, 0)) * conjugate()).complex(); }
}
} /// Spherical linear interpolation between this and another quaternion weighted by t.
} Quat slerp(Quat q, float t) {
void make_exp() { float omega = std::acos(clamp(x * q.x + y * q.y + z * q.z + w * q.w, -1.0f, 1.0f));
float angle = std::sqrt(x*x + y*y + z*z);
float sin_angle = std::sin(angle); if (std::abs(omega) < 16.0f * FLT_EPSILON) {
w = std::cos(angle); omega = 16.0f * FLT_EPSILON;
if(sin_angle > EPS_F) { }
float coeff = sin_angle / angle; float som = std::sin(omega);
x *= coeff; float st0 = std::sin((1 - t) * omega) / som;
y *= coeff; float st1 = std::sin(t * omega) / som;
z *= coeff;
} return Quat(x * st0 + q.x * st1, y * st0 + q.y * st1, z * st0 + q.z * st1,
} w * st0 + q.w * st1);
}
/// Convert quaternion to equivalent euler angle rotation (XYZ)
Vec3 to_euler() const { /// Are all members real numbers?
return unit().to_mat().to_euler(); bool valid() const {
} return !(std::isinf(x) || std::isinf(y) || std::isinf(z) || std::isinf(w));
}
/// Convert quaternion to equivalent rotation matrix (orthonormal, 3x3)
Mat4 to_mat() const { union {
return Mat4{ struct {
Vec4{1-2*y*y-2*z*z, 2*x*y + 2*z*w, 2*x*z - 2*y*w, 0.0f}, float x;
Vec4{2*x*y - 2*z*w, 1-2*x*x-2*z*z, 2*y*z + 2*x*w, 0.0f}, float y;
Vec4{2*x*z + 2*y*w, 2*y*z - 2*x*w, 1-2*x*x-2*y*y, 0.0f}, float z;
Vec4{0.0f, 0.0f, 0.0f, 1.0f} float w;
}; };
} float data[4] = {};
};
/// Apply rotation to given vector
Vec3 rotate(Vec3 v) const {
return (((*this) * Quat(v, 0)) * conjugate()).complex();
}
/// Spherical linear interpolation between this and another quaternion weighted by t.
Quat slerp(Quat q, float t) {
float omega = std::acos(clamp(x*q.x + y*q.y +
z*q.z + w*q.w,
-1.0f, 1.0f));
if(std::abs(omega) < 16.0f * FLT_EPSILON) {
omega = 16.0f * FLT_EPSILON;
}
float som = std::sin(omega);
float st0 = std::sin((1-t) * omega) / som;
float st1 = std::sin(t * omega) / som;
return Quat(x*st0 + q.x*st1,
y*st0 + q.y*st1,
z*st0 + q.z*st1,
w*st0 + q.w*st1);
}
/// Are all members real numbers?
bool valid() const {
return !(std::isinf(x) || std::isinf(y) || std::isinf(z) || std::isinf(w));
}
union {
struct {
float x;
float y;
float z;
float w;
};
float data[4] = {};
};
}; };
inline Quat slerp(Quat q0, Quat q1, float t) { inline Quat slerp(Quat q0, Quat q1, float t) { return q0.slerp(q1, t); }
return q0.slerp(q1, t);
}
inline std::ostream& operator<<(std::ostream& out, Quat q) { inline std::ostream &operator<<(std::ostream &out, Quat q) {
out << "Quat{" << q.x << "," << q.y << "," << q.z << "," << q.w << "}"; out << "Quat{" << q.x << "," << q.y << "," << q.z << "," << q.w << "}";
return out; return out;
} }
...@@ -2,50 +2,43 @@ ...@@ -2,50 +2,43 @@
#pragma once #pragma once
#include <cmath> #include <cmath>
#include <ostream>
#include <limits> #include <limits>
#include <ostream>
#include "../lib/mathlib.h" #include "../lib/mathlib.h"
struct Ray { struct Ray {
Ray() : time_bounds(0.0f, std::numeric_limits<float>::max()) {} Ray() : time_bounds(0.0f, std::numeric_limits<float>::max()) {}
/// Create Ray from point and direction
explicit Ray(Vec3 point, Vec3 dir) : /// Create Ray from point and direction
point(point), explicit Ray(Vec3 point, Vec3 dir)
dir(dir.unit()), : point(point), dir(dir.unit()), time_bounds(0.0f, std::numeric_limits<float>::max()) {}
time_bounds(0.0f, std::numeric_limits<float>::max()) {
} Ray(const Ray &) = default;
Ray(const Ray& src) = default; Ray &operator=(const Ray &) = default;
~Ray() = default;
Ray operator=(Ray v) {
point = v.point; /// Get point on Ray at time t
dir = v.dir; Vec3 at(float t) const { return point + t * dir; }
return *this;
} /// Move ray into the space defined by this tranform matrix
void transform(const Mat4 &trans) {
/// Get point on Ray at time t point = trans * point;
Vec3 at(float t) const { dir = trans.rotate(dir);
return point + t * dir; }
}
/// Move ray into the space defined by this tranform matrix
void transform(const Mat4& trans) {
point = trans * point;
dir = trans.rotate(dir);
}
/// The origin or starting point of this ray /// The origin or starting point of this ray
Vec3 point; Vec3 point;
/// The unit direction the ray travels in /// The unit direction the ray travels in
Vec3 dir; Vec3 dir;
/// The minimum and maximum time/distance at which this ray should exist /// The minimum and maximum time/distance at which this ray should exist
Vec2 time_bounds; Vec2 time_bounds;
/// Recursive depth of ray /// Recursive depth of ray
size_t depth = 0; size_t depth = 0;
}; };
inline std::ostream& operator<<(std::ostream& out, Ray r) { inline std::ostream &operator<<(std::ostream &out, Ray r) {
out << "Ray{" << r.point << "," << r.dir << "}"; out << "Ray{" << r.point << "," << r.dir << "}";
return out; return out;
} }
...@@ -9,127 +9,93 @@ ...@@ -9,127 +9,93 @@
struct Spectrum { struct Spectrum {
Spectrum() { Spectrum() {
r = 0.0f; r = 0.0f;
g = 0.0f; g = 0.0f;
b = 0.0f; b = 0.0f;
} }
explicit Spectrum(float _r, float _g, float _b) { explicit Spectrum(float _r, float _g, float _b) {
r = _r; r = _r;
g = _g; g = _g;
b = _b; b = _b;
} }
explicit Spectrum(float f) { explicit Spectrum(float f) { r = g = b = f; }
r = g = b = f;
} Spectrum(const Spectrum &) = default;
Spectrum(const Spectrum& src) { Spectrum &operator=(const Spectrum &) = default;
r = src.r; ~Spectrum() = default;
g = src.g;
b = src.b; Spectrum operator+=(Spectrum v) {
} r += v.r;
~Spectrum() = default; g += v.g;
b += v.b;
Spectrum operator=(Spectrum v) { return *this;
r = v.r; }
g = v.g; Spectrum operator*=(Spectrum v) {
b = v.b; r *= v.r;
return *this; g *= v.g;
} b *= v.b;
Spectrum operator+=(Spectrum v) { return *this;
r += v.r; }
g += v.g; Spectrum operator*=(float s) {
b += v.b; r *= s;
return *this; g *= s;
} b *= s;
Spectrum operator*=(Spectrum v) { return *this;
r *= v.r; }
g *= v.g;
b *= v.b; static Spectrum direction(Vec3 v) {
return *this; v.normalize();
} Spectrum s(std::abs(v.x), std::abs(v.y), std::abs(v.z));
Spectrum operator*=(float s) { s.make_linear();
r *= s; return s;
g *= s; }
b *= s;
return *this; void make_srgb() {
} r = std::pow(r, 1.0f / GAMMA);
g = std::pow(g, 1.0f / GAMMA);
static Spectrum direction(Vec3 v) { b = std::pow(b, 1.0f / GAMMA);
v.normalize(); }
Spectrum s(std::abs(v.x), std::abs(v.y), std::abs(v.z)); void make_linear() {
s.make_linear(); r = std::pow(r, GAMMA);
return s; g = std::pow(g, GAMMA);
} b = std::pow(b, GAMMA);
}
void make_srgb() {
r = std::pow(r, 1.0f / GAMMA); Spectrum operator+(Spectrum v) const { return Spectrum(r + v.r, g + v.g, b + v.b); }
g = std::pow(g, 1.0f / GAMMA); Spectrum operator-(Spectrum v) const { return Spectrum(r - v.r, g - v.g, b - v.b); }
b = std::pow(b, 1.0f / GAMMA); Spectrum operator*(Spectrum v) const { return Spectrum(r * v.r, g * v.g, b * v.b); }
}
void make_linear() { Spectrum operator+(float s) const { return Spectrum(r + s, g + s, b + s); }
r = std::pow(r, GAMMA); Spectrum operator*(float s) const { return Spectrum(r * s, g * s, b * s); }
g = std::pow(g, GAMMA); Spectrum operator/(float s) const { return Spectrum(r / s, g / s, b / s); }
b = std::pow(b, GAMMA);
} bool operator==(Spectrum v) const { return r == v.r && g == v.g && b == v.b; }
bool operator!=(Spectrum v) const { return r != v.r || g != v.g || b != v.b; }
Spectrum operator+(Spectrum v) const {
return Spectrum(r + v.r, g + v.g, b + v.b); float luma() const { return 0.2126f * r + 0.7152f * g + 0.0722f * b; }
}
Spectrum operator-(Spectrum v) const { bool valid() const {
return Spectrum(r - v.r, g - v.g, b - v.b); return !(std::isinf(r) || std::isinf(g) || std::isinf(b) || std::isnan(r) ||
} std::isnan(g) || std::isnan(b));
Spectrum operator*(Spectrum v) const { }
return Spectrum(r * v.r, g * v.g, b * v.b);
} Vec3 to_vec() const { return Vec3(r, g, b); }
Spectrum operator+(float s) const { union {
return Spectrum(r + s, g + s, b + s); struct {
} float r;
Spectrum operator*(float s) const { float g;
return Spectrum(r * s, g * s, b * s); float b;
} };
Spectrum operator/(float s) const { float data[3] = {};
return Spectrum(r / s, g / s, b / s); };
}
bool operator==(Spectrum v) const {
return r == v.r && g == v.g && b == v.b;
}
bool operator!=(Spectrum v) const {
return r != v.r || g != v.g || b != v.b;
}
float luma() const {
return 0.2126f * r + 0.7152f * g + 0.0722f * b;
}
bool valid() const {
return !(std::isinf(r) || std::isinf(g) || std::isinf(b) ||
std::isnan(r) || std::isnan(g) || std::isnan(b));
}
Vec3 to_vec() const {
return Vec3(r,g,b);
}
union {
struct {
float r;
float g;
float b;
};
float data[3] = {};
};
}; };
inline Spectrum operator+(float s, Spectrum v) { inline Spectrum operator+(float s, Spectrum v) { return Spectrum(v.r + s, v.g + s, v.b + s); }
return Spectrum(v.r + s, v.g + s, v.b + s); inline Spectrum operator*(float s, Spectrum v) { return Spectrum(v.r * s, v.g * s, v.b * s); }
}
inline Spectrum operator*(float s, Spectrum v) {
return Spectrum(v.r * s, v.g * s, v.b * s);
}
inline std::ostream& operator<<(std::ostream& out, Spectrum v) { inline std::ostream &operator<<(std::ostream &out, Spectrum v) {
out << "Spectrum{" << v.r << "," << v.g << "," << v.b << "}"; out << "Spectrum{" << v.r << "," << v.g << "," << v.b << "}";
return out; return out;
} }
#pragma once #pragma once
#include <cmath>
#include <algorithm> #include <algorithm>
#include <cmath>
#include <ostream> #include <ostream>
#include "log.h" #include "log.h"
struct Vec2 { struct Vec2 {
Vec2() { Vec2() {
x = 0.0f; x = 0.0f;
y = 0.0f; y = 0.0f;
} }
explicit Vec2(float _x, float _y) { explicit Vec2(float _x, float _y) {
x = _x; x = _x;
y = _y; y = _y;
} }
explicit Vec2(float f) { explicit Vec2(float f) { x = y = f; }
x = y = f; explicit Vec2(int _x, int _y) {
} x = (float)_x;
explicit Vec2(int _x, int _y) { y = (float)_y;
x = (float)_x; }
y = (float)_y;
} Vec2(const Vec2 &) = default;
Vec2(const Vec2& src) { Vec2 &operator=(const Vec2 &) = default;
x = src.x; ~Vec2() = default;
y = src.y;
} float &operator[](int idx) {
~Vec2() = default; assert(idx >= 0 && idx <= 1);
return data[idx];
Vec2 operator=(Vec2 v) { }
x = v.x; float operator[](int idx) const {
y = v.y; assert(idx >= 0 && idx <= 1);
return *this; return data[idx];
} }
float& operator[](int idx) { Vec2 operator+=(Vec2 v) {
assert(idx >= 0 && idx <= 1); x += v.x;
return data[idx]; y += v.y;
} return *this;
float operator[](int idx) const { }
assert(idx >= 0 && idx <= 1); Vec2 operator-=(Vec2 v) {
return data[idx]; x -= v.x;
} y -= v.y;
return *this;
Vec2 operator+=(Vec2 v) { }
x += v.x; Vec2 operator*=(Vec2 v) {
y += v.y; x *= v.x;
return *this; y *= v.y;
} return *this;
Vec2 operator-=(Vec2 v) { }
x -= v.x; Vec2 operator/=(Vec2 v) {
y -= v.y; x /= v.x;
return *this; y /= v.y;
} return *this;
Vec2 operator*=(Vec2 v) { }
x *= v.x;
y *= v.y; Vec2 operator+=(float s) {
return *this; x += s;
} y += s;
Vec2 operator/=(Vec2 v) { return *this;
x /= v.x; }
y /= v.y; Vec2 operator-=(float s) {
return *this; x -= s;
} y -= s;
return *this;
Vec2 operator+=(float s) { }
x += s; Vec2 operator*=(float s) {
y += s; x *= s;
return *this; y *= s;
} return *this;
Vec2 operator-=(float s) { }
x -= s; Vec2 operator/=(float s) {
y -= s; x /= s;
return *this; y /= s;
} return *this;
Vec2 operator*=(float s) { }
x *= s;
y *= s; Vec2 operator+(Vec2 v) const { return Vec2(x + v.x, y + v.y); }
return *this; Vec2 operator-(Vec2 v) const { return Vec2(x - v.x, y - v.y); }
} Vec2 operator*(Vec2 v) const { return Vec2(x * v.x, y * v.y); }
Vec2 operator/=(float s) { Vec2 operator/(Vec2 v) const { return Vec2(x / v.x, y / v.y); }
x /= s;
y /= s; Vec2 operator+(float s) const { return Vec2(x + s, y + s); }
return *this; Vec2 operator-(float s) const { return Vec2(x - s, y - s); }
} Vec2 operator*(float s) const { return Vec2(x * s, y * s); }
Vec2 operator/(float s) const { return Vec2(x / s, y / s); }
Vec2 operator+(Vec2 v) const {
return Vec2(x + v.x, y + v.y); bool operator==(Vec2 v) const { return x == v.x && y == v.y; }
} bool operator!=(Vec2 v) const { return x != v.x || y != v.y; }
Vec2 operator-(Vec2 v) const {
return Vec2(x - v.x, y - v.y); /// Absolute value
} Vec2 abs() const { return Vec2(std::abs(x), std::abs(y)); }
Vec2 operator*(Vec2 v) const { /// Negation
return Vec2(x * v.x, y * v.y); Vec2 operator-() const { return Vec2(-x, -y); }
} /// Are all members real numbers?
Vec2 operator/(Vec2 v) const { bool valid() const {
return Vec2(x / v.x, y / v.y); return !(std::isinf(x) || std::isinf(y) || std::isnan(x) || std::isnan(y));
} }
Vec2 operator+(float s) const { /// Modify vec to have unit length
return Vec2(x + s, y + s); Vec2 normalize() {
} float n = norm();
Vec2 operator-(float s) const { x /= n;
return Vec2(x - s, y - s); y /= n;
} return *this;
Vec2 operator*(float s) const { }
return Vec2(x * s, y * s); /// Return unit length vec in the same direction
} Vec2 unit() const {
Vec2 operator/(float s) const { float n = norm();
return Vec2(x / s, y / s); return Vec2(x / n, y / n);
} }
bool operator==(Vec2 v) const { float norm_squared() const { return x * x + y * y; }
return x == v.x && y == v.y; float norm() const { return std::sqrt(norm_squared()); }
}
bool operator!=(Vec2 v) const { Vec2 range(float min, float max) const {
return x != v.x || y != v.y; if (!valid())
} return Vec2();
Vec2 r = *this;
/// Absolute value float range = max - min;
Vec2 abs() const { while (r.x < min)
return Vec2(std::abs(x), std::abs(y)); r.x += range;
} while (r.x >= max)
/// Negation r.x -= range;
Vec2 operator-() const { while (r.y < min)
return Vec2(-x, -y); r.y += range;
} while (r.y >= max)
/// Are all members real numbers? r.y -= range;
bool valid() const { return r;
return !(std::isinf(x) || std::isinf(y) || }
std::isnan(x) || std::isnan(y));
} union {
struct {
/// Modify vec to have unit length float x;
Vec2 normalize() { float y;
float n = norm(); };
x /= n; float data[2] = {};
y /= n; };
return *this;
}
/// Return unit length vec in the same direction
Vec2 unit() const {
float n = norm();
return Vec2(x / n, y / n);
}
float norm_squared() const {
return x * x + y * y;
}
float norm() const {
return std::sqrt(norm_squared());
}
Vec2 range(float min, float max) const {
if(!valid()) return Vec2();
Vec2 r = *this;
float range = max - min;
while(r.x < min) r.x += range;
while(r.x >= max) r.x -= range;
while(r.y < min) r.y += range;
while(r.y >= max) r.y -= range;
return r;
}
union {
struct {
float x;
float y;
};
float data[2] = {};
};
}; };
inline Vec2 operator+(float s, Vec2 v) { inline Vec2 operator+(float s, Vec2 v) { return Vec2(v.x + s, v.y + s); }
return Vec2(v.x + s, v.y + s); inline Vec2 operator-(float s, Vec2 v) { return Vec2(v.x - s, v.y - s); }
} inline Vec2 operator*(float s, Vec2 v) { return Vec2(v.x * s, v.y * s); }
inline Vec2 operator-(float s, Vec2 v) { inline Vec2 operator/(float s, Vec2 v) { return Vec2(s / v.x, s / v.y); }
return Vec2(v.x - s, v.y - s);
}
inline Vec2 operator*(float s, Vec2 v) {
return Vec2(v.x * s, v.y * s);
}
inline Vec2 operator/(float s, Vec2 v) {
return Vec2(s / v.x, s / v.y);
}
/// Take minimum of each component /// Take minimum of each component
inline Vec2 hmin(Vec2 l, Vec2 r) { inline Vec2 hmin(Vec2 l, Vec2 r) { return Vec2(std::min(l.x, r.x), std::min(l.y, r.y)); }
return Vec2(std::min(l.x, r.x), std::min(l.y, r.y));
}
/// Take maximum of each component /// Take maximum of each component
inline Vec2 hmax(Vec2 l, Vec2 r) { inline Vec2 hmax(Vec2 l, Vec2 r) { return Vec2(std::max(l.x, r.x), std::max(l.y, r.y)); }
return Vec2(std::max(l.x, r.x), std::max(l.y, r.y));
}
/// 2D dot product /// 2D dot product
inline float dot(Vec2 l, Vec2 r) { inline float dot(Vec2 l, Vec2 r) { return l.x * r.x + l.y * r.y; }
return l.x * r.x + l.y * r.y;
}
inline std::ostream& operator<<(std::ostream& out, Vec2 v) { inline std::ostream &operator<<(std::ostream &out, Vec2 v) {
out << "{" << v.x << "," << v.y << "}"; out << "{" << v.x << "," << v.y << "}";
return out; return out;
} }
#pragma once #pragma once
#include <cmath>
#include <algorithm> #include <algorithm>
#include <cmath>
#include <ostream> #include <ostream>
#include "log.h" #include "log.h"
struct Vec3 { struct Vec3 {
Vec3() { Vec3() {
x = 0.0f; x = 0.0f;
y = 0.0f; y = 0.0f;
z = 0.0f; z = 0.0f;
} }
explicit Vec3(float _x, float _y, float _z) { explicit Vec3(float _x, float _y, float _z) {
x = _x; x = _x;
y = _y; y = _y;
z = _z; z = _z;
} }
explicit Vec3(int _x, int _y, int _z) { explicit Vec3(int _x, int _y, int _z) {
x = (float)_x; x = (float)_x;
y = (float)_y; y = (float)_y;
z = (float)_z; z = (float)_z;
} }
explicit Vec3(float f) { explicit Vec3(float f) { x = y = z = f; }
x = y = z = f;
} Vec3(const Vec3 &) = default;
Vec3(const Vec3& src) { Vec3 &operator=(const Vec3 &) = default;
x = src.x; ~Vec3() = default;
y = src.y;
z = src.z; float &operator[](int idx) {
} assert(idx >= 0 && idx <= 2);
~Vec3() = default; return data[idx];
}
Vec3 operator=(Vec3 v) { float operator[](int idx) const {
x = v.x; assert(idx >= 0 && idx <= 2);
y = v.y; return data[idx];
z = v.z; }
return *this;
} Vec3 operator+=(Vec3 v) {
x += v.x;
float& operator[](int idx) { y += v.y;
assert(idx >= 0 && idx <= 2); z += v.z;
return data[idx]; return *this;
} }
float operator[](int idx) const { Vec3 operator-=(Vec3 v) {
assert(idx >= 0 && idx <= 2); x -= v.x;
return data[idx]; y -= v.y;
} z -= v.z;
return *this;
Vec3 operator+=(Vec3 v) { }
x += v.x; Vec3 operator*=(Vec3 v) {
y += v.y; x *= v.x;
z += v.z; y *= v.y;
return *this; z *= v.z;
} return *this;
Vec3 operator-=(Vec3 v) { }
x -= v.x; Vec3 operator/=(Vec3 v) {
y -= v.y; x /= v.x;
z -= v.z; y /= v.y;
return *this; z /= v.z;
} return *this;
Vec3 operator*=(Vec3 v) { }
x *= v.x;
y *= v.y; Vec3 operator+=(float s) {
z *= v.z; x += s;
return *this; y += s;
} z += s;
Vec3 operator/=(Vec3 v) { return *this;
x /= v.x; }
y /= v.y; Vec3 operator-=(float s) {
z /= v.z; x -= s;
return *this; y -= s;
} z -= s;
return *this;
Vec3 operator+=(float s) { }
x += s; Vec3 operator*=(float s) {
y += s; x *= s;
z += s; y *= s;
return *this; z *= s;
} return *this;
Vec3 operator-=(float s) { }
x -= s; Vec3 operator/=(float s) {
y -= s; x /= s;
z -= s; y /= s;
return *this; z /= s;
} return *this;
Vec3 operator*=(float s) { }
x *= s;
y *= s; Vec3 operator+(Vec3 v) const { return Vec3(x + v.x, y + v.y, z + v.z); }
z *= s; Vec3 operator-(Vec3 v) const { return Vec3(x - v.x, y - v.y, z - v.z); }
return *this; Vec3 operator*(Vec3 v) const { return Vec3(x * v.x, y * v.y, z * v.z); }
} Vec3 operator/(Vec3 v) const { return Vec3(x / v.x, y / v.y, z / v.z); }
Vec3 operator/=(float s) {
x /= s; Vec3 operator+(float s) const { return Vec3(x + s, y + s, z + s); }
y /= s; Vec3 operator-(float s) const { return Vec3(x - s, y - s, z - s); }
z /= s; Vec3 operator*(float s) const { return Vec3(x * s, y * s, z * s); }
return *this; Vec3 operator/(float s) const { return Vec3(x / s, y / s, z / s); }
}
bool operator==(Vec3 v) const { return x == v.x && y == v.y && z == v.z; }
Vec3 operator+(Vec3 v) const { bool operator!=(Vec3 v) const { return x != v.x || y != v.y || z != v.z; }
return Vec3(x + v.x, y + v.y, z + v.z);
} /// Absolute value
Vec3 operator-(Vec3 v) const { Vec3 abs() const { return Vec3(std::abs(x), std::abs(y), std::abs(z)); }
return Vec3(x - v.x, y - v.y, z - v.z); /// Negation
} Vec3 operator-() const { return Vec3(-x, -y, -z); }
Vec3 operator*(Vec3 v) const { /// Are all members real numbers?
return Vec3(x * v.x, y * v.y, z * v.z); bool valid() const {
} return !(std::isinf(x) || std::isinf(y) || std::isinf(z) || std::isnan(x) ||
Vec3 operator/(Vec3 v) const { std::isnan(y) || std::isnan(z));
return Vec3(x / v.x, y / v.y, z / v.z); }
}
/// Modify vec to have unit length
Vec3 operator+(float s) const { Vec3 normalize() {
return Vec3(x + s, y + s, z + s); float n = norm();
} x /= n;
Vec3 operator-(float s) const { y /= n;
return Vec3(x - s, y - s, z - s); z /= n;
} return *this;
Vec3 operator*(float s) const { }
return Vec3(x * s, y * s, z * s); /// Return unit length vec in the same direction
} Vec3 unit() const {
Vec3 operator/(float s) const { float n = norm();
return Vec3(x / s, y / s, z / s); return Vec3(x / n, y / n, z / n);
} }
bool operator==(Vec3 v) const { float norm_squared() const { return x * x + y * y + z * z; }
return x == v.x && y == v.y && z == v.z; float norm() const { return std::sqrt(norm_squared()); }
}
bool operator!=(Vec3 v) const { /// Make sure all components are in the range [min,max) with floating point mod logic
return x != v.x || y != v.y || z != v.z; Vec3 range(float min, float max) const {
} if (!valid())
return Vec3();
/// Absolute value Vec3 r = *this;
Vec3 abs() const { float range = max - min;
return Vec3(std::abs(x), std::abs(y), std::abs(z)); while (r.x < min)
} r.x += range;
/// Negation while (r.x >= max)
Vec3 operator-() const { r.x -= range;
return Vec3(-x, -y, -z); while (r.y < min)
} r.y += range;
/// Are all members real numbers? while (r.y >= max)
bool valid() const { r.y -= range;
return !(std::isinf(x) || std::isinf(y) || std::isinf(z) || while (r.z < min)
std::isnan(x) || std::isnan(y) || std::isnan(z)); r.z += range;
} while (r.z >= max)
r.z -= range;
/// Modify vec to have unit length return r;
Vec3 normalize() { }
float n = norm();
x /= n; union {
y /= n; struct {
z /= n; float x;
return *this; float y;
} float z;
/// Return unit length vec in the same direction };
Vec3 unit() const { float data[3] = {};
float n = norm(); };
return Vec3(x / n, y / n, z / n);
}
float norm_squared() const {
return x * x + y * y + z * z;
}
float norm() const {
return std::sqrt(norm_squared());
}
/// Make sure all components are in the range [min,max) with floating point mod logic
Vec3 range(float min, float max) const {
if(!valid()) return Vec3();
Vec3 r = *this;
float range = max - min;
while(r.x < min) r.x += range;
while(r.x >= max) r.x -= range;
while(r.y < min) r.y += range;
while(r.y >= max) r.y -= range;
while(r.z < min) r.z += range;
while(r.z >= max) r.z -= range;
return r;
}
union {
struct {
float x;
float y;
float z;
};
float data[3] = {};
};
}; };
inline Vec3 operator+(float s, Vec3 v) { inline Vec3 operator+(float s, Vec3 v) { return Vec3(v.x + s, v.y + s, v.z + s); }
return Vec3(v.x + s, v.y + s, v.z + s); inline Vec3 operator-(float s, Vec3 v) { return Vec3(v.x - s, v.y - s, v.z - s); }
} inline Vec3 operator*(float s, Vec3 v) { return Vec3(v.x * s, v.y * s, v.z * s); }
inline Vec3 operator-(float s, Vec3 v) { inline Vec3 operator/(float s, Vec3 v) { return Vec3(s / v.x, s / v.y, s / v.z); }
return Vec3(v.x - s, v.y - s, v.z - s);
}
inline Vec3 operator*(float s, Vec3 v) {
return Vec3(v.x * s, v.y * s, v.z * s);
}
inline Vec3 operator/(float s, Vec3 v) {
return Vec3(s / v.x, s / v.y, s / v.z);
}
/// Take minimum of each component /// Take minimum of each component
inline Vec3 hmin(Vec3 l, Vec3 r) { inline Vec3 hmin(Vec3 l, Vec3 r) {
return Vec3(std::min(l.x, r.x), std::min(l.y, r.y), std::min(l.z, r.z)); return Vec3(std::min(l.x, r.x), std::min(l.y, r.y), std::min(l.z, r.z));
} }
/// Take maximum of each component /// Take maximum of each component
inline Vec3 hmax(Vec3 l, Vec3 r) { inline Vec3 hmax(Vec3 l, Vec3 r) {
return Vec3(std::max(l.x, r.x), std::max(l.y, r.y), std::max(l.z, r.z)); return Vec3(std::max(l.x, r.x), std::max(l.y, r.y), std::max(l.z, r.z));
} }
/// 3D dot product /// 3D dot product
inline float dot(Vec3 l, Vec3 r) { inline float dot(Vec3 l, Vec3 r) { return l.x * r.x + l.y * r.y + l.z * r.z; }
return l.x * r.x + l.y * r.y + l.z * r.z;
}
/// 3D cross product /// 3D cross product
inline Vec3 cross(Vec3 l, Vec3 r) { inline Vec3 cross(Vec3 l, Vec3 r) {
return Vec3(l.y * r.z - l.z * r.y, l.z * r.x - l.x * r.z, l.x * r.y - l.y * r.x); return Vec3(l.y * r.z - l.z * r.y, l.z * r.x - l.x * r.z, l.x * r.y - l.y * r.x);
} }
inline std::ostream& operator<<(std::ostream& out, Vec3 v) { inline std::ostream &operator<<(std::ostream &out, Vec3 v) {
out << "{" << v.x << "," << v.y << "," << v.z << "}"; out << "{" << v.x << "," << v.y << "," << v.z << "}";
return out; return out;
} }
#pragma once #pragma once
#include <cmath>
#include <algorithm> #include <algorithm>
#include <cmath>
#include <ostream> #include <ostream>
#include "log.h" #include "log.h"
...@@ -10,233 +10,178 @@ ...@@ -10,233 +10,178 @@
struct Vec4 { struct Vec4 {
Vec4() { Vec4() {
x = 0.0f; x = 0.0f;
y = 0.0f; y = 0.0f;
z = 0.0f; z = 0.0f;
w = 0.0f; w = 0.0f;
} }
explicit Vec4(float _x, float _y, float _z, float _w) { explicit Vec4(float _x, float _y, float _z, float _w) {
x = _x; x = _x;
y = _y; y = _y;
z = _z; z = _z;
w = _w; w = _w;
} }
explicit Vec4(float f) { explicit Vec4(float f) { x = y = z = w = f; }
x = y = z = w = f; explicit Vec4(int _x, int _y, int _z, int _w) {
} x = (float)_x;
explicit Vec4(int _x, int _y, int _z, int _w) { y = (float)_y;
x = (float)_x; z = (float)_z;
y = (float)_y; w = (float)_w;
z = (float)_z; }
w = (float)_w; explicit Vec4(Vec3 xyz, float _w) {
} x = xyz.x;
explicit Vec4(Vec3 xyz, float _w) { y = xyz.y;
x = xyz.x; z = xyz.z;
y = xyz.y; w = _w;
z = xyz.z; }
w = _w;
} Vec4(const Vec4 &) = default;
Vec4(const Vec4& src) { Vec4 &operator=(const Vec4 &) = default;
x = src.x; ~Vec4() = default;
y = src.y;
z = src.z; float &operator[](int idx) {
w = src.w; assert(idx >= 0 && idx <= 3);
} return data[idx];
~Vec4() = default; }
float operator[](int idx) const {
Vec4 operator=(Vec4 v) { assert(idx >= 0 && idx <= 3);
x = v.x; return data[idx];
y = v.y; }
z = v.z;
w = v.w; Vec4 operator+=(Vec4 v) {
return *this; x += v.x;
} y += v.y;
z += v.z;
float& operator[](int idx) { w += v.w;
assert(idx >= 0 && idx <= 3); return *this;
return data[idx]; }
} Vec4 operator-=(Vec4 v) {
float operator[](int idx) const { x -= v.x;
assert(idx >= 0 && idx <= 3); y -= v.y;
return data[idx]; z -= v.z;
} w -= v.w;
return *this;
Vec4 operator+=(Vec4 v) { }
x += v.x; Vec4 operator*=(Vec4 v) {
y += v.y; x *= v.x;
z += v.z; y *= v.y;
w += v.w; z *= v.z;
return *this; w *= v.w;
} return *this;
Vec4 operator-=(Vec4 v) { }
x -= v.x; Vec4 operator/=(Vec4 v) {
y -= v.y; x /= v.x;
z -= v.z; y /= v.y;
w -= v.w; z /= v.z;
return *this; w /= v.w;
} return *this;
Vec4 operator*=(Vec4 v) { }
x *= v.x;
y *= v.y; Vec4 operator+=(float s) {
z *= v.z; x += s;
w *= v.w; y += s;
return *this; z += s;
} w += s;
Vec4 operator/=(Vec4 v) { return *this;
x /= v.x; }
y /= v.y; Vec4 operator-=(float s) {
z /= v.z; x -= s;
w /= v.w; y -= s;
return *this; z -= s;
} w -= s;
return *this;
Vec4 operator+=(float s) { }
x += s; Vec4 operator*=(float s) {
y += s; x *= s;
z += s; y *= s;
w += s; z *= s;
return *this; w *= s;
} return *this;
Vec4 operator-=(float s) { }
x -= s; Vec4 operator/=(float s) {
y -= s; x /= s;
z -= s; y /= s;
w -= s; z /= s;
return *this; w /= s;
} return *this;
Vec4 operator*=(float s) { }
x *= s;
y *= s; Vec4 operator+(Vec4 v) const { return Vec4(x + v.x, y + v.y, z + v.z, w + v.w); }
z *= s; Vec4 operator-(Vec4 v) const { return Vec4(x - v.x, y - v.y, z - v.z, w - v.w); }
w *= s; Vec4 operator*(Vec4 v) const { return Vec4(x * v.x, y * v.y, z * v.z, w * v.w); }
return *this; Vec4 operator/(Vec4 v) const { return Vec4(x / v.x, y / v.y, z / v.z, w / v.w); }
}
Vec4 operator/=(float s) { Vec4 operator+(float s) const { return Vec4(x + s, y + s, z + s, w + s); }
x /= s; Vec4 operator-(float s) const { return Vec4(x - s, y - s, z - s, w - s); }
y /= s; Vec4 operator*(float s) const { return Vec4(x * s, y * s, z * s, w * s); }
z /= s; Vec4 operator/(float s) const { return Vec4(x / s, y / s, z / s, w / s); }
w /= s;
return *this; bool operator==(Vec4 v) const { return x == v.x && y == v.y && z == v.z && w == v.w; }
} bool operator!=(Vec4 v) const { return x != v.x || y != v.y || z != v.z || w != v.w; }
Vec4 operator+(Vec4 v) const { /// Absolute value
return Vec4(x + v.x, y + v.y, z + v.z, w + v.w); Vec4 abs() const { return Vec4(std::abs(x), std::abs(y), std::abs(z), std::abs(w)); }
} /// Negation
Vec4 operator-(Vec4 v) const { Vec4 operator-() const { return Vec4(-x, -y, -z, -w); }
return Vec4(x - v.x, y - v.y, z - v.z, w - v.w); /// Are all members real numbers?
} bool valid() const {
Vec4 operator*(Vec4 v) const { return !(std::isinf(x) || std::isinf(y) || std::isinf(z) || std::isinf(w) ||
return Vec4(x * v.x, y * v.y, z * v.z, w * v.w); std::isnan(x) || std::isnan(y) || std::isnan(z) || std::isnan(w));
} }
Vec4 operator/(Vec4 v) const {
return Vec4(x / v.x, y / v.y, z / v.z, w / v.w); /// Modify vec to have unit length
} Vec4 normalize() {
float n = norm();
Vec4 operator+(float s) const { x /= n;
return Vec4(x + s, y + s, z + s, w + s); y /= n;
} z /= n;
Vec4 operator-(float s) const { w /= n;
return Vec4(x - s, y - s, z - s, w - s); return *this;
} }
Vec4 operator*(float s) const { /// Return unit length vec in the same direction
return Vec4(x * s, y * s, z * s, w * s); Vec4 unit() const {
} float n = norm();
Vec4 operator/(float s) const { return Vec4(x / n, y / n, z / n, w / n);
return Vec4(x / s, y / s, z / s, w / s); }
}
float norm_squared() const { return x * x + y * y + z * z + w * w; }
bool operator==(Vec4 v) const { float norm() const { return std::sqrt(norm_squared()); }
return x == v.x && y == v.y && z == v.z && w == v.w;
} /// Returns first three components
bool operator!=(Vec4 v) const { Vec3 xyz() const { return Vec3(x, y, z); }
return x != v.x || y != v.y || z != v.z || w != v.w; /// Performs perspective division (xyz/w)
} Vec3 project() const { return Vec3(x / w, y / w, z / w); }
/// Absolute value union {
Vec4 abs() const { struct {
return Vec4(std::abs(x), std::abs(y), std::abs(z), std::abs(w)); float x;
} float y;
/// Negation float z;
Vec4 operator-() const { float w;
return Vec4(-x, -y, -z, -w); };
} float data[4] = {};
/// Are all members real numbers? };
bool valid() const {
return !(std::isinf(x) || std::isinf(y) || std::isinf(z) || std::isinf(w) ||
std::isnan(x) || std::isnan(y) || std::isnan(z) || std::isnan(w));
}
/// Modify vec to have unit length
Vec4 normalize() {
float n = norm();
x /= n;
y /= n;
z /= n;
w /= n;
return *this;
}
/// Return unit length vec in the same direction
Vec4 unit() const {
float n = norm();
return Vec4(x / n, y / n, z / n, w / n);
}
float norm_squared() const {
return x * x + y * y + z * z + w * w;
}
float norm() const {
return std::sqrt(norm_squared());
}
/// Returns first three components
Vec3 xyz() const {
return Vec3(x, y, z);
}
/// Performs perspective division (xyz/w)
Vec3 project() const {
return Vec3(x / w, y / w, z / w);
}
union {
struct {
float x;
float y;
float z;
float w;
};
float data[4] = {};
};
}; };
inline Vec4 operator+(float s, Vec4 v) { inline Vec4 operator+(float s, Vec4 v) { return Vec4(v.x + s, v.y + s, v.z + s, v.w + s); }
return Vec4(v.x + s, v.y + s, v.z + s, v.w + s); inline Vec4 operator-(float s, Vec4 v) { return Vec4(v.x - s, v.y - s, v.z - s, v.w - s); }
} inline Vec4 operator*(float s, Vec4 v) { return Vec4(v.x * s, v.y * s, v.z * s, v.w * s); }
inline Vec4 operator-(float s, Vec4 v) { inline Vec4 operator/(float s, Vec4 v) { return Vec4(s / v.x, s / v.y, s / v.z, s / v.w); }
return Vec4(v.x - s, v.y - s, v.z - s, v.w - s);
}
inline Vec4 operator*(float s, Vec4 v) {
return Vec4(v.x * s, v.y * s, v.z * s, v.w * s);
}
inline Vec4 operator/(float s, Vec4 v) {
return Vec4(s / v.x, s / v.y, s / v.z, s / v.w);
}
/// Take minimum of each component /// Take minimum of each component
inline Vec4 hmin(Vec4 l, Vec4 r) { inline Vec4 hmin(Vec4 l, Vec4 r) {
return Vec4(std::min(l.x, r.x), std::min(l.y, r.y), std::min(l.z, r.z), std::min(l.w, r.w)); return Vec4(std::min(l.x, r.x), std::min(l.y, r.y), std::min(l.z, r.z), std::min(l.w, r.w));
} }
/// Take maximum of each component /// Take maximum of each component
inline Vec4 hmax(Vec4 l, Vec4 r) { inline Vec4 hmax(Vec4 l, Vec4 r) {
return Vec4(std::max(l.x, r.x), std::max(l.y, r.y), std::max(l.z, r.z), std::max(l.w, r.w)); return Vec4(std::max(l.x, r.x), std::max(l.y, r.y), std::max(l.z, r.z), std::max(l.w, r.w));
} }
/// 4D dot product /// 4D dot product
inline float dot(Vec4 l, Vec4 r) { inline float dot(Vec4 l, Vec4 r) { return l.x * r.x + l.y * r.y + l.z * r.z + l.w * r.w; }
return l.x * r.x + l.y * r.y + l.z * r.z + l.w * r.w;
}
inline std::ostream& operator<<(std::ostream& out, Vec4 v) { inline std::ostream &operator<<(std::ostream &out, Vec4 v) {
out << "{" << v.x << "," << v.y << "," << v.z << "," << v.w << "}"; out << "{" << v.x << "," << v.y << "," << v.z << "," << v.w << "}";
return out; return out;
} }
#include <sf_libs/CLI11.hpp>
#include "platform/platform.h" #include "platform/platform.h"
#include "util/rand.h" #include "util/rand.h"
#include <sf_libs/CLI11.hpp>
int main(int argc, char** argv) { int main(int argc, char **argv) {
RNG::seed(); RNG::seed();
App::Settings settings; App::Settings settings;
CLI::App args{"Scotty3D - 15-462"}; CLI::App args{"Scotty3D - 15-462"};
args.add_option("-s,--scene", settings.scene_file, "Scene file to load"); args.add_option("-s,--scene", settings.scene_file, "Scene file to load");
args.add_option("--env_map", settings.env_map_file, "Override scene environment map"); args.add_option("--env_map", settings.env_map_file, "Override scene environment map");
args.add_flag("--headless", settings.headless, "Path-trace scene without opening the GUI"); args.add_flag("--headless", settings.headless, "Path-trace scene without opening the GUI");
args.add_option("-o,--output", settings.output_file, "Image file to write (if headless)"); args.add_option("-o,--output", settings.output_file, "Image file to write (if headless)");
args.add_flag("--animate", settings.animate, "Output animation frames (if headless)"); args.add_flag("--animate", settings.animate, "Output animation frames (if headless)");
args.add_option("--width", settings.w, "Output image width (if headless)"); args.add_option("--width", settings.w, "Output image width (if headless)");
args.add_option("--height", settings.h, "Output image height (if headless)"); args.add_option("--height", settings.h, "Output image height (if headless)");
args.add_flag("--use_ar", settings.w_from_ar, "Compute output image width based on camera AR (if headless)"); args.add_flag("--use_ar", settings.w_from_ar,
args.add_option("--depth", settings.d, "Maximum ray depth (if headless)"); "Compute output image width based on camera AR (if headless)");
args.add_option("--samples", settings.s, "Pixel samples (if headless)"); args.add_option("--depth", settings.d, "Maximum ray depth (if headless)");
args.add_option("--exposure", settings.exp, "Output exposure (if headless)"); args.add_option("--samples", settings.s, "Pixel samples (if headless)");
args.add_option("--area_samples", settings.ls, "Area light samples (if headless)"); args.add_option("--exposure", settings.exp, "Output exposure (if headless)");
args.add_option("--area_samples", settings.ls, "Area light samples (if headless)");
CLI11_PARSE(args, argc, argv); CLI11_PARSE(args, argc, argv);
if(!settings.headless) { if (!settings.headless) {
if(settings.scene_file.empty() && settings.env_map_file.empty()) Platform plt;
Platform::remove_console(); App app(settings, &plt);
Platform plt; plt.loop(app);
App app(settings, &plt); } else {
plt.loop(app); App app(settings);
} else { }
App app(settings); return 0;
}
return 0;
} }
This source diff could not be displayed because it is too large. You can view the blob instead.
#include "gl.h" #include "gl.h"
#include "../lib/log.h" #include "../lib/log.h"
...@@ -12,985 +12,989 @@ static bool is_gl45 = false; ...@@ -12,985 +12,989 @@ static bool is_gl45 = false;
static bool is_gl41 = false; static bool is_gl41 = false;
void setup() { void setup() {
std::string ver = version(); std::string ver = version();
is_gl45 = ver.find("4.5") != std::string::npos; is_gl45 = ver.find("4.5") != std::string::npos;
is_gl41 = ver.find("4.1") != std::string::npos; is_gl41 = ver.find("4.1") != std::string::npos;
setup_debug_proc(); setup_debug_proc();
Effects::init(); Effects::init();
} }
void shutdown() { void shutdown() {
Effects::destroy(); Effects::destroy();
check_leaked_handles(); check_leaked_handles();
} }
void color_mask(bool enable) { void color_mask(bool enable) { glColorMask(enable, enable, enable, enable); }
glColorMask(enable, enable, enable, enable);
}
std::string version() { std::string version() { return std::string((char *)glGetString(GL_VERSION)); }
return std::string((char*)glGetString(GL_VERSION)); std::string renderer() { return std::string((char *)glGetString(GL_RENDERER)); }
}
std::string renderer() {
return std::string((char*)glGetString(GL_RENDERER));
}
void global_params() { void global_params() {
glEnable(GL_BLEND); glEnable(GL_BLEND);
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
glPolygonOffset(1.0f, 1.0f); glPolygonOffset(1.0f, 1.0f);
glEnable(GL_DEPTH_TEST); glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_GREATER); glDepthFunc(GL_GREATER);
glClearDepth(0.0); glClearDepth(0.0);
if(glClipControl) glClipControl(GL_LOWER_LEFT, GL_ZERO_TO_ONE); if (glClipControl)
glCullFace(GL_BACK); glClipControl(GL_LOWER_LEFT, GL_ZERO_TO_ONE);
glCullFace(GL_BACK);
} }
void clear_screen(Vec4 col) { void clear_screen(Vec4 col) {
Framebuffer::bind_screen(); Framebuffer::bind_screen();
glClearColor(col.x, col.y, col.z, col.w); glClearColor(col.x, col.y, col.z, col.w);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
} }
void enable(Opt opt) { void enable(Opt opt) {
switch(opt) { switch (opt) {
case Opt::wireframe: { case Opt::wireframe: {
glEnable(GL_POLYGON_OFFSET_LINE); glEnable(GL_POLYGON_OFFSET_LINE);
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
} break; } break;
case Opt::offset: { case Opt::offset: {
glEnable(GL_POLYGON_OFFSET_FILL); glEnable(GL_POLYGON_OFFSET_FILL);
} break; } break;
case Opt::culling: { case Opt::culling: {
glEnable(GL_CULL_FACE); glEnable(GL_CULL_FACE);
} break; } break;
case Opt::depth_write: { case Opt::depth_write: {
glDepthMask(GL_TRUE); glDepthMask(GL_TRUE);
} break; } break;
} }
} }
void disable(Opt opt) { void disable(Opt opt) {
switch(opt) { switch (opt) {
case Opt::wireframe: { case Opt::wireframe: {
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
glDisable(GL_POLYGON_OFFSET_LINE); glDisable(GL_POLYGON_OFFSET_LINE);
} break; } break;
case Opt::offset: { case Opt::offset: {
glDisable(GL_POLYGON_OFFSET_FILL); glDisable(GL_POLYGON_OFFSET_FILL);
} break; } break;
case Opt::culling: { case Opt::culling: {
glDisable(GL_CULL_FACE); glDisable(GL_CULL_FACE);
} break; } break;
case Opt::depth_write: { case Opt::depth_write: {
glDepthMask(GL_FALSE); glDepthMask(GL_FALSE);
} break; } break;
} }
} }
void viewport(Vec2 dim) { void viewport(Vec2 dim) { glViewport(0, 0, (GLsizei)dim.x, (GLsizei)dim.y); }
glViewport(0, 0, (GLsizei)dim.x, (GLsizei)dim.y);
}
int max_msaa() { int max_msaa() {
int samples; int samples;
glGetIntegerv(GL_MAX_SAMPLES, &samples); glGetIntegerv(GL_MAX_SAMPLES, &samples);
return samples; return samples;
} }
Tex2D::Tex2D() { Tex2D::Tex2D() { id = 0; }
id = 0;
}
Tex2D::Tex2D(Tex2D&& src) { Tex2D::Tex2D(Tex2D &&src) {
id = src.id; src.id = 0; id = src.id;
src.id = 0;
} }
Tex2D::~Tex2D() { Tex2D::~Tex2D() {
if(id) glDeleteTextures(1, &id); if (id)
id = 0; glDeleteTextures(1, &id);
id = 0;
} }
void Tex2D::operator=(Tex2D&& src) { void Tex2D::operator=(Tex2D &&src) {
if(id) glDeleteTextures(1, &id); if (id)
id = src.id; src.id = 0; glDeleteTextures(1, &id);
id = src.id;
src.id = 0;
} }
void Tex2D::bind(int idx) const { void Tex2D::bind(int idx) const {
glActiveTexture(GL_TEXTURE0 + idx); glActiveTexture(GL_TEXTURE0 + idx);
glBindTexture(GL_TEXTURE_2D, id); glBindTexture(GL_TEXTURE_2D, id);
} }
void Tex2D::image(int w, int h, unsigned char* img) { void Tex2D::image(int w, int h, unsigned char *img) {
if(!id) glGenTextures(1, &id); if (!id)
glBindTexture(GL_TEXTURE_2D, id); glGenTextures(1, &id);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, img); glBindTexture(GL_TEXTURE_2D, id);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, img);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glBindTexture(GL_TEXTURE_2D, 0); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
} glBindTexture(GL_TEXTURE_2D, 0);
}
TexID Tex2D::get_id() const {
return id; TexID Tex2D::get_id() const { return id; }
}
Mesh::Mesh() { create(); }
Mesh::Mesh() {
create(); Mesh::Mesh(std::vector<Vert> &&vertices, std::vector<Index> &&indices) {
} create();
recreate(std::move(vertices), std::move(indices));
Mesh::Mesh(std::vector<Vert>&& vertices, std::vector<Index>&& indices) { }
create();
recreate(std::move(vertices), std::move(indices)); Mesh::Mesh(Mesh &&src) {
} vao = src.vao;
src.vao = 0;
Mesh::Mesh(Mesh&& src) { ebo = src.ebo;
vao = src.vao; src.vao = 0; src.ebo = 0;
ebo = src.ebo; src.ebo = 0; vbo = src.vbo;
vbo = src.vbo; src.vbo = 0; src.vbo = 0;
dirty = src.dirty; src.dirty = true; dirty = src.dirty;
n_elem = src.n_elem; src.n_elem = 0; src.dirty = true;
_bbox = src._bbox; src._bbox.reset(); n_elem = src.n_elem;
_verts = std::move(src._verts); src.n_elem = 0;
_idxs = std::move(src._idxs); _bbox = src._bbox;
} src._bbox.reset();
_verts = std::move(src._verts);
void Mesh::operator=(Mesh&& src) { _idxs = std::move(src._idxs);
destroy(); }
vao = src.vao; src.vao = 0;
vbo = src.vbo; src.vbo = 0; void Mesh::operator=(Mesh &&src) {
ebo = src.ebo; src.ebo = 0; destroy();
dirty = src.dirty; src.dirty = true; vao = src.vao;
n_elem = src.n_elem; src.n_elem = 0; src.vao = 0;
_bbox = src._bbox; src._bbox.reset(); vbo = src.vbo;
_verts = std::move(src._verts); src.vbo = 0;
_idxs = std::move(src._idxs); ebo = src.ebo;
} src.ebo = 0;
dirty = src.dirty;
Mesh::~Mesh() { src.dirty = true;
destroy(); n_elem = src.n_elem;
} src.n_elem = 0;
_bbox = src._bbox;
src._bbox.reset();
_verts = std::move(src._verts);
_idxs = std::move(src._idxs);
}
Mesh::~Mesh() { destroy(); }
void Mesh::create() { void Mesh::create() {
// Hack to let stuff get created for headless mode // Hack to let stuff get created for headless mode
if(!glGenVertexArrays) return; if (!glGenVertexArrays)
return;
glGenVertexArrays(1, &vao); glGenVertexArrays(1, &vao);
glGenBuffers(1, &vbo); glGenBuffers(1, &vbo);
glGenBuffers(1, &ebo); glGenBuffers(1, &ebo);
glBindVertexArray(vao); glBindVertexArray(vao);
glBindBuffer(GL_ARRAY_BUFFER, vbo); glBindBuffer(GL_ARRAY_BUFFER, vbo);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(Vert), (GLvoid*)0); glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(Vert), (GLvoid *)0);
glEnableVertexAttribArray(0); glEnableVertexAttribArray(0);
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(Vert), (GLvoid*)sizeof(Vec3)); glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(Vert), (GLvoid *)sizeof(Vec3));
glEnableVertexAttribArray(1); glEnableVertexAttribArray(1);
glVertexAttribIPointer(2, 1, GL_UNSIGNED_INT, sizeof(Vert), (GLvoid*)(2 * sizeof(Vec3))); glVertexAttribIPointer(2, 1, GL_UNSIGNED_INT, sizeof(Vert), (GLvoid *)(2 * sizeof(Vec3)));
glEnableVertexAttribArray(2); glEnableVertexAttribArray(2);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
glBindVertexArray(0); glBindVertexArray(0);
} }
void Mesh::destroy() { void Mesh::destroy() {
// Hack to let stuff get destroyed for headless mode // Hack to let stuff get destroyed for headless mode
if(!glDeleteBuffers) return; if (!glDeleteBuffers)
return;
glDeleteBuffers(1, &ebo); glDeleteBuffers(1, &ebo);
glDeleteBuffers(1, &vbo); glDeleteBuffers(1, &vbo);
glDeleteVertexArrays(1, &vao); glDeleteVertexArrays(1, &vao);
ebo = vao = vbo = 0; ebo = vao = vbo = 0;
} }
void Mesh::update() { void Mesh::update() {
glBindVertexArray(vao); glBindVertexArray(vao);
glBindBuffer(GL_ARRAY_BUFFER, vbo); glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferData(GL_ARRAY_BUFFER, sizeof(Vert) * _verts.size(), _verts.data(), GL_DYNAMIC_DRAW); glBufferData(GL_ARRAY_BUFFER, sizeof(Vert) * _verts.size(), _verts.data(), GL_DYNAMIC_DRAW);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(Index) * _idxs.size(), _idxs.data(), GL_DYNAMIC_DRAW); glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(Index) * _idxs.size(), _idxs.data(),
GL_DYNAMIC_DRAW);
glBindVertexArray(0); glBindVertexArray(0);
dirty = false; dirty = false;
} }
void Mesh::recreate(std::vector<Vert>&& vertices, std::vector<Index>&& indices) { void Mesh::recreate(std::vector<Vert> &&vertices, std::vector<Index> &&indices) {
dirty = true; dirty = true;
_verts = std::move(vertices); _verts = std::move(vertices);
_idxs = std::move(indices); _idxs = std::move(indices);
_bbox.reset(); _bbox.reset();
for(auto& v : _verts) { for (auto &v : _verts) {
_bbox.enclose(v.pos); _bbox.enclose(v.pos);
} }
n_elem = (GLuint)_idxs.size(); n_elem = (GLuint)_idxs.size();
} }
GLuint Mesh::tris() const { GLuint Mesh::tris() const { return n_elem / 3; }
return n_elem / 3;
}
std::vector<Mesh::Vert>& Mesh::edit_verts() { std::vector<Mesh::Vert> &Mesh::edit_verts() {
dirty = true; dirty = true;
return _verts; return _verts;
} }
std::vector<Mesh::Index>& Mesh::edit_indices() { std::vector<Mesh::Index> &Mesh::edit_indices() {
dirty = true; dirty = true;
return _idxs; return _idxs;
} }
const std::vector<Mesh::Vert>& Mesh::verts() const { const std::vector<Mesh::Vert> &Mesh::verts() const { return _verts; }
return _verts;
}
const std::vector<Mesh::Index>& Mesh::indices() const { const std::vector<Mesh::Index> &Mesh::indices() const { return _idxs; }
return _idxs;
}
BBox Mesh::bbox() const { BBox Mesh::bbox() const { return _bbox; }
return _bbox;
}
void Mesh::render() { void Mesh::render() {
if(dirty) update(); if (dirty)
glBindVertexArray(vao); update();
glDrawElements(GL_TRIANGLES, n_elem, GL_UNSIGNED_INT, nullptr); glBindVertexArray(vao);
glBindVertexArray(0); glDrawElements(GL_TRIANGLES, n_elem, GL_UNSIGNED_INT, nullptr);
glBindVertexArray(0);
} }
Instances::Instances(GL::Mesh&& mesh) : mesh(std::move(mesh)) { Instances::Instances(GL::Mesh &&mesh) : mesh(std::move(mesh)) { create(); }
create();
}
Instances::Instances(Instances&& src) { Instances::Instances(Instances &&src) {
mesh = std::move(src.mesh); mesh = std::move(src.mesh);
data = std::move(src.data); data = std::move(src.data);
vbo = src.vbo; src.vbo = 0; vbo = src.vbo;
dirty = src.dirty; src.dirty = true; src.vbo = 0;
dirty = src.dirty;
src.dirty = true;
} }
Instances::~Instances() { Instances::~Instances() { destroy(); }
destroy();
}
void Instances::operator=(Instances&& src) { void Instances::operator=(Instances &&src) {
destroy(); destroy();
mesh = std::move(src.mesh); mesh = std::move(src.mesh);
data = std::move(src.data); data = std::move(src.data);
vbo = src.vbo; src.vbo = 0; vbo = src.vbo;
dirty = src.dirty; src.dirty = true; src.vbo = 0;
dirty = src.dirty;
src.dirty = true;
} }
void Instances::create() { void Instances::create() {
// Hack to let stuff get created for headless mode // Hack to let stuff get created for headless mode
if(!glGenBuffers) return; if (!glGenBuffers)
return;
glGenBuffers(1, &vbo);
glBindVertexArray(mesh.vao); glGenBuffers(1, &vbo);
glBindBuffer(GL_ARRAY_BUFFER, vbo); glBindVertexArray(mesh.vao);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glEnableVertexAttribArray(3);
glVertexAttribIPointer(3, 1, GL_UNSIGNED_INT, sizeof(Info), (GLvoid*)0); glEnableVertexAttribArray(3);
glVertexAttribDivisor(3, 1); glVertexAttribIPointer(3, 1, GL_UNSIGNED_INT, sizeof(Info), (GLvoid *)0);
glVertexAttribDivisor(3, 1);
const int base_idx = 4;
for(int i = 0; i < 4; i++) { const int base_idx = 4;
glEnableVertexAttribArray(base_idx + i); for (int i = 0; i < 4; i++) {
glVertexAttribPointer(base_idx + i, 4, GL_FLOAT, GL_FALSE, sizeof(Info), (void*)(sizeof(GLuint) + sizeof(Vec4) * i)); glEnableVertexAttribArray(base_idx + i);
glVertexAttribDivisor(base_idx + i, 1); glVertexAttribPointer(base_idx + i, 4, GL_FLOAT, GL_FALSE, sizeof(Info),
} (void *)(sizeof(GLuint) + sizeof(Vec4) * i));
glBindVertexArray(0); glVertexAttribDivisor(base_idx + i, 1);
}
glBindVertexArray(0);
} }
void Instances::render() { void Instances::render() {
if(mesh.dirty) mesh.update();
if(dirty) update();
glBindVertexArray(mesh.vao); if (mesh.dirty)
glDrawElementsInstanced(GL_TRIANGLES, mesh.n_elem, GL_UNSIGNED_INT, nullptr, (GLsizei)data.size()); mesh.update();
glBindVertexArray(0); if (dirty)
update();
glBindVertexArray(mesh.vao);
glDrawElementsInstanced(GL_TRIANGLES, mesh.n_elem, GL_UNSIGNED_INT, nullptr,
(GLsizei)data.size());
glBindVertexArray(0);
} }
Instances::Info& Instances::get(size_t idx) { Instances::Info &Instances::get(size_t idx) {
dirty = true; dirty = true;
return data[idx]; return data[idx];
} }
size_t Instances::add(const Mat4& transform, GLuint id) { size_t Instances::add(const Mat4 &transform, GLuint id) {
data.push_back({id, transform}); data.push_back({id, transform});
dirty = true; dirty = true;
return data.size() - 1; return data.size() - 1;
} }
void Instances::clear() { void Instances::clear() {
data.clear(); data.clear();
dirty = true; dirty = true;
} }
void Instances::update() { void Instances::update() {
glBindVertexArray(mesh.vao); glBindVertexArray(mesh.vao);
glBindBuffer(GL_ARRAY_BUFFER, vbo); glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferData(GL_ARRAY_BUFFER, sizeof(Info) * data.size(), data.data(), GL_DYNAMIC_DRAW); glBufferData(GL_ARRAY_BUFFER, sizeof(Info) * data.size(), data.data(), GL_DYNAMIC_DRAW);
glBindVertexArray(0); glBindVertexArray(0);
dirty = false; dirty = false;
} }
void Instances::destroy() { void Instances::destroy() {
// Hack to let stuff get destroyed for headless mode // Hack to let stuff get destroyed for headless mode
if(!glDeleteBuffers) return; if (!glDeleteBuffers)
return;
glDeleteBuffers(1, &vbo); glDeleteBuffers(1, &vbo);
vbo = 0; vbo = 0;
mesh.destroy(); mesh.destroy();
} }
Lines::Lines(std::vector<Vert>&& verts, float thickness) Lines::Lines(std::vector<Vert> &&verts, float thickness)
: thickness(thickness), vertices(std::move(verts)) { : thickness(thickness), vertices(std::move(verts)) {
create(); create();
dirty = true; dirty = true;
} }
Lines::Lines(float thickness) : thickness(thickness) { Lines::Lines(float thickness) : thickness(thickness) { create(); }
create();
}
Lines::Lines(Lines&& src) { Lines::Lines(Lines &&src) {
dirty = src.dirty; src.dirty = true; dirty = src.dirty;
thickness = src.thickness; src.thickness = 0.0f; src.dirty = true;
vao = src.vao; src.vao = 0; thickness = src.thickness;
vbo = src.vbo; src.vbo = 0; src.thickness = 0.0f;
vertices = std::move(src.vertices); vao = src.vao;
src.vao = 0;
vbo = src.vbo;
src.vbo = 0;
vertices = std::move(src.vertices);
} }
void Lines::operator=(Lines&& src) { void Lines::operator=(Lines &&src) {
destroy(); destroy();
dirty = src.dirty; src.dirty = true; dirty = src.dirty;
thickness = src.thickness; src.thickness = 0.0f; src.dirty = true;
vao = src.vao; src.vao = 0; thickness = src.thickness;
vbo = src.vbo; src.vbo = 0; src.thickness = 0.0f;
vertices = std::move(src.vertices); vao = src.vao;
src.vao = 0;
vbo = src.vbo;
src.vbo = 0;
vertices = std::move(src.vertices);
} }
Lines::~Lines() { Lines::~Lines() { destroy(); }
destroy();
}
void Lines::update() const { void Lines::update() const {
glBindVertexArray(vao); glBindVertexArray(vao);
glBindBuffer(GL_ARRAY_BUFFER, vbo); glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferData(GL_ARRAY_BUFFER, sizeof(Vert) * vertices.size(), vertices.data(), GL_DYNAMIC_DRAW); glBufferData(GL_ARRAY_BUFFER, sizeof(Vert) * vertices.size(), vertices.data(), GL_DYNAMIC_DRAW);
glBindVertexArray(0); glBindVertexArray(0);
dirty = false; dirty = false;
} }
void Lines::render(bool smooth) const { void Lines::render(bool smooth) const {
if(dirty) update(); if (dirty)
update();
glLineWidth(thickness); glLineWidth(thickness);
if(smooth) glEnable(GL_LINE_SMOOTH); if (smooth)
else glDisable(GL_LINE_SMOOTH); glEnable(GL_LINE_SMOOTH);
else
glDisable(GL_LINE_SMOOTH);
glBindVertexArray(vao); glBindVertexArray(vao);
glDrawArrays(GL_LINES, 0, (GLsizei)vertices.size()); glDrawArrays(GL_LINES, 0, (GLsizei)vertices.size());
glBindVertexArray(0); glBindVertexArray(0);
} }
void Lines::clear() { void Lines::clear() {
vertices.clear(); vertices.clear();
dirty = true; dirty = true;
} }
void Lines::pop() { void Lines::pop() {
vertices.pop_back(); vertices.pop_back();
vertices.pop_back(); vertices.pop_back();
dirty = true; dirty = true;
} }
void Lines::add(Vec3 start, Vec3 end, Vec3 color) { void Lines::add(Vec3 start, Vec3 end, Vec3 color) {
vertices.push_back({start, color}); vertices.push_back({start, color});
vertices.push_back({end, color}); vertices.push_back({end, color});
dirty = true; dirty = true;
} }
void Lines::create() { void Lines::create() {
// Hack to let stuff get created for headless mode // Hack to let stuff get created for headless mode
if(!glGenBuffers) return; if (!glGenBuffers)
return;
glGenVertexArrays(1, &vao); glGenVertexArrays(1, &vao);
glGenBuffers(1, &vbo); glGenBuffers(1, &vbo);
glBindVertexArray(vao); glBindVertexArray(vao);
glBindBuffer(GL_ARRAY_BUFFER, vbo); glBindBuffer(GL_ARRAY_BUFFER, vbo);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(Vert), (GLvoid*)0); glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(Vert), (GLvoid *)0);
glEnableVertexAttribArray(0); glEnableVertexAttribArray(0);
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(Vert), (GLvoid*)sizeof(Vec3)); glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(Vert), (GLvoid *)sizeof(Vec3));
glEnableVertexAttribArray(1); glEnableVertexAttribArray(1);
glBindVertexArray(0); glBindVertexArray(0);
} }
void Lines::destroy() { void Lines::destroy() {
// Hack to let stuff get destroyed for headless mode // Hack to let stuff get destroyed for headless mode
if(!glDeleteBuffers) return; if (!glDeleteBuffers)
return;
glDeleteBuffers(1, &vbo); glDeleteBuffers(1, &vbo);
glDeleteVertexArrays(1, &vao); glDeleteVertexArrays(1, &vao);
vao = vbo = 0; vao = vbo = 0;
vertices.clear(); vertices.clear();
dirty = false; dirty = false;
} }
Shader::Shader() {} Shader::Shader() {}
Shader::Shader(std::string vertex, std::string fragment) { Shader::Shader(std::string vertex, std::string fragment) { load(vertex, fragment); }
load(vertex, fragment);
}
Shader::Shader(Shader&& src) { Shader::Shader(Shader &&src) {
program = src.program; src.program = 0; program = src.program;
v = src.v; src.v = 0; src.program = 0;
f = src.f; src.f = 0; v = src.v;
src.v = 0;
f = src.f;
src.f = 0;
} }
void Shader::operator=(Shader&& src) { void Shader::operator=(Shader &&src) {
destroy(); destroy();
program = src.program; src.program = 0; program = src.program;
v = src.v; src.v = 0; src.program = 0;
f = src.f; src.f = 0; v = src.v;
src.v = 0;
f = src.f;
src.f = 0;
} }
Shader::~Shader() { Shader::~Shader() { destroy(); }
destroy();
}
void Shader::bind() const { void Shader::bind() const { glUseProgram(program); }
glUseProgram(program);
}
void Shader::destroy() { void Shader::destroy() {
// Hack to let stuff get destroyed for headless mode // Hack to let stuff get destroyed for headless mode
if(!glUseProgram) return; if (!glUseProgram)
return;
glUseProgram(0); glUseProgram(0);
glDeleteShader(v); glDeleteShader(v);
glDeleteShader(f); glDeleteShader(f);
glDeleteProgram(program); glDeleteProgram(program);
v = f = program = 0; v = f = program = 0;
} }
void Shader::uniform_block(std::string name, GLuint i) const { void Shader::uniform_block(std::string name, GLuint i) const {
GLuint idx = glGetUniformBlockIndex(program, name.c_str()); GLuint idx = glGetUniformBlockIndex(program, name.c_str());
glUniformBlockBinding(program, idx, i); glUniformBlockBinding(program, idx, i);
} }
void Shader::uniform(std::string name, int count, const Vec2 items[]) const { void Shader::uniform(std::string name, int count, const Vec2 items[]) const {
glUniform2fv(loc(name), count, (GLfloat*)items); glUniform2fv(loc(name), count, (GLfloat *)items);
} }
void Shader::uniform(std::string name, GLfloat fl) const { void Shader::uniform(std::string name, GLfloat fl) const { glUniform1f(loc(name), fl); }
glUniform1f(loc(name), fl);
}
void Shader::uniform(std::string name, const Mat4& mat) const { void Shader::uniform(std::string name, const Mat4 &mat) const {
glUniformMatrix4fv(loc(name), 1, GL_FALSE, mat.data); glUniformMatrix4fv(loc(name), 1, GL_FALSE, mat.data);
} }
void Shader::uniform(std::string name, Vec3 vec3) const { void Shader::uniform(std::string name, Vec3 vec3) const { glUniform3fv(loc(name), 1, vec3.data); }
glUniform3fv(loc(name), 1, vec3.data);
}
void Shader::uniform(std::string name, Vec2 vec2) const { void Shader::uniform(std::string name, Vec2 vec2) const { glUniform2fv(loc(name), 1, vec2.data); }
glUniform2fv(loc(name), 1, vec2.data);
}
void Shader::uniform(std::string name, GLint i) const { void Shader::uniform(std::string name, GLint i) const { glUniform1i(loc(name), i); }
glUniform1i(loc(name), i);
}
void Shader::uniform(std::string name, GLuint i) const { void Shader::uniform(std::string name, GLuint i) const { glUniform1ui(loc(name), i); }
glUniform1ui(loc(name), i);
}
void Shader::uniform(std::string name, bool b) const { void Shader::uniform(std::string name, bool b) const { glUniform1i(loc(name), b); }
glUniform1i(loc(name), b);
}
GLuint Shader::loc(std::string name) const {
return glGetUniformLocation(program, name.c_str()); GLuint Shader::loc(std::string name) const { return glGetUniformLocation(program, name.c_str()); }
}
void Shader::load(std::string vertex, std::string fragment) { void Shader::load(std::string vertex, std::string fragment) {
v = glCreateShader(GL_VERTEX_SHADER); v = glCreateShader(GL_VERTEX_SHADER);
f = glCreateShader(GL_FRAGMENT_SHADER); f = glCreateShader(GL_FRAGMENT_SHADER);
const GLchar* vs_c = vertex.c_str(); const GLchar *vs_c = vertex.c_str();
const GLchar* fs_c = fragment.c_str(); const GLchar *fs_c = fragment.c_str();
glShaderSource(v, 1, &vs_c, NULL); glShaderSource(v, 1, &vs_c, NULL);
glShaderSource(f, 1, &fs_c, NULL); glShaderSource(f, 1, &fs_c, NULL);
glCompileShader(v); glCompileShader(v);
glCompileShader(f); glCompileShader(f);
if(!validate(v)) { if (!validate(v)) {
destroy(); destroy();
return; return;
} }
if(!validate(f)) { if (!validate(f)) {
destroy(); destroy();
return; return;
} }
program = glCreateProgram(); program = glCreateProgram();
glAttachShader(program, v); glAttachShader(program, v);
glAttachShader(program, f); glAttachShader(program, f);
glLinkProgram(program); glLinkProgram(program);
} }
bool Shader::validate(GLuint program) { bool Shader::validate(GLuint program) {
GLint compiled = 0; GLint compiled = 0;
glGetShaderiv(program, GL_COMPILE_STATUS, &compiled); glGetShaderiv(program, GL_COMPILE_STATUS, &compiled);
if(compiled == GL_FALSE) { if (compiled == GL_FALSE) {
GLint len = 0;
glGetShaderiv(program, GL_INFO_LOG_LENGTH, &len);
GLchar* msg = new GLchar[len]; GLint len = 0;
glGetShaderInfoLog(program, len, &len, msg); glGetShaderiv(program, GL_INFO_LOG_LENGTH, &len);
warn("Shader %d failed to compile: %s", program, msg); GLchar *msg = new GLchar[len];
delete[] msg; glGetShaderInfoLog(program, len, &len, msg);
return false; warn("Shader %d failed to compile: %s", program, msg);
} delete[] msg;
return true;
return false;
}
return true;
} }
Framebuffer::Framebuffer() {} Framebuffer::Framebuffer() {}
Framebuffer::Framebuffer(int outputs, Vec2 dim, int samples, bool d) { Framebuffer::Framebuffer(int outputs, Vec2 dim, int samples, bool d) {
setup(outputs, dim, samples, d); setup(outputs, dim, samples, d);
} }
void Framebuffer::setup(int outputs, Vec2 dim, int samples, bool d) { void Framebuffer::setup(int outputs, Vec2 dim, int samples, bool d) {
destroy(); destroy();
assert(outputs >= 0 && outputs < 31); assert(outputs >= 0 && outputs < 31);
depth = d; depth = d;
output_textures.resize(outputs); output_textures.resize(outputs);
resize(dim, samples); resize(dim, samples);
} }
Framebuffer::Framebuffer(Framebuffer&& src) { Framebuffer::Framebuffer(Framebuffer &&src) {
output_textures = std::move(src.output_textures); output_textures = std::move(src.output_textures);
depth_tex = src.depth_tex; src.depth_tex = 0; depth_tex = src.depth_tex;
framebuffer = src.framebuffer; src.framebuffer = 0; src.depth_tex = 0;
w = src.w; src.w = 0; framebuffer = src.framebuffer;
h = src.h; src.h = 0; src.framebuffer = 0;
s = src.s; src.s = 0; w = src.w;
} src.w = 0;
h = src.h;
void Framebuffer::operator=(Framebuffer&& src) { src.h = 0;
destroy(); s = src.s;
output_textures = std::move(src.output_textures); src.s = 0;
depth_tex = src.depth_tex; src.depth_tex = 0; }
framebuffer = src.framebuffer; src.framebuffer = 0;
w = src.w; src.w = 0; void Framebuffer::operator=(Framebuffer &&src) {
h = src.h; src.h = 0; destroy();
s = src.s; src.s = 0; output_textures = std::move(src.output_textures);
} depth_tex = src.depth_tex;
src.depth_tex = 0;
Framebuffer::~Framebuffer() { framebuffer = src.framebuffer;
destroy(); src.framebuffer = 0;
} w = src.w;
src.w = 0;
h = src.h;
src.h = 0;
s = src.s;
src.s = 0;
}
Framebuffer::~Framebuffer() { destroy(); }
void Framebuffer::create() { void Framebuffer::create() {
// Hack to let stuff get created for headless mode // Hack to let stuff get created for headless mode
if(!glGenFramebuffers) return; if (!glGenFramebuffers)
return;
glGenFramebuffers(1, &framebuffer); glGenFramebuffers(1, &framebuffer);
glGenTextures((GLsizei)output_textures.size(), (GLuint*)output_textures.data()); glGenTextures((GLsizei)output_textures.size(), (GLuint *)output_textures.data());
if(depth) glGenTextures(1, &depth_tex); if (depth)
glGenTextures(1, &depth_tex);
} }
void Framebuffer::destroy() { void Framebuffer::destroy() {
// Hack to let stuff get destroyed for headless mode // Hack to let stuff get destroyed for headless mode
if(!glDeleteFramebuffers) return; if (!glDeleteFramebuffers)
return;
glDeleteTextures(1, &depth_tex); glDeleteTextures(1, &depth_tex);
glDeleteTextures((GLsizei)output_textures.size(), (GLuint*)output_textures.data()); glDeleteTextures((GLsizei)output_textures.size(), (GLuint *)output_textures.data());
glDeleteFramebuffers(1, &framebuffer); glDeleteFramebuffers(1, &framebuffer);
depth_tex = framebuffer = 0; depth_tex = framebuffer = 0;
} }
void Framebuffer::resize(Vec2 dim, int samples) { void Framebuffer::resize(Vec2 dim, int samples) {
destroy(); destroy();
create(); create();
w = (int)dim.x;
h = (int)dim.y;
s = samples;
assert(w > 0 && h > 0 && s > 0);
glBindFramebuffer(GL_FRAMEBUFFER, framebuffer); w = (int)dim.x;
h = (int)dim.y;
s = samples;
assert(w > 0 && h > 0 && s > 0);
GLenum type = samples == 1 ? GL_TEXTURE_2D : GL_TEXTURE_2D_MULTISAMPLE; glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
std::vector<GLenum> draw_buffers; GLenum type = samples == 1 ? GL_TEXTURE_2D : GL_TEXTURE_2D_MULTISAMPLE;
for(GLenum i = 0; i < output_textures.size(); i++) { std::vector<GLenum> draw_buffers;
glBindTexture(type, output_textures[i]); for (GLenum i = 0; i < output_textures.size(); i++) {
if(s > 1) { glBindTexture(type, output_textures[i]);
glTexImage2DMultisample(type, s, GL_RGB8, w, h, GL_TRUE);
} else {
glTexImage2D(type, 0, GL_RGB8, w, h, 0, GL_RGB, GL_UNSIGNED_BYTE, 0);
glTexParameteri(type, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(type, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
}
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + i, type, output_textures[i], 0); if (s > 1) {
glTexImage2DMultisample(type, s, GL_RGB8, w, h, GL_TRUE);
} else {
glTexImage2D(type, 0, GL_RGB8, w, h, 0, GL_RGB, GL_UNSIGNED_BYTE, 0);
glTexParameteri(type, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(type, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
}
draw_buffers.push_back(GL_COLOR_ATTACHMENT0 + i); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + i, type, output_textures[i],
0);
glBindTexture(type, 0); draw_buffers.push_back(GL_COLOR_ATTACHMENT0 + i);
}
if(depth) { glBindTexture(type, 0);
glBindTexture(type, depth_tex); }
if(s > 1) {
glTexImage2DMultisample(type, s, GL_DEPTH_COMPONENT32F, w, h, GL_TRUE);
} else {
glTexImage2D(type, 0, GL_DEPTH_COMPONENT32F, w, h, 0, GL_DEPTH_COMPONENT, GL_FLOAT, 0);
glTexParameteri(type, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(type, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
}
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, type, depth_tex, 0);
glBindTexture(type, 0);
}
glDrawBuffers((GLsizei)draw_buffers.size(), draw_buffers.data()); if (depth) {
glBindTexture(type, depth_tex);
if (s > 1) {
glTexImage2DMultisample(type, s, GL_DEPTH_COMPONENT32F, w, h, GL_TRUE);
} else {
glTexImage2D(type, 0, GL_DEPTH_COMPONENT32F, w, h, 0, GL_DEPTH_COMPONENT, GL_FLOAT, 0);
glTexParameteri(type, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(type, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
}
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, type, depth_tex, 0);
glBindTexture(type, 0);
}
glBindFramebuffer(GL_FRAMEBUFFER, 0); glDrawBuffers((GLsizei)draw_buffers.size(), draw_buffers.data());
glBindFramebuffer(GL_FRAMEBUFFER, 0);
} }
void Framebuffer::clear(int buf, Vec4 col) const { void Framebuffer::clear(int buf, Vec4 col) const {
assert(buf >= 0 && buf < (int)output_textures.size()); assert(buf >= 0 && buf < (int)output_textures.size());
bind(); bind();
glClearBufferfv(GL_COLOR, buf, col.data); glClearBufferfv(GL_COLOR, buf, col.data);
} }
void Framebuffer::clear_d() const { void Framebuffer::clear_d() const {
bind(); bind();
glClear(GL_DEPTH_BUFFER_BIT); glClear(GL_DEPTH_BUFFER_BIT);
} }
void Framebuffer::bind_screen() { void Framebuffer::bind_screen() { glBindFramebuffer(GL_FRAMEBUFFER, 0); }
glBindFramebuffer(GL_FRAMEBUFFER, 0);
}
void Framebuffer::bind() const { void Framebuffer::bind() const { glBindFramebuffer(GL_FRAMEBUFFER, framebuffer); }
glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
}
GLuint Framebuffer::get_output(int buf) const { GLuint Framebuffer::get_output(int buf) const {
assert(buf >= 0 && buf < (int)output_textures.size()); assert(buf >= 0 && buf < (int)output_textures.size());
return output_textures[buf]; return output_textures[buf];
} }
int Framebuffer::bytes() const { int Framebuffer::bytes() const { return w * h * 4; }
return w * h * 4;
}
int Framebuffer::samples() const { int Framebuffer::samples() const { return s; }
return s;
}
void Framebuffer::flush() const { void Framebuffer::flush() const { glFlush(); }
glFlush();
}
GLuint Framebuffer::get_depth() const { GLuint Framebuffer::get_depth() const {
assert(depth_tex); assert(depth_tex);
return depth_tex; return depth_tex;
} }
bool Framebuffer::can_read_at() const { bool Framebuffer::can_read_at() const { return is_gl45 && s == 1; }
return is_gl45 && s == 1;
}
void Framebuffer::read_at(int buf, int x, int y, GLubyte* data) const { void Framebuffer::read_at(int buf, int x, int y, GLubyte *data) const {
assert(can_read_at()); assert(can_read_at());
assert(buf >= 0 && buf < (int)output_textures.size()); assert(buf >= 0 && buf < (int)output_textures.size());
glGetTextureSubImage(output_textures[buf], 0, x, y, 0, 1, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, 4, data); glGetTextureSubImage(output_textures[buf], 0, x, y, 0, 1, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, 4,
data);
} }
void Framebuffer::read(int buf, GLubyte* data) const { void Framebuffer::read(int buf, GLubyte *data) const {
assert(s == 1); assert(s == 1);
assert(buf >= 0 && buf < (int)output_textures.size()); assert(buf >= 0 && buf < (int)output_textures.size());
glBindTexture(GL_TEXTURE_2D, output_textures[buf]); glBindTexture(GL_TEXTURE_2D, output_textures[buf]);
glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE, data); glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
} }
void Framebuffer::blit_to(int buf, const Framebuffer& fb, bool avg) const { void Framebuffer::blit_to(int buf, const Framebuffer &fb, bool avg) const {
assert(buf >= 0 && buf < (int)output_textures.size()); assert(buf >= 0 && buf < (int)output_textures.size());
if(s > 1) { if (s > 1) {
Effects::resolve_to(buf, *this, fb, avg); Effects::resolve_to(buf, *this, fb, avg);
return; return;
} }
glBindFramebuffer(GL_READ_FRAMEBUFFER, framebuffer); glBindFramebuffer(GL_READ_FRAMEBUFFER, framebuffer);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fb.framebuffer); glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fb.framebuffer);
glReadBuffer(GL_COLOR_ATTACHMENT0 + buf); glReadBuffer(GL_COLOR_ATTACHMENT0 + buf);
glBlitFramebuffer(0, 0, w, h, 0, 0, fb.w, fb.h, GL_COLOR_BUFFER_BIT, GL_NEAREST); glBlitFramebuffer(0, 0, w, h, 0, 0, fb.w, fb.h, GL_COLOR_BUFFER_BIT, GL_NEAREST);
glBindFramebuffer(GL_FRAMEBUFFER, 0); glBindFramebuffer(GL_FRAMEBUFFER, 0);
} }
void Framebuffer::blit_to_screen(int buf, Vec2 dim) const { void Framebuffer::blit_to_screen(int buf, Vec2 dim) const {
assert(buf >= 0 && buf < (int)output_textures.size()); assert(buf >= 0 && buf < (int)output_textures.size());
if(s > 1) { if (s > 1) {
Effects::resolve_to_screen(buf, *this); Effects::resolve_to_screen(buf, *this);
return; return;
} }
glBindFramebuffer(GL_READ_FRAMEBUFFER, framebuffer); glBindFramebuffer(GL_READ_FRAMEBUFFER, framebuffer);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
glReadBuffer(GL_COLOR_ATTACHMENT0 + buf); glReadBuffer(GL_COLOR_ATTACHMENT0 + buf);
glBlitFramebuffer(0, 0, w, h, 0, 0, (GLint)dim.x, (GLint)dim.y, GL_COLOR_BUFFER_BIT, GL_NEAREST); glBlitFramebuffer(0, 0, w, h, 0, 0, (GLint)dim.x, (GLint)dim.y, GL_COLOR_BUFFER_BIT,
GL_NEAREST);
glBindFramebuffer(GL_FRAMEBUFFER, 0); glBindFramebuffer(GL_FRAMEBUFFER, 0);
} }
bool Framebuffer::is_multisampled() const { bool Framebuffer::is_multisampled() const { return s > 1; }
return s > 1;
}
void Effects::init() { void Effects::init() {
// Hack to let stuff get created for headless mode // Hack to let stuff get created for headless mode
if(!glGenVertexArrays) return; if (!glGenVertexArrays)
return;
glGenVertexArrays(1, &vao); glGenVertexArrays(1, &vao);
resolve_shader.load(effects_v, resolve_f); resolve_shader.load(effects_v, resolve_f);
outline_shader.load(effects_v, outline_f); outline_shader.load(effects_v, outline_f);
outline_shader_ms.load(effects_v, is_gl45 || is_gl41 ? outline_ms_f_4 : outline_ms_f_33); outline_shader_ms.load(effects_v, is_gl45 || is_gl41 ? outline_ms_f_4 : outline_ms_f_33);
} }
void Effects::destroy() { void Effects::destroy() {
// Hack to let stuff get destroyed for headless mode // Hack to let stuff get destroyed for headless mode
if(!glDeleteVertexArrays) return; if (!glDeleteVertexArrays)
return;
glDeleteVertexArrays(1, &vao);
vao = 0; glDeleteVertexArrays(1, &vao);
resolve_shader.~Shader(); vao = 0;
outline_shader.~Shader(); resolve_shader.~Shader();
outline_shader_ms.~Shader(); outline_shader.~Shader();
} outline_shader_ms.~Shader();
}
void Effects::outline(const Framebuffer& from, const Framebuffer& to, Vec3 color, Vec2 min, Vec2 max) {
void Effects::outline(const Framebuffer &from, const Framebuffer &to, Vec3 color, Vec2 min,
glFlush(); Vec2 max) {
to.bind();
glFlush();
Vec2 quad[] = { to.bind();
Vec2{min.x, max.y},
min, Vec2 quad[] = {Vec2{min.x, max.y}, min, max, Vec2{max.x, min.y}};
max,
Vec2{max.x, min.y} if (from.is_multisampled()) {
}; glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, from.get_depth());
outline_shader_ms.bind();
if(from.is_multisampled()) { outline_shader_ms.uniform("depth", 0);
glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, from.get_depth()); outline_shader_ms.uniform("color", color);
outline_shader_ms.bind(); outline_shader_ms.uniform("bounds", 4, quad);
outline_shader_ms.uniform("depth", 0); } else {
outline_shader_ms.uniform("color", color); glBindTexture(GL_TEXTURE_2D, from.get_depth());
outline_shader_ms.uniform("bounds", 4, quad); outline_shader.bind();
} else { outline_shader.uniform("depth", 0);
glBindTexture(GL_TEXTURE_2D, from.get_depth()); outline_shader.uniform("color", color);
outline_shader.bind(); outline_shader.uniform("i_screen_size", 1.0f / Vec2(from.w, from.h));
outline_shader.uniform("depth", 0); outline_shader.uniform("bounds", 4, quad);
outline_shader.uniform("color", color); }
outline_shader.uniform("i_screen_size", 1.0f / Vec2(from.w, from.h));
outline_shader.uniform("bounds", 4, quad); glBindVertexArray(vao);
} glDisable(GL_DEPTH_TEST);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
glBindVertexArray(vao); glEnable(GL_DEPTH_TEST);
glDisable(GL_DEPTH_TEST); glBindVertexArray(0);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); glFlush();
glEnable(GL_DEPTH_TEST); }
glBindVertexArray(0);
glFlush(); void Effects::resolve_to_screen(int buf, const Framebuffer &framebuffer) {
}
Framebuffer::bind_screen();
void Effects::resolve_to_screen(int buf, const Framebuffer& framebuffer) {
resolve_shader.bind();
Framebuffer::bind_screen();
assert(buf >= 0 && buf < (int)framebuffer.output_textures.size());
resolve_shader.bind(); glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, framebuffer.output_textures[buf]);
assert(buf >= 0 && buf < (int)framebuffer.output_textures.size()); resolve_shader.uniform("tex", 0);
glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, framebuffer.output_textures[buf]); resolve_shader.uniform("samples", framebuffer.s);
resolve_shader.uniform("bounds", 4, screen_quad);
resolve_shader.uniform("tex", 0);
resolve_shader.uniform("samples", framebuffer.s); glBindVertexArray(vao);
resolve_shader.uniform("bounds", 4, screen_quad); glDisable(GL_DEPTH_TEST);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
glBindVertexArray(vao); glEnable(GL_DEPTH_TEST);
glDisable(GL_DEPTH_TEST); glBindVertexArray(0);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); }
glEnable(GL_DEPTH_TEST);
glBindVertexArray(0); void Effects::resolve_to(int buf, const Framebuffer &from, const Framebuffer &to, bool avg) {
}
to.bind();
void Effects::resolve_to(int buf, const Framebuffer& from, const Framebuffer& to, bool avg) {
resolve_shader.bind();
to.bind();
assert(buf >= 0 && buf < (int)from.output_textures.size());
resolve_shader.bind(); glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, from.output_textures[buf]);
assert(buf >= 0 && buf < (int)from.output_textures.size()); resolve_shader.uniform("tex", 0);
glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, from.output_textures[buf]); resolve_shader.uniform("samples", avg ? from.s : 1);
resolve_shader.uniform("bounds", 4, screen_quad);
resolve_shader.uniform("tex", 0);
resolve_shader.uniform("samples", avg ? from.s : 1); glBindVertexArray(vao);
resolve_shader.uniform("bounds", 4, screen_quad); glDisable(GL_DEPTH_TEST);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
glBindVertexArray(vao); glEnable(GL_DEPTH_TEST);
glDisable(GL_DEPTH_TEST); glBindVertexArray(0);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); }
glEnable(GL_DEPTH_TEST);
glBindVertexArray(0); static void debug_proc(GLenum glsource, GLenum gltype, GLuint, GLenum severity, GLsizei,
} const GLchar *glmessage, const void *) {
static void debug_proc(GLenum glsource, GLenum gltype, GLuint, GLenum severity, GLsizei, const GLchar* glmessage, const void*) { std::string message(glmessage);
std::string source, type;
std::string message(glmessage);
std::string source, type; switch (glsource) {
case GL_DEBUG_SOURCE_API:
switch(glsource) { source = "OpenGL API";
case GL_DEBUG_SOURCE_API: break;
source = "OpenGL API"; case GL_DEBUG_SOURCE_WINDOW_SYSTEM:
break; source = "Window System";
case GL_DEBUG_SOURCE_WINDOW_SYSTEM: break;
source = "Window System"; case GL_DEBUG_SOURCE_SHADER_COMPILER:
break; source = "Shader Compiler";
case GL_DEBUG_SOURCE_SHADER_COMPILER: break;
source = "Shader Compiler"; case GL_DEBUG_SOURCE_THIRD_PARTY:
break; source = "Third Party";
case GL_DEBUG_SOURCE_THIRD_PARTY: break;
source = "Third Party"; case GL_DEBUG_SOURCE_APPLICATION:
break; source = "Application";
case GL_DEBUG_SOURCE_APPLICATION: break;
source = "Application"; case GL_DEBUG_SOURCE_OTHER:
break; source = "Other";
case GL_DEBUG_SOURCE_OTHER: break;
source = "Other"; }
break;
} switch (gltype) {
case GL_DEBUG_TYPE_ERROR:
switch(gltype) { type = "Error";
case GL_DEBUG_TYPE_ERROR: break;
type = "Error"; case GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR:
break; type = "Deprecated";
case GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR: break;
type = "Deprecated"; case GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR:
break; type = "Undefined Behavior";
case GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR: break;
type = "Undefined Behavior"; case GL_DEBUG_TYPE_PORTABILITY:
break; type = "Portability";
case GL_DEBUG_TYPE_PORTABILITY: break;
type = "Portability"; case GL_DEBUG_TYPE_PERFORMANCE:
break; type = "Performance";
case GL_DEBUG_TYPE_PERFORMANCE: break;
type = "Performance"; case GL_DEBUG_TYPE_MARKER:
break; type = "Marker";
case GL_DEBUG_TYPE_MARKER: break;
type = "Marker"; case GL_DEBUG_TYPE_PUSH_GROUP:
break; type = "Push Group";
case GL_DEBUG_TYPE_PUSH_GROUP: break;
type = "Push Group"; case GL_DEBUG_TYPE_POP_GROUP:
break; type = "Pop Group";
case GL_DEBUG_TYPE_POP_GROUP: break;
type = "Pop Group"; case GL_DEBUG_TYPE_OTHER:
break; type = "Other";
case GL_DEBUG_TYPE_OTHER: break;
type = "Other"; }
break;
} switch (severity) {
case GL_DEBUG_SEVERITY_HIGH:
switch(severity) { case GL_DEBUG_SEVERITY_MEDIUM:
case GL_DEBUG_SEVERITY_HIGH: warn("OpenGL | source: %s type: %s message: %s", source.c_str(), type.c_str(),
case GL_DEBUG_SEVERITY_MEDIUM: message.c_str());
warn("OpenGL | source: %s type: %s message: %s", source.c_str(), type.c_str(), message.c_str()); break;
break; }
}
} }
static void check_leaked_handles() { static void check_leaked_handles() {
#define GL_CHECK(type) if(glIs##type && glIs##type(i) == GL_TRUE) { warn("Leaked OpenGL handle %u of type %s", i, #type); leaked = true;} #define GL_CHECK(type) \
if (glIs##type && glIs##type(i) == GL_TRUE) { \
warn("Leaked OpenGL handle %u of type %s", i, #type); \
leaked = true; \
}
bool leaked = false; bool leaked = false;
for(GLuint i = 0; i < 10000; i++) { for (GLuint i = 0; i < 10000; i++) {
GL_CHECK(Texture); GL_CHECK(Texture);
GL_CHECK(Buffer); GL_CHECK(Buffer);
GL_CHECK(Framebuffer); GL_CHECK(Framebuffer);
GL_CHECK(Renderbuffer); GL_CHECK(Renderbuffer);
GL_CHECK(VertexArray); GL_CHECK(VertexArray);
GL_CHECK(Program); GL_CHECK(Program);
GL_CHECK(ProgramPipeline); GL_CHECK(ProgramPipeline);
GL_CHECK(Query); GL_CHECK(Query);
if(glIsShader(i) == GL_TRUE) { if (glIsShader(i) == GL_TRUE) {
leaked = true; leaked = true;
GLint shader_len = 0; GLint shader_len = 0;
glGetShaderiv(i, GL_SHADER_SOURCE_LENGTH, &shader_len); glGetShaderiv(i, GL_SHADER_SOURCE_LENGTH, &shader_len);
GLchar* shader = new GLchar[shader_len]; GLchar *shader = new GLchar[shader_len];
glGetShaderSource(i, shader_len, nullptr, shader); glGetShaderSource(i, shader_len, nullptr, shader);
warn("Leaked OpenGL shader %u. Source: %s", i, shader); warn("Leaked OpenGL shader %u. Source: %s", i, shader);
delete[] shader; delete[] shader;
} }
} }
if(leaked) { if (leaked) {
warn("Leaked OpenGL objects!"); warn("Leaked OpenGL objects!");
} }
#undef GL_CHECK #undef GL_CHECK
} }
static void setup_debug_proc() { static void setup_debug_proc() {
if(!glDebugMessageCallback || !glDebugMessageControl) return; if (!glDebugMessageCallback || !glDebugMessageControl)
glEnable(GL_DEBUG_OUTPUT); return;
glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS); glEnable(GL_DEBUG_OUTPUT);
glDebugMessageCallback(debug_proc, nullptr); glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS);
glDebugMessageControl(GL_DONT_CARE, GL_DONT_CARE, GL_DONT_CARE, 0, nullptr, GL_TRUE); glDebugMessageCallback(debug_proc, nullptr);
glDebugMessageControl(GL_DONT_CARE, GL_DONT_CARE, GL_DONT_CARE, 0, nullptr, GL_TRUE);
} }
const std::string Effects::effects_v = R"( const std::string Effects::effects_v = R"(
...@@ -1095,7 +1099,7 @@ void main() { ...@@ -1095,7 +1099,7 @@ void main() {
})"; })";
namespace Shaders { namespace Shaders {
const std::string line_v = R"( const std::string line_v = R"(
#version 330 core #version 330 core
layout (location = 0) in vec3 v_pos; layout (location = 0) in vec3 v_pos;
...@@ -1108,7 +1112,7 @@ void main() { ...@@ -1108,7 +1112,7 @@ void main() {
gl_Position = mvp * vec4(v_pos, 1.0f); gl_Position = mvp * vec4(v_pos, 1.0f);
f_col = v_col; f_col = v_col;
})"; })";
const std::string line_f = R"( const std::string line_f = R"(
#version 330 core #version 330 core
layout (location = 0) out vec4 out_col; layout (location = 0) out vec4 out_col;
...@@ -1120,8 +1124,8 @@ smooth in vec3 f_col; ...@@ -1120,8 +1124,8 @@ smooth in vec3 f_col;
void main() { void main() {
out_id = vec4(0.0f); out_id = vec4(0.0f);
out_col = vec4(f_col, alpha); out_col = vec4(f_col, alpha);
})"; })";
const std::string mesh_v = R"( const std::string mesh_v = R"(
#version 330 core #version 330 core
layout (location = 0) in vec3 v_pos; layout (location = 0) in vec3 v_pos;
...@@ -1138,7 +1142,7 @@ void main() { ...@@ -1138,7 +1142,7 @@ void main() {
f_norm = (normal * vec4(v_norm, 0.0f)).xyz; f_norm = (normal * vec4(v_norm, 0.0f)).xyz;
gl_Position = mvp * vec4(v_pos, 1.0f); gl_Position = mvp * vec4(v_pos, 1.0f);
})"; })";
const std::string inst_v = R"( const std::string inst_v = R"(
#version 330 core #version 330 core
layout (location = 0) in vec3 v_pos; layout (location = 0) in vec3 v_pos;
...@@ -1161,7 +1165,7 @@ void main() { ...@@ -1161,7 +1165,7 @@ void main() {
f_norm = (n * vec4(v_norm, 0.0f)).xyz; f_norm = (n * vec4(v_norm, 0.0f)).xyz;
gl_Position = proj * mv * vec4(v_pos, 1.0f); gl_Position = proj * mv * vec4(v_pos, 1.0f);
})"; })";
const std::string mesh_f = R"( const std::string mesh_f = R"(
#version 330 core #version 330 core
uniform bool solid, use_v_id; uniform bool solid, use_v_id;
...@@ -1193,7 +1197,7 @@ void main() { ...@@ -1193,7 +1197,7 @@ void main() {
float light = clamp(0.3f + 0.6f * ndotl, 0.0f, alpha); float light = clamp(0.3f + 0.6f * ndotl, 0.0f, alpha);
out_col = vec4(light * use_color, alpha); out_col = vec4(light * use_color, alpha);
} }
})"; })";
const std::string dome_v = R"( const std::string dome_v = R"(
#version 330 core #version 330 core
...@@ -1237,6 +1241,7 @@ void main() { ...@@ -1237,6 +1241,7 @@ void main() {
} }
} }
else discard; else discard;
})"; })";
}
} } // namespace Shaders
} // namespace GL
...@@ -2,11 +2,11 @@ ...@@ -2,11 +2,11 @@
#pragma once #pragma once
#include <string> #include <string>
#include <vector>
#include <unordered_map> #include <unordered_map>
#include <vector>
#include <glad/glad.h>
#include "../lib/mathlib.h" #include "../lib/mathlib.h"
#include <glad/glad.h>
namespace GL { namespace GL {
...@@ -20,12 +20,7 @@ void clear_screen(Vec4 col); ...@@ -20,12 +20,7 @@ void clear_screen(Vec4 col);
void viewport(Vec2 dim); void viewport(Vec2 dim);
int max_msaa(); int max_msaa();
enum class Opt { enum class Opt { wireframe, offset, culling, depth_write };
wireframe,
offset,
culling,
depth_write
};
void enable(Opt opt); void enable(Opt opt);
void disable(Opt opt); void disable(Opt opt);
...@@ -36,250 +31,248 @@ using TexID = GLuint; ...@@ -36,250 +31,248 @@ using TexID = GLuint;
class Tex2D { class Tex2D {
public: public:
Tex2D();
Tex2D(const Tex2D &src) = delete;
Tex2D(Tex2D &&src);
~Tex2D();
Tex2D(); void operator=(const Tex2D &src) = delete;
Tex2D(const Tex2D& src) = delete; void operator=(Tex2D &&src);
Tex2D(Tex2D&& src);
~Tex2D();
void operator=(const Tex2D& src) = delete; void image(int w, int h, unsigned char *img);
void operator=(Tex2D&& src); TexID get_id() const;
void bind(int idx = 0) const;
void image(int w, int h, unsigned char* img);
TexID get_id() const;
void bind(int idx = 0) const;
private: private:
GLuint id; GLuint id;
}; };
class Mesh { class Mesh {
public: public:
typedef GLuint Index; typedef GLuint Index;
struct Vert { struct Vert {
Vec3 pos; Vec3 pos;
Vec3 norm; Vec3 norm;
GLuint id; GLuint id;
}; };
Mesh(); Mesh();
Mesh(std::vector<Vert>&& vertices, std::vector<Index>&& indices); Mesh(std::vector<Vert> &&vertices, std::vector<Index> &&indices);
Mesh(const Mesh& src) = delete; Mesh(const Mesh &src) = delete;
Mesh(Mesh&& src); Mesh(Mesh &&src);
~Mesh(); ~Mesh();
void operator=(const Mesh& src) = delete; void operator=(const Mesh &src) = delete;
void operator=(Mesh&& src); void operator=(Mesh &&src);
/// Assumes proper shader is already bound /// Assumes proper shader is already bound
void render(); void render();
void recreate(std::vector<Vert>&& vertices, std::vector<Index>&& indices); void recreate(std::vector<Vert> &&vertices, std::vector<Index> &&indices);
std::vector<Vert>& edit_verts(); std::vector<Vert> &edit_verts();
std::vector<Index>& edit_indices(); std::vector<Index> &edit_indices();
BBox bbox() const; BBox bbox() const;
const std::vector<Vert>& verts() const; const std::vector<Vert> &verts() const;
const std::vector<Index>& indices() const; const std::vector<Index> &indices() const;
GLuint tris() const; GLuint tris() const;
private: private:
void update(); void update();
void create(); void create();
void destroy(); void destroy();
BBox _bbox; BBox _bbox;
GLuint vao = 0, vbo = 0, ebo = 0; GLuint vao = 0, vbo = 0, ebo = 0;
GLuint n_elem = 0; GLuint n_elem = 0;
bool dirty = true; bool dirty = true;
std::vector<Vert> _verts; std::vector<Vert> _verts;
std::vector<Index> _idxs; std::vector<Index> _idxs;
friend class Instances; friend class Instances;
}; };
class Instances { class Instances {
public: public:
Instances(GL::Mesh&& mesh); Instances(GL::Mesh &&mesh);
Instances(const Instances& src) = delete; Instances(const Instances &src) = delete;
Instances(Instances&& src); Instances(Instances &&src);
~Instances(); ~Instances();
void operator=(const Instances& src) = delete; void operator=(const Instances &src) = delete;
void operator=(Instances&& src); void operator=(Instances &&src);
struct Info { struct Info {
GLuint id; GLuint id;
Mat4 transform; Mat4 transform;
}; };
void render(); void render();
size_t add(const Mat4& transform, GLuint id = 0); size_t add(const Mat4 &transform, GLuint id = 0);
Info& get(size_t idx); Info &get(size_t idx);
void clear(); void clear();
private: private:
void create(); void create();
void destroy(); void destroy();
void update(); void update();
GLuint vbo = 0; GLuint vbo = 0;
bool dirty = false; bool dirty = false;
Mesh mesh; Mesh mesh;
std::vector<Info> data; std::vector<Info> data;
}; };
class Lines { class Lines {
public: public:
struct Vert { struct Vert {
Vec3 pos; Vec3 pos;
Vec3 color; Vec3 color;
}; };
Lines(float thickness = 1.0f); Lines(float thickness = 1.0f);
Lines(std::vector<Vert>&& verts, float thickness = 1.0f); Lines(std::vector<Vert> &&verts, float thickness = 1.0f);
Lines(const Lines& src) = delete; Lines(const Lines &src) = delete;
Lines(Lines&& src); Lines(Lines &&src);
~Lines(); ~Lines();
void operator=(const Lines& src) = delete; void operator=(const Lines &src) = delete;
void operator=(Lines&& src); void operator=(Lines &&src);
/// Assumes proper shader is already bound /// Assumes proper shader is already bound
void render(bool smooth) const; void render(bool smooth) const;
void add(Vec3 start, Vec3 end, Vec3 color); void add(Vec3 start, Vec3 end, Vec3 color);
void pop(); void pop();
void clear(); void clear();
private: private:
void create(); void create();
void destroy(); void destroy();
void update() const; void update() const;
mutable bool dirty = false; mutable bool dirty = false;
float thickness = 0.0f; float thickness = 0.0f;
GLuint vao = 0, vbo = 0; GLuint vao = 0, vbo = 0;
std::vector<Vert> vertices; std::vector<Vert> vertices;
}; };
class Shader { class Shader {
public: public:
Shader(); Shader();
Shader(std::string vertex_file, std::string fragment_file); Shader(std::string vertex_file, std::string fragment_file);
Shader(const Shader& src) = delete; Shader(const Shader &src) = delete;
Shader(Shader&& src); Shader(Shader &&src);
~Shader(); ~Shader();
void operator=(const Shader& src) = delete; void operator=(const Shader &src) = delete;
void operator=(Shader&& src); void operator=(Shader &&src);
void bind() const; void bind() const;
void load(std::string vertex, std::string fragment); void load(std::string vertex, std::string fragment);
void uniform(std::string name, const Mat4& mat) const; void uniform(std::string name, const Mat4 &mat) const;
void uniform(std::string name, Vec3 vec3) const; void uniform(std::string name, Vec3 vec3) const;
void uniform(std::string name, Vec2 vec2) const; void uniform(std::string name, Vec2 vec2) const;
void uniform(std::string name, GLint i) const; void uniform(std::string name, GLint i) const;
void uniform(std::string name, GLuint i) const; void uniform(std::string name, GLuint i) const;
void uniform(std::string name, GLfloat f) const; void uniform(std::string name, GLfloat f) const;
void uniform(std::string name, bool b) const; void uniform(std::string name, bool b) const;
void uniform(std::string name, int count, const Vec2 items[]) const; void uniform(std::string name, int count, const Vec2 items[]) const;
void uniform_block(std::string name, GLuint i) const; void uniform_block(std::string name, GLuint i) const;
private: private:
GLuint loc(std::string name) const; GLuint loc(std::string name) const;
static bool validate(GLuint program); static bool validate(GLuint program);
GLuint program = 0; GLuint program = 0;
GLuint v = 0, f = 0; GLuint v = 0, f = 0;
void destroy(); void destroy();
}; };
/// this is very restrictive; it assumes a set number of gl_rgb8 output /// this is very restrictive; it assumes a set number of gl_rgb8 output
/// textures and a floating point depth render buffer. /// textures and a floating point depth render buffer.
class Framebuffer { class Framebuffer {
public: public:
Framebuffer(); Framebuffer();
Framebuffer(int outputs, Vec2 dim, int samples = 1, bool depth = true); Framebuffer(int outputs, Vec2 dim, int samples = 1, bool depth = true);
Framebuffer(const Framebuffer& src) = delete; Framebuffer(const Framebuffer &src) = delete;
Framebuffer(Framebuffer&& src); Framebuffer(Framebuffer &&src);
~Framebuffer(); ~Framebuffer();
void operator=(const Framebuffer& src) = delete; void operator=(const Framebuffer &src) = delete;
void operator=(Framebuffer&& src); void operator=(Framebuffer &&src);
static void bind_screen(); static void bind_screen();
void setup(int outputs, Vec2 dim, int samples, bool d); void setup(int outputs, Vec2 dim, int samples, bool d);
void resize(Vec2 dim, int samples = 1); void resize(Vec2 dim, int samples = 1);
void bind() const; void bind() const;
bool is_multisampled() const; bool is_multisampled() const;
GLuint get_output(int buf) const; GLuint get_output(int buf) const;
GLuint get_depth() const; GLuint get_depth() const;
void flush() const; void flush() const;
int samples() const; int samples() const;
int bytes() const; int bytes() const;
bool can_read_at() const; bool can_read_at() const;
void read_at(int buf, int x, int y, GLubyte* data) const; void read_at(int buf, int x, int y, GLubyte *data) const;
void read(int buf, GLubyte* data) const; void read(int buf, GLubyte *data) const;
void blit_to_screen(int buf, Vec2 dim) const; void blit_to_screen(int buf, Vec2 dim) const;
void blit_to(int buf, const Framebuffer& fb, bool avg = true) const; void blit_to(int buf, const Framebuffer &fb, bool avg = true) const;
void clear(int buf, Vec4 col) const; void clear(int buf, Vec4 col) const;
void clear_d() const; void clear_d() const;
private: private:
void create(); void create();
void destroy(); void destroy();
std::vector<GLuint> output_textures; std::vector<GLuint> output_textures;
GLuint depth_tex = 0; GLuint depth_tex = 0;
GLuint framebuffer = 0; GLuint framebuffer = 0;
int w = 0, h = 0, s = 0; int w = 0, h = 0, s = 0;
bool depth = true; bool depth = true;
friend class Effects; friend class Effects;
}; };
class Effects { class Effects {
public: public:
static void resolve_to_screen(int buf, const Framebuffer& framebuffer); static void resolve_to_screen(int buf, const Framebuffer &framebuffer);
static void resolve_to(int buf, const Framebuffer& from, const Framebuffer& to, bool avg = true); static void resolve_to(int buf, const Framebuffer &from, const Framebuffer &to,
bool avg = true);
static void outline(const Framebuffer& from, const Framebuffer& to, Vec3 color, Vec2 min, Vec2 max); static void outline(const Framebuffer &from, const Framebuffer &to, Vec3 color, Vec2 min,
Vec2 max);
private: private:
static void init(); static void init();
static void destroy(); static void destroy();
static inline Shader resolve_shader, outline_shader, outline_shader_ms; static inline Shader resolve_shader, outline_shader, outline_shader_ms;
static inline GLuint vao; static inline GLuint vao;
static inline const Vec2 screen_quad[] = { static inline const Vec2 screen_quad[] = {Vec2{-1.0f, 1.0f}, Vec2{-1.0f, -1.0f},
Vec2{-1.0f, 1.0f}, Vec2{1.0f, 1.0f}, Vec2{1.0f, -1.0f}};
Vec2{-1.0f, -1.0f},
Vec2{1.0f, 1.0f}, friend void setup();
Vec2{1.0f, -1.0f} friend void shutdown();
};
static const std::string effects_v;
friend void setup(); static const std::string outline_f, outline_ms_f_33, outline_ms_f_4;
friend void shutdown(); static const std::string resolve_f;
static const std::string effects_v;
static const std::string outline_f, outline_ms_f_33, outline_ms_f_4;
static const std::string resolve_f;
}; };
namespace Shaders { namespace Shaders {
extern const std::string line_v, line_f; extern const std::string line_v, line_f;
extern const std::string mesh_v, mesh_f; extern const std::string mesh_v, mesh_f;
extern const std::string inst_v; extern const std::string inst_v;
extern const std::string dome_v, dome_f; extern const std::string dome_v, dome_f;
}
} } // namespace Shaders
} // namespace GL
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment