From 7575f458006f4a39ebd103476e6534ebe9b9ac63 Mon Sep 17 00:00:00 2001 From: Robert Osfield Date: Mon, 1 Sep 2003 21:53:53 +0000 Subject: [PATCH] Support for BumpMapping added by Marco Jez. --- VisualStudio/VisualStudio.dsw | 3 + VisualStudio/osgFX/osgFX.dsp | 8 + VisualStudio/osgPlugins/osgFX/dot_osgFX.dsp | 4 + include/osgFX/BumpMapping | 198 ++++++ src/osgFX/BumpMapping.cpp | 635 ++++++++++++++++++++ src/osgFX/GNUmakefile | 1 + src/osgPlugins/osgFX/GNUmakefile | 1 + src/osgPlugins/osgFX/IO_BumpMapping.cpp | 83 +++ src/osgUtil/TangentSpaceGenerator.cpp | 65 +- 9 files changed, 981 insertions(+), 17 deletions(-) create mode 100644 include/osgFX/BumpMapping create mode 100644 src/osgFX/BumpMapping.cpp create mode 100644 src/osgPlugins/osgFX/IO_BumpMapping.cpp diff --git a/VisualStudio/VisualStudio.dsw b/VisualStudio/VisualStudio.dsw index e80de36e1..9516ed039 100644 --- a/VisualStudio/VisualStudio.dsw +++ b/VisualStudio/VisualStudio.dsw @@ -44,6 +44,9 @@ Package=<4> Begin Project Dependency Project_Dep_Name Core osgUtil End Project Dependency + Begin Project Dependency + Project_Dep_Name Core osgDB + End Project Dependency }}} ############################################################################### diff --git a/VisualStudio/osgFX/osgFX.dsp b/VisualStudio/osgFX/osgFX.dsp index 93cbb8b06..aea373461 100644 --- a/VisualStudio/osgFX/osgFX.dsp +++ b/VisualStudio/osgFX/osgFX.dsp @@ -150,6 +150,10 @@ DEP_CPP_ANISO=\ # End Source File # Begin Source File +SOURCE=..\..\src\osgFX\BumpMapping.cpp +# End Source File +# Begin Source File + SOURCE=..\..\src\osgFX\Cartoon.cpp DEP_CPP_CARTO=\ "..\..\Include\Osg\Array"\ @@ -499,6 +503,10 @@ SOURCE=..\..\include\osgFX\AnisotropicLighting # End Source File # Begin Source File +SOURCE=..\..\include\osgFX\BumpMapping +# End Source File +# Begin Source File + SOURCE=..\..\include\osgFX\Cartoon # End Source File # Begin Source File diff --git a/VisualStudio/osgPlugins/osgFX/dot_osgFX.dsp b/VisualStudio/osgPlugins/osgFX/dot_osgFX.dsp index 8d687a695..5989367c3 100644 --- a/VisualStudio/osgPlugins/osgFX/dot_osgFX.dsp +++ b/VisualStudio/osgPlugins/osgFX/dot_osgFX.dsp @@ -154,6 +154,10 @@ DEP_CPP_IO_AN=\ # End Source File # Begin Source File +SOURCE=..\..\..\src\osgPlugins\osgFX\IO_BumpMapping.cpp +# End Source File +# Begin Source File + SOURCE=..\..\..\src\osgPlugins\osgFX\IO_Cartoon.cpp DEP_CPP_IO_CA=\ "..\..\..\Include\osg\ArgumentParser"\ diff --git a/include/osgFX/BumpMapping b/include/osgFX/BumpMapping new file mode 100644 index 000000000..70106d036 --- /dev/null +++ b/include/osgFX/BumpMapping @@ -0,0 +1,198 @@ +/* -*-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. +*/ +//osgFX - Copyright (C) 2003 Marco Jez + +#ifndef OSGFX_BUMPMAPPING_ +#define OSGFX_BUMPMAPPING_ + +#include +#include + +#include +#include + +namespace osgFX +{ + + /** + This effect makes surfaces appear bumpy. The child node must use two textures, + one for diffuse color and one for the normal map (which can be created + from a height map with tools like nVIDIA's normal map generator). Furthermore, + tangent-space basis vectors must be created and assigned to each Geometry; this + can be done quickly by calling BumpMapping::prepareChild(). Note that both + diffuse and normal map textures must have corresponding UV maps defined in + Geometry objects. + This effect defines a preferred technique which uses ARB vertex & fragment + programs, and a fallback technique which doesn't use fragment programs. The + latter is more limited though since it can't handle ambient and specular + components. + */ + class OSGFX_EXPORT BumpMapping: public Effect { + public: + BumpMapping(); + BumpMapping(const BumpMapping ©, const osg::CopyOp ©op = osg::CopyOp::SHALLOW_COPY); + + META_Effect(osgFX, BumpMapping, + + "Bump Mapping", + + "This effect makes surfaces appear bumpy. The child node must use two textures, " + "one for diffuse color and one for the normal map (which can be created " + "from a height map with tools like nVIDIA's normal map generator). Furthermore, " + "tangent-space basis vectors must be created and assigned to each Geometry; this " + "can be done quickly by calling BumpMapping::prepareChild(). Note that both " + "diffuse and normal map textures must have corresponding UV maps defined in " + "Geometry objects.\n" + "This effect defines a preferred technique which uses ARB vertex & fragment " + "programs, and a fallback technique which doesn't use fragment programs. The " + "latter is more limited though since it can't handle ambient and specular " + "components.", + + "Marco Jez"); + + + /** get the OpenGL light number */ + inline int getLightNumber() const; + + /** set the OpenGL light number that will be used in lighting computations */ + inline void setLightNumber(int n); + + /** get the texture unit that contains diffuse color texture. Default is 1 */ + inline int getDiffuseTextureUnit() const; + + /** set the texture unit that contains diffuse color texture. Default is 1 */ + inline void setDiffuseTextureUnit(int n); + + /** get the texture unit that contains normal map texture. Default is 0 */ + inline int getNormalMapTextureUnit() const; + + /** set the texture unit that contains normal map texture. Default is 0 */ + inline void setNormalMapTextureUnit(int n); + + /** get the diffuse color texture that overrides the child's texture */ + inline osg::Texture2D *getOverrideDiffuseTexture(); + + /** get the const diffuse color texture that overrides the child's texture */ + inline const osg::Texture2D *getOverrideDiffuseTexture() const; + + /** set the diffuse color texture that overrides the child's texture */ + inline void setOverrideDiffuseTexture(osg::Texture2D *texture); + + /** get the normal map texture that overrides the child's texture */ + inline osg::Texture2D *getOverrideNormalMapTexture(); + + /** get the const normal map texture that overrides the child's texture */ + inline const osg::Texture2D *getOverrideNormalMapTexture() const; + + /** set the normal map texture that overrides the child's texture */ + inline void setOverrideNormalMapTexture(osg::Texture2D *texture); + + /** + prepare a Geometry for bump lighting. Tangent-space basis vectors are + generated and attached to the geometry as vertex attribute arrays. + */ + void prepareGeometry(osg::Geometry *geo); + + /** prepare a Node for bump lighting, calling prepareGeometry() for each Geometry */ + void prepareNode(osg::Node *node); + + /** prepare the current child for bump lighting. Actually calls prepareNode(getChild()) */ + void prepareChild(); + + /** set up a demo environment with predefined diffuse and normal maps, as well as texture coordinates */ + void setUpDemo(); + + protected: + virtual ~BumpMapping() {} + BumpMapping &operator=(const BumpMapping &) { return *this; } + + bool define_techniques(); + + private: + int lightnum_; + int diffuseunit_; + int normalunit_; + osg::ref_ptr diffuse_tex_; + osg::ref_ptr normal_tex_; + }; + + // INLINE METHODS + + inline int BumpMapping::getLightNumber() const + { + return lightnum_; + } + + inline void BumpMapping::setLightNumber(int n) + { + lightnum_ = n; + dirtyTechniques(); + } + + inline int BumpMapping::getDiffuseTextureUnit() const + { + return diffuseunit_; + } + + inline void BumpMapping::setDiffuseTextureUnit(int n) + { + diffuseunit_ = n; + dirtyTechniques(); + } + + inline int BumpMapping::getNormalMapTextureUnit() const + { + return normalunit_; + } + + inline void BumpMapping::setNormalMapTextureUnit(int n) + { + normalunit_ = n; + dirtyTechniques(); + } + + inline osg::Texture2D *BumpMapping::getOverrideDiffuseTexture() + { + return diffuse_tex_.get(); + } + + inline const osg::Texture2D *BumpMapping::getOverrideDiffuseTexture() const + { + return diffuse_tex_.get(); + } + + inline void BumpMapping::setOverrideDiffuseTexture(osg::Texture2D *texture) + { + diffuse_tex_ = texture; + dirtyTechniques(); + } + + inline osg::Texture2D *BumpMapping::getOverrideNormalMapTexture() + { + return normal_tex_.get(); + } + + inline const osg::Texture2D *BumpMapping::getOverrideNormalMapTexture() const + { + return normal_tex_.get(); + } + + inline void BumpMapping::setOverrideNormalMapTexture(osg::Texture2D *texture) + { + normal_tex_ = texture; + dirtyTechniques(); + } + +} + +#endif diff --git a/src/osgFX/BumpMapping.cpp b/src/osgFX/BumpMapping.cpp new file mode 100644 index 000000000..3ee56fb22 --- /dev/null +++ b/src/osgFX/BumpMapping.cpp @@ -0,0 +1,635 @@ +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include + +using namespace osgFX; + +namespace +{ + + // this is a visitor class that prepares all geometries in a subgraph + // by calling prepareGeometry() which in turn generates tangent-space + // basis vectors + class TsgVisitor: public osg::NodeVisitor { + public: + TsgVisitor(BumpMapping *bm): osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN), bm_(bm) {} + void apply(osg::Geode &geode) + { + for (unsigned i=0; i(geode.getDrawable(i)); + if (geo) { + bm_->prepareGeometry(geo); + } + } + osg::NodeVisitor::apply(geode); + } + private: + osg::ref_ptr bm_; + }; + + + // this visitor generates texture coordinates for all geometries in a + // subgraph. It is used only for demo purposes. + class TexCoordGenerator: public osg::NodeVisitor { + public: + TexCoordGenerator(int du, int nu): osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN), du_(du), nu_(nu) {} + void apply(osg::Geode &geode) + { + const osg::BoundingSphere &bsphere = geode.getBound(); + float scale = 10; + if (bsphere.radius() != 0) { + scale = 5 / bsphere.radius(); + } + for (unsigned i=0; i(geode.getDrawable(i)); + if (geo) { + osg::ref_ptr tc = generate_coords(geo->getVertexArray(), geo->getNormalArray(), scale); + geo->setTexCoordArray(du_, tc.get()); + geo->setTexCoordArray(nu_, tc.get()); + } + } + osg::NodeVisitor::apply(geode); + } + + protected: + osg::Vec2Array *generate_coords(osg::Array *vx, osg::Array *nx, float scale) + { + osg::Vec2Array *v2a = dynamic_cast(vx); + osg::Vec3Array *v3a = dynamic_cast(vx); + osg::Vec4Array *v4a = dynamic_cast(vx); + osg::Vec2Array *n2a = dynamic_cast(nx); + osg::Vec3Array *n3a = dynamic_cast(nx); + osg::Vec4Array *n4a = dynamic_cast(nx); + + osg::ref_ptr tc = new osg::Vec2Array; + for (unsigned i=0; igetNumElements(); ++i) { + + osg::Vec3 P; + if (v2a) P.set(v2a->at(i).x(), v2a->at(i).y(), 0); + if (v3a) P.set(v3a->at(i).x(), v3a->at(i).y(), v3a->at(i).z()); + if (v4a) P.set(v4a->at(i).x(), v4a->at(i).y(), v4a->at(i).z()); + + osg::Vec3 N(0, 0, 1); + if (n2a) N.set(n2a->at(i).x(), n2a->at(i).y(), 0); + if (n3a) N.set(n3a->at(i).x(), n3a->at(i).y(), n3a->at(i).z()); + if (n4a) N.set(n4a->at(i).x(), n4a->at(i).y(), n4a->at(i).z()); + + int axis = 0; + if (N.y() > N.x() && N.y() > N.z()) axis = 1; + if (-N.y() > N.x() && -N.y() > N.z()) axis = 1; + if (N.z() > N.x() && N.z() > N.y()) axis = 2; + if (-N.z() > N.x() && -N.z() > N.y()) axis = 2; + + osg::Vec2 uv; + + switch (axis) { + case 0: uv.set(P.y(), P.z()); break; + case 1: uv.set(P.x(), P.z()); break; + case 2: uv.set(P.x(), P.y()); break; + default: ; + } + + tc->push_back(uv * scale); + } + return tc.take(); + } + + private: + int du_; + int nu_; + }; + +} + +namespace +{ + + // a state attribute class that grabs the initial inverse view matrix + // and sends it to a VertexProgram. + // NOTE: due to lack of support for per-context parameters in VertexProgram, + // this class will send the matrix to the vp only while the first context + // is being rendered. All subsequent contexts will use the first context's + // matrix. + class ViewMatrixExtractor: public osg::StateAttribute { + public: + ViewMatrixExtractor() + : osg::StateAttribute(), + vp_(0), + param_(0), + first_context_(-1) + { + } + + ViewMatrixExtractor(const ViewMatrixExtractor ©, const osg::CopyOp ©op) + : osg::StateAttribute(copy, copyop), + vp_(static_cast(copyop(copy.vp_.get()))), + param_(copy.param_), + first_context_(-1) + { + } + + ViewMatrixExtractor(osg::VertexProgram *vp, int param) + : osg::StateAttribute(), + vp_(vp), + param_(param), + first_context_(-1) + { + } + + META_StateAttribute(osgFX, ViewMatrixExtractor, 0x564d4548); + + int compare(const osg::StateAttribute &sa) const + { + COMPARE_StateAttribute_Types(ViewMatrixExtractor, sa); + if (vp_.get() != rhs.vp_.get()) return -1; + if (param_ < rhs.param_) return -1; + if (param_ > rhs.param_) return 1; + return 0; + } + + void apply(osg::State &state) const + { + if (first_context_ == -1) { + first_context_ = state.getContextID(); + } + if (state.getContextID() == first_context_) { + if (vp_.valid()) { + osg::Matrix M = state.getInitialInverseViewMatrix(); + for (int i=0; i<4; ++i) { + vp_->setProgramLocalParameter(param_+i, osg::Vec4(M(0, i), M(1, i), M(2, i), M(3, i))); + } + } + } + } + + private: + mutable osg::ref_ptr vp_; + int param_; + mutable int first_context_; + }; + +} + + +namespace +{ + // let's register this cool effect! :) + Registry::Proxy proxy(new BumpMapping); +} + + +namespace +{ + + // "Full ARB" technique uses ARB vertex program and fragment program. + // Handles ambient, diffuse and specular lighting transparently. A texture + // for the diffuse component is required as well as a normal map texture. + class FullArbTechnique: public Technique { + public: + + FullArbTechnique(int lightnum, int diffuseunit, int normalunit, osg::Texture2D *diffuse_tex, osg::Texture2D *normal_tex) + : Technique(), + lightnum_(lightnum), + diffuseunit_(diffuseunit), + normalunit_(normalunit), + diffuse_tex_(diffuse_tex), + normal_tex_(normal_tex) + { + } + + META_Technique( + "FullArbTechnique", + "Single-pass technique, requires ARB_vertex_program and ARB_fragment_program." + ); + + void getRequiredExtensions(std::vector &extensions) const + { + extensions.push_back("GL_ARB_vertex_program"); + extensions.push_back("GL_ARB_fragment_program"); + } + + protected: + + void define_passes() + { + // vertex program + std::ostringstream vp_oss; + vp_oss << + "!!ARBvp1.0\n" + "OPTION ARB_position_invariant;" + "PARAM c4 = { 0, 0, 0, 1 };" + "PARAM c5 = { 0.5, 4, 0, 0 };" + "TEMP R0, R1, R2, R3, R4, R5, R6, R7, R8;" + "ATTRIB v5 = vertex.attrib[15];" + "ATTRIB v4 = vertex.attrib[7];" + "ATTRIB v3 = vertex.attrib[6];" + "ATTRIB v25 = vertex.texcoord[1];" + "ATTRIB v24 = vertex.texcoord[0];" + "ATTRIB v18 = vertex.normal;" + "ATTRIB v16 = vertex.position;" + "PARAM s259[4] = { state.matrix.mvp };" + "PARAM s18 = state.light[0].position;" + "PARAM s77 = state.lightprod[0].specular;" + "PARAM s4 = state.material.shininess;" + "PARAM s75 = state.lightprod[0].ambient;" + "PARAM s223[4] = { state.matrix.modelview[0] };" + "PARAM c0[4] = { program.local[0..3] };" + " MOV result.texcoord[2].xyz, s75.xyzx;" + " MOV result.texcoord[2].w, s4.x;" + " MOV result.texcoord[0].zw, s77.xyxy;" + " MOV result.texcoord[0].xy, v24;" + " MOV result.texcoord[1].zw, s77.zwzw;" + " MOV result.texcoord[1].xy, v25;" + " MOV R5, c0[0];" + " MUL R0, R5.y, s223[1];" + " MAD R0, R5.x, s223[0], R0;" + " MAD R0, R5.z, s223[2], R0;" + " MAD R0, R5.w, s223[3], R0;" + " DP4 R1.x, R0, v16;" + " MOV R4, c0[1];" + " MUL R2, R4.y, s223[1];" + " MAD R2, R4.x, s223[0], R2;" + " MAD R2, R4.z, s223[2], R2;" + " MAD R7, R4.w, s223[3], R2;" + " DP4 R1.y, R7, v16;" + " MOV R3, c0[2];" + " MUL R2, R3.y, s223[1];" + " MAD R2, R3.x, s223[0], R2;" + " MAD R2, R3.z, s223[2], R2;" + " MAD R6, R3.w, s223[3], R2;" + " DP4 R1.z, R6, v16;" + " MOV R2, c0[3];" + " MUL R8, R2.y, s223[1];" + " MAD R8, R2.x, s223[0], R8;" + " MAD R8, R2.z, s223[2], R8;" + " MAD R8, R2.w, s223[3], R8;" + " MOV R8.x, R5.w;" + " MOV R8.y, R4.w;" + " MOV R8.z, R3.w;" + " ADD R1.yzw, R8.xxyz, -R1.xxyz;" + " DP3 R1.x, R1.yzwy, R1.yzwy;" + " RSQ R1.x, R1.x;" + " DP4 R5.x, R5, s18;" + " DP4 R5.y, R4, s18;" + " DP4 R5.z, R3, s18;" + " DP3 R2.x, R5.xyzx, R5.xyzx;" + " RSQ R2.x, R2.x;" + " MUL R5.xyz, R2.x, R5.xyzx;" + " MAD R1.yzw, R1.x, R1.yyzw, R5.xxyz;" + " DP3 R1.x, R1.yzwy, R1.yzwy;" + " RSQ R1.x, R1.x;" + " MUL R4.xyz, R1.x, R1.yzwy;" + " DP3 R3.x, R0.xyzx, v3.xyzx;" + " DP3 R3.y, R7.xyzx, v3.xyzx;" + " DP3 R3.z, R6.xyzx, v3.xyzx;" + " DP3 R8.x, R3.xyzx, R4.xyzx;" + " DP3 R2.x, R0.xyzx, v4.xyzx;" + " DP3 R2.y, R7.xyzx, v4.xyzx;" + " DP3 R2.z, R6.xyzx, v4.xyzx;" + " DP3 R8.y, R2.xyzx, R4.xyzx;" + " DP3 R1.x, R0.xyzx, v5.xyzx;" + " DP3 R1.y, R7.xyzx, v5.xyzx;" + " DP3 R1.z, R6.xyzx, v5.xyzx;" + " DP3 R8.z, R1.xyzx, R4.xyzx;" + " MAD result.color.front.secondary.xyz, c5.x, R8.xyzx, c5.x;" + " DP3 R0.y, R0.xyzx, v18.xyzx;" + " DP3 R0.z, R7.xyzx, v18.xyzx;" + " DP3 R0.w, R6.xyzx, v18.xyzx;" + " DP3 R0.x, R0.yzwy, R0.yzwy;" + " RSQ R0.x, R0.x;" + " MUL R6.xyz, R0.x, R0.yzwy;" + " DP3 R0.x, R6.xyzx, R4.xyzx;" + " MUL result.color.front.secondary.w, c5.y, R0.x;" + " DP3 R0.x, R3.xyzx, R5.xyzx;" + " DP3 R0.y, R2.xyzx, R5.xyzx;" + " DP3 R0.z, R1.xyzx, R5.xyzx;" + " MAD result.color.front.primary.xyz, c5.x, R0.xyzx, c5.x;" + " DP3 R0.x, R6.xyzx, R5.xyzx;" + " MUL result.color.front.primary.w, c5.y, R0.x;" + "END\n"; + + // fragment program + std::ostringstream fp_oss; + fp_oss << + "!!ARBfp1.0\n" + "PARAM c0 = {1, 2, 0.5, 0};" + "PARAM c1 = {0, 0, 0, 1};" + "TEMP R0;" + "TEMP R1;" + "TEMP R2;" + "TEX R0, fragment.texcoord[0], texture[0], 2D;" + "TEX R1, fragment.texcoord[1], texture[1], 2D;" + "ADD R0, R0, -c0.z;" + "MUL R0.xyz, c0.y, R0;" + "ADD R2.xyz, fragment.color.primary, -c0.z;" + "MUL R2.xyz, c0.y, R2;" + "DP3_SAT R0.w, R0, R2;" + "ADD R2, fragment.color.secondary, -c0.z;" + "MUL R2.xyz, c0.y, R2;" + "DP3_SAT R0.x, R0, R2;" + "POW R0.x, R0.x, fragment.texcoord[2].w;" + "MOV R2.xyz, fragment.texcoord[2].xyyx;" + "MOV R2.w, c1.w;" + "MOV_SAT R0.y, fragment.color.primary.w;" + "MUL R0.w, R0.y, R0.w;" + "ADD R2, R2, R0.w;" + "MUL R1.xyz, R1, R2;" + "MOV_SAT R0.y, fragment.color.secondary.w;" + "MUL R0.xyz, R0.y, R0.x;" + "MOV R2.xy, fragment.texcoord[1].zwzz;" + "MOV R2.z, fragment.texcoord[0].z;" + "MUL R2.xyz, R0, R2;" + "ADD R2.xyz, R1, R2;" + "MOV result.color.xyz, R2;" + "MOV result.color.w, c0.x;" + "END\n"; + + osg::ref_ptr ss = new osg::StateSet; + + osg::ref_ptr vp = new osg::VertexProgram; + vp->setVertexProgram(vp_oss.str()); + ss->setAttributeAndModes(vp.get(), osg::StateAttribute::OVERRIDE|osg::StateAttribute::ON); + + osg::ref_ptr fp = new osg::FragmentProgram; + fp->setFragmentProgram(fp_oss.str()); + ss->setAttributeAndModes(fp.get(), osg::StateAttribute::OVERRIDE|osg::StateAttribute::ON); + + ss->setAttributeAndModes(new ViewMatrixExtractor(vp.get(), 0), osg::StateAttribute::OVERRIDE|osg::StateAttribute::ON); + + if (diffuse_tex_.valid()) { + ss->setTextureAttributeAndModes(diffuseunit_, diffuse_tex_.get(), osg::StateAttribute::OVERRIDE|osg::StateAttribute::ON); + } + + if (normal_tex_.valid()) { + ss->setTextureAttributeAndModes(normalunit_, normal_tex_.get(), osg::StateAttribute::OVERRIDE|osg::StateAttribute::ON); + } + + addPass(ss.get()); + } + + private: + int lightnum_; + int diffuseunit_; + int normalunit_; + osg::ref_ptr diffuse_tex_; + osg::ref_ptr normal_tex_; + }; + +} + +namespace +{ + + // "ARB Vp" technique uses ARB vertex program and DOT3 texture environment. + // Ambient and specular components are not handled. A texture for the diffuse + // component is required as well as a normal map texture. + class ArbVpTechnique: public Technique { + public: + ArbVpTechnique(int lightnum, int diffuseunit, int normalunit, osg::Texture2D *diffuse_tex, osg::Texture2D *normal_tex) + : Technique(), + lightnum_(lightnum), + diffuseunit_(diffuseunit), + normalunit_(normalunit), + diffuse_tex_(diffuse_tex), + normal_tex_(normal_tex) + { + } + + META_Technique( + "ArbVpTechnique", + "Two-passes technique, requires ARB_vertex_program and ARB_texture_env_dot3." + "Only diffuse lighting, no ambient, no specularity." + ); + + void getRequiredExtensions(std::vector &extensions) const + { + extensions.push_back("GL_ARB_vertex_program"); + extensions.push_back("GL_ARB_texture_env_dot3"); + } + + void define_passes() + { + if (diffuseunit_ != (normalunit_ + 1)) { + osg::notify(osg::WARN) << "Warning: osgFX::BumpMapping: this technique (ArbVpTechnique) requires that diffuse_unit == (normal_unit + 1). Effect may not show up properly.\n"; + } + + // first pass, diffuse bump + { + std::ostringstream vp_oss; + vp_oss << + "!!ARBvp1.0\n" + "OPTION ARB_position_invariant;" + "PARAM c0 = { 0.5, 1, 0, 0 };" + "TEMP R0, R1, R2;" + "ATTRIB v5 = vertex.attrib[15];" + "ATTRIB v4 = vertex.attrib[7];" + "ATTRIB v3 = vertex.attrib[6];" + "ATTRIB v24 = vertex.texcoord[" << normalunit_ << "];" + "ATTRIB v25 = vertex.texcoord[" << diffuseunit_ << "];" + "ATTRIB v18 = vertex.normal;" + "ATTRIB v16 = vertex.position;" + "PARAM s259[4] = { state.matrix.mvp };" + "PARAM s18 = state.light[" << lightnum_ << "].position;" + "PARAM s223[4] = { state.matrix.modelview[0] };" + " MOV result.texcoord[" << diffuseunit_ << "].xy, v25;" + " MOV result.texcoord[" << normalunit_ << "].xy, v24;" + " DP3 R0.y, s223[0].xyzx, v3.xyzx;" + " DP3 R0.z, s223[1].xyzx, v3.xyzx;" + " DP3 R0.w, s223[2].xyzx, v3.xyzx;" + " DP3 R0.x, s18.xyzx, s18.xyzx;" + " RSQ R0.x, R0.x;" + " MUL R2.xyz, R0.x, s18.xyzx;" + " DP3 R1.x, R0.yzwy, R2.xyzx;" + " DP3 R0.x, s223[0].xyzx, v4.xyzx;" + " DP3 R0.y, s223[1].xyzx, v4.xyzx;" + " DP3 R0.z, s223[2].xyzx, v4.xyzx;" + " DP3 R1.y, R0.xyzx, R2.xyzx;" + " DP3 R0.x, s223[0].xyzx, v5.xyzx;" + " DP3 R0.y, s223[1].xyzx, v5.xyzx;" + " DP3 R0.z, s223[2].xyzx, v5.xyzx;" + " DP3 R1.z, R0.xyzx, R2.xyzx;" + " MAD result.color.front.primary.xyz, c0.x, R1.xyzx, c0.x;" + " MOV result.color.front.primary.w, c0.y;" + "END\n"; + + osg::ref_ptr ss = new osg::StateSet; + + osg::ref_ptr vp = new osg::VertexProgram; + vp->setVertexProgram(vp_oss.str()); + ss->setAttributeAndModes(vp.get(), osg::StateAttribute::OVERRIDE|osg::StateAttribute::ON); + + if (diffuse_tex_.valid()) { + ss->setTextureAttributeAndModes(diffuseunit_, diffuse_tex_.get(), osg::StateAttribute::OVERRIDE|osg::StateAttribute::ON); + } + + if (normal_tex_.valid()) { + ss->setTextureAttributeAndModes(normalunit_, normal_tex_.get(), osg::StateAttribute::OVERRIDE|osg::StateAttribute::ON); + } + + osg::ref_ptr tec = new osg::TexEnvCombine; + tec->setCombine_RGB(osg::TexEnvCombine::DOT3_RGB); + tec->setSource0_RGB(osg::TexEnvCombine::PRIMARY_COLOR); + tec->setSource1_RGB(osg::TexEnvCombine::TEXTURE); + ss->setTextureAttributeAndModes(normalunit_, tec.get(), osg::StateAttribute::OVERRIDE|osg::StateAttribute::ON); + + osg::ref_ptr te = new osg::TexEnv; + te->setMode(osg::TexEnv::MODULATE); + ss->setTextureAttributeAndModes(diffuseunit_, te.get(), osg::StateAttribute::OVERRIDE|osg::StateAttribute::ON); + + addPass(ss.get()); + } + + // second pass, self-shadowing + { + std::ostringstream vp_oss; + vp_oss << + "!!ARBvp1.0\n" + "OPTION ARB_position_invariant;" + "PARAM c0 = { 8, 0, 1, 0 };" + "TEMP R0;" + "ATTRIB v18 = vertex.normal;" + "ATTRIB v16 = vertex.position;" + "PARAM s259[4] = { state.matrix.mvp };" + "PARAM s18 = state.light[" << lightnum_ << "].position;" + "PARAM s631[4] = { state.matrix.modelview[0].invtrans };" + " DP4 R0.x, s631[0], v18;" + " DP4 R0.y, s631[1], v18;" + " DP4 R0.z, s631[2], v18;" + " DP3 R0.x, R0.xyzx, s18.xyzx;" + " MAX R0.x, R0.x, c0.y;" + " MUL R0.x, c0.x, R0.x;" + " MIN result.color.front.primary.xyz, R0.x, c0.z;" + " MOV result.color.front.primary.w, c0.z;" + "END\n"; + + osg::ref_ptr ss = new osg::StateSet; + + osg::ref_ptr depth = new osg::Depth; + depth->setFunction(osg::Depth::EQUAL); + ss->setAttributeAndModes(depth.get(), osg::StateAttribute::OVERRIDE|osg::StateAttribute::ON); + + osg::ref_ptr vp = new osg::VertexProgram; + vp->setVertexProgram(vp_oss.str()); + ss->setAttributeAndModes(vp.get(), osg::StateAttribute::OVERRIDE|osg::StateAttribute::ON); + + osg::ref_ptr bf = new osg::BlendFunc; + bf->setFunction(osg::BlendFunc::DST_COLOR, osg::BlendFunc::ZERO); + ss->setAttributeAndModes(bf.get(), osg::StateAttribute::OVERRIDE|osg::StateAttribute::ON); + + ss->setTextureMode(diffuseunit_, GL_TEXTURE_2D, osg::StateAttribute::OVERRIDE|osg::StateAttribute::OFF); + ss->setTextureMode(normalunit_, GL_TEXTURE_2D, osg::StateAttribute::OVERRIDE|osg::StateAttribute::OFF); + + addPass(ss.get()); + } + + } + + protected: + int lightnum_; + int diffuseunit_; + int normalunit_; + osg::ref_ptr diffuse_tex_; + osg::ref_ptr normal_tex_; + }; + +} + + +BumpMapping::BumpMapping() +: Effect(), + lightnum_(0), + diffuseunit_(1), + normalunit_(0) +{ +} + +BumpMapping::BumpMapping(const BumpMapping ©, const osg::CopyOp ©op) +: Effect(copy, copyop), + lightnum_(copy.lightnum_), + diffuseunit_(copy.diffuseunit_), + normalunit_(copy.normalunit_), + diffuse_tex_(static_cast(copyop(copy.diffuse_tex_.get()))), + normal_tex_(static_cast(copyop(copy.normal_tex_.get()))) +{ +} + +bool BumpMapping::define_techniques() +{ + addTechnique(new FullArbTechnique(lightnum_, diffuseunit_, normalunit_, diffuse_tex_.get(), normal_tex_.get())); + addTechnique(new ArbVpTechnique(lightnum_, diffuseunit_, normalunit_, diffuse_tex_.get(), normal_tex_.get())); + return true; +} + +void BumpMapping::prepareGeometry(osg::Geometry *geo) +{ + osg::ref_ptr tsg = new osgUtil::TangentSpaceGenerator; + tsg->generate(geo, normalunit_); + if (!geo->getVertexAttribArray(6)) + geo->setVertexAttribArray(6, false, tsg->getTangentArray(), osg::Geometry::BIND_PER_VERTEX); + if (!geo->getVertexAttribArray(7)) + geo->setVertexAttribArray(7, false, tsg->getBinormalArray(), osg::Geometry::BIND_PER_VERTEX); + if (!geo->getVertexAttribArray(15)) + geo->setVertexAttribArray(15, false, tsg->getNormalArray(), osg::Geometry::BIND_PER_VERTEX); +} + +void BumpMapping::prepareNode(osg::Node *node) +{ + TsgVisitor tv(this); + node->accept(tv); +} + +void BumpMapping::prepareChild() +{ + if (getChild()) + prepareNode(getChild()); +} + +void BumpMapping::setUpDemo() +{ + // generate texture coordinates + TexCoordGenerator tcg(diffuseunit_, normalunit_); + getChild()->accept(tcg); + + // set up diffuse texture + if (!diffuse_tex_.valid()) { + diffuse_tex_ = new osg::Texture2D; + diffuse_tex_->setImage(osgDB::readImageFile("whitemetal_diffuse.jpg")); + diffuse_tex_->setFilter(osg::Texture::MIN_FILTER, osg::Texture::LINEAR_MIPMAP_LINEAR); + diffuse_tex_->setFilter(osg::Texture::MAG_FILTER, osg::Texture::LINEAR); + diffuse_tex_->setWrap(osg::Texture::WRAP_S, osg::Texture::REPEAT); + diffuse_tex_->setWrap(osg::Texture::WRAP_T, osg::Texture::REPEAT); + diffuse_tex_->setMaxAnisotropy(8); + } + + // set up normal map texture + if (!normal_tex_.valid()) { + normal_tex_ = new osg::Texture2D; + normal_tex_->setImage(osgDB::readImageFile("whitemetal_normal.jpg")); + normal_tex_->setFilter(osg::Texture::MIN_FILTER, osg::Texture::LINEAR_MIPMAP_LINEAR); + normal_tex_->setFilter(osg::Texture::MAG_FILTER, osg::Texture::LINEAR); + normal_tex_->setWrap(osg::Texture::WRAP_S, osg::Texture::REPEAT); + normal_tex_->setWrap(osg::Texture::WRAP_T, osg::Texture::REPEAT); + normal_tex_->setMaxAnisotropy(8); + } + + // generate tangent-space basis vector + prepareChild(); + + // recreate techniques on next step + dirtyTechniques(); +} diff --git a/src/osgFX/GNUmakefile b/src/osgFX/GNUmakefile index 234592c3f..080b9aa3e 100644 --- a/src/osgFX/GNUmakefile +++ b/src/osgFX/GNUmakefile @@ -3,6 +3,7 @@ include $(TOPDIR)/Make/makedefs CXXFILES =\ AnisotropicLighting.cpp\ + BumpMapping.cpp\ Cartoon.cpp\ Effect.cpp\ Registry.cpp\ diff --git a/src/osgPlugins/osgFX/GNUmakefile b/src/osgPlugins/osgFX/GNUmakefile index a56a7306c..20891879f 100644 --- a/src/osgPlugins/osgFX/GNUmakefile +++ b/src/osgPlugins/osgFX/GNUmakefile @@ -3,6 +3,7 @@ include $(TOPDIR)/Make/makedefs CXXFILES =\ IO_AnisotropicLighting.cpp\ + IO_BumpMapping.cpp\ IO_Cartoon.cpp\ IO_Scribe.cpp\ IO_SpecularHighlights.cpp\ diff --git a/src/osgPlugins/osgFX/IO_BumpMapping.cpp b/src/osgPlugins/osgFX/IO_BumpMapping.cpp new file mode 100644 index 000000000..fb0d7aa7d --- /dev/null +++ b/src/osgPlugins/osgFX/IO_BumpMapping.cpp @@ -0,0 +1,83 @@ +#include + +#include +#include +#include + +bool BumpMapping_readLocalData(osg::Object &obj, osgDB::Input &fr); +bool BumpMapping_writeLocalData(const osg::Object &obj, osgDB::Output &fw); + +osgDB::RegisterDotOsgWrapperProxy BumpMapping_Proxy +( + new osgFX::BumpMapping, + "osgFX::BumpMapping", + "Object Node osgFX::Effect osgFX::BumpMapping", + BumpMapping_readLocalData, + BumpMapping_writeLocalData +); + +bool BumpMapping_readLocalData(osg::Object &obj, osgDB::Input &fr) +{ + osgFX::BumpMapping &myobj = static_cast(obj); + bool itAdvanced = false; + + if (fr[0].matchWord("lightNumber")) { + int n; + if (fr[1].getInt(n)) { + myobj.setLightNumber(n); + fr += 2; + itAdvanced = true; + } + } + + if (fr[0].matchWord("diffuseUnit")) { + int n; + if (fr[1].getInt(n)) { + myobj.setDiffuseTextureUnit(n); + fr += 2; + itAdvanced = true; + } + } + + if (fr[0].matchWord("normalMapUnit")) { + int n; + if (fr[1].getInt(n)) { + myobj.setNormalMapTextureUnit(n); + fr += 2; + itAdvanced = true; + } + } + + osg::ref_ptr diffuse_tex = static_cast(fr.readObjectOfType(osgDB::type_wrapper())); + if (diffuse_tex.valid()) { + myobj.setOverrideDiffuseTexture(diffuse_tex.get()); + itAdvanced = true; + } + + osg::ref_ptr normal_tex = static_cast(fr.readObjectOfType(osgDB::type_wrapper())); + if (normal_tex.valid()) { + myobj.setOverrideDiffuseTexture(normal_tex.get()); + itAdvanced = true; + } + + return itAdvanced; +} + +bool BumpMapping_writeLocalData(const osg::Object &obj, osgDB::Output &fw) +{ + const osgFX::BumpMapping &myobj = static_cast(obj); + + fw.indent() << "lightNumber " << myobj.getLightNumber() << "\n"; + fw.indent() << "diffuseUnit " << myobj.getDiffuseTextureUnit() << "\n"; + fw.indent() << "normalMapUnit " << myobj.getNormalMapTextureUnit() << "\n"; + + if (myobj.getOverrideDiffuseTexture()) { + fw.writeObject(*myobj.getOverrideDiffuseTexture()); + } + + if (myobj.getOverrideNormalMapTexture()) { + fw.writeObject(*myobj.getOverrideNormalMapTexture()); + } + + return true; +} diff --git a/src/osgUtil/TangentSpaceGenerator.cpp b/src/osgUtil/TangentSpaceGenerator.cpp index 8f394890f..0d892fe4e 100644 --- a/src/osgUtil/TangentSpaceGenerator.cpp +++ b/src/osgUtil/TangentSpaceGenerator.cpp @@ -35,36 +35,67 @@ void TangentSpaceGenerator::generate(osg::Geometry *geo, int normal_map_tex_unit unsigned i; // VC6 doesn't like for-scoped variables - for (i=0; igetNumPrimitiveSets(); ++i) { - osg::PrimitiveSet *pset = geo->getPrimitiveSet(i); + for (unsigned pri=0; prigetNumPrimitiveSets(); ++pri) { + osg::PrimitiveSet *pset = geo->getPrimitiveSet(pri); + + unsigned N = pset->getNumIndices(); switch (pset->getMode()) { + case osg::PrimitiveSet::TRIANGLES: - for (i=0; igetNumIndices(); i+=3) { + 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); + if (pset->getType() == osg::PrimitiveSet::DrawArrayLengthsPrimitiveType) { + osg::DrawArrayLengths *dal = static_cast(pset); + unsigned j = 0; + for (osg::DrawArrayLengths::const_iterator pi=dal->begin(); pi!=dal->end(); ++pi) { + unsigned iN = static_cast(*pi-2); + for (i=0; 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); + if (pset->getType() == osg::PrimitiveSet::DrawArrayLengthsPrimitiveType) { + osg::DrawArrayLengths *dal = static_cast(pset); + unsigned j = 0; + for (osg::DrawArrayLengths::const_iterator pi=dal->begin(); pi!=dal->end(); ++pi) { + unsigned iN = static_cast(*pi-2); + for (i=0; i