Explorar o código

Merge branch 'entity_model'

* entity_model:
  Default shaders
  Add some graphics helper functions
  Fix a bug where we have not yet started openGL properly when we load the default textures.
  Make it possible to invoke the game from an external runner.
  Fixing up keys a little (in progress)... Add graphics utilities to osx_env.mm Use double for delta-time.
  Doing some cleanup and relocation.
  Adding quad-collision detection, and triangle collision detection.
  Start making collision detection. TODO: intersects(quad, quad)
  Add update/render propagation to scenes.
  Add default behavior for to_vec
  Load the quad data immediately.
  Move code into serialization file
Sam Jaffe %!s(int64=6) %!d(string=hai) anos
pai
achega
1146b64e5d

+ 10 - 6
engine/engine.xcodeproj/project.pbxproj

@@ -7,13 +7,14 @@
 	objects = {
 
 /* Begin PBXBuildFile section */
-		CD2973951D7B117E00E37217 /* time.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CD2973931D7B117E00E37217 /* time.cpp */; };
 		CD62D8462251A94C0023219A /* libgraphics.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = CD62D8452251A94C0023219A /* libgraphics.dylib */; };
 		CD62D8482251A9500023219A /* libmath.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = CD62D8472251A9500023219A /* libmath.dylib */; };
 		CD62FCCE22904A8900376440 /* libengine.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = CDB1F8AE1D7A30CD00700C6B /* libengine.dylib */; };
 		CD62FCD622904A9B00376440 /* GoogleMock.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CD62FCBE22904A7C00376440 /* GoogleMock.framework */; };
 		CD62FD35229364DB00376440 /* entity.cxx in Sources */ = {isa = PBXBuildFile; fileRef = CD62FD33229364DB00376440 /* entity.cxx */; };
 		CD62FD3C229370E200376440 /* libjsoncpp.1.8.4.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = CD62FD3922936E9C00376440 /* libjsoncpp.1.8.4.dylib */; };
+		CD62FD402293746900376440 /* serial.cxx in Sources */ = {isa = PBXBuildFile; fileRef = CD62FD3E2293746900376440 /* serial.cxx */; };
+		CD7E87792295FAB400D877FE /* libgameutils.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = CD7E87782295FAB400D877FE /* libgameutils.dylib */; };
 		CDB1F8C81D7A312B00700C6B /* game_dispatch.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CDB1F8C61D7A312B00700C6B /* game_dispatch.cpp */; };
 		CDB1F8CC1D7A319A00700C6B /* scene.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CDB1F8CA1D7A319A00700C6B /* scene.cpp */; };
 		CDB1F8D21D7A32A300700C6B /* events.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CDB1F8D01D7A32A300700C6B /* events.cpp */; };
@@ -65,7 +66,6 @@
 /* End PBXContainerItemProxy section */
 
 /* Begin PBXFileReference section */
-		CD2973931D7B117E00E37217 /* time.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = time.cpp; sourceTree = "<group>"; };
 		CD62D8452251A94C0023219A /* libgraphics.dylib */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.dylib"; path = libgraphics.dylib; sourceTree = BUILT_PRODUCTS_DIR; };
 		CD62D8472251A9500023219A /* libmath.dylib */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.dylib"; path = libmath.dylib; sourceTree = BUILT_PRODUCTS_DIR; };
 		CD62FCB622904A7B00376440 /* GoogleMock.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = GoogleMock.xcodeproj; path = "../../gmock-xcode-master/GoogleMock.xcodeproj"; sourceTree = "<group>"; };
@@ -73,6 +73,8 @@
 		CD62FCCD22904A8900376440 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
 		CD62FD33229364DB00376440 /* entity.cxx */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = entity.cxx; sourceTree = "<group>"; };
 		CD62FD3922936E9C00376440 /* libjsoncpp.1.8.4.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libjsoncpp.1.8.4.dylib; path = ../../../../../../../opt/local/lib/libjsoncpp.1.8.4.dylib; sourceTree = "<group>"; };
+		CD62FD3E2293746900376440 /* serial.cxx */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = serial.cxx; sourceTree = "<group>"; };
+		CD7E87782295FAB400D877FE /* libgameutils.dylib */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.dylib"; path = libgameutils.dylib; sourceTree = BUILT_PRODUCTS_DIR; };
 		CDA34D8422515C99008036A7 /* game */ = {isa = PBXFileReference; lastKnownFileType = folder; name = game; path = include/game; sourceTree = "<group>"; };
 		CDB1F8AE1D7A30CD00700C6B /* libengine.dylib */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.dylib"; includeInIndex = 0; path = libengine.dylib; sourceTree = BUILT_PRODUCTS_DIR; };
 		CDB1F8C61D7A312B00700C6B /* game_dispatch.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = game_dispatch.cpp; sourceTree = "<group>"; };
@@ -94,6 +96,7 @@
 			isa = PBXFrameworksBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
+				CD7E87792295FAB400D877FE /* libgameutils.dylib in Frameworks */,
 				CD62FD3C229370E200376440 /* libjsoncpp.1.8.4.dylib in Frameworks */,
 				CD62D8482251A9500023219A /* libmath.dylib in Frameworks */,
 				CD62D8462251A94C0023219A /* libgraphics.dylib in Frameworks */,
