Hi everyone,
I’m building an editor that allows users to design their own sweaters.
This is done by providing blank models and allowing users to change the color, add text, upload prints, etc.
We also want to give users the option to download the modify 3D model (as GLB file).
The app is built with Next.js
and mainly uses React Three Fiber
.
I’m using a React ref
to take the model from the scene and pass it to my download3DModel
function.
import { GLTFExporter } from 'three/examples/jsm/exporters/GLTFExporter'
import { WebIO } from '@gltf-transform/core'
import { KHRONOS_EXTENSIONS } from '@gltf-transform/extensions'
import { draco } from '@gltf-transform/functions'
const download3DModel = async (object) => {
const options = { binary: true }
new GLTFExporter().parse(
object,
exporterCallback,
errorCallback,
options
)
async function exporterCallback(gltf) {
const io = new WebIO()
.registerExtensions(KHRONOS_EXTENSIONS)
.registerDependencies({
'draco3d.encoder': await new DracoEncoderModule(),
'draco3d.decoder': await new DracoDecoderModule()
})
const doc = await io.readBinary(new Uint8Array(gltf))
await doc.transform(
draco() // Compress mesh geometry with Draco.
)
// Create compressed GLB, in an ArrayBuffer.
const arrayBuffer = await io.writeBinary(doc)
const optimisedBlob = new Blob([arrayBuffer])
saveAs(optimisedBlob, 'sweater.glb')
}
}
While this is broadly working, there are still several problems when it comes to the details.
I’ve been working with three.js
for a few months now and would consider myself somewhere between beginner and intermediate by now, but the issues stemming from exporting are the hardest ones I’ve faced so far and in all three cases I’m not even sure how to even go about properly inspecting the errors.
Those problems are:
1. Decals Become Distorted
The design from the screenshot above turns into this when exported:
I have experimented with this extensively and noticed that some of the compression comes just with the exporting, but most of it comes from the compression.
Here is the code I’m using to render the decal:
<Decal
position={[x, y, 0.1]}
rotation={[Math.PI, 0, Math.PI]}
scale={[scaleX, scaleY, scaleZ]}
opacity={1}
map={print}
map-anisotropy={16}
/>
It is rendered onto the model’s body <mesh>
.
I tried using a polygonOffset
, but that didn’t work.
@vis_prime on Discord suggested running a loop and offsetting the decal’s vertices, but I’m still working on this.
I get the following console warnings when exporting:
THREE. GLTFExporter: Merged metalnessMap and roughnessMap textures.
THREE.GLTFExporter: Creating normalized normal attribute from the non-normalized one.
[KHR_draco_mesh_compression] Skipping Draco compression on non-indexed primitive.
2. Color Changes
As you see from the above screenshots, the color is faded when compared to the one rendered on the canvas.
When using darker colors, the exported versions are much more glossy.
(note how the decal is also more faded)
I’m not entirely sure what to make of this and have few indications of what might be causing this.
3. Using Text Prevents Exports
When rendering text (as a decal) and trying to download the model, I get the following type error:
TypeError: Failed to execute 'drawImage' on 'CanvasRenderingContext2D': The provided value is not of type '(CSSImageValue or HTMLCanvasElement or HTMLImageElement or HTMLVideoElement or ImageBitmap or OffscreenCanvas or SVGImageElement or VideoFrame)'. window.console.error @ next-dev.js:24
.
Here is how I render text decals on the canvas:
<Decal
position={[x, y, -0.5]}
rotation={[Math.PI, 0, Math.PI]}
>
<meshBasicMaterial
transparent
polygonOffset={true}
polygonOffsetFactor={-10}
>
<RenderTexture attach='map' anisotropy={16}>
<PerspectiveCamera
makeDefault
aspect={1 / 1}
position={[0, 0, 1]}
/>
<Text
color={hex}
fontSize={size}
font={`/fonts/${font}.ttf`}
>
{content}
</Text>
</RenderTexture>
</meshBasicMaterial>
</Decal>
I’m not really sure what to make of this as it almost seems like it’s an internal issue, but I feel like something might be wrong with the structure of my text decal.
I’d be thankful for any type of input as I’m trying to debug these issues.
Thank you everyone for your help!
- Daniel