Files
OpenSceneGraph/src/osgPlugins/lwo/Converter.cpp
Robert Osfield 43f0efd6d2 Reorganised the Group::removeChild and Geode::removeDrawable methods so
that removeChild(Node*), removeChild(uint) and equivilant Geode methods are
now inline methods, not designed to be overriden, and seperated out the
multiple remove method to be called removeChildren(uint, uint) which is
now the only virtual method.  There removeChildren is now the method to
override in subclasses.

This reorganisation requires some call code to be rename removeChild usage
to removeChildren.
2006-05-02 09:45:31 +00:00

306 lines
11 KiB
C++

/*******************************************************
Lightwave Object Loader for OSG
Copyright (C) 2004 Marco Jez <marco.jez@poste.it>
OpenSceneGraph is (C) 2004 Robert Osfield
********************************************************/
#include "Converter.h"
#include "Tessellator.h"
#include <osg/Notify>
#include <osg/Geode>
#include <osg/Geometry>
#include <osg/CullFace>
#include <osg/LightModel>
#include <osgDB/FileUtils>
#include "lwo2parser.h"
#include <fstream>
using namespace lwosg;
namespace
{
struct GeometryBin {
osg::ref_ptr<osg::DrawElementsUInt> deui_points;
osg::ref_ptr<osg::DrawElementsUInt> deui_lines;
osg::ref_ptr<osg::DrawElementsUInt> deui_triangles;
GeometryBin()
: deui_points(new osg::DrawElementsUInt(GL_POINTS)),
deui_lines(new osg::DrawElementsUInt(GL_LINES)),
deui_triangles(new osg::DrawElementsUInt(GL_TRIANGLES))
{}
};
}
Converter::Converter()
: root_(new osg::Group)
{
}
Converter::Converter(const Options &options, const osgDB::ReaderWriter::Options* db_options)
: root_(new osg::Group),
options_(options),
db_options_(db_options)
{
}
osg::Group *Converter::convert(Object &obj)
{
if (root_->getNumChildren() > 0) {
root_->removeChildren(0, root_->getNumChildren());
}
osg::notify(osg::INFO) << "INFO: lwosg::Converter: flattening per-polygon vertex maps\n";
for (Object::Layer_map::iterator i=obj.layers().begin(); i!=obj.layers().end(); ++i) {
for (Layer::Unit_list::iterator j=i->second.units().begin(); j!=i->second.units().end(); ++j) {
j->flatten_maps();
}
}
osg::notify(osg::INFO) << "INFO: lwosg::Converter: creating scene graph\n";
build_scene_graph(obj);
return root_.get();
}
void Converter::build_scene_graph(Object &obj)
{
// generate layer structure
typedef std::map<int, osg::ref_ptr<osg::Group> > Layer_group_map;
Layer_group_map lymap;
osg::notify(osg::DEBUG_INFO) << "DEBUG INFO: lwosg::Converter: creating layer structure\n";
// create a flat layer structure, no parenting since it's handled in scene files
for (Object::Layer_map::const_iterator i=obj.layers().begin(); i!=obj.layers().end(); ++i) {
const Layer &layer = i->second;
osg::ref_ptr<osg::Group> new_group = new osg::Group;
lymap[layer.number()] = new_group.get();
if (layer.get_layer_chunk()) {
new_group->setName(layer.get_layer_chunk()->name);
if (layer.get_layer_chunk()->flags & 1) {
new_group->setNodeMask(0);
}
} else {
new_group->setName("Default_layer");
}
root_->addChild(new_group.get());
}
for (Object::Layer_map::iterator li=obj.layers().begin(); li!=obj.layers().end(); ++li) {
Layer &layer = li->second;
osg::Group *layer_group = lymap[layer.number()].get();
osg::notify(osg::DEBUG_INFO) << "DEBUG INFO: lwosg::Converter: processing layer '" << layer_group->getName() << "'\n";
for (Layer::Unit_list::iterator j=layer.units().begin(); j!=layer.units().end(); ++j) {
osg::notify(osg::DEBUG_INFO) << "DEBUG INFO: lwosg::Converter: \tcreating primitives\n";
int tess_success = 0;
int tess_fail = 0;
typedef std::map<const Surface *, GeometryBin> GeometryBin_map;
GeometryBin_map bins;
typedef std::map<const Surface *, Unit::Index_list> Remapping_map;
Remapping_map remappings;
// compute remapping map for default surface
j->compute_vertex_remapping(0, remappings[0]);
// compute remapping maps for other surfaces
for (Object::Surface_map::const_iterator h=obj.surfaces().begin(); h!=obj.surfaces().end(); ++h) {
j->compute_vertex_remapping(&h->second, remappings[&h->second]);
}
// create primitive sets, taking into account remapping maps
for (unsigned k=0; k<j->polygons().size(); ++k) {
const Polygon &poly = j->polygons()[k];
GeometryBin &bin = bins[poly.get_surface()];
const Unit::Index_list &remapping = remappings[poly.get_surface()];
if (poly.indices().size() == 1) {
bin.deui_points->push_back(remapping[poly.indices()[0]]);
}
if (poly.indices().size() == 2) {
bin.deui_lines->push_back(remapping[poly.indices()[0]]);
bin.deui_lines->push_back(remapping[poly.indices()[1]]);
}
if (poly.indices().size() == 3) {
bin.deui_triangles->push_back(remapping[poly.indices()[0]]);
bin.deui_triangles->push_back(remapping[poly.indices()[1]]);
bin.deui_triangles->push_back(remapping[poly.indices()[2]]);
}
if (poly.indices().size() >= 4) {
Tessellator tess;
if (tess.tessellate(poly, j->points(), bin.deui_triangles.get(), &remapping)) {
++tess_success;
} else {
++tess_fail;
}
}
}
if (tess_success > 0) {
osg::notify(osg::DEBUG_INFO) << "DEBUG INFO: lwosg::Converter: " << tess_success << " polygons have been tessellated correctly\n";
}
if (tess_fail > 0) {
osg::notify(osg::WARN) << "Warning: lwosg::Converter: could not tessellate " << tess_fail << " polygons correctly. This is probably due to self-intersecting polygons being used, try to Triple them in Lightwave and restart the conversion" << std::endl;
}
// create normal array
osg::ref_ptr<osg::Vec3Array> normals = j->normals()->asVec3Array(j->points()->size());
// create first geode
osg::ref_ptr<osg::Geode> geode = new osg::Geode;
for (GeometryBin_map::iterator i=bins.begin(); i!=bins.end(); ++i) {
const Surface *surface = i->first;
GeometryBin &bin = i->second;
const Unit::Index_list &remapping = remappings[surface];
// clean up points and normals according to remapping map
osg::notify(osg::DEBUG_INFO) << "DEBUG INFO: lwosg::Converter: \tcleaning up redundant vertices and vertex attributes for surface '" << (surface ? surface->get_name() : std::string("anonymous")) << "'\n";
osg::ref_ptr<osg::Vec3Array> new_points = new osg::Vec3Array;
osg::ref_ptr<osg::Vec3Array> new_normals = new osg::Vec3Array;
for (unsigned pi=0; pi<j->points()->size(); ++pi) {
if (remapping[pi] != -1) {
new_points->push_back((*j->points())[pi]);
new_normals->push_back((*normals)[pi]);
}
}
osg::notify(osg::DEBUG_INFO) << "DEBUG INFO: lwosg::Converter: \tcreating geometry for surface '" << (surface ? surface->get_name() : std::string("anonymous")) << "'\n";
osg::ref_ptr<osg::Geometry> geo = new osg::Geometry;
geo->setVertexArray(new_points.get());
geo->setNormalArray(new_normals.get());
geo->setNormalBinding(osg::Geometry::BIND_PER_VERTEX);
bool group_used = false;
if (surface) {
if (!options_.combine_geodes)
{
geode->setName(surface->get_name());
}
// apply surface parameters and texture/color maps according to remapping map
osg::ref_ptr<VertexMap_map> rm_texture_maps = j->texture_maps()->remap(remapping);
osg::ref_ptr<VertexMap_map> rm_rgb_maps = j->rgb_maps()->remap(remapping);
osg::ref_ptr<VertexMap_map> rm_rgba_maps = j->rgba_maps()->remap(remapping);
osg::Group *sgrp = surface->apply(geo.get(),
rm_texture_maps.get(),
rm_rgb_maps.get(),
rm_rgba_maps.get(),
options_.max_tex_units,
options_.use_osgfx,
options_.force_arb_compression,
options_.texturemap_bindings,
db_options_.get());
if (sgrp)
{
group_used = true;
osg::ref_ptr<osg::Geode> grp_geode = new osg::Geode;
grp_geode->setName(surface->get_name());
grp_geode->addDrawable(geo.get());
sgrp->addChild(grp_geode.get());
layer_group->addChild(sgrp);
}
}
if (!group_used)
{
geode->addDrawable(geo.get());
if (geode->getNumParents() == 0)
{
layer_group->addChild(geode.get());
}
}
if (!options_.combine_geodes)
{
geode = new osg::Geode;
}
// add primitive sets to geometry
if (!bin.deui_points->empty()) geo->addPrimitiveSet(bin.deui_points.get());
if (!bin.deui_lines->empty()) geo->addPrimitiveSet(bin.deui_lines.get());
if (!bin.deui_triangles->empty()) geo->addPrimitiveSet(bin.deui_triangles.get());
}
}
osg::ref_ptr<osg::CullFace> cf = new osg::CullFace;
cf->setMode(osg::CullFace::BACK);
root_->getOrCreateStateSet()->setAttributeAndModes(cf.get());
if (options_.apply_light_model) {
osg::ref_ptr<osg::LightModel> lm = new osg::LightModel;
lm->setTwoSided(true);
lm->setColorControl(osg::LightModel::SEPARATE_SPECULAR_COLOR);
lm->setAmbientIntensity(osg::Vec4(0, 0, 0, 0));
lm->setLocalViewer(true);
root_->getOrCreateStateSet()->setAttributeAndModes(lm.get());
}
}
}
osg::Group *Converter::convert(const iff::Chunk_list &data)
{
Object obj(data);
obj.set_coordinate_system_fixer(options_.csf.get());
return convert(obj);
}
osg::Group *Converter::convert(const std::string &filename)
{
std::string file = osgDB::findDataFile(filename, db_options_.get());
if (file.empty()) return 0;
std::ifstream ifs(file.c_str(), std::ios_base::in | std::ios_base::binary);
if (!ifs.is_open()) return 0;
std::vector<char> buffer;
char c;
while (ifs.get(c)) buffer.push_back(c);
lwo2::Parser<std::vector<char>::const_iterator > parser(osg::notify(osg::DEBUG_INFO));
try
{
parser.parse(buffer.begin(), buffer.end());
}
catch(lwo2::parser_error &e)
{
std::cerr << e.what() << std::endl;
return 0;
}
for (iff::Chunk_list::const_iterator i=parser.chunks().begin(); i!=parser.chunks().end(); ++i) {
const lwo2::FORM *form = dynamic_cast<const lwo2::FORM *>(*i);
if (form) {
Object obj(form->data);
obj.set_coordinate_system_fixer(options_.csf.get());
if (!convert(obj)) {
return 0;
}
root_->setName(file);
return root_.get();
}
}
return 0;
}