+/* $Id$
+
+This file is part of libmspgl
+Copyright © 2011 Mikko Rasa, Mikkosoft Productions
+Distributed under the LGPL
+*/
+
+#include <msp/core/except.h>
+#include "batch.h"
+#include "buffer.h"
+#include "camera.h"
+#include "material.h"
+#include "program.h"
+#include "programdata.h"
+#include "renderer.h"
+#include "texture.h"
+#include "texturing.h"
+#include "vertexarray.h"
+
+using namespace std;
+
+namespace Msp {
+namespace GL {
+
+Renderer::Renderer(const Camera *c):
+ mtx_changed(false),
+ camera(c),
+ state_stack(1),
+ state(&state_stack.back()),
+ vertex_array(0),
+ vertex_array_changed(false),
+ element_buffer(0)
+{
+ MatrixStack::modelview().push();
+ if(camera)
+ {
+ MatrixStack::projection().push();
+ camera->apply();
+ mtx_stack = camera->get_matrix();
+ }
+ else
+ mtx_stack = MatrixStack::modelview().top();
+}
+
+Renderer::~Renderer()
+{
+ if(camera)
+ MatrixStack::projection().pop();
+ MatrixStack::modelview().pop();
+
+ Texturing::unbind();
+ Texture::unbind_from(0);
+ Material::unbind();
+ Program::unbind();
+ Buffer::unbind_from(ELEMENT_ARRAY_BUFFER);
+}
+
+MatrixStack &Renderer::matrix_stack()
+{
+ mtx_changed = true;
+ return mtx_stack;
+}
+
+void Renderer::set_texture(const Texture *t)
+{
+ state->texture = t;
+ state->texturing = 0;
+}
+
+void Renderer::set_texturing(const Texturing *t)
+{
+ state->texturing = t;
+ state->texture = 0;
+}
+
+void Renderer::set_material(const Material *m)
+{
+ state->material = m;
+}
+
+void Renderer::set_shader(const Program *p, const ProgramData *d)
+{
+ state->shprog = p;
+ if(d)
+ state->shdata.assign(1, d);
+ else
+ state->shdata.clear();
+}
+
+void Renderer::add_shader_data(const ProgramData *d)
+{
+ if(!state->shprog)
+ throw InvalidState("No shader program");
+
+ state->shdata.push_back(d);
+}
+
+void Renderer::set_vertex_array(const VertexArray *a)
+{
+ vertex_array_changed = (a!=vertex_array);
+ vertex_array = a;
+}
+
+void Renderer::set_element_buffer(const Buffer *b)
+{
+ element_buffer = b;
+}
+
+void Renderer::push_state()
+{
+ state_stack.push_back(state_stack.back());
+ state = &state_stack.back();
+ mtx_stack.push();
+}
+
+void Renderer::pop_state()
+{
+ if(state_stack.size()==1)
+ throw InvalidState("Can't pop the last state");
+
+ state_stack.pop_back();
+ state = &state_stack.back();
+ mtx_stack.pop();
+}
+
+void Renderer::escape()
+{
+ apply_state();
+ Buffer::unbind_from(ELEMENT_ARRAY_BUFFER);
+}
+
+void Renderer::draw(const Batch &batch)
+{
+ if(!vertex_array)
+ throw InvalidState("Can't draw without a vertex array");
+
+ apply_state();
+
+ // Until VertexArray acquires VAO support and becomes Bindable
+ if(vertex_array_changed)
+ vertex_array->apply();
+
+ if(element_buffer)
+ element_buffer->bind_to(ELEMENT_ARRAY_BUFFER);
+ else
+ Buffer::unbind_from(ELEMENT_ARRAY_BUFFER);
+
+ batch.draw();
+}
+
+void Renderer::apply_state()
+{
+ // We let the objects themselves figure out if the binding has changed
+
+ if(state->texturing)
+ state->texturing->bind();
+ else
+ {
+ Texturing::unbind();
+ if(state->texture)
+ state->texture->bind_to(0);
+ else
+ Texture::unbind_from(0);
+ }
+
+ if(state->material)
+ state->material->bind();
+ else
+ Material::unbind();
+
+ if(state->shprog)
+ {
+ state->shprog->bind();
+ for(vector<const ProgramData *>::const_iterator i=state->shdata.begin(); i!=state->shdata.end(); ++i)
+ (*i)->apply();
+ }
+ else
+ Program::unbind();
+
+ if(mtx_changed)
+ {
+ MatrixStack::modelview() = mtx_stack.top();
+ mtx_changed = false;
+ }
+}
+
+
+Renderer::State::State():
+ texture(0),
+ texturing(0),
+ material(0),
+ shprog(0)
+{ }
+
+} // namespace GL
+} // namespace Msp