I want to clone a model loaded with loader, I found this issue on github,but the solution doesn't work. It seems there has been an structural change in Object3D.
How can I clone an Object3D in current stable version of Three.js?
In this new version of three.js you have a method clone.
For example, I use a queen from chess and I had to duplicate multiple times:
// queen is a mesh
var newQueen = queen.clone();
// make sure to re position to be able to see the new queen!
newQueen.position.set(100,100,100); // or any other coordinates
It will work with any mesh.
I used three.js r61.
Actually mrdoob's answer is your answer...
The loader output a geometry you use to create a mesh.
You need to create a new mesh with the loader-created mesh's geometry and material.
for ( var i = 0; i < 10; i ++ ) {
var mesh = new THREE.Mesh( loadedMesh.geometry, loadedMesh.material );
mesh.position.set( i * 100, 0, 0 );
scene.add( mesh );
}
You want to clone a Mesh and not an Object3D because the output of the loader is a Mesh.
I found one fast solution (not the most efficient)
The GLTFLoader uses the THREE.FileLoader() method internally, which allows you to cache files.
To do so, you need to add this line before you create an instance of the GLTFLoader
THREE.Cache.enabled = true;
Then you can load multiple times the same model, but only the first time will take longer, for example:
THREE.Cache.enabled = true;
var loader = new GLTFLoader();
var deeplyClonedModels = [];
for( var i = 0; i < 10; i++ ){
loader.load('foo.gltf', function ( gltf ) {
deeplyClonedModels.push(gltf.scene);
});
}
Related
The question is related to Is there a way of visualising a line in drake visualizer where I had asked about how to visualize a line in the drake visualizer (about 3 years ago, which worked fine with v0.10.0). I am trying to achieve the same with the new API and was wondering if there was any example/documentation which can guide me on how to publish a line onto the visualizer. My previous method used for publishing a line looks like:
void publishLine(const std::vector<std::vector<double>>& pts,
const std::vector<std::string>& path, lcm::DrakeLcm& lcm,
std::vector<double> color) {
long long int now = getUnixTime() * 1000 * 1000;
nlohmann::json j = {{"timestamp", now},
{
"setgeometry",
{{{"path", path},
{"geometry",
{
{"type", "line"},
{"points", pts},
{"color", color},
{"radius", 0.1},
}}}},
},
{"settransform", nlohmann::json({})},
{"delete", nlohmann::json({})}};
auto msg = robotlocomotion::viewer2_comms_t();
msg.utime = now;
msg.format = "treeviewer_json";
msg.format_version_major = 1;
msg.format_version_minor = 0;
msg.data.clear();
for (auto& c : j.dump()) msg.data.push_back(c);
msg.num_bytes = j.dump().size();
// Use channel 0 for remote viewer communications.
lcm.get_lcm_instance()->publish("DIRECTOR_TREE_VIEWER_REQUEST_<0>", &msg);
}
You can use Meshcat::SetLine or Meshcat::SetLineSegments https://drake.mit.edu/doxygen_cxx/classdrake_1_1geometry_1_1_meshcat.html#aa5b082d79e267c040cbd066a11cdcb54
One caveat is that many browsers/webGL implementations do not support the linewidth property in ThreeJS. For thick lines, consider adding a cylinder using SetObject.
I need help understanding how to pass an additional attribute in an existing system. I am using
nexus to render my mesh. Nexus sort of uses three.js, but down the line it actually does straight calls to web gl. I want to add a cool looking wireframe effect to it and i found this guy wireframe shader. Long story short, i added a barycentric attribute to the my shader that nexus loads. I can get the location of the attribute and all that jazz. What i am having trouble with is understanding how to modify nexus webgl calls to pass in additional data for that attribute. Code in question is this:
function readyNode(node) {
var m = node.mesh;
var n = node.id;
var nv = m.nvertices[n];
var nf = m.nfaces[n];
var model = node.model;
var vertices;
var indices;
indices = new Uint8Array(node.buffer, nv*m.vsize, nf*m.fsize);
const konSize = nv * m.vsize + 12 * nv;
vertices = new Uint8Array(konSize);
var view = new Uint8Array(node.buffer, 0, nv * m.vsize);
var v = view.subarray(0, nv*12);
vertices.set(v);
var konOff = nv*12;
var off = nv*12;
//var barycentric = addBarycentricCoordinates( indices, false );
//vertices.set(barycentric, konOff);
//var konOff = nv*12;
vertices.set(v, konOff);
konOff += nv*12;
if(m.vertex.texCoord) {
var uv = view.subarray(off, off + nv*8);
vertices.set(uv, konOff);
konOff += nv*8;
off += nv*8;
}
if(m.vertex.normal && m.vertex.color) {
var no = view.subarray(off, off + nv*6);
var co = view.subarray(off + nv*6, off + nv*6 + nv*4);
vertices.set(co, konOff );
vertices.set(no, konOff + nv*4);
}
else {
if(m.vertex.normal) {
var no = view.subarray(off, off + nv*6);
vertices.set(no, off);
}
if(m.vertex.color) {
var co = view.subarray(off, off + nv*4);
vertices.set(co, off);
}
}
var gl = node.context.gl;
var vbo = m.vbo[n] = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, vbo);
gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);
var ibo = m.ibo[n] = gl.createBuffer();
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, ibo);
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indices, gl.STATIC_DRAW);
}
Original code does not have barycentric part and without it, it all works. What i don't understand is how to correctly add barycentric data to the "vertices" array to pass it to the shader. The code above doesn't crash, but it does cause UVs to be out of whack. Barycentric coordinates are just a vec3, similar to position. They are just 1s and 0s. As i understand, 1 per vertex. I think I should be able to pack them in the "vertices" array with correct offsets. I even modified the above code to pass in positions instead of barycentric data, and it still doesn't work (it casues UVs to be out whack). Not sure if this enough context to reason about it.
Am i doing something wrong with offsets?
Also, do I need to de-index my data for this to work? Wireframe library does do that.
Let me know if there is more info I can provide.
P.S. Nexus is a chunking / LODing system. It loads patches over time. As far as i understand it's only one "mesh" object, it just adds more data to the buffers as it downloads it.
I want to make histogram of my data so, I use histogram class at c# using MathNet.Numerics.Statistics.
double[] array = { 2, 2, 5,56,78,97,3,3,5,23,34,67,12,45,65 };
Vector<double> data = Vector<double>.Build.DenseOfArray(array);
int binAmount = 3;
Histogram _currentHistogram = new Histogram(data, binAmount);
How can I get the count of the biggest bin? Or just the index of the bigest bin? I try to get it by using GetBucketOf but to do this I need the element in this bucket :(
Is there any other way to do this? I read the documentation and Google and I can't find anything.
(Hi, I would use a comment for this but i just joined so today and don't yet have 50 reputation to comment!) I just had a look at - http://numerics.mathdotnet.com/api/MathNet.Numerics.Statistics/Histogram.htm. That documentation page (footer says it was built using http://docu.jagregory.com/) shows a public property named Item which returns a Bucket. I'm wondering if that is the property you need to use because the automatically generated documentation states that the Item property "Gets' the n'th bucket" but isn't clear how the Item property acts as an indexer. Looking at your code i would try _currentHistogram.Item[n] first (if that doesn't work try _currentHistogram[n]) where you are iterating the Buckets in the histogram using something like -
var countOfBiggest = -1;
var indexOfBiggest = -1;
for (var n = 0; n < _currentHistogram.BucketCount; n++)
{
if (_currentHistogram.Item[n].Count > countOfBiggest)
{
countOfBiggest = _currentHistogram.Item[n].Count;
indexOfBiggest = n;
}
}
The code above assumes that Histogram uses 0-based and not 1-based indexing.
I purchased 3d human teeth model from turbosquid in a 3ds scene format. All I want to do is to extract individaul teeth from the file and use them in threejs script to display them on web page. What I did was exported one tooth from 3ds Max in .obj format and converted that to json using the converter provided with threejs. Though the image appears on the page but with no textures applied to it.
I am new to 3ds Max and Threejs having no idea what am I missing. Can you please guide me.
Edit:
Here is the Json metadata
"metadata" :
{
"formatVersion" : 3.1,
"sourceFile" : "toothz.obj",
"generatedBy" : "OBJConverter",
"vertices" : 1636,
"faces" : 1634,
"normals" : 1636,
"colors" : 0,
"uvs" : 1636,
"materials" : 1
},
"scale" : 1.000000,
"materials": [ {
"DbgColor" : 15658734,
"DbgIndex" : 0,
"DbgName" : "Teeth",
"colorAmbient" : [0.584314, 0.584314, 0.584314],
"colorDiffuse" : [0.584314, 0.584314, 0.584314],
"colorSpecular" : [0.538824, 0.538824, 0.538824],
"illumination" : 2,
"opticalDensity" : 1.5,
"specularCoef" : 70.0,
"transparency" : 1.0
}],
Edit:
Here's the complete code
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(75, window.innerWidth/window.innerHeight, 1, 2000);
loader = new THREE.JSONLoader();
loader.load( "js/JsonModels/toothz.js", function( geometry, materials ) {
materials[0].shading = THREE.SmoothShading;
var material = new THREE.MeshFaceMaterial( materials );
mesh = new THREE.Mesh( geometry, material );
mesh.scale.set( 3, 3, 3 );
mesh.position.y = 0;
mesh.position.x = 0;
scene.add( mesh );
} );
camera.position.z = 340;
//var ambient = new THREE.AmbientLight( 0x101030 );
//scene.add( ambient );
var directionalLight = new THREE.DirectionalLight( 0xffeedd );
directionalLight.position.set( 0, 0, 1 ).normalize();
scene.add( directionalLight );
var renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
function render() {
requestAnimationFrame(render);
renderer.render(scene, camera);
}
render();
Texture exporting is usually tricky in many exporters because there isn't always a clear mapping between 3d program materials and three.js materials. Furthermore, there is no materials defined at all in the .obj file, but a separate .mtl is required. I'm not sure if a) .mtl is exported and b) obj converter uses it, but in any case, your JSON is missing the texture definition in the material. You have a few choices:
Try the MAX exporter, which should allow exporting your stuff directly to JSON, without the intermediate .obj step.
With the OBJ route, you should check that all relevant options in the exporter are checked, a .mtl file is generated, and the obj converter finds it.
Alternatively add the texture manually into the JSON: "mapDiffuse": "my_texture_filename.jpg" (into the material definition in the "materials" section of the file).
You could also add the texture to the material in your model loading callback. However, this is a big hack and not recommended.
See the three.js Migration wiki.
Geometry no longer has a materials property, and loader callbacks, which previously had only a geometry parameter, are now also passed a second one, materials.
EDIT: You need to do something like this:
loader = new THREE.JSONLoader();
loader.load( "js/JsonModels/toothz.js", function( geometry, materials ) {
materials[0].shading = THREE.SmoothShading;
var material = new THREE.MeshFaceMaterial( materials );
mesh = new THREE.Mesh( geometry, material );
mesh.scale.set( 3, 3, 3 );
mesh.position.y = 0;
mesh.position.x = 0;
scene.add( mesh );
} );
three.js r.53
I have created a bunch of movie clips which all have similar names and then after some other event I have built up a string like:
var clipName = "barLeft42"
which is held inside another movie clip called 'thing'.
I have been able to get hold of a reference using:
var movieClip = Eval( "_root.thing." + clipName )
But that feels bad - is there a better way?
Movie clips are collections in actionscript (like most and similar to javascript, everything is basically key-value pairs). You can index into the collection using square brackets and a string for the key name like:
_root.thing[ "barLeft42" ]
That should do the trick for you...
The better way, which avoids using the deprecated eval, is to index with square brackets:
var movieClip = _root.thing[ "barLeft42" ]
But the best way is to keep references to the clips you make, and access them by reference, rather than by name:
var movieClipArray = new Array();
for (var i=0; i<45; i++) {
var mc = _root.thing.createEmptyMovieClip( "barLeft"+i, i );
// ...
movieClipArray.push( mc );
}
// ...
var movieClip = movieClipArray[ 42 ];
You can use brackets and include variables within them... so if you wanted to loop through them all you can do this:
for (var i=0; i<99; i++) {
var clipName = _root.thing["barLeft"+i];
}