瀏覽代碼

Merge branch 'graphics_mgr'

* graphics_mgr:
  Moving error helping code to its own file. Rename opengl_*.cxx files to openGL/*.cxx
  Fix some bugs: - Linker error due to implicit destructor - Out-of-order ctor references caused us to use id:0 for shaders
  Perform housekeeping on header includes/forward declaration lists.
  Have engine start using manager to provide graphics objects, thus isolating the API.
  Complete refactor to a centralized caching solution for graphics objects.
Sam Jaffe 6 年之前
父節點
當前提交
e91dd2ed65
共有 35 個文件被更改,包括 523 次插入424 次删除
  1. 2 2
      engine/include/game/engine/engine_fwd.hpp
  2. 3 2
      engine/include/game/engine/entity.hpp
  3. 3 5
      engine/include/game/engine/fps_counter.hpp
  4. 2 7
      engine/include/game/engine/game_dispatch.hpp
  5. 1 6
      engine/include/game/engine/scene.hpp
  6. 3 7
      engine/include/game/engine/serial.hpp
  7. 2 2
      engine/src/entity.cxx
  8. 5 5
      engine/src/fps_counter.cxx
  9. 4 2
      engine/src/game_dispatch.cpp
  10. 21 16
      engine/src/serial.cxx
  11. 31 15
      graphics/graphics.xcodeproj/project.pbxproj
  12. 28 0
      graphics/include/game/graphics/graphics_fwd.h
  13. 41 0
      graphics/include/game/graphics/manager.hpp
  14. 9 16
      graphics/include/game/graphics/material.hpp
  15. 2 5
      graphics/include/game/graphics/object.hpp
  16. 13 11
      graphics/include/game/graphics/renderer.hpp
  17. 4 12
      graphics/include/game/graphics/shader.hpp
  18. 5 12
      graphics/include/game/graphics/shader_program.hpp
  19. 8 7
      graphics/include/game/graphics/texture.hpp
  20. 7 9
      graphics/src/helper.hpp
  21. 132 0
      graphics/src/manager.cxx
  22. 7 43
      graphics/src/material.cpp
  23. 107 0
      graphics/src/openGL/error_formatter.cxx
  24. 26 114
      graphics/src/opengl_helper.cxx
  25. 14 7
      graphics/src/opengl_renderer.cxx
  26. 6 2
      graphics/src/renderer.cxx
  27. 3 5
      graphics/src/renderer_impl.hpp
  28. 2 20
      graphics/src/shader.cpp
  29. 3 27
      graphics/src/shader_program.cpp
  30. 12 20
      graphics/src/texture.cpp
  31. 3 4
      math/math.xcodeproj/project.pbxproj
  32. 3 4
      util/gameutils.xcodeproj/project.pbxproj
  33. 0 33
      util/include/game/util/flyweight.hpp
  34. 4 4
      util/include/game/util/hash.hpp
  35. 7 0
      util/include/game/util/identity.hpp

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

@@ -19,6 +19,8 @@ namespace engine {
 
   using key_binding = std::unordered_map<raw_key_t, key_enum_t>;
 
+  class entity;
+  class fps_counter;
   class game_dispatch;
   class scene;
 
@@ -26,6 +28,4 @@ namespace engine {
     struct key_event;
     struct mouse_event;
   }
-
-  struct tick;
 }

+ 3 - 2
engine/include/game/engine/entity.hpp

@@ -11,14 +11,15 @@
 #include <json/forwards.h>
 
 #include "game/engine/engine_fwd.hpp"
+#include "game/graphics/graphics_fwd.h"
 #include "game/graphics/object.hpp"
 #include "game/util/identity.hpp"
 
 namespace engine {
-  class scene;
   class entity : identity<entity> {
   public:
-    entity(Json::Value const & json);
+    // TODO: Extract this out?
+    entity(Json::Value const & json, graphics::manager const & mgr);
     void update(float delta);
     void collide(entity const &);
 

+ 3 - 5
engine/include/game/engine/fps_counter.hpp

@@ -10,16 +10,14 @@
 
 #include <vector>
 
+#include "game/graphics/graphics_fwd.h"
 #include "game/util/time.hpp"
 
-namespace graphics {
-  class object;
-}
-
 namespace engine {
   class fps_counter {
   public:
-    fps_counter(env::clock::duration const & fpscap, std::size_t precision = 3);
+    fps_counter(env::clock::duration const & fpscap,
+                graphics::manager const & mgr, std::size_t precision = 3);
     ~fps_counter();
 
     void set_frame_step(env::clock::duration const & since);

+ 2 - 7
engine/include/game/engine/game_dispatch.hpp

@@ -16,18 +16,14 @@
 
 #include "engine_fwd.hpp"
 #include "events.hpp"
+#include "game/graphics/graphics_fwd.h"
 #include "game/util/time.hpp"
 
-namespace graphics {
-  class renderer;
-}
-
 namespace engine {
-  class fps_counter;
-
   class game_dispatch {
   public:
     game_dispatch(std::shared_ptr<graphics::renderer> renderer);
+    ~game_dispatch();
 
     void process_key_event(raw_key_t key, bool press);
     void process_mouse_event(math::vec2 click, bool press);
@@ -41,7 +37,6 @@ namespace engine {
 
   private:
     env::clock::tick next_frame();
-    tick get_tick();
 
   private:
     std::shared_ptr<graphics::renderer> renderer;

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

@@ -11,19 +11,14 @@
 #include <unordered_map>
 #include <vector>
 
+#include "game/graphics/graphics_fwd.h"
 #include "game/math/math_fwd.hpp"
 #include "game/util/identity.hpp"
 #include "vector/vector.hpp"
 
 #include "engine_fwd.hpp"
 
-namespace graphics {
-  class renderer;
-}
-
 namespace engine {
-  class entity;
-
   class scene : public identity<scene, std::string> {
   public:
     using identity<scene, std::string>::identity;

+ 3 - 7
engine/include/game/engine/serial.hpp

@@ -10,16 +10,12 @@
 
 #include <json/forwards.h>
 
+#include "game/graphics/graphics_fwd.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);
+  graphics::object to_object(Json::Value const & json,
+                             graphics::manager const & mgr);
 }

+ 2 - 2
engine/src/entity.cxx

@@ -21,10 +21,10 @@ static unsigned int next_id() {
   return ++id;
 }
 
-entity::entity(Json::Value const & json)
+entity::entity(Json::Value const & json, graphics::manager const & mgr)
     : 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, mgr)), 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;

+ 5 - 5
engine/src/fps_counter.cxx

@@ -9,18 +9,18 @@
 #include "game/engine/fps_counter.hpp"
 
 #include <json/value.h>
+#include <unordered_map>
 
 #include "game/engine/serial.hpp"
 #include "game/graphics/object.hpp"
-#include <iostream>
 
 using namespace engine;
 
-static graphics::object create() {
+static graphics::object create(graphics::manager const & mgr) {
   Json::Value value;
   value["material"]["texture"]["file"] = "data/images/font.bmp";
   value["material"]["texture"]["uniform"] = "u_normalMap";
-  return engine::to_object(value);
+  return engine::to_object(value, mgr);
 }
 
 static std::size_t digits(env::clock::duration const & fpscap) {
@@ -47,9 +47,9 @@ math::vec2 index(char value) {
 math::vec2 number_index(int num) { return index(char(num + '0')); }
 
 fps_counter::fps_counter(env::clock::duration const & fpscap,
-                         std::size_t precision)
+                         graphics::manager const & mgr, std::size_t precision)
     : digits_(precision), magnitude_(std::pow(10, digits_)),
-      glyphs_(digits(fpscap) + digits_ + 1, create()) {
+      glyphs_(digits(fpscap) + digits_ + 1, create(mgr)) {
   auto pixels = make_vector(0.125f, 0.125f);
   for (std::size_t i = 0; i < glyphs_.size(); ++i) {
     glyphs_[i].location.origin =

+ 4 - 2
engine/src/game_dispatch.cpp

@@ -27,8 +27,10 @@ namespace engine {
   game_dispatch::game_dispatch(std::shared_ptr<graphics::renderer> renderer)
       : renderer(renderer), screen_size(env::screen_size()),
         minimum_frame_duration(env::fps::v60),
-        counter(new fps_counter(minimum_frame_duration)), scenes(),
-        current_timestamp(env::clock::now()), curr_scene() {}
+        counter(new fps_counter(minimum_frame_duration, *renderer->manager())),
+        scenes(), current_timestamp(env::clock::now()), curr_scene() {}
+
+  game_dispatch::~game_dispatch() {}
 
   game_dispatch::current_scene_info::current_scene_info() {}
 

+ 21 - 16
engine/src/serial.cxx

@@ -10,9 +10,10 @@
 
 #include <json/json.h>
 
+#include "game/graphics/manager.hpp"
 #include "game/graphics/material.hpp"
 #include "game/graphics/object.hpp"
-#include "game/util/flyweight.hpp"
+#include "game/util/identity.hpp"
 #include "vector/vector.hpp"
 
 namespace engine {
@@ -24,27 +25,31 @@ namespace engine {
     return json.isNull() ? backup : to_vec2(json);
   }
 
-  flyweight<graphics::shader_program> to_program(Json::Value const & json) {
-    using graphics::shader_program;
+  identity<graphics::shader_program> to_program(Json::Value const & json,
+                                                graphics::manager const & mgr) {
     if (json.empty()) {
-      return shader_program::create("data/shaders/BlankShader.fragment.glsl",
-                                    "data/shaders/BlankShader.vertex.glsl");
+      return mgr.get("data/shaders/BlankShader.fragment.glsl",
+                     "data/shaders/BlankShader.vertex.glsl");
     }
-    return shader_program::create(json["fragmentShader"].asString(),
-                                  json["vertexShader"].asString());
+    return mgr.get(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());
+  identity<graphics::material> to_material(Json::Value const & json,
+                                           graphics::manager const & mgr) {
+    return mgr.get(to_program(json["shaderProgram"], mgr),
+                   json["texture"]["file"].asString(),
+                   json["texture"]["uniform"].asString());
   }
 
-  graphics::object to_object(Json::Value const & json) {
-    flyweight<graphics::material> mat = to_material(json["material"]);
+  graphics::object to_object(Json::Value const & json,
+                             graphics::manager const & mgr) {
+    identity<graphics::material> mat = to_material(json["material"], mgr);
     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}};
+    auto origin = to_vec2(json["position"]);
+    return mgr.create_object(mat, origin, frame_size);
+    //    math::dim2::rectangle pos = {to_vec2(json["position"]),
+    //                                 mat.actual().size() * frame_size};
+    //    return {pos, pos, mat, {{{0, 0}}, frame_size}};
   }
 }

+ 31 - 15
graphics/graphics.xcodeproj/project.pbxproj

@@ -7,19 +7,21 @@
 	objects = {
 
 /* Begin PBXBuildFile section */
