Files
OpenSceneGraph/src/osgPlugins/OpenFlight/GeometryRecords.cpp
Robert Osfield 927dfc0a52 From Brede Johansen,
"Geometry.cpp
Make sure number of normals match number of vertices when lit or
vertex-normal pairs are separated when geometries are merged by the
optimizer.

Ancillary.cpp
Improved support for multitexture effect field and use texture
environment from .attr file.

PaletteRecords.cpp
Use search path when looking for shader files.

PrimaryRecords.cpp
Added preset uniforms "TextureUnit0", "TextureUnit1", "TextureUnit2"
and "TextureUnit3" for GLSL shaders."
2007-02-13 14:19:39 +00:00

1280 lines
40 KiB
C++

//
// OpenFlight® loader for OpenSceneGraph
//
// Copyright (C) 2005-2006 Brede Johansen
//
#include <assert.h>
#include <osg/Geode>
#include <osg/Billboard>
#include <osg/Geometry>
#include <osg/Texture2D>
#include <osg/CullFace>
#include <osg/PolygonOffset>
#include <osg/Depth>
#include <osg/BlendFunc>
#include <osgUtil/TransformAttributeFunctor>
#include "Registry.h"
#include "Document.h"
#include "RecordInputStream.h"
namespace flt {
/* Face record
*/
class Face : public PrimaryRecord
{
// flags
static const unsigned int TERRAIN_BIT = 0x80000000u >> 0;
static const unsigned int NO_COLOR_BIT = 0x80000000u >> 1;
static const unsigned int NO_ALT_COLOR_BIT = 0x80000000u >> 2;
static const unsigned int PACKED_COLOR_BIT = 0x80000000u >> 3;
static const unsigned int FOOTPRINT_BIT = 0x80000000u >> 4; // Terrain culture cutout
static const unsigned int HIDDEN_BIT = 0x80000000u >> 5;
static const unsigned int ROOFLINE_BIT = 0x80000000u >> 6;
osg::Vec4 _primaryColor;
uint8 _drawFlag;
uint8 _template;
uint16 _transparency;
uint32 _flags;
uint8 _lightMode;
osg::ref_ptr<osg::Geode> _geode;
osg::ref_ptr<osg::Geometry> _geometry;
public:
Face() :
_primaryColor(1,1,1,1),
_drawFlag(SOLID_NO_BACKFACE),
_template(FIXED_NO_ALPHA_BLENDING),
_transparency(0),
_flags(0),
_lightMode(FACE_COLOR)
{
}
META_Record(Face)
META_setID(_geode)
META_setComment(_geode)
META_setMatrix(_geode)
META_setMultitexture(_geode)
// draw mode
enum DrawMode
{
SOLID_BACKFACED = 0,
SOLID_NO_BACKFACE = 1,
WIREFRAME_CLOSED = 2,
WIREFRAME_NOT_CLOSED = 3,
SURROUND_ALTERNATE_COLOR = 4,
OMNIDIRECTIONAL_LIGHT = 8,
UNIDIRECTIONAL_LIGHT = 9,
BIDIRECTIONAL_LIGHT = 10
};
inline DrawMode getDrawMode() const { return (DrawMode)_drawFlag; }
// lighting
enum LightMode
{
FACE_COLOR = 0,
VERTEX_COLOR = 1,
FACE_COLOR_LIGHTING = 2,
VERTEX_COLOR_LIGHTING = 3
};
inline LightMode getLightMode() const { return (LightMode)_lightMode; }
inline bool isLit() const { return (_lightMode==FACE_COLOR_LIGHTING) || (_lightMode==VERTEX_COLOR_LIGHTING); }
inline bool isGouraud() const { return (_lightMode==VERTEX_COLOR) || (_lightMode==VERTEX_COLOR_LIGHTING); }
// flags
inline bool noColor() const { return (_flags & NO_COLOR_BIT)!=0; }
inline bool isHidden() const { return (_flags & HIDDEN_BIT)!=0; }
inline bool isTerrain() const { return (_flags & TERRAIN_BIT)!=0; }
inline bool isFootprint() const { return (_flags & FOOTPRINT_BIT)!=0; }
inline bool isRoofline() const { return (_flags & ROOFLINE_BIT)!=0; }
inline bool packedColorMode() const { return (_flags & PACKED_COLOR_BIT)!=0; }
// billboard
enum TemplateMode
{
FIXED_NO_ALPHA_BLENDING = 0,
FIXED_ALPHA_BLENDING = 1,
AXIAL_ROTATE_WITH_ALPHA_BLENDING = 2,
POINT_ROTATE_WITH_ALPHA_BLENDING = 4
};
inline TemplateMode getTemplateMode() const { return (TemplateMode)_template; }
// transparency & alpha
inline bool isAlphaBlend() const
{
return (_template==FIXED_ALPHA_BLENDING) ||
(_template==AXIAL_ROTATE_WITH_ALPHA_BLENDING) ||
(_template==POINT_ROTATE_WITH_ALPHA_BLENDING);
}
inline osg::Vec4 getPrimaryColor() const { return _primaryColor; }
inline float getTransparency() const { return (float)_transparency / 65535.0f; }
inline bool isTransparent() const { return _transparency > 0; }
virtual void addChild(osg::Node& child)
{
// Add subface to parent.
if (_parent.valid())
_parent->addChild(child);
}
virtual void addVertex(Vertex& vertex)
{
osg::Vec3Array* vertices = getOrCreateVertexArray(*_geometry);
vertices->push_back(vertex._coord);
if (isGouraud())
{
osg::Vec4Array* colors = getOrCreateColorArray(*_geometry);
if (vertex.validColor())
{
colors->push_back(vertex._color);
}
else
{
// Use face color if vertex color is -1 in a gouraud polygon.
// http://www.multigen-paradigm.com/ubb/Forum1/HTML/000967.html
colors->push_back(_primaryColor);
}
}
bool strict = false; // prepare for "strict" reader option.
if (strict)
{
if (vertex.validNormal())
{
osg::Vec3Array* normals = getOrCreateNormalArray(*_geometry);
normals->push_back(vertex._normal);
}
}
else
{
// Add normal only if lit.
if (isLit())
{
osg::Vec3Array* normals = getOrCreateNormalArray(*_geometry);
if (vertex.validNormal())
normals->push_back(vertex._normal);
else // if lit and no normal in Vertex
{
// Use previous normal if available.
if (normals->empty())
normals->push_back(osg::Vec3(0,0,1));
else
normals->push_back(normals->back());
}
}
}
for (int layer=0; layer<Vertex::MAX_LAYERS; layer++)
{
if (vertex.validUV(layer))
{
osg::Vec2Array* UVs = getOrCreateTextureArray(*_geometry,layer);
UVs->push_back(vertex._uv[layer]);
}
}
}
virtual void addVertexUV(int unit, const osg::Vec2& uv)
{
osg::Vec2Array* UVs = getOrCreateTextureArray(*_geometry,unit);
UVs->push_back(uv);
}
virtual void addMorphVertex(Vertex& vertex0, Vertex& /*vertex100*/)
{
osg::Vec3Array* vertices = getOrCreateVertexArray(*_geometry);
vertices->push_back(vertex0._coord);
if (isGouraud())
{
osg::Vec4Array* colors = getOrCreateColorArray(*_geometry);
if (vertex0.validColor())
{
colors->push_back(vertex0._color);
}
else
{
// Use face color if vertex color is -1 in a gouraud polygon.
// http://www.multigen-paradigm.com/ubb/Forum1/HTML/000967.html
colors->push_back(_primaryColor);
}
}
if (vertex0.validNormal())
{
osg::Vec3Array* normals = getOrCreateNormalArray(*_geometry);
normals->push_back(vertex0._normal);
}
for (int layer=0; layer<Vertex::MAX_LAYERS; layer++)
{
if (vertex0.validUV(layer))
{
osg::Vec2Array* UVs = getOrCreateTextureArray(*_geometry,layer);
UVs->push_back(vertex0._uv[layer]);
}
}
}
protected:
virtual void readRecord(RecordInputStream& in, Document& document)
{
std::string id = in.readString(8);
/*int32 IRColor =*/ in.readInt32();
/*int16 relativePriority =*/ in.readInt16();
_drawFlag = in.readUInt8(SOLID_NO_BACKFACE);
uint8 texturedWhite = in.readUInt8();
int16 primaryNameIndex = in.readInt16(-1);
/*int16 secondaryNameIndex =*/ in.readInt16(-1);
in.forward(1);
_template = in.readUInt8(FIXED_NO_ALPHA_BLENDING);
/*int detailTexture =*/ in.readInt16(-1);
int textureIndex = in.readInt16(-1);
int materialIndex = in.readInt16(-1);
/*int16 surface =*/ in.readInt16();
/*int16 feature =*/ in.readInt16();
/*int32 IRMaterial =*/ in.readInt32(-1);
_transparency = in.readUInt16(0);
// version > 13
/*uint8 influenceLOD =*/ in.readUInt8();
/*uint8 linestyle =*/ in.readUInt8();
_flags = in.readUInt32(0);
_lightMode = in.readUInt8(FACE_COLOR);
in.forward(7);
osg::Vec4 primaryPackedColor = in.readColor32();
osg::Vec4 secondaryPackedColor = in.readColor32();
// version >= VERSION_15_1
/*int textureMappingIndex =*/ in.readInt16(-1);
in.forward(2);
int primaryColorIndex = in.readInt32(-1);
/*int alternateColorIndex =*/ in.readInt32(-1);
// version >= 16
in.forward(2);
int shaderIndex = in.readInt16(-1);
// Create Geode or Billboard.
switch (_template)
{
case AXIAL_ROTATE_WITH_ALPHA_BLENDING:
{
osg::Billboard* billboard = new osg::Billboard;
billboard->setMode(osg::Billboard::AXIAL_ROT);
_geode = billboard;
}
break;
case POINT_ROTATE_WITH_ALPHA_BLENDING:
{
osg::Billboard* billboard = new osg::Billboard;
billboard->setMode(osg::Billboard::POINT_ROT_WORLD);
_geode = billboard;
}
break;
default:
_geode = new osg::Geode;
}
_geode->setDataVariance(osg::Object::STATIC);
_geode->setName(id);
_geometry = new osg::Geometry;
_geometry->setDataVariance(osg::Object::STATIC);
_geode->addDrawable(_geometry.get());
// StateSet
osg::ref_ptr<osg::StateSet> stateset = new osg::StateSet;
// Hidden
if (isHidden())
_geode->setNodeMask(0);
// Face color
if (texturedWhite!=0 && textureIndex>=0)
{
_primaryColor = osg::Vec4(1,1,1,1);
}
else
{
if (packedColorMode())
{
_primaryColor = primaryPackedColor;
}
else
{
if (document.version() < VERSION_15_1)
_primaryColor = document.getColorPool()->getColor(primaryNameIndex);
else // >= VERSION_15_1
_primaryColor = document.getColorPool()->getColor(primaryColorIndex);
}
}
// Lighting
stateset->setMode(GL_LIGHTING, isLit() ? osg::StateAttribute::ON : osg::StateAttribute::OFF);
// Material
if (isLit() || materialIndex>=0)
{
// MaterialPool will return a "default" material if no material is defined for materialIndex.
// http://www.multigen-paradigm.com/ubb/Forum1/HTML/000228.html
osg::Vec4 col = _primaryColor;
col.a() = 1.0f - getTransparency();
osg::Material* material = document.getOrCreateMaterialPool()->getOrCreateMaterial(materialIndex,col);
stateset->setAttribute(material);
}
// Shaders
if (shaderIndex >= 0)
{
ShaderPool* sp = document.getOrCreateShaderPool();
osg::Program* program = sp->get(shaderIndex);
if (program)
stateset->setAttributeAndModes(program, osg::StateAttribute::ON);
}
// Texture
TexturePool* tp = document.getOrCreateTexturePool();
osg::StateSet* textureStateSet = tp->get(textureIndex);
if (textureStateSet)
{
// Merge face stateset with texture stateset
stateset->merge(*textureStateSet);
}
// Cull face
switch(_drawFlag)
{
case SOLID_BACKFACED: // Enable backface culling
{
static osg::ref_ptr<osg::CullFace> cullFace = new osg::CullFace(osg::CullFace::BACK);
stateset->setAttributeAndModes(cullFace.get(), osg::StateAttribute::ON);
break;
}
case SOLID_NO_BACKFACE: // Disable backface culling
stateset->setMode(GL_CULL_FACE,osg::StateAttribute::OFF);
break;
}
// Subface
if (document.subfaceLevel() > 0)
{
static osg::ref_ptr<osg::PolygonOffset> polygonOffset = new osg::PolygonOffset(-10.0f, -40.0f);
stateset->setAttributeAndModes(polygonOffset.get(), osg::StateAttribute::ON);
static osg::ref_ptr<osg::Depth> depth = new osg::Depth(osg::Depth::LESS, 0.0, 1.0,false);
stateset->setAttribute(depth.get());
stateset->setRenderBinDetails(document.subfaceLevel(),"RenderBin");
}
// A simple share stateset optimization.
static osg::ref_ptr<osg::StateSet> lastStateset;
if (lastStateset.valid() && (stateset->compare(*lastStateset,false)==0))
stateset = lastStateset;
else
lastStateset = stateset;
_geode->setStateSet(stateset.get());
// Add to parent.
if (_parent.valid())
_parent->addChild(*_geode);
}
osg::PrimitiveSet::Mode getPrimitiveSetMode(int numVertices)
{
switch(getDrawMode())
{
case WIREFRAME_NOT_CLOSED:
return osg::PrimitiveSet::LINE_STRIP;
case WIREFRAME_CLOSED:
return osg::PrimitiveSet::LINE_LOOP;
case OMNIDIRECTIONAL_LIGHT:
case UNIDIRECTIONAL_LIGHT:
case BIDIRECTIONAL_LIGHT:
return osg::PrimitiveSet::POINTS;
default: break;
}
switch (numVertices)
{
case 1: return osg::PrimitiveSet::POINTS;
case 2: return osg::PrimitiveSet::LINES;
case 3: return osg::PrimitiveSet::TRIANGLES;
case 4: return osg::PrimitiveSet::QUADS;
default: break;
}
return osg::PrimitiveSet::POLYGON;
}
virtual void popLevel(Document& document)
{
if (_geode.valid())
{
for (unsigned int i=0; i<_geode->getNumDrawables(); ++i)
{
osg::Geometry* geometry = dynamic_cast<osg::Geometry*>(_geode->getDrawable(i));
if (geometry)
{
osg::Array* vertices = geometry->getVertexArray();
if (vertices)
{
GLint first = 0;
GLsizei count = vertices->getNumElements();
osg::PrimitiveSet::Mode mode = getPrimitiveSetMode(count);
geometry->addPrimitiveSet(new osg::DrawArrays(mode,first,count));
}
// Color binding
if (isGouraud())
{
// Color per vertex
geometry->setColorBinding(osg::Geometry::BIND_PER_VERTEX);
}
else
{
// Color per face
osg::Vec4 col = getPrimaryColor();
col[3] = 1.0f - getTransparency();
geometry->setColorBinding(osg::Geometry::BIND_OVERALL);
osg::Vec4Array* colors = new osg::Vec4Array(1);
(*colors)[0] = col;
geometry->setColorArray(colors);
}
// Normal binding
if (isLit())
{
geometry->setNormalBinding(osg::Geometry::BIND_PER_VERTEX );
}
else
{
geometry->setNormalBinding(osg::Geometry::BIND_OFF);
geometry->setNormalArray(NULL);
}
}
}
osg::StateSet* stateset = _geode->getOrCreateStateSet();
// Translucent image?
bool isImageTranslucent = false;
if (document.getUseTextureAlphaForTransparancyBinning())
{
for (unsigned int i=0; i<stateset->getTextureAttributeList().size(); ++i)
{
osg::StateAttribute* sa = stateset->getTextureAttribute(i,osg::StateAttribute::TEXTURE);
osg::Texture2D* texture = dynamic_cast<osg::Texture2D*>(sa);
if (texture)
{
osg::Image* image = texture->getImage();
if (image && image->isImageTranslucent())
isImageTranslucent = true;
}
}
}
// Transparent Material?
bool isMaterialTransparent = false;
osg::Material* material = dynamic_cast<osg::Material*>(stateset->getAttribute(osg::StateAttribute::MATERIAL));
if (material)
{
isMaterialTransparent = material->getDiffuse(osg::Material::FRONT).a() < 0.99f;
}
// Enable alpha blend?
if (isAlphaBlend() || isTransparent() || isImageTranslucent || isMaterialTransparent)
{
static osg::ref_ptr<osg::BlendFunc> blendFunc = new osg::BlendFunc(osg::BlendFunc::SRC_ALPHA, osg::BlendFunc::ONE_MINUS_SRC_ALPHA);
stateset->setAttributeAndModes(blendFunc.get(), osg::StateAttribute::ON);
stateset->setRenderingHint(osg::StateSet::TRANSPARENT_BIN);
}
if (document.getUseBillboardCenter())
{
// Set billboard rotation point to center of face.
osg::Billboard* billboard = dynamic_cast<osg::Billboard*>(_geode.get());
if (billboard)
{
for (unsigned int i=0; i<billboard->getNumDrawables(); ++i)
{
osg::BoundingBox bb = billboard->getDrawable(i)->getBound();
billboard->setPosition(i,bb.center());
osgUtil::TransformAttributeFunctor tf(osg::Matrix::translate(-bb.center()));
billboard->getDrawable(i)->accept(tf);
billboard->getDrawable(i)->dirtyBound();
}
billboard->dirtyBound();
}
}
}
}
};
RegisterRecordProxy<Face> g_Face(FACE_OP);
/** VertexList -
* The VertexList is a leaf record.
* Possible parents: Face, Mesh & LightPoint
*/
class VertexListRecord : public PrimaryRecord
{
public:
VertexListRecord() {}
META_Record(VertexListRecord)
virtual void addVertex(Vertex& vertex)
{
// forward vertex to parent.
if (_parent.valid())
_parent->addVertex(vertex);
}
virtual void addVertexUV(int layer,const osg::Vec2& uv)
{
// forward uv to parent.
if (_parent.valid())
_parent->addVertexUV(layer,uv);
}
protected:
virtual ~VertexListRecord() {}
virtual void readRecord(RecordInputStream& in, Document& document)
{
VertexPool* vp = document.getVertexPool();
if (vp)
{
int vertices = (in.getRecordSize()-4) / 4;
// Use the Vertex pool as a record stream.
RecordInputStream inVP(vp->rdbuf());
for (int n=0; n<vertices; n++)
{
// Get position of vertex.
uint32 pos = in.readUInt32();
// Get vertex from vertex pool.
inVP.seekg((std::istream::pos_type)pos);
inVP.readRecord(document);
}
}
}
};
RegisterRecordProxy<VertexListRecord> g_VertexList(VERTEX_LIST_OP);
/** MorphVertexList -
* The MorphVertexList is a leaf record.
*/
class MorphVertexList : public PrimaryRecord
{
enum Mode
{
UNDEFINED,
MORPH_0,
MORPH_100
};
Mode _mode;
Vertex _vertex0;
Vertex _vertex100;
public:
MorphVertexList():
_mode(UNDEFINED)
{
}
META_Record(MorphVertexList)
virtual void addVertex(Vertex& vertex)
{
switch (_mode)
{
case MORPH_0:
_vertex0 = vertex;
break;
case MORPH_100:
_vertex100 = vertex;
// forward vertex to parent.
if (_parent.valid())
_parent->addMorphVertex(_vertex0, _vertex100);
break;
case UNDEFINED:
break;
}
}
//virtual void addVertexUV(int layer,const osg::Vec2& uv)
//{
// // forward uv to parent.
// if (_parent.valid())
// _parent->addVertexUV(layer,uv);
//}
protected:
virtual ~MorphVertexList() {}
virtual void readRecord(RecordInputStream& in, Document& document)
{
VertexPool* vp = document.getVertexPool();
if (vp)
{
int vertices = (in.getRecordSize()-4) / 8;
// Use the Vertex pool as a record stream.
RecordInputStream inVP(vp->rdbuf());
for (int n=0; n<vertices; n++)
{
// Get position of vertex.
uint32 offset0 = in.readUInt32();
uint32 offset100 = in.readUInt32();
// Get vertex from vertex pool.
// 0%
_mode = MORPH_0;
inVP.seekg((std::istream::pos_type)offset0);
inVP.readRecord(document);
// 100%
_mode = MORPH_100;
inVP.seekg((std::istream::pos_type)offset100);
inVP.readRecord(document);
}
}
}
};
RegisterRecordProxy<MorphVertexList> g_MorphVertexList(MORPH_VERTEX_LIST_OP);
/* Mesh record
*/
class Mesh : public PrimaryRecord
{
// flags
static const unsigned int TERRAIN_BIT = 0x80000000u >> 0;
static const unsigned int NO_COLOR_BIT = 0x80000000u >> 1;
static const unsigned int NO_ALT_COLOR_BIT = 0x80000000u >> 2;
static const unsigned int PACKED_COLOR_BIT = 0x80000000u >> 3;
static const unsigned int FOOTPRINT_BIT = 0x80000000u >> 4; // Terrain culture cutout
static const unsigned int HIDDEN_BIT = 0x80000000u >> 5;
static const unsigned int ROOFLINE_BIT = 0x80000000u >> 6;
osg::Vec4 _primaryColor;
uint8 _drawFlag;
uint8 _template;
uint16 _transparency;
uint32 _flags;
uint8 _lightMode;
osg::ref_ptr<osg::Geode> _geode;
public:
Mesh() :
_primaryColor(1,1,1,1),
_drawFlag(SOLID_NO_BACKFACE),
_template(FIXED_NO_ALPHA_BLENDING),
_transparency(0),
_flags(0),
_lightMode(FACE_COLOR)
{
}
META_Record(Mesh)
META_setID(_geode)
META_setComment(_geode)
META_setMatrix(_geode)
META_setMultitexture(_geode)
// draw mode
enum DrawMode
{
SOLID_BACKFACED = 0,
SOLID_NO_BACKFACE = 1,
WIREFRAME_CLOSED = 2,
WIREFRAME_NOT_CLOSED = 3,
SURROUND_ALTERNATE_COLOR = 4,
OMNIDIRECTIONAL_LIGHT = 8,
UNIDIRECTIONAL_LIGHT = 9,
BIDIRECTIONAL_LIGHT = 10
};
inline DrawMode getDrawMode() const { return (DrawMode)_drawFlag; }
// lighting
enum LightMode
{
FACE_COLOR = 0,
VERTEX_COLOR = 1,
FACE_COLOR_LIGHTING = 2,
VERTEX_COLOR_LIGHTING = 3
};
inline LightMode getLightMode() const { return (LightMode)_lightMode; }
inline bool isLit() const { return (_lightMode==FACE_COLOR_LIGHTING) || (_lightMode==VERTEX_COLOR_LIGHTING); }
inline bool isGouraud() const { return (_lightMode==VERTEX_COLOR) || (_lightMode==VERTEX_COLOR_LIGHTING); }
// flags
inline bool noColor() const { return (_flags & NO_COLOR_BIT)!=0; }
inline bool isHidden() const { return (_flags & HIDDEN_BIT)!=0; }
inline bool isTerrain() const { return (_flags & TERRAIN_BIT)!=0; }
inline bool isFootprint() const { return (_flags & FOOTPRINT_BIT)!=0; }
inline bool isRoofline() const { return (_flags & ROOFLINE_BIT)!=0; }
inline bool packedColorMode() const { return (_flags & PACKED_COLOR_BIT)!=0; }
// billboard
enum TemplateMode
{
FIXED_NO_ALPHA_BLENDING = 0,
FIXED_ALPHA_BLENDING = 1,
AXIAL_ROTATE_WITH_ALPHA_BLENDING = 2,
POINT_ROTATE_WITH_ALPHA_BLENDING = 4
};
inline TemplateMode getTemplateMode() const { return (TemplateMode)_template; }
// transparency & alpha
inline bool isAlphaBlend() const
{
return (_template==FIXED_ALPHA_BLENDING) ||
(_template==AXIAL_ROTATE_WITH_ALPHA_BLENDING) ||
(_template==POINT_ROTATE_WITH_ALPHA_BLENDING);
}
inline osg::Vec4 getPrimaryColor() const { return _primaryColor; }
inline float getTransparency() const { return (float)_transparency / 65535.0f; }
inline bool isTransparent() const { return _transparency > 0; }
virtual void addChild(osg::Node& child)
{
// Add subface to parent.
if (_parent.valid())
_parent->addChild(child);
}
virtual void addGeometry(osg::Geometry& geometry)
{
_geode->addDrawable(&geometry);
}
protected:
virtual void readRecord(RecordInputStream& in, Document& document)
{
std::string id = in.readString(8);
in.forward(4);
/*int32 IRColor =*/ in.readInt32();
/*int16 relativePriority =*/ in.readInt16();
_drawFlag = in.readUInt8(SOLID_NO_BACKFACE);
uint8 texturedWhite = in.readUInt8();
int16 primaryNameIndex = in.readInt16(-1);
/*int16 secondaryNameIndex =*/ in.readInt16(-1);
in.forward(1);
_template = in.readUInt8(FIXED_NO_ALPHA_BLENDING);
/*int detailTexture =*/ in.readInt16(-1);
int textureIndex = in.readInt16(-1);
int materialIndex = in.readInt16(-1);
/*int16 surface =*/ in.readInt16();
/*int16 feature =*/ in.readInt16();
/*int32 IRMaterial =*/ in.readInt32(-1);
_transparency = in.readUInt16(0);
// version > 13
/*uint8 influenceLOD =*/ in.readUInt8();
/*uint8 linestyle =*/ in.readUInt8();
_flags = in.readUInt32(0);
_lightMode = in.readUInt8(FACE_COLOR);
in.forward(7);
osg::Vec4 primaryPackedColor = in.readColor32();
osg::Vec4 secondaryPackedColor = in.readColor32();
// version >= VERSION_15_1
/*int textureMappingIndex =*/ in.readInt16(-1);
in.forward(2);
int primaryColorIndex = in.readInt32(-1);
/*int alternateColorIndex =*/ in.readInt32(-1);
// version >= 16
in.forward(2);
int shaderIndex = in.readInt16(-1);
// Create Geode or Billboard.
switch (_template)
{
case AXIAL_ROTATE_WITH_ALPHA_BLENDING:
{
osg::Billboard* billboard = new osg::Billboard;
billboard->setMode(osg::Billboard::AXIAL_ROT);
_geode = billboard;
}
break;
case POINT_ROTATE_WITH_ALPHA_BLENDING:
{
osg::Billboard* billboard = new osg::Billboard;
billboard->setMode(osg::Billboard::POINT_ROT_WORLD);
_geode = billboard;
}
break;
default:
_geode = new osg::Geode;
}
_geode->setDataVariance(osg::Object::STATIC);
_geode->setName(id);
// StateSet
osg::ref_ptr<osg::StateSet> stateset = new osg::StateSet;
// Hidden
if (isHidden())
_geode->setNodeMask(0);
// Face color
if (texturedWhite!=0 && textureIndex>=0)
{
_primaryColor = osg::Vec4(1,1,1,1);
}
else
{
if (packedColorMode())
{
_primaryColor = primaryPackedColor;
}
else
{
if (document.version() < VERSION_15_1)
_primaryColor = document.getColorPool()->getColor(primaryNameIndex);
else // >= VERSION_15_1
_primaryColor = document.getColorPool()->getColor(primaryColorIndex);
}
}
// Lighting
stateset->setMode(GL_LIGHTING, isLit() ? osg::StateAttribute::ON : osg::StateAttribute::OFF);
// Material
if (isLit() || materialIndex>=0)
{
// MaterialPool will return a "default" material if no material is defined for materialIndex.
// http://www.multigen-paradigm.com/ubb/Forum1/HTML/000228.html
osg::Vec4 col = _primaryColor;
col.a() = 1.0f - getTransparency();
osg::Material* material = document.getOrCreateMaterialPool()->getOrCreateMaterial(materialIndex,col);
stateset->setAttribute(material);
}
// Shaders
if (shaderIndex >= 0)
{
ShaderPool* sp = document.getOrCreateShaderPool();
osg::Program* program = sp->get(shaderIndex);
if (program)
stateset->setAttributeAndModes(program, osg::StateAttribute::ON);
}
// Texture
TexturePool* tp = document.getOrCreateTexturePool();
osg::StateSet* textureStateSet = tp->get(textureIndex);
if (textureStateSet)
{
// Merge face stateset with texture stateset
stateset->merge(*textureStateSet);
}
// Cull face
switch(_drawFlag)
{
case SOLID_BACKFACED: // Enable backface culling
{
static osg::ref_ptr<osg::CullFace> cullFace = new osg::CullFace(osg::CullFace::BACK);
stateset->setAttributeAndModes(cullFace.get(), osg::StateAttribute::ON);
break;
}
case SOLID_NO_BACKFACE: // Disable backface culling
stateset->setMode(GL_CULL_FACE,osg::StateAttribute::OFF);
break;
}
// Subface
if (document.subfaceLevel() > 0)
{
static osg::ref_ptr<osg::PolygonOffset> polygonOffset = new osg::PolygonOffset(-10.0f, -40.0f);
stateset->setAttributeAndModes(polygonOffset.get(), osg::StateAttribute::ON);
static osg::ref_ptr<osg::Depth> depth = new osg::Depth(osg::Depth::LESS, 0.0, 1.0,false);
stateset->setAttribute(depth.get());
stateset->setRenderBinDetails(document.subfaceLevel(),"RenderBin");
}
// A simple share stateset optimization.
static osg::ref_ptr<osg::StateSet> lastStateset;
if (lastStateset.valid() && (stateset->compare(*lastStateset,false)==0))
stateset = lastStateset;
else
lastStateset = stateset;
_geode->setStateSet(stateset.get());
// Add to parent.
if (_parent.valid())
_parent->addChild(*_geode);
}
virtual void popLevel(Document& document)
{
if (_geode.valid())
{
osg::StateSet* stateset = _geode->getOrCreateStateSet();
// Translucent image?
bool isImageTranslucent = false;
if (document.getUseTextureAlphaForTransparancyBinning())
{
for (unsigned int i=0; i<stateset->getTextureAttributeList().size(); ++i)
{
osg::StateAttribute* sa = stateset->getTextureAttribute(i,osg::StateAttribute::TEXTURE);
osg::Texture2D* texture = dynamic_cast<osg::Texture2D*>(sa);
if (texture)
{
osg::Image* image = texture->getImage();
if (image && image->isImageTranslucent())
isImageTranslucent = true;
}
}
}
// Transparent Material?
bool isMaterialTransparent = false;
osg::Material* material = dynamic_cast<osg::Material*>(stateset->getAttribute(osg::StateAttribute::MATERIAL));
if (material)
{
isMaterialTransparent = material->getDiffuse(osg::Material::FRONT).a() < 0.99f;
}
// Enable alpha blend?
if (isAlphaBlend() || isTransparent() || isImageTranslucent || isMaterialTransparent)
{
static osg::ref_ptr<osg::BlendFunc> blendFunc = new osg::BlendFunc(osg::BlendFunc::SRC_ALPHA, osg::BlendFunc::ONE_MINUS_SRC_ALPHA);
stateset->setAttributeAndModes(blendFunc.get(), osg::StateAttribute::ON);
stateset->setRenderingHint(osg::StateSet::TRANSPARENT_BIN);
}
if (document.getUseBillboardCenter())
{
// Set billboard rotation point to center of face.
osg::Billboard* billboard = dynamic_cast<osg::Billboard*>(_geode.get());
if (billboard)
{
for (unsigned int i=0; i<billboard->getNumDrawables(); ++i)
{
osg::BoundingBox bb = billboard->getDrawable(i)->getBound();
billboard->setPosition(i,bb.center());
osgUtil::TransformAttributeFunctor tf(osg::Matrix::translate(-bb.center()));
billboard->getDrawable(i)->accept(tf);
billboard->getDrawable(i)->dirtyBound();
}
billboard->dirtyBound();
}
}
}
}
};
RegisterRecordProxy<Mesh> g_Mesh(MESH_OP);
/** LocalVertexPool -
*/
class LocalVertexPool : public Record
{
// Attribute Mask
static const unsigned int HAS_POSITION = 0x80000000u >> 0;
static const unsigned int HAS_COLOR_INDEX = 0x80000000u >> 1;
static const unsigned int HAS_RGBA_COLOR = 0x80000000u >> 2;
static const unsigned int HAS_NORMAL = 0x80000000u >> 3;
static const unsigned int HAS_BASE_UV = 0x80000000u >> 4;
static const unsigned int HAS_UV_LAYER1 = 0x80000000u >> 5;
static const unsigned int HAS_UV_LAYER2 = 0x80000000u >> 6;
static const unsigned int HAS_UV_LAYER3 = 0x80000000u >> 7;
static const unsigned int HAS_UV_LAYER4 = 0x80000000u >> 8;
static const unsigned int HAS_UV_LAYER5 = 0x80000000u >> 9;
static const unsigned int HAS_UV_LAYER6 = 0x80000000u >> 10;
static const unsigned int HAS_UV_LAYER7 = 0x80000000u >> 11;
public:
LocalVertexPool() {}
META_Record(LocalVertexPool)
protected:
virtual ~LocalVertexPool() {}
virtual void readRecord(RecordInputStream& in, Document& document)
{
uint32 vertices = in.readUInt32();
uint32 mask = in.readUInt32();
osg::ref_ptr<VertexList> _vertexList = new VertexList(vertices);
for (unsigned int n=0; n<vertices; n++)
{
Vertex vertex;
if (mask & HAS_POSITION)
{
osg::Vec3d coord = in.readVec3d();
vertex.setCoord(coord*document.unitScale());
if (!coord.valid())
{
osg::notify(osg::NOTICE)<<"Warning: data error detected in LocalVertexPool::readRecord coord="<<coord.x()<<" "<<coord.y()<<" "<<coord.z()<<std::endl;
}
}
if (mask & HAS_COLOR_INDEX)
{
uint32 alphaIndex = in.readUInt32();
int index = alphaIndex & 0x00ffffff;
uint8 alpha = alphaIndex >> 24;
osg::Vec4 color = document.getColorPool()->getColor(index);
color.a() = (float)alpha/255;
vertex.setColor(color);
if (!color.valid())
{
osg::notify(osg::NOTICE)<<"Warning: data error detected in LocalVertexPool::readRecord color="<<color.r()<<" "<<color.g()<<" "<<color.b()<<" "<<color.a()<<std::endl;
}
}
if (mask & HAS_RGBA_COLOR)
{
osg::Vec4f color = in.readColor32();
vertex.setColor(color);
if (!color.valid())
{
osg::notify(osg::NOTICE)<<"Warning: data error detected in LocalVertexPool::readRecord color="<<color.r()<<" "<<color.g()<<" "<<color.b()<<" "<<color.a()<<std::endl;
}
}
if (mask & HAS_NORMAL)
{
osg::Vec3f normal = in.readVec3f();
vertex.setNormal(normal);
if (!normal.valid())
{
osg::notify(osg::NOTICE)<<"Warning: data error detected in LocalVertexPool::readRecord normal="<<normal.x()<<" "<<normal.y()<<" "<<normal.z()<<std::endl;
}
}
for (unsigned int layer=0; layer<8; layer++)
{
if (mask & (HAS_BASE_UV >> layer))
{
osg::Vec2f uv = in.readVec2f();
vertex.setUV(layer,uv);
if (!uv.valid())
{
osg::notify(osg::NOTICE)<<"Warning: data error detected in LocalVertexPool::readRecord uv="<<uv.x()<<" "<<uv.y()<<std::endl;
}
}
}
(*_vertexList)[n] = vertex;
}
if (_parent.valid())
_parent->setLocalVertexPool(_vertexList.get());
}
};
RegisterRecordProxy<LocalVertexPool> g_LocalVertexPool(LOCAL_VERTEX_POOL_OP);
/** MeshPrimitive -
*/
class MeshPrimitive : public PrimaryRecord
{
enum PrimitiveType
{
TRIANGLE_STRIP = 1,
TRIANGLE_FAN = 2,
QUADRILATERAL_STRIP = 3,
INDEXED_POLYGON = 4
};
public:
MeshPrimitive() {}
META_Record(MeshPrimitive)
protected:
virtual ~MeshPrimitive() {}
virtual void readRecord(RecordInputStream& in, Document& /*document*/)
{
Mesh* mesh = dynamic_cast<Mesh*>(_parent.get());
if (!mesh) return;
VertexList* vertexList = mesh->getLocalVertexPool();
if (!vertexList) return;
int16 type = in.readInt16();
uint16 indexSize = in.readUInt16();
uint32 vertexCount = in.readUInt32();
GLenum mode = 0;
switch(type)
{
case TRIANGLE_STRIP:
mode = osg::PrimitiveSet::TRIANGLE_STRIP;
break;
case TRIANGLE_FAN:
mode = osg::PrimitiveSet::TRIANGLE_FAN;
break;
case QUADRILATERAL_STRIP:
mode = osg::PrimitiveSet::QUAD_STRIP;
break;
case INDEXED_POLYGON:
mode = osg::PrimitiveSet::POLYGON;
break;
}
osg::ref_ptr<osg::Geometry> geometry = new osg::Geometry;
geometry->addPrimitiveSet(new osg::DrawArrays(mode,0,vertexCount));
for (unsigned int n=0; n<vertexCount; n++)
{
unsigned int index = 0;
switch (indexSize)
{
case 1:
index = in.readUInt8();
break;
case 2:
index = in.readUInt16();
break;
case 4:
index = in.readUInt32();
break;
}
if (index < vertexList->size())
{
Vertex& vertex = (*vertexList)[index];
osg::Vec3Array* vertices = getOrCreateVertexArray(*geometry);
vertices->push_back(vertex._coord);
if (vertex.validColor())
{
osg::Vec4Array* colors = getOrCreateColorArray(*geometry);
colors->push_back(vertex._color);
}
if (vertex.validNormal())
{
osg::Vec3Array* normals = getOrCreateNormalArray(*geometry);
normals->push_back(vertex._normal);
}
for (int layer=0; layer<Vertex::MAX_LAYERS; layer++)
{
if (vertex.validUV(layer))
{
osg::Vec2Array* UVs = getOrCreateTextureArray(*geometry,layer);
UVs->push_back(vertex._uv[layer]);
}
}
}
}
// Color binding
if (mesh->isGouraud())
{
// Color per vertex
geometry->setColorBinding(osg::Geometry::BIND_PER_VERTEX);
}
else
{
// Color per face
osg::Vec4 col = mesh->getPrimaryColor();
col[3] = 1.0f - mesh->getTransparency();
geometry->setColorBinding(osg::Geometry::BIND_OVERALL);
osg::Vec4Array* colors = new osg::Vec4Array(1);
(*colors)[0] = col;
geometry->setColorArray(colors);
}
// Normal binding
if (mesh->isLit())
{
geometry->setNormalBinding(osg::Geometry::BIND_PER_VERTEX );
}
else
{
geometry->setNormalBinding(osg::Geometry::BIND_OFF);
geometry->setNormalArray(NULL);
}
mesh->addGeometry(*geometry);
}
};
RegisterRecordProxy<MeshPrimitive> g_MeshPrimitive(MESH_PRIMITIVE_OP);
} // end namespace