-
-
Notifications
You must be signed in to change notification settings - Fork 3.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
added createModel() to enable loading stl/obj files from a plaintext string within editor #6936
base: main
Are you sure you want to change the base?
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -9,6 +9,204 @@ | |
import p5 from '../core/main'; | ||
import './p5.Geometry'; | ||
|
||
/** | ||
* Load a 3d model from an OBJ or STL string. | ||
* | ||
* <a href="#/p5/loadModel">createModel()</a> should be placed inside of <a href="#/p5/preload">preload()</a>. | ||
* This allows the model to load fully before the rest of your code is run. | ||
* | ||
* One of the limitations of the OBJ and STL format is that it doesn't have a built-in | ||
* sense of scale. This means that models exported from different programs might | ||
* be very different sizes. If your model isn't displaying, try calling | ||
* <a href="#/p5/loadModel">loadModel()</a> with the normalized parameter set to true. This will resize the | ||
* model to a scale appropriate for p5. You can also make additional changes to | ||
* the final size of your model with the <a href="#/p5/scale">scale()</a> function. | ||
* | ||
* 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 plain text string of either an stl or obj file to be loaded. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Rather than specifying a There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I added the filetype to the argument and fixed some of the overloads, let me know if you like it more now. |
||
* - `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 createModel | ||
* @param {String} object String of the object to be loaded | ||
* @param {Boolean} normalize If true, scale the model to a | ||
* standardized size when loading | ||
* @param {function(p5.Geometry)} [successCallback] Function to be called | ||
* once the model is loaded. Will be passed | ||
* the 3D model object. | ||
* @param {function(Event)} [failureCallback] called with event error if | ||
* the model fails to load. | ||
* @param {String} [fileType] The file extension of the model | ||
* (<code>.stl</code>, <code>.obj</code>). | ||
* @return {p5.Geometry} the <a href="#/p5.Geometry">p5.Geometry</a> object | ||
* | ||
* @example | ||
* <div> | ||
* <code> | ||
* const octahedron_model = ` | ||
* v 0.000000E+00 0.000000E+00 40.0000 | ||
* v 22.5000 22.5000 0.000000E+00 | ||
* v 22.5000 -22.5000 0.000000E+00 | ||
* v -22.5000 -22.5000 0.000000E+00 | ||
* v -22.5000 22.5000 0.000000E+00 | ||
* v 0.000000E+00 0.000000E+00 -40.0000 | ||
* f 1 2 3 | ||
* f 1 3 4 | ||
* f 1 4 5 | ||
* f 1 5 2 | ||
* f 6 5 4 | ||
* f 6 4 3 | ||
* f 6 3 2 | ||
* f 6 2 1 | ||
* f 6 1 5 | ||
* `; | ||
* //draw a spinning octahedron | ||
* let octahedron; | ||
* | ||
* function preload() { | ||
* octahedron = createModel(octahedron_model); | ||
* } | ||
* | ||
* function setup() { | ||
* createCanvas(100, 100, WEBGL); | ||
* describe('Vertically rotating 3-d octahedron.'); | ||
* } | ||
* | ||
* function draw() { | ||
* background(200); | ||
* rotateX(frameCount * 0.01); | ||
* rotateY(frameCount * 0.01); | ||
* model(octahedron); | ||
*} | ||
* </code> | ||
* </div> | ||
*/ | ||
/** | ||
* @method createModel | ||
* @param {String} modelString | ||
* @param {function(p5.Geometry)} [successCallback] | ||
* @param {function(Event)} [failureCallback] | ||
* @param {String} [fileType] | ||
* @return {p5.Geometry} the <a href="#/p5.Geometry">p5.Geometry</a> object | ||
*/ | ||
/** | ||
* @method createModel | ||
* @param {String} modelString | ||
* @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 <a href="#/p5.Geometry">p5.Geometry</a> object | ||
*/ | ||
p5.prototype.createModel = function(modelString, options) { | ||
p5._validateParameters('loadModel', arguments); | ||
let normalize= false; | ||
let successCallback; | ||
let failureCallback; | ||
let flipU = false; | ||
let flipV = false; | ||
let fileType = 'obj'; | ||
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 { | ||
successCallback = typeof arguments[1] === 'function' ? arguments[1] : undefined; | ||
failureCallback = arguments[2]; | ||
if (typeof arguments[3] !== 'undefined') { | ||
fileType = arguments[3]; | ||
} | ||
} | ||
const model = new p5.Geometry(); | ||
model.gid = `octa.obj|${normalize}`; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this might lead to conflicting IDs if you make more than one. Can we maybe make a global counter that increments each time this is called so that every object gets a unique gid? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah definitely something I missed, took your suggestion and added the global counter. Set up the gid as |
||
|
||
if (fileType.match('stl')) { | ||
try { | ||
let uint8array = new TextEncoder().encode(modelString); | ||
let arrayBuffer = uint8array.buffer; | ||
parseSTL(model, arrayBuffer); | ||
if (normalize) { | ||
model.normalize(); | ||
} | ||
|
||
if (flipU) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It looks like these steps get called regardless of the type of the model. Maybe we can make the if statement by inside the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I was trying to follow the convention of |
||
model.flipU(); | ||
} | ||
|
||
if (flipV) { | ||
model.flipV(); | ||
} | ||
|
||
if (typeof successCallback === 'function') { | ||
successCallback(model); | ||
} | ||
} catch (error) { | ||
if (failureCallback) { | ||
failureCallback(error); | ||
} else { | ||
p5._friendlyError('Error during parsing: ' + error.message); | ||
} | ||
return; | ||
} | ||
} else if (fileType.match('obj')) { | ||
try { | ||
const lines = modelString.split('\n'); | ||
parseObj(model, lines); | ||
|
||
if (normalize) { | ||
model.normalize(); | ||
} | ||
|
||
if (flipU) { | ||
model.flipU(); | ||
} | ||
|
||
if (flipV) { | ||
model.flipV(); | ||
} | ||
if (typeof successCallback === 'function') { | ||
successCallback(model); | ||
} | ||
} catch (error) { | ||
if (failureCallback) { | ||
failureCallback(error); | ||
} else { | ||
p5._friendlyError('Error during parsing: ' + error.message); | ||
} | ||
return; | ||
} | ||
} else { | ||
p5._friendlyFileLoadError(3, modelString); | ||
if (failureCallback) { | ||
failureCallback(); | ||
} else { | ||
p5._friendlyError( | ||
'Sorry, the file type is invalid. Only OBJ and STL files are supported.' | ||
); | ||
} | ||
} | ||
return model; | ||
}; | ||
/** | ||
* Load a 3d model from an OBJ or STL file. | ||
* | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can probably take this sentence out since I think we don't do any preloading in this method?