-		CD1C82BD22988EC700825C4E /* matrix.cxx in Sources */ = {isa = PBXBuildFile; fileRef = CD1C82BC22988EC700825C4E /* matrix.cxx */; settings = {COMPILER_FLAGS = "-fvisibility=default"; }; };
-		CD3AC6F21D2C03B7002B4BB0 /* material.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CD3AC6F01D2C03B7002B4BB0 /* material.cpp */; settings = {COMPILER_FLAGS = "-fvisibility=default"; }; };
-		CD3AC6F81D2C0518002B4BB0 /* texture.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CD3AC6F61D2C0518002B4BB0 /* texture.cpp */; settings = {COMPILER_FLAGS = "-fvisibility=default"; }; };
-		CD3AC6FD1D2C06B5002B4BB0 /* shader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CD3AC6FB1D2C06B5002B4BB0 /* shader.cpp */; settings = {COMPILER_FLAGS = "-fvisibility=default"; }; };
-		CD3AC7191D2C0950002B4BB0 /* shader_program.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CD3AC7171D2C0950002B4BB0 /* shader_program.cpp */; settings = {COMPILER_FLAGS = "-fvisibility=default"; }; };
+		CD1C82BD22988EC700825C4E /* matrix.cxx in Sources */ = {isa = PBXBuildFile; fileRef = CD1C82BC22988EC700825C4E /* matrix.cxx */; };
+		CD1C83E922998E2600825C4E /* manager.cxx in Sources */ = {isa = PBXBuildFile; fileRef = CD1C83E722998E2600825C4E /* manager.cxx */; settings = {COMPILER_FLAGS = "-fvisibility=default"; }; };
+		CD1C840F2299B81500825C4E /* error_formatter.cxx in Sources */ = {isa = PBXBuildFile; fileRef = CD1C840D2299B81500825C4E /* error_formatter.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 */; };
+		CD3AC7191D2C0950002B4BB0 /* shader_program.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CD3AC7171D2C0950002B4BB0 /* shader_program.cpp */; };
 		CD3AC7261D2C0C63002B4BB0 /* object.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CD3AC7241D2C0C63002B4BB0 /* object.cpp */; settings = {COMPILER_FLAGS = "-fvisibility=default"; }; };
 		CD62FCF72290DC9000376440 /* helper.hpp in Headers */ = {isa = PBXBuildFile; fileRef = CD62FCF52290DC9000376440 /* helper.hpp */; };
-		CD62FCF82290DC9000376440 /* opengl_helper.cxx in Sources */ = {isa = PBXBuildFile; fileRef = CD62FCF62290DC9000376440 /* opengl_helper.cxx */; };
+		CD62FCF82290DC9000376440 /* helper.cxx in Sources */ = {isa = PBXBuildFile; fileRef = CD62FCF62290DC9000376440 /* helper.cxx */; };
 		CD62FCFA2290E2E500376440 /* OpenGL.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CD62FCF92290E2E500376440 /* OpenGL.framework */; };
 		CD62FD062291970F00376440 /* libgameutils.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = CD62FD052291970F00376440 /* libgameutils.dylib */; };
 		CD62FD1E2292412900376440 /* renderer.cxx in Sources */ = {isa = PBXBuildFile; fileRef = CD62FD1C2292412900376440 /* renderer.cxx */; settings = {COMPILER_FLAGS = "-fvisibility=default"; }; };
 		CD62FD222292C76B00376440 /* renderer_impl.hpp in Headers */ = {isa = PBXBuildFile; fileRef = CD62FD202292C76B00376440 /* renderer_impl.hpp */; };
-		CD62FD232292C76B00376440 /* opengl_renderer.cxx in Sources */ = {isa = PBXBuildFile; fileRef = CD62FD212292C76B00376440 /* opengl_renderer.cxx */; };
+		CD62FD232292C76B00376440 /* renderer_impl.cxx in Sources */ = {isa = PBXBuildFile; fileRef = CD62FD212292C76B00376440 /* renderer_impl.cxx */; };
 		CDA34D9A22517A3D008036A7 /* libmath.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = CDA34D9922517A3D008036A7 /* libmath.dylib */; };
 /* End PBXBuildFile section */
 
@@ -57,6 +59,8 @@
 /* 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>"; };
+		CD1C83E722998E2600825C4E /* manager.cxx */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = manager.cxx; sourceTree = "<group>"; };
+		CD1C840D2299B81500825C4E /* error_formatter.cxx */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = error_formatter.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>"; };
@@ -65,12 +69,12 @@
 		CD3AC7241D2C0C63002B4BB0 /* object.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = object.cpp; sourceTree = "<group>"; };
 		CD62FCD722904AD100376440 /* GoogleMock.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = GoogleMock.xcodeproj; path = "../../gmock-xcode-master/GoogleMock.xcodeproj"; sourceTree = "<group>"; };
 		CD62FCF52290DC9000376440 /* helper.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = helper.hpp; sourceTree = "<group>"; };
-		CD62FCF62290DC9000376440 /* opengl_helper.cxx */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = opengl_helper.cxx; sourceTree = "<group>"; };
+		CD62FCF62290DC9000376440 /* helper.cxx */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = helper.cxx; sourceTree = "<group>"; };
 		CD62FCF92290E2E500376440 /* OpenGL.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = OpenGL.framework; path = System/Library/Frameworks/OpenGL.framework; sourceTree = SDKROOT; };
 		CD62FD052291970F00376440 /* libgameutils.dylib */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.dylib"; path = libgameutils.dylib; sourceTree = BUILT_PRODUCTS_DIR; };
 		CD62FD1C2292412900376440 /* renderer.cxx */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = renderer.cxx; sourceTree = "<group>"; };
 		CD62FD202292C76B00376440 /* renderer_impl.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = renderer_impl.hpp; sourceTree = "<group>"; };
-		CD62FD212292C76B00376440 /* opengl_renderer.cxx */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = opengl_renderer.cxx; sourceTree = "<group>"; };
+		CD62FD212292C76B00376440 /* renderer_impl.cxx */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = renderer_impl.cxx; sourceTree = "<group>"; };
 		CDA34D86225171AA008036A7 /* game */ = {isa = PBXFileReference; lastKnownFileType = folder; name = game; path = include/game; sourceTree = "<group>"; };
 		CDA34D9922517A3D008036A7 /* libmath.dylib */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.dylib"; path = libmath.dylib; sourceTree = BUILT_PRODUCTS_DIR; };
 /* End PBXFileReference section */
