3d model embed with Three JS - working example

Hi Folks

With my very basic knowledge of JS I finally managed to successfully embed a 3d object partially based on this awesome CodePen:

https://codepen.io/ricardoolivaalonso/pen/poYEpwP

Here is the example project:
Three JS.zip (2.2 MB)

As of now the canvas has a fixed size but in the future I want to explore wrapping the canvas in a flex container for more responsiveness. Or maybe there is a way not to define the size of the render and make it always take the full width and height of a custom container.

Anybody up for a challenge?

Would try add something like this:

https://levelup.gitconnected.com/dynamically-resizing-the-html5-canvas-with-vanilla-javascript-c64588a0b798

1 Like

You do have flexible layout pins turned on for the element, so all you would need to do is check the scale boxes in the scene size setting to allow the document to change its size:

Screenshot 2024-03-12 at 5.15.52 PM

Presently you have Head HTML code that is making the canvas size take up the full size of the Hype element:

#loader {
    ...
    width: 100%;
    height: 100%;

If you want it to be something else, you could define those width/height sizes in another metric, like px.

1 Like

All of the suggested solutions work but here is what I decided to do in the end as I think there are some good practice lessons on displaying 3d objects to be learned here.

The canvas element needs to have an explicit size in units either in the JS or as attributes within the element. You can then apply additional CSS properties on the contained in which your canvas is wrapped but I think the best option is to have a fixed aspect ratio of the canvas especially if you enable zooming on the object to avoid clipping. If the height and width are not the same and one is significantly smaller than the other, then your object will be clipped especially when zoomed.

Using the flexible layout options within Hype of the canvas element works but in this project I wanted the object to be part of a Bootstrap Card.

This is what I did in the end. I used a Flexbox container within the card and had the object along with text displayed in a row. Then I used custom '.col' classes on the flex children so that when the card container becomes a bit tight the row changes into a column and the object is moved above the text.

Here is the final version. Hope you like it!

ThreeJS Final.zip (2.6 MB)

And here is the complete source code:
https://codepen.io/grzegorz-rogala/pen/KKYgveb

Obviously and suggestions, tweaks and improvements are welcome. I;m really glad I managed to get ThreeJS going within Hype!

Credits:

ThreeJS code based on the amazing work of Ricardo Olivia Alonso:
https://codepen.io/ricardoolivaalonso

3d model by Stratovarius1980 on Sketchfab
https://sketchfab.com/3d-models/ancient-greek-helmet-2841e71562f643a6ac9cd7290bb68c8a

Thanks folks!

2 Likes

Thanks for the writeup on how you got it all working! It looks pretty great - I definitely think this presentation is much more useful than anything static/2D for sure.

Very nice.

Had a little play to make the cards into symbols. :smile:
This can be refined further to individualise further using the Additional Html attributes.
But for now I just use them to determine the .glb file to load into the symbol.

We give a symbol the file name as the value for data-glb in the attributes.

With some changes to the css and js and structure.
We do not use any innerHTML code directly.

We mirror the div structure you had using groups and give them the appropriate classes.

Inside the third dic/group (object-3d d-flex) we add an oval and give it a class name of canvas

We add thyme text elements to the div/group ( card-3d-body )
The text elements get their appropriate class names.

The canvas is loaded into the oval / canvas element via the loadObject() function which is now called on symbol load.

We do not query the document here, we query the symbol element for it;''s child elements using the class names.

This is how a canvas is  created and then loaded.

	 function loadCanvas() {
        var canvas = document.createElement('canvas');
        div = element.querySelector(".canvas"); 
        canvas.class   = "webgl";
        canvas.width= 300;
        canvas.height= 300;
        canvas.style.cursor="grab";
        //canvas.style.position = "absolute";
        
        div.appendChild(canvas)
        return  canvas
    }
    
const loading = element.querySelector('#loader')

const canvas =   loadCanvas() //document.querySelector('.webgl')
const scene = new THREE.Scene()
const textureLoader = new THREE.TextureLoader()
const sizes = {
    width: canvas.width,
    height: canvas.height
}

Later on

//Loader
const loader = new THREE.GLTFLoader()
const threedObject = element.dataset.glb
 
 
loader.load('${resourcesFolderName}/' + threedObject ,
    (gltf) => {
        const model = gltf.scene
        scene.add(model)
		scene.position.set(0,0,0)
		loading.style.display = 'none'

    },
    ( xhr ) => {
		console.log( ( xhr.loaded / xhr.total * 100 ) + '% loaded' )
    }
)

All seems to work ok. Just thought it would be more useful as a symbol. As I mention above, it can be refined further. Including the css.

ThreeJS MH.zip (2.7 MB)

4 Likes

You really took it to the next level Mark :slight_smile:

I like the idea to turn the card into a symbol as the model could be shared across scenes.

2 Likes

Hi Mark

Just one final question to wrap up importing the THREE library into Hype as I made some changes to my previous approach and need some help.

So before, to use THREE in Hype I would just import the library with other add-ons using a script tag with source attribute in the head of the document with a URL to a CDN and that worked fine. That also meant that I could only use older versions of the library.

However, a while back Three library was changes and they are now recommending creating an import map to the latest version in their documentation:

https://threejs.org/docs/index.html#manual/en/introduction/Installation

I created an import map within the head of the document but don't know where would be a good place to put the import declarations. I included them in the function that loads the model but it does not work.

ThreeJS Final V2.zip (2.6 MB)

Any pointers on how to make this work are greatly appreciated.
Thanks for your help.

Greg

I'm not an expert on modules/imports, but was able to get it working with the following steps:

  1. make the importmap script the top item in the head html
  2. add a new script just below that of type "module" that has your imports (as imports I think only work from modules)
  3. Add items from your module to the window object so they are globals
  4. Modify your loadObject() function to remove the imports
  5. Modify your loadObject() function to not use addons via the THREE object, as it is not possible to extend the THREE object. Instead just call the addons directly. (eg THREE.OrbitControls just becomes OrbitControls.)

Please see:
ThreeJS Final V2-fixed.hype.zip (2.6 MB)

Maybe there's a better way :slight_smile:.

2 Likes

first use https://optimizeglb.com that helmet is at over 3.5Mb who needs this kind load time? That helmet is less than 300kb now

That worked well. Thanks Jonathan!

The most confusing thing I find about JS modules is that some of them use paths and some for the use URLs to CDM sites in the import declaration.

When I tried to deploy a similar project in CodePen and include the import declaration in the JS section, CodePen displayed a warning message saying:

Imports are only supported as native ES Modules. Would you like to replace invalid imports with esm.sh?"

And then it converted the declaration to URLs ro ESM eliminating the need for an import map altogether.

1 Like

Thanks Peter.

Compressed glb files require DracoLoader which I'm still struggling with.

Draco wa now? Sigh, my wish for Hype to one day just give everyone the ability to load 3d files compressed not compressed just simply select add and allow drag, keyframe as for now everything is just some sort of js injection, some code on top of code that needs to be added and its always a make it or break it of an experimentation.

1 Like

Like @Jonathan , I am not familiar with modules but had been looking at this and got it to the point of no errors but still could not get it loading. I can see the last steps I need to do. Glad he got it working for you.

2 Likes