@@ -717,6 +717,7 @@
|
|
717
717
|
if (!this.isNewEvent(evt.timeStamp, id, this._pointerUpTimestamp)) continue;
|
718
718
|
const ne = this.createPointerEventFromTouch("pointerup", touch.identifier, touch.clientX, touch.clientY, touch.force, evt);
|
719
719
|
this.onUp(ne);
|
720
|
+
this._pointerIds.splice(this._pointerIds.indexOf(id), 1); // remove pointer id from used pointers
|
720
721
|
}
|
721
722
|
}
|
722
723
|
private createPointerEventFromTouch(type: InputEventNames, touchIdentifier: number, x: number, y: number, force: number, evt: Event): NEPointerEvent {
|
@@ -830,24 +831,8 @@
|
|
830
831
|
}
|
831
832
|
if (debug) console.log(evt.pointerType, "DOWN", index);
|
832
833
|
if (!this.isInRect(evt)) return;
|
834
|
+
if (this.isMouseEventEmmitedFromTouch(evt)) return;
|
833
835
|
|
834
|
-
// TODO: this whole pointer handling doesnt consider that in VR we have multiple pointers. It's not enough to store the button as the pointer ID anymore
|
835
|
-
|
836
|
-
// check if we received an mouse UP event for a touch (for some reason we get a mouse.down for touch.up)
|
837
|
-
if (evt.pointerType === PointerType.Mouse) {
|
838
|
-
const upTime = this._pointerUpTimestamp[index];
|
839
|
-
if (upTime > 0 && evt.source?.timeStamp !== undefined) {
|
840
|
-
const diff = (evt.source.timeStamp - upTime);
|
841
|
-
// on android touch up and mouse up have the exact same value
|
842
|
-
// but on iOS they are not the same but a few milliseconds apart
|
843
|
-
if (diff < 60 && diff >= 0) {
|
844
|
-
// we received an UP event for a touch, ignore this DOWN event
|
845
|
-
if (debug) console.log("Ignoring mouse.down for touch.up");
|
846
|
-
return;
|
847
|
-
}
|
848
|
-
}
|
849
|
-
}
|
850
|
-
|
851
836
|
this.setPointerState(index, this._pointerPressed, true);
|
852
837
|
this.setPointerState(index, this._pointerDown, true);
|
853
838
|
this.setPointerStateT(index, this._pointerEvent, evt.source);
|
@@ -874,6 +859,7 @@
|
|
874
859
|
const isDown = this.getPointerPressed(index);
|
875
860
|
if (isDown === false && !this.isInRect(evt)) return;
|
876
861
|
if (evt.pointerType === PointerType.Touch && !isDown) return;
|
862
|
+
if (this.isMouseEventEmmitedFromTouch(evt)) return;
|
877
863
|
if (debug) console.log(evt.pointerType, "MOVE", index, "hasSpace=" + evt.space != null);
|
878
864
|
|
879
865
|
this.updatePointerPosition(evt);
|
@@ -887,7 +873,9 @@
|
|
887
873
|
if (debug) console.log(evt.pointerType, "UP", index, "was not down");
|
888
874
|
return;
|
889
875
|
}
|
876
|
+
if (this.isMouseEventEmmitedFromTouch(evt)) return;
|
890
877
|
if (debug) console.log(evt.pointerType, "UP", index);
|
878
|
+
|
891
879
|
this.setPointerState(index, this._pointerPressed, false);
|
892
880
|
this.setPointerStateT(index, this._pointerEvent, evt.source);
|
893
881
|
this.setPointerState(index, this._pointerUp, true);
|
@@ -927,6 +915,36 @@
|
|
927
915
|
this.onDispatchEvent(evt);
|
928
916
|
}
|
929
917
|
|
918
|
+
private isMouseEventEmmitedFromTouch(evt: NEPointerEvent) {
|
919
|
+
if (evt.pointerType === PointerType.Mouse) {
|
920
|
+
const lastPointer = this._pointerUpTimestamp.map((v, i) => ({ timestamp: v, index: i})).sort((a, b) => a.timestamp - b.timestamp).at(-1);
|
921
|
+
if(lastPointer && this._pointerTypes[lastPointer.index] === PointerType.Touch) {
|
922
|
+
if (lastPointer.timestamp > 0 && evt.source?.timeStamp !== undefined) {
|
923
|
+
const diff = (evt.source.timeStamp - lastPointer.timestamp);
|
924
|
+
if (diff < 320 && diff >= 0) {
|
925
|
+
// we received an UP event for a touch, ignore this DOWN event
|
926
|
+
if (debug) console.log("Ignoring mouse.down for touch.up");
|
927
|
+
|
928
|
+
return true;
|
929
|
+
}
|
930
|
+
}
|
931
|
+
}
|
932
|
+
|
933
|
+
/* const upTime = this._pointerUpTimestamp[index];
|
934
|
+
if (upTime > 0 && evt.source?.timeStamp !== undefined) {
|
935
|
+
const diff = (evt.source.timeStamp - upTime);
|
936
|
+
// on android touch up and mouse up have the exact same value
|
937
|
+
// but on iOS they are not the same but a few milliseconds apart
|
938
|
+
if (diff < 60 && diff >= 0) {
|
939
|
+
// we received an UP event for a touch, ignore this DOWN event
|
940
|
+
if (debug) console.log("Ignoring mouse.down for touch.up");
|
941
|
+
return;
|
942
|
+
}
|
943
|
+
} */
|
944
|
+
}
|
945
|
+
return false;
|
946
|
+
}
|
947
|
+
|
930
948
|
private updatePointerPosition(evt: NEPointerEvent) {
|
931
949
|
const index = evt.pointerId;
|
932
950
|
|
@@ -346,7 +346,7 @@
|
|
346
346
|
}
|
347
347
|
this.hoveredByID.delete(args.pointerId);
|
348
348
|
|
349
|
-
// if it was up, it means it
|
349
|
+
// if it was up, it means it should notify things that it down on before
|
350
350
|
if (args.isUp) {
|
351
351
|
this.pressedByID.get(args.pointerId)?.handlers.forEach(h => this.invokeOnPointerUp(args, h));
|
352
352
|
this.pressedByID.delete(args.pointerId);
|
@@ -515,7 +515,7 @@
|
|
515
515
|
case "hand":
|
516
516
|
// for hands and controller we assume they are never totally still (except for simulated environments)
|
517
517
|
// we might want to add a threshold here (e.g. if a user holds their hand very still or controller)
|
518
|
-
// so maybe check the angle
|
518
|
+
// so maybe check the angle every frame?
|
519
519
|
break;
|
520
520
|
}
|
521
521
|
|
@@ -558,6 +558,14 @@
|
|
558
558
|
// The original component that received the down event SHOULD also receive the up event
|
559
559
|
pressedEvent?.handlers.delete(comp);
|
560
560
|
}
|
561
|
+
|
562
|
+
// handle touch onExit (touchUp) since the pointer stops existing
|
563
|
+
// mouse onExit (mouseUp) is handled when we hover over something else / on nothing
|
564
|
+
// Mouse 0 is always persistent
|
565
|
+
if (comp.onPointerExit && args.event?.pointerType === PointerType.Touch) {
|
566
|
+
this.handlePointerExit(comp, args);
|
567
|
+
this.hoveredByID.delete(args.pointerId!);
|
568
|
+
}
|
561
569
|
}
|
562
570
|
|
563
571
|
if (args.isClick) {
|
@@ -312,7 +312,7 @@
|
|
312
312
|
const t = gripPose.transform;
|
313
313
|
this._gripPosition.set(t.position.x, t.position.y, t.position.z);
|
314
314
|
this._gripQuaternion.set(t.orientation.x, t.orientation.y, t.orientation.z, t.orientation.w);
|
315
|
-
if (gripPose.linearVelocity)
|
315
|
+
if ("linearVelocity" in gripPose && gripPose.linearVelocity)
|
316
316
|
this._linearVelocity.set(gripPose.linearVelocity.x, gripPose.linearVelocity.y, gripPose.linearVelocity.z);
|
317
317
|
}
|
318
318
|
}
|
@@ -33,9 +33,10 @@
|
|
33
33
|
import { Renderer } from '../../Renderer.js';
|
34
34
|
|
35
35
|
function makeNameSafe( str ) {
|
36
|
+
// Remove characters that are not allowed in USD ASCII identifiers
|
36
37
|
str = str.replace( /[^a-zA-Z0-9_]/g, '' );
|
37
38
|
|
38
|
-
//
|
39
|
+
// If name doesn't start with a-zA-Z_, add _ to the beginning – required by USD
|
39
40
|
if ( !str.match( /^[a-zA-Z_]/ ) )
|
40
41
|
str = '_' + str;
|
41
42
|
|
@@ -87,6 +88,7 @@
|
|
87
88
|
|
88
89
|
uuid: string;
|
89
90
|
name: string;
|
91
|
+
displayName: string;
|
90
92
|
matrix: Matrix4;
|
91
93
|
private _isDynamic: boolean;
|
92
94
|
get isDynamic() { return this._isDynamic; }
|
@@ -123,6 +125,7 @@
|
|
123
125
|
|
124
126
|
this.uuid = id;
|
125
127
|
this.name = makeNameSafe( name );
|
128
|
+
this.displayName = name;
|
126
129
|
this.matrix = matrix.clone();
|
127
130
|
this.geometry = mesh;
|
128
131
|
this.material = material;
|
@@ -616,7 +619,7 @@
|
|
616
619
|
if ( canvas ) {
|
617
620
|
|
618
621
|
const blob = await canvas.convertToBlob( {type: isRGBA ? 'image/png' : 'image/jpeg', quality: 0.95 } );
|
619
|
-
files[ `textures
|
622
|
+
files[ `textures/${id}.${isRGBA ? 'png' : 'jpg'}` ] = new Uint8Array( await blob.arrayBuffer() );
|
620
623
|
|
621
624
|
} else {
|
622
625
|
|
@@ -702,6 +705,8 @@
|
|
702
705
|
|
703
706
|
if ( model ) {
|
704
707
|
|
708
|
+
model.displayName = object.name;
|
709
|
+
|
705
710
|
if ( parentModel ) {
|
706
711
|
|
707
712
|
parentModel.add( model );
|
@@ -804,7 +809,7 @@
|
|
804
809
|
|
805
810
|
if ( material && ( 'isMeshStandardMaterial' in material && material.isMeshStandardMaterial || 'isMeshBasicMaterial' in material && material.isMeshBasicMaterial ) ) { // TODO convert unlit to lit+emissive
|
806
811
|
|
807
|
-
const geometryFileName = 'geometries/
|
812
|
+
const geometryFileName = 'geometries/' + getGeometryName(geometry, object.name) + '.usda';
|
808
813
|
|
809
814
|
if ( ! ( geometryFileName in context.files ) ) {
|
810
815
|
|
@@ -1034,10 +1039,14 @@
|
|
1034
1039
|
|
1035
1040
|
}
|
1036
1041
|
|
1037
|
-
function getBoneName(bone) {
|
1042
|
+
function getBoneName(bone: Object3D) {
|
1038
1043
|
return makeNameSafe(bone.name || 'bone_' + bone.uuid);
|
1039
1044
|
}
|
1040
1045
|
|
1046
|
+
function getGeometryName(geometry: BufferGeometry, fallbackName: string) {
|
1047
|
+
return makeNameSafe(geometry.name || fallbackName || 'Geometry_') + geometry.id;
|
1048
|
+
}
|
1049
|
+
|
1041
1050
|
function getPathToSkeleton(bone: Object3D, assumedRoot: Object3D) {
|
1042
1051
|
let path = getBoneName(bone);
|
1043
1052
|
let current = bone.parent;
|
@@ -1084,20 +1093,24 @@
|
|
1084
1093
|
// NE-4084: To use the doubleSided workaround with skeletal meshes we'd have to
|
1085
1094
|
// also emit extra data for jointIndices etc., so we're skipping skinned meshes here.
|
1086
1095
|
if (context.quickLookCompatible && material && material.side === DoubleSide && !isSkinnedMesh)
|
1087
|
-
writer.appendLine(`prepend references = @./geometries
|
1096
|
+
writer.appendLine(`prepend references = @./geometries/${getGeometryName(geometry, name)}.usda@</Geometry_doubleSided>`);
|
1088
1097
|
else
|
1089
|
-
writer.appendLine(`prepend references = @./geometries
|
1098
|
+
writer.appendLine(`prepend references = @./geometries/${getGeometryName(geometry, name)}.usda@</Geometry>`);
|
1090
1099
|
writer.appendLine(`prepend apiSchemas = [${apiSchemas}]`);
|
1091
|
-
writer.closeBlock( ")" );
|
1092
|
-
writer.beginBlock();
|
1093
1100
|
}
|
1094
1101
|
else if ( camera )
|
1095
|
-
writer.beginBlock( `def Camera "${name}"
|
1102
|
+
writer.beginBlock( `def Camera "${name}"`, "(", false );
|
1096
1103
|
else
|
1097
|
-
writer.beginBlock( `def Xform "${name}"
|
1104
|
+
writer.beginBlock( `def Xform "${name}"`, "(", false);
|
1098
1105
|
|
1106
|
+
if (model.displayName)
|
1107
|
+
writer.appendLine(`displayName = "${model.displayName}"`);
|
1108
|
+
writer.closeBlock( ")" );
|
1109
|
+
writer.beginBlock();
|
1110
|
+
|
1099
1111
|
if ( geometry && material ) {
|
1100
|
-
|
1112
|
+
const materialName = makeNameSafe(material.name) + `_${material.id}`;
|
1113
|
+
writer.appendLine( `rel material:binding = </StageRoot/Materials/${materialName}>` );
|
1101
1114
|
|
1102
1115
|
// Turns out QuickLook / RealityKit doesn't support the doubleSided attribute, so we
|
1103
1116
|
// work around that by emitting additional indices above, and then we shouldn't emit the attribute either as geometry is
|
@@ -1183,7 +1196,7 @@
|
|
1183
1196
|
|
1184
1197
|
// Mesh
|
1185
1198
|
|
1186
|
-
function buildMeshObject( geometry, bonesArray: Bone[] = [] ) {
|
1199
|
+
function buildMeshObject( geometry: BufferGeometry, bonesArray: Bone[] = [] ) {
|
1187
1200
|
|
1188
1201
|
const mesh = buildMesh( geometry, bonesArray );
|
1189
1202
|
return `
|
@@ -1446,22 +1459,47 @@
|
|
1446
1459
|
|
1447
1460
|
}
|
1448
1461
|
|
1462
|
+
/** Slot of the exported texture. Some slots (e.g. normal, opacity) require additional processing. */
|
1463
|
+
declare type MapType = 'diffuse' | 'normal' | 'occlusion' | 'opacity' | 'roughness' | 'emissive' | 'metallic' | 'transmission';
|
1464
|
+
|
1449
1465
|
function buildMaterial( material: MeshBasicMaterial, textures: TextureMap, quickLookCompatible = false ) {
|
1450
1466
|
|
1451
1467
|
// https://graphics.pixar.com/usd/docs/UsdPreviewSurface-Proposal.html
|
1452
1468
|
|
1453
|
-
const pad = '
|
1469
|
+
const pad = ' ';
|
1454
1470
|
const inputs: Array<string> = [];
|
1455
1471
|
const samplers: Array<string> = [];
|
1472
|
+
const materialName = makeNameSafe(material.name) + `_${material.id}`;
|
1456
1473
|
|
1457
|
-
function
|
1474
|
+
function texName(tex: Texture) {
|
1475
|
+
return makeNameSafe(tex.name) + '_' + tex.id;
|
1476
|
+
}
|
1458
1477
|
|
1459
|
-
|
1478
|
+
function buildTexture( texture: Texture, mapType: MapType, color: Color | undefined = undefined, opacity: number | undefined = undefined ) {
|
1460
1479
|
|
1480
|
+
const name = texName(texture);
|
1481
|
+
// File names need to be unique; so when we're baking color and/or opacity into a texture we need to keep track of that.
|
1482
|
+
// TODO This currently results in textures potentially being exported multiple times that are actually identical!
|
1483
|
+
// const colorHex = color ? color.getHexString() : undefined;
|
1484
|
+
// const colorId = ( color && colorHex != 'ffffff' ? '_' + color.getHexString() : '' );
|
1485
|
+
const id = name + ( opacity !== undefined && opacity !== 1.0 ? '_' + opacity : '' );
|
1486
|
+
|
1461
1487
|
// Seems neither QuickLook nor usdview support scale/bias on .a values, so we need to bake opacity multipliers into
|
1462
1488
|
// the texture. This is not ideal.
|
1463
|
-
const opacityIsAppliedToTextureAndNotAsScale = quickLookCompatible && opacity !== undefined;
|
1489
|
+
const opacityIsAppliedToTextureAndNotAsScale = quickLookCompatible && opacity !== undefined && opacity !== 1.0;
|
1464
1490
|
const scaleToApply = opacityIsAppliedToTextureAndNotAsScale ? new Vector4(1, 1, 1, opacity) : undefined;
|
1491
|
+
|
1492
|
+
// Treat undefined opacity as 1
|
1493
|
+
if (opacity === undefined)
|
1494
|
+
opacity = 1.0;
|
1495
|
+
|
1496
|
+
// When we're baking opacity into the texture, we shouldn't apply it as scale as well – so we set it to 1.
|
1497
|
+
if (opacityIsAppliedToTextureAndNotAsScale)
|
1498
|
+
opacity = 1.0;
|
1499
|
+
|
1500
|
+
// sanitize scaleToApply.w === 0 - this isn't working right now, seems the PNG can't be read back properly.
|
1501
|
+
if (scaleToApply && scaleToApply.w <= 0.05) scaleToApply.w = 0.05;
|
1502
|
+
|
1465
1503
|
textures[ id ] = { texture, scale: scaleToApply };
|
1466
1504
|
|
1467
1505
|
const uv = texture.channel > 0 ? 'st' + texture.channel : 'st';
|
@@ -1508,7 +1546,7 @@
|
|
1508
1546
|
}
|
1509
1547
|
|
1510
1548
|
const needsTextureTransform = ( repeat.x != 1 || repeat.y != 1 || offset.x != 0 || offset.y != 0 || rotation != 0 );
|
1511
|
-
const textureTransformInput = `${materialRoot}
|
1549
|
+
const textureTransformInput = `${materialRoot}/${materialName}/${'uvReader_' + uv}.outputs:result>`; const textureTransformOutput = `${materialRoot}/${materialName}/Transform2d_${mapType}.outputs:result>`;
|
1512
1550
|
const needsTextureScale = mapType !== 'normal' && (color && (color.r !== 1 || color.g !== 1 || color.b !== 1 || opacity !== 1)) || false;
|
1513
1551
|
|
1514
1552
|
const needsNormalScaleAndBias = mapType === 'normal';
|
@@ -1532,14 +1570,14 @@
|
|
1532
1570
|
float2 outputs:result
|
1533
1571
|
}
|
1534
1572
|
` : '' }
|
1535
|
-
def Shader "
|
1573
|
+
def Shader "${name}_${mapType}"
|
1536
1574
|
{
|
1537
1575
|
uniform token info:id = "UsdUVTexture"
|
1538
|
-
asset inputs:file = @textures
|
1576
|
+
asset inputs:file = @textures/${id}.${isRGBA ? 'png' : 'jpg'}@
|
1539
1577
|
token inputs:sourceColorSpace = "${ texture.colorSpace === 'srgb' ? 'sRGB' : 'raw' }"
|
1540
1578
|
float2 inputs:st.connect = ${needsTextureTransform ? textureTransformOutput : textureTransformInput}
|
1541
1579
|
${needsTextureScale ? `
|
1542
|
-
float4 inputs:scale = (${color ? color.r + ', ' + color.g + ', ' + color.b : '1, 1, 1'}, ${
|
1580
|
+
float4 inputs:scale = (${color ? color.r + ', ' + color.g + ', ' + color.b : '1, 1, 1'}, ${opacity})
|
1543
1581
|
` : `` }
|
1544
1582
|
${needsNormalScaleAndBias ? `
|
1545
1583
|
float4 inputs:scale = (${normalScaleValueString}, ${normalScaleValueString}, ${normalScaleValueString}, 1)
|
@@ -1557,6 +1595,8 @@
|
|
1557
1595
|
}
|
1558
1596
|
|
1559
1597
|
let effectiveOpacity = ( material.transparent || material.alphaTest ) ? material.opacity : 1;
|
1598
|
+
let haveConnectedOpacity = false;
|
1599
|
+
let haveConnectedOpacityThreshold = false;
|
1560
1600
|
|
1561
1601
|
if ( material instanceof MeshPhysicalMaterial && material.transmission !== undefined) {
|
1562
1602
|
|
@@ -1565,18 +1605,21 @@
|
|
1565
1605
|
|
1566
1606
|
}
|
1567
1607
|
|
1568
|
-
if ( material.map
|
1608
|
+
if ( material.map ) {
|
1569
1609
|
|
1570
|
-
inputs.push( `${pad}color3f inputs:diffuseColor.connect = ${materialRoot}
|
1610
|
+
inputs.push( `${pad}color3f inputs:diffuseColor.connect = ${materialRoot}/${materialName}/${texName(material.map)}_diffuse.outputs:rgb>` );
|
1571
1611
|
|
1572
1612
|
if ( material.transparent ) {
|
1573
1613
|
|
1574
|
-
inputs.push( `${pad}float inputs:opacity.connect = ${materialRoot}
|
1614
|
+
inputs.push( `${pad}float inputs:opacity.connect = ${materialRoot}/${materialName}/${texName(material.map)}_diffuse.outputs:a>` );
|
1615
|
+
haveConnectedOpacity = true;
|
1575
1616
|
|
1576
1617
|
} else if ( material.alphaTest > 0.0 ) {
|
1577
1618
|
|
1578
|
-
inputs.push( `${pad}float inputs:opacity.connect = ${materialRoot}
|
1619
|
+
inputs.push( `${pad}float inputs:opacity.connect = ${materialRoot}/${materialName}/${texName(material.map)}_diffuse.outputs:a>` );
|
1620
|
+
haveConnectedOpacity = true;
|
1579
1621
|
inputs.push( `${pad}float inputs:opacityThreshold = ${material.alphaTest}` );
|
1622
|
+
haveConnectedOpacityThreshold = true;
|
1580
1623
|
|
1581
1624
|
}
|
1582
1625
|
|
@@ -1588,9 +1631,22 @@
|
|
1588
1631
|
|
1589
1632
|
}
|
1590
1633
|
|
1634
|
+
if ( material.alphaHash ) {
|
1635
|
+
|
1636
|
+
// Seems we can do this to basically enforce alpha hashing / dithered transparency in QuickLook –
|
1637
|
+
// works completely different in usdview though...
|
1638
|
+
if (haveConnectedOpacityThreshold) {
|
1639
|
+
console.warn('Opacity threshold for ' + material.name + ' was already connected. Skipping alphaHash opacity threshold.');
|
1640
|
+
}
|
1641
|
+
else {
|
1642
|
+
inputs.push( `${pad}float inputs:opacityThreshold = 0.0000000001` );
|
1643
|
+
haveConnectedOpacityThreshold = true;
|
1644
|
+
}
|
1645
|
+
}
|
1646
|
+
|
1591
1647
|
if ( material.aoMap ) {
|
1592
1648
|
|
1593
|
-
inputs.push( `${pad}float inputs:occlusion.connect = ${materialRoot}
|
1649
|
+
inputs.push( `${pad}float inputs:occlusion.connect = ${materialRoot}/${materialName}/${texName(material.aoMap)}_occlusion.outputs:r>` );
|
1594
1650
|
|
1595
1651
|
samplers.push( buildTexture( material.aoMap, 'occlusion' ) );
|
1596
1652
|
|
@@ -1598,19 +1654,32 @@
|
|
1598
1654
|
|
1599
1655
|
if ( material.alphaMap ) {
|
1600
1656
|
|
1601
|
-
inputs.push( `${pad}float inputs:opacity.connect = ${materialRoot}
|
1602
|
-
|
1657
|
+
inputs.push( `${pad}float inputs:opacity.connect = ${materialRoot}/${materialName}/${texName(material.alphaMap)}_opacity.outputs:r>` );
|
1658
|
+
// TODO this is likely not correct and will prevent blending from working correctly – masking will be used instead
|
1659
|
+
inputs.push( `${pad}float inputs:opacityThreshold = 0.0000000001` );
|
1660
|
+
haveConnectedOpacity = true;
|
1661
|
+
haveConnectedOpacityThreshold = true;
|
1603
1662
|
|
1604
1663
|
samplers.push( buildTexture( material.alphaMap, 'opacity', new Color( 1, 1, 1 ), effectiveOpacity ) );
|
1605
1664
|
|
1606
|
-
} else {
|
1665
|
+
} else { // opacity will always be connected if we have a map
|
1607
1666
|
|
1608
|
-
|
1667
|
+
if (haveConnectedOpacity) {
|
1668
|
+
console.warn('Opacity for ' + material.name + ' was already connected. Skipping default opacity.');
|
1669
|
+
} else {
|
1670
|
+
inputs.push( `${pad}float inputs:opacity = ${effectiveOpacity}` );
|
1671
|
+
haveConnectedOpacity = true;
|
1672
|
+
}
|
1609
1673
|
|
1610
1674
|
if ( material.alphaTest > 0.0 ) {
|
1611
1675
|
|
1612
|
-
|
1613
|
-
|
1676
|
+
if (haveConnectedOpacityThreshold) {
|
1677
|
+
console.warn('Opacity threshold for ' + material.name + ' was already connected. Skipping default opacity threshold.');
|
1678
|
+
}
|
1679
|
+
else {
|
1680
|
+
inputs.push( `${pad}float inputs:opacityThreshold = ${material.alphaTest}` );
|
1681
|
+
haveConnectedOpacityThreshold = true;
|
1682
|
+
}
|
1614
1683
|
}
|
1615
1684
|
|
1616
1685
|
}
|
@@ -1619,7 +1688,7 @@
|
|
1619
1688
|
|
1620
1689
|
if ( material.emissiveMap ) {
|
1621
1690
|
|
1622
|
-
inputs.push( `${pad}color3f inputs:emissiveColor.connect = ${materialRoot}
|
1691
|
+
inputs.push( `${pad}color3f inputs:emissiveColor.connect = ${materialRoot}/${materialName}/${texName(material.emissiveMap)}_emissive.outputs:rgb>` );
|
1623
1692
|
const color = material.emissive.clone();
|
1624
1693
|
color.multiplyScalar( material.emissiveIntensity );
|
1625
1694
|
samplers.push( buildTexture( material.emissiveMap, 'emissive', color ) );
|
@@ -1632,13 +1701,14 @@
|
|
1632
1701
|
|
1633
1702
|
} else {
|
1634
1703
|
|
1635
|
-
|
1704
|
+
// We don't need to export (0,0,0) as emissive color
|
1705
|
+
// inputs.push( `${pad}color3f inputs:emissiveColor = (0, 0, 0)` );
|
1636
1706
|
|
1637
1707
|
}
|
1638
1708
|
|
1639
1709
|
if ( material.normalMap ) {
|
1640
1710
|
|
1641
|
-
inputs.push( `${pad}normal3f inputs:normal.connect = ${materialRoot}
|
1711
|
+
inputs.push( `${pad}normal3f inputs:normal.connect = ${materialRoot}/${materialName}/${texName(material.normalMap)}_normal.outputs:rgb>` );
|
1642
1712
|
|
1643
1713
|
samplers.push( buildTexture( material.normalMap, 'normal' ) );
|
1644
1714
|
|
@@ -1646,7 +1716,7 @@
|
|
1646
1716
|
|
1647
1717
|
if ( material.roughnessMap && material.roughness === 1 ) {
|
1648
1718
|
|
1649
|
-
inputs.push( `${pad}float inputs:roughness.connect = ${materialRoot}
|
1719
|
+
inputs.push( `${pad}float inputs:roughness.connect = ${materialRoot}/${materialName}/${texName(material.roughnessMap)}_roughness.outputs:g>` );
|
1650
1720
|
|
1651
1721
|
samplers.push( buildTexture( material.roughnessMap, 'roughness' ) );
|
1652
1722
|
|
@@ -1658,7 +1728,7 @@
|
|
1658
1728
|
|
1659
1729
|
if ( material.metalnessMap && material.metalness === 1 ) {
|
1660
1730
|
|
1661
|
-
inputs.push( `${pad}float inputs:metallic.connect = ${materialRoot}
|
1731
|
+
inputs.push( `${pad}float inputs:metallic.connect = ${materialRoot}/${materialName}/${texName(material.metalnessMap)}_metallic.outputs:b>` );
|
1662
1732
|
|
1663
1733
|
samplers.push( buildTexture( material.metalnessMap, 'metallic' ) );
|
1664
1734
|
|
@@ -1678,7 +1748,7 @@
|
|
1678
1748
|
|
1679
1749
|
if ( !material.transparent && ! (material.alphaTest > 0.0) && material.transmissionMap) {
|
1680
1750
|
|
1681
|
-
inputs.push( `${pad}float inputs:opacity.connect = ${materialRoot}
|
1751
|
+
inputs.push( `${pad}float inputs:opacity.connect = ${materialRoot}/${materialName}/${texName(material.transmissionMap)}_transmission.outputs:r>` );
|
1682
1752
|
|
1683
1753
|
samplers.push( buildTexture( material.transmissionMap, 'transmission' ) );
|
1684
1754
|
}
|
@@ -1686,37 +1756,39 @@
|
|
1686
1756
|
}
|
1687
1757
|
|
1688
1758
|
return `
|
1689
|
-
|
1690
|
-
|
1691
|
-
|
1692
|
-
|
1693
|
-
|
1759
|
+
def Material "${materialName}" (
|
1760
|
+
${material.name ? `displayName = "${material.name}"` : ''}
|
1761
|
+
)
|
1762
|
+
{
|
1763
|
+
def Shader "PreviewSurface"
|
1764
|
+
{
|
1765
|
+
uniform token info:id = "UsdPreviewSurface"
|
1694
1766
|
${inputs.join( '\n' )}
|
1695
|
-
|
1696
|
-
|
1697
|
-
|
1767
|
+
int inputs:useSpecularWorkflow = 0
|
1768
|
+
token outputs:surface
|
1769
|
+
}
|
1698
1770
|
|
1699
|
-
|
1771
|
+
token outputs:surface.connect = ${materialRoot}/${materialName}/PreviewSurface.outputs:surface>
|
1700
1772
|
|
1701
|
-
|
1702
|
-
|
1703
|
-
|
1704
|
-
|
1705
|
-
|
1706
|
-
|
1707
|
-
|
1773
|
+
def Shader "uvReader_st"
|
1774
|
+
{
|
1775
|
+
uniform token info:id = "UsdPrimvarReader_float2"
|
1776
|
+
token inputs:varname = "st"
|
1777
|
+
float2 inputs:fallback = (0.0, 0.0)
|
1778
|
+
float2 outputs:result
|
1779
|
+
}
|
1708
1780
|
|
1709
|
-
|
1710
|
-
|
1711
|
-
|
1712
|
-
|
1713
|
-
|
1714
|
-
|
1715
|
-
|
1781
|
+
def Shader "uvReader_st2"
|
1782
|
+
{
|
1783
|
+
uniform token info:id = "UsdPrimvarReader_float2"
|
1784
|
+
token inputs:varname = "st2"
|
1785
|
+
float2 inputs:fallback = (0.0, 0.0)
|
1786
|
+
float2 outputs:result
|
1787
|
+
}
|
1716
1788
|
|
1717
1789
|
${samplers.join( '\n' )}
|
1718
1790
|
|
1719
|
-
|
1791
|
+
}
|
1720
1792
|
`;
|
1721
1793
|
|
1722
1794
|
}
|