@@ -89,6 +93,16 @@
 /* End PBXFrameworksBuildPhase section */
 
 /* Begin PBXGroup section */
+		CD1C84052299B72C00825C4E /* openGL */ = {
+			isa = PBXGroup;
+			children = (
+				CD62FD212292C76B00376440 /* renderer_impl.cxx */,
+				CD62FCF62290DC9000376440 /* helper.cxx */,
+				CD1C840D2299B81500825C4E /* error_formatter.cxx */,
+			);
+			path = openGL;
+			sourceTree = "<group>";
+		};
 		CD3AC6D91D2C0364002B4BB0 = {
 			isa = PBXGroup;
 			children = (
@@ -111,10 +125,9 @@
 		CD3AC6E41D2C0364002B4BB0 /* src */ = {
 			isa = PBXGroup;
 			children = (
+				CD1C84052299B72C00825C4E /* openGL */,
 				CD62FD1A22923B8E00376440 /* renderer */,
 				CD62FD1822923B8100376440 /* model */,
-				CD62FCF52290DC9000376440 /* helper.hpp */,
-				CD62FCF62290DC9000376440 /* opengl_helper.cxx */,
 			);
 			path = src;
 			sourceTree = "<group>";
@@ -133,6 +146,8 @@
 		CD62FD1822923B8100376440 /* model */ = {
 			isa = PBXGroup;
 			children = (
+				CD62FCF52290DC9000376440 /* helper.hpp */,
+				CD1C83E722998E2600825C4E /* manager.cxx */,
 				CD3AC7241D2C0C63002B4BB0 /* object.cpp */,
 				CD3AC6F01D2C03B7002B4BB0 /* material.cpp */,
 				CD3AC6FB1D2C06B5002B4BB0 /* shader.cpp */,
@@ -145,11 +160,10 @@
 		CD62FD1A22923B8E00376440 /* renderer */ = {
 			isa = PBXGroup;
 			children = (
-				CD62FD1C2292412900376440 /* renderer.cxx */,
 				CD62FD202292C76B00376440 /* renderer_impl.hpp */,
+				CD62FD1C2292412900376440 /* renderer.cxx */,
 				CD1C82B722988E4E00825C4E /* matrix.hpp */,
 				CD1C82BC22988EC700825C4E /* matrix.cxx */,
-				CD62FD212292C76B00376440 /* opengl_renderer.cxx */,
 			);
 			name = renderer;
 			sourceTree = "<group>";
@@ -294,11 +308,13 @@
 				CD3AC7191D2C0950002B4BB0 /* shader_program.cpp in Sources */,
 				CD1C82BD22988EC700825C4E /* matrix.cxx in Sources */,
 				CD3AC6F21D2C03B7002B4BB0 /* material.cpp in Sources */,
+				CD1C840F2299B81500825C4E /* error_formatter.cxx in Sources */,
 				CD62FD1E2292412900376440 /* renderer.cxx in Sources */,
 				CD3AC6F81D2C0518002B4BB0 /* texture.cpp in Sources */,
 				CD3AC7261D2C0C63002B4BB0 /* object.cpp in Sources */,
-				CD62FCF82290DC9000376440 /* opengl_helper.cxx in Sources */,
-				CD62FD232292C76B00376440 /* opengl_renderer.cxx in Sources */,
+				CD1C83E922998E2600825C4E /* manager.cxx in Sources */,
+				CD62FCF82290DC9000376440 /* helper.cxx in Sources */,
+				CD62FD232292C76B00376440 /* renderer_impl.cxx in Sources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};

+ 28 - 0
graphics/include/game/graphics/graphics_fwd.h

@@ -0,0 +1,28 @@
+//
+//  graphics_fwd.h
+//  graphics
+//
+//  Created by Sam Jaffe on 5/25/19.
+//  Copyright © 2019 Sam Jaffe. All rights reserved.
+//
+
+#pragma once
+
+#include "game/util/identity.hpp"
+
+namespace graphics {
+  // API classes
+  class manager;
+  struct object;
+  class renderer;
+  struct vertex;
+
+  // Implementation classes
+  struct material;
+  struct shader;
+  struct shader_program;
+  class texture;
+  namespace shaders {
+    enum class type : unsigned int;
+  }
+}

+ 41 - 0
graphics/include/game/graphics/manager.hpp

@@ -0,0 +1,41 @@
+//
+//  manager.hpp
+//  graphics
+//
+//  Created by Sam Jaffe on 5/25/19.
+//  Copyright © 2019 Sam Jaffe. All rights reserved.
+//
+
+#pragma once
+
+#include <memory>
+#include <unordered_map>
+
+#include "game/graphics/graphics_fwd.h"
+#include "game/math/math_fwd.hpp"
+
+namespace graphics {
+  class manager {
+  public:
+    manager();
+    ~manager();
+
+    identity<material> get(identity<shader_program> program,
+                           std::string const & texture,
+                           std::string const & uniform) const;
+    identity<shader> get(shaders::type type, std::string const & path) const;
+    identity<shader_program> get(std::string const & fragment,
+                                 std::string const & vertex) const;
+    identity<texture> get(std::string const & path) const;
+
+    object create_object(identity<material> fromMaterial, math::vec2 atPosition,
+                         math::vec2 frameWidth) const;
+    material const & get(identity<material> identity) const;
+
+  private:
+    identity<texture> texture_or_uniform(std::string const & path,
+                                         std::string const & uniform) const;
+
+    std::unique_ptr<struct manager_impl> pimpl_;
+  };
+}

+ 9 - 16
graphics/include/game/graphics/material.hpp

@@ -9,31 +9,24 @@
 
 #include <vector>
 
+#include "game/graphics/graphics_fwd.h"
 #include "game/math/math_fwd.hpp"
-#include "game/util/identity.hpp"
-#include "shader_program.hpp"
+#include "vector/vector.hpp"
 
 namespace graphics {
-  class texture;
   struct uniform {
-    flyweight<texture> texture;
+    identity<texture> texture;
     int uniform_id; // TODO (sjaffe): use an enum and hide remapping?
   };
 
-  class material : public identity<material> {
-  public:
-    shader_program const shaders;
-    std::vector<uniform> uniforms;
-
-  public:
-    static flyweight<material> create(shader_program const & sp,
-                                      std::string const & texture,
-                                      std::string const & uniform);
+  struct material : public identity<material> {
+    material(identity<shader_program> const & sp, math::vec2i size,
+             std::vector<uniform> const & uniforms);
 
-    math::vec2i size() const;
     void activate() const;
 
-  private:
-    material(unsigned int, shader_program const &);
+    identity<shader_program> program;
+    math::vec2i size;
+    std::vector<uniform> uniforms;
   };
 }

+ 2 - 5
graphics/include/game/graphics/object.hpp

@@ -9,19 +9,16 @@
 
 #include <vector>
 
+#include "game/graphics/graphics_fwd.h"
 #include "game/math/math_fwd.hpp"
 #include "game/math/shape.hpp"
-#include "game/util/flyweight.hpp"
 #include "vector/vector.hpp"
 
 namespace graphics {
-  class material;
-  struct vertex;
-
   struct object {
     math::dim2::rectangle location;
     math::dim2::quad points;
-    flyweight<material> material;
+    identity<material> material;
     math::dim2::rectangle frame;
   }; // size:56, align:4
 

+ 13 - 11
graphics/include/game/graphics/renderer.hpp

@@ -8,21 +8,19 @@
 
 #pragma once
 
+#include <unordered_map>
+
+#include "game/graphics/graphics_fwd.h"
 #include "game/math/math_fwd.hpp"
-#include "game/util/flyweight.hpp"
 #include "game/util/hash.hpp"
 #include "vector/vector.hpp"
 
 namespace graphics {
-  class material;
-  struct object;
-  struct renderer_impl;
-  struct vertex;
-
   struct renderer {
     virtual ~renderer() {}
+    virtual std::shared_ptr<manager const> manager() const = 0;
     virtual void draw(object const & obj) = 0;
-    virtual void draw(flyweight<material>, math::matr4 const &,
+    virtual void draw(identity<material>, math::matr4 const &,
                       std::vector<vertex> const &) = 0;
     virtual void clear() = 0;
     virtual void flush() = 0;
@@ -33,22 +31,26 @@ namespace graphics {
   class direct_renderer : public renderer {
   public:
     direct_renderer(driver d);
+    std::shared_ptr<class manager const> manager() const override;
     void draw(object const & obj) override;
-    void draw(flyweight<material>, math::matr4 const &,
+    void draw(identity<material>, math::matr4 const &,
               std::vector<vertex> const &) override;
     void clear() override;
     void flush() override;
 
   private:
-    renderer_impl * pimpl;
+    struct renderer_impl * pimpl;
   };
 
   class batch_renderer : public renderer {
   public:
     batch_renderer(renderer * impl, std::size_t batch_size = 0);
     ~batch_renderer();
+    std::shared_ptr<class manager const> manager() const override {
+      return impl_->manager();
+    }
     void draw(object const & obj) override;
-    void draw(flyweight<material>, math::matr4 const &,
+    void draw(identity<material>, math::matr4 const &,
               std::vector<vertex> const &) override;
     void clear() override { impl_->clear(); }
     void flush() override;
@@ -59,7 +61,7 @@ namespace graphics {
 
   private:
     renderer * impl_;
-    std::unordered_map<flyweight<material>, std::vector<vertex>> batches_;
+    std::unordered_map<identity<material>, std::vector<vertex>> batches_;
     std::size_t batch_size_;
     std::size_t elements_;
   };

+ 4 - 12
graphics/include/game/graphics/shader.hpp

@@ -9,21 +9,13 @@
 
 #include <string>
 
-#include "game/util/flyweight.hpp"
+#include "game/graphics/graphics_fwd.h"
 
 namespace graphics {
-  namespace shaders {
-    enum class type : unsigned int;
-  }
-  class shader : public identity<shader> {
-  public:
+  struct shader : public identity<shader> {
+    shader(shaders::type, std::string const &);
+
     shaders::type type;
     std::string path;
-
-  public:
-    static flyweight<shader> create(shaders::type tp, std::string const & path);
-
-  private:
-    shader(unsigned int, shaders::type, std::string const &);
   };
 }

+ 5 - 12
graphics/include/game/graphics/shader_program.hpp

@@ -9,22 +9,15 @@
 
 #include <string>
 
-#include "game/util/flyweight.hpp"
-
-#include "shader.hpp"
+#include "game/graphics/graphics_fwd.h"
 
 namespace graphics {
-  class shader_program : public identity<shader_program> {
-  public:
-    flyweight<shader> fragment_shader;
-    flyweight<shader> vertex_shader;
+  struct shader_program : public identity<shader_program> {
+    shader_program(identity<shader>, identity<shader>);
 
-  public:
-    static flyweight<shader_program> create(std::string const & frag,
-                                            std::string const & vert);
     void activate() const;
 
-  private:
-    shader_program(unsigned int, flyweight<shader>, flyweight<shader>);
+    identity<shader> fragment_shader;
+    identity<shader> vertex_shader;
   };
 }

+ 8 - 7
graphics/include/game/graphics/texture.hpp

@@ -8,22 +8,23 @@
 #pragma once
 
 #include "game/math/math_fwd.hpp"
-#include "game/util/flyweight.hpp"
+#include "game/util/identity.hpp"
 #include "vector/vector.hpp"
 
 namespace graphics {
   class texture : public identity<texture> {
   public:
+    texture(std::string const & str);
+
     static texture const WHITE();
     static texture const DARK_YELLOW();
     static texture const LIGHT_BLUE();
-    math::vec2i const size;
-
-  public:
-    static flyweight<texture> create(std::string const & imagefile);
 
   private:
-    static texture create(char const *, math::vec2i);
-    texture(unsigned int, math::vec2i = math::vec2i{{0, 0}});
+    texture(std::pair<unsigned int, math::vec2i>);
+    texture(char const *, math::vec2i);
+
+  public:
+    math::vec2i const size;
   };
 }

+ 7 - 9
graphics/src/helper.hpp

@@ -11,13 +11,10 @@
 #include <string>
 #include <utility>
 
+#include "game/graphics/graphics_fwd.h"
 #include "game/math/math_fwd.hpp"
 
-template <typename> class flyweight;
-
 namespace graphics {
-  class shader;
-  class shader_program;
   struct uniform;
   namespace textures {
     enum class format { RGB, RGBA };
@@ -26,13 +23,14 @@ namespace graphics {
   namespace shaders {
     enum class type : unsigned int { FRAGMENT, VERTEX };
     unsigned int init(type, std::string const &);
-    unsigned int init(flyweight<shader> const & fragmentShader,
-                      flyweight<shader> const & vertexShader);
-    void activate(unsigned int id);
-    int uniform_location(unsigned int id, std::string const & uniform);
+    unsigned int init(identity<shader> const & fragmentShader,
+                      identity<shader> const & vertexShader);
+    void activate(identity<shader_program> id);
+    int uniform_location(identity<shader_program> id,
+                         std::string const & uniform);
   }
   namespace materials {
-    void activate(flyweight<shader_program> program,
+    void activate(identity<shader_program> program,
                   std::vector<uniform> const & uniforms);
   }
 }

+ 132 - 0
graphics/src/manager.cxx

@@ -0,0 +1,132 @@
+//
+//  manager.cxx
+//  graphics
+//
+//  Created by Sam Jaffe on 5/25/19.
+//  Copyright © 2019 Sam Jaffe. All rights reserved.
+//
+
+#include "game/graphics/manager.hpp"
+
+#include "game/graphics/material.hpp"
+#include "game/graphics/object.hpp"
+#include "game/graphics/shader.hpp"
+#include "game/graphics/shader_program.hpp"
+#include "game/graphics/texture.hpp"
+#include "game/math/shape.hpp"
+#include "game/util/hash.hpp"
+#include "helper.hpp"
+
+using namespace graphics;
+
+template <typename> struct key;
+template <typename T> using key_t = typename key<T>::type;
+
+template <> struct key<texture> { using type = std::string; };
+template <> struct key<shader> {
+  using type = std::pair<shaders::type, std::string>;
+};
+template <> struct key<shader_program> {
+  using type = std::pair<std::string, std::string>;
+};
+template <> struct key<material> {
+  using type = std::tuple<identity<shader_program>, std::string, std::string>;
+};
+
+template <typename T> struct cache {
+  identity<T> emplace(key_t<T> const & key, T && value);
+  std::unordered_map<identity<T>, key_t<T>> keys;
+  std::unordered_map<key_t<T>, T> values;
+};
+
+template <typename T>
+identity<T> cache<T>::emplace(key_t<T> const & key, T && value) {
+  keys.emplace(value, key);
+  return values.emplace(key, std::forward<T>(value)).first->second;
+}
+
+struct graphics::manager_impl {
+  cache<material> materials;
+  cache<shader> shaders;
+  cache<shader_program> programs;
+  cache<texture> textures;
+};
+
+manager::manager() : pimpl_(new manager_impl) {}
+manager::~manager() {}
+
+identity<material> manager::get(identity<shader_program> program,
+                                std::string const & texture,
+                                std::string const & uniform) const {
+  auto key = std::make_tuple(program, texture, uniform);
+  auto & cache = pimpl_->materials;
+  auto found = cache.values.find(key);
+  if (found != cache.values.end()) { return found->second; }
+
+  std::vector<struct uniform> uniforms;
+  uniforms.push_back({texture_or_uniform(texture, uniform),
+                      shaders::uniform_location(program, uniform)});
+  math::vec2i size = make_vector(1, 1);
+  //  if (!uniforms.empty()) { size = ...; }
+  return cache.emplace(key, material(program, size, uniforms));
+}
+
+material const & manager::get(identity<material> identity) const {
+  auto & cache = pimpl_->materials;
+  return cache.values.find(cache.keys.find(identity)->second)->second;
+}
+
+identity<shader> manager::get(shaders::type type,
+                              std::string const & path) const {
+  auto key = std::make_pair(type, path);
+  auto & cache = pimpl_->shaders;
+  auto found = cache.values.find(key);
+  if (found != cache.values.end()) { return found->second; }
+  return cache.emplace(key, shader(type, path));
+}
+
+identity<texture>
+manager::texture_or_uniform(std::string const & path,
+                            std::string const & uniform) const {
+  if (!path.empty()) {
+    try {
+      return get(path);
+    } catch (std::exception const & e) {
+      // TODO: Logging
+    }
+  }
+  if (uniform == "u_normalMap") {
+    return texture::LIGHT_BLUE();
+  } else if (uniform == "u_specularMap") {
+    return texture::DARK_YELLOW();
+  } else if (uniform == "u_diffuseMap") {
+    return texture::WHITE();
+  }
+  throw;
+}
+
+identity<shader_program> manager::get(std::string const & fragment,
+                                      std::string const & vertex) const {
+  auto key = std::make_pair(fragment, vertex);
+  auto & cache = pimpl_->programs;
+  auto found = cache.values.find(key);
+  if (found != cache.values.end()) { return found->second; }
+  auto fragment_shader = get(shaders::type::FRAGMENT, fragment);
+  auto vertex_shader = get(shaders::type::VERTEX, vertex);
+
+  return cache.emplace(key, shader_program(fragment_shader, vertex_shader));
+}
+
+identity<texture> manager::get(std::string const & path) const {
+  auto & cache = pimpl_->textures;
+  auto found = cache.values.find(path);
+  if (found != cache.values.end()) { return found->second; }
+  return cache.emplace(path, texture(path));
+}
+
+object manager::create_object(identity<material> fromMaterial,
+                              math::vec2 atPosition,
+                              math::vec2 frameWidth) const {
+  math::dim2::rectangle bounds{atPosition, frameWidth * get(fromMaterial).size};
+  return {bounds, bounds, fromMaterial, {make_vector(0.f, 0.f), frameWidth}};
+}

+ 7 - 43
graphics/src/material.cpp

@@ -13,52 +13,16 @@
 
 using namespace graphics;
 
-namespace {
-  using key_t = std::tuple<flyweight<shader_program>, std::string, std::string>;
-  std::unordered_map<key_t, flyweight<material>> g_materials;
-}
-
 static math::vec2i ZERO{{0, 0}};
 
-flyweight<texture> get_texture(std::string const & texture,
-                               std::string const & uniform) {
-  if (!texture.empty()) {
-    try {
-      return texture::create(texture);
-    } catch (std::exception const & e) {
-      // TODO: Logging
-    }
-  }
-  if (uniform == "u_normalMap") {
-    return texture::LIGHT_BLUE();
-  } else if (uniform == "u_specularMap") {
-    return texture::DARK_YELLOW();
-  } else if (uniform == "u_diffuseMap") {
-    return texture::WHITE();
-  }
-  throw;
-}
-
-flyweight<material> material::create(shader_program const & sp,
-                                     std::string const & texture,
-                                     std::string const & uniform) {
-  key_t key = std::make_tuple(sp, texture, uniform);
-  auto found = g_materials.find(key);
-  if (found != g_materials.end()) { return found->second; }
-
+static unsigned int next_id() {
   static unsigned int id{0};
-  material mat{++id, sp};
-  mat.uniforms.push_back({get_texture(texture, uniform),
-                          shaders::uniform_location(sp.id, uniform)});
-  flyweight<material> fly{id, mat};
-  return g_materials.emplace(key, fly).first->second;
+  return ++id;
 }
 
-material::material(unsigned int id, shader_program const & sp)
-    : identity<material>(id), shaders(sp) {}
-
-math::vec2i material::size() const {
-  return uniforms.empty() ? ZERO : uniforms.front().texture.actual().size;
-}
+material::material(identity<shader_program> const & sp, math::vec2i size,
+                   std::vector<uniform> const & uniforms)
+    : identity<material>(next_id()), program(sp), size(size),
+      uniforms(uniforms) {}
 
-void material::activate() const { materials::activate(shaders, uniforms); }
+void material::activate() const { materials::activate(program, uniforms); }

+ 107 - 0
graphics/src/openGL/error_formatter.cxx

@@ -0,0 +1,107 @@
+//
+//  error_formatter.cxx
+//  graphics
+//
+//  Created by Sam Jaffe on 5/25/19.
+//  Copyright © 2019 Sam Jaffe. All rights reserved.
+//
+
+#include <iostream>
+#include <sstream>
+#include <string>
+#ifdef __APPLE__
+#include <OpenGL/gl3.h>
+#endif
+
+struct error_formatter {
+  typedef void (*GLGetIntFor)(GLuint, GLenum, GLint *);
+  typedef void (*GLGetLog)(GLuint, GLsizei, GLsizei *, GLchar *);
+
+  void operator()() const;
+  static void shader_error(GLuint id, std::string const & path);
+  static void shader_program_error(GLuint id, std::string const & frag_path,
+                                   std::string const & vert_path);
+
+  unsigned int obj;
+  std::string fileName, path, eNoticeMessage, eFileMessage, eWindowTitle;
+  GLGetIntFor getIntv;
+  GLGetLog getInfoLog;
+};
+
+void error_formatter::operator()() const {
+  std::stringstream errorString;
+  std::string errorLineNumber;
+
+  int infologLength = 0;
+  int maxLength = 0;
+
+  getIntv(obj, GL_INFO_LOG_LENGTH, &maxLength);
+  errorString << eNoticeMessage << "\n";
+
+  std::unique_ptr<char[]> infoLog(new char[maxLength + 1]);
+  getInfoLog(obj, maxLength, &infologLength, infoLog.get());
+
+  std::string errorLog(infoLog.get());
+  infoLog.reset();
+  if (infologLength <= 0) return;
+
+  std::size_t openParen = errorLog.find('(');
+  std::size_t closeParen = errorLog.find(')');
+  errorLineNumber = errorLog.substr(openParen + 1, closeParen - openParen - 1);
+
+  std::string errorStart = errorLog.find("error") != std::string::npos
+                               ? errorLog.substr(errorLog.find("error"))
+                               : errorLog;
+
+  errorString << errorLog << "\n";
+
+  errorString << "OpenGL Version: " << glGetString(GL_VERSION) << "\n";
+  errorString << "GLSL Shading Version: "
+              << glGetString(GL_SHADING_LANGUAGE_VERSION) << "\n";
+
+  errorString << eFileMessage << "\n";
+  errorString << "RAW ERROR LOG: " << errorLog;
+
+  std::stringstream consoleOutput;
+
+  consoleOutput << "1>" << path << '(' << errorLineNumber << ')' << ": "
+                << errorStart
+                << "in OpenGL version: " << glGetString(GL_VERSION) << " and "
+                << "GLSL Shading Version: "
+                << glGetString(GL_SHADING_LANGUAGE_VERSION) << std::endl;
+
+#ifdef _WIN32
+  OutputDebugStringA(consoleOutput.str().c_str());
+  MessageBoxA(nullptr, errorString.str().c_str(), eWindowTitle.c_str(), MB_OK);
+#else
+  std::cerr << errorString.str() << std::endl;
+#endif
+}
+
+namespace graphics {
+  void print_shader_error(GLuint id, std::string const & path) {
+    std::string fileName = path.substr(path.find_last_of("/") + 1);
+    error_formatter{id,
+                    fileName,
+                    path,
+                    "GLSL shader compile error!",
+                    "File location: " + path,
+                    "GLSL Compile Error in " + fileName,
+                    glGetShaderiv,
+                    glGetShaderInfoLog}();
+  }
+
+  void print_shader_program_error(GLuint id, std::string const & frag_path,
+                                  std::string const & vert_path) {
+    std::string fileName = frag_path.substr(frag_path.find_last_of("/") + 1);
+    error_formatter{id,
+                    fileName,
+                    frag_path,
+                    "GLSL program link error!",
+                    "File location of vertex shader: " + vert_path +
+                        "\nFile location of fragment shader: " + frag_path,
+                    "GLSL Link Error",
+                    glGetProgramiv,
+                    glGetProgramInfoLog}();
+  }
+}

+ 26 - 114
graphics/src/opengl_helper.cxx

@@ -24,99 +24,10 @@
 #include "game/util/files.hpp"
 #include "game/util/time.hpp"
 
-namespace {
-  typedef void (*GLGetIntFor)(GLuint, GLenum, GLint *);
-  typedef void (*GLGetLog)(GLuint, GLsizei, GLsizei *, GLchar *);
-  struct error_formatter {
-    void operator()() const;
-    static void shader_error(GLuint id, std::string const & path);
-    static void shader_program_error(GLuint id, std::string const & frag_path,
-                                     std::string const & vert_path);
-
-    GLuint obj;
-    std::string fileName, path, eNoticeMessage, eFileMessage, eWindowTitle;
-    GLGetIntFor getIntv;
-    GLGetLog getInfoLog;
-  };
-
-  void error_formatter::operator()() const {
-    std::stringstream errorString;
-    std::string errorLineNumber;
-
-    int infologLength = 0;
-    int maxLength = 0;
-
-    getIntv(obj, GL_INFO_LOG_LENGTH, &maxLength);
-    errorString << eNoticeMessage << "\n";
-
-    std::unique_ptr<char[]> infoLog(new char[maxLength + 1]);
-    getInfoLog(obj, maxLength, &infologLength, infoLog.get());
-
-    std::string errorLog(infoLog.get());
-    infoLog.reset();
-    if (infologLength <= 0) return;
-
-    std::size_t openParen = errorLog.find('(');
-    std::size_t closeParen = errorLog.find(')');
-    errorLineNumber =
-        errorLog.substr(openParen + 1, closeParen - openParen - 1);
-
-    std::string errorStart = errorLog.find("error") != std::string::npos
-                                 ? errorLog.substr(errorLog.find("error"))
-                                 : errorLog;
-
-    errorString << errorLog << "\n";
-
-    errorString << "OpenGL Version: " << glGetString(GL_VERSION) << "\n";
-    errorString << "GLSL Shading Version: "
-                << glGetString(GL_SHADING_LANGUAGE_VERSION) << "\n";
-
-    errorString << eFileMessage << "\n";
-    errorString << "RAW ERROR LOG: " << errorLog;
-
-    std::stringstream consoleOutput;
-
-    consoleOutput << "1>" << path << '(' << errorLineNumber << ')' << ": "
-                  << errorStart
-                  << "in OpenGL version: " << glGetString(GL_VERSION) << " and "
-                  << "GLSL Shading Version: "
-                  << glGetString(GL_SHADING_LANGUAGE_VERSION) << std::endl;
-
-#ifdef _WIN32
-    OutputDebugStringA(consoleOutput.str().c_str());
-    MessageBoxA(nullptr, errorString.str().c_str(), eWindowTitle.c_str(),
-                MB_OK);
-#else
-    std::cerr << errorString.str() << std::endl;
-#endif
-  }
-
-  void error_formatter::shader_error(GLuint id, std::string const & path) {
-    std::string fileName = path.substr(path.find_last_of("/") + 1);
-    error_formatter{id,
-                    fileName,
-                    path,
-                    "GLSL shader compile error!",
-                    "File location: " + path,
-                    "GLSL Compile Error in " + fileName,
-                    glGetShaderiv,
-                    glGetShaderInfoLog}();
-  }
-
-  void error_formatter::shader_program_error(GLuint id,
-                                             std::string const & frag_path,
-                                             std::string const & vert_path) {
-    std::string fileName = frag_path.substr(frag_path.find_last_of("/") + 1);
-    error_formatter{id,
-                    fileName,
-                    frag_path,
-                    "GLSL program link error!",
-                    "File location of vertex shader: " + vert_path +
-                        "\nFile location of fragment shader: " + frag_path,
-                    "GLSL Link Error",
-                    glGetProgramiv,
-                    glGetProgramInfoLog}();
-  }
+namespace graphics {
+  extern void print_shader_error(GLuint, std::string const &);
+  extern void print_shader_program_error(GLuint, std::string const &,
+                                         std::string const &);
 }
 
 namespace graphics { namespace textures {
@@ -244,14 +155,14 @@ namespace graphics { namespace shaders {
     glGetShaderiv(id, GL_COMPILE_STATUS, &return_code);
 
     if (return_code != GL_TRUE) {
-      error_formatter::shader_error(id, abs_path);
+      print_shader_error(id, abs_path);
       throw compilation_error("Could not compile the shader file");
     }
     return id;
   }
 
-  unsigned int init(flyweight<shader> const & fragmentShader,
-                    flyweight<shader> const & vertexShader) {
+  unsigned int init(identity<shader> const & fragmentShader,
+                    identity<shader> const & vertexShader) {
     // 9. Create a new shader program ID
     unsigned int id = glCreateProgram();
 
@@ -274,16 +185,16 @@ namespace graphics { namespace shaders {
     glGetProgramiv(id, GL_LINK_STATUS, &return_code);
 
     if (return_code != GL_TRUE) {
-      error_formatter::shader_program_error(id, fragmentShader.actual().path,
-                                            vertexShader.actual().path);
+      //      print_shader_program_error(id, fragmentShader.actual().path,
+      //                                 vertexShader.actual().path);
       throw linker_error("Could not link shader program");
     }
     return id;
   }
 
-  void activate(unsigned int id) {
+  void activate(identity<shader_program> program) {
     // 100. Use the shader program ID to "turn it on" for all subsequent drawing
-    glUseProgram(id);
+    glUseProgram(program.id);
 
     /*
      // 101. Enable texturing and Bind texture(s) to GPU texture units
@@ -306,13 +217,13 @@ namespace graphics { namespace shaders {
 
     // 102. Get the location # of each named uniform you wish to pass in to the
     // shader
-    int timeUniformLocation = glGetUniformLocation(id, "u_time");
-    int scale = glGetUniformLocation(id, "Scale");
-    int diffuseMapUniformLocation = glGetUniformLocation(id, "u_diffuseMap");
-    int normalMapUniformLocation = glGetUniformLocation(id, "u_normalMap");
-    int specularMapUniformLocation = glGetUniformLocation(id, "u_specularMap");
-    int emissiveMapUniformLocation = glGetUniformLocation(id, "u_emissiveMap");
-    int debugWave = glGetUniformLocation(id, "g_debugWave");
+    int timeUniformLocation = glGetUniformLocation(program.id, "u_time");
+    int scale = glGetUniformLocation(program.id, "Scale");
+    int diffuseMap = glGetUniformLocation(program.id, "u_diffuseMap");
+    int normalMap = glGetUniformLocation(program.id, "u_normalMap");
+    int specularMap = glGetUniformLocation(program.id, "u_specularMap");
+    int emissiveMap = glGetUniformLocation(program.id, "u_emissiveMap");
+    int debugWave = glGetUniformLocation(program.id, "g_debugWave");
 
     // 103. Set the uniform values, including the texture unit numbers for
     // texture (sampler) uniforms
@@ -321,22 +232,23 @@ namespace graphics { namespace shaders {
     glUniform1f(scale, 2.f);
     glUniform1i(debugWave, 1); // TODO: m_waveEffectOn in ShaderProgram??
     // for GL_TEXTURE0, texture unit 0
-    glUniform1i(diffuseMapUniformLocation, 0);
+    glUniform1i(diffuseMap, 0);
     // for GL_TEXTURE1, texture unit 1
-    glUniform1i(normalMapUniformLocation, 1);
+    glUniform1i(normalMap, 1);
     // for GL_TEXTURE2, texture unit 2
-    glUniform1i(specularMapUniformLocation, 2);
+    glUniform1i(specularMap, 2);
     // for GL_TEXTURE3, texture unit 3
-    glUniform1i(emissiveMapUniformLocation, 3);
+    glUniform1i(emissiveMap, 3);
   }
 
-  int uniform_location(unsigned int id, std::string const & uniform) {
-    return glGetUniformLocation(id, uniform.c_str());
+  int uniform_location(identity<shader_program> program,
+                       std::string const & uniform) {
+    return glGetUniformLocation(program.id, uniform.c_str());
   }
 }}
 
 namespace graphics { namespace materials {
-  void activate(flyweight<shader_program> program,
+  void activate(identity<shader_program> program,
                 std::vector<uniform> const & uniforms) {
     glUseProgram(program.id);
 

+ 14 - 7
graphics/src/opengl_renderer.cxx

@@ -12,6 +12,7 @@
 #include <OpenGL/gl3.h>
 #endif
 
+#include "game/graphics/manager.hpp"
 #include "game/graphics/material.hpp"
 #include "game/graphics/vertex.h"
 #include "game/util/env.hpp"
@@ -27,19 +28,26 @@ public:
   opengl_renderer();
   ~opengl_renderer();
 
-  void draw(flyweight<material>, math::matr4 const &,
+  void draw(identity<material>, math::matr4 const &,
             std::vector<vertex> const &) override;
   void clear() override;
   void flush() override;
 
+  std::shared_ptr<class manager const> manager() const override { return mgr; }
+
 private:
   const math::matr4 identity{math::matrix::identity<float, 4>()};
+
+  std::shared_ptr<class manager> mgr;
+  unsigned int active_material;
+
   math::matr4 world_to_clip{identity};
   double current_time{0.0};
   unsigned int vertex_array_object{0}, vertex_buffer_object{0};
 };
 
-opengl_renderer::opengl_renderer() {
+opengl_renderer::opengl_renderer()
+    : mgr(new class manager), active_material(0) {
   glGenVertexArrays(1, &vertex_array_object);
   glBindVertexArray(vertex_array_object);
   glGenBuffers(1, &vertex_buffer_object);
@@ -73,14 +81,13 @@ void opengl_renderer::clear() {
 
 void opengl_renderer::flush() { glFlush(); }
 
-void opengl_renderer::draw(flyweight<material> material,
+void opengl_renderer::draw(::identity<material> material_id,
                            math::matr4 const & object_to_world,
                            std::vector<vertex> const & vertices) {
-  // TODO: Don't activate here?
-  auto const & mat = material.actual();
+  material const & mat = manager()->get(material_id);
+  if (material_id != active_material) { mat.activate(); }
   // TODO: Attatch shader-id to material-id
-  unsigned int const id = mat.shaders.id;
-  mat.activate();
+  unsigned int const id = mat.program.id;
 
   int objectLocation = glGetUniformLocation(id, "u_objectToWorldMatrix");
   // Argument 2: GL_TRUE -> row-major ; GL_FALSE -> column-major

+ 6 - 2
graphics/src/renderer.cxx

@@ -28,13 +28,17 @@ renderer_impl * get_renderer_impl(driver d) {
 
 direct_renderer::direct_renderer(driver d) : pimpl(::get_renderer_impl(d)) {}
 
+std::shared_ptr<class manager const> direct_renderer::manager() const {
+  return pimpl->manager();
+}
+
 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,
+void direct_renderer::draw(identity<material> material,
                            math::matr4 const & object_to_world,
                            std::vector<vertex> const & verts) {
   pimpl->draw(material, object_to_world, verts);
@@ -57,7 +61,7 @@ void batch_renderer::draw(object const & obj) {
 }
 
 // TODO (sjaffe): object-to-world matrix...
-void batch_renderer::draw(flyweight<material> material, math::matr4 const &,
+void batch_renderer::draw(identity<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());

+ 3 - 5
graphics/src/renderer_impl.hpp

@@ -8,19 +8,17 @@
 
 #pragma once
 
+#include "game/graphics/graphics_fwd.h"
 #include "game/graphics/renderer.hpp"
 #include "game/math/math_fwd.hpp"
-#include "game/util/flyweight.hpp"
 
 namespace graphics {
   template <driver> renderer_impl * get_renderer_impl();
 
-  class material;
-  struct vertex;
-
   struct renderer_impl {
     virtual ~renderer_impl() {}
-    virtual void draw(flyweight<material>, math::matr4 const &,
+    virtual std::shared_ptr<manager const> manager() const = 0;
+    virtual void draw(identity<material>, math::matr4 const &,
                       std::vector<vertex> const &) = 0;
     virtual void clear() = 0;
     virtual void flush() = 0;

+ 2 - 20
graphics/src/shader.cpp

@@ -7,27 +7,9 @@
 
 #include "game/graphics/shader.hpp"
 
-#include <unordered_map>
-
-#include "game/util/hash.hpp"
 #include "helper.hpp"
 
 using namespace graphics;
 
-namespace {
-  using key_t = std::pair<shaders::type, std::string>;
-  std::unordered_map<key_t, flyweight<shader>> g_shaders;
-}
-
-flyweight<shader> shader::create(shaders::type tp, std::string const & path) {
-  auto key = std::make_pair(tp, path);
-  auto found = g_shaders.find(key);
-  if (found != g_shaders.end()) { return found->second; }
-
-  auto id = shaders::init(tp, path);
-  flyweight<shader> fly{id, {id, tp, path}};
-  return g_shaders.emplace(key, fly).first->second;
-}
-
-shader::shader(unsigned int id, shaders::type type, std::string const & path)
-    : identity<shader>(id), type(type), path(path) {}
+shader::shader(shaders::type type, std::string const & path)
+    : identity<shader>(shaders::init(type, path)), type(type), path(path) {}

+ 3 - 27
graphics/src/shader_program.cpp

@@ -7,37 +7,13 @@
 
 #include "game/graphics/shader_program.hpp"
 
-#include <unordered_map>
-#include <utility>
-
 #include "game/graphics/shader.hpp"
-#include "game/util/hash.hpp"
 #include "helper.hpp"
 
 using namespace graphics;
 
-namespace {
-  using key_t = std::pair<std::string, std::string>;
-  std::unordered_map<key_t, flyweight<shader_program>> g_shader_programs;
-}
-
-flyweight<shader_program> shader_program::create(std::string const & frag,
-                                                 std::string const & vert) {
-  auto key = std::make_pair(frag, vert);
-  auto found = g_shader_programs.find(key);
-  if (found != g_shader_programs.end()) { return found->second; }
-
-  auto fragment_shader = shader::create(shaders::type::FRAGMENT, frag);
-  auto vertex_shader = shader::create(shaders::type::VERTEX, vert);
-
-  auto id = shaders::init(fragment_shader, vertex_shader);
-  flyweight<shader_program> fly{id, {id, fragment_shader, vertex_shader}};
-  return g_shader_programs.emplace(key, fly).first->second;
-}
-
-shader_program::shader_program(unsigned int id, flyweight<shader> frag,
-                               flyweight<shader> vert)
-    : identity<shader_program>(id), fragment_shader(frag), vertex_shader(vert) {
-}
+shader_program::shader_program(identity<shader> frag, identity<shader> vert)
+    : identity<shader_program>(shaders::init(frag, vert)),
+      fragment_shader(frag), vertex_shader(vert) {}
 
 void shader_program::activate() const { shaders::activate(id); }

+ 12 - 20
graphics/src/texture.cpp

@@ -26,10 +26,6 @@ void stbi_image_free(void *);
 
 using namespace graphics;
 
-namespace {
-  std::unordered_map<std::string, flyweight<texture>> g_textures;
-}
-
 static textures::format format(int comps) {
   switch (comps) {
   case 3:
@@ -41,36 +37,32 @@ static textures::format format(int comps) {
   }
 }
 
-flyweight<texture> texture::create(std::string const & imagefile) {
-  auto found = g_textures.find(imagefile);
-  if (found != g_textures.end()) { return found->second; }
-
+std::pair<unsigned int, math::vec2i> create(std::string const & path) {
   int components = 0;
   math::vec2i size;
-  std::string file = env::resource_file(imagefile);
+  std::string file = env::resource_file(path);
   auto data = stbi_load(file.c_str(), &size.x(), &size.y(), &components, 0);
   scope(exit) { stbi_image_free(data); };
-  auto id = textures::init(format(components), size, data);
-  flyweight<texture> fly{id, {id, size}};
-  return g_textures.emplace(imagefile, fly).first->second;
+  return {textures::init(format(components), size, data), size};
 }
 
-texture texture::create(char const * data, math::vec2i size) {
-  return {textures::init(format(4), size, data), size};
-}
+texture::texture(std::string const & path) : texture(create(path)) {}
+
+texture::texture(std::pair<unsigned int, math::vec2i> pair)
+    : identity<texture>(pair.first), size(pair.second) {}
 
-texture::texture(unsigned int id, math::vec2i sz)
-    : identity<texture>(id), size(sz) {}
+texture::texture(char const * data, math::vec2i size)
+    : identity<texture>(textures::init(format(4), size, data)), size(size) {}
 
 texture const texture::WHITE() {
-  static auto t = texture::create("\xFF\xFF\xFF\xFF", {{1, 1}});
+  static auto t = texture("\xFF\xFF\xFF\xFF", {{1, 1}});
   return t;
 }
 texture const texture::DARK_YELLOW() {
-  static auto t = texture::create("\x80\x80\x00\xFF", {{1, 1}});
+  static auto t = texture("\x80\x80\x00\xFF", {{1, 1}});
   return t;
 }
 texture const texture::LIGHT_BLUE() {
-  static auto t = texture::create("\x80\x80\xFF\xFF", {{1, 1}});
+  static auto t = texture("\x80\x80\xFF\xFF", {{1, 1}});
   return t;
 }

+ 3 - 4
math/math.xcodeproj/project.pbxproj

@@ -10,9 +10,9 @@
 		CD1FCFD2227E194D00F9BF93 /* libmath.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = CD3786181CF9F61100BE89B2 /* libmath.dylib */; };
 		CD1FCFD8227E195B00F9BF93 /* shape_test.cxx in Sources */ = {isa = PBXBuildFile; fileRef = CD1FCFC8227E193000F9BF93 /* shape_test.cxx */; };
 		CD1FCFE9227E198100F9BF93 /* GoogleMock.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CD1FCFE1227E197800F9BF93 /* GoogleMock.framework */; };
-		CD1FCFEC227E4C2E00F9BF93 /* common.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CD3C80BA1D68902300ACC795 /* common.cpp */; };
-		CD3AC71E1D2C0AF8002B4BB0 /* shape.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CD3AC71C1D2C0AF8002B4BB0 /* shape.cpp */; };
-		CD3C809F1D675AB100ACC795 /* angle.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CD3C809D1D675AB100ACC795 /* angle.cpp */; };
+		CD1FCFEC227E4C2E00F9BF93 /* common.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CD3C80BA1D68902300ACC795 /* common.cpp */; settings = {COMPILER_FLAGS = "-fvisibility=default"; }; };
+		CD3AC71E1D2C0AF8002B4BB0 /* shape.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CD3AC71C1D2C0AF8002B4BB0 /* shape.cpp */; settings = {COMPILER_FLAGS = "-fvisibility=default"; }; };
+		CD3C809F1D675AB100ACC795 /* angle.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CD3C809D1D675AB100ACC795 /* angle.cpp */; settings = {COMPILER_FLAGS = "-fvisibility=default"; }; };
 		CDA34D9522517967008036A7 /* matrix_helpers.hpp in Headers */ = {isa = PBXBuildFile; fileRef = CDA34D8C22517680008036A7 /* matrix_helpers.hpp */; };
 		CDA34D9622517969008036A7 /* matrix.hpp in Headers */ = {isa = PBXBuildFile; fileRef = CDA34D8D22517680008036A7 /* matrix.hpp */; };
 		CDA34D972251796B008036A7 /* vector.hpp in Headers */ = {isa = PBXBuildFile; fileRef = CDA34D9022517689008036A7 /* vector.hpp */; };