@@ -106,6 +109,7 @@
 		CD62D8442251A94C0023219A /* Frameworks */ = {
 			isa = PBXGroup;
 			children = (
+				CD7E87782295FAB400D877FE /* libgameutils.dylib */,
 				CD62FD3922936E9C00376440 /* libjsoncpp.1.8.4.dylib */,
 				CD62D8452251A94C0023219A /* libgraphics.dylib */,
 				CD62D8472251A9500023219A /* libmath.dylib */,
@@ -164,11 +168,11 @@
 		CDB1F8B01D7A30CD00700C6B /* src */ = {
 			isa = PBXGroup;
 			children = (
-				CDB1F8C61D7A312B00700C6B /* game_dispatch.cpp */,
-				CDB1F8CA1D7A319A00700C6B /* scene.cpp */,
 				CD62FD33229364DB00376440 /* entity.cxx */,
 				CDB1F8D01D7A32A300700C6B /* events.cpp */,
-				CD2973931D7B117E00E37217 /* time.cpp */,
+				CDB1F8C61D7A312B00700C6B /* game_dispatch.cpp */,
+				CDB1F8CA1D7A319A00700C6B /* scene.cpp */,
+				CD62FD3E2293746900376440 /* serial.cxx */,
 			);
 			path = src;
 			sourceTree = "<group>";
@@ -340,10 +344,10 @@
 			buildActionMask = 2147483647;
 			files = (
 				CD62FD35229364DB00376440 /* entity.cxx in Sources */,
-				CD2973951D7B117E00E37217 /* time.cpp in Sources */,
 				CDB1F8D21D7A32A300700C6B /* events.cpp in Sources */,
 				CDB1F8CC1D7A319A00700C6B /* scene.cpp in Sources */,
 				CDB1F8C81D7A312B00700C6B /* game_dispatch.cpp in Sources */,
+				CD62FD402293746900376440 /* serial.cxx in Sources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};

+ 2 - 0
engine/include/game/engine/engine_fwd.hpp

@@ -11,6 +11,8 @@
 #include <unordered_map>
 
 namespace engine {
+  using collision_t = int;
+
   using raw_key_t = int;
   using key_enum_t = int;
   using scene_id_t = std::string;

+ 7 - 4
engine/include/game/engine/entity.hpp

@@ -19,7 +19,10 @@ namespace engine {
   class entity : identity<entity> {
   public:
     entity(Json::Value const & json);
-    void update(tick const & tk);
+    void update(float delta);
+    void collide(entity const &);
+
+    graphics::object const & render_info() const { return render_info_; }
 
   private:
     // The scene that owns this object
@@ -31,14 +34,14 @@ namespace engine {
     math::vec2 acceleration;
     float angular_velocity;
     // A mix of position and graphics info
-    graphics::object render_info;
+    graphics::object render_info_;
     // Graphics info
     std::size_t frame_index;
     std::vector<math::vec2> frame_texture_coords;
 
     float scale;
 
-    int collides_with;
-    int collides_as;
+    collision_t collides_with;
+    collision_t collides_as;
   };
 }

+ 10 - 11
engine/include/game/engine/events.hpp

@@ -13,17 +13,16 @@
 #include "engine_fwd.hpp"
 
 namespace engine {
-  namespace key {
-    enum default_keys : key_enum_t {
-      FORWARD,
-      LEFT,
-      BACKWARD,
-      RIGHT,
-      JUMP,
-      CROUCH,
-      INTERACT,
-      QUIT
-    };
+  namespace keys {
+    enum default_keys : key_enum_t { QUIT = 'q' };
+    extern key_enum_t const UP_ARROW;
+    extern key_enum_t const DOWN_ARROW;
+    extern key_enum_t const LEFT_ARROW;
+    extern key_enum_t const RIGHT_ARROW;
+    extern key_enum_t const MOD_SHIFT;
+    extern key_enum_t const MOD_CONTROL;
+    extern key_enum_t const MOD_ALT;
+    extern key_enum_t const MOD_COMMAND;
   }
 
   namespace event {

+ 9 - 6
engine/include/game/engine/game_dispatch.hpp

@@ -16,7 +16,7 @@
 
 #include "engine_fwd.hpp"
 #include "events.hpp"
-#include "time.hpp"
+#include "game/util/time.hpp"
 
 namespace engine {
   class game_dispatch {
@@ -26,14 +26,17 @@ namespace engine {
     void process_key_event(raw_key_t key, bool press);
     void process_mouse_event(math::vec2 click, bool press);
 
-    [[noreturn]] void gameloop();
-    void quit();
+    bool is_running() const { return running; }
+    void quit() { running = false; }
+
+  public:
+    void update();
+    void render();
 
   private:
-    void single_frame(tick t) {}
+    float next_frame();
     tick get_tick();
-    tick next_frame();
-    void cleanup_resources() {} // TODO ???
+
   private:
     math::vec2 screen_size;
     duration minimum_frame_duration;

+ 10 - 1
engine/include/game/engine/scene.hpp

@@ -8,6 +8,7 @@
 #pragma once
 
 #include <memory>
+#include <unordered_map>
 #include <vector>
 
 #include "game/math/math_fwd.hpp"
@@ -21,12 +22,14 @@ namespace graphics {
 }
 
 namespace engine {
+  class entity;
+
   class scene : public identity<scene, std::string> {
   public:
     using identity<scene, std::string>::identity;
     virtual ~scene();
 
-    virtual void update(tick);
+    virtual void update(float delta);
     virtual void render();
     virtual void handle_key_event(event::key_event evt);
     virtual void handle_mouse_event(event::mouse_event evt);
@@ -39,6 +42,12 @@ namespace engine {
 
   protected:
     graphics::renderer * renderer;
+    std::vector<entity> entities;
+
+    // Map from entity::collides_with -> [entity*]
+    std::unordered_map<collision_t, std::vector<entity *>> colliders;
+    // Map from entity::collides_as -> [entity*]
+    std::unordered_map<collision_t, std::vector<entity *>> collidables;
 
   private:
     math::vec2 local_scene_dimension_;

+ 25 - 0
engine/include/game/engine/serial.hpp

@@ -0,0 +1,25 @@
+//
+//  serial.hpp
+//  engine
+//
+//  Created by Sam Jaffe on 5/20/19.
+//  Copyright © 2019 Sam Jaffe. All rights reserved.
+//
+
+#pragma once
+
+#include <json/forwards.h>
+
+#include "game/math/math_fwd.hpp"
+
+template <typename> class flyweight;
+
+namespace graphics {
+  struct object;
+}
+
+namespace engine {
+  math::vec2 to_vec2(Json::Value const & json);
+  math::vec2 to_vec2(Json::Value const & json, math::vec2 const & backup);
+  graphics::object to_object(Json::Value const & json);
+}

+ 14 - 37
engine/src/entity.cxx

@@ -10,8 +10,9 @@
 
 #include <json/json.h>
 
-#include "game/engine/time.hpp"
+#include "game/engine/serial.hpp"
 #include "game/graphics/material.hpp"
+#include "game/util/time.hpp"
 
 using namespace engine;
 
@@ -20,50 +21,26 @@ static unsigned int next_id() {
   return ++id;
 }
 
-static math::vec2 to_vec2(Json::Value const & json) {
-  return make_vector(json[0].asFloat(), json[1].asFloat());
-}
-
-static flyweight<graphics::shader_program>
-to_program(Json::Value const & json) {
-  return graphics::shader_program::create(json["fragmentShader"].asString(),
-                                          json["vertexShader"].asString());
-}
-
-static flyweight<graphics::material> to_material(Json::Value const & json) {
-  graphics::shader_program prog = to_program(json["shaderProgram"]).actual();
-  return graphics::material::create(prog, json["texture"]["file"].asString(),
-                                    json["texture"]["uniform"].asString());
-}
-
-static graphics::object to_object(Json::Value const & json) {
-  flyweight<graphics::material> mat = to_material(json["material"]);
-  auto frame_size = to_vec2(json["frameSize"]);
-  return {{to_vec2(json["position"]), mat.actual().size() * frame_size},
-          {},
-          mat,
-          {{}, frame_size}};
-}
-
 entity::entity(Json::Value const & json)
     : identity<entity>(next_id()), in_scene(), last_position({-1, -1}),
       velocity(to_vec2(json["velocity"])), acceleration(),
-      angular_velocity(0.f), render_info(to_object(json)), frame_index(0),
+      angular_velocity(0.f), render_info_(to_object(json)), frame_index(0),
       frame_texture_coords({make_vector(0.f, 0.f)}),
       scale(json["size"].asFloat()), collides_with(0), collides_as(0) {
-  render_info.location.size *= scale;
-  render_info.frame.origin = frame_texture_coords[frame_index];
+  render_info_.location.size *= scale;
+  render_info_.frame.origin = frame_texture_coords[frame_index];
 }
 
-void entity::update(tick const & tk) {
-  last_position = render_info.location.origin;
-  float delta = tk.since.count();
+void entity::update(float delta) {
+  last_position = render_info_.location.origin;
   auto delta_pos = velocity * delta;
-  render_info.location.origin += delta_pos;
-  render_info.points.ll += delta_pos;
-  render_info.points.lr += delta_pos;
-  render_info.points.ur += delta_pos;
-  render_info.points.ul += delta_pos;
+  render_info_.location.origin += delta_pos;
+  render_info_.points.ll += delta_pos;
+  render_info_.points.lr += delta_pos;
+  render_info_.points.ur += delta_pos;
+  render_info_.points.ul += delta_pos;
   velocity += acceleration * delta;
   //  render_info.angle += angular_velocity * delta;
 }
+
+void entity::collide(entity const &) {}

+ 10 - 0
engine/src/events.cpp

@@ -6,3 +6,13 @@
 //
 
 #include "game/engine/events.hpp"
+
+engine::key_enum_t const engine::keys::RIGHT_ARROW = 0x0CF;
+engine::key_enum_t const engine::keys::LEFT_ARROW = 0x0D0;
+engine::key_enum_t const engine::keys::DOWN_ARROW = 0x0D1;
+engine::key_enum_t const engine::keys::UP_ARROW = 0x0D2;
+
+engine::key_enum_t const engine::keys::MOD_CONTROL = 0x160; // 0x164
+engine::key_enum_t const engine::keys::MOD_SHIFT = 0x161;   // 0x165
+engine::key_enum_t const engine::keys::MOD_ALT = 0x162;     // 0x166
+engine::key_enum_t const engine::keys::MOD_COMMAND = 0x163; // 0x167

+ 8 - 15
engine/src/game_dispatch.cpp

@@ -11,6 +11,7 @@
 
 #include "game/engine/events.hpp"
 #include "game/engine/scene.hpp"
+#include "game/util/env.hpp"
 
 namespace engine {
   namespace {
@@ -21,8 +22,9 @@ namespace engine {
   }
 
   game_dispatch::game_dispatch()
-      : screen_size({1920.f, 1080.f}), minimum_frame_duration(engine::fps::v60),
-        scenes(), current_timestamp(clock::now()), curr_scene() {}
+      : screen_size(env::screen_size()),
+        minimum_frame_duration(engine::fps::v60), scenes(),
+        current_timestamp(clock::now()), curr_scene() {}
 
   game_dispatch::current_scene_info::current_scene_info() {}
 
@@ -53,7 +55,7 @@ namespace engine {
     return {now, now - current_timestamp};
   }
 
-  tick game_dispatch::next_frame() {
+  float game_dispatch::next_frame() {
     tick t = get_tick();
 
     while (t.since < minimum_frame_duration) {
@@ -62,19 +64,10 @@ namespace engine {
     }
 
     current_timestamp = t.now;
-    return t;
+    return std::chrono::duration<float>(t.since).count();
   }
 
-  void game_dispatch::gameloop() try {
-    while (running) {
-      single_frame(next_frame());
-    }
-    cleanup_resources();
-    exit(EXIT_SUCCESS);
-  } catch (std::exception const & ex) {
-    // todo: logging
-    exit(EXIT_FAILURE);
-  }
+  void game_dispatch::update() { curr_scene.ptr->update(next_frame()); }
 
-  void game_dispatch::quit() { running = false; }
+  void game_dispatch::render() { curr_scene.ptr->render(); }
 }

+ 33 - 12
engine/src/scene.cpp

@@ -7,26 +7,47 @@
 
 #include "game/engine/scene.hpp"
 
+#include "game/engine/entity.hpp"
 #include "game/engine/events.hpp"
 #include "game/engine/game_dispatch.hpp"
+#include "game/graphics/renderer.hpp"
+#include "game/math/common.hpp"
 
-namespace engine {
+using namespace engine;
 
-  scene::~scene() {}
+scene::~scene() {}
 
-  void scene::update(tick) {}
+void scene::update(float delta) {
+  for (auto & ent : entities) {
+    ent.update(delta);
+  }
+  for (auto & pair : colliders) {
+    auto & collidable = collidables[pair.first];
+    for (auto & ent : pair.second) {
+      for (auto & hit : collidable) {
+        if (math::intersects(ent->render_info().points,
+                             hit->render_info().points)) {
+          ent->collide(*hit);
+        }
+      }
+    }
+  }
+}
 
-  void scene::render() {}
+void scene::render() {
+  for (auto & ent : entities) {
+    renderer->draw(ent.render_info());
+  }
+}
 
-  void scene::handle_key_event(event::key_event evt) {
-    if (evt.type & event::PRESSED_MASK && evt.key == key::QUIT) {
-      dispatch_.lock()->quit();
-    }
+void scene::handle_key_event(event::key_event evt) {
+  if (evt.type & event::PRESSED_MASK && evt.key == keys::QUIT) {
+    dispatch_.lock()->quit();
   }
+}
 
-  void scene::handle_mouse_event(event::mouse_event evt) {}
+void scene::handle_mouse_event(event::mouse_event evt) {}
 
-  math::vec2 scene::size() const { return local_scene_dimension_; }
+math::vec2 scene::size() const { return local_scene_dimension_; }
 
-  key_binding const & scene::keys() const { return keys_; }
-}
+key_binding const & scene::keys() const { return keys_; }

+ 50 - 0
engine/src/serial.cxx

@@ -0,0 +1,50 @@
+//
+//  serial.cxx
+//  engine
+//
+//  Created by Sam Jaffe on 5/20/19.
+//  Copyright © 2019 Sam Jaffe. All rights reserved.
+//
+
+#include "game/engine/serial.hpp"
+
+#include <json/json.h>
+
+#include "game/graphics/material.hpp"
+#include "game/graphics/object.hpp"
+#include "game/util/flyweight.hpp"
+#include "vector/vector.hpp"
+
+namespace engine {
+  math::vec2 to_vec2(Json::Value const & json) {
+    return make_vector(json[0].asFloat(), json[1].asFloat());
+  }
+
+  math::vec2 to_vec2(Json::Value const & json, math::vec2 const & backup) {
+    return json.isNull() ? backup : to_vec2(json);
+  }
+
+  flyweight<graphics::shader_program> to_program(Json::Value const & json) {
+    using graphics::shader_program;
+    if (json.empty()) {
+      return shader_program::create("data/shaders/BlankShader.fragment.glsl",
+                                    "data/shaders/BlankShader.vertex.glsl");
+    }
+    return shader_program::create(json["fragmentShader"].asString(),
+                                  json["vertexShader"].asString());
+  }
+
+  flyweight<graphics::material> to_material(Json::Value const & json) {
+    auto & prog = to_program(json["shaderProgram"]).actual();
+    return graphics::material::create(prog, json["texture"]["file"].asString(),
+                                      json["texture"]["uniform"].asString());
+  }
+
+  graphics::object to_object(Json::Value const & json) {
+    flyweight<graphics::material> mat = to_material(json["material"]);
+    auto frame_size = to_vec2(json["frameSize"], make_vector(1.f, 1.f));
+    math::dim2::rectangle pos = {to_vec2(json["position"]),
+                                 mat.actual().size() * frame_size};
+    return {pos, pos, mat, {{{0, 0}}, frame_size}};
+  }
+}

+ 6 - 0
graphics/graphics.xcodeproj/project.pbxproj

@@ -7,6 +7,7 @@
 	objects = {
 
 /* Begin PBXBuildFile section */
+		CD1C82BD22988EC700825C4E /* matrix.cxx in Sources */ = {isa = PBXBuildFile; fileRef = CD1C82BC22988EC700825C4E /* matrix.cxx */; };
 		CD3AC6F21D2C03B7002B4BB0 /* material.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CD3AC6F01D2C03B7002B4BB0 /* material.cpp */; };
 		CD3AC6F81D2C0518002B4BB0 /* texture.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CD3AC6F61D2C0518002B4BB0 /* texture.cpp */; };
 		CD3AC6FD1D2C06B5002B4BB0 /* shader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CD3AC6FB1D2C06B5002B4BB0 /* shader.cpp */; };
@@ -54,6 +55,8 @@
 /* End PBXContainerItemProxy section */
 
 /* Begin PBXFileReference section */
+		CD1C82B722988E4E00825C4E /* matrix.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = matrix.hpp; sourceTree = "<group>"; };
+		CD1C82BC22988EC700825C4E /* matrix.cxx */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = matrix.cxx; sourceTree = "<group>"; };
 		CD3AC6E21D2C0364002B4BB0 /* libgraphics.dylib */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.dylib"; includeInIndex = 0; path = libgraphics.dylib; sourceTree = BUILT_PRODUCTS_DIR; };
 		CD3AC6F01D2C03B7002B4BB0 /* material.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = material.cpp; sourceTree = "<group>"; };
 		CD3AC6F61D2C0518002B4BB0 /* texture.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = texture.cpp; sourceTree = "<group>"; };
@@ -144,6 +147,8 @@
 			children = (
 				CD62FD1C2292412900376440 /* renderer.cxx */,
 				CD62FD202292C76B00376440 /* renderer_impl.hpp */,
