Commit c2535f0f authored by TheNumbat's avatar TheNumbat
Browse files

upstream changes

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