@@ -544,7 +544,6 @@
 				GCC_ENABLE_CPP_EXCEPTIONS = YES;
 				GCC_ENABLE_CPP_RTTI = YES;
 				GCC_SYMBOLS_PRIVATE_EXTERN = YES;
-				OTHER_LDFLAGS = "-fvisibility=default";
 				PRODUCT_NAME = "$(TARGET_NAME)";
 			};
 			name = Release;

+ 3 - 4
util/gameutils.xcodeproj/project.pbxproj

@@ -7,9 +7,9 @@
 	objects = {
 
 /* 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 */; };
+		CD62FD04229195FF00376440 /* files.cxx in Sources */ = {isa = PBXBuildFile; fileRef = CD62FD02229195FF00376440 /* files.cxx */; settings = {COMPILER_FLAGS = "-fvisibility=default"; }; };
+		CD62FD082291988F00376440 /* osx_env.mm in Sources */ = {isa = PBXBuildFile; fileRef = CD62FD072291988F00376440 /* osx_env.mm */; settings = {COMPILER_FLAGS = "-fvisibility=default"; }; };
+		CD7E87772295FA1F00D877FE /* time.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CD7E876F2295FA1F00D877FE /* time.cpp */; settings = {COMPILER_FLAGS = "-fvisibility=default"; }; };
 		CD7E884822960F4800D877FE /* AppKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CD7E884722960F4800D877FE /* AppKit.framework */; };
 /* End PBXBuildFile section */
 
