Ver código fonte

Changed and simplified handling of custom material auto bindings by adding a new abstract class RenderState::AutoBindingResolver.
Games can now simply extend this class and implement the one virtual function in it (resolveAutoBinding) to provide support for custom material auto bindings.

sgrenier 12 anos atrás
pai
commit
6a99cd1bc8

+ 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;