Skip to content

Commit

Permalink
USDZExporter: Added textures support
Browse files Browse the repository at this point in the history
  • Loading branch information
mrdoob committed Feb 10, 2021
1 parent b397418 commit e99aa7b
Show file tree
Hide file tree
Showing 2 changed files with 129 additions and 7 deletions.
2 changes: 1 addition & 1 deletion editor/js/Menubar.File.js
Original file line number Diff line number Diff line change
Expand Up @@ -390,7 +390,7 @@ function MenubarFile( editor ) {

var exporter = new USDZExporter();

saveArrayBuffer( exporter.parse( editor.scene, { binary: true } ), 'model.usdz' );
saveArrayBuffer( await exporter.parse( editor.scene, { binary: true } ), 'model.usdz' );

} );
options.add( option );
Expand Down
134 changes: 128 additions & 6 deletions examples/jsm/exporters/USDZExporter.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,26 +2,71 @@ import { zipSync, strToU8 } from '../libs/fflate.module.min.js';

class USDZExporter {

parse( scene ) {
async parse( scene ) {

let output = buildHeader();

const materials = {};
const textures = {};

scene.traverse( ( object ) => {

if ( object.isMesh ) {

materials[ object.material.uuid ] = object.material;
output += buildXform( object, buildMesh( object.geometry, object.material ) );
const geometry = object.geometry;
const material = object.material;

materials[ material.uuid ] = material;

if ( material.map !== null ) textures[ material.map.uuid ] = material.map;
if ( material.normalMap !== null ) textures[ material.normalMap.uuid ] = material.normalMap;
if ( material.aoMap !== null ) textures[ material.aoMap.uuid ] = material.aoMap;
if ( material.roughnessMap !== null ) textures[ material.roughnessMap.uuid ] = material.roughnessMap;
if ( material.metalnessMap !== null ) textures[ material.metalnessMap.uuid ] = material.metalnessMap;
if ( material.emissiveMap !== null ) textures[ material.emissiveMap.uuid ] = material.emissiveMap;

output += buildXform( object, buildMesh( geometry, material ) );

}

} );

output += buildMaterials( materials );
output += buildTextures( textures );

const files = {};

for ( const uuid in textures ) {

return zipSync( { 'model.usda': strToU8( output ) }, { level: 0 } );
const texture = textures[ uuid ];
files[ 'Texture_' + texture.id + '.jpg' ] = await img2U8( texture.image );

}

return zipSync( { 'model.usda': strToU8( output ), 'textures': files }, { level: 0 } );

}

}

async function img2U8( image ) {

if ( ( typeof HTMLImageElement !== 'undefined' && image instanceof HTMLImageElement ) ||
( typeof HTMLCanvasElement !== 'undefined' && image instanceof HTMLCanvasElement ) ||
( typeof OffscreenCanvas !== 'undefined' && image instanceof OffscreenCanvas ) ||
( typeof ImageBitmap !== 'undefined' && image instanceof ImageBitmap ) ) {

const canvas = document.createElement( 'canvas' );
canvas.width = image.width;
canvas.height = image.height;

const context = canvas.getContext( '2d' );
context.translate( 0, canvas.height );
context.scale( 1, - 1 );
context.drawImage( image, 0, 0, canvas.width, canvas.height );

const blob = await new Promise( resolve => canvas.toBlob( resolve, 'image/jpeg' ) );
return new Uint8Array( await blob.arrayBuffer() );

}

Expand Down Expand Up @@ -201,6 +246,45 @@ ${ array.join( '' ) }

function buildMaterial( material ) {

const textures = [];

if ( material.map !== null ) {

textures.push( ` float3 inputs:diffuseColor.connect = </Textures/Texture_${ material.map.id }.outputs:rgb>` );

}

if ( material.normalMap !== null ) {

textures.push( ` float3 inputs:normal.connect = </Textures/Texture_${ material.normalMap.id }.outputs:rgb>` );

}

if ( material.aoMap !== null ) {

textures.push( ` float inputs:occlusion.connect = </Textures/Texture_${ material.aoMap.id }.outputs:rgb>` );

}

if ( material.roughnessMap !== null ) {

textures.push( ` float inputs:roughness.connect = </Textures/Texture_${ material.roughnessMap.id }.outputs:rgb>` );

}

if ( material.metalnessMap !== null ) {

textures.push( ` float inputs:metalness.connect = </Textures/Texture_${ material.metalnessMap.id }.outputs:rgb>` );

}

if ( material.emissiveMap !== null ) {

textures.push( ` float3 inputs:emissive.connect = </Textures/Texture_${ material.emissiveMap.id }.outputs:rgb>` );

}


return `
def Material "Material_${ material.id }"
{
Expand All @@ -209,17 +293,55 @@ function buildMaterial( material ) {
def Shader "PreviewSurface"
{
uniform token info:id = "UsdPreviewSurface"
color3f inputs:diffuseColor = ${ buildColor( material.color ) }
color3f inputs:emissiveColor = ${ buildColor( material.emissive ) }
float3 inputs:diffuseColor = ${ buildColor( material.color ) }
float inputs:metallic = ${ material.metalness }
float inputs:roughness = ${ material.roughness }
${ textures.join( '\n' ) }
int inputs:useSpecularWorkflow = 0
token outputs:surface
}
}
`;

}

function buildTextures( textures ) {

const array = [];

for ( const uuid in textures ) {

const texture = textures[ uuid ];

array.push( buildTexture( texture ) );

}

return `def "Textures"
{
${ array.join( '' ) }
}
`;

}

function buildTexture( texture ) {

return `
def Shader "Texture_${ texture.id }"
{
uniform token info:id = "UsdUVTexture"
asset inputs:file = @textures/Texture_${ texture.id }.jpg@
token inputs:isSRGB = "auto"
token inputs:wrapS = "repeat"
token inputs:wrapT = "repeat"
float3 outputs:rgb
}
`;

}

function buildColor( color ) {

return `(${ color.r }, ${ color.g }, ${ color.b })`;
Expand Down

0 comments on commit e99aa7b

Please sign in to comment.