I'm loading quite large 3d models using BinaryLoader (up to 6MB), and I would like to display progress information.
Using loader.showStatus was already a 1st step (now my app displays "Loading" while the model is loading), but I would like to show which percentage of the model has already been loaded.
Reading the code for BinaryLoader I found the 'showProgress' flag, but if I set it to true, I get multiple errors like this one:
Uncaught Error: InvalidStateError: DOM Exception 11
xhr.onreadystatechange
at line BinaryLoader.js:99, which corresponds to:
callbackProgress( { total: length, loaded: xhr.responseText.length } );
I also tried configuring loader.onLoadProgress, but I wasn't able to figure out how to do it...
thanks in advance!
3 years later I ran into the same problem as you. Here is my solution:
var onLoadComplete= function (geometry) {
model = new THREE.Mesh(geometry, material);
scene.add(model);
//etc
};
var onLoadProgress = function (e) {
var percentage = Math.round((e.loaded / e.total * 100));
jQuery('#some-loader-div').html('Loading ' + percentage + '%')
};
loader.load(file_url, onLoadComplete, onLoadProgress);
Reference: http://definitelytyped.org/docs/threejs--three/classes/three.jsonloader.html#onloadprogress
Hope this helps someone!
Related
I have been buying animated 3D models from TurboSquid and they fall into two starkly different categories:
Ready to use - I just load the FBX file and it looks fine in my ThreeJS scene
Missing textures - The loaded FBX model looks geometrically fine and the animations work, but all the surfaces are white
I want to know how to load the provided textures using the FBXLoader module. I have noticed that all of the FBX models, which I believe are PBR material based, have this set of files:
*002_BaseColor.png
*002_Emissive.png
*002_Height.png
*002_Metallic.png
*002_Roughness.png
*002_Normal.png
I have these questions:
How would I use the FBXLoader module to load the textures "in the right places"? In other words, how do I make the calls so that the BaseColor texture goes to the right place, the Emissive texture so it goes where it should, etc?
Do I need to pass it a custom material loader to the FBXLoader and if so, which one and how?
UPDATE: I trace through the ThreeJS FBX loader code I discovered that the FBX model that is not showing any textures does not have a Texture node at all. The FBX model that does show the textures does, as you can see in this screenshot of the debugger:
I'll keep tracing. If worse comes to worse I'll write custom code to add a Textures node with the right fields since I don't know how to use Blender to add the textures there. My concern is of course that even if I create a Textures node at run-time, they still won't end up in the proper place on the model.
This is the code I am currently using to load FBX models:
this.initializeModel = function (
parentAnimManagerObj,
initModelArgs= null,
funcCallWhenInitialized = null,
) {
const methodName = self.constructor.name + '::' + `initializeModel`;
const errPrefix = '(' + methodName + ') ';
self.CAC.parentAnimManagerObj = parentAnimManagerObj;
self.CAC.modelLoader = new FBXLoader();
self.CAC.modelLoader.funcTranslateUrl =
((url) => {
// Fix up the texture file names.
const useRobotColor = misc_shared_lib.uppercaseFirstLetter(robotColor);
let useUrl =
url.replace('low_Purple_TarologistRobot', `Tarologist${useRobotColor}`);
return useUrl;
});
// PARAMETERS: url, onLoad, onProgress, onError
self.CAC.modelLoader.load(
MODEL_FILE_NAME_TAROLOGIST_ROBOT_1,
(fbxObj) => {
CommonAnimationCode.allChildrenCastAndReceiveShadows(fbxObj);
fbxObj.scale.x = initModelArgs.theScale;
fbxObj.scale.y = initModelArgs.theScale;
fbxObj.scale.z = initModelArgs.theScale;
self.CAC.modelInstance = fbxObj;
// Set the initial position.
self.CAC.modelInstance.position.x = initModelArgs.initialPos_X;
self.CAC.modelInstance.position.y = initModelArgs.initialPos_Y;
self.CAC.modelInstance.position.z = initModelArgs.initialPos_Z;
// Create an animation mixer for this model.
self.CAC.modelAnimationMixer = new THREE.AnimationMixer(self.CAC.modelInstance);
// Speed
self.CAC.modelAnimationMixer.timeScale = initModelArgs.theTimeScale;
// Add the model to the scene.
g_ThreeJsScene.add(self.CAC.modelInstance);
// Don't let this part of the code crash the entire
// load call. We may have just added a new model and
// don't know certain values yet.
try {
// Play the active action.
self.CAC.activeAction = self.CAC.modelActions[DEFAULT_IDLE_ANIMATION_NAME_FOR_TAROLOGIST_ROBOT_1];
self.CAC.activeAction.play();
// Execute the desired callback function, if any.
if (funcCallWhenInitialized)
funcCallWhenInitialized(self);
} catch (err) {
const errMsg =
errPrefix + misc_shared_lib.conformErrorObjectMsg(err);
console.error(`${errMsg} - try`);
}
},
// onProgress
undefined,
// onError
(err) => {
// Log the error message from the loader.
console.error(errPrefix + misc_shared_lib.conformErrorObjectMsg(err));
}
);
}
I've created two identical fiddles with different OpenLayers-Versions:
OpenLayers v3.18.0 and OpenLayers 4.1.1
The objective is to export a PNG in high resolution. I didn't include the actual exporting of the file. It is explained here if interested.
It all worked fine up to the newer version (I think until a 4.x version).
If you have the DPI-Setting in windows on 100%, both fiddles do the same - but if you change your DPI-Setting to 125%, the latter fiddle does not update the text Some text! and it becomes really small and is located in the wrong place.
The map stays like that, until I click into it (or I call map.updateSize()). So I thought, I add map.updateSize() at the end of precompose - but no matter where I do it, the exported image is wrong as the updateSize() seems to be async and happening AFTER postcompose.
I didn't find a breaking change regarding this issue. Am I overlooking something or is it a bug? Any suggestion for a workaround?
Thanks to the issue I opened on github I came up with the following solution. The most interesting part is the creation of a second ol.Map with a desired pixelRatio:
saveToFile = function (fileName, opt_ChangeSize, opt_PixelRatio, opt_DelayRenderPromise) {
var newMapComponent,
originalSize = mapComponent.getSize();
opt_ChangeSize = opt_ChangeSize || { width: originalSize[0], height: originalSize[1] };
var div = $(document.createElement("div"));
div.attr('id', 'DIV_SaveToFile_Renderer_' + (new Date()).getTime());
div.css('position', 'absolute');
div.css('top', '0');
div.css('left', '0');
div.css('visibility', 'hidden');
div.css('width', opt_ChangeSize.width + 'px');
div.css('height', opt_ChangeSize.height + 'px');
$('body').append(div);
newMapComponent = new ol.Map({
target: div[0].id,
layers: mapComponent.getLayers(),
pixelRatio: opt_PixelRatio,
view: mapComponent.getView()
});
// opt_DelayRenderPromise in this case returns when loading of some features has completed. It could also be postrender of map or whatever.
$q.when(opt_DelayRenderPromise).then(function () {
$timeout(function () {
var data,
canvas = div.find('canvas')[0];
data = canvas.toDataURL('image/png');
data = data.replace(/^data:image\/(png);base64,/, "");
MyUtilities.SaveBlobFromBase64(data, fileName);
div.remove();
mapComponent.setSize(originalSize);
mapComponent.renderSync();
});
});
mapComponent.setSize([opt_ChangeSize.width, opt_ChangeSize.height]);
mapComponent.renderSync();
};
I'm facing shieldUI wicket integration and I'm trying to get base 64 image dfom a shieldUi chart using this tutorial: https://www.shieldui.com/documentation/javascript.chart/exporting
I tried to run the code below:
function render_image_box(chart_id) {
var result = false;
var svg_chart = $("#" + chart_id);
if (svg_chart) {
var chart = svg_chart.swidget();
if (chart != null) {
chart.exportToImage();
result = true;
}
}
// setTimeout(find_image_source, 100) // wait before continuing
return chart;
}
in both the $(document).ready(..) and $(window).load(...) functions and the load event raised from the library (https://www.shieldui.com/documentation/javascript.chart/events/load) as well.
In none of these function chart is rendered yet, so the svg_chart.swidget() returns null.
Is there any other event to use to accomplish my goal or any other way to get the image source?
Thanks in advance,
Laura
You can access the chart instance using .swidget() only after you have initialized it with the .shieldChart() constructor.
To make your code work, you should also turn off animation for the chart, because right after you initialize it, the rendering will not be over and there will be no image contents.
Here is a complete JSBin to get you started...
The title explains it all...
I need to perform a custom action when I know a user has finished resizing, but from what I can find in the Kendo UI documentation there is no event for this accessible to me other that 'resize' which I cannot use as is.
Perhaps i just missed the event?
if not:
Is there a way to use the 'resize' event to determine that a user has stopped resizing?
So here's my answer thus far:
Mine differs slightly due to architectural needs, but here's a general solution
var isResizing = false;
var wndw = $(element).kendoWindow({
// .....
resize: OnResize,
// .....
}).data('kendoWindow');
function onResize() {
isResizing = true;
}
$('body').on('mouseup', '.k-window', function() {
if(isResizing){
// **Your 'Stopped' code here**
isResizing = false;
}
});
Have you considered using underscore.js debounce? I have used it successfully to only trigger then change after the resize events have stopped coming for a certain period (in the case below 300ms). This does add a small delay to captureing the end, but if like me you just want to store the final size then that works fine. Here is the version of the code above but using underscore debounce:
var wndw = $(element).kendoWindow({
// .....
resize: _.debounce( this.hasResized, 300)
// .....
}).data('kendoWindow');
//This is called at the end of a resize operation (using _.debounce)
function hasResized (args) {
// ** Your code here **
};
Hope that helps.
I'm trying to animate the duration of the value change in a dojo gauge, but I think I'm missing out something, and I can't figure out what it is.
So far, I've got this code working, but the indicator just moves from one point to another, with no animation whatsoever.
require(["dojo/ready", "dojo/dom", "dojox/dgauges/components/black/CircularLinearGauge", "dojox/dgauges/GaugeBase"],
function(ready, dom, CircularLinearGauge, GaugeBase) {
var gauge = new CircularLinearGauge({value:10, animationDuration:5000}, dom.byId("circularGauge"));
setInterval(function() {
var randomValue = Math.floor((Math.random() * 100) + 1);
gauge.set("value", randomValue);
gauge.refreshRendering();
}, 10000);
});
Any help would be highly appreciated, thanks in advance
Looks like its an issue with dojox.dgauges.components.DefaultPropertiesMixin. If you replace the _setValueAttr function to
_setValueAttr: function(v) {
this.getElement("scale").getIndicator("indicator").set("value", v);
}
it should animate for you.
As a side note, all the other functions in DefaultPropertiesMixin sets each property directly instead of using the set function. It might be advisable to change them to use the set function instead.