@@ -372,7 +372,6 @@
 				GCC_ENABLE_CPP_EXCEPTIONS = YES;
 				GCC_ENABLE_CPP_RTTI = YES;
 				GCC_SYMBOLS_PRIVATE_EXTERN = YES;
-				OTHER_LDFLAGS = "-fvisibility=default";
 				PRODUCT_NAME = "$(TARGET_NAME)";
 			};
 			name = Release;

+ 0 - 33
util/include/game/util/flyweight.hpp

@@ -1,33 +0,0 @@
-//
-//  flyweight.hpp
-//  gameutils
-//
-//  Created by Sam Jaffe on 5/19/19.
-//
-
-#pragma once
-
-#include <unordered_map>
-
-#include "identity.hpp"
-
-template <typename T> class flyweight {
-private:
-  static std::unordered_map<unsigned int, T> actual_;
-
-public:
-  const unsigned int id;
-
-public:
-  flyweight(unsigned int id, T actual) : id(id) {
-    actual_.emplace(id, std::move(actual));
-  }
-  flyweight(identity<T> actual) : id(actual.id) {}
-  T const & actual() const { return actual_.find(this->id)->second; }
-
-  friend bool operator==(flyweight lhs, flyweight rhs) {
-    return lhs.id == rhs.id;
-  }
-};
-
-template <typename T> std::unordered_map<unsigned int, T> flyweight<T>::actual_;

+ 4 - 4
util/include/game/util/hash.hpp

@@ -11,12 +11,12 @@
 #include <tuple>
 #include <utility>
 
-template <typename> class flyweight;
+template <typename, typename> class identity;
 
 namespace std {
-  template <typename T> struct hash<flyweight<T>> {
-    std::size_t operator()(flyweight<T> tp) const {
-      return std::hash<unsigned int>()(tp.id);
+  template <typename T, typename I> struct hash<identity<T, I>> {
+    std::size_t operator()(identity<T, I> tp) const {
+      return std::hash<I>()(tp.id);
     }
   };
 

+ 7 - 0
util/include/game/util/identity.hpp

@@ -17,4 +17,11 @@ private:
   friend T;
   identity(ID _id) : id(std::move(_id)) {}
   identity() = delete;
+
+  friend bool operator==(identity const & lhs, identity const & rhs) {
+    return lhs.id == rhs.id;
+  }
+  friend bool operator!=(identity const & lhs, ID const & rhs) {
+    return lhs.id != rhs;
+  }
 };