Importing SnappyTree (proctree) model - three.js

I'm trying to display a model created by SnappyTree/Proctree ( http://www.snappytree.com/ ).
Proctree is designed to work with GLGE, but I'm nearly there in using the library generated data in Three.js. Basically I construct a custom json object, add proctree data to it and use JSONLoader to generate the final geometry.
What I suspect is happening, the vertices (pointcloud) are imported correctly, but the array faces refer to wrong vertices or are otherwise interpreted wrong.
var tree = new Tree(json); // Proctree
// window.console.log(tree);
var model = { "metadata" : { "formatVersion" : 3.1, "generatedBy": "bb3d2proctree", "vertices": 0, "faces": 0, "description": "Autogenerated from proctree." },
"materials": [{ // just testing...
"diffuse": 20000
}],
"colors": [0xff00ff, 0xff0000] // just testing
};
model.vertices = Tree.flattenArray(tree.verts);
model.normals = Tree.flattenArray(tree.normals);
model.uvs = [Tree.flattenArray(tree.UV)];
model.faces = Tree.flattenArray(tree.faces);
var loader = new THREE.JSONLoader();
loader.createModel(model, function(geometry, materials) {
// cut out for brewity... see jsfiddle
}
It's almost working (haven't got into materials yet..) The tree looks kinda correct, however the faces are a bit messed up, and I'm sure there is some simple format difference, and it should be possible to modify the faces array so it works correctly with Three.js.
JSFiddle here: http://jsfiddle.net/nrZuS/
How could I import the data correctly into Three.js?
This is how it should look like: http://www.snappytree.com/#seed=861&segments=10&levels=5&vMultiplier=0.66&twigScale=0.47&initalBranchLength=0.5&lengthFalloffFactor=0.85&lengthFalloffPower=0.99&clumpMax=0.449&clumpMin=0.404&branchFactor=2.75&dropAmount=0.07&growAmount=-0.005&sweepAmount=0.01&maxRadius=0.269&climbRate=0.626&trunkKink=0.108&treeSteps=4&taperRate=0.876&radiusFalloffRate=0.66&twistRate=2.7&trunkLength=1.55&trunkMaterial=TrunkType2&twigMaterial=BranchType5 (except my code so far only tries to import the trunk without twigs, and without textures, so at this point I'm only worried about the trunk shape as can be seen in the jsfiddle)

Never mind, got it to work.
Instead of:
model.faces = Tree.flattenArray(tree.faces);
I do:
model.faces = [];
for (var i = 0; i < tree.faces.length; i++) {
var face = tree.faces[i];
model.faces.push(0);
model.faces.push(face[0]); // v1
model.faces.push(face[1]); // v2
model.faces.push(face[2]); // v3
}
Updated jsFiddle here: http://jsfiddle.net/KY7eq/

Related

Collider made of geometry does not match mesh data(position, scale, ..etc)

I wanted to load the spatial model using the gltfloader, and then create a collision with geometry data.
By the way, I’m trying to get help with a different result than I expected.
problem image
here is sample image
You can see that the collider is larger than the visible Mesh data.
Most of the spatial data has the above situation.
The example code used is as follows. (tsx on react)
function Space(props) {
const url = props.data?.spaceFileName ? `https://space-test.vrin.co.kr/files/${props.data.spaceFileName}` : null;
const model = useGLTF(url);
const clone = model.scene;
const getCollide = (child) => {
console.log(child);
const geometry = new Geometry().fromBufferGeometry(child.geometry);
const vertices = geometry.vertices.map((v) => new THREE.Vector3().copy(v));
const faces = geometry.faces.map((f) => [f.a, f.b, f.c]);
const normals = geometry.faces.map((f) => new THREE.Vector3().copy(f.normal));
return <Object key={child.uuid} clone={clone} args={[vertices, faces, normals]} model={model} />
};
const traverseObject = (child) => child.children.map((c) => {
if (c.type === 'Object3D' || c.type === 'Group') {
return traverseObject(c);
} if (c.type === 'Mesh') {
return getCollide(c);
}
});
const objects = traverseObject(model.scene)
return <group dispose={null}>{objects}</group>;
}
Some spatial data generate collisions properly, but some spatial data do not generate collisions properly.
make sample image
here is sample what i made on blender
As a result of watching the Blender program, it is expected that it does not seem to be able to properly load the Geometry layer at the top.
blender2
If you look at the collation in the image above, there is a group called Sketchfeb at the top.
If i erase that group layer,
blender2
It can be seen that it is very similar to the shape of the collision body shown in the first picture.
I don’t know if I wrote the recursive function incorrectly or if there is an internal problem with the module.
Does anyone know the solution?
I don’t know if I wrote the recursive function incorrectly or if there is an internal problem with the module.
Does anyone know the solution?

THREE.WebGLProgram Shader Error because of defined v1 constant

I am using Three.js and I am loading an HDR as an environment map for the scene. Upon loading, I receive this error:
The line in questions is this:
I am assuming that the defined v1 causes an issue in this line because it is not undefined.
I am loading the HDR map like this:
return new Promise((resolve, reject) => {
new RGBELoader()
.setDataType(THREE.HalfFloatType)
.load(
path, // <-- hdr file
(texture) => {
// I tried fromEquirectangular and fromCubemap
// const envMap = this.pmremGenerator.fromEquirectangular(texture).texture;
const envMap = this.pmremGenerator.fromCubemap(texture).texture;
texture.needsUpdate = true;
resolve({ envMap });
},
undefined,
reject
);
});
Does anyone know what is causing this issue in THREE?
const envMap = this.pmremGenerator.fromCubemap(texture).texture;
I doubt this method call is correct. RGBELoader can not load textures in the cube map format. You probably want to use fromEquirectangular(). Before using this method, you need this line in your onLoad() callback:
texture.mapping = THREE.EquirectangularReflectionMapping;
Besides, please check if the usage of PMREMGenerator is actually necessary in your app. In latest releases, three.js internally uses PMREMGenerator to prepare environment maps for the usage with PBR materials.

The function "getRenderProxy" to get a ThreeJS Mesh from an object's fragId in Forge Viewer doesn't work correctly

I'm trying to get a threeJS Mesh from Autodek Forge objects using the function
'viewer.impl.getRenderProxy(viewer.model, fragId)'.
The problem that I encounter is if I put this function in a loop routine to get Meshs of multiple objects, I get just a random Mesh.
To find out the problem's origin, I used a similar function that is :
'viewer.impl.getFragmentProxy(viewer.model, fragId)'
and it worked just fine.
Her is the routine code that I use and the result :
for(let i = 0, len = nodNamee.length; i < (len); i = i+3){
var instanceTree = viewer.model.getData().instanceTree;
var fragIds = [];
instanceTree.enumNodeFragments(nodNamee[i+1], function(fragId){
fragIds.push(fragId);
});
fragIds.forEach(function(fragId) {
var renderProxy = viewer.impl.getRenderProxy(viewer.model, fragId);
fragtoMesh.push(renderProxy);
//var fragmentproxy = viewer.impl.getFragmentProxy(viewer.model, fragId);
//fragtoProxy.push(fragmentproxy);
});
}
Result :
Arry of fragtoMesh
This is because the getFragmentProxy method always returns the same instance of THREE.Mesh, just with different properties. Basically, the method works like this under the hood:
let cachedMesh = new THREE.Mesh();
// ...
getRenderProxy(model, fragId) {
// Find the geometry, material, and other properties of the fragment
cachedMesh.geometry = fragGeometry;
cachedMesh.material = fragMaterial;
// ...
return cachedMesh;
}
// ...
Note that this is a performance optimization because if the getFragmentProxy (which is only meant for internal use) function returned a new instance every time it's called by other parts of Forge Viewer, it would cause a huge memory churn.
So in your case, if you really need to store all the THREE.Mesh instances in an array, you'll need to clone them or copy their individual properties into separate THREE.Mesh objects.

Design Pattern in A-Frame to reuse geometries

The following code pen illustrates 50 cubes being generated directly in three.js and with A-Frame: https://codepen.io/ubermario/pen/XavVxY
What would be the A-Frame design pattern to duplicate the geometry reuse algorithm implemented when referencing three.js directly?
Code snippet:
//Re-use geometry if it was already instantiated;
try {
cubeGeom = uniqueGeom.filter(function(obj) { return obj.intDim == d.intDim})[0].geometry;
}
catch(err) {
cubeGeom = new THREE.CubeGeometry(d.intDim,d.intDim,d.intDim);
uniqueGeom.push({intDim:d.intDim, geometry:cubeGeom})
}
A-Frame hashes geometry properties to a string and places it inside a map.
e.g.,
{
'primitive:box;width:5': THREE.BoxBufferGeometry(),
'primitive:sphere;radius:2' THREE.SphereBufferGeometry()
}
Then when creating new geometry, create a hash using same function, and check the map.

Threejs: mesh standard material reflection issues

I've stumbled upon the problem that some browsers and devices render the MeshStandardMaterial reflection poorly.
Consider the example below:
and this example below:
Both comparisons are running simultaneously on the same computer, same graphics card, identical attributes, but different browsers. As you can see, the reflections on the right are almost unidentifiable.
Additionally, I'm getting some triangulation issues at sharp angles that make it seem as if the reflection is being calculated in the vertex shader:
I understand that different browsers have different WebGL capabilities, as the results on http://webglreport.com/ illustrate:
Does anybody know what WebGL extension or feature the IE/Edge browsers are missing that I can look for? I want to put a sniffer that uses a different material if it doesn't meet the necessary requirements. Or if anybody has a full solution, that would be even better. I've already tried playing with the EnvMap's minFilter attribute, but the reflections are still calculated differently.
I don't know which extensions are needed but you can easily test. Before you init THREE.js put some code like this
const extensionsToDisable = [
"OES_texture_float",
"OES_texture_float_linear",
];
WebGLRenderingContext.prototype.getExtension = function(oldFn) {
return function(extensionName) {
if (extensionsToDisable.indexOf(extensionName) >= 0) {
return null;
}
return oldFn.call(this, name);
};
}(WebGLRenderingContext.prototype.getExtension);
WebGLRenderingContext.prototype.getSupportedExtensions = function(oldFn) {
return function() {
const extensions = oldFn.call(this);
return extensions.filter(e => extensionsToDisable.indexOf(e) < 0);
};
}(WebGLRenderingContext.prototype.getSupportedExtensions);
Then just selectively disable extensions until Firefox/Chrome look the same as IE/Edge.
The first thing I'd test is disabling every extension that's in Chrome/Firefox that's not in IE/Edge just to verify that turning them all off reproduces the IE/Edge behavior.
If it does reproduce the issue then I'd do a binary search (turn on half the disabled extensions), and repeat until I found the required ones.
const extensionsToDisable = [
"EXT_blend_minmax",
"EXT_disjoint_timer_query",
"EXT_shader_texture_lod",
"EXT_sRGB",
"OES_vertex_array_object",
"WEBGL_compressed_texture_s3tc_srgb",
"WEBGL_debug_shaders",
"WEBKIT_WEBGL_depth_texture",
"WEBGL_draw_buffers",
"WEBGL_lose_context",
"WEBKIT_WEBGL_lose_context",
];
WebGLRenderingContext.prototype.getExtension = function(oldFn) {
return function(extensionName) {
if (extensionsToDisable.indexOf(extensionName) >= 0) {
return null;
}
return oldFn.call(this, name);
};
}(WebGLRenderingContext.prototype.getExtension);
WebGLRenderingContext.prototype.getSupportedExtensions = function(oldFn) {
return function() {
const extensions = oldFn.call(this);
return extensions.filter(e => extensionsToDisable.indexOf(e) < 0);
};
}(WebGLRenderingContext.prototype.getSupportedExtensions);
const gl = document.createElement("canvas").getContext("webgl");
console.log(gl.getSupportedExtensions().join('\n'));
console.log("WEBGL_draw_buffers:", gl.getExtension("WEBGL_draw_buffers"));

Resources