Files
OpenSceneGraph/src/osgPlugins/lwo/Lwo2.cpp
Robert Osfield 2ac175484a Updates from Pavel.
1. Storing color of geometry in ColorArray e.g. its don't work when
    lighting off.

  2.  Detecting of alpha channel and automatically enabling blending.
2003-01-20 10:41:21 +00:00

792 lines
18 KiB
C++

/*
* Lightwave Object version 2 loader for Open Scene Graph
* Version 2 introduced in Lightwave v6.0
*
* Copyright (C) 2002 Pavel Moloshtan <pasha@moloshtan.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* The Open Scene Graph (OSG) is a cross platform C++/OpenGL library for
* real-time rendering of large 3D photo-realistic models.
* The OSG homepage is http://www.openscenegraph.org/
*/
#include <osg/Notify>
#include <osg/Geode>
#include <osg/Group>
#include <osg/Texture2D>
#include <osg/Material>
#include <osg/CullFace>
#include <osg/BlendFunc>
#include <osgDB/Registry>
#include <osgDB/ReadFile>
#include <iostream>
#include <fstream>
#include "Lwo2.h"
#include "Lwo2Layer.h"
Lwo2::Lwo2():
_current_layer(0),
_successfully_read(false)
{
}
Lwo2::~Lwo2()
{
// delete all layers
for (IteratorLayers itr = _layers.begin(); itr != _layers.end(); itr++)
{
delete (*itr).second;
}
// delete all surfaces
for (IteratorSurfaces itr_surf = _surfaces.begin(); itr_surf != _surfaces.end(); itr_surf++)
{
delete (*itr_surf).second;
}
};
bool
Lwo2::ReadFile( const string& filename )
{
notify(INFO) << "Opening file: " << filename << std::endl;
_fin.open(filename.c_str(), ios::in | ios::binary );
if (!_fin.is_open())
{
notify(INFO) << "Can't open file '" << filename << "'" << std::endl;
return false;
}
// checking EA-IFF85 format
// http://www.lightwave3d.com/developer/75lwsdk/docs/filefmts/eaiff85.html
if (_read_long() != tag_FORM)
{
notify(INFO) << "File '" << filename << "' is not IFF format file." << std::endl;
_fin.close();
return false;
}
else
{
notify(INFO) << "Detected EA-IFF85 format" << std::endl;
}
unsigned long form_size = _read_long();
notify(INFO) << "Form size: " << form_size << std::endl;
// checking LWO2 format
// http://www.lightwave3d.com/developer/75lwsdk/docs/filefmts/lwo2.html
if (_read_long() != tag_LWO2)
{
unsigned long make_id(const char*);
notify(INFO) << "File '" << filename << "' is not LWO2 format file." << std::endl;
_fin.close();
return false;
}
else
{
notify(INFO) << "Detected LWO2 format" << std::endl;
}
unsigned long read_bytes = 4;
unsigned long current_tag_name;
unsigned long current_tag_size;
// main loop for reading tags
while (read_bytes < form_size && !_fin.eof()) {
current_tag_name = _read_long();
current_tag_size = _read_long();
read_bytes += 8 + current_tag_size + current_tag_size % 2;
_print_tag(current_tag_name, current_tag_size);
if (current_tag_name == tag_TAGS)
{
_read_tag_strings(current_tag_size);
}
else if (current_tag_name == tag_LAYR)
{
_read_layer(current_tag_size);
}
else if (current_tag_name == tag_PNTS)
{
_read_points(current_tag_size);
}
else if (current_tag_name == tag_VMAP)
{
_read_vertex_mapping(current_tag_size);
}
else if (current_tag_name == tag_VMAD)
{
_read_polygons_mapping(current_tag_size);
}
else if (current_tag_name == tag_POLS)
{
_read_polygons(current_tag_size);
}
else if (current_tag_name == tag_PTAG)
{
_read_polygon_tag_mapping(current_tag_size);
}
else if (current_tag_name == tag_CLIP)
{
_read_image_definition(current_tag_size);
}
else if (current_tag_name == tag_SURF)
{
_read_surface(current_tag_size);
}
else
{
_fin.seekg(current_tag_size + current_tag_size % 2, ios::cur);
}
}
_fin.close();
return _successfully_read = true;
}
unsigned char
Lwo2::_read_char()
{
char c;
if (_fin.is_open())
{
_fin.read(&c, 1);
}
return static_cast<unsigned char>(c);
}
unsigned long
Lwo2::_read_long()
{
return
(_read_char() << 24) |
(_read_char() << 16) |
(_read_char() << 8) |
_read_char();
}
unsigned short
Lwo2::_read_short()
{
return
(_read_char() << 8) |
_read_char();
}
float
Lwo2::_read_float()
{
unsigned long x = _read_long();
return *(float*)&x;
}
// read null terminated string
string&
Lwo2::_read_string(string& str)
{
char c;
do {
c = _read_char();
str += c;
} while (c != 0);
// if length of string (including \0) is odd skip another byte
if (str.length() % 2) {
_read_char();
}
return str;
}
// print 4-char tag to debug out
void
Lwo2::_print_tag(unsigned int tag, unsigned int size) {
notify(DEBUG_INFO) << "Found tag "
<< char(tag >> 24)
<< char(tag >> 16)
<< char(tag >> 8)
<< char(tag)
<< " size " << size << " bytes"
<< std::endl;
}
// print 4-char type
void
Lwo2::_print_type(unsigned int type) {
notify(DEBUG_INFO) << " type \t"
<< char(type >> 24)
<< char(type >> 16)
<< char(type >> 8)
<< char(type)
<< std::endl;
}
// read TAGS info
void
Lwo2::_read_tag_strings(unsigned long size)
{
while (size > 0)
{
string name;
_read_string(name);
size -= name.length() + name.length() % 2;
_tags.push_back(name);
notify(DEBUG_INFO) << " name \t'" << name.c_str() << "'" << std::endl;
}
}
// read LAYR info
void
Lwo2::_read_layer(unsigned long size)
{
unsigned short number = _read_short();
size -= 2;
Lwo2Layer* layer = new Lwo2Layer();
_layers[number] = layer;
_current_layer = layer;
layer->_number = number;
layer->_flags = _read_short();
size -= 2;
float x = _read_float();
float y = _read_float();
float z = _read_float();
layer->_pivot.set(x, y, z);
size -= 4 * 3;
_read_string(layer->_name);
size -= layer->_name.length() + layer->_name.length() % 2;
if (size > 2) {
layer->_parent = _read_short();
size -= 2;
}
_fin.seekg(size + size % 2, ios::cur);
}
// read PNTS info
void
Lwo2::_read_points(unsigned long size)
{
int count = size / 12;
notify(DEBUG_INFO) << " count \t" << count << std::endl;
while (count--)
{
PointData point;
float x = _read_float();
float y = _read_float();
float z = _read_float();
point.coord = Vec3(x, y, z);
_current_layer->_points.push_back(point);
}
}
// read VMAP info
void
Lwo2::_read_vertex_mapping(unsigned long size)
{
unsigned int type = _read_long();
size -= 4;
_print_type(type);
short dimension = _read_short();
size -= 2;
notify(DEBUG_INFO) << " dimension \t" << dimension << std::endl;
string name;
_read_string(name);
size -= name.length() + name.length() % 2;
notify(DEBUG_INFO) << " name \t'" << name.c_str() << "'" << std::endl;
if (type == tag_TXUV && dimension == 2)
{
int count = size / 10;
unsigned short n;
float u;
float v;
while (count--)
{
n = _read_short();
u = _read_float();
v = _read_float();
// point coords must be read previously
if (n < _current_layer->_points.size())
{
_current_layer->_points[n].texcoord = Vec2(u, v);
}
}
}
else
{
// not recognized yet
notify(DEBUG_INFO) << " skipping..." << std::endl;
_fin.seekg(size + size % 2, ios::cur);
}
}
// read POLS info
void
Lwo2::_read_polygons(unsigned long size)
{
unsigned int type = _read_long();
size -= 4;
_print_type(type);
if (type == tag_FACE)
{
unsigned short vertex_count;
while (size > 0)
{
PointData point;
vertex_count = _read_short() & 0x03FF;
size -= 2;
PointsList points_list;
while (vertex_count--)
{
unsigned short point_index = _read_short();
point = _current_layer->_points[point_index];
point.point_index = point_index;
points_list.push_back(point);
size -= 2;
}
_current_layer->_polygons.push_back(points_list);
}
}
else
{
// not recognized yet
notify(DEBUG_INFO) << " skipping..." << std::endl;
_fin.seekg(size + size % 2, ios::cur);
}
}
// read PTAG info
void
Lwo2::_read_polygon_tag_mapping(unsigned long size)
{
unsigned int type = _read_long();
size -= 4;
_print_type(type);
if (type == tag_SURF)
{
int count = size / 4;
_current_layer->_polygons_tag.resize(count);
short polygon_index;
short tag_index;
while (count--)
{
polygon_index = _read_short();
tag_index = _read_short();
_current_layer->_polygons_tag[polygon_index] = tag_index;
}
}
else
{
// not recognized yet
notify(DEBUG_INFO) << " skipping..." << std::endl;
_fin.seekg(size + size % 2, ios::cur);
}
}
// read VMAD info
void
Lwo2::_read_polygons_mapping(unsigned long size)
{
unsigned int type = _read_long();
size -= 4;
_print_type(type);
short dimension = _read_short();
size -= 2;
notify(DEBUG_INFO) << " dimension \t" << dimension << std::endl;
string name;
_read_string(name);
size -= name.length() + name.length() % 2;
notify(DEBUG_INFO) << " name \t'" << name.c_str() << "'" << std::endl;
if (type == tag_TXUV && dimension == 2)
{
notify(DEBUG_INFO) << " polygons mappings:" << endl;
notify(DEBUG_INFO) << "\tpoint\tpolygon\ttexcoord" << endl;
notify(DEBUG_INFO) << "\t=====\t=======\t========" << endl;
int count = size / 12;
short point_index;
short polygon_index;
float u;
float v;
while (count--)
{
point_index = _read_short();
polygon_index = _read_short();
u = _read_float();
v = _read_float();
notify(DEBUG_INFO) << " \t" << point_index << "\t" << polygon_index << "\t" << Vec2(u, v) << endl;
// apply texture coordinates
PointsList& points_list = _current_layer->_polygons[polygon_index];
for (unsigned int i = 0; i < points_list.size(); i++)
{
if (points_list[i].point_index == point_index)
{
points_list[i].texcoord = Vec2(u, v);
}
}
}
}
else
{
// not recognized yet
notify(DEBUG_INFO) << " skipping..." << std::endl;
_fin.seekg(size + size % 2, ios::cur);
}
}
// read CLIP info
void
Lwo2::_read_image_definition(unsigned long size)
{
unsigned int index = _read_long();
size -= 4;
notify(DEBUG_INFO) << " index \t" << index << std::endl;
unsigned int type;
while (size > 0)
{
type = _read_long();
size -= 4;
_print_type(type);
// size of name
// not included in specification ??
_read_short();
size -= 2;
string name;
_read_string(name);
size -= name.length() + name.length() % 2;
if (index + 1 > _images.size())
{
_images.resize(index + 1);
}
_images[index] = name.c_str();
notify(DEBUG_INFO) << " name \t'" << name.c_str() << "'" << std::endl;
}
}
// read SURF info
void
Lwo2::_read_surface(unsigned long size)
{
Lwo2Surface* surface = new Lwo2Surface();
surface->image_index = -1;
surface->state_set = NULL;
_read_string(surface->name);
size -= surface->name.length() + surface->name.length() % 2;
notify(DEBUG_INFO) << " name \t'" << surface->name.c_str() << "'" << std::endl;
string source;
_read_string(source);
size -= source.length() + source.length() % 2;
notify(DEBUG_INFO) << " source \t'" << source.c_str() << "'" << std::endl;
unsigned long current_tag_name;
unsigned short current_tag_size;
while (size > 0 && !_fin.eof())
{
current_tag_name = _read_long();
size -= 4;
current_tag_size = _read_short();
size -= 2;
_print_tag(current_tag_name, current_tag_size);
if (current_tag_name == tag_BLOK)
{
// BLOK
int blok_size = current_tag_size;
size -= blok_size;
while (blok_size > 0)
{
current_tag_name = _read_long();
blok_size -= 4;
current_tag_size = _read_short();
blok_size -= 2;
notify(DEBUG_INFO) << " ";
_print_tag(current_tag_name, current_tag_size);
if (current_tag_name == tag_IMAG)
{
surface->image_index = _read_short();
notify(DEBUG_INFO) << " image index\t" << surface->image_index << std::endl;
blok_size -= 2;
}
else if (current_tag_name == tag_IMAP)
{
// IMAP
int imap_size = current_tag_size;
blok_size -= imap_size;
string ordinal;
_read_string(ordinal);
imap_size -= ordinal.length() + ordinal.length() % 2;
notify(DEBUG_INFO) << " ordinal \t'" << ordinal.c_str() << "'" << std::endl;
while(imap_size > 0)
{
current_tag_name = _read_long();
imap_size -= 4;
current_tag_size = _read_short();
imap_size -= 2;
notify(DEBUG_INFO) << " ";
_print_tag(current_tag_name, current_tag_size);
_fin.seekg(current_tag_size + current_tag_size % 2, ios::cur);
imap_size -= current_tag_size + current_tag_size % 2;
}
}
else
{
_fin.seekg(current_tag_size + current_tag_size % 2, ios::cur);
blok_size -= current_tag_size + current_tag_size % 2;
}
}
}
else if (current_tag_name == tag_COLR)
{
float r = _read_float();
float g = _read_float();
float b = _read_float();
surface->color.set(r,g,b);
notify(DEBUG_INFO) << " color \t" << surface->color << std::endl;
current_tag_size -= 12;
size -= 12;
// skip ununderstooded envelope
_fin.seekg(current_tag_size + current_tag_size % 2, ios::cur);
size -= current_tag_size + current_tag_size % 2;
}
else
{
_fin.seekg(current_tag_size + current_tag_size % 2, ios::cur);
size -= current_tag_size + current_tag_size % 2;
}
}
_surfaces[surface->name] = surface;
}
// Generation OSG Geode object from parsed LWO2 file
bool
Lwo2::GenerateGroup( Group& group )
{
if (!_successfully_read) return false;
// generate StateSets for each surface
_generate_statesets_from_surfaces();
// create geometry from all layers
for (IteratorLayers itr = _layers.begin(); itr != _layers.end(); itr++)
{
osg::Geode* geode = new osg::Geode();
notify(DEBUG_INFO) << "Generate geode for layer " << (*itr).first << std::endl;
DrawableToTagMapping tag_mapping;
(*itr).second->GenerateGeode(*geode, _tags.size(), tag_mapping);
// assign StateSet for each PTAG group
for (unsigned int i = 0; i < geode->getNumDrawables(); i++)
{
notify(DEBUG_INFO) << " Assigning surface " << _tags[tag_mapping[i]] << " to drawable " << i << std::endl;
geode->getDrawable(i)->setStateSet(_surfaces[_tags[tag_mapping[i]]]->state_set);
// copy material color to color array of geometry
// because when lighting off color not applyed
Geometry* geometry = geode->getDrawable(i)->asGeometry();
if (geometry)
{
Material* material = dynamic_cast<Material*>(_surfaces[_tags[tag_mapping[i]]]->state_set->getAttribute(StateAttribute::MATERIAL));
if (material) {
Vec4Array* colors = new Vec4Array();
colors->push_back(material->getDiffuse(Material::FRONT_AND_BACK));
geometry->setColorBinding(Geometry::BIND_OVERALL);
geometry->setColorArray(colors);
}
}
}
group.addChild(geode);
}
return true;
}
// generate StateSets for each surface
void
Lwo2::_generate_statesets_from_surfaces()
{
ref_ptr<BlendFunc> blending = new BlendFunc();
blending->setFunction(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
ref_ptr<CullFace> culling = new CullFace();
culling->setMode(CullFace::BACK);
for (IteratorSurfaces itr_surf = _surfaces.begin(); itr_surf != _surfaces.end(); itr_surf++)
{
Lwo2Surface* surface = (*itr_surf).second;
StateSet* state_set = new osg::StateSet;
bool use_blending = false;
notify(DEBUG_INFO) << "\tcreating surface " << (*itr_surf).first << std::endl;
// check if exist texture image for this surface
if (surface->image_index >= 0)
{
Image* image = osgDB::readImageFile(_images[surface->image_index]);
notify(DEBUG_INFO) << "\tloaded image '" << _images[surface->image_index] << "'" << std::endl;
notify(DEBUG_INFO) << "\tresult - " << image << std::endl;
if (image)
{
// create texture
Texture2D* texture = new osg::Texture2D;
texture->setImage(image);
state_set->setTextureAttributeAndModes(0, texture, StateAttribute::ON);
// setup texture wrapping
texture->setWrap(Texture::WRAP_S, Texture::REPEAT);
texture->setWrap(Texture::WRAP_T, Texture::REPEAT);
// detect blending
if (image->getPixelSizeInBits() == 32)
{
for (int i = 0; i < image->s(); i++)
{
for (int j = 0; j < image->t(); j++)
{
unsigned char* data = image->data(i, j);
data++; // skip r
data++; // skip g
data++; // skip b
// check alpha
if (*data < 255)
{
use_blending = true;
break;
}
}
if (use_blending) break;
}
}
}
}
// set color
Material* material = new Material();
material->setDiffuse(Material::FRONT_AND_BACK, Vec4(surface->color, 1.0f));
state_set->setAttribute(material);
state_set->setMode(GL_NORMALIZE, StateAttribute::ON);
if (use_blending)
{
// setup blending
state_set->setAttribute(blending.get());
state_set->setMode(GL_BLEND, StateAttribute::ON);
// setup depth sorting
state_set->setRenderingHint(StateSet::TRANSPARENT_BIN);
}
else
{
// setup culling
state_set->setAttribute(culling.get());
state_set->setMode(GL_CULL_FACE, StateAttribute::ON);
}
surface->state_set = state_set;
}
}
// makes 4-byte integer tag from four chars
// used in IFF standard
unsigned long
make_id(const char* tag)
{
unsigned long result = 0;
for (unsigned int i = 0; i < strlen(tag) && i < 4; i++)
{
result <<= 8;
result += int(tag[i]);
}
return result;
}