Browse Source

Merge pull request #1334 from sgrenier/next

Material auto binding changes
Steve Grenier 12 years ago
parent
commit
6ce336707e

+ 3 - 3
gameplay/src/MaterialParameter.cpp

@@ -497,11 +497,11 @@ void MaterialParameter::bind(Effect* effect)
         effect->setValue(_uniform, _value.samplerArrayValue, _count);
         break;
     case MaterialParameter::METHOD:
-        GP_ASSERT(_value.method);
-        _value.method->setValue(effect);
+        if (_value.method)
+            _value.method->setValue(effect);
         break;
     default:
-        GP_ERROR("Unsupported material parameter type (%d).", _type);
+        GP_WARN("Unknown type (%d) for material parameter: %s", _type, _name.c_str());
         break;
     }
 }

+ 18 - 14
gameplay/src/RenderState.cpp

@@ -26,7 +26,7 @@ namespace gameplay
 {
 
 RenderState::StateBlock* RenderState::StateBlock::_defaultState = NULL;
-std::vector<RenderState::ResolveAutoBindingCallback> RenderState::_customAutoBindingResolvers;
+std::vector<RenderState::AutoBindingResolver*> RenderState::_customAutoBindingResolvers;
 
 RenderState::RenderState()
     : _nodeBinding(NULL), _state(NULL), _parent(NULL)
@@ -57,11 +57,6 @@ void RenderState::finalize()
     SAFE_RELEASE(StateBlock::_defaultState);
 }
 
-void RenderState::registerAutoBindingResolver(ResolveAutoBindingCallback callback)
-{
-    _customAutoBindingResolvers.push_back(callback);
-}
-
 MaterialParameter* RenderState::getParameter(const char* name) const
 {
     GP_ASSERT(name);
@@ -248,16 +243,13 @@ void RenderState::applyAutoBinding(const char* uniformName, const char* autoBind
     bool bound = false;
 
     // First attempt to resolve the binding using custom registered resolvers.
-    if (_customAutoBindingResolvers.size() > 0)
+    for (size_t i = 0, count = _customAutoBindingResolvers.size(); i < count; ++i)
     {
-        for (size_t i = 0, count = _customAutoBindingResolvers.size(); i < count; ++i)
+        if (_customAutoBindingResolvers[i]->resolveAutoBinding(autoBinding, _nodeBinding, param))
         {
-            if (_customAutoBindingResolvers[i](autoBinding, _nodeBinding, param))
-            {
-                // Handled by custom auto binding resolver
-                bound = true;
-                break;
-            }
+            // Handled by custom auto binding resolver
+            bound = true;
+            break;
         }
     }
 
@@ -1220,4 +1212,16 @@ void RenderState::StateBlock::setStencilOperation(StencilOperation sfail, Stenci
 	}
 }
 
+RenderState::AutoBindingResolver::AutoBindingResolver()
+{
+    _customAutoBindingResolvers.push_back(this);
+}
+
+RenderState::AutoBindingResolver::~AutoBindingResolver()
+{
+    std::vector<RenderState::AutoBindingResolver*>::iterator itr = std::find(_customAutoBindingResolvers.begin(), _customAutoBindingResolvers.end(), this);
+    if (itr != _customAutoBindingResolvers.end())
+        _customAutoBindingResolvers.erase(itr);
+}
+
 }

+ 69 - 51
gameplay/src/RenderState.h

@@ -26,6 +26,74 @@ class RenderState : public Ref
 
 public:
 