+				CD1C82B722988E4E00825C4E /* matrix.hpp */,
+				CD1C82BC22988EC700825C4E /* matrix.cxx */,
 				CD62FD212292C76B00376440 /* opengl_renderer.cxx */,
 			);
 			name = renderer;
@@ -287,6 +292,7 @@
 			files = (
 				CD3AC6FD1D2C06B5002B4BB0 /* shader.cpp in Sources */,
 				CD3AC7191D2C0950002B4BB0 /* shader_program.cpp in Sources */,
+				CD1C82BD22988EC700825C4E /* matrix.cxx in Sources */,
 				CD3AC6F21D2C03B7002B4BB0 /* material.cpp in Sources */,
 				CD62FD1E2292412900376440 /* renderer.cxx in Sources */,
 				CD3AC6F81D2C0518002B4BB0 /* texture.cpp in Sources */,

+ 4 - 0
graphics/include/game/graphics/renderer.hpp

@@ -15,11 +15,13 @@
 
 namespace graphics {
   class material;
+  struct object;
   struct renderer_impl;
   struct vertex;
 
   struct renderer {
     virtual ~renderer() {}
+    virtual void draw(object const & obj) = 0;
     virtual void draw(flyweight<material>, math::matr4 const &,
                       std::vector<vertex> const &) = 0;
     virtual void clear() = 0;
@@ -31,6 +33,7 @@ namespace graphics {
   class direct_renderer : public renderer {
   public:
     direct_renderer(driver d);
+    void draw(object const & obj) override;
     void draw(flyweight<material>, math::matr4 const &,
               std::vector<vertex> const &) override;
     void clear() override;
@@ -43,6 +46,7 @@ namespace graphics {
   class batch_renderer : public renderer {
   public:
     batch_renderer(renderer * impl, std::size_t batch_size = 0);
+    void draw(object const & obj) override;
     void draw(flyweight<material>, math::matr4 const &,
               std::vector<vertex> const &) override;
     void clear() override { impl_->clear(); }

+ 3 - 3
graphics/include/game/graphics/texture.hpp

@@ -14,9 +14,9 @@
 namespace graphics {
   class texture : public identity<texture> {
   public:
-    static texture const WHITE;
-    static texture const DARK_YELLOW;
-    static texture const LIGHT_BLUE;
+    static texture const WHITE();
+    static texture const DARK_YELLOW();
+    static texture const LIGHT_BLUE();
     math::vec2i const size;
 
   public:

+ 3 - 3
graphics/src/material.cpp

@@ -30,11 +30,11 @@ flyweight<texture> get_texture(std::string const & texture,
     }
   }
   if (uniform == "u_normalMap") {
-    return texture::LIGHT_BLUE;
+    return texture::LIGHT_BLUE();
   } else if (uniform == "u_specularMap") {
-    return texture::DARK_YELLOW;
+    return texture::DARK_YELLOW();
   } else if (uniform == "u_diffuseMap") {
-    return texture::WHITE;
+    return texture::WHITE();
   }
   throw;
 }

+ 50 - 0
graphics/src/matrix.cxx

@@ -0,0 +1,50 @@
+//
+//  matrix.cxx
+//  graphics
+//
+//  Created by Sam Jaffe on 5/24/19.
+//  Copyright © 2019 Sam Jaffe. All rights reserved.
+//
+
+#include "matrix.hpp"
+
+#include "game/math/angle.hpp"
+#include "matrix/matrix.hpp"
+
+namespace graphics {
+  math::matr4 orthogonal_view(float left, float right, float bottom, float top,
+                              float near, float far) {
+    math::matr4 matrix;
+    matrix(0, 0) = 2.f / (right - left);
+    matrix(1, 1) = 2.f / (top - bottom);
+    matrix(2, 2) = -2.f / (far - near);
+    matrix(0, 3) = -(right + left) / (right - left);
+    matrix(1, 3) = -(top + bottom) / (top - bottom);
+    matrix(2, 3) = -(far + near) / (far - near);
+    matrix(3, 3) = 1.f;
+    return matrix;
+  }
+
+  math::matr4 frustum_view(float left, float right, float bottom, float top,
+                           float near, float far) {
+    math::matr4 matrix;
+    matrix(0, 0) = 2.f * near / (right - left);
+    matrix(1, 1) = 2.f * near / (top - bottom);
+    matrix(2, 0) = (right + left) / (right - left);
+    matrix(2, 1) = (top + bottom) / (top - bottom);
+    matrix(2, 2) = -(far + near) / (far - near);
+    matrix(2, 3) = -1.f;
+    matrix(3, 2) = -(2.f * far * near) / (far - near);
+    matrix(3, 3) = 0.f;
+    return matrix;
+  }
+
+  math::matr4 perspective(math::degree fovY, float aspect, float front,
+                          float back) {
+    float tangent = tan(fovY);
+    float height = front * tangent;
+    float width = height * aspect;
+
+    return frustum_view(-width, width, -height, height, front, back);
+  }
+}

+ 20 - 0
graphics/src/matrix.hpp

@@ -0,0 +1,20 @@
+//
+//  matrix.hpp
+//  graphics
+//
+//  Created by Sam Jaffe on 5/24/19.
+//  Copyright © 2019 Sam Jaffe. All rights reserved.
+//
+
+#pragma once
+
+#include "game/math/math_fwd.hpp"
+
+namespace graphics {
+  math::matr4 orthogonal_view(float left, float right, float bottom, float top,
+                              float near, float far);
+  math::matr4 frustum_view(float left, float right, float bottom, float top,
+                           float near, float far);
+  math::matr4 perspective(math::degree fovY, float aspect, float front,
+                          float back);
+}

+ 18 - 8
graphics/src/opengl_renderer.cxx

@@ -14,6 +14,9 @@
 
 #include "game/graphics/material.hpp"
 #include "game/graphics/vertex.h"
+#include "game/util/env.hpp"
+#include "game/util/time.hpp"
+#include "matrix.hpp"
 #include "matrix/matrix.hpp"
 #include "matrix/matrix_helpers.hpp"
 
@@ -51,19 +54,25 @@ opengl_renderer::~opengl_renderer() {
 void opengl_renderer::clear() {
   world_to_clip = identity;
 
-  // vec2i resolution = env::resolution();
-  // orthogonal_view(0.0, resolution[0], 0.0, resolution[1], -1.0, 1.0);
+  // math::vec2i resolution = env::resolution();
+  math::vec2i resolution = env::screen_size();
+  world_to_clip = graphics::orthogonal_view(0.f, resolution[0], 0.f,
+                                            resolution[1], -1.f, 1.f);
 
   glClearDepth(1.f);
   glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
   glClearColor(0.f, 0.f, 0.0f, 1.f);
   glEnable(GL_DEPTH_TEST);
 
-  // TODO: Use a unified time calculation
-  current_time = time(NULL);
+  // TODO (sjaffe): Refactor this out...
+  current_time =
+      std::chrono::duration<double>(engine::clock::now().time_since_epoch())
+          .count();
 
-  // TODO: ???
-  // scale(0.5, 0.5, 1); // Don't know why I need to do this
+  // TODO (sjaffe): I labeled this as needed before, but I don't know why
+  //      I think it has to do with the resolution being 2x the screen size
+  //  static auto const rescale = make_vector(0.5f, 0.5f, 1.f);
+  //  world_to_clip = world_to_clip * math::matrix::scalar(rescale);
 }
 
 void opengl_renderer::flush() { glFlush(); }
@@ -78,10 +87,11 @@ void opengl_renderer::draw(flyweight<material> material,
   mat.activate();
 
   int objectLocation = glGetUniformLocation(id, "u_objectToWorldMatrix");
-  glUniformMatrix4fv(objectLocation, 1, false, &object_to_world(0, 0));
+  // Argument 2: GL_TRUE -> row-major ; GL_FALSE -> column-major
+  glUniformMatrix4fv(objectLocation, 1, GL_TRUE, &object_to_world(0, 0));
 
   int clipLocation = glGetUniformLocation(id, "u_worldToClipMatrix");
-  glUniformMatrix4fv(clipLocation, 1, false, &world_to_clip(0, 0));
+  glUniformMatrix4fv(clipLocation, 1, GL_TRUE, &world_to_clip(0, 0));
 
   int timeLocation = glGetUniformLocation(id, "u_time");
   glUniform1d(timeLocation, current_time);

+ 16 - 0
graphics/src/renderer.cxx

@@ -9,6 +9,7 @@
 #include "game/graphics/renderer.hpp"
 #include <vector>
 
+#include "game/graphics/object.hpp"
 #include "game/graphics/vertex.h"
 #include "matrix/matrix.hpp"
 #include "renderer_impl.hpp"
@@ -26,6 +27,12 @@ renderer_impl * get_renderer_impl(driver d) {
 
 direct_renderer::direct_renderer(driver d) : pimpl(::get_renderer_impl(d)) {}
 
+void direct_renderer::draw(object const & obj) {
+  std::vector<vertex> verts;
+  vertices(verts, obj);
+  draw(obj.material, math::matr4(), verts);
+}
+
 void direct_renderer::draw(flyweight<material> material, math::matr4 const &,
                            std::vector<vertex> const & verts) {
   pimpl->draw(material, {}, verts);
@@ -37,11 +44,20 @@ void direct_renderer::flush() { pimpl->flush(); }
 batch_renderer::batch_renderer(renderer * impl, std::size_t batch_size)
     : impl_(impl), batches_(), batch_size_(batch_size), elements_(0) {}
 
+void batch_renderer::draw(object const & obj) {
+  std::vector<vertex> & batch_verts = batches_[obj.material];
+  auto old_size = batch_verts.size();
+  vertices(batch_verts, obj);
+  elements_ += batch_verts.size() - old_size;
+  check();
+}
+
 // TODO (sjaffe): object-to-world matrix...
 void batch_renderer::draw(flyweight<material> material, math::matr4 const &,
                           std::vector<vertex> const & verts) {
   auto & batch_verts = batches_[material];
   batch_verts.insert(batch_verts.end(), verts.begin(), verts.end());
+  elements_ += verts.size();
   check();
 }
 

+ 12 - 5
graphics/src/texture.cpp

@@ -61,8 +61,15 @@ texture texture::create(char const * data, math::vec2i size) {
 texture::texture(unsigned int id, math::vec2i sz)
     : identity<texture>(id), size(sz) {}
 
-texture const texture::WHITE = texture::create("\xFF\xFF\xFF\xFF", {{1, 1}});
-texture const texture::DARK_YELLOW =
-    texture::create("\x80\x80\x00\xFF", {{1, 1}});
-texture const texture::LIGHT_BLUE =
-    texture::create("\x80\x80\xFF\xFF", {{1, 1}});
+texture const texture::WHITE() {
+  static auto t = texture::create("\xFF\xFF\xFF\xFF", {{1, 1}});
+  return t;
+}
+texture const texture::DARK_YELLOW() {
+  static auto t = texture::create("\x80\x80\x00\xFF", {{1, 1}});
+  return t;
+}
+texture const texture::LIGHT_BLUE() {
+  static auto t = texture::create("\x80\x80\xFF\xFF", {{1, 1}});
+  return t;
+}

+ 1 - 0
math/include/game/math/angle.hpp

@@ -22,4 +22,5 @@ namespace math {
 
   double sin(radian r);
   double cos(radian r);
+  double tan(radian r);
 }

+ 4 - 0
math/include/game/math/shape.hpp

@@ -25,6 +25,10 @@ namespace math { namespace dim2 {
     float radius;
   };
 
+  struct triangle {
+    point a, b, c;
+  };
+
   struct quad {
     point ll, lr, ur, ul;
   };

+ 1 - 0
math/src/angle.cpp

@@ -18,4 +18,5 @@ namespace math {
 
   double sin(radian r) { return std::sin(r.value); }
   double cos(radian r) { return std::cos(r.value); }
+  double tan(radian r) { return std::tan(r.value); }
 }

+ 23 - 1
math/src/common.cpp

@@ -109,7 +109,29 @@ namespace math {
            contains(lhs, rhs.center);
   }
 
-  bool intersects(dim2::quad const & lhs, dim2::quad const & rhs);
+  bool check_edges(dim2::line const & seg, dim2::triangle const & tri) {
+    return orient(seg, tri.a) == clockwise && orient(seg, tri.b) == clockwise &&
+           orient(seg, tri.c) == clockwise;
+  }
+
+  bool intersects(dim2::triangle const & lhs, dim2::triangle const & rhs) {
+    if (check_edges({lhs.a, lhs.b}, rhs)) return false;
+    if (check_edges({lhs.b, lhs.c}, rhs)) return false;
+    if (check_edges({lhs.c, lhs.a}, rhs)) return false;
+    if (check_edges({rhs.a, rhs.b}, lhs)) return false;
+    if (check_edges({rhs.b, rhs.c}, lhs)) return false;
+    if (check_edges({rhs.c, rhs.a}, lhs)) return false;
+    return true;
+  }
+
+  bool intersects(dim2::quad const & lhs, dim2::quad const & rhs) {
+    dim2::triangle l1{lhs.ll, lhs.lr, lhs.ul};
+    dim2::triangle l2{lhs.ul, lhs.ur, lhs.ll};
+    dim2::triangle r1{rhs.ll, rhs.lr, rhs.ul};
+    dim2::triangle r2{rhs.ul, rhs.ur, rhs.ll};
+    return intersects(l1, r1) || intersects(l2, r2) || intersects(l1, r2) ||
+           intersects(l2, r1);
+  }
 
   bool intersects(dim2::circle const & lhs, dim2::circle const & rhs) {
     vec2 const delta = rhs.center - lhs.center;

+ 18 - 2
util/gameutils.xcodeproj/project.pbxproj

@@ -9,6 +9,8 @@
 /* Begin PBXBuildFile section */
 		CD62FD04229195FF00376440 /* files.cxx in Sources */ = {isa = PBXBuildFile; fileRef = CD62FD02229195FF00376440 /* files.cxx */; };
 		CD62FD082291988F00376440 /* osx_env.mm in Sources */ = {isa = PBXBuildFile; fileRef = CD62FD072291988F00376440 /* osx_env.mm */; };
+		CD7E87772295FA1F00D877FE /* time.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CD7E876F2295FA1F00D877FE /* time.cpp */; };
+		CD7E884822960F4800D877FE /* AppKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CD7E884722960F4800D877FE /* AppKit.framework */; };
 /* End PBXBuildFile section */
 
 /* Begin PBXContainerItemProxy section */
@@ -49,6 +51,8 @@
 		CD62FCFF2291953700376440 /* game */ = {isa = PBXFileReference; lastKnownFileType = folder; name = game; path = include/game; sourceTree = "<group>"; };
 		CD62FD02229195FF00376440 /* files.cxx */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = files.cxx; sourceTree = "<group>"; };
 		CD62FD072291988F00376440 /* osx_env.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = osx_env.mm; sourceTree = "<group>"; };
+		CD7E876F2295FA1F00D877FE /* time.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = time.cpp; sourceTree = "<group>"; };
+		CD7E884722960F4800D877FE /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = System/Library/Frameworks/AppKit.framework; sourceTree = SDKROOT; };
 /* End PBXFileReference section */
 
 /* Begin PBXFrameworksBuildPhase section */
@@ -56,6 +60,7 @@
 			isa = PBXFrameworksBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
+				CD7E884822960F4800D877FE /* AppKit.framework in Frameworks */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -69,6 +74,7 @@
 				CD62FCFF2291953700376440 /* game */,
 				CD3AC70A1D2C0726002B4BB0 /* src */,
 				CD3AC7091D2C0726002B4BB0 /* Products */,
+				CD7E884622960F4700D877FE /* Frameworks */,
 			);
 			sourceTree = "<group>";
 		};
@@ -86,6 +92,7 @@
 				CD62FCFC2291951400376440 /* linux_env.cxx */,
 				CD62FD072291988F00376440 /* osx_env.mm */,
 				CD62FD02229195FF00376440 /* files.cxx */,
+				CD7E876F2295FA1F00D877FE /* time.cpp */,
 			);
 			path = src;
 			sourceTree = "<group>";
@@ -101,6 +108,14 @@
 			name = Products;
 			sourceTree = "<group>";
 		};
+		CD7E884622960F4700D877FE /* Frameworks */ = {
+			isa = PBXGroup;
+			children = (
+				CD7E884722960F4800D877FE /* AppKit.framework */,
+			);
+			name = Frameworks;
+			sourceTree = "<group>";
+		};
 /* End PBXGroup section */
 
 /* Begin PBXHeadersBuildPhase section */
@@ -227,6 +242,7 @@
 			files = (
 				CD62FD082291988F00376440 /* osx_env.mm in Sources */,
 				CD62FD04229195FF00376440 /* files.cxx in Sources */,
+				CD7E87772295FA1F00D877FE /* time.cpp in Sources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -283,7 +299,7 @@
 				MTL_ENABLE_DEBUG_INFO = YES;
 				ONLY_ACTIVE_ARCH = YES;
 				SDKROOT = macosx;
-				USER_HEADER_SEARCH_PATHS = "$(PROJECT_DIR)/include $(PROJECT_DIR)/../include/";
+				USER_HEADER_SEARCH_PATHS = "$(PROJECT_DIR)/include $(PROJECT_DIR)/../include/ $(PROJECT_DIR)/../math/ $(PROJECT_DIR)/../math/include/ $(PROJECT_DIR)/../include/expect/include/";
 			};
 			name = Debug;
 		};
@@ -330,7 +346,7 @@
 				MACOSX_DEPLOYMENT_TARGET = 10.10;
 				MTL_ENABLE_DEBUG_INFO = NO;
 				SDKROOT = macosx;
-				USER_HEADER_SEARCH_PATHS = "$(PROJECT_DIR)/include $(PROJECT_DIR)/../include/";
+				USER_HEADER_SEARCH_PATHS = "$(PROJECT_DIR)/include $(PROJECT_DIR)/../include/ $(PROJECT_DIR)/../math/ $(PROJECT_DIR)/../math/include/ $(PROJECT_DIR)/../include/expect/include/";
 			};
 			name = Release;
 		};

+ 4 - 0
util/include/game/util/env.hpp

@@ -10,6 +10,10 @@
 
 #include <string>
 
+#include "game/math/math_fwd.hpp"
+
 namespace env {
   std::string resource_file(std::string const & relative_path);
+  void resize_screen(math::vec2i const & size);
+  math::vec2i screen_size();
 }

engine/include/game/engine/time.hpp → util/include/game/util/time.hpp


+ 30 - 0
util/src/osx_env.mm

@@ -10,6 +10,8 @@
 
 #import <Cocoa/Cocoa.h>
 
+#include "vector/vector.hpp"
+
 namespace {
   NSUInteger encoding = NSUTF8StringEncoding;
   NSString* translate(std::string const & str) {
@@ -32,4 +34,32 @@ namespace env {
     char const* abs_path = [url cStringUsingEncoding:encoding];
     return abs_path ? std::string(abs_path) : std::string();
   }
+  
+  namespace detail {
+    math::vec2i init_screen() {
+      NSRect frame = [[NSView focusView] frame];
+      return make_vector(static_cast<int>(frame.size.width),
+                         static_cast<int>(frame.size.height));
+    }
+    
+    math::vec2i screenSize = init_screen();
+    
+    void resize_screen(math::vec2i const& size) {
+      screenSize = size;
+    }
+  }
+  
+  math::vec2i screen_resolution() {
+    return detail::screenSize * 2; // TODO: aquire this programmatically
+  }
+  
+  math::vec2i screen_size() {
+    return detail::screenSize;
+  }
+  
+  void resize_screen(math::vec2i const& size) {
+    NSSize sz = NSMakeSize(size[0], size[1]);
+    [[NSView focusView] setFrameSize:sz];
+    detail::resize_screen(size);
+  }
 }

+ 1 - 1
engine/src/time.cpp

@@ -5,7 +5,7 @@
 //  Created by Sam Jaffe on 9/3/16.
 //
 
-#include "game/engine/time.hpp"
+#include "game/util/time.hpp"
 
 namespace engine {
   using std::chrono::duration_cast;