I'm attempting to do conditional loading of a model into an a-entity. The reason for this is that I've got a scene which I would like to give the user a choice to load a large model or not. So far I've got a scene with the following entity:
id="modelname-entity"
scale="2 2 2"
position="0 0 -5"
drag-rotate="rotateY:false;"
model-rotate-loadprogress="modelUrl:modelname.gltf;modelRef:modelname-model"
></a-entity>
which has a component model-rotate-loadprogress which loads the gltf model with THREE.js syntax:
AFRAME.registerComponent('model-rotate-loadprogress',{
schema : {
modelUrl: {
type: "string"
},
modelRef: {
type: "string"
}
},
init : function(){
this.loadModel();
},
loadModel: function() {
if (!this.data.modelUrl || !this.data.modelRef) {
console.warn("Model details not given for model rotate loader");
return;
}
if ( document.getElementById(this.data.modelRef) ) {
console.warn("Assets already has an asset with the ID of " , this.data.modelRef );
}
// Using THREE.js file loader
var dis = this;
var loader = new THREE.GLTFLoader().setPath( '/assets/static/models/' );
loader.load(
this.data.modelUrl,
gltf => {
// Add the model to the scene for now.
dis.el.sceneEl.object3D.add(gltf.scene);
},
xhr => {
console.log( (xhr.loaded / xhr.total * 100) + '% loaded' );
},
error => {
console.error( error );
}
);
}
}
The model loads and gets displayed to the scene but how to I attach it to the entity instead?
I got the model populating the <a-entity> in the similar way to how I got it attaching it to the scene. Here's the final code:
loadGLTFModel: function() {
var dis = this;
var loader = new THREE.GLTFLoader().setPath( this.PATH_MODELS );
loader.load(
`${this.PATH_MODELS}${this.data.gltfModel}`,
gltf => {
dis.el.object3D.add(gltf.scene)
},
progress => {
this.onProgress(progress);
},
error => {
console.error( "ERROR : " , error );
}
);
},
onProgress: function(progress) {
this.progressBar.setAttribute("geometry", {
thetaLength: (progress.loaded / progress.total * 360)
})
},
If I were to add the heavy model to the <a-assets> which is the recommended way of doing things, would result in the whole application being blocked until all the assets are loaded and ready. In my scenario the user has a choice of skipping the download. If the user chooses to load the model then he/she gets a progressbar (actually a ring) which gets updated.
Here's code how to load obj and mtl models:
loadOBJModel: function() {
var dis = this;
if (!this.data.mtlMaterial) {
console.error("No material given for model");
return;
}
var mtlLoader = new THREE.MTLLoader();
mtlLoader.load(
`${this.PATH_MODELS}${this.data.mtlMaterial}`,
materials => {
materials.preload();
var objLoader = new THREE.OBJLoader();
objLoader.setMaterials( materials );
objLoader.setPath( this.PATH_MODELS );
objLoader.load(
this.data.objModel,
object => {
dis.el.object3D.add(object)
},
progress => {
this.onProgress(progress);
},
error => {
console.error( "ERROR : " , error );
}
);
}
);
},
No need to invoke the GLTFLoader. Use the gltf-model component:
loadModel: function() {
if (!this.data.modelUrl || !this.data.modelRef) {
console.warn("Model details not given for model rotate loader");
return;
}
if ( document.getElementById(this.data.modelRef) ) {
console.warn("Assets already has an asset with the ID of " , this.data.modelRef );
}
this.el.setAttribute(‘gltf-model’, ‘url(‘ + this.data.modelUrl + ‘)’);
}
I recommend pre loading the model in a-assets so user doesn’t have to wait for network. Pre load both the large and small models.
Related
I load the fbx model into the scene, but such mesh errors appear, everything is fine with the mesh in 3ds max, blender and Unity, I checked in sketchfab, everything is fine too, and in other programs running on three js this error appears, what is the problem and how can I fix it?
const fbxLoader = new FBXLoader( loadingManager )
fbxLoader.load(
'Meshes/Landscape_N_Buildings_UPD.fbx',
(object) => {
object.traverse(function (child) {
if ((child as THREE.Mesh).isMesh) {
(child as THREE.Mesh).material = material;
(child as THREE.Mesh).castShadow = true;
(child as THREE.Mesh).receiveShadow = true;
}
})
object.position.set(0, 2.5, 0)
object.scale.set(0.001, 0.001, 0.001)
object.castShadow = true //default is false
object.receiveShadow = true //default
scene.add(object)
},
(xhr) => {
console.log((xhr.loaded / xhr.total) * 100 + '% loaded')
},
(error) => {
console.log(error)
}
)
First pic
Second pic
In 3ds max
I tried using CKEditor5 for my project and when I activated insert image and tried using it, It says ReferenceError: server is not defined. Here is the code:
class MyUploadAdapter {
constructor( loader ) {
this.loader = loader;
}
upload() {
server.onUploadProgress( data => {
loader.uploadTotal = data.total;
loader.uploaded = data.uploaded;
} );
return loader.file
.then( file => server.upload( file ) );
}
abort() {
// Reject the promise returned from the upload() method.
server.abortUpload();
}
_initRequest() {
const xhr = this.xhr = new XMLHttpRequest();
xhr.open( 'POST', '{{ route('ck5_store')}}',true );
xhr.setRequestHeader('X-CSRF-TOKEN',$('meta[name="csrf-token"]').attr('content'));
xhr.responseType = 'json';
}
_initListeners( resolve, reject, file ) {
const xhr = this.xhr;
const loader = this.loader;
const genericErrorText = `Couldn't upload file: ${ file.name }.`;
xhr.addEventListener( 'error', () => reject( genericErrorText ) );
xhr.addEventListener( 'abort', () => reject() );
xhr.addEventListener( 'load', () => {
const response = xhr.response;
if ( !response || response.error ) {
return reject( response && response.error ? response.error.message : genericErrorText );
}
resolve( {
default: response.url
} );
} );
if ( xhr.upload ) {
xhr.upload.addEventListener( 'progress', evt => {
if ( evt.lengthComputable ) {
loader.uploadTotal = evt.total;
loader.uploaded = evt.loaded;
}
} );
}
}
_sendRequest( file ) {
const data = new FormData();
data.append( 'upload', file );
this.xhr.send( data );
}
}
function SimpleUploadAdapterPlugin( editor ) {
editor.plugins.get( 'FileRepository' ).createUploadAdapter = ( loader ) => {
return new MyUploadAdapter( loader );
};
}
ClassicEditor
.create( document.querySelector( '#tab-content-{{$MODULE}} form#{{$MODULE}}_form textarea[id=form_{{$MODULE}}_details]') ,
{
extraPlugins: [ SimpleUploadAdapterPlugin ],
})
.then( editor => {
console.log( editor );
} )
.catch( error => {
console.error( error );
} );
Any idea on what is the problem? Already tried looking for solutions but cannot find anywhere else. Thank you in advance.
I was having the same issue. My solution:
// Starts the upload process.
upload() {
return this.loader.file
.then( file => new Promise( ( resolve, reject ) => {
this._initRequest();
this._initListeners( resolve, reject, file );
this._sendRequest( file );
} ) );
}
// Aborts the upload process.
abort() {
if ( this.xhr ) {
this.xhr.abort();
}
}
I found this solution following documentation.
When importing a .gltf file into threejs (in expo), I get the following error:
Error: Event {
"isTrusted": false,
}
I am using the GLTFLoader from three(/examples/jsm/loaders/GLTFLoader.js) in an expo react native project with the following code:
loader.load(
"../body.gltf",
(gltf) => {
console.log("Object: ", gltf);
scene.add(gltf.scene);
},
(progress) => console.log("Progress: ", progress),
(err) => console.log("Error: ", err)
);
Is there something in my permissions I am not aware of, is it something in expo or something else?
Loading assets:
useEffect(() => {
(async () => {
const assets = [
Asset.fromModule(require("./assets/abductors.gltf")),
Asset.fromModule(require("./assets/abs.gltf")),
Asset.fromModule(require("./assets/adductors.gltf")),
Asset.fromModule(require("./assets/biceps.gltf")),
Asset.fromModule(require("./assets/bracheoradialis.gltf")),
Asset.fromModule(require("./assets/calves.gltf")),
Asset.fromModule(require("./assets/chest.gltf")),
Asset.fromModule(require("./assets/feet.gltf")),
Asset.fromModule(require("./assets/flexors.gltf")),
Asset.fromModule(require("./assets/forearms.gltf")),
Asset.fromModule(require("./assets/glutes.gltf")),
Asset.fromModule(require("./assets/hamstrings.gltf")),
Asset.fromModule(require("./assets/hands.gltf")),
Asset.fromModule(require("./assets/head.gltf")),
Asset.fromModule(require("./assets/lats.gltf")),
Asset.fromModule(require("./assets/obliques.gltf")),
Asset.fromModule(require("./assets/pelvic.gltf")),
Asset.fromModule(require("./assets/quads.gltf")),
Asset.fromModule(require("./assets/rotators.gltf")),
Asset.fromModule(require("./assets/serratus.gltf")),
Asset.fromModule(require("./assets/shoulders.gltf")),
Asset.fromModule(require("./assets/tibalis.gltf")),
Asset.fromModule(require("./assets/transverse.gltf")),
Asset.fromModule(require("./assets/traps.gltf")),
Asset.fromModule(require("./assets/triceps.gltf"))
];
let body = new THREE.Scene();
for (let i = 0; i < assets.length; i++) {
await assets[i].downloadAsync();
const loader = new GLTFLoader();
loader.load(
assets[i].uri || "",
(gltf) => {
assets[i] = gltf.scene;
body.add(gltf.scene);
},
(xhr) => {
console.log(`${(xhr.loaded / xhr.total) * 100}% loaded`);
},
(error) => {
console.error("An error happened", error);
}
);
}
setBody(body);
})();
}, []);
Showing them:
const _onContextCreate = async (gl) => {
const { drawingBufferWidth: width, drawingBufferHeight: height } = gl;
let renderer = new ExpoTHREE.Renderer({ gl });
renderer.setSize(width, height);
renderer.setClearColor(0xffffff);
let camera = new THREE.PerspectiveCamera(80, width / height, 0.01, 1000);
camera.position.z = 5;
let scene = new THREE.Scene();
const light = new THREE.PointLight(0xffffff, 1, 500);
light.position.set(5, 10, 10);
scene.add(light);
scene.add(body);
body.position.set(0, -2, -5);
body.scale.set(1.2, 1.2, 1.2);
body.rotation.set(0, 0, 0);
[...]
};
return (
<Model
_onContextCreate={_onContextCreate}
body={body}
setActiveMuscles={setActiveMuscles}
onChangeStroke={onChangeStroke}
/>
);
I baked some lightmap from on blender 2.8(Bake type - combined, Influence - "Direct", "Indirect", "Diffuse").
After that, I tried to add lightmap to part of my model.
function initColor(parent, type, mtl) {
parent.traverse(o => {
if (o.isMesh) {
if (o.name.includes(type)) {
o.material = mtl;
}
}
});
}
var lightmapTexture = new THREE.ImageUtils.loadTexture( "images/FrontLightmap.png" );
var front_material = new THREE.MeshBasicMaterial({lightMap: lightmapTexture });
const INITIAL_MAP = [
{childID: "Front", mtl: front_material}
];
for (let object of INITIAL_MAP) {
initColor(theModel, object.childID, object.mtl);
}
But the result - black material(front part).
Is there is possible to separately usage of lightmap and different material colors at the same time?
I am trying to remove the point light (or preferrably any light source), but I am new to ThreeJS... I tried babylonScene.remove(object); but I get an error that "cannot call traverse on undefined" or something to that affect. Happy to post the error if that is the suggested approach.
The code below is taken from the ThreeJS example code with the additional removal code.
https://github.com/mrdoob/three.js/blob/master/examples/webgl_loader_babylon.html
var loader = new THREE.BabylonLoader( manager );
loader.load( data.src, function ( babylonScene ) {
babylonScene.traverse( function ( object ) {
if ( object instanceof THREE.Mesh ) {
object.material = new THREE.MeshPhongMaterial( {
color: Math.random() * 0xffffff
});
}
else if(object instanceof THREE.PointLight){
console.log("Removing PointLight");
object.remove();
}
});
...
}, onProgress, onError );
The best idea would be to set the light intensity at 0, this means you won't see it anymore:
var loader = new THREE.BabylonLoader( manager );
loader.load( data.src, function ( babylonScene ) {
babylonScene.traverse( function ( object ) {
if ( object instanceof THREE.Mesh ) {
object.material = new THREE.MeshPhongMaterial( {
color: Math.random() * 0xffffff
});
}
else if(object instanceof THREE.PointLight){
console.log("Removing PointLight");
object.intensity = 0
}
});
...
}, onProgress, onError );
Hope this helps!