Problem moveing 'loadImageFromUrl' inside a func

var ChangeMaterial2 = pc.createScript('changeMaterial2');

ChangeMaterial2.attributes.add("targetMaterial", {type: 'number'});

var _myMaterial;    
var imageUrl = https://s3-us-west-2.amazonaws.com/ticomsoft-image-repo/1.png;

var me;

ChangeMaterial2.prototype.initialize = function() {
    _myMaterial = this.targetMaterial;
    var material = this.entity.model.meshInstances[_myMaterial].material;    
    
    // allow cross origin texture requests
    this.app.loader.getHandler("texture").crossOrigin = "anonymous";
        
    var asset = new pc.Asset("myTexture", "texture", {
        url: imageUrl
    });
    
    this.app.assets.add(asset);
    asset.on("load", function (asset) {
        material.diffuseMap = asset.resource;
        material.update();
    });
    
    this.app.assets.load(asset);

};

this works fine

but, if i move the


    // allow cross origin texture requests
    this.app.loader.getHandler("texture").crossOrigin = "anonymous";
        
    var asset = new pc.Asset("myTexture", "texture", {
        url: imageUrl

inside ImgFromURLWithCORS:

var ChangeMaterial2 = pc.createScript('changeMaterial2');

ChangeMaterial2.attributes.add("targetMaterial", {type: 'number'});

var _myMaterial;    
var imageUrl = https://s3-us-west-2.amazonaws.com/ticomsoft-image-repo/1.png;

ChangeMaterial2.prototype.initialize = function() {
    _myMaterial = this.targetMaterial;
    var material = this.entity.model.meshInstances[_myMaterial].material;    
    
    ImgFromURLWithCORS();
};

function ImgFromURLWithCORS()
{
    // allow cross origin texture requests
    this.app.loader.getHandler("texture").crossOrigin = "anonymous";
        
    var asset = new pc.Asset("myTexture", "texture", {
        url: imageUrl
    });
    this.app.assets.add(asset);
    asset.on("load", function (asset) {
        material.diffuseMap = asset.resource;
        material.update();
    });
    
    this.app.assets.load(asset);
}

I get a strange error that _TypeError: Cannot read property ‘loader’ of undefined.

is _this.app _only exists in the initilize scope?

ImgFromURLwithCORS is not within the scope of the PlayCanvas script object so the this pointer isn’t pointing to the object that you think it is.

Change to (untested as I needed to fix scoping issues):

var ChangeMaterial2 = pc.createScript('changeMaterial2');

ChangeMaterial2.attributes.add("targetMaterial", {type: 'number'});

var imageUrl = https://s3-us-west-2.amazonaws.com/ticomsoft-image-repo/1.png;

ChangeMaterial2.prototype.initialize = function() {
    this.material = this.entity.model.meshInstances[this.targetMaterial].material;    
    this.ImgFromURLwithCORS();
};

ChangeMaterial2.prototype.ImgFromURLwithCORS = function ()
{
    var self = this;
    // allow cross origin texture requests
    this.app.loader.getHandler("texture").crossOrigin = "anonymous";
        
    var asset = new pc.Asset("myTexture", "texture", {
        url: imageUrl
    });

    this.app.assets.add(asset);
    asset.on("load", function (asset) {
        self.material.diffuseMap = asset.resource;
        self.material.update();
    });
    
    this.app.assets.load(asset);
}
1 Like

yup
looks like it works
thank you

could you please explain to me where does the function ImgFromURLWithCORS
points and where does the ChangeMaterial2.prototype.ImgFromURLwithCORS so I can better understand the proper usage?

I know what prototype is , just not where is it pointing.

In a nutshell, in this instance:

In this case, global scope. It depends on where it is defined that can change the scope of the function or what it can access. (See function closures: https://www.w3schools.com/js/js_function_closures.asp).

Class scope

so if i try to make some resemblance to C# for instance

then the prototype.function are like writing inside the “public class”

while functions outside exist in the global scope?

  1. does it mean that can i or can’t i call a prototype.function from another script?
  2. can they co exist?
var ChangeMaterial2 = pc.createScript('changeMaterial2');
var _app;
var _myID;
var _Material;    


var URL_prefix ='https://s3-us-west-2.amazonaws.com/ticomsoft-image-repo/';
var URL_sufix = '.png';

// exposed fields
ChangeMaterial2.attributes.add("targetMaterial", {type: 'number'}); //targetMaterial
ChangeMaterial2.attributes.add("ID", {type: 'number'}); //ID


var imageUrl;
var material;

ChangeMaterial2.prototype.initialize = function() {
    _Material = this.targetMaterial;
    _myID = this.ID;
    _app=this.app;
    this.UpdateID();
    
    material = this.entity.model.meshInstances[_Material].material;    
    
    this.ImgFromURLwithCORS();
};

function NextImage()
{
    _myID++;
     this.UpdateID();
}


function PrevImage()
{
    _myID--;
    this.UpdateID();
}


ChangeMaterial2.prototype.ImgFromURLwithCORS = function()
{
        // allow cross origin texture requests
    _app.loader.getHandler("texture").crossOrigin = "anonymous";
        
    var asset = new pc.Asset("myTexture", "texture", {
        url: imageUrl
    });
    
    _app.assets.add(asset);
    asset.on("load", function (asset) {
        material.diffuseMap = asset.resource;
        material.update();
    });
    
    _app.assets.load(asset);

};

ChangeMaterial2.prototype.UpdateID = function()
{
    imageUrl = URL_prefix+_myID+URL_sufix;
};

here I want to use ‘next image’ and ‘prev image’ externally.
but if I call them, I get an exception that UpdateID does not exist (why? because the functions live in the open world where class scope vars do not exist?)
if so, how would I call a the ‘next image’ and ‘prev image’ functions?
and if again - prototype, then I’m starting to loose the grasp of when should I use the function foo(){}

  1. Everything in JS is public. As long as you have an instance of the class instance, you can call the function. A common pattern for JS is to have a prefix _ to show that they shouldn’t be used external to the class and that they are ‘private’. They can still be accessed though if a developer wants to.

  2. Yes. You just have to be mindful of what can be accessed where based on variable scope. In the earlier case, a global function won’t have access to a class instance unless it gets passed in as an parameter or if it is in global scope as well.

Let’s put this another way, how would you write this in C#?

i’de write (pseudo code)

public class ChangeMaterial2(){

int id
//... all the rest of the vars

private func init(){
    //init vars...
    UpdateID();
    ImgFromURLwithCORS(){}
}

public function next(){
    id++;
    UpdateID()
    ImgFromURLwithCORS();
}

public function prev(){
    id--;
    UpdateID()
   ImgFromURLwithCORS();
}

private func UpdateID(){...}

private func ImgFromURLwithCORS(){...}

}

but that’s cause the public func has access to the private and vice versa… its only a matter of external access (so i guess that the private vars will be marked with _).

the problem is that the function next(){} cannot access the this.UpdateID() when i define it as ChangeMaterial2.prototype.UpdateID(){}.

This is what your C# class would translate to.

In your C# class, you have the next and prev functions within the class scope but your JS code doesn’t do that. Why?

All the functions are part of the class prototype so the this reference will be pointing to the instance of the class and therefore can reference all the attributes and functions of the class.

Again, untested but you should get the general idea.

var ChangeMaterial2 = pc.createScript('changeMaterial2');

// Changing this to class scope rather than polluting the global scope
ChangeMaterial2.URL_prefix ='https://s3-us-west-2.amazonaws.com/ticomsoft-image-repo/';
ChangeMaterial2.URL_sufix = '.png';

ChangeMaterial2.attributes.add("targetMaterial", {type: 'number'}); //targetMaterial
ChangeMaterial2.attributes.add("id", {type: 'number'}); //ID

ChangeMaterial2.prototype.initialize = function() 
{
    this.imageUrl = "";
    this.material = this.entity.model.meshInstances[this.targetMaterial].material;    
 
    this.updateID();   
    this.imgFromURLwithCORS();
};

ChangeMaterial2.prototype.next = function() 
{
    this.id++;
    this.updateID()
    this.imgFromURLwithCORS();
};

ChangeMaterial2.prototype.prev = function()
{
    this.id--;
    this.updateID()
    this.imgFromURLwithCORS();
};

ChangeMaterial2.prototype.imgFromURLwithCORS = function()
{
	var app = this.app;
	var self = this;
	
    // allow cross origin texture requests
    app.loader.getHandler("texture").crossOrigin = "anonymous";
        
    var asset = new pc.Asset("myTexture", "texture", {
        url: this.imageUrl
    });
    
    app.assets.add(asset);
    asset.on("load", function (asset) {
        self.material.diffuseMap = asset.resource;
        self.material.update();
    });
    
	app.assets.load(asset);

};

ChangeMaterial2.prototype.updateID = function()
{
    this.imageUrl = ChangeMaterial2.URL_prefix + this.id + ChangeMaterial2.URL_sufix;
};