Converting this cross sectional shape only to cylinder geometry and not from square

Hi i am trying to use fabric js output data url of canvas to fit exactly as if it is rolled and cylinder is created but not getting how would one do that?

As i worked on project that was needed to be completed fast, i used same square geometry which is what three js gives as a default and nicely converted to cylinder of intended shape.

This is how my shape is currently which is inside the mask of canvas in fabric js. I am not sure how to exactly prepare png from mask but that i will figure out on my own

but unfortunately the shape which is inside clipping area is not what being currently converted to 3d but it is this square image which i am getting after performing toDataUrl over canvas.

and thus, all parts which are outside clipping mask are also coming to 3d…

I only need to convert the cross section shape to cylinder so that it is perfectly covered with whole texture as it it were rollled over that. How to do that?

Here is some code how 2d image is prepared in fabric js

window.__canvas.clone(function(clonedCanvas) {
    clonedCanvas.clipPath = null;

    window.clonedCanvas = clonedCanvas;
    const clonedCanvasObjects = clonedCanvas.getObjects();

    clonedCanvasObjects.forEach(function(clonedCanvasObject,i) {
        
        clonedCanvasObject.set({
            clipPath: null,
            
        })
        if(clonedCanvasObject.excludeFromExportIn3D) {
            clonedCanvas.remove(clonedCanvasObject)
        }
    });

    if(self.isInside3dView) {
        window.dispatchEvent(new CustomEvent('toggle-3d', { detail: {
            url: url,
            topBottomTextureUrl: clonedCanvas.backgroundImage?.src,
            formData: formData,
            tagImageTextureUrl: img._originalElement.src
        }, bubbles: true }));
    }
},['excludeFromExportIn3D','name', 'filters', 'viewportTransform'])

|
and once toggle-3d event is fired, here is how i handled it in three js via script type module in other file.

import * as THREE from 'three';
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';

const canvas3dwrapper = document.querySelector('.canvas-wrapper-container');
const canvas3dholder = document.getElementById('canvas-3d-holder')

var renderer = new THREE.WebGLRenderer({alpha: false, antialias : true});
renderer.setClearColor( 0xFFFFFF, 0 );

renderer.setSize( canvas3dwrapper.offsetWidth, canvas3dwrapper.offsetWidth );
canvas3dholder.appendChild( renderer.domElement );
//addMouseHandler(renderer.domElement )

const cylinder = new THREE.Mesh();
const camera = new THREE.PerspectiveCamera( 75, 1, 0.1, 800 );
const controls = new OrbitControls( camera, renderer.domElement );
controls.maxZoom = 20;
controls.minZoom = 1;

const scene = new THREE.Scene();


camera.position.set( 0, 0, 20);

//camera.position.z = 30;

function animate() {
    requestAnimationFrame( animate );
    controls.update();
    renderer.render( scene, camera );
}
animate();



window.addEventListener('toggle-3d',function(event) {

    renderer.setSize( canvas3dwrapper.offsetWidth, canvas3dwrapper.offsetWidth );
    
    //remove old objects from scene
    while(scene.children.length > 0){ 
        scene.remove(scene.children[0]);
    }
    
    
    const textureUrl = event.detail.url;
    const texture = new THREE.TextureLoader().load(textureUrl,(texture) => {
        

        texture.colorSpace = THREE.SRGBColorSpace;
        const materials = [
            new THREE.MeshBasicMaterial({map: texture, side: THREE.DoubleSide})
        ];
        if(event.detail.topBottomTextureUrl) {
            const topBottomTexture = new THREE.TextureLoader().load( event.detail.topBottomTextureUrl,(texture) => {
                texture.colorSpace = THREE.SRGBColorSpace;
                const material = new THREE.MeshBasicMaterial({
                    map: texture
                })
                materials.push(material, material)
            }) 
        } else {
            const material = new THREE.MeshBasicMaterial({
                color: 0Xffffff
            });
            materials.push(material, material)
           
        }

        const geometry = new THREE.CylinderGeometry(0.7, 3.0, 13.5, 1500);

        
        cylinder.geometry = geometry;
        cylinder.material =  materials; 
        //const cylinder = new THREE.Mesh( geometry, materials );
        cylinder.position.set(0, 2, 0);
        cylinder.rotation.y = Math.PI;

        if(event.detail.formData) {
            camera.position.set( 0, 0, 20);
        }
        scene.add(cylinder);
    })
})

Is the png and mask shape always the same? Or are they customisable by the user?

Png might be different depending on what user adds on the canvas like background, text etc but mask would be always same.

In that case the easiest way to map only a specific region of an image onto a cone is by just creating it in Blender and UV mapping it to fit the mask (the resolution of the PNG won’t matter, since UVs are based on the coordinates, not the resolution itself, so you will be able to put any image as the texture as long as the mask shape doesn’t differ.)

2 Likes

Ah, I was also reading some questions and also came across that word blender.
Few questions-

  • Can you tell me more about blender as i am not familiar with it?

  • how i can use it for my use case for creating that mapping? I am also not familiar with uv mapping as well.

  • and btw where do i need to use that mapping after lets say i create that?

Each vertex in a 3d model has an additional 2d coordinate associated with it… called the “uv” coordinate, or a “texture coordinate”. You can use 3d modelling software like Blender to visualize and edit these coordinates.
I loaded your texture into blender, and added it to a cylinder:

That is what the default mapping looks like ^
(uv map is the image on the left, the 3d view is on the right)

I did some editing of the uv’s and vertices and came up with this:

You can export from blender as a glb (binary gltf) file and load it in your app… then just switch out the material.map to the image you need, assuming it has the same layout.

Here’s the .blend file…
shashant.blend (1.2 MB)

And the exported .glb

shashant.glb (258.7 KB)

that you can drag on here:

To preview it.

3 Likes

@manthrax
Thanks for the response but i will still need code for that. It would be helpful if you can give me uv mapping may be? i am not sure though
How can i use these two files (blend and glb) in javascript code? to convert to exact 3d of that cross sectional image? Do i need to use uv mapping? and how?

I am using the same code as i asked in this question and i have event.detail.textureUrl which i load in 3d and use that texture which i got as the function parameter to wrap over the cone (which is made from cylinder)

Yeah. Instead of the cylinder, you can load the .glb file exported from the .blend file.
Then you swap the cone mesh.material.map to whatever texture you have (that has the same layout) and it will be mapped onto the cone.