diff --git a/src/webgl/loading.js b/src/webgl/loading.js index 1e616b052f..51d12ced9b 100755 --- a/src/webgl/loading.js +++ b/src/webgl/loading.js @@ -25,6 +25,15 @@ import './p5.Geometry'; * Also, the support for colored STL files is not present. STL files with color will be * rendered without color properties. * + * Options can include: + * - `path`: Specifies the location or path of the 3D model file for loading. + * - `normalize`: Enables standardized size scaling during loading if set to true. + * - `successCallback`: Callback for post-loading actions with the 3D model object. + * - `failureCallback`: Handles errors if model loading fails, receiving an event error. + * - `fileType`: Defines the file extension of the model. + * - `flipU`: Flips the U texture coordinates of the model. + * - `flipV`: Flips the V texture coordinates of the model. + * * @method loadModel * @param {String} path Path of the model to be loaded * @param {Boolean} normalize If true, scale the model to a @@ -103,22 +112,42 @@ import './p5.Geometry'; * @param {String} [fileType] * @return {p5.Geometry} the p5.Geometry object */ -p5.prototype.loadModel = function(path) { +/** + * @method loadModel + * @param {String} path + * @param {Object} [options] + * @param {function(p5.Geometry)} [options.successCallback] + * @param {function(Event)} [options.failureCallback] + * @param {String} [options.fileType] + * @param {boolean} [options.normalize] + * @param {boolean} [options.flipU] + * @param {boolean} [options.flipV] + * @return {p5.Geometry} the p5.Geometry object + */ +p5.prototype.loadModel = function(path,options) { p5._validateParameters('loadModel', arguments); - let normalize; + let normalize= false; let successCallback; let failureCallback; + let flipU = false; + let flipV = false; let fileType = path.slice(-4); - if (typeof arguments[1] === 'boolean') { - normalize = arguments[1]; + if (options && typeof options === 'object') { + normalize = options.normalize || false; + successCallback = options.successCallback; + failureCallback = options.failureCallback; + fileType = options.fileType || fileType; + flipU = options.flipU || false; + flipV = options.flipV || false; + } else if (typeof options === 'boolean') { + normalize = options; successCallback = arguments[2]; failureCallback = arguments[3]; if (typeof arguments[4] !== 'undefined') { fileType = arguments[4]; } } else { - normalize = false; - successCallback = arguments[1]; + successCallback = typeof arguments[1] === 'function' ? arguments[1] : undefined; failureCallback = arguments[2]; if (typeof arguments[3] !== 'undefined') { fileType = arguments[3]; @@ -140,6 +169,15 @@ p5.prototype.loadModel = function(path) { if (normalize) { model.normalize(); } + + if (flipU) { + model.flipU(); + } + + if (flipV) { + model.flipV(); + } + self._decrementPreload(); if (typeof successCallback === 'function') { successCallback(model); @@ -157,6 +195,14 @@ p5.prototype.loadModel = function(path) { model.normalize(); } + if (flipU) { + model.flipU(); + } + + if (flipV) { + model.flipV(); + } + self._decrementPreload(); if (typeof successCallback === 'function') { successCallback(model); diff --git a/src/webgl/p5.Geometry.js b/src/webgl/p5.Geometry.js index 76c8ed8ad4..18731dfcb5 100644 --- a/src/webgl/p5.Geometry.js +++ b/src/webgl/p5.Geometry.js @@ -143,6 +143,150 @@ p5.Geometry = class Geometry { return this; } /** + * Flips the U texture coordinates of the model. + * @method flipU + * @for p5.Geometry + * + * @returns {p5.Geometry} + * + * @example + *
+ * + * let img; + * let model1; + * let model2; + * + * function preload() { + * img = loadImage('assets/laDefense.jpg'); + * } + * + * function setup() { + * createCanvas(150, 150, WEBGL); + * background(200); + * + * model1 = createShape(50, 50); + * model2 = createShape(50, 50); + * model2.flipU(); + * } + * + * function draw() { + * background(0); + * + * // original + * push(); + * translate(-40, 0, 0); + * texture(img); + * noStroke(); + * plane(50); + * model(model1); + * pop(); + * + * // flipped + * push(); + * translate(40, 0, 0); + * texture(img); + * noStroke(); + * plane(50); + * model(model2); + * pop(); + * } + * + * function createShape(w, h) { + * return buildGeometry(() => { + * textureMode(NORMAL); + * beginShape(); + * vertex(-w / 2, -h / 2, 0, 0); + * vertex(w / 2, -h / 2, 1, 0); + * vertex(w / 2, h / 2, 1, 1); + * vertex(-w / 2, h / 2, 0, 1); + * endShape(CLOSE); + * }); + * } + * + *
+ */ + flipU() { + this.uvs = this.uvs.flat().map((val, index) => { + if (index % 2 === 0) { + return 1 - val; + } else { + return val; + } + }); + } + /** + * Flips the V texture coordinates of the model. + * @method flipV + * @for p5.Geometry + * + * @returns {p5.Geometry} + * + * @example + *
+ * + * let img; + * let model1; + * let model2; + * + * function preload() { + * img = loadImage('assets/laDefense.jpg'); + * } + * + * function setup() { + * createCanvas(150, 150, WEBGL); + * background(200); + * + * model1 = createShape(50, 50); + * model2 = createShape(50, 50); + * model2.flipV(); + * } + * + * function draw() { + * background(0); + * + * // original + * push(); + * translate(-40, 0, 0); + * texture(img); + * noStroke(); + * plane(50); + * model(model1); + * pop(); + * + * // flipped + * push(); + * translate(40, 0, 0); + * texture(img); + * noStroke(); + * plane(50); + * model(model2); + * pop(); + * } + * + * function createShape(w, h) { + * return buildGeometry(() => { + * textureMode(NORMAL); + * beginShape(); + * vertex(-w / 2, -h / 2, 0, 0); + * vertex(w / 2, -h / 2, 1, 0); + * vertex(w / 2, h / 2, 1, 1); + * vertex(-w / 2, h / 2, 0, 1); + * endShape(CLOSE); + * }); + * } + * + *
+ */ + flipV() { + this.uvs = this.uvs.flat().map((val, index) => { + if (index % 2 === 0) { + return val; + } else { + return 1 - val; + } + }); + } + /** * computes faces for geometry objects based on the vertices. * @method computeFaces * @chainable