, chunk_range(4) {
        EntityModel::Buffer buf;
        {
-               CuboidShape shape({{ -0.25f, -0.5f, -0.25f }, { 0.25f, 0.5f, 0.25f }});
+               AABB bounds{{ -0.25f, -0.5f, -0.25f }, { 0.25f, 0.5f, 0.25f }};
+               CuboidShape shape(bounds);
                shape.Vertices(buf, 1.0f);
                buf.colors.resize(shape.VertexCount(), { 1.0f, 1.0f, 0.0f });
                models[0].Update(buf);
+               skeletons[0].Bounds(bounds);
+               skeletons[0].SetNodeModel(&models[0]);
        }
        {
-               CuboidShape shape({{ -0.5f, -0.25f, -0.5f }, { 0.5f, 0.25f, 0.5f }});
+               AABB bounds{{ -0.5f, -0.25f, -0.5f }, { 0.5f, 0.25f, 0.5f }};
+               CuboidShape shape(bounds);
                buf.Clear();
                shape.Vertices(buf, 2.0f);
                buf.colors.resize(shape.VertexCount(), { 0.0f, 1.0f, 1.0f });
                models[1].Update(buf);
+               skeletons[1].Bounds(bounds);
+               skeletons[1].SetNodeModel(&models[1]);
        }
        {
-               StairShape shape({{ -0.5f, -0.5f, -0.5f }, { 0.5f, 0.5f, 0.5f }}, { 0.4f, 0.4f });
+               AABB bounds{{ -0.5f, -0.5f, -0.5f }, { 0.5f, 0.5f, 0.5f }};
+               StairShape shape(bounds, { 0.4f, 0.4f });
                buf.Clear();
                shape.Vertices(buf, 3.0f);
                buf.colors.resize(shape.VertexCount(), { 1.0f, 0.0f, 1.0f });
                models[2].Update(buf);
+               skeletons[2].Bounds(bounds);
+               skeletons[2].SetNodeModel(&models[2]);
        }
 
        timer.Start();
        e.Position(chunk, pos);
        e.Bounds({ { -0.5f, -0.5f, -0.5f }, { 0.5f, 0.5f, 0.5f } });
        e.WorldCollidable(true);
-       e.GetModel().SetNodeModel(&models[rand() % 3]);
+       skeletons[rand() % 3].Instantiate(e.GetModel());
        e.AngularVelocity(rot);
        Controller *ctrl;
        if (rand() % 2) {
 
 #define BLANK_AI_SPAWNER_HPP_
 
 #include "../app/IntervalTimer.hpp"
+#include "../model/CompositeModel.hpp"
 #include "../model/EntityModel.hpp"
 
 #include <vector>
        std::vector<Controller *> controllers;
 
        EntityModel models[3];
+       CompositeModel skeletons[3];
 
        IntervalTimer timer;
        float despawn_range;
 
--- /dev/null
+#ifndef BLANK_MODEL_COMPOSITEINSTANCE_HPP_
+#define BLANK_MODEL_COMPOSITEINSTANCE_HPP_
+
+#include <vector>
+#include <glm/glm.hpp>
+#include <glm/gtc/quaternion.hpp>
+
+
+namespace blank {
+
+class CompositeModel;
+class DirectionalLighting;
+
+// TODO: this doesn't have to be a tree, actually
+//       linearizing might be a good opportunity to optimize
+class CompositeInstance {
+
+       friend class CompositeModel;
+
+public:
+       CompositeInstance();
+
+       operator bool() const noexcept { return part_model; }
+
+       const glm::vec3 &Position() const noexcept { return position; }
+       void Position(const glm::vec3 &p) noexcept { position = p; }
+
+       const glm::quat &Orientation() const noexcept { return orientation; }
+       void Orientation(const glm::quat &o) noexcept { orientation = o; }
+
+       glm::mat4 LocalTransform() const noexcept;
+       glm::mat4 GlobalTransform() const noexcept;
+
+       void Render(const glm::mat4 &, DirectionalLighting &) const;
+
+private:
+       CompositeInstance &AddPart();
+       bool HasParent() const noexcept { return parent; }
+       CompositeInstance &Parent() const noexcept { return *parent; }
+       bool IsRoot() const noexcept { return !HasParent(); }
+
+private:
+       const CompositeModel *part_model;
+       CompositeInstance *parent;
+
+       glm::vec3 position;
+       glm::quat orientation;
+
+       std::vector<CompositeInstance> parts;
+
+};
+
+}
+
+#endif
 
+++ /dev/null
-#include "CompositeModel.hpp"
-
-#include "EntityModel.hpp"
-#include "../graphics/DirectionalLighting.hpp"
-
-#include <glm/gtx/quaternion.hpp>
-
-
-namespace blank {
-
-CompositeModel::CompositeModel()
-: node_model(nullptr)
-, position(0.0f)
-, orientation(1.0f, 0.0f, 0.0f, 0.0f)
-, parts() {
-
-}
-
-
-CompositeModel &CompositeModel::AddPart() {
-       parts.emplace_back();
-       parts.back().parent = this;
-       return parts.back();
-}
-
-
-glm::mat4 CompositeModel::LocalTransform() const noexcept {
-       glm::mat4 transform(toMat4(orientation));
-       transform[3].x = position.x;
-       transform[3].y = position.y;
-       transform[3].z = position.z;
-       return transform;
-}
-
-glm::mat4 CompositeModel::GlobalTransform() const noexcept {
-       if (HasParent()) {
-               return Parent().GlobalTransform() * LocalTransform();
-       } else {
-               return LocalTransform();
-       }
-}
-
-
-void CompositeModel::Render(const glm::mat4 &M, DirectionalLighting &prog) const {
-       glm::mat4 transform(M * LocalTransform());
-       if (HasNodeModel()) {
-               prog.SetM(transform);
-               NodeModel().Draw();
-       }
-       for (const CompositeModel &part : parts) {
-               part.Render(transform, prog);
-       }
-}
-
-}
 
 #ifndef BLANK_MODEL_COMPOSITEMODEL_HPP_
 #define BLANK_MODEL_COMPOSITEMODEL_HPP_
 
+#include "geometry.hpp"
+
 #include <list>
 #include <glm/glm.hpp>
 #include <glm/gtc/quaternion.hpp>
 
 namespace blank {
 
-class DirectionalLighting;
+class CompositeInstance;
 class EntityModel;
 
 class CompositeModel {
        CompositeModel(const CompositeModel &) = delete;
        CompositeModel &operator =(const CompositeModel &) = delete;
 
+       const AABB &Bounds() const noexcept { return bounds; }
+       void Bounds(const AABB &b) noexcept { bounds = b; }
+
        const glm::vec3 &Position() const noexcept { return position; }
        void Position(const glm::vec3 &p) noexcept { position = p; }
 
        glm::mat4 LocalTransform() const noexcept;
        glm::mat4 GlobalTransform() const noexcept;
 
-       void Render(const glm::mat4 &, DirectionalLighting &) const;
+       void Instantiate(CompositeInstance &) const;
 
 private:
        CompositeModel *parent;
        const EntityModel *node_model;
 
+       AABB bounds;
+
        glm::vec3 position;
        glm::quat orientation;
 
 
--- /dev/null
+#include "CompositeModel.hpp"
+#include "CompositeInstance.hpp"
+
+#include "EntityModel.hpp"
+#include "../graphics/DirectionalLighting.hpp"
+
+#include <glm/gtx/quaternion.hpp>
+
+
+namespace blank {
+
+CompositeModel::CompositeModel()
+: node_model(nullptr)
+, position(0.0f)
+, orientation(1.0f, 0.0f, 0.0f, 0.0f)
+, parts() {
+
+}
+
+
+CompositeModel &CompositeModel::AddPart() {
+       parts.emplace_back();
+       parts.back().parent = this;
+       return parts.back();
+}
+
+
+glm::mat4 CompositeModel::LocalTransform() const noexcept {
+       glm::mat4 transform(toMat4(orientation));
+       transform[3].x = position.x;
+       transform[3].y = position.y;
+       transform[3].z = position.z;
+       return transform;
+}
+
+glm::mat4 CompositeModel::GlobalTransform() const noexcept {
+       if (HasParent()) {
+               return Parent().GlobalTransform() * LocalTransform();
+       } else {
+               return LocalTransform();
+       }
+}
+
+
+void CompositeModel::Instantiate(CompositeInstance &inst) const {
+       inst.part_model = this;
+       inst.position = position;
+       inst.orientation = orientation;
+       inst.parts.clear();
+       inst.parts.reserve(parts.size());
+       for (const CompositeModel &part : parts) {
+               part.Instantiate(inst.AddPart());
+       }
+}
+
+
+CompositeInstance::CompositeInstance()
+: part_model(nullptr)
+, parent(nullptr)
+, position(0.0f)
+, orientation(1.0f, 0.0f, 0.0f, 0.0f)
+, parts() {
+
+}
+
+
+CompositeInstance &CompositeInstance::AddPart() {
+       parts.emplace_back();
+       parts.back().parent = this;
+       return parts.back();
+}
+
+
+glm::mat4 CompositeInstance::LocalTransform() const noexcept {
+       glm::mat4 transform(toMat4(orientation));
+       transform[3].x = position.x;
+       transform[3].y = position.y;
+       transform[3].z = position.z;
+       return transform;
+}
+
+glm::mat4 CompositeInstance::GlobalTransform() const noexcept {
+       if (HasParent()) {
+               return Parent().GlobalTransform() * LocalTransform();
+       } else {
+               return LocalTransform();
+       }
+}
+
+
+void CompositeInstance::Render(const glm::mat4 &M, DirectionalLighting &prog) const {
+       glm::mat4 transform(M * LocalTransform());
+       if (part_model->HasNodeModel()) {
+               prog.SetM(transform);
+               part_model->NodeModel().Draw();
+       }
+       for (const CompositeInstance &part : parts) {
+               part.Render(transform, prog);
+       }
+}
+
+}
 
 #define BLANK_WORLD_ENTITY_HPP_
 
 #include "Chunk.hpp"
-#include "../model/CompositeModel.hpp"
+#include "../model/CompositeInstance.hpp"
 #include "../model/geometry.hpp"
 
 #include <string>
 public:
        Entity() noexcept;
 
-       CompositeModel &GetModel() noexcept { return model; }
-       const CompositeModel &GetModel() const noexcept { return model; }
+       CompositeInstance &GetModel() noexcept { return model; }
+       const CompositeInstance &GetModel() const noexcept { return model; }
 
        const std::string &Name() const noexcept { return name; }
        void Name(const std::string &n) { name = n; }
        void Update(int dt) noexcept;
 
        void Render(const glm::mat4 &M, DirectionalLighting &prog) noexcept {
-               model.Render(M, prog);
+               if (model) model.Render(M, prog);
        }
 
 private:
-       CompositeModel model;
+       CompositeInstance model;
 
        std::string name;