Autodesk Forge Viewer: Change Texture in IFC Model - autodesk-forge

we run a webapplication using Autodesk Forge. In the webapplication we'd like to change surface apperances. Therefore we use the following Audodesk functions
...
event.fragIdsArray.forEach(frag => {
const model = this.viewer.model;
model.getFragmentList().setMaterial(frag, this.material)
var object = this.viewer.impl.getFragmentProxy(this.viewer.impl.model, frag)
object.updateAnimTransform()
}
The code works fine for Autodesk Revit imported model. Using imported IFC models does not work as expected. Both models were imported to the AD Forge viewer by ADs model derivate api.
To geht our expected results we tried to use MeshBasicMaterial and MeshPhongMaterial. Both with the same result: Revit model is fine, IFC model aint so.
In Order to lookup for some workaround we tried to copy the fragment meshes and creating overlays with the same mashes and changed materials. Code was like
...
var obj = this.viewer.impl.getRenderProxy(this.viewer.impl.model, frag)
var meshProxy = new THREE.Mesh(obj.geometry, this.material);
meshProxy.matrix.copy(obj.matrixWorld);
meshProxy.matrixWorldNeedsUpdate = true;
meshProxy.matrixAutoUpdate = false;
meshProxy.frustumCulled = false;
this.viewer.impl.addOverlay("parkett", meshProxy);
...
The result is shown in the image (right side is the expected result):
Somehow it looks like the image texture is not shown "detailed" enough...
Thanks in advance for any suggestion!

From the question I'm not entirely sure what the problem is. Are there no visible changes when applying a custom material to IFC models? Or is the custom material applied, but in a "wrong way"?
If the custom material is not applied at all, make sure that the model is not consolidated. You can ensure that using viewer.model.unconsolidate();.
If the custom material is applied but its texture doesn't look correct, it could be because the geoemtries in the IFC model do not include proper texture coordinates. In that case you would have to map the texture yourself, for example, using a custom shader: https://github.com/petrbroz/forge-basic-app/blob/custom-texture-mapping/public/CustomTextureExtension.js.

Related

load 2D & 3D forge viewers in single web page

I would like to link between elements from the 2D sheet and 3D model, so when I select the element from 2D it should reflect and select (isolate) in the 3D also if I change the color it does the same on both e.g. and the other way around.
so I can use the document browser extensions to open the 2d sheet on 1st viewer and the 3d model on the 2nd viewer:
const firstModel = new Autodesk.Viewing.Private.GuiViewer3D(document.getElementById('MyViewerDiv1'));
const secondModel = new Autodesk.Viewing.Private.GuiViewer3D(document.getElementById('MyViewerDiv2'));
Autodesk.Viewing.Initializer(options1, function() {
viewer1.start();
viewer1.load(...);
});
Autodesk.Viewing.Initializer(options2, function() {
viewer2.start();
viewer2.load(...);
});
if the example above is correct I am still missing how to links both viewers.
I hope someone could help me with this issue
Note that we have a viewer extension that might already give you what you're looking for: https://github.com/Autodesk-Forge/forge-extensions/blob/master/public/extensions/NestedViewerExtension/README.md.
If you want to implement the cross-selection between two viewer instances yourself, you can. Just subscribe to the SELECTION_CHANGED event in one of the viewers, get the selected IDs, and select the same IDs in the other viewer using the usual viewer.select([...]); method.
Btw. regarding your code snippet:
the Autodesk.Viewing.Initializer only needs to be called once per the entire webpage
the Autodesk.Viewing.Private.GuiViewer3D instances should be created after the initializer has done its work

Autodesk Forge Viewer getting fragment position

I'm trying to get the position of separate meshes in a model (translated from a revit file).
What I'm doing is to get fragmentProxy, then use getOriginalWorldMatrix() to get the THREE.Matrix4(). Then from the Matrix4, call getPosition() to get the THREE.Vector3 world position of the fragment.
However, every mesh returns the same position value. Is that because of how the model is built originally? Or I have to get the fragment position using a different method?
Your process of retrieving the fragment transform is correct. Alternatively, you could use something like this:
function getFragmentTransform(model, fragid) {
const frags = model.getFragmentList();
let xform = new THREE.Matrix4();
frags.getOriginalWorldMatrix(fragid, xform);
return xform;
}
I'm afraid you are correct that, in some cases, the transform may be baked directly into the mesh vertices.

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.

Forge Viewer THREE.MeshLambertMaterial

One of the properties available on the var material = NEW THREE.MeshLambertMaterial is texture maps: {( map: new THREE.TextureLoader().load('wool.jpg') )}
We're very curious whether it would be possible to load in a texture this way and apply it to a specific object in the model?
Yes, but the way you modify materials in the Forge Viewer is a little bit different than what you would do in Three.js.
I have an extensive sample that illustrates how to modify materials, including custom textures here:
Viewing.Extension.Material
The live demo is there. To use it, load a model first with "Model Loader" +, then activate either "Theming color", "Material color" or "Texture" and pick a component of the loaded model. You can click in the square in each option to change color/texture.

threejs cannot parse material in json object

Dear all I am using angular 2.4.10 and three js 0.85.0. After downloading a JSON file from the server, I have at my disposal the following JSON object:
https://drive.google.com/file/d/0B7IcIHZN137RdmVRTXpLZmlPaDg/view?usp=sharing
I am trying to load the object without using the loader URL using the following code suggested in another StackOverflow post:
loadModel(aJSONObject) {
console.log(aJSONObject);
let loader = new THREE.ObjectLoader();
let model = loader.parse( aJSONObject );
console.log(model);
.....
}
and it's working but it is not getting the materials. How can I get the material from the JSON file?
I tried to load your model and the unique material got imported correctly.
However, there is no texture nor color defined for this material in the JSON model. So the object will look white, the default color used for three.js materials.
Fiddle here.
To see your model in three.js make sure to scale it appropriately based on you camera position (I had to scale it down).Moreover, as the model is using a THREE.MeshPhongMaterial, don't forget to add some lights.
By the way, there are some X, Y and Z individual attributes defined in your model that increase its size uselessly (three.js won't use them).