Loading objs with callbacks

B/c file operations in JS are asynchronous we can’t control the order they’re finished loading.

Also we don’t control what arguments are passed to a callback b/c they’re pre-determined by the caller.

Otherwise you could easily pass the index to be used for each loaded file.

However JS had a hidden hack gem which can create a clone of a function w/ modified context this and/or pre-filled parameters in addition to regular passed arguments:

Let’s take for example your loadObjectElement() function which invokes p5js loadModel() passing objectLoaded() as its callback:

'use strict';

const
  ASSETS  = 8,
  TOTAL   = ASSETS << 1, // 16

  FOLDER  = 'assets/',
  OBJ_EXT = '.obj',
  IMG_EXT = '.png',

  objects = Array(ASSETS).fill(),
  images  = Array(ASSETS).fill();

var
  loading = true,
  objCounter = 0,
  imgCounter = 0;

function setup() {
  // blah, blah, blah...

  for (var i = 0; i++ < ASSETS; ) {
    loadObj(FOLDER + i + OBJ_EXT, i - 1);
    loadImg(FOLDER + i + IMG_EXT, i - 1);
  }
}

function draw() {
  if (loading)  return;

  // blah, blah, blah...
}

function loadObj(filename, index) {
  loadModel(filename, true, objectLoaded.bind(null, index));
}

function objectLoaded(idx, obj) {
  objects[idx] = obj;
  if (++objCounter + imgCounter == TOTAL)  loading = false;
}

function loadImg(filename, index) {
  loadImage(filename, imageLoaded.bind(null, index));
}

function imageLoaded(idx, img) {
  images[idx] = img;
  if (++imgCounter + objCounter == TOTAL)  loading = false;
}

By passing a bind() callback to a loading function, we can force any number of preceding parameters to be filled w/ pre-determined arguments in addition to the caller’s sent arguments.

We already know the loading caller sends 1 argument to its callback, which is the loaded asset itself.

That’s why your objectLoaded() callback has 1 parameter named object:

In my “hacked” version I’ve added 1 extra parameter which comes before the regular parameter:

function objectLoaded(idx, obj) {
  objects[idx] = obj;
  if (++objCounter + imgCounter == TOTAL)  loading = false;
}

That extra parameter is gonna be filled w/ the index value I’ve passed to bind():

function loadObj(filename, index) {
  loadModel(filename, true, objectLoaded.bind(null, index));
}

And that’s the hacked trick!

BtW, the code gets smaller & less complex if you instead just use preload() to load your assets:

'use strict';

const
  ASSETS  = 8,
  FOLDER  = 'assets/',
  OBJ_EXT = '.obj',
  IMG_EXT = '.png',

  objects = Array(ASSETS).fill(),
  images  = Array(ASSETS).fill();

function preload() {
  for (var i = 0; i++ < ASSETS; ) {
    loadObj(FOLDER + i + OBJ_EXT, i - 1);
    loadImg(FOLDER + i + IMG_EXT, i - 1);
  }
}

function loadObj(filename, index) {
  loadModel(filename, true, objectLoaded.bind(null, index));
}

function objectLoaded(idx, obj) {
  objects[idx] = obj;
}

function loadImg(filename, index) {
  loadImage(filename, imageLoaded.bind(null, index));
}

function imageLoaded(idx, img) {
  images[idx] = img;
}