From 77c862d3058aa481111b34037ca7d555720d65db Mon Sep 17 00:00:00 2001 From: Robert Osfield Date: Wed, 27 Aug 2003 14:13:12 +0000 Subject: [PATCH] From Marco Jez, tangent space generator. --- VisualStudio/osgUtil/osgUtil.dsp | 8 + include/osgUtil/TangentSpaceGenerator | 71 +++++++++ src/osgUtil/GNUmakefile | 1 + src/osgUtil/TangentSpaceGenerator.cpp | 218 ++++++++++++++++++++++++++ 4 files changed, 298 insertions(+) create mode 100644 include/osgUtil/TangentSpaceGenerator create mode 100644 src/osgUtil/TangentSpaceGenerator.cpp diff --git a/VisualStudio/osgUtil/osgUtil.dsp b/VisualStudio/osgUtil/osgUtil.dsp index 0753371e9..db2f55c1a 100755 --- a/VisualStudio/osgUtil/osgUtil.dsp +++ b/VisualStudio/osgUtil/osgUtil.dsp @@ -165,6 +165,10 @@ SOURCE=..\..\src\osgUtil\SmoothingVisitor.cpp # End Source File # Begin Source File +SOURCE=..\..\src\osgUtil\TangentSpaceGenerator.cpp +# End Source File +# Begin Source File + SOURCE=..\..\src\osgUtil\Tesselator.cpp # End Source File # Begin Source File @@ -281,6 +285,10 @@ SOURCE=..\..\include\osgUtil\Statistics # End Source File # Begin Source File +SOURCE=..\..\include\osgUtil\TangentSpaceGenerator +# End Source File +# Begin Source File + SOURCE=..\..\include\osgUtil\Tesselator # End Source File # Begin Source File diff --git a/include/osgUtil/TangentSpaceGenerator b/include/osgUtil/TangentSpaceGenerator new file mode 100644 index 000000000..5c2e4b044 --- /dev/null +++ b/include/osgUtil/TangentSpaceGenerator @@ -0,0 +1,71 @@ +/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2003 Robert Osfield + * + * This library is open source and may be redistributed and/or modified under + * the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or + * (at your option) any later version. The full license is in LICENSE file + * included with this distribution, and on the openscenegraph.org website. + * + * 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 + * OpenSceneGraph Public License for more details. +*/ + +#ifndef OSGUTIL_TANGENTSPACEGENERATOR_ +#define OSGUTIL_TANGENTSPACEGENERATOR_ + +#include + +#include +#include +#include +#include + +namespace osgUtil +{ + + /** + This class generates three arrays containing tangent-space basis vectors. It takes + a texture-mapped Geometry object as input, traverses its primitive sets and computes + Tangent, Normal and Binormal vectors for each vertex, storing them into arrays. + The resulting arrays can be used as vertex program varying (per-vertex) parameters, + enabling advanced effects like bump-mapping. + To use this class, simply call the generate() method specifying the Geometry object + you want to process and the texture unit that contains UV mapping for the normal map; + then you can retrieve the TBN arrays by calling getTangentArray(), getNormalArray() + and getBinormalArray() methods. + */ + class OSGUTIL_EXPORT TangentSpaceGenerator: public osg::Referenced { + public: + TangentSpaceGenerator(); + TangentSpaceGenerator(const TangentSpaceGenerator ©, const osg::CopyOp ©op = osg::CopyOp::SHALLOW_COPY); + + void generate(osg::Geometry *geo, int normal_map_tex_unit = 0); + + inline osg::Vec4Array *getTangentArray() { return T_.get(); } + inline const osg::Vec4Array *getTangentArray() const { return T_.get(); } + inline void setTangentArray(osg::Vec4Array *array) { T_ = array; } + + inline osg::Vec4Array *getNormalArray() { return N_.get(); } + inline const osg::Vec4Array *getNormalArray() const { return N_.get(); } + inline void setNormalArray(osg::Vec4Array *array) { N_ = array; } + + inline osg::Vec4Array *getBinormalArray() { return B_.get(); } + inline const osg::Vec4Array *getBinormalArray() const { return B_.get(); } + inline void setBinormalArray(osg::Vec4Array *array) { B_ = array; } + + protected: + virtual ~TangentSpaceGenerator() {} + TangentSpaceGenerator &operator=(const TangentSpaceGenerator &) { return *this; } + + void compute_basis_vectors(osg::PrimitiveSet *pset, const osg::Array *vx, const osg::Array *tx, int iA, int iB, int iC); + + private: + osg::ref_ptr T_; + osg::ref_ptr B_; + osg::ref_ptr N_; + }; + +} + +#endif diff --git a/src/osgUtil/GNUmakefile b/src/osgUtil/GNUmakefile index 39adbab6f..7294af3c4 100644 --- a/src/osgUtil/GNUmakefile +++ b/src/osgUtil/GNUmakefile @@ -19,6 +19,7 @@ CXXFILES = \ RenderToTextureStage.cpp\ SceneView.cpp\ SmoothingVisitor.cpp\ + TangentSpaceGenerator.cpp\ Tesselator.cpp\ TransformCallback.cpp\ TransformAttributeFunctor.cpp\ diff --git a/src/osgUtil/TangentSpaceGenerator.cpp b/src/osgUtil/TangentSpaceGenerator.cpp new file mode 100644 index 000000000..8f394890f --- /dev/null +++ b/src/osgUtil/TangentSpaceGenerator.cpp @@ -0,0 +1,218 @@ +#include + +#include + +using namespace osgUtil; + +TangentSpaceGenerator::TangentSpaceGenerator() +: osg::Referenced(), + T_(new osg::Vec4Array), + B_(new osg::Vec4Array), + N_(new osg::Vec4Array) +{ +} + +TangentSpaceGenerator::TangentSpaceGenerator(const TangentSpaceGenerator ©, const osg::CopyOp ©op) +: osg::Referenced(copy), + T_(static_cast(copyop(copy.T_.get()))), + B_(static_cast(copyop(copy.B_.get()))), + N_(static_cast(copyop(copy.N_.get()))) +{ +} + +void TangentSpaceGenerator::generate(osg::Geometry *geo, int normal_map_tex_unit) +{ + const osg::Array *vx = geo->getVertexArray(); + const osg::Array *tx = geo->getTexCoordArray(normal_map_tex_unit); + + if (!vx || !tx) return; + + unsigned int vertex_count = vx->getNumElements(); + + T_->assign(vertex_count, osg::Vec4()); + B_->assign(vertex_count, osg::Vec4()); + N_->assign(vertex_count, osg::Vec4()); + + unsigned i; // VC6 doesn't like for-scoped variables + + for (i=0; igetNumPrimitiveSets(); ++i) { + osg::PrimitiveSet *pset = geo->getPrimitiveSet(i); + + switch (pset->getMode()) { + case osg::PrimitiveSet::TRIANGLES: + for (i=0; igetNumIndices(); i+=3) { + compute_basis_vectors(pset, vx, tx, i, i+1, i+2); + } + break; + + case osg::PrimitiveSet::TRIANGLE_STRIP: + for (i=0; igetNumIndices()-2; ++i) { + if ((i%2) == 0) { + compute_basis_vectors(pset, vx, tx, i, i+1, i+2); + } else { + compute_basis_vectors(pset, vx, tx, i, i+2, i+1); + } + } + break; + + case osg::PrimitiveSet::TRIANGLE_FAN: + for (i=2; igetNumIndices(); ++i) { + compute_basis_vectors(pset, vx, tx, 0, i-1, i); + } + break; + + case osg::PrimitiveSet::QUADS: + for (i=0; igetNumIndices(); i+=4) { + compute_basis_vectors(pset, vx, tx, i, i+1, i+2); + compute_basis_vectors(pset, vx, tx, i+2, i+3, i); + } + break; + + case osg::PrimitiveSet::POINTS: + case osg::PrimitiveSet::LINES: + case osg::PrimitiveSet::LINE_STRIP: + case osg::PrimitiveSet::LINE_LOOP: + break; + + default: osg::notify(osg::WARN) << "Warning: TangentSpaceGenerator: unknown primitive mode " << pset->getMode() << "\n"; + } + } + + // normalize basis vectors and force the normal vector to match + // the triangle normal's direction + + for (i=0; iat(i); + osg::Vec4 &vB = B_->at(i); + osg::Vec4 &vN = N_->at(i); + + osg::Vec3 txN = osg::Vec3(vT.x(), vT.y(), vT.z()) ^ osg::Vec3(vB.x(), vB.y(), vB.z()); + + if (txN * osg::Vec3(vN.x(), vN.y(), vN.z()) >= 0) { + vN = osg::Vec4(txN, 0); + } else { + vN = osg::Vec4(-txN, 0); + } + + vT.normalize(); + vB.normalize(); + vN.normalize(); + } +} + +void TangentSpaceGenerator::compute_basis_vectors(osg::PrimitiveSet *pset, const osg::Array *vx, const osg::Array *tx, int iA, int iB, int iC) +{ + iA = pset->index(iA); + iB = pset->index(iB); + iC = pset->index(iC); + + osg::Vec3 P1; + osg::Vec3 P2; + osg::Vec3 P3; + + int i; // VC6 doesn't like for-scoped variables + + switch (vx->getType()) + { + case osg::Array::Vec2ArrayType: + for (i=0; i<2; ++i) { + P1.ptr()[i] = static_cast(vx)->at(iA).ptr()[i]; + P2.ptr()[i] = static_cast(vx)->at(iB).ptr()[i]; + P3.ptr()[i] = static_cast(vx)->at(iC).ptr()[i]; + } + break; + + case osg::Array::Vec3ArrayType: + P1 = static_cast(vx)->at(iA); + P2 = static_cast(vx)->at(iB); + P3 = static_cast(vx)->at(iC); + break; + + case osg::Array::Vec4ArrayType: + for (i=0; i<3; ++i) { + P1.ptr()[i] = static_cast(vx)->at(iA).ptr()[i]; + P2.ptr()[i] = static_cast(vx)->at(iB).ptr()[i]; + P3.ptr()[i] = static_cast(vx)->at(iC).ptr()[i]; + } + break; + + default: + osg::notify(osg::WARN) << "Warning: TangentSpaceGenerator: vertex array must be Vec2Array, Vec3Array or Vec4Array" << std::endl; + } + + osg::Vec2 uv1; + osg::Vec2 uv2; + osg::Vec2 uv3; + + switch (tx->getType()) + { + case osg::Array::Vec2ArrayType: + uv1 = static_cast(tx)->at(iA); + uv2 = static_cast(tx)->at(iB); + uv3 = static_cast(tx)->at(iC); + break; + + case osg::Array::Vec3ArrayType: + for (i=0; i<2; ++i) { + uv1.ptr()[i] = static_cast(tx)->at(iA).ptr()[i]; + uv2.ptr()[i] = static_cast(tx)->at(iB).ptr()[i]; + uv3.ptr()[i] = static_cast(tx)->at(iC).ptr()[i]; + } + break; + + case osg::Array::Vec4ArrayType: + for (i=0; i<2; ++i) { + uv1.ptr()[i] = static_cast(tx)->at(iA).ptr()[i]; + uv2.ptr()[i] = static_cast(tx)->at(iB).ptr()[i]; + uv3.ptr()[i] = static_cast(tx)->at(iC).ptr()[i]; + } + break; + + default: + osg::notify(osg::WARN) << "Warning: TangentSpaceGenerator: texture coord array must be Vec2Array, Vec3Array or Vec4Array" << std::endl; + } + + osg::Vec3 face_normal = (P2 - P1) ^ (P3 - P1); + + osg::Vec3 V; + + V = osg::Vec3(P2.x() - P1.x(), uv2.x() - uv1.x(), uv2.y() - uv1.y()) ^ + osg::Vec3(P3.x() - P1.x(), uv3.x() - uv1.x(), uv3.y() - uv1.y()); + if (V.x() != 0) { + V.normalize(); + T_->at(iA).x() += -V.y() / V.x(); + B_->at(iA).x() += -V.z() / V.x(); + T_->at(iB).x() += -V.y() / V.x(); + B_->at(iB).x() += -V.z() / V.x(); + T_->at(iC).x() += -V.y() / V.x(); + B_->at(iC).x() += -V.z() / V.x(); + } + + V = osg::Vec3(P2.y() - P1.y(), uv2.x() - uv1.x(), uv2.y() - uv1.y()) ^ + osg::Vec3(P3.y() - P1.y(), uv3.x() - uv1.x(), uv3.y() - uv1.y()); + if (V.x() != 0) { + V.normalize(); + T_->at(iA).y() += -V.y() / V.x(); + B_->at(iA).y() += -V.z() / V.x(); + T_->at(iB).y() += -V.y() / V.x(); + B_->at(iB).y() += -V.z() / V.x(); + T_->at(iC).y() += -V.y() / V.x(); + B_->at(iC).y() += -V.z() / V.x(); + } + + V = osg::Vec3(P2.z() - P1.z(), uv2.x() - uv1.x(), uv2.y() - uv1.y()) ^ + osg::Vec3(P3.z() - P1.z(), uv3.x() - uv1.x(), uv3.y() - uv1.y()); + if (V.x() != 0) { + V.normalize(); + T_->at(iA).z() += -V.y() / V.x(); + B_->at(iA).z() += -V.z() / V.x(); + T_->at(iB).z() += -V.y() / V.x(); + B_->at(iB).z() += -V.z() / V.x(); + T_->at(iC).z() += -V.y() / V.x(); + B_->at(iC).z() += -V.z() / V.x(); + } + + N_->at(iA) += osg::Vec4(face_normal, 0); + N_->at(iB) += osg::Vec4(face_normal, 0); + N_->at(iC) += osg::Vec4(face_normal, 0); +}