+/* $Id$
+
+This file is part of libmspgl
+Copyright © 2007 Mikko Rasa, Mikkosoft Productions
+Distributed under the LGPL
+*/
+
+#include <msp/core/refptr.h>
+#include <msp/datafile/collection.h>
+#include <msp/strings/formatter.h>
+#include "material.h"
+#include "renderpass.h"
+#include "program.h"
+#include "programdata.h"
+#include "texture.h"
+
+using namespace std;
+
+namespace Msp {
+namespace GL {
+
+const RenderPass *RenderPass::current=0;
+
+RenderPass::RenderPass():
+ shprog(0),
+ shdata(0),
+ own_material(false),
+ material(0)
+{ }
+
+RenderPass::RenderPass(const RenderPass &other):
+ shprog(other.shprog),
+ shdata(other.shdata ? new ProgramData(*other.shdata) : 0),
+ own_material(other.own_material),
+ material(own_material ? new Material(*other.material) : other.material),
+ textures(other.textures)
+{ }
+
+/*RenderPass &RenderPass::operator=(const RenderPass &other)
+{
+ shprog=other.shprog;
+ delete shdata;
+ shdata=(other.shdata ? new ProgramData(*other.shdata) : 0);
+ material=other.material;
+ use_material=other.use_material;
+ textures=other.textures;
+ use_textures=other.use_textures;
+
+ return *this;
+}*/
+
+RenderPass::~RenderPass()
+{
+ delete shdata;
+ if(own_material)
+ delete material;
+}
+
+void RenderPass::set_material(const Material *mat)
+{
+ material=mat;
+}
+
+unsigned RenderPass::get_texture_index(const string &slot) const
+{
+ for(unsigned i=0; i<textures.size(); ++i)
+ if(textures[i].name==slot)
+ return i;
+
+ throw KeyError("Unknown texture slot", slot);
+}
+
+void RenderPass::set_texture(const string &slot, const Texture *tex)
+{
+ textures[get_texture_index(slot)]=tex;
+}
+
+void RenderPass::bind() const
+{
+ if(this==current)
+ return;
+
+ const RenderPass *old=current;
+ current=this;
+
+ if(shprog)
+ {
+ shprog->bind();
+ shdata->apply();
+ }
+ else if(old && !old->shprog)
+ GL::Program::unbind();
+
+ if(material)
+ material->bind();
+ else if(old && !old->material)
+ GL::Material::unbind();
+
+ for(unsigned i=0; i<textures.size(); ++i)
+ if(textures[i].texture)
+ textures[i].texture->bind_to(i);
+ if(old)
+ {
+ for(unsigned i=textures.size(); i<old->textures.size(); ++i)
+ GL::Texture::unbind_from(i);
+ }
+}
+
+void RenderPass::unbind()
+{
+ if(current)
+ {
+ if(current->shprog)
+ GL::Program::unbind();
+
+ if(current->material)
+ GL::Material::unbind();
+
+ for(unsigned i=current->textures.size(); i--; )
+ GL::Texture::unbind_from(i);
+
+ current=0;
+ }
+}
+
+
+RenderPass::Loader::Loader(RenderPass &p, Collection &c):
+ DataFile::CollectionObjectLoader<RenderPass>(p, &c)
+{
+ add("shader", &RenderPass::shprog);
+ add("material", &Loader::material);
+ add("material", &RenderPass::material);
+ add("texture", &Loader::texture);
+ add("uniforms", &Loader::uniforms);
+}
+
+void RenderPass::Loader::finish()
+{
+ if(obj.shprog)
+ {
+ if(!obj.shdata)
+ obj.shdata=new ProgramData;
+
+ for(unsigned i=0; i<obj.textures.size(); ++i)
+ {
+ unsigned loc=obj.shprog->get_uniform_location(obj.textures[i].name);
+ obj.shdata->uniform(loc, static_cast<int>(i));
+ }
+ }
+}
+
+void RenderPass::Loader::material()
+{
+ // XXX Potential memory management trouble with multiple material statements
+ RefPtr<Material> mat=new Material;
+ load_sub(*mat);
+ obj.material=mat.release();
+ obj.own_material=true;
+}
+
+void RenderPass::Loader::texture(const string &n)
+{
+ const Texture *tex=(n.empty() ? 0 : get_collection().get<Texture>(n));
+ TextureSlot slot(tex);
+ slot.name=(obj.textures.empty() ? "texture" : format("texture%d", obj.textures.size()));
+ load_sub(slot);
+ obj.textures.push_back(slot);
+}
+
+void RenderPass::Loader::uniforms()
+{
+ if(!obj.shprog)
+ throw InvalidState("Can't load uniforms without a shader program");
+ if(!obj.shdata)
+ obj.shdata=new ProgramData;
+ load_sub(*obj.shdata, *obj.shprog);
+}
+
+
+RenderPass::TextureSlot::TextureSlot(const Texture *t):
+ texture(t)
+{ }
+
+
+RenderPass::TextureSlot::Loader::Loader(TextureSlot &s):
+ DataFile::ObjectLoader<TextureSlot>(s)
+{
+ add("name", &TextureSlot::name);
+}
+
+} // namespace GL
+} // namespace Msp