Forge Viewer - getWorldCoordinates is giving different values on different occasions - autodesk-forge

I created below function to get worldCoordinates back, but it gives different values on two occasions.
While clicking a dbId, I get dbid cordinates and I pass it to below function which give me world coordinates, but you can see while I save that dbId selection to DB and reloading page next time to see it back, it gives me different coordinates.
Why it happen so?
saving dbid phase
dbid coordinates
x: -26.277027130126953
y: 18.102033615112305
z: -7.173819303512573
getWorldCoordinates
x: 256.76347287180107
y: 306.8180434914181
z: 0
relaoding page phase
dbid coordinates
x: -26.277027130126953
y: 18.102033615112305
z: -7.173819303512573
getWorldCoordinates
x: 422.50000131979897
y: 249.49997927733767
z: 0
function getWorldCoordinates(position){
var screenpoint = viewer.worldToClient(
new THREE.Vector3(position.x,
position.y,
position.z,));
return screenpoint
}
function getObjPosition(dbId) {
function getObjPosition(dbId) {
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();
return position;
}

Unfortunately I was unable to reproduce the issue...
Try live demo here - refresh the page and see the world coords prompted when the model completes loading ...
Be sure to convert the coords after the model finishes loading (e.g. after the TEXTURES_LOADED_EVENT) otherwise you may get erratic results:
NOP_VIEWER.addEventListener(Autodesk.Viewing.TEXTURES_LOADED_EVENT,()=>{
alert(JSON.stringify(NOP_VIEWER.worldToClient(
new THREE.Vector3(-26.277027130126953,
18.102033615112305,
-7.173819303512573,))))
})

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);

Google Place Autocomplete - geometry bounds properties

I cannot find this answer in the Google docs so I am posting my question here.
As per the Google Places Autocomplete response docs, bounds is one of the returned fields.
However, when console logging bounds in the browser, the object has strangely named keys such as Ba, Ab, Ra and so on..
Furthermore, I have noticed that these keys change overtime.
For example, the following code might fail within a few days. In the initial search for say, New York, bounds.Ab.g may contain a number value.
however, after a few days bounds.Ab.g might become bounds.Bb.g and the original value will be undefined.
import PlacesAutocomplete, { geocodeByAddress } from 'react-places-autocomplete'
const GooglePlacesSearchBar = () => {
const handleSelect = async userInput => {
const result = await geocodeByAddress(userInput)
const { place_id, geometry, formatted_address } = result[0]
const { bounds } = geometry
const swBounds = [bounds.Ab.g, bounds.Ra.g]
const neBounds = [bounds.Ab.h, bounds.Ra.h]
...
}
This is an example of the bounds object printed in console.
bounds: _.Wf
Bb: Vf
g: 49.19817700000001
h: 49.3172939
[[Prototype]]: Object
Ra: Qf
g: -123.22474
h: -123.023068
Could anyone point to a doc or explain what these keys stand for and why they keep changing?
thanks to #geocodezip for the answers in the comments section.
here is the solution
const result = await geocodeByAddress(userInput)
const { geometry } = result[0]
const { bounds } = geometry
const NELat = bounds.getNorthEast().lat()
const NELng = bounds.getNorthEast().lng()
const SWLat = bounds.getSouthWest().lat()
const SWLng = bounds.getSouthWest().lng()
Strange decision by Google indeed.

x, y, z coordinates of an object for a nwc file in forge viewer

I am trying to find x,y,z coordinates of an object inside nwc model, and I am using below code.
Despite that this code was working for rvt files, it is not working for nwc model.
Is there a way to get x,y,z coordinates from a nwc model?
getFragmentWorldMatrixByNodeId(nodeId) {
let result = {
fragId: [],
matrix: [],
};
let viewer = this.viewer;
this.tree.enumNodeFragments(nodeId, function (frag) {
let fragProxy = viewer.impl.getFragmentProxy(viewer.model, frag);
let matrix = new THREE.Matrix4();
fragProxy.getWorldMatrix(matrix);
result.fragId.push(frag);
result.matrix.push(matrix);
});
return result;
}
You mentioned you are looking for the "x,y,z coordinates of an object". What exactly do you mean by that? I'm going to assume that you want the coordinates of the center point of the object's bounding box, as that is what people ask for usually. In your code snippet, however, you're retrieving the entire transformation matrix, not a position.
If the center point of a bounding box works for you, you could obtain it like so:
function getObjectBoundingBox(model, dbid) {
const tree = model.getInstanceTree();
const frags = model.getFragmentList();
let totalBounds = new THREE.Box3();
tree.enumNodeFragments(dbid, function (fragid) {
let fragBounds = new THREE.Box3();
frags.getWorldBounds(fragid, fragBounds);
totalBounds.union(fragBounds);
}, true);
return totalBounds;
}
getObjectBoundingBox(viewer.model, 123).center();

Revit shared coordinates to Forge viewer

What is the correct process for getting a transform between Forge coordinates and Revit's shared coordinates? I know there is globalOffset, but does it reference the Revit project internal coordinate system or shared coordinates?
Update Jun 11th, 2021
Now my MultipleModelUtil.js supports the alignments I shared below. Also, we can easily tell Forge Viewer to use By shared coordinates to aggregate models. Here is the code snippet, and you can check out here to know supported alignments
const util = new MultipleModelUtil( viewer );
util.options = {
alignment: MultipleModelAlignmentType.ShareCoordinates
};
const models = [
{ name: '1.rvt', urn: 'urn:dXJuOmFkc2sud2lwcHJvZDpmcy5maWxlOnZmLlNpaHgxOTVuUVJDMHIyWXZUSVRuZFE_dmVyc2lvbj0x' },
{ name: '2.rvt', urn: 'urn:dXJuOmFkc2sud2lwcHJvZDpmcy5maWxlOnZmLldVRHJ4ajZ6UTBPLTRrbWZrZ3ZoLUE_dmVyc2lvbj0x' },
{ name: '3.rvt', urn: 'urn:dXJuOmFkc2sud2lwcHJvZDpmcy5maWxlOnZmLjRyZW5HRTNUU25xNHhYaW5xdWtyaWc_dmVyc2lvbj0x' }
];
util.processModels( models );
==================
First, Forge Viewer supports 3 kinds of Revit link methods as the below, and you can take a look at the 3rd one (By shared coordinates).
Origin to origin: Apply the globalOffset of the 1st model to others. Check MultipleModelUtil/MultipleModelUtil.js for the demo
Center to center: the default way of the viewer.
By shared coordinates: set up applyRefpoint: true and make the globalOffset to the refPoint. This method is the one you are looking for.
The refPoint is the Revit survey point location inside Revit internal coordinate system. It's accessible with the AecModelData. Meanwhile, you can take advantage of the AggregatedView to use this aligning option. Here is an example of telling how to use AggregatedView:
https://gist.github.com/yiskang/c404af571ba4d631b5929c777503891e
If you want to use this logic with the Viewer class directly, here is a code snippet for you:
let globalOffset = null;
const aecModelData = bubbleNode.getAecModelData();
const tf = aecModelData && aecModelData.refPointTransformation; // Matrix4x3 as array[12]
const refPoint = tf ? { x: tf[9], y: tf[10], z: 0.0 } : { x: 0, y: 0, z: 0 };
// Check if the current globalOffset is sufficiently close to the refPoint to avoid inaccuracies.
const MaxDistSqr = 4.0e6;
const distSqr = globalOffset && THREE.Vector3.prototype.distanceToSquared.call(refPoint, globalOffset);
if (!globalOffset || distSqr > MaxDistSqr) {
globalOffset = new THREE.Vector3().copy(refPoint);
}
viewer.loadDocumentNode(doc, bubbleNode, { applyRefpoint: true, globalOffset: globalOffset, keepCurrentModels: true });
The bubbleNode can be either of the following:
bubbleNode = doc.getRoot().getDefaultGeometry()
//Or
const viewables = viewerDocument.getRoot().search({'type':'geometry'});
bubbleNode = viewables[0];
To get AecModelData, please refer to my gist: https://gist.github.com/yiskang/c404af571ba4d631b5929c777503891e#file-index-html-L67
// Call this line before using AecModelData
await doc.downloadAecModelData();
// doc.downloadAecModelData(() => resolve(doc));
See here for details of the AecModelData: https://forge.autodesk.com/blog/consume-aec-data-which-are-model-derivative-api
I've also found success feeding the refPointTransformation into a matrix4.
This way, the orientation of the model is also taken into account. (This is based off Eason's Answer).
const bubbleNode = doc.getRoot().getDefaultGeometry();
await doc.downloadAecModelData();
const aecModelData = bubbleNode.getAecModelData();
const tf = aecModelData && aecModelData.refPointTransformation;
const matrix4 = new THREE.Matrix4()
.makeBasis(
new THREE.Vector3(tf[0], tf[1], tf[2]),
new THREE.Vector3(tf[3], tf[4], tf[5]),
new THREE.Vector3(tf[6], tf[7], tf[8])
)
.setPosition(new THREE.Vector3(tf[9], tf[10], tf[11]))
viewer.loadDocumentNode(doc, viewables, {
placementTransform: matrix4,
keepCurrentModels: true,
globalOffset: {
"x": 0,
"y": 0,
"z": 0
},
applyRefpoint: true,
applyScaling: 'ft',
})