+    /**
+    * An abstract base class that can be extended to support custom material auto bindings.
+    *
+    * Implementing a custom auto binding resolver allows the set of built-in parameter auto
+    * bindings to be extended or overridden. Any parameter auto binding that is set on a
+    * material will be forwarded to any custom auto binding resolvers, in the order in which
+    * they are registered. If a registered resolver returns true (specifying that it handles
+    * the specified autoBinding), no further code will be executed for that autoBinding.
+    * This allows auto binding resolvers to not only implement new/custom binding strings,
+    * but it also lets them override existing/built-in ones. For this reason, you should
+    * ensure that you ONLY return true if you explicitly handle a custom auto binding; return
+    * false otherwise.
+    *
+    * Note that the custom resolver is called only once for a RenderState object when its
+    * node binding is initially set. This occurs when a material is initially bound to a
+    * renderable (Model, Terrain, etc) that belongs to a Node. The resolver is NOT called
+    * each frame or each time the RenderState is bound. Therefore, when implementing custom
+    * auto bindings for values that change over time, you should bind a method pointer to
+    * the passed in MaterialParaemter using the MaterialParameter::bindValue method. This way,
+    * the bound method will be called each frame to set an updated value into the MaterialParameter.
+    *
+    * If no registered resolvers explicitly handle an auto binding, the binding will attempt
+    * to be resolved using the internal/built-in resolver, which is able to handle any
+    * auto bindings found in the RenderState::AutoBinding enumeration.
+    *
+    * When an instance of a class that extends AutoBindingResolver is created, it is automatically
+    * registered as a custom auto binding handler. Likewise, it is automatically deregistered
+    * on destruction.
+    *
+    * @script{ignore}
+    */
+    class AutoBindingResolver
+    {
+    public:
+
+        /**
+         * Destructor.
+         */
+        virtual ~AutoBindingResolver();
+
+        /**
+        * Called when an unrecognized material auto binding is encountered
+        * during material loading.
+        *
+        * Implemenations of this method should do a string comparison on the passed
+        * in name parameter and decide whether or not they should handle the
+        * parameter. If the parameter is not handled, false should be returned so
+        * that other auto binding resolvers get a chance to handle the parameter.
+        * Otherwise, the parameter should be set or bound and true should be returned.
+        *
+        * @param autoBinding Name of the auto binding to be resolved.
+        * @param node The node that the material is attached to.
+        * @param parameter The material parameter to be bound (if true is returned).
+        *
+        * @return True if the auto binding is handled and the associated parmeter is
+        *      bound, false otherwise.
+        */
+        virtual bool resolveAutoBinding(const char* autoBinding, Node* node, MaterialParameter* parameter) = 0;
+
+    protected:
+
+        /**
+         * Constructor.
+         */
+        AutoBindingResolver();
+
+    };
+
     /**
      * Built-in auto-bind targets for material parameters.
      */
@@ -94,26 +162,6 @@ public:
         SCENE_AMBIENT_COLOR
     };
 
-    /**
-     * Callback function prototype for resolving material parameter auto bindings.
-     *
-     * Functions matching this callback signature can be registered via the 
-     * RenderState::registerAutoBindingResolver method to extend or override the set
-     * of built-in material parameter auto bindings.
-     *
-     * @param autoBinding Name of the auto binding to resolve.
-     * @param node Node that is bound to the material of the specified parameter.
-     * @param parameter Material parameter to set the binding on.
-     *
-     * @return True ONLY if the implementations explicitly handles the auto binding, false otherwise.
-     *      Returning true here will prevent any further code (including built-in resolving code) from
-     *      handling the auto binding.
-     *
-     * @see RenderState::registerAutoBindingResolver(const char*, RenderState::AutoBindingResolver)
-     * @script{ignore}
-     */
-    typedef bool (*ResolveAutoBindingCallback) (const char* autoBinding, Node* node, MaterialParameter* parameter);
-
     /**
      * Defines blend constants supported by the blend function.
      */
@@ -523,36 +571,6 @@ public:
      */
     virtual void setNodeBinding(Node* node);
 
-    /**
-     * Registers a custom auto binding resolver.
-     *
-     * Implementing a custom auto binding resolver allows the set of built-in parameter auto
-     * bindings to be extended or overridden. Any parameter auto binding that is set on a
-     * material will be forwarded to any custom auto binding resolvers, in the order in which
-     * they are registered. If a registered resolver returns true (specifying that it handles
-     * the specified autoBinding), no further code will be executed for that autoBinding.
-     * This allows auto binding resolvers to not only implement new/custom binding strings,
-     * but it also lets them override existing/built-in ones. For this reason, you should
-     * ensure that you ONLY return true if you explicitly handle a custom auto binding; return
-     * false otherwise.
-     *
-     * Note that the custom resolver is called only once for a RenderState object when its
-     * node binding is initially set. This occurs when a material is initially bound to a
-     * Model that belongs to a Node. The resolver is NOT called each frame or each time
-     * the RenderState is bound. Therefore, when implementing custom auto bindings for values
-     * that change over time, the you should bind a method pointer onto the passed in
-     * MaterialParaemter using the MaterialParameter::bindValue method. This way, the bound
-     * method will be called each frame to set an updated value into the MaterialParameter.
-     *
-     * If no registered resolvers explicitly handle an auto binding, the binding will attempt
-     * to be resolved using the internal/built-in resolver, which is able to handle any
-     * auto bindings found in the RenderState::AutoBinding enumeration.
-     *
-     * @param callback Callback function for resolving parameter auto bindings.
-     * @script{ignore}
-     */
-    static void registerAutoBindingResolver(ResolveAutoBindingCallback callback);
-
 protected:
 
     /**
@@ -661,7 +679,7 @@ protected:
     /**
      * Map of custom auto binding resolvers.
      */
-    static std::vector<ResolveAutoBindingCallback> _customAutoBindingResolvers;
+    static std::vector<AutoBindingResolver*> _customAutoBindingResolvers;
 };
 
 }

+ 3 - 1
gameplay/src/Terrain.h

@@ -13,6 +13,7 @@ namespace gameplay
 
 class Node;
 class TerrainPatch;
+class TerrainAutoBindingResolver;
 
 /**
  * Defines a Terrain that is capable of rendering large landscapes from 2D heightmap images.
@@ -82,9 +83,10 @@ class TerrainPatch;
 class Terrain : public Ref, public Transform::Listener
 {
     friend class Node;
-    friend class TerrainPatch;
     friend class PhysicsController;
     friend class PhysicsRigidBody;
+    friend class TerrainPatch;
+    friend class TerrainAutoBindingResolver;
 
 public:
 

+ 11 - 8
gameplay/src/TerrainPatch.cpp

@@ -23,17 +23,20 @@ template <class T> T clamp(T value, T min, T max) { return value < min ? min : (
 #define TERRAINPATCH_DIRTY_LEVEL 4
 #define TERRAINPATCH_DIRTY_ALL (TERRAINPATCH_DIRTY_MATERIAL | TERRAINPATCH_DIRTY_BOUNDS | TERRAINPATCH_DIRTY_LEVEL)
 
-static bool __autoBindingResolverRegistered = false;
+/**
+ * Custom material auto-binding resolver for terrain.
+ * @script{ignore}
+ */
+class TerrainAutoBindingResolver : RenderState::AutoBindingResolver
+{
+    bool resolveAutoBinding(const char* autoBinding, Node* node, MaterialParameter* parameter);
+};
+static TerrainAutoBindingResolver __autoBindingResolver;
 static int __currentPatchIndex = -1;
 
 TerrainPatch::TerrainPatch() :
-_terrain(NULL), _row(0), _column(0), _camera(NULL), _level(0), _bits(TERRAINPATCH_DIRTY_ALL)
+    _terrain(NULL), _row(0), _column(0), _camera(NULL), _level(0), _bits(TERRAINPATCH_DIRTY_ALL)
 {
-    if (!__autoBindingResolverRegistered)
-    {
-        RenderState::registerAutoBindingResolver(TerrainPatch::autoBindingResolver);
-        __autoBindingResolverRegistered = true;
-    }
 }
 
 TerrainPatch::~TerrainPatch()
@@ -708,7 +711,7 @@ void TerrainPatch::updateNodeBinding(Node* node)
     __currentPatchIndex = -1;
 }
 
-bool TerrainPatch::autoBindingResolver(const char* autoBinding, Node* node, MaterialParameter* parameter)
+bool TerrainAutoBindingResolver::resolveAutoBinding(const char* autoBinding, Node* node, MaterialParameter* parameter)
 {
     if (strcmp(autoBinding, "TERRAIN_WORLD_MATRIX") == 0)
     {

+ 2 - 2
gameplay/src/TerrainPatch.h

@@ -8,6 +8,7 @@ namespace gameplay
 {
 
 class Terrain;
+class TerrainAutoBindingResolver;
 
 /**
  * Defines a single patch for a Terrain.
@@ -15,6 +16,7 @@ class Terrain;
 class TerrainPatch : public Camera::Listener
 {
     friend class Terrain;
+    friend class TerrainAutoBindingResolver;
 
 public:
 
@@ -134,8 +136,6 @@ private:
 
     std::string passCreated(Pass* pass);
 
-    static bool autoBindingResolver(const char* autoBinding, Node* node, MaterialParameter* parameter);
-
     Terrain* _terrain;
     unsigned int _index;
     unsigned int _row;

+ 3 - 0
samples/browser/res/common/terrain/shapes.material

@@ -2,7 +2,10 @@ material textured
 {
     u_worldViewProjectionMatrix = WORLD_VIEW_PROJECTION_MATRIX
     u_inverseTransposeWorldViewMatrix = INVERSE_TRANSPOSE_WORLD_VIEW_MATRIX
+    
     u_ambientColor = SCENE_AMBIENT_COLOR
+    u_directionalLightDirection[0] = LIGHT0_DIRECTION_VIEW
+    u_directionalLightColor[0] = LIGHT0_COLOR
 
     sampler u_diffuseTexture
     {

+ 2 - 2
samples/browser/res/common/terrain/terrain.material

@@ -9,8 +9,8 @@ material terrain
     u_surfaceLayerMaps = TERRAIN_LAYER_MAPS
 
     u_ambientColor = SCENE_AMBIENT_COLOR
-    u_directionalLightDirection[0] = DIRECTIONAL_LIGHT_DIRECTION
-    u_directionalLightColor[0] = DIRECTIONAL_LIGHT_COLOR
+    u_directionalLightDirection[0] = LIGHT0_DIRECTION_WORLD
+    u_directionalLightColor[0] = LIGHT0_COLOR
 
     renderState
     {

+ 20 - 31
samples/browser/src/TerrainSample.cpp

@@ -21,21 +21,11 @@ struct TerrainHitFilter : public PhysicsController::HitFilter
     PhysicsCollisionObject* terrainObject;
 };
 
-static TerrainSample* __instance = NULL;
-
 TerrainSample::TerrainSample()
 	: _font(NULL), _scene(NULL), _terrain(NULL), _sky(NULL), _form(NULL), _formVisible(true),
 	  _wireframe(false), _debugPhysics(false), _snapToGround(true), _vsync(true),
       _mode(MODE_LOOK), _sphere(NULL), _box(NULL), _directionalLight(NULL)
 {
-    __instance = this;
-
-    static bool registered = false;
-    if (!registered)
-    {
-        RenderState::registerAutoBindingResolver(&resolveAutoBinding);
-        registered = true;
-    }
 }
 
 TerrainSample::~TerrainSample()
@@ -45,9 +35,6 @@ TerrainSample::~TerrainSample()
     SAFE_RELEASE(_form);
     SAFE_RELEASE(_font);
     SAFE_RELEASE(_scene);
-
-    if (__instance == this)
-        __instance = NULL;
 }
 
 void TerrainSample::initialize()
@@ -62,10 +49,12 @@ void TerrainSample::initialize()
     Bundle* bundle;
     bundle = Bundle::create("res/common/sphere.gpb");
     _sphere = bundle->loadNode("sphere");
+    _sphere->getModel()->setMaterial("res/common/terrain/shapes.material#sphere", 0);
     SAFE_RELEASE(bundle);
 
     bundle = Bundle::create("res/common/box.gpb");
     _box = bundle->loadNode("box");
+    _box->getModel()->setMaterial("res/common/terrain/shapes.material#box", 0);
     SAFE_RELEASE(bundle);
 
     // Load font
@@ -253,7 +242,6 @@ void TerrainSample::touchEvent(Touch::TouchEvent evt, int x, int y, unsigned int
             {
                 Node* clone = NULL;
                 PhysicsCollisionShape::Definition rbShape;
-                const char* materialUrl = NULL;
 
                 switch (_mode)
                 {
@@ -261,7 +249,6 @@ void TerrainSample::touchEvent(Touch::TouchEvent evt, int x, int y, unsigned int
                     {
                         clone = _sphere->clone();
                         rbShape = PhysicsCollisionShape::sphere();
-                        materialUrl = "res/common/terrain/shapes.material#sphere";
                     }
                     break;
 
@@ -269,7 +256,6 @@ void TerrainSample::touchEvent(Touch::TouchEvent evt, int x, int y, unsigned int
                     {
                         clone = _box->clone();
                         rbShape = PhysicsCollisionShape::box();
-                        materialUrl = "res/common/terrain/shapes.material#box";
                     }
                     break;
                 }
@@ -383,34 +369,37 @@ void TerrainSample::setMessage(const char* message)
     _form->getControl("messageBox")->setVisible(message ? true : false);
 }
 
-Vector3 TerrainSample::getDirectionalLightDirection() const
+Vector3 TerrainSample::getLight0DirectionWorld() const
 {
-    // Terrain expects world-space light vectors
     return _directionalLight->getNode()->getForwardVectorWorld();
 }
 
-Vector3 TerrainSample::getDirectionalLightColor() const
+Vector3 TerrainSample::getLight0DirectionView() const
+{
+    return _directionalLight->getNode()->getForwardVectorView();
+}
+
+Vector3 TerrainSample::getLight0Color() const
 {
     return _directionalLight->getColor();
 }
 
 bool TerrainSample::resolveAutoBinding(const char* autoBinding, Node* node, MaterialParameter* parameter)
 {
-    if (strcmp(autoBinding, "DIRECTIONAL_LIGHT_DIRECTION") == 0)
+    if (strcmp(autoBinding, "LIGHT0_DIRECTION_WORLD") == 0)
     {
-        if (__instance)
-        {
-            parameter->bindValue(__instance, &TerrainSample::getDirectionalLightDirection);
-            return true;
-        }
+        parameter->bindValue(this, &TerrainSample::getLight0DirectionWorld);
+        return true;
     }
-    else if (strcmp(autoBinding, "DIRECTIONAL_LIGHT_COLOR") == 0)
+    if (strcmp(autoBinding, "LIGHT0_DIRECTION_VIEW") == 0)
     {
-        if (__instance)
-        {
-            parameter->bindValue(__instance, &TerrainSample::getDirectionalLightColor);
-            return true;
-        }
+        parameter->bindValue(this, &TerrainSample::getLight0DirectionView);
+        return true;
+    }
+    else if (strcmp(autoBinding, "LIGHT0_COLOR") == 0)
+    {
+        parameter->bindValue(this, &TerrainSample::getLight0Color);
+        return true;
     }
 
     return false;

+ 6 - 6
samples/browser/src/TerrainSample.h

@@ -6,7 +6,7 @@
 
 using namespace gameplay;
 
-class TerrainSample : public Sample, public Control::Listener
+class TerrainSample : public Sample, public Control::Listener, private RenderState::AutoBindingResolver
 {
 public:
 
@@ -22,8 +22,6 @@ public:
 
     void controlEvent(Control* control, EventType evt);
 
-    static bool resolveAutoBinding(const char* autoBinding, Node* node, MaterialParameter* parameter);
-
 protected:
 
     void initialize();
@@ -49,9 +47,11 @@ private:
         MODE_DROP_BOX
     };
 
-    Vector3 getDirectionalLightDirection() const;
-    
-    Vector3 getDirectionalLightColor() const;
+    Vector3 getLight0DirectionWorld() const;
+    Vector3 getLight0DirectionView() const;
+    Vector3 getLight0Color() const;
+
+    bool resolveAutoBinding(const char* autoBinding, Node* node, MaterialParameter* parameter);
 
 	Font* _font;
 	Scene* _scene;