[Solved] How To Trigger Editor To Redraw Node

Hi all,

I have written a custom shader program that I would like to apply on my node to distort the sprite attached on my node.

I called the line below to set the program on the node in the script attached to the node:
node.setShaderProgram(customProgram);

This works, and when I play the project on the Browser I can see my shader program activated on the node. But in the Cocos Creator editor it still shows the normal undistorted sprite. How do I get the editor to redraw the node with the shader applied on it? What if I need to animate the shader effect on the editor with a variable passed in from the node’s property?

I see this in the Creator code:
_sgNode._renderCmd.setDirtyFlag(_ccsg.Node._dirtyFlags.contentDirty);

but am not sure how to use it to trigger a redraw on the editor. Can anybody please help?

After poking around I found the solution.

To make the editor redraw and animate the shader, you would have to define a set() method for your property, and in that set method you will need to call your shader program and update it with the current value.

Example for my property:

properties: {
   _strength: 1.0,
  strength: {
        get: function () {
            return this._strength;
        },
        set: function (value) {
            this._strength = parseFloat(value);
            this.setShader();
            if(this._program) { // the shader is set
                this._program.use();
                this._program.setUniformLocationWith1f( this._strengthLoc, this._strength );
            }
        },
        type: cc.Float,
        animatable: true,
        displayName: 'Distortion Strength',
    },
},

No need for any setDirty! :slight_smile: Hope this helps someone.

1 Like

You could also use notify for efficiency sake. I found this method by reading the engine codes. Here’s a snip from cc.Label:

// Returns a function, that, as long as it continues to be invoked, will not
// be triggered. The function will be called after it stops being called for
// N milliseconds. If `immediate` is passed, trigger the function on the
// leading edge, instead of the trailing.

function debounce(func, wait, immediate) {
var timeout;
return CC_JSB ? function (...args) {
    var context = this;
    var later = function () {
        timeout = null;
        if (!immediate) func.apply(context, args);
    };
    var callNow = immediate && !timeout;
    clearTimeout(timeout);
    timeout = setTimeout(later, wait);
    if (callNow) func.apply(context, args);
} : function () {
    var context = this;
    var later = function () {
        timeout = null;
        if (!immediate) func.apply(context, arguments);
    };
    var callNow = immediate && !timeout;
    clearTimeout(timeout);
    timeout = setTimeout(later, wait);
    if (callNow) func.apply(context, arguments);
};
}

properties: {
    _useOriginalSize: true,
    /**
     * !#en Content string of label.
     * !#zh 标签显示的文本内容。
     * @property {String} string
     */
    string: {
        default: 'Label',
        multiline: true,
        tooltip: CC_DEV && 'i18n:COMPONENT.label.string',
        notify: function () {
            if (this._sgNode) {
                if (CC_EDITOR) {
                    if (this.overflow === cc.Label.Overflow.SHRINK) {
                        this.fontSize = this._userDefinedFontSize;
                    }
                    this._debouncedUpdateSgNodeString();
                } else {
                    this._updateSgNodeString();
                }
            }
        }
    },
1 Like