Multiple material on single obj file with three.js - javascript

I'm using three.js to load an obj file, a ring with some pearls. I haven't got an mtl file, as the software we use to export the obj (rhinoceros(?)) won't generate it with an obj file (this is what I was told by the graphic designer).
I need to set a metallic material ONLY for the ring, and glass material ONLY for the stone/pearls.
This is a link to the test page where I actually load the file:
jaaxlab.com/test_youring
...for the rest I don't know how to set a single material and also multiple.
Link to obj file jaaxlab.com/test_youring/obj/prova1.obj

Your object has several children. The child with index 0 is the ring. Also, its name is sezione_B_misura13_Riviera_mix_full anello_sezione_B_misura13.
So, you can access it like:
obj.children[0].material = new THREE.MeshStandardMaterial(...);
or
obj.getObjectByName('sezione_B_misura13_Riviera_mix_full anello_sezione_B_misura13').material = new THREE.MeshStandardMaterial(...);
Given that obj is the object, loaded with you loader.
Take a look at this jsfiddle. Load your file, then check the console log. You can also click any part of the object and see its id and name in the console.

Related

How to combine .gltf file with other assets; bin and png files for use in babylonjs

I feel like I'm close but still missing something. Here's what I have so far
var files = window.files;
console.log(files) // => (3) [File, File, File]
// File objects:
// 0: File {correctName: 'test.bin', name: 'test.bin' …}
// 1: File {correctName: 'test.gltf', name: 'test.gltf', …}
// 2: File {correctName: 'test.png', name: 'teest.png', …}
var fi = new BABYLON.FilesInput(engine, scene, (s) => {
console.log('here in scene loaded callback');
})
fi.loadFiles({ dataTransfer: { files: asheFiles } });
The scene loaded callback never gets called however. I also don't see anything being rendered on the canvas element in the page.
I know there's clearly a way to do it based on the example they have here:
https://sandbox.babylonjs.com/
I'm just trying to figure out how to recreate that.
Okay folks, I do have a solution.
Overview
After searching through many of their documentation pages, forums online and similar questions here on Stackoverflow, I've realized that Babylon js doesn't really sport a direct file load API. They have functions that can take file objects as arguments, but ultimately that does not meet the needs of digesting a gltf file that's split up into various parts, namely .gltf, .png, and .bin. As using a provided method such as BABYLON.SceneLoader.Append on the gltf file will fail once BABYLON parses it and immediately starts requesting the accompanying files (.png and .bin) over the web. Here you could theoretically use a .glb file, but that's not the file format I'm using in my project.
You need to hack the library into allowing you to do what you need. I started looking at the example code used in the linked sandbox above. They use this FileInput class to somehow load all the accompanying files together after a drag and drop event. I emulated the drag and drop functionality via:
function sceneLoadedCB(newScene) { window.scene = newSene; initCamera(newScene, canvas); }
const fi = new BABYLON.FilesInput(engine, scene, sceneLoadedCB)
fi.loadFiles({
dataTransfer: { files: [gltfFileObject, pngFileObject, binFileObject] }
})
This would've been good enough for my purposes except that it will always create another scene object in place of the current one, stopping me from loading up a scene with multiple gltf objects. Asset containers will not work to solve this as they are always bound to the scene they are created with; you cannot transfer assets from one scene to another.
The solution
I didn't reverse engineer the logic that parses and groups the files as all being part of the same gltf object. But I did find this static property of the FilesInputStore class being used as a place to store actual javascript File objects. If you populate this store with your gltf file accompanying file assets you will be able to load them together as all being part of your gltf file.
Demo:
var theScene // your babylon scene instance
const renderLoop = (sceneToRender) => {
sceneToRender = sceneToRender || window.scene
sceneToRender.render();
sceneToRender.getEngine().runRenderLoop(renderLoop);
};
yourGLTFFiles.forEach((file) => {
window.BABYLON.FilesInputStore.FilesToLoad[file.name] = file
})
const gltfFile = yourGLTFFiles.find((file) => file.name.includes('gltf'))
BABYLON.SceneLoader.Append('file:', gltfFile, theScene, function (loadedScene) {
console.log('here in scene loaded callback, scene: ', loadedScene)
// below will be true since append adds to the scene instance you passed it
console.log(loadedScene === theScene)
renderLoop(theScene)
});
This took me forever to solve. I hope this solution saves you time in building your awesome project!

Three.js texture is not loading - what would cause this?

I am trying to create a scene using .obj and .mtl files exported from blender. The object is literally just a rectangle (it needs to be a .obj file. more objects will be added to create a scene) I am able to see the material load, but cannot see the texture being applied in chrome or firefox.
the mtl file text
the obj file text
Here is the javascript code:
const obj_loader = new THREE.OBJLoader(),
mtl_loader = new THREE.MTLLoader();
// uses example of OBJ + MTL from three.js/examples
mtl_loader
.setTexturePath('bar/')
.setPath('bar/')
.load('floor.mtl', (materials) => {
materials.preload()
obj_loader
.setMaterials(materials)
setPath('bar/')
.load('floor.obj', (object) => {
// everything returns status 200!
// material is being applied but no texture
scene.add(object);
})
});
project file structure
Checking the console, requests for the mtl, obj, and image files are returning 200 status codes
but the model renders without texture
No errors are in the console at all. What would cause this issue in Three.js? I suspect something is wrong with the .obj or .mtl but I cannot find the problem. (the file paths are correct based on the logged ajax request).
This may be due to the .preload() method not being called on the materials object returned in the material load callback. The preload() method basically creates the material objects loaded by the MTLLoader.
Consider the following updates, with this method call added:
const mtl_loader = new THREE.MTLLoader();
mtl_loader.setTexturePath('bar/');
mtl_loader.setPath('bar/');
mtl_loader.load('floor.mtl', function(materials) {
// Add this (see link below for more detail)
materials.preload()
const obj_loader = new THREE.OBJLoader();
obj_loader.setMaterials(materials);
obj_loader.setPath('bar/');
obj_loader.load('floor.obj', function(object) {
scene.add(object);
})
});
Here is a link to the THREE source code which shows the inner workings of .preload() - hope this helps!
It turned out to be an issue with the UV mapping of the texture. In Blender, you can create a texture, and Blender will show the texture in 'render' mode, even if the UV map is not set (It will wrap automatically).
Blender showing render, even without UV Map
This was especially frustrating because I had these settings when exporting the file:
export settings for OBJ
The MTL and OBJ file appeared to have all the content in it, but by manually UV mapping the texture (instead of using a simple repeat in the "texture" menu) the issue was resolved when I exported again.
UV/Image editor needed to be used
Sorry for any confusion that may have caused. I hope it helps anyone who has the same issue exporting from Blender.

Three.js materialLoader doesn't load embedded texture image

I'm exporting a three.js material using material.toJSON() provided method, this is the result:
{
"metadata":{"version":4.5,"type":"Material","generator":"Material.toJSON"},
"uuid":"8E6F9A32-1952-4E12-A099-632637DBD732",
"type":"MeshStandardMaterial",
"color":11141120,
"roughness":1,
"metalness":0.5,
"emissive":0,
"map":"876D3309-43AD-4EEE-946F-A8AE8BA53C9E",
"transparent":true,"depthFunc":3,"depthTest":true,"depthWrite":true,
"textures":[
{
"uuid":"876D3309-43AD-4EEE-946F-A8AE8BA53C9E",
"name":"",
"mapping":300,
"repeat":[1,1],
"offset":[0,0],
"center":[0,0],
"rotation":0,
"wrap":[1001,1001],
"minFilter":1008,
"magFilter":1006,
"anisotropy":1,
"flipY":true,
"image":"C6B4FEDA-8E7E-490A-A1AD-866ECE36E952"}],
"images":[
{
"uuid":"C6B4FEDA-8E7E-490A-A1AD-866ECE36E952",
"url":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAgAAAAEACAYAAADFkM5nAAAg[...]"
}]}
I try to use MaterialLoader as the example suggest
https://threejs.org/docs/#api/loaders/MaterialLoader
but at parsing time I always get this error
THREE.MaterialLoader: Undefined texture
876D3309-43AD-4EEE-946F-A8AE8BA53C9E
I'm I wrong if I expect from the MaterialLoader to use the embedded resources?
I'm doing it wrong or missing something?
How can I also load the images in the json file into the related texture?
Thanks!
here a fiddle:
http://jsfiddle.net/akmcv7Lh/211/
Notice that MaterialLoader is not able to load textures. It expects that textures are set via MaterialLoader.setTextures() before loading a JSON file like ObjectLoader does:
const loader = new MaterialLoader();
loader.setTextures( textures );
So MaterialLoader can only be used as a standalone loader if the respective materials have no textures. Otherwise you have to prepare the textures on app level similar how ObjectLoader does.

Meteor-Files thumbnail link

I am following this tutorial, https://github.com/VeliovGroup/Meteor-Files/wiki/Image-Processing
I am successful on creating thumbnails.
I am successful on adding versions in the collection.
I want to display the thumbnail. My problem now is how can I get link for <img> tag?
For the original image, I used FilesCollection.link(fileRef).
How can I do it same in thumbnails?
I am using Veliov meteor-files package and GraphicMagick.
I had the same issue, I think. Just to verify, there is a code snippet in the thumbnail generation that looks like this:
const upd = { $set: {} };
upd['$set']['versions.thumbnail'] = fileRef.versions.thumbnail;
This means that in the fileObj for the main file, there is a property named "versions" which now has a sub-property named "thumbnail".
Reading through the code, I find that you can access this from the main fileObj thus:
fileObj.link('thumbnail')
I have verified that this works. If you add other versions, I'm sure they work the same way.

Override DateTimeShortcut in Django Admin

I'm trying to implement the top solution here, the one that only uses javascript:
Django: how to change the choices of AdminTimeWidget
It basically uses regex to create the different time choices by overriding the time options.
The only problem I have is that my script loads before DateTimeShortcuts.js, so I get an Uncaught ReferenceError: DateTimeShortcuts is not defined. Does anyone know how I can force the DateTimeShortcuts.js file to load after my js file that references it?
If I create a second reference to DateTimeShortcuts.js, it'll work properly, but I'll have two clocks up there, only the 2nd one will modify, because it's loading after the 2nd DateTimeShortcuts.js
I'm calling my file like this, where admin_clock.js references DateTimeShortcuts.js and has the override code:
class EventAdmin(admin.ModelAdmin):
list_filter = ('film', 'partner',)
list_display = ('id', partner', 'film', 'date_time', 'venue_name', 'city')
class Media:
js = ('tiny_mce/tiny_mce.js', 'tiny_mce/textareas.js', 'admin_clock.js',)
Apologies for not commenting on the original answer, I need more points to comment there.
The way I solved this was to add the javascript directly to the extrahead block of my version of the change_form template, overriding that block in the template: https://docs.djangoproject.com/en/1.6/ref/contrib/admin/#overriding-admin-templates

Categories

Resources