How to call animate() to add CSS2DRenderer labels

Hello,
Following this post : Speckle Viewer for History of Geometry
My colleague @ahmedwael94 has achieved to embed nominations via Speckle.TopSolid which I can get in the console :tada:
I have looked at this example you’ve provided : https://codesandbox.io/p/sandbox/dawn-bird-ft36lr?file=%2Fsrc%2FLabelling.ts%3A83%2C12-83%2C48

As I’m not using a public constructor, I don’t get how to achieve these lines :
public onRender() {
this.labelRenderer.render(
this.viewer.getRenderer().scene,
this.viewer.getRenderer().renderingCamera
);
}

In the three example here : three.js/examples/css2d_label.html at 817a222f2d12baf44a38baf256bc9d4f65d82465 · mrdoob/three.js · GitHub
They put it inside an animate () function.

What corresponds to this animate function inside Speckle Viewer ?
Thanks!

1 Like

Hi @RaphaelVouilloz

Cool project! I’ve read your post from showcase and I’m very happy to hear you’re finding Speckle useful! :partying_face:

The example you are mentioning is using a custom viewer extension to display the labels. Generally, extensions are the goto approach on adding/customizing viewer functionality. Extensions offer out-of-the-box frame callbacks like onRender which is being used in the example

I’m not entirely sure what you mean by this, but my assumption is that you are not developing viewer extensions, but rather customizing the viewer library entirely. Maybe you want to give extensions a chance, see if they can make your life easier :wink:

The animate function in that three.js example is just a callback for the browser’s requestAnimationFrame. There are multiple ways you can get yourself an animation frame callback. One way would be to extend the SpeckleRenderer and override it’s render or update functions that get automatically called by the viewer. But probably an easier approach would be to just stack your own animation frame callback like they do in the three.js example:

function animate() {
  requestAnimationFrame( animate );
  // You code here
}

Just remember that you need to manually call animate once in order to get the loop going

Let us know if you need more help!

Cheers

2 Likes

Hi @alex
Thank you very much !
Indeed, to launch the prototype I was playing with a single js file. You are definitely right about extensions, so I have started a new ts project similar to your example.
But I still have a problem : I guess this line doesn’t work from my Labelling.ts to my main.ts file:

this.viewer.getRenderer().scene.add(earthLabel);

I have no vs code error or console error… but when I inspect the html the earthDiv is just not inside the global CSS2D div
I put the code here: speckle-viewer/src at main · rvouilloz/speckle-viewer · GitHub

The main-.ts file is a test, adding a CSS2DObject to a basic three scene, which works. I am missing something either with ts Extensions or with speckle.viewer.

Thanks

Hi @RaphaelVouilloz

I’ve checkout out your repo and there is something you are missing. In Labelling.ts extension

public addLabel() {
    const earthDiv = document.createElement("div");
    earthDiv.className = "label";
    earthDiv.textContent = "Earth";
    const earthLabel = new CSS2DObject(earthDiv);
    earthLabel.position.set(0, 0, 0);
    earthLabel.layers.set(ObjectLayers.OVERLAY); <-- This is required
    this.viewer.getRenderer().scene.add(earthLabel); // This line doesn't work, earthDiv isnt' added to html
  }

The viewer works with object layers as a means of separating stuff that needs to get rendered into ‘categories’ as it were. You don’t necessary need to use ObjectLayers.OVERLAY, as the label’s layer, other layers will work as well, however ObjectLayers.OVERLAY is recommended in this case, because that layer gets rendered at the end and it works nicely with 2D elements.

This would be the only change required in order to get things running, however another thing I recommend is to use the viewer’s ViewerEvent.LoadComplete event to start executing stuff that is stream related. Loading streams is asynchronous in the viewer, so until that event gets fired, you won’t have all the data from the stream available to work with. Also, you don’t need to manually call the onRender function of extensions. That gets called automatically each frame.

Cheers

2 Likes

Fantastic, it works! Thanks @alex :clap:
May I add one question : I’ve added a getNomination() function to the repo : from this, I can get each vertex.Name and vertex.namePosVector of the TopSolid sketches points to which we’ve added nominations. The last value is the position of the letter to the point relative to the 2d screen (so to be handled as css padding or margin)

My question is : these points are of speckle_type: “Objects.Geometry.Point”. How can I obtain there position in space? Three getWorldPosition() method doesn’t seem to work

Hi @RaphaelVouilloz

For ease of use, I’ve made this live sandbox which shows how you can get point positions and also adds labels to those locations (not sure if that was your intention, but I did it anyway :slight_smile: )

Currently there is extra complexity in retrieving point and line geometry data, but we are planning on adding some abstractions on top, like we did with meshes via BatchObject, sometime in the future

Cheers

2 Likes

Hi @alex
Awesome, yes that is exactly what we want!
That was the function I was searching for :

const pos = this.getPointPosition(vertex.id);

Now we have all that we need. I’ll share a prototype in the coming weeks
Thanks!

1 Like