Forge Viewer poor rendering quality - autodesk-forge

I'm using the Forge Viewer to display some models converted from IFC (2x3) files.
For some of them, the quality is perfect, but for others the rendering is very poor like the picture bellow.
I've tried to export in SVF, SVF2 and same result.
I've tired different settings to load the model
let config = {
keepCurrentModels: true,
applyScaling: { to: "m" },
applyRefPoint: true,
globalOffset: { x: 0, y: 0, z: 0 }}; //make the view flicker on weird rendered model
None of those settings improved the view except globalOffset who makes the view flicker.
Have you any idea how to fix this ?

This kind of deformation of geometries is typically an indication that the model is very far from the origin. So far that the GPU rendering starts running into floating point precision issues.
Loading the model with globalOffset: new THREE.Vector3(0, 0, 0) should help in this case as it would basically force the viewer not to re-apply the original global offset (which is potentially very large) to all geometry vertices. I'm not sure why the view would flicker after using this option, though, that might be a separate issue.

Related

How can I use setDisplayEdges() for multi-model loading?

I would like to turn the BIM edges on for all models. I have set setDisplayEdges(true) during the initialization of the viewer as well as adding isAEC: true to the options in loadModel().
Unfortunately, this does not display the edges.
How do you recommend turning on the edges for multi-model loading?
I'm not sure which model format your road model is, but passing the createWireframe: true option should help turn on the model edges with my research.
viewer.loadDocumentNode(doc, bubble, {createWireframe: true, keepCurrentModels: true})

How to fix shaking geometry on models with large shared coordinates

I'm loading multiple Revit models in the Forge modelviewer. To align them properly I am using the following load options as mentioned here
var modelOptions = {
sharedPropertyDbPath: doc.getPropertyDbPath(),
globalOffset: { x: 0, y: 0, z: 0 },
applyRefPoint: true,
isAEC: true
};
This works fine on Revit files where the project base point doesn't have too large coordinates. However, if the project base point of a Revit file has large coordinates ( like x:6698000, y:297500) then this results in shaking behavior when rotating the model or even a messed up triangulated view.
This problem also seems to happen when you set the placementTransform to a large coordinate as can be seen in the sample here
How can I solve this shaking behavior on these kind of models?
Can you try adjusting the offset using globaloffset...
Here's an example:
Model aggregating in viewer - coordinate issue

Forge Viewer - Cannot view multiple different models properly

I'm having trouble loading different models in the viewer. I suspect the problem comes from mixing up different units (meters and millimeters) in the models.
So I have 3 Models:
IFC 1, is using millimeters as unit.
When loading the SVF derrivative into the viewer, doing
console.log(model.getUnitScale(), model.getUnitString());
outputs:
0.001, mm
IFC 2, using millimeters as unit. Getting the same output as IFC 1
Obj. Model of a simple cube with center of cube at origin [0, 0, 0]. This does not seem to have any inherent unit.
When loading the SVF derrivative into the viewer, doing
console.log(model.getUnitScale(), model.getUnitString());
outputs:
1, null
In order to load the models with the right coordinates I use the following options:
IFC 1 and 2:
{
globalOffset: {x: 1000000, y: 100000, z: 7000},
sharedPropertyDbPath: doc.getPropertyDbPath(),
}
Obj:
let mat = new THREE.Matrix4();
mat.makeTranslation(1000000, 100000,7000);
{
placementTransform: mat,
sharedPropertyDbPath: doc.getPropertyDbPath(),
}
The rationale here is that the IFC models are located far away from the origin, while the Obj model is located at origin. Using globalOffset for the IFCs seems necessary to get them to align in the viewer, using placementTransform is necessary to put the Obj close to the IFC models.
I'm struggling with the following problems here:
Navigating the models is hard, when highlighting the Obj model, it seems like it is set to using y-up or something, making it hard to do orientation navigation for the other models.
When trying to change zoom, only the Obj seems to be affected. Could this be due to different scale settings?
EDIT 1:
Looks like making the Obj cube the same size as the other models fixes the zooming problem.
Also, if loading the IFC files first, the orientation navigation is right. It's only when loading the OBJ file first that we get the "y-up orientation" problem
Edit 2:
The orientation navigation problem can be fixed with viewer.navigation.setWorldUpVector(new THREE.Vector3(0,0,1), false);.
Is it possible to also control behavior like this globally instead always letting the different models set the behavior?
I think the last loaded model will always have precedence over any previously set world-up vector. So if you want to control the world-up globally, you'll need to use the viewer.navigation.setWorldUpVector method manually after all the models have been loaded.

Changing materials in Forge

We are currently making the client retrieve the object states when the page loads (which will cause the 'pending' objects in the model to turn into different colors). Then we poll for changes to update the coloring (Firstly: pending object gets colored when the viewer loads, and then we keep polling to check and change state again, to make Forge render those in a different color and store their old color/material. When the polling received a change that an object should no longer be colored, it tells Forge to use the old color/material again.
The problem:
We've found out what the problem is, but we couldn't find out how to fix it. The problem is that changing materials in Forge doesn't work after startup anymore, it only works in the first ~3 seconds or so (the materials were used to show the colors).
However, setting overlays works even after the first ~3 seconds, (showing overlays instead of materials to show the colors).
This is not what we want to achieve. This looks unoptimized, because overlays will be shown through everything.
The materials, however, seem to be 'locked', as in, they cannot be changed anymore after the first ~3 seconds. It seems like they aren't refreshed or something
In the examples, we found they used viewer.impl.invalidate(true) to refresh the Forge viewer, but that doesn't do anything after ~3 seconds.
We've also tried every combination of viewer.impl.invalidate(true, true, true) as well as setting material.needsUpdate to true, as well as trying to re-render the entire scene.
We also found this: https://github.com/mrdoob/three.js/issues/790, but we couldn't find a good way to do that in Forge, we tried viewer.requestSilentRender() but that didn't do anything either.
Anyway, we've tried everything we could come up with and could find online to make the materials work, but nothing made a difference.
We are looking to find someone that's more experienced with how Forge works that can see what the material code is doing wrong.
As for the content, here is all the code you will need to understand what is happening:
DROPBOX LINK
And here is a small part of the "index.html" file that sets the color:
try
{
viewer.restoreAllColorOverlays(); //for materials instead of overlays: viewer.restoreAllColorMaterials();
$.each(colors, function(color, selectionIds)
{
viewer.setColorOverlay(selectionIds, color); //for materials instead of overlays: viewer.setColorMaterial(selectionIds, color);
});
}
catch(error)
{
console.error(error);
}
I have no idea how you implement your app, so I only tell what I found in your codes. If you want to resolve the issue you addressed, you can consider providing a reproducible case demonstrating that, I will gladly pass it to our dev team. Those following items should be in the reproducible case:
A short exact description of what you are trying to achieve. The behavior you observe versus what you expect, and why this is a problem.
A complete yet minimal sample source model to run a test in.
A complete yet minimal Forge app that can be run and debugged with a simple procedure to analyze its behavior lives in the sample model.
A complete yet minimal pure three.js app that can be run and demonstrated the shader effect you want. Note. Forge Viewer is using r71 three.js.
Detailed step-by-step instructions for reproducing the issue, e.g. which element to pick, what command to launch etc.
If your reproducible case could not be posted here publicly, please send it to the forge.help#autodesk.com and remove sensitive data or information before you send.
=== Something I found in your codes:
I found here are some wrong types and missing actions in your ColorMaterial extension. The color property of an material should the a type of the THREE.Color. Here is my modification:
Autodesk.Viewing.Viewer3D.prototype.setColorMaterial = function(objectIds, color)
{
if( !(color instanceof THREE.Color) ) throw 'Invalid argument: Color';
var material = new THREE.MeshPhongMaterial
({
color: color,
opacity: 0.8,
transparent: true
});
viewer.impl.matman().addMaterial( 'ColorMaterial-' + new Date().getTime(), material, true );
// ...........
};
Its' result is here:
In the ColorOverlay extension, The type of material color property is also wrong, it should be a type of THREE.Color, too. Changing it into THREE.Color should work fine. In addition, overlay is covers on 3D objects, so you should call viewer.hide() with your setColorOverlay() together. Otherwise, it won't look like a transparent object.
Without hidding 3D object of the wall:
hide 3D object of the wall:

Webgl update region using viewport+scissor

I've been trying to create a multiviewport webgl application.
I got everything rendering quite nice using viewport+scissor for each view.
But now I would like to improve rendering and just render the view which is updated, so skip overdrawing.
I've made a little demo showing the idea: http://kile.stravaganza.org/lab/js/scissor/
As I understand scissor it's suposse that it will just render the current scissor box and keep the rest of the canvas untouched. But it seems that it just keeps clearing the whole canvas on each frame, no matter what I tried :(
This is the rendering code (The last view it's supossed to be rendered just once and keep it on each frame):
function drawScene()
{
gl.clearColor(1.0, 0.0, 0.0, 0.0);
gl.scissor(0,0,200,200);
gl.viewport(0,0,200,200);
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
drawFigures();
gl.clearColor(0.0, 1.0, 0.0, 0.0);
gl.scissor(200,0,200,200);
gl.viewport(200,0,200,200);
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
drawFigures();
gl.clearColor(0.0, 0.0, 1.0, 0.0);
gl.scissor(200,200,200,200);
gl.viewport(200,200,200,200);
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
drawFigures();
// Render just once
if (first)
{
gl.clearColor(1.0, 1.0, 0.0, 0.0);
gl.scissor(0,200,200,200);
gl.viewport(0,200,200,200);
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
drawFigures();
first=false;
}
}
Any idea how could I achieve this effect?
Thank you very much in advance
You can use the preserveDrawingBuffer attribute:
gl = canvas.getContext("experimental-webgl", { preserveDrawingBuffer: true });
It isn't recommended to use this in production. The WebGL specifications states:
While it is sometimes desirable to preserve the drawing buffer, it can
cause significant performance loss on some platforms. Whenever
possible this flag should remain false and other techniques used.
Techniques like synchronous drawing buffer access (e.g., calling
readPixels or toDataURL in the same function that renders to the
drawing buffer) can be used to get the contents of the drawing buffer.
If the author needs to render to the same drawing buffer over a series
of calls, a Framebuffer Object can be used.
This SO question contains also relevant information regarding preserveDrawingBuffer: When WebGL decide to update the display?