Forge Viewer, raster images - autodesk-forge

Is possible to directly load a raster image (PNG, JPG, TIFF) to Forge Viewer?
I see the Autodesk.PDF add-in that can load PDF, I cant find any Autodesk.IMAGE add-in...
Otherwise I need to prior convert Image into PDF and than load it through Autodesk.PDF.

The Autodesk Forge Viewer is based on Three.js - therefore you can use the Three.js API to load an image/texture, there is no need of a Viewer extension for that.
However it depends what you want to do. In case you just want to load an image in the scene, that code is enough.
const texture = THREE.ImageUtils.loadTexture( "thumbnail256.png" );
const material = new THREE.MeshBasicMaterial({ map : texture });
const geometry = new THREE.PlaneGeometry(5, 20, 32);
const planeMesh = new THREE.Mesh(geometry, material);
const planeMesh.position.set(1, 2, 3);
NOP_VIEWER.overlays.addScene('custom-scene');
NOP_VIEWER.overlays.addMesh(planeMesh, 'custom-scene');
But if you want to apply the texture on an existing element in the loaded scene, you need to proceed like this:
const texture = THREE.ImageUtils.loadTexture( "thumbnail256.png" );
const material = new THREE.MeshBasicMaterial({ map : texture, side: THREE.DoubleSide });
NOP_VIEWER.impl.matman().addMaterial('custom-material', material, true);
const model = NOP_VIEWER.model;
model.unconsolidate(); // If the model is consolidated, material changes won't have any effect
const tree = model.getInstanceTree();
const frags = model.getFragmentList();
const dbids = NOP_VIEWER.getSelection();
for (const dbid of dbids) {
tree.enumNodeFragments(dbid, (fragid) => {
frags.setMaterial(fragid, material);
});
}
NOP_VIEWER.impl.invalidate(true, true, false);
Note you may need to work out the texture uv, depending on the geometry.

Related

Autodesk Forge - Absolute coordinates

from the Forge viewer is it possible to retrieve the absolute coordinates of the objects?
With the following code I was able to obtain the relative coordinates of the selected object:
const viewer = NOP_VIEWER;
const dbId = NOP_VIEWER.getSelection()[0];
const selSet = viewer.getSelection();
const targetElem = selSet[0];
const model = viewer.model;
const instanceTree = model.getData().instanceTree;
const fragList = model.getFragmentList();
let bounds = new THREE.Box3();
instanceTree.enumNodeFragments( dbId, ( fragId ) => {
let box = new THREE.Box3();
fragList.getWorldBounds( fragId, box );
bounds.union( box );
}, true );
const position = bounds.center();
With the getAecModelData method I was able to retrieve the refPointTransformation:
What do the values ​​in this array refer to?
TIA
Alder
I'm not sure what exactly you mean by absolute coordinates but if we're talking about geolocation, note that there's actually a Geolocation extension for the viewer:
https://forge.autodesk.com/en/docs/viewer/v6/reference/Extensions/GeolocationExtension
The extension leverages the refPointTransformation in AEC Model Data, letting you transform lat/long (WGS84) coordinates to viewer coordinates and vice versa.
Here's a related blog post that might also help: https://forge.autodesk.com/blog/mini-map-geolocation-extension.

how to add custom material to a fragment in forge viewer

I have an extension with such methods, and when I call this method to change the material (color it red), the object becomes transparent and the following errors appear in the console
WebGLRenderer.js:5561 WebGL: INVALID_VALUE: uniform3fv: no array
[.WebGL-0000737C06582900] GL_INVALID_OPERATION: Active draw buffers with missing fragment shader outputs.
setColorToItem = (id) => {
const material = this.createMaterial('#ff0000');
const model = this.viewer.model;
const frags = model.getFragmentList();
model.unconsolidate();
this.tree.enumNodeFragments(
id,
(fragId) => {
frags.setMaterial(fragId, material);
this.viewer.impl.getFragmentProxy(model, fragId).updateAnimTransform();
},
true,
);
this.viewer.impl.invalidate(true);
};
createMaterial = (color) => {
const threeColor = new THREE.Color(color);
const material = new THREE.MeshPhongMaterial({
side: THREE.DoubleSide,
flatShading: true,
color: threeColor,
});
const materials = this.viewer.impl.matman();
materials.addMaterial('CustomMaterial' + color.toString(), material, true);
return material;
};
What could be the problem?
I tried using different materials (MeshBasicMaterial, MeshLambertMaterial)
version forge-viewer 7
version threejs 0.71 as indicated here https://forge.autodesk.com/en/docs/viewer/v7/developers_guide/viewer_basics /
also tried the latest
I tried this with a slightly modified version of your code snippet (as shown below) with the viewer version 7.*, and I can set the color to the material successfully. Is there perhaps any other custom JavaScript logic in your app that could interfere with the new materials? From the error logs it looks like the shader is having issues obtaining the 3 float values (red, green, blue) from the THREE.Color object.
function createMaterial(viewer, color) {
const material = new THREE.MeshPhongMaterial({
side: THREE.DoubleSide,
flatShading: true,
color: new THREE.Color(color),
});
const materials = viewer.impl.matman();
materials.addMaterial('CustomMaterialRed', material, true);
return material;
}
function applyMaterial(model, dbid, material) {
const tree = model.getInstanceTree();
const frags = model.getFragmentList();
tree.enumNodeFragments(
dbid,
(fragid) => frags.setMaterial(fragid, material),
true
);
model.unconsolidate();
}
// ...
let mat = createMaterial(viewer, '#ff0000');
applyMaterial(viewer.model, 1234, mat);

Is it possible to dimm a model except the objects NOT affected by setThemingColor?

We use the viewer (version 7) to show the issues on 3d model colouring the affected objects using a user defined palette.
The remainig objects of the model are colored with a gray tone.
To achieve this I'm using the setThemingColor technique: I set the theming color grey for the rootid recursive and then I set the correct theme color to the specific issued object.
All the colors used are THREE.Vector4 with the opacity set to 1. In this way the themingColor is non blended with the "natural" color of the object but it "covers" object.
To improve the user experience we'd like to allow the user to dimm the objects not affected by issue instead of set the gray color using setThemingColor.
And now the question: is it possible to change the color (material?) of a group of objects by specifying a fade level up to the ghost of the viewer hide method while preserving the selection functionality?
I have tried the following approach with no success:
const mat = new THREE.MeshBasicMaterial({ color: 0x00ff00, opacity: 0.925, transparent: false });
(this.viewer as any).impl.getMaterials().addMaterial('ghost-material', mat, true);
const model = (this.viewer as any).model;
const tree = model.getInstanceTree();
const frags = model.getFragmentList();
[19240, 20373, 34364, 58813].map(dbid => {
tree.enumNodeFragments(dbid, (fragid) => {
console.log(fragid);
frags.setMaterial(fragid, mat);
(this.viewer as any).impl.invalidate(true);
}, true);
});
[19240, 20373, 34364, 58813] are valid dbids.
The opacity is almost 1 and the material is not transparent hoping to see something.
I tried to invalidate the viewer for each fragment.
After running this script, the 3d model remains the same.
The 'ghost-material' is regularly registered on matman but does not 'replace' the native one.
Where I am doing wrong?
The most important thing to do is to call the method:
model.unconsolidate(); // If the model is consolidated, material changes won't have any effect
before apply the new material to the fragments as explained in the following post
https://forge.autodesk.com/blog/custom-shader-materials-forge-viewer
The right answer is
const mat = new THREE.MeshBasicMaterial({ color: 0x00ff00, opacity: 0.925, transparent: false });
(this.viewer as any).impl.getMaterials().addMaterial('ghost-material', mat, true);
const model = (this.viewer as any).model;
const tree = model.getInstanceTree();
const frags = model.getFragmentList();
// without this it is all useless
model.unconsolidate();
[19240, 20373, 34364, 58813].map(dbid => {
tree.enumNodeFragments(dbid, (fragid) => {
console.log(fragid);
frags.setMaterial(fragid, mat);
(this.viewer as any).impl.invalidate(true);
}, true);
});
Yes, it's a bit of an advanced topic but you can customize materials assigned to individual objects (fragments). The process would be as follows:
Create a custom, semi-transparent material, e.g., a simple THREE.MeshBasicMaterial, and add it to the viewer's material manager
Use the instance tree to enumerate fragments of all objects in your scene
Use the fragment list to retrieve the original material of each fragment, store its reference somewhere, and set its material to your custom one
The code could look roughly like so:
const mat = new THREE.MeshBasicMaterial({ color: 0x00ff00, opacity: 0.25, transparent: true });
viewer.impl.getMaterials().addMaterial('my-material', mat, true);
const tree = viewer.model.getInstanceTree();
const frags = viewer.model.getFragmentList();
tree.enumNodeFragments(tree.getRootId(), (fragid) => { frags.setMaterial(fragid, mat) }, true);
When needed, repeat the process, and reset each fragment to its original material.

How to change the color of sphere objects dynamically (used SceneBuilder in Autodesk forge)

I am working on the example from Custom models in Forge Viewer blog by Petr Broz. I am facing issue in updating the color of sphere objects dynamically. I am getting the value of sphere's color from a json file like this "color": "#FF0000". I have created 3 spheres and I am getting the color of first sphere for the rest also. Why the color is not updating for the other spheres? If the problem is on using same material then I tried giving the sphereMaterial in array also as shown below. Is that wrong or how can i update the color?
var spherecolor='';
var sphereMaterial = [];
const button = document.getElementById('button-geometry');
button.addEventListener('click', async function () {
const sceneBuilder = await viewer.loadExtension('Autodesk.Viewing.SceneBuilder');
const modelBuilder = await sceneBuilder.addNewModel({ conserveMemory: true, modelNameOverride: 'My Custom Model' });
for (var i = 0; i < numOfSphere;i++) {
addGeometry(modelBuilder, jsonGeomConfig.geom[i].dbId, i);
}
});
function addGeometry(modelBuilder, dbId, i) {
const sphereGeometry = new THREE.BufferGeometry().fromGeometry(new THREE.SphereGeometry(0.05, 8, 10));
//Getting spherecolor from json file
spherecolor = jsonGeomConfig.geom[i].color;
sphereMaterial[i] = new THREE.MeshPhongMaterial({ color: spherecolor });
const sphereTransform = new THREE.Matrix4().compose(
new THREE.Vector3(jsonGeomConfig.geom[i].Position.posX, jsonGeomConfig.geom[i].Position.posY, jsonGeomConfig.geom[i].Position.posZ),
new THREE.Quaternion(0, 0, 0, 1),
new THREE.Vector3(2,2,2)
);
modelBuilder.addMaterial('MyCustomMaterial', sphereMaterial[i]);
const sphereGeomId = modelBuilder.addGeometry(sphereGeometry);
const sphereFragId = modelBuilder.addFragment(sphereGeomId, 'MyCustomMaterial', sphereTransform);
modelBuilder.changeFragmentsDbId(sphereFragId, dbId);
}
Be sure to give the materials with different colors different names ... otherwise it'd get overridden - see this live environment:
modelBuilder.addMaterial('MyCustomMaterial'+i, sphereMaterial[i]);
const sphereGeomId = modelBuilder.addGeometry(sphereGeometry);
const sphereFragId = modelBuilder.addFragment(sphereGeomId, 'MyCustomMaterial'+i, sphereTransform);

Default material for model in Forge Viewer

I'd need to have the complete model use a default material (e.g. grey color) and then use externally defined materials for each node.
So I'm looking for some advice on two points:
1) Setting a default material on all nodes.
2) Setting the material / color for given nodes after they're fetched from an external source.
Could this be done at some point before the model is loaded into the viewer? (i.e. server-side)? If not, can it be done in the viewer?
All geometry coming from Forge will always have some material defined for it, but you can iterate over dbIDs of all objects on the model and set a custom THREE.js material for them using something along these lines:
function setCustomMaterial(viewer, dbids) {
const material = new THREE.MeshPhongMaterial({
color: 0xAB00EE,
specular: 0xEEABEE
});
viewer.impl.matman().addMaterial('CustomMaterial', material, true);
const fragList = viewer.model.getFragmentList();
const instanceTree = viewer.model.getData().instanceTree;
for (let dbid of dbids) {
instanceTree.enumNodeFragments(dbid, function(frag) {
fragList.setMaterial(frag, material);
});
}
}