I am working on an aframe project where runtime loading of 3D object is required. I have read A-Frame documentations, and aframe doesn't seem to support runtime assets loading at all.
I discovered this aframe-asset-on-demand-component by protyze (https://github.com/protyze/aframe-asset-on-demand-component) and it seems to allow runtime loading of img, audio and video. But its documentation does not indicate the possibility of loading 3D objects like files in .obj or .dae at runtime.
Has anyone tried using the above-mentioned component to achieve runtime loading of 3D objects? Or is there any alternative ways to achieve this purpose?
Ignore <a-assets> since that is for pre-runtime network preloading.
Just set the model component with setAttribute:
el.setAttribute('gltf-model', 'path/to/model.gltf')
or
el.setAttribute('obj-model', {obj: 'path/to/model.obj'})
You can create a custom component that will use one of the three.js loaders, to load the model when you want to:
// instantiate a loader
var loader = new THREE.OBJLoader();
// load a resource
loader.load(
// resource URL
'models/monster.obj',
// called when resource is loaded
function ( object ) {
scene.add( object );
},
// called when loading is in progresses
function ( xhr ) {
console.log( ( xhr.loaded / xhr.total * 100 ) + '% loaded' );
},
// called when loading has errors
function ( error ) {
console.log( 'An error happened' );
}
);
Links to three.js docs: Do a search for loaders to see all the ones available that you can use in an a-frame component:
https://threejs.org/docs/#examples/loaders/OBJLoader
A-frame components using the threejs loaders:
https://github.com/aframevr/aframe/blob/master/src/components/obj-model.js
https://github.com/aframevr/aframe/blob/master/src/components/collada-model.js
#ngokevin, Great answer! I've created plunker demo for that.
https://plnkr.co/edit/yOmsxOSSfkVuX7c739Q1?p=info
<a-scene embedded arjs="sourceType: webcam;">
<a-gltf-model id="lantern" src="#lantern" position="" 0 0 0"></a-gltf-model>
<a-marker-camera preset="hiro"></a-marker-camera>
</a-scene>
<script>
var lantern = document.getElementById('lantern');
setTimeout(() => {
alert('duck');
lantern.setAttribute('gltf-model', 'https://raw.githubusercontent.com/KhronosGroup/glTF-Sample-Models/master/2.0/Duck/glTF/Duck.gltf');
}, 3000);
setTimeout(() => {
alert('cesium');
lantern.setAttribute('gltf-model', 'https://raw.githubusercontent.com/KhronosGroup/glTF-Sample-Models/master/2.0/CesiumMan/glTF/CesiumMan.gltf');
}, 9000);
</script>
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 have a gltf model without an animation that I've loaded in aframe, and I'd like to add an animation that is connected to a different model. I've written some javascript to pull in the animation data and even bind it to the original GLB using GLTF loader, but not sure how to replace the aframe entity beyond just replacing the url.
Any help on how to solve this, or if there's a more efficient way to do this would be much appreciated! 🙂
HTML code:
<a-entity gltf-model="https://cdn.theoasis.xyz/public/bollywoodified/01.glb" id="center" position="0 0.03 -1.6" rotation="0 0 0" scale="1.3 1.3 1.3" animation-mixer\>\</a-entity\>
Javascript code:
import { GLTFLoader } from 'https://cdn.skypack.dev/three#0.129.0/examples/jsm/loaders/GLTFLoader.js';
getAnimations('https://cdn.theoasis.xyz/glb/djland.glb');
function getAnimations(glb) {
const loader = new GLTFLoader();
loader.load(glb, function ( gltf ) {
loader.load(oasis_data.metadata[0].glb, function (data) {
data.animations = gltf.animations;
});
});
}
I tried to pull the animationClip from another glb and was successful, and then was able to replace the animation array on the intended glb with the one from the animated glb. But I do not know how to actually update the entity within AFrame, nor do i know if this is the most efficient solution!
I would like to find the best way to add lights from a blender scene into a THREE.js project. I can not find an explanation of how to import the lighting from blender to THREE.js anywhere. You are expected to already have lighting in your THREE.js project. This is not very efficient when working with large scenes.
THREE.js output from blender import using gltf format
Scene Render From Blender
This is my import statement
initGLTFLoader(){
this.gltfLoader = new THREE.GLTFLoader();
}
loadCTF(){
// Load a glTF resource
this.gltfLoader.load(
// resource URL
'ctf.gltf',
// called when the resource is loaded
function ( gltf ) {
// this console.log indicates that the light are actually in the import,
// but dont get added to the scene
console.log(gltf);
this.scene.add( gltf.scene );
}.bind(this),
// called while loading is progressing
function ( xhr ) {
console.log( ( xhr.loaded / xhr.total * 100 ) + '% loaded' );
},
// called when loading has errors
function ( error ) {
console.log( 'An error happened', error );
}
);
}
I have a web application where the users can add objects to the scene, move it around, change the rotation etc. But, I have a drop down which decides the unit system to be followed on the whole page for various parameters. The drop down, on change should go through a page refresh. This causes the whole scene to be reset. Is there any way to save the scene and then reload it to the previous state in three js?
A beautiful example of what you are trying to achieve is the three.js editor itself.You can find its source on github.
What it does it that it stores the editor's state in (i.e configuration, camera and scene objects) into the local storage and indexedDB (you can live with only one also) and then when the page is initialized it checks if scene's state is set there in indexedDB, it loads it from there.
You might have to do some reading of the code itself but I'll explain the main logic here.
1. Loading the scene from local storage when the page loads
You can find that in index.html when there is this piece of code
editor.storage.init( function () {
editor.storage.get( function ( state ) {
if ( state !== undefined ) {
editor.fromJSON( state );
}
var selected = editor.config.getKey( 'selected' );
if ( selected !== undefined ) {
editor.selectByUuid( selected );
}
} );
So this checks that if there is data in the state it goes to fromJSON function in /js/Editor.js which basically sets the camera and scene etc. to whatever was stored in indexedDB .See the exact code is
fromJSON: function ( json ) {
var loader = new THREE.ObjectLoader();
// backwards
if ( json.scene === undefined ) {
var scene = loader.parse( json );
this.setScene( scene );
return;
}
// TODO: Clean this up somehow
var camera = loader.parse( json.camera );
this.camera.position.copy( camera.position );
this.camera.rotation.copy( camera.rotation );
this.camera.aspect = camera.aspect;
this.camera.near = camera.near;
this.camera.far = camera.far;
this.setScene( loader.parse( json.scene ) );
this.scripts = json.scripts;
}
To check how its loaded from the local storage/IndexedDB exactly you can check Config.js and Storage.js files located in the JS folder itself.
2nd Storing the data into IndexedDB periodically
Again in index.html you can find the following code and updates the model in the IndexedDB this behavior can be triggered on an event or on a timeout or even manually by calling this piece of code editor.storage.set( editor.toJSON() );.
var saveState = function ( scene ) {
if ( editor.config.getKey( 'autosave' ) === false ) {
return;
}
clearTimeout( timeout );
timeout = setTimeout( function () {
editor.signals.savingStarted.dispatch();
timeout = setTimeout( function () {
editor.storage.set( editor.toJSON() );
editor.signals.savingFinished.dispatch();
}, 100 );
}, 1000 );
};
Hoping you can make use of this example to achieve your target.
How to show a gif image loader to indicate the status of the model on before load the 3d models using OBJMTLLoader orAssimpJSONLoader ? am using three.js version 67 and am using below code load the gif loader, but the function doesn't have any effect while load the model
var loadManager = new THREE.LoadingManager();
loadManager.onProgress = function ( item, loaded, total ) {
alert('hi');
console.log( item, loaded, total );
};
var objloader = new THREE.OBJMTLLoader(loadManager);
what am doing wrong here, am missing anything?
Having a quick read of the LoadingManager docs it seems that the class does not show progress of a single loader, but it shows the progress of many.
So if you send the loading manager 4 loaders, the onProgress event would fire 4 times with the 'loaded' variable being 1, 2, 3 and 4.