-/* $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 "error.h"
+#include "lighting.h"
#include "material.h"
#include "program.h"
#include "programdata.h"
+#include "renderable.h"
#include "renderer.h"
#include "texture.h"
#include "texturing.h"
mtx_changed(false),
camera(c),
state_stack(1),
- state(&state_stack.back()),
+ lighting_changed(false),
vertex_array(0),
- vertex_array_changed(false),
element_buffer(0)
{
+ state_stack.reserve(16);
+ shdata_stack.reserve(32);
+ state = &state_stack.back();
+
MatrixStack::modelview().push();
if(camera)
{
state->material = m;
}
-void Renderer::set_shader(const Program *p, const ProgramData *d)
+void Renderer::set_lighting(const Lighting *l)
{
- state->shprog = p;
- if(d)
- state->shdata.assign(1, d);
- else
- state->shdata.clear();
+ state->lighting = l;
+ state->lighting_matrix = mtx_stack.top();
+ lighting_changed = true;
}
-void Renderer::add_shader_data(const ProgramData *d)
+void Renderer::set_shader_program(const Program *p, const ProgramData *d)
{
- if(!state->shprog)
- throw InvalidState("No shader program");
+ state->shprog = p;
+ if(p && d)
+ add_shader_data(*d);
+
+ /* Even if we have no new shdata, the existing ones need to be re-applied
+ to the new program */
+ shdata_changed = true;
+}
- state->shdata.push_back(d);
+void Renderer::add_shader_data(const ProgramData &d)
+{
+ shdata_stack.push_back(&d);
+ state->shdata_count = shdata_stack.size();
+ shdata_changed = true;
}
void Renderer::set_vertex_array(const VertexArray *a)
{
- vertex_array_changed = (a!=vertex_array);
vertex_array = a;
}
state->winding_test = w;
}
+void Renderer::set_reverse_winding(bool r)
+{
+ state->reverse_winding = r;
+}
+
void Renderer::push_state()
{
state_stack.push_back(state_stack.back());
void Renderer::pop_state()
{
if(state_stack.size()==1)
- throw InvalidState("Can't pop the last state");
+ throw stack_underflow("Renderer::pop_state");
state_stack.pop_back();
state = &state_stack.back();
+ if(shdata_stack.size()>state->shdata_count)
+ shdata_stack.erase(shdata_stack.begin()+state->shdata_count, shdata_stack.end());
mtx_stack.pop();
mtx_changed = true;
+ shdata_changed = true;
}
void Renderer::escape()
Buffer::unbind_from(ELEMENT_ARRAY_BUFFER);
}
+void Renderer::exclude(const Renderable &renderable)
+{
+ excluded.insert(&renderable);
+}
+
+void Renderer::include(const Renderable &renderable)
+{
+ excluded.erase(&renderable);
+}
+
+void Renderer::render(const Renderable &renderable, const Tag &tag)
+{
+ if(!excluded.count(&renderable))
+ renderable.render(*this, tag);
+}
+
void Renderer::draw(const Batch &batch)
{
if(!vertex_array)
- throw InvalidState("Can't draw without a vertex array");
+ throw invalid_operation("Renderer::draw");
apply_state();
- // Until VertexArray acquires VAO support and becomes Bindable
- if(vertex_array_changed)
- {
- vertex_array->apply();
- vertex_array_changed = false;
- }
+ vertex_array->apply();
if(element_buffer)
element_buffer->bind_to(ELEMENT_ARRAY_BUFFER);
void Renderer::apply_state()
{
- // We let the objects themselves figure out if the binding has changed
+ /* We (mostly) let the objects themselves figure out if the binding has
+ changed */
if(state->texturing)
state->texturing->bind();
else
Material::unbind();
+ if(lighting_changed)
+ {
+ if(state->lighting)
+ {
+ MatrixStack::modelview() = state->lighting_matrix;
+ state->lighting->bind();
+ mtx_changed = true;
+ lighting_changed = false;
+ }
+ else
+ Lighting::unbind();
+ }
+
if(state->shprog)
{
state->shprog->bind();
- for(vector<const ProgramData *>::const_iterator i=state->shdata.begin(); i!=state->shdata.end(); ++i)
- (*i)->apply();
+ if(shdata_changed)
+ {
+ for(vector<const ProgramData *>::const_iterator i=shdata_stack.begin(); i!=shdata_stack.end(); ++i)
+ (*i)->apply();
+ shdata_changed = false;
+ }
}
else
Program::unbind();
if(state->winding_test)
- state->winding_test->bind();
+ {
+ if(state->reverse_winding)
+ state->winding_test->get_reverse().bind();
+ else
+ state->winding_test->bind();
+ }
else
WindingTest::unbind();
texture(0),
texturing(0),
material(0),
+ lighting(0),
shprog(0),
+ shdata_count(0),
winding_test(0)
{ }