updated with enemy

This commit is contained in:
sebastianhampel1 2023-04-10 23:58:47 -04:00
parent deda6a7810
commit 2815cfe844
419 changed files with 33788 additions and 854 deletions

8
Assets/Adobe.meta Normal file
View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 4a9976dfedf6d8b45b5bb3f03320094d
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: b663793eb4aeb35429a1791214f3a9f9
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: dfe765b550e1b604682ed4d23a504e89
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,19 @@
{
"name": "Adobe.SubstanceEditor",
"rootNamespace": "Adobe.SubstanceEditor",
"references": [
"GUID:0322a29457affe94ab4ade0a1555000f",
"GUID:b78cd19d3dcc04e4a8eb1915990b2db1"
],
"includePlatforms": [
"Editor"
],
"excludePlatforms": [],
"allowUnsafeCode": false,
"overrideReferences": false,
"precompiledReferences": [],
"autoReferenced": true,
"defineConstraints": [],
"versionDefines": [],
"noEngineReferences": false
}

View File

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: b5520fe70c6e8e046b4383b7dd2c75c8
AssemblyDefinitionImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: c96dedd774b1103439d2b3517649cf88
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@ -0,0 +1,108 @@
fileFormatVersion: 2
guid: 30be34334090e2b478db7867a8d81934
TextureImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 11
mipmaps:
mipMapMode: 0
enableMipMap: 0
sRGBTexture: 1
linearTexture: 0
fadeOut: 0
borderMipMap: 0
mipMapsPreserveCoverage: 0
alphaTestReferenceValue: 0.5
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: 0.25
normalMapFilter: 0
isReadable: 0
streamingMipmaps: 0
streamingMipmapsPriority: 0
vTOnly: 0
grayScaleToAlpha: 0
generateCubemap: 6
cubemapConvolution: 0
seamlessCubemap: 0
textureFormat: 1
maxTextureSize: 2048
textureSettings:
serializedVersion: 2
filterMode: 1
aniso: 1
mipBias: 0
wrapU: 1
wrapV: 1
wrapW: 0
nPOTScale: 0
lightmap: 0
compressionQuality: 50
spriteMode: 1
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: 0.5, y: 0.5}
spritePixelsToUnits: 100
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spriteGenerateFallbackPhysicsShape: 1
alphaUsage: 1
alphaIsTransparency: 1
spriteTessellationDetail: -1
textureType: 8
textureShape: 1
singleChannelComponent: 0
flipbookRows: 1
flipbookColumns: 1
maxTextureSizeSet: 0
compressionQualitySet: 0
textureFormatSet: 0
ignorePngGamma: 0
applyGammaDecoding: 0
platformSettings:
- serializedVersion: 3
buildTarget: DefaultTexturePlatform
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 0
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Standalone
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 0
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites: []
outline: []
physicsShape: []
bones: []
spriteID: 5e97eb03825dee720800000000000000
internalID: 0
vertices: []
indices:
edges: []
weights: []
secondaryTextures: []
spritePackingTag:
pSDRemoveMatte: 0
pSDShowRemoveMatteOption: 0
userData:
assetBundleName:
assetBundleVariant:

Binary file not shown.

After

Width:  |  Height:  |  Size: 464 B

View File

@ -0,0 +1,108 @@
fileFormatVersion: 2
guid: f582f763e5df3c241b43ca4f4eac7f4c
TextureImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 11
mipmaps:
mipMapMode: 0
enableMipMap: 0
sRGBTexture: 1
linearTexture: 0
fadeOut: 0
borderMipMap: 0
mipMapsPreserveCoverage: 0
alphaTestReferenceValue: 0.5
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: 0.25
normalMapFilter: 0
isReadable: 0
streamingMipmaps: 0
streamingMipmapsPriority: 0
vTOnly: 0
grayScaleToAlpha: 0
generateCubemap: 6
cubemapConvolution: 0
seamlessCubemap: 0
textureFormat: 1
maxTextureSize: 2048
textureSettings:
serializedVersion: 2
filterMode: 1
aniso: 1
mipBias: 0
wrapU: 1
wrapV: 1
wrapW: 0
nPOTScale: 0
lightmap: 0
compressionQuality: 50
spriteMode: 1
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: 0.5, y: 0.5}
spritePixelsToUnits: 100
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spriteGenerateFallbackPhysicsShape: 1
alphaUsage: 1
alphaIsTransparency: 1
spriteTessellationDetail: -1
textureType: 8
textureShape: 1
singleChannelComponent: 0
flipbookRows: 1
flipbookColumns: 1
maxTextureSizeSet: 0
compressionQualitySet: 0
textureFormatSet: 0
ignorePngGamma: 0
applyGammaDecoding: 0
platformSettings:
- serializedVersion: 3
buildTarget: DefaultTexturePlatform
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 0
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Standalone
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 0
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites: []
outline: []
physicsShape: []
bones: []
spriteID: 5e97eb03825dee720800000000000000
internalID: 0
vertices: []
indices:
edges: []
weights: []
secondaryTextures: []
spritePackingTag:
pSDRemoveMatte: 0
pSDShowRemoveMatteOption: 0
userData:
assetBundleName:
assetBundleVariant:

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

View File

@ -0,0 +1,108 @@
fileFormatVersion: 2
guid: cf4c0b432f60d4c42bcf5e564e5a356a
TextureImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 11
mipmaps:
mipMapMode: 0
enableMipMap: 0
sRGBTexture: 1
linearTexture: 0
fadeOut: 0
borderMipMap: 0
mipMapsPreserveCoverage: 0
alphaTestReferenceValue: 0.5
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: 0.25
normalMapFilter: 0
isReadable: 0
streamingMipmaps: 0
streamingMipmapsPriority: 0
vTOnly: 0
grayScaleToAlpha: 0
generateCubemap: 6
cubemapConvolution: 0
seamlessCubemap: 0
textureFormat: 1
maxTextureSize: 2048
textureSettings:
serializedVersion: 2
filterMode: 1
aniso: 1
mipBias: 0
wrapU: 1
wrapV: 1
wrapW: 0
nPOTScale: 0
lightmap: 0
compressionQuality: 50
spriteMode: 1
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: 0.5, y: 0.5}
spritePixelsToUnits: 100
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spriteGenerateFallbackPhysicsShape: 1
alphaUsage: 1
alphaIsTransparency: 1
spriteTessellationDetail: -1
textureType: 8
textureShape: 1
singleChannelComponent: 0
flipbookRows: 1
flipbookColumns: 1
maxTextureSizeSet: 0
compressionQualitySet: 0
textureFormatSet: 0
ignorePngGamma: 0
applyGammaDecoding: 0
platformSettings:
- serializedVersion: 3
buildTarget: DefaultTexturePlatform
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 0
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Standalone
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 0
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites: []
outline: []
physicsShape: []
bones: []
spriteID: 5e97eb03825dee720800000000000000
internalID: 0
vertices: []
indices:
edges: []
weights: []
secondaryTextures: []
spritePackingTag:
pSDRemoveMatte: 0
pSDShowRemoveMatteOption: 0
userData:
assetBundleName:
assetBundleVariant:

Binary file not shown.

After

Width:  |  Height:  |  Size: 670 B

View File

@ -0,0 +1,108 @@
fileFormatVersion: 2
guid: 0aa309be25f1381499253b908921e763
TextureImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 11
mipmaps:
mipMapMode: 0
enableMipMap: 0
sRGBTexture: 1
linearTexture: 0
fadeOut: 0
borderMipMap: 0
mipMapsPreserveCoverage: 0
alphaTestReferenceValue: 0.5
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: 0.25
normalMapFilter: 0
isReadable: 0
streamingMipmaps: 0
streamingMipmapsPriority: 0
vTOnly: 0
grayScaleToAlpha: 0
generateCubemap: 6
cubemapConvolution: 0
seamlessCubemap: 0
textureFormat: 1
maxTextureSize: 2048
textureSettings:
serializedVersion: 2
filterMode: 1
aniso: 1
mipBias: 0
wrapU: 1
wrapV: 1
wrapW: 0
nPOTScale: 0
lightmap: 0
compressionQuality: 50
spriteMode: 1
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: 0.5, y: 0.5}
spritePixelsToUnits: 100
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spriteGenerateFallbackPhysicsShape: 1
alphaUsage: 1
alphaIsTransparency: 1
spriteTessellationDetail: -1
textureType: 8
textureShape: 1
singleChannelComponent: 0
flipbookRows: 1
flipbookColumns: 1
maxTextureSizeSet: 0
compressionQualitySet: 0
textureFormatSet: 0
ignorePngGamma: 0
applyGammaDecoding: 0
platformSettings:
- serializedVersion: 3
buildTarget: DefaultTexturePlatform
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 0
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Standalone
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 0
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites: []
outline: []
physicsShape: []
bones: []
spriteID: 5e97eb03825dee720800000000000000
internalID: 0
vertices: []
indices:
edges: []
weights: []
secondaryTextures: []
spritePackingTag:
pSDRemoveMatte: 0
pSDShowRemoveMatteOption: 0
userData:
assetBundleName:
assetBundleVariant:

Binary file not shown.

After

Width:  |  Height:  |  Size: 978 B

View File

@ -0,0 +1,108 @@
fileFormatVersion: 2
guid: 729db32ebfbf92e49aec2d8b254e28e7
TextureImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 11
mipmaps:
mipMapMode: 0
enableMipMap: 0
sRGBTexture: 1
linearTexture: 0
fadeOut: 0
borderMipMap: 0
mipMapsPreserveCoverage: 0
alphaTestReferenceValue: 0.5
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: 0.25
normalMapFilter: 0
isReadable: 0
streamingMipmaps: 0
streamingMipmapsPriority: 0
vTOnly: 0
grayScaleToAlpha: 0
generateCubemap: 6
cubemapConvolution: 0
seamlessCubemap: 0
textureFormat: 1
maxTextureSize: 2048
textureSettings:
serializedVersion: 2
filterMode: 1
aniso: 1
mipBias: 0
wrapU: 1
wrapV: 1
wrapW: 0
nPOTScale: 0
lightmap: 0
compressionQuality: 50
spriteMode: 1
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: 0.5, y: 0.5}
spritePixelsToUnits: 100
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spriteGenerateFallbackPhysicsShape: 1
alphaUsage: 1
alphaIsTransparency: 1
spriteTessellationDetail: -1
textureType: 8
textureShape: 1
singleChannelComponent: 0
flipbookRows: 1
flipbookColumns: 1
maxTextureSizeSet: 0
compressionQualitySet: 0
textureFormatSet: 0
ignorePngGamma: 0
applyGammaDecoding: 0
platformSettings:
- serializedVersion: 3
buildTarget: DefaultTexturePlatform
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 0
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Standalone
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 0
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites: []
outline: []
physicsShape: []
bones: []
spriteID: 5e97eb03825dee720800000000000000
internalID: 0
vertices: []
indices:
edges: []
weights: []
secondaryTextures: []
spritePackingTag:
pSDRemoveMatte: 0
pSDShowRemoveMatteOption: 0
userData:
assetBundleName:
assetBundleVariant:

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.0 KiB

View File

@ -0,0 +1,135 @@
fileFormatVersion: 2
guid: 1ee05e940668f9f4c9dd8e52aba9675f
TextureImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 12
mipmaps:
mipMapMode: 0
enableMipMap: 1
sRGBTexture: 1
linearTexture: 0
fadeOut: 0
borderMipMap: 0
mipMapsPreserveCoverage: 0
alphaTestReferenceValue: 0.5
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: 0.25
normalMapFilter: 0
isReadable: 0
streamingMipmaps: 0
streamingMipmapsPriority: 0
vTOnly: 0
ignoreMasterTextureLimit: 0
grayScaleToAlpha: 0
generateCubemap: 6
cubemapConvolution: 0
seamlessCubemap: 0
textureFormat: 1
maxTextureSize: 2048
textureSettings:
serializedVersion: 2
filterMode: 1
aniso: 1
mipBias: 0
wrapU: 0
wrapV: 0
wrapW: 0
nPOTScale: 0
lightmap: 0
compressionQuality: 50
spriteMode: 1
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: 0.5, y: 0.5}
spritePixelsToUnits: 100
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spriteGenerateFallbackPhysicsShape: 1
alphaUsage: 1
alphaIsTransparency: 0
spriteTessellationDetail: -1
textureType: 8
textureShape: 1
singleChannelComponent: 0
flipbookRows: 1
flipbookColumns: 1
maxTextureSizeSet: 0
compressionQualitySet: 0
textureFormatSet: 0
ignorePngGamma: 0
applyGammaDecoding: 0
cookieLightType: 0
platformSettings:
- serializedVersion: 3
buildTarget: DefaultTexturePlatform
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Standalone
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Server
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: WebGL
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites: []
outline: []
physicsShape: []
bones: []
spriteID: 5e97eb03825dee720800000000000000
internalID: 0
vertices: []
indices:
edges: []
weights: []
secondaryTextures: []
nameFileIdTable: {}
spritePackingTag:
pSDRemoveMatte: 0
pSDShowRemoveMatteOption: 0
userData:
assetBundleName:
assetBundleVariant:

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

View File

@ -0,0 +1,135 @@
fileFormatVersion: 2
guid: 2aa042885e232cc44a033f24979e8def
TextureImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 12
mipmaps:
mipMapMode: 0
enableMipMap: 1
sRGBTexture: 1
linearTexture: 0
fadeOut: 0
borderMipMap: 0
mipMapsPreserveCoverage: 0
alphaTestReferenceValue: 0.5
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: 0.25
normalMapFilter: 0
isReadable: 0
streamingMipmaps: 0
streamingMipmapsPriority: 0
vTOnly: 0
ignoreMasterTextureLimit: 0
grayScaleToAlpha: 0
generateCubemap: 6
cubemapConvolution: 0
seamlessCubemap: 0
textureFormat: 1
maxTextureSize: 2048
textureSettings:
serializedVersion: 2
filterMode: 1
aniso: 1
mipBias: 0
wrapU: 0
wrapV: 0
wrapW: 0
nPOTScale: 0
lightmap: 0
compressionQuality: 50
spriteMode: 1
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: 0.5, y: 0.5}
spritePixelsToUnits: 100
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spriteGenerateFallbackPhysicsShape: 1
alphaUsage: 1
alphaIsTransparency: 0
spriteTessellationDetail: -1
textureType: 8
textureShape: 1
singleChannelComponent: 0
flipbookRows: 1
flipbookColumns: 1
maxTextureSizeSet: 0
compressionQualitySet: 0
textureFormatSet: 0
ignorePngGamma: 0
applyGammaDecoding: 0
cookieLightType: 0
platformSettings:
- serializedVersion: 3
buildTarget: DefaultTexturePlatform
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Standalone
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Server
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: WebGL
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites: []
outline: []
physicsShape: []
bones: []
spriteID: 5e97eb03825dee720800000000000000
internalID: 0
vertices: []
indices:
edges: []
weights: []
secondaryTextures: []
nameFileIdTable: {}
spritePackingTag:
pSDRemoveMatte: 0
pSDShowRemoveMatteOption: 0
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: dbef35dc04f100549aa054b79a370e9a
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: c561c4b0d14ef6e44bce6495d361246e
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,15 @@
using System;
using System.Runtime.InteropServices;
using UnityEngine;
using Adobe.Substance;
namespace Adobe.SubstanceEditor
{
internal static class PhysicalSizeExtension
{
public static bool IsSupported()
{
return PluginPipelines.IsHDRP();
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 459462fea64803a4b8c2e0fe9979fba8
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,186 @@
using Adobe.Substance;
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using UnityEditor;
using UnityEngine;
namespace Adobe.SubstanceEditor
{
internal static class SubstanceGraphSOExtensions
{
/// <summary>
/// Updates SubstanceGraphSO related assets using the renderResult data.
/// </summary>
/// <param name="renderResult">Target render result.</param>
/// <param name="substance">Owner substance.</param>
/// <returns>Returns true if textures must be reassigned to the material.</returns>
internal static bool UpdateAssociatedAssets(this SubstanceGraphSO graph, IntPtr renderResult, bool forceRebuild)
{
if (!forceRebuild && CheckIfTextureAssetsExist(graph))
{
ResizeExistingTexturesIfNeeded(renderResult, graph);
graph.UpdateOutputTextures(renderResult);
return false;
}
graph.CreateAndUpdateOutputTextures(renderResult);
if (graph.IsRuntimeOnly)
return true;
graph.SaveOutputsTGAFiles();
AssetDatabase.Refresh();
return true;
}
/// <summary>
/// Updates TGA files using the currect values of the outputs Texture2D.
/// </summary>
/// <param name="graph"></param>
private static void SaveOutputsTGAFiles(this SubstanceGraphSO graph)
{
foreach (var substanceOutput in graph.Output)
{
var texture = substanceOutput.OutputTexture;
if (texture == null)
continue;
var textureOutput = graph.GetAssociatedAssetPath(substanceOutput.Description.Identifier, "tga");
var bytes = texture.EncodeToTGA();
File.WriteAllBytes(textureOutput, bytes);
}
AssetDatabase.Refresh();
graph.ConfigureTextureImporter();
}
/// <summary>
/// Properly configure TextureImporters settings based on output types.
/// </summary>
/// <param name="graph"></param>
private static void ConfigureTextureImporter(this SubstanceGraphSO graph)
{
foreach (var substanceOutput in graph.Output)
{
var texture = substanceOutput.OutputTexture;
if (texture == null)
continue;
var textureOutput = graph.GetAssociatedAssetPath(substanceOutput.Description.Identifier, "tga");
substanceOutput.OutputTexture = AssetDatabase.LoadAssetAtPath<Texture2D>(textureOutput);
ConfigureTextureImporter(substanceOutput);
}
}
/// <summary>
/// Resizes existing texture to match the expected values from renderResult.
/// </summary>
/// <param name="renderResult">Render result.</param>
/// <param name="graph">Target graph.</param>
private static void ResizeExistingTexturesIfNeeded(IntPtr renderResult, SubstanceGraphSO graph)
{
var renderResultsSizes = graph.GetResizedOutputs(renderResult);
//Resize existing output textures.
if (renderResultsSizes.Count != 0)
{
foreach (var resultSize in renderResultsSizes)
{
var outputIndex = resultSize.Item1;
var outputSize = resultSize.Item2;
var targetOutput = graph.Output.FirstOrDefault(a => a.Index == outputIndex);
#if UNITY_2021_2_OR_NEWER
targetOutput.OutputTexture.Reinitialize(outputSize.x, outputSize.y);
#else
targetOutput.OutputTexture.Resize(outputSize.x, outputSize.y);
#endif
if (!graph.IsRuntimeOnly)
{
var bytes = targetOutput.OutputTexture.EncodeToTGA();
var assetPath = AssetDatabase.GetAssetPath(targetOutput.OutputTexture);
File.WriteAllBytes(assetPath, bytes);
}
}
AssetDatabase.Refresh();
}
}
/// <summary>
/// Try to get the texture2D instances for a give graph.
/// </summary>
/// <param name="graph">Target graph.</param>
/// <param name="textures">Array of texture2D instances attached to each substance output.</param>
/// <returns>True if all textures instances exists. If false they must be rebuild.</returns>
private static bool CheckIfTextureAssetsExist(SubstanceGraphSO graph)
{
foreach (var output in graph.Output)
{
if (!output.IsStandardOutput(graph.OutputMaterial) && !graph.GenerateAllOutputs)
{
if (output.OutputTexture != null)
{
var assetPath = AssetDatabase.GetAssetPath(output.OutputTexture);
if (!string.IsNullOrEmpty(assetPath))
AssetDatabase.DeleteAsset(assetPath);
output.OutputTexture = null;
}
continue;
}
if (output.OutputTexture == null)
return false;
output.OutputTexture = TextureUtils.EnsureTextureCorrectness(output.OutputTexture, !output.IsNormalMap(), graph.GenerateAllMipmaps);
}
return true;
}
/// <summary>
/// Configures the texture importer settings to the associated texture output.
/// </summary>
/// <param name="textureOutput">Target output texture.</param>
private static void ConfigureTextureImporter(SubstanceOutputTexture textureOutput)
{
var texturePath = AssetDatabase.GetAssetPath(textureOutput.OutputTexture);
TextureImporter importer = AssetImporter.GetAtPath(texturePath) as TextureImporter;
if (importer == null)
return;
importer.textureCompression = TextureImporterCompression.Uncompressed;
importer.isReadable = true;
importer.maxTextureSize = 4096;
importer.sRGBTexture = textureOutput.sRGB;
if (textureOutput.IsNormalMap())
{
importer.textureType = TextureImporterType.NormalMap;
}
else
{
var defaultSettings = importer.GetDefaultPlatformTextureSettings();
if (defaultSettings.format != TextureImporterFormat.RGBA32)
{
defaultSettings.format = TextureImporterFormat.RGBA32;
importer.SetPlatformTextureSettings(defaultSettings);
}
}
EditorUtility.SetDirty(importer);
AssetDatabase.WriteImportSettingsIfDirty(texturePath);
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 36f38a90f11f0544a89dffe1f5752bae
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 82768c317d7769d4ba20e5a0daa7f62b
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,33 @@
using Adobe.Substance.Input.Description;
using System.Collections;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
namespace Adobe.SubstanceEditor
{
/// <summary>
/// Custome GUIContent class that provides extra information for drawing input parameters.
/// </summary>
internal class SubstanceInputGUIContent : GUIContent
{
/// <summary>
/// Description info for the input SerializedProperty.
/// </summary>
public SubstanceInputDescription Description;
public SerializedProperty DataProp;
public SubstanceInputGUIContent(SubstanceInputDescription description, SerializedProperty dataProp) : base(description.Label, description.Identifier)
{
Description = description;
DataProp = dataProp;
}
public SubstanceInputGUIContent(SubstanceInputDescription description, SerializedProperty dataProp, string text) : base(text)
{
Description = description;
DataProp = dataProp;
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 4796d6b0fe84aa64d9385f2fe71bb4bc
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,173 @@
using Adobe.Substance;
using Adobe.Substance.Input.Description;
using UnityEditor;
using UnityEngine;
namespace Adobe.SubstanceEditor
{
internal class SubstanceInt4GUIContent : SubstanceInputGUIContent
{
/// <summary>
/// Numerical input description for the target SerializedProperty.
/// </summary>
public SubstanceInputDescNumericalInt4 NumericalDescription;
public SubstanceInt4GUIContent(SubstanceInputDescription description, SerializedProperty dataProp, SubstanceInputDescNumericalInt4 numDescription) : base(description, dataProp)
{
NumericalDescription = numDescription;
}
public SubstanceInt4GUIContent(SubstanceInputDescription description, SerializedProperty dataProp, SubstanceInputDescNumericalInt4 numDescription, string text) : base(description, dataProp, text)
{
NumericalDescription = numDescription;
}
}
internal class SubstanceInt3GUIContent : SubstanceInputGUIContent
{
/// <summary>
/// Numerical input description for the target SerializedProperty.
/// </summary>
public SubstanceInputDescNumericalInt3 NumericalDescription;
public SubstanceInt3GUIContent(SubstanceInputDescription description, SerializedProperty dataProp, SubstanceInputDescNumericalInt3 numDescription) : base(description, dataProp)
{
NumericalDescription = numDescription;
}
public SubstanceInt3GUIContent(SubstanceInputDescription description, SerializedProperty dataProp, SubstanceInputDescNumericalInt3 numDescription, string text) : base(description, dataProp, text)
{
NumericalDescription = numDescription;
}
}
internal class SubstanceInt2GUIContent : SubstanceInputGUIContent
{
/// <summary>
/// Numerical input description for the target SerializedProperty.
/// </summary>
public SubstanceInputDescNumericalInt2 NumericalDescription;
public SubstanceInt2GUIContent(SubstanceInputDescription description, SerializedProperty dataProp, SubstanceInputDescNumericalInt2 numDescription) : base(description, dataProp)
{
NumericalDescription = numDescription;
}
public SubstanceInt2GUIContent(SubstanceInputDescription description, SerializedProperty dataProp, SubstanceInputDescNumericalInt2 numDescription, string text) : base(description, dataProp, text)
{
NumericalDescription = numDescription;
}
}
internal class SubstanceIntGUIContent : SubstanceInputGUIContent
{
/// <summary>
/// Numerical input description for the target SerializedProperty.
/// </summary>
public SubstanceInputDescNumericalInt NumericalDescription;
public SubstanceIntGUIContent(SubstanceInputDescription description, SerializedProperty dataProp, SubstanceInputDescNumericalInt numDescription) : base(description, dataProp)
{
NumericalDescription = numDescription;
}
public SubstanceIntGUIContent(SubstanceInputDescription description, SerializedProperty dataProp, SubstanceInputDescNumericalInt numDescription, string text) : base(description, dataProp, text)
{
NumericalDescription = numDescription;
}
}
internal class SubstanceFloat4GUIContent : SubstanceInputGUIContent
{
/// <summary>
/// Numerical input description for the target SerializedProperty.
/// </summary>
public SubstanceInputDescNumericalFloat4 NumericalDescription;
public SubstanceFloat4GUIContent(SubstanceInputDescription description, SerializedProperty dataProp, SubstanceInputDescNumericalFloat4 numDescription) : base(description, dataProp)
{
NumericalDescription = numDescription;
}
public SubstanceFloat4GUIContent(SubstanceInputDescription description, SerializedProperty dataProp, SubstanceInputDescNumericalFloat4 numDescription, string text) : base(description, dataProp, text)
{
NumericalDescription = numDescription;
}
}
internal class SubstanceFloat3GUIContent : SubstanceInputGUIContent
{
/// <summary>
/// Numerical input description for the target SerializedProperty.
/// </summary>
public SubstanceInputDescNumericalFloat3 NumericalDescription;
public SubstanceFloat3GUIContent(SubstanceInputDescription description, SerializedProperty dataProp, SubstanceInputDescNumericalFloat3 numDescription) : base(description, dataProp)
{
NumericalDescription = numDescription;
}
public SubstanceFloat3GUIContent(SubstanceInputDescription description, SerializedProperty dataProp, SubstanceInputDescNumericalFloat3 numDescription, string text) : base(description, dataProp, text)
{
NumericalDescription = numDescription;
}
}
internal class SubstanceFloat2GUIContent : SubstanceInputGUIContent
{
/// <summary>
/// Numerical input description for the target SerializedProperty.
/// </summary>
public SubstanceInputDescNumericalFloat2 NumericalDescription;
public SubstanceFloat2GUIContent(SubstanceInputDescription description, SerializedProperty dataProp, SubstanceInputDescNumericalFloat2 numDescription) : base(description, dataProp)
{
NumericalDescription = numDescription;
}
public SubstanceFloat2GUIContent(SubstanceInputDescription description, SerializedProperty dataProp, SubstanceInputDescNumericalFloat2 numDescription, string text) : base(description, dataProp, text)
{
NumericalDescription = numDescription;
}
}
internal class SubstanceFloatGUIContent : SubstanceInputGUIContent
{
/// <summary>
/// Numerical input description for the target SerializedProperty.
/// </summary>
public SubstanceInputDescNumericalFloat NumericalDescription;
public SubstanceFloatGUIContent(SubstanceInputDescription description, SerializedProperty dataProp, SubstanceInputDescNumericalFloat numDescription) : base(description, dataProp)
{
NumericalDescription = numDescription;
}
public SubstanceFloatGUIContent(SubstanceInputDescription description, SerializedProperty dataProp, SubstanceInputDescNumericalFloat numDescription, string text) : base(description, dataProp, text)
{
NumericalDescription = numDescription;
}
}
internal class SubstanceIntComboBoxGUIContent : SubstanceIntGUIContent
{
public GUIContent[] EnumValuesGUI { get; }
public int[] EnumValues { get; }
public SubstanceIntComboBoxGUIContent(SubstanceInputDescription description, SubstanceInputDescNumericalInt intDescription, SerializedProperty dataProp) : base(description, dataProp, intDescription)
{
var enumValues = intDescription.EnumValues;
EnumValuesGUI = new GUIContent[enumValues.Length];
EnumValues = new int[enumValues.Length];
for (int i = 0; i < EnumValuesGUI.Length; i++)
{
var enumElement = enumValues[i];
EnumValuesGUI[i] = new GUIContent(enumElement.Label);
EnumValues[i] = enumElement.Value;
}
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 3ee2316ba345eed46837d35e55cfa832
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 88b3a286e79783948aac3ed8572e25f1
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,200 @@
using System.Collections.Generic;
using System.IO;
using UnityEditor;
using UnityEngine;
using System.Threading.Tasks;
using Adobe.Substance;
#if UNITY_2020_2_OR_NEWER
using UnityEditor.AssetImporters;
#else
using UnityEditor.Experimental.AssetImporters;
#endif
namespace Adobe.SubstanceEditor.Importer
{
internal class SubstanceAssetModificationProcessor : UnityEditor.AssetModificationProcessor
{
public static AssetDeleteResult OnWillDeleteAsset(string assetPath, RemoveAssetOptions rao)
{
if (string.IsNullOrEmpty(assetPath))
return AssetDeleteResult.DidNotDelete;
if (AssetDatabase.IsValidFolder(assetPath))
return CanDeleteFolder(assetPath, rao) ? AssetDeleteResult.DidNotDelete : AssetDeleteResult.FailedDelete;
var substanceInstance = AssetDatabase.LoadAssetAtPath<SubstanceGraphSO>(assetPath);
if (substanceInstance != null)
{
if (substanceInstance.FlagedForDelete || !File.Exists(substanceInstance.AssetPath))
{
return AssetDeleteResult.DidNotDelete;
}
else
{
Debug.LogWarning($"The target file cannot be manually deleted because it is associated with {substanceInstance.AssetPath}. In order to delete it, first the .sbsar file must be deleted.");
return AssetDeleteResult.FailedDelete;
}
}
if (Path.GetExtension(assetPath.ToLower()) != ".sbsar")
return AssetDeleteResult.DidNotDelete;
SubstanceImporter importer = AssetImporter.GetAtPath(assetPath) as SubstanceImporter;
if (importer != null)
{
foreach (var materialInstance in importer._fileAsset.GetGraphs())
{
if (materialInstance == null)
continue;
SubstanceEditorEngine.instance.ReleaseInstance(materialInstance);
materialInstance.FlagedForDelete = true;
AssetDatabase.DeleteAsset(AssetDatabase.GetAssetPath(materialInstance));
}
}
return AssetDeleteResult.DidNotDelete;
}
public static AssetMoveResult OnWillMoveAsset(string from, string to)
{
if (string.IsNullOrEmpty(from))
return AssetMoveResult.DidNotMove;
AssetDatabase.Refresh();
if (Path.GetExtension(from.ToLower()) == ".asset")
{
var substanceInstance = AssetDatabase.LoadAssetAtPath<SubstanceGraphSO>(from);
if (substanceInstance != null)
{
substanceInstance.Move(to);
}
else
{
SubstanceEditorEngine.instance.PushMoveOperation(from, to);
}
return AssetMoveResult.DidNotMove;
}
if (Path.GetExtension(from.ToLower()) != ".sbsar")
return AssetMoveResult.DidNotMove;
var importer = AssetImporter.GetAtPath(from) as SubstanceImporter;
if (importer != null)
{
var so = new SerializedObject(importer);
var prop = so.FindProperty("_assetPath");
if (prop != null && !string.IsNullOrEmpty(prop.stringValue))
{
prop.stringValue = to;
so.ApplyModifiedPropertiesWithoutUndo();
}
}
AssetDatabase.Refresh(ImportAssetOptions.Default);
var fileObject = AssetDatabase.LoadAssetAtPath<SubstanceFileSO>(from);
foreach (var materialInstance in fileObject.GetGraphs())
materialInstance.AssetPath = to;
return AssetMoveResult.DidNotMove;
}
/// <summary>
/// Checks if the target folder has sbsar file generated assets that can be deleted or not.
/// </summary>
/// <param name="assetPath">Path to the target folder.</param>
/// <param name="rao">Remove asset options.</param>
/// <returns>True if the folder can be deleted.</returns>
private static bool CanDeleteFolder(string assetPath, RemoveAssetOptions rao)
{
var assetsGUIDs = AssetDatabase.FindAssets($"t:{nameof(SubstanceGraphSO)}", new[] { assetPath });
foreach (var guid in assetsGUIDs)
{
var targetPath = AssetDatabase.GUIDToAssetPath(guid);
var substanceInstance = AssetDatabase.LoadAssetAtPath<SubstanceGraphSO>(targetPath);
if (substanceInstance != null)
{
if (!substanceInstance.FlagedForDelete && File.Exists(substanceInstance.AssetPath))
{
Debug.LogWarning($"The target folder cannot be deleted manually because it has assets associated with {substanceInstance.AssetPath}. In order to delete it, first the .sbsar file must be deleted.");
return false;
}
}
}
return true;
}
}
/// <summary>
/// Importer for Substance Material Assets using the .sbsar extension .
/// </summary>
[ScriptedImporter(Adobe.Substance.Version.ImporterVersion, "sbsar")]
public sealed class SubstanceImporter : ScriptedImporter
{
[SerializeField]
public SubstanceFileSO _fileAsset;
public override void OnImportAsset(AssetImportContext ctx)
{
var rawData = ScriptableObject.CreateInstance<SubstanceFileRawData>();
rawData.FileContent = File.ReadAllBytes(ctx.assetPath);
rawData.hideFlags = HideFlags.HideInHierarchy | HideFlags.HideInInspector;
ctx.AddObjectToAsset("Substance Data", rawData);
_fileAsset = ScriptableObject.CreateInstance<SubstanceFileSO>();
CreateSubstanceFile(ctx, rawData);
ctx.AddObjectToAsset("Substance File", _fileAsset);
ctx.SetMainObject(_fileAsset);
}
public SubstanceGraphSO GetDefaultGraph()
{
var graphs = _fileAsset.GetGraphs();
if (graphs == null || graphs.Count == 0)
return null;
return graphs[0];
}
private void CreateSubstanceFile(AssetImportContext ctx, SubstanceFileRawData rawData)
{
var graphCount = Engine.GetFileGraphCount(rawData.FileContent);
var initInfos = new List<EditorTools.SubstanceInstanceInfo>();
for (int i = 0; i < graphCount; i++)
{
var guid = System.Guid.NewGuid().ToString();
initInfos.Add(new EditorTools.SubstanceInstanceInfo()
{
GUID = guid,
Index = i,
IsRoot = true,
Name = $"graph_{i}"
});
}
EditorTools.CreateSubstanceInstanceAsync(ctx.assetPath, rawData, initInfos);
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: dda12f74ff9116a42b23482252b2c482
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,643 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using UnityEditor;
using UnityEngine;
using System.Text.RegularExpressions;
using Adobe.Substance;
#if UNITY_2020_2_OR_NEWER
using UnityEditor.AssetImporters;
#else
using UnityEditor.Experimental.AssetImporters;
#endif
namespace Adobe.SubstanceEditor.Importer
{
[CustomEditor(typeof(SubstanceImporter)), CanEditMultipleObjects]
internal class SubstanceImporterEditor : ScriptedImporterEditor
{
protected override bool needsApplyRevert => false;
public override bool showImportedObject => false;
private int _selectedInstance = 0;
//Size of the preview thumbnail for instances in the list.
private const int _instanceListPreviewSize = 64;
private const int _isntanceListElementSpacing = 0;
private Vector2 _scrollPosition = Vector2.zero;
public SubstanceImporter _importer;
public List<SubstanceGraphSO> _graphs;
public string _tempLabelName;
private Dictionary<SubstanceGraphSO, SubstanceGraphSOEditor> _elementsEditors;
private Dictionary<SubstanceGraphSO, MaterialEditor> _previewEditors;
private SubstanceGraphSOEditor _currentEditor;
private Texture2D _backgroundImage = default;
private Texture2D _textHightlightBackground = default;
public override void OnEnable()
{
base.OnEnable();
_elementsEditors = new Dictionary<SubstanceGraphSO, SubstanceGraphSOEditor>();
_previewEditors = new Dictionary<SubstanceGraphSO, MaterialEditor>();
_importer = target as SubstanceImporter;
_graphs = _importer._fileAsset.GetGraphs();
EditorApplication.projectWindowItemOnGUI += OnHierarchyWindowItemOnGUI;
Undo.undoRedoPerformed += UndoCallback;
EnsureRefreshedMaterials();
EnsureRequiredTextures();
}
public override void OnDisable()
{
EditorApplication.projectWindowItemOnGUI -= OnHierarchyWindowItemOnGUI;
Undo.undoRedoPerformed -= UndoCallback;
SaveCurrentInsatance();
base.OnDisable();
}
private void SaveCurrentInsatance()
{
if (_currentEditor != null)
{
_currentEditor.SaveEditorChanges();
return;
}
if (_graphs == null || _graphs.Count <= _selectedInstance)
{
return;
}
var currentInstance = _graphs[_selectedInstance];
if (_elementsEditors.TryGetValue(currentInstance, out SubstanceGraphSOEditor editor))
editor.SaveEditorChanges();
}
private void UndoCallback()
{
if (Selection.activeObject is SubstanceFileSO)
{
var target = Selection.activeObject as SubstanceFileSO;
if (_importer._fileAsset == target)
{
var targetGraph = _graphs[_selectedInstance];
targetGraph.RenderTextures = true;
SubstanceEditorEngine.instance.PushAllInputsToUpdate();
EditorUtility.SetDirty(targetGraph);
}
}
}
protected static void OnHierarchyWindowItemOnGUI(string guid, Rect rt)
{
var currentEvent = Event.current;
if ("Duplicate" == currentEvent.commandName && currentEvent.type == EventType.ExecuteCommand)
{
var assetPath = AssetDatabase.GUIDToAssetPath(guid);
if (Path.GetExtension(assetPath) == ".sbsar")
{
Debug.LogWarning("Substance graph can not be manually duplicated.");
currentEvent.Use();
}
}
}
#region Draw Body
public override void OnInspectorGUI()
{
DrawMainUI();
}
private void DrawMainUI()
{
if (_graphs.Count == 0)
return;
EditorGUILayout.BeginVertical();
{
//Draw instances list.
EditorGUILayout.BeginVertical();
DrawInstancesListSection();
EditorGUILayout.EndVertical();
DrawUILine();
//Draw shader UI.
EditorGUILayout.BeginHorizontal();
DrawShaderSelectionSection();
EditorGUILayout.EndHorizontal();
DrawUILine();
//Draw selected instance properties.
EditorGUILayout.BeginVertical();
DrawSelectedInstanceProperties(_graphs[_selectedInstance]);
EditorGUILayout.EndVertical();
}
EditorGUILayout.EndVertical();
}
private void DrawInstancesListSection()
{
int numGraphs = _graphs.Count;
if (numGraphs == 0)
return;
//Instance UI width = preview texture size + padding.
float entryWidth = _instanceListPreviewSize + 16;
//Instance UI height = preview texture size + name text.
float entryHeight = _instanceListPreviewSize + EditorGUIUtility.singleLineHeight;
EditorGUILayout.LabelField("Substance Graphs");
EditorGUILayout.Space();
using (var scrollViewScope = new EditorGUILayout.ScrollViewScope(_scrollPosition, false, false))
{
scrollViewScope.handleScrollWheel = false;
_scrollPosition = scrollViewScope.scrollPosition;
var listStyle = new GUIStyle();
listStyle.padding = new RectOffset(15, 15, 15, 15);
var rect = EditorGUILayout.BeginHorizontal(listStyle);
{
//Gray area
var targetRect = new Rect(rect.x + 5, rect.y, rect.width - 10, rect.height);
DrawGrayRectangle(targetRect);
//Text styles
var normalTextStyle = new GUIStyle();
normalTextStyle.wordWrap = true;
normalTextStyle.alignment = TextAnchor.MiddleCenter;
if (EditorGUIUtility.isProSkin)
normalTextStyle.normal.textColor = new Color(0.8f, 0.8f, 0.8f, 1);
var highlightTextStyle = new GUIStyle();
highlightTextStyle.alignment = TextAnchor.MiddleCenter;
highlightTextStyle.normal.textColor = Color.white;
highlightTextStyle.normal.background = _textHightlightBackground;
highlightTextStyle.wordWrap = true;
for (int instanceIndex = 0; instanceIndex < numGraphs; instanceIndex++)
{
if (TryGetInstanceByIndex(instanceIndex, out SubstanceGraphSO instance))
{
EditorGUILayout.BeginVertical(GUILayout.Width(entryWidth), GUILayout.Height(entryHeight));
{
DrawListElement(instance, instanceIndex, entryWidth - 12, normalTextStyle, highlightTextStyle);
}
EditorGUILayout.EndVertical();
GUILayout.Space(_isntanceListElementSpacing);
}
}
}
EditorGUILayout.EndHorizontal();
}
EditorGUILayout.Space(5);
EditorGUILayout.BeginHorizontal();
DrawAddAndRemove();
EditorGUILayout.EndHorizontal();
}
private void DrawGrayRectangle(Rect rect)
{
var style = new GUIStyle();
style.normal.background = _backgroundImage;
GUI.Box(rect, GUIContent.none, style);
}
private void DrawListElement(SubstanceGraphSO instance, int instanceIndex, float entryWidth, GUIStyle normalStyle, GUIStyle highlightStyle)
{
Material graphMaterial = instance.OutputMaterial;
if (graphMaterial == null)
return;
Texture2D miniThumbnail = AssetPreview.GetAssetPreview(graphMaterial);
if (GUILayout.Button(miniThumbnail, GUILayout.Width(_instanceListPreviewSize),
GUILayout.Height(_instanceListPreviewSize)))
{
OnInstanceSelected(instanceIndex);
return;
}
if (instanceIndex != _selectedInstance)
{
GUILayout.Label(instance.Name, normalStyle, GUILayout.Width(entryWidth));
return;
}
var label = _tempLabelName ?? instance.Name;
_tempLabelName = GUILayout.TextField(label, highlightStyle, GUILayout.Width(entryWidth));
if (!_tempLabelName.Equals(instance.Name, StringComparison.Ordinal))
{
Event e = Event.current;
if (e.keyCode == KeyCode.Return)
{
if (e.type == EventType.KeyUp)
{
if (!TryRenameInstance(instance, _tempLabelName))
{
EditorUtility.DisplayDialog("Error", "The provided name can't be assigned to a substance instance.", "Ok");
_tempLabelName = instance.Name;
}
}
}
}
}
private void DrawAddAndRemove()
{
if (GUILayout.Button("Copy graph"))
{
CreateNewInstance();
GUIUtility.ExitGUI();
}
if (GUILayout.Button("Delete graph"))
{
if (TryGetSelectedInstance(out SubstanceGraphSO instanceSO))
{
DeleteInstance(instanceSO);
GUIUtility.ExitGUI();
}
}
}
private void DrawShaderSelectionSection()
{
EditorGUILayout.LabelField("Shader", GUILayout.Width(55));
if (!TryGetCurrentGraph(out SubstanceGraphSO graph))
return;
if (graph.OutputMaterial == null || graph.OutputMaterial.shader == null)
return;
var currentShader = graph.OutputMaterial.shader;
var shaderNames = ShaderUtil.GetAllShaderInfo().Select((a) => a.name).Where(b => !b.StartsWith("Hidden") && !b.StartsWith("GUI")).ToArray();
var selectedElement = shaderNames.FirstOrDefault(a => a == currentShader.name);
var selectedIndex = Array.IndexOf(shaderNames, selectedElement);
var newSelected = EditorGUILayout.Popup(selectedIndex, shaderNames, GUILayout.MaxWidth(320));
if (newSelected != selectedIndex)
{
var newSelectedElement = shaderNames[newSelected];
_graphs[_selectedInstance].OutputMaterial.shader = Shader.Find(newSelectedElement);
EditorUtility.SetDirty(_graphs[_selectedInstance].OutputMaterial);
AssetDatabase.Refresh();
}
if (GUILayout.Button("Edit", GUILayout.MaxWidth(60)))
{
EditorUtility.FocusProjectWindow();
Selection.activeObject = currentShader;
}
}
private void DrawSelectedInstanceProperties(SubstanceGraphSO currentInstance)
{
if (!_elementsEditors.TryGetValue(currentInstance, out SubstanceGraphSOEditor editor))
{
editor = SubstanceGraphSOEditor.CreateEditor(currentInstance) as SubstanceGraphSOEditor;
_elementsEditors.Add(currentInstance, editor);
}
if (_currentEditor != null && _currentEditor != editor)
{
_currentEditor.SaveEditorChanges();
}
_currentEditor = editor;
editor.OnInspectorGUI();
}
#endregion Draw Body
#region Static Preview
public override Texture2D RenderStaticPreview(string assetPath, UnityEngine.Object[] subAssets, int width, int height)
{
if (_importer == null)
return null;
if (_graphs[0] == null)
return null;
if (_graphs[0].HasThumbnail)
{
return _graphs[0].GetThumbnailTexture();
}
else
{
var icon = UnityPackageInfo.GetSubstanceIcon(width, height);
if (icon != null)
{
Texture2D tex = new Texture2D(width, height);
EditorUtility.CopySerialized(icon, tex);
return tex;
}
}
return base.RenderStaticPreview(assetPath, subAssets, width, height);
}
#endregion Static Preview
#region Preview
public override bool HasPreviewGUI()
{
return true;
}
public override GUIContent GetPreviewTitle()
{
if (!TryGetCurrentGraph(out SubstanceGraphSO graph))
return new GUIContent("Material", null, "");
if (graph.OutputMaterial == null)
return new GUIContent("Material", null, "");
if (string.IsNullOrEmpty(graph.OutputMaterial.name))
return new GUIContent("Material", null, "");
return new GUIContent(graph.OutputMaterial.name, null, "");
}
public override void OnPreviewSettings()
{
if (_graphs == null || _graphs.Count == 0)
{
Debug.LogWarning("No graphs found. Please make sure to not rename folders with that are managed by the Substance Plugin.");
return;
}
var selectedInstance = _graphs[_selectedInstance];
if (selectedInstance == null)
return;
if (!_previewEditors.TryGetValue(selectedInstance, out MaterialEditor editor))
{
var material = selectedInstance?.OutputMaterial;
if (material != null)
{
editor = MaterialEditor.CreateEditor(material) as MaterialEditor;
_previewEditors.Add(selectedInstance, editor);
}
}
if (editor != null)
editor.OnPreviewSettings();
}
public override void OnPreviewGUI(Rect r, GUIStyle background)
{
if (_graphs == null || _graphs.Count == 0)
{
Debug.LogWarning("No graphs found. Please make sure to not rename folders with that are managed by the Substance Plugin.");
return;
}
var selectedInstance = _graphs[_selectedInstance];
if (selectedInstance == null)
return;
if (!_previewEditors.TryGetValue(selectedInstance, out MaterialEditor editor))
{
var material = selectedInstance?.OutputMaterial;
if (material != null)
{
editor = MaterialEditor.CreateEditor(material) as MaterialEditor;
_previewEditors.Add(selectedInstance, editor);
}
}
if (editor != null)
editor.OnPreviewGUI(r, background);
}
public override void DrawPreview(Rect previewArea)
{
OnPreviewGUI(previewArea, new GUIStyle());
}
#endregion Preview
/// <summary>
/// Creates a new instance of a SubstanceGraphSO with a copy of the values from the current selected instance.
/// </summary>
/// <param name="name">Name for the new instance.</param>
/// <param name="currentInstance">Current selected instance.</param>
private void CreateNewInstance()
{
if (!TryGetSelectedInstance(out SubstanceGraphSO rootInstance))
{
if (!TryGetInstanceByIndex(0, out rootInstance))
{
return;
}
}
var newInstanceName = GenerateNewInstanceName(rootInstance);
var instanceAsset = EditorTools.CreateSubstanceInstance(_importer.assetPath, rootInstance.RawData, newInstanceName, rootInstance.GetNativeID(), System.Guid.NewGuid().ToString(), copy: rootInstance);
SubstanceEditorEngine.instance.RenderInstanceAsync(instanceAsset);
_graphs.Add(instanceAsset);
EditorUtility.SetDirty(_importer);
AssetDatabase.Refresh();
_selectedInstance = _graphs.Count - 1;
ResetTempName();
}
private void DeleteInstance(SubstanceGraphSO instance)
{
if (instance.IsRoot)
{
EditorUtility.DisplayDialog("Invalid operation", "Can't delete root instance.", "OK");
return;
}
_graphs.Remove(instance);
EditorUtility.SetDirty(_importer);
SubstanceEditorEngine.instance.ReleaseInstance(instance);
instance.FlagedForDelete = true;
EditorUtility.SetDirty(instance);
var assetPath = AssetDatabase.GetAssetPath(instance);
AssetDatabase.DeleteAsset(assetPath);
AssetDatabase.Refresh();
_selectedInstance = 0;
ResetTempName();
}
private void OnInstanceSelected(int instanceIndex)
{
_selectedInstance = instanceIndex;
if (TryGetSelectedInstance(out SubstanceGraphSO target))
EditorGUIUtility.PingObject(target);
ResetTempName();
GUIUtility.ExitGUI();
}
private bool TryRenameInstance(SubstanceGraphSO instanceSO, string name)
{
if (!IsValidAndNoConflict(name))
return false;
instanceSO.Rename(name);
return true;
}
#region Utilities
private string GenerateNewInstanceName(SubstanceGraphSO currentInstance)
{
var index = _graphs.Count;
var newName = currentInstance.Name + $"_copy";
while (!IsValidAndNoConflict(newName))
newName = currentInstance.Name + $"__copy{index++}";
return newName;
}
private bool IsValidAndNoConflict(string name)
{
if (!IsValidName(name))
return false;
if (_graphs.Where(a => a != null).FirstOrDefault(a => a.Name == name) != null)
return false;
return true;
}
private bool IsValidName(string name)
{
if (string.IsNullOrEmpty(name) || string.IsNullOrWhiteSpace(name))
return false;
Regex containsABadCharacter = new Regex("[" + Regex.Escape(new string(Path.GetInvalidFileNameChars())) + "]");
if (containsABadCharacter.IsMatch(name))
return false;
return true;
}
private bool TryGetCurrentGraph(out SubstanceGraphSO graph)
{
graph = null;
if (!TryGetSelectedInstance(out graph))
return false;
return true;
}
private bool TryGetInstanceByIndex(int index, out SubstanceGraphSO instance)
{
instance = null;
if (_importer == null || _graphs == null || _graphs.Count == 0)
return false;
if (_graphs.Count <= index)
return false;
instance = _graphs[index];
return instance != null;
}
private bool TryGetSelectedInstance(out SubstanceGraphSO instance)
{
return TryGetInstanceByIndex(_selectedInstance, out instance);
}
private static void DrawUILine()
{
var rect = EditorGUILayout.BeginVertical();
{
Handles.color = Color.black;
EditorGUILayout.Space(15);
Handles.DrawLine(new Vector2(rect.x - 40, rect.y + (rect.height / 2)), new Vector2(rect.width + 20, rect.y + (rect.height / 2)));
EditorGUILayout.Space(15);
}
EditorGUILayout.EndVertical();
}
private void ResetTempName()
{
if (TryGetSelectedInstance(out SubstanceGraphSO material))
_tempLabelName = material.Name;
}
private void EnsureRequiredTextures()
{
float c = (EditorGUIUtility.isProSkin) ? 0.35f : 0.65f;
_backgroundImage = Globals.CreateColoredTexture(64, 64, new Color(c, c, c, 1));
_textHightlightBackground = Globals.CreateColoredTexture(_instanceListPreviewSize, _instanceListPreviewSize, Color.gray);
}
private void EnsureRefreshedMaterials()
{
foreach (var instance in _graphs)
{
if (instance == null)
return;
var material = instance.OutputMaterial;
if (material == null)
continue;
EditorUtility.SetDirty(material);
}
AssetDatabase.Refresh();
}
#endregion Utilities
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 87dac30333bc79345bec7a3b39edcc45
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: c179733d89e9d09458a2e90cf842d409
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,115 @@
using Adobe.Substance;
using System.Linq;
using UnityEditor;
using UnityEngine;
namespace Adobe.SubstanceEditor
{
internal static class UnityPackageInfo
{
public const string PackageName = "com.adobe.substanceforunity";
private static UnityEditor.PackageManager.PackageInfo _cachedPackageInfo = null;
private static string IconPath_256 => $"{PathUtils.SubstanceRootPath}/Editor/Assets/Logo_256.png";
private static string IconPath_128 => $"{PathUtils.SubstanceRootPath}/Editor/Assets/Logo_128.png";
private static string IconPath_64 = $"{PathUtils.SubstanceRootPath}/Editor/Assets/Logo_64.png";
private static string IconPath_32 = $"{PathUtils.SubstanceRootPath}/Editor/Assets/Logo_32.png";
private static Texture2D _cachedIcon256 = null;
private static Texture2D _cachedIcon128 = null;
private static Texture2D _cachedIcon64 = null;
private static Texture2D _cachedIcon32 = null;
public static UnityEditor.PackageManager.PackageInfo GetPackageInfo()
{
#pragma warning disable 162
if (!PathUtils.UsingPackageManager)
return null;
#pragma warning restore 162
if (_cachedPackageInfo != null)
return _cachedPackageInfo;
var packageInfoRequest = UnityEditor.PackageManager.Client.List(true);
while (packageInfoRequest.Status == UnityEditor.PackageManager.StatusCode.InProgress)
continue;
var packages = packageInfoRequest.Result?.FirstOrDefault(a => a.name == PackageName);
if (packages != null)
_cachedPackageInfo = packages;
return _cachedPackageInfo;
}
public static Texture2D GetSubstanceIcon(int width, int height)
{
if (width != height)
return GetSubstanceIconResized(width, height);
switch (width)
{
case 256:
return GetSubstanceIcon256();
case 128:
return GetSubstanceIcon128();
case 64:
return GetSubstanceIcon64();
case 32:
return GetSubstanceIcon32();
default:
return GetSubstanceIconResized(width, height);
}
}
public static Texture2D GetSubstanceIconResized(int width, int height)
{
var icon = GetSubstanceIcon256();
Texture2D tex = new Texture2D(width, height);
EditorUtility.CopySerialized(icon, tex);
return tex;
}
public static Texture2D GetSubstanceIcon256()
{
if (_cachedIcon256 != null)
return _cachedIcon256;
_cachedIcon256 = AssetDatabase.LoadAssetAtPath<Texture2D>(IconPath_256);
return _cachedIcon256;
}
public static Texture2D GetSubstanceIcon128()
{
if (_cachedIcon128 != null)
return _cachedIcon128;
_cachedIcon128 = AssetDatabase.LoadAssetAtPath<Texture2D>(IconPath_128);
return _cachedIcon128;
}
public static Texture2D GetSubstanceIcon64()
{
if (_cachedIcon64 != null)
return _cachedIcon64;
_cachedIcon64 = AssetDatabase.LoadAssetAtPath<Texture2D>(IconPath_64);
return _cachedIcon64;
}
public static Texture2D GetSubstanceIcon32()
{
if (_cachedIcon32 != null)
return _cachedIcon32;
_cachedIcon32 = AssetDatabase.LoadAssetAtPath<Texture2D>(IconPath_32);
return _cachedIcon32;
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 72cba7e39e029164f841301c6320fd30
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 741dbc64474bb4d4683c75c99534538a
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,29 @@
using Adobe.Substance;
using UnityEditor;
using UnityEngine;
namespace Adobe.SubstanceEditor.ProjectSettings
{
internal static class Extensions
{
public static void DrawAboutWindow()
{
string aboutMessage = "Plugin Info:\n";
aboutMessage += "Package Name: Substance 3D for Unity" + "\n";
aboutMessage += "Package Version: " + Version.PluginVersion + "\n";
aboutMessage += "Engine Version: " + Version.EngineVersion + "\n";
bool state = EditorUtility.DisplayDialog("About Substance 3D", aboutMessage, "Copy to clipboard", "Close");
if (state == true)
{
// Copy to clipboard:
TextEditor _textEditor = new TextEditor();
_textEditor.text = aboutMessage;
_textEditor.OnFocus();
_textEditor.Copy();
DrawAboutWindow();
}
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: bd5f8823e57c3e04fbc09267e76055db
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,191 @@
using Adobe.Substance;
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using UnityEditor;
using UnityEngine;
using UnityEngine.UIElements;
namespace Adobe.SubstanceEditor.ProjectSettings
{
/// <summary>
/// Settings provider for the Adobe Substance tab in the Unity project settings UI.
/// </summary>
internal class SubstanceEditorSettingsProvider : SettingsProvider
{
private const string substanceURL = "https://substance3d.adobe.com/assets/";
private SerializedObject _editorSettings;
private SerializedProperty _generateAllTextureProp;
private SerializedProperty _targetResolutionProp;
private static string SubstanceAssetLogoPath => $"{PathUtils.SubstanceRootPath}/Editor/Assets/S_3DHeart_18_N_nudged.png";
private static string SubstanceCommunityAssetLogoPath => $"{PathUtils.SubstanceRootPath}/Editor/Assets/S_3DCummunityAssets_18_N.png";
private const string SubstanceCommunityURL = "https://substance3d.adobe.com/community-assets";
private const string SubstanceAssetURL = "https://substance3d.adobe.com/assets";
private class Contents
{
public static readonly GUIContent GenerateAllTexturesText = new GUIContent("Generate all outputs", "Generate all output textures for the substance graphs.");
public static readonly GUIContent TextureResoltuionText = new GUIContent("Texture resolution", "Texture resolution for all graphs outputs.");
public static readonly GUIContent SubstanceAssetsIcon = new GUIContent();
public static readonly GUIContent SubstanceCommunityIcon = new GUIContent();
}
private class Styles
{
public static GUIStyle AssetButtonsStyle;
public static readonly GUIStyle SubstanceAssetButtonsPanelStyle = new GUIStyle();
public static readonly GUIStyle RichTextStyle = new GUIStyle() { richText = true };
}
public SubstanceEditorSettingsProvider(string path, SettingsScope scope = SettingsScope.User) : base(path, scope)
{
}
public override void OnActivate(string searchContext, VisualElement rootElement)
{
_editorSettings = SubstanceEditorSettingsSO.GetSerializedSettings();
_generateAllTextureProp = _editorSettings.FindProperty("_generateAllTexture");
_targetResolutionProp = _editorSettings.FindProperty("_targetResolution");
Contents.SubstanceAssetsIcon.image = AssetDatabase.LoadAssetAtPath<Texture2D>(SubstanceAssetLogoPath);
Contents.SubstanceAssetsIcon.tooltip = SubstanceAssetURL;
Contents.SubstanceCommunityIcon.image = AssetDatabase.LoadAssetAtPath<Texture2D>(SubstanceCommunityAssetLogoPath);
Contents.SubstanceCommunityIcon.tooltip = SubstanceCommunityURL;
}
public override void OnGUI(string searchContext)
{
_editorSettings.Update();
if (Styles.AssetButtonsStyle == null)
{
Styles.AssetButtonsStyle = new GUIStyle(GUI.skin.label);
Styles.AssetButtonsStyle.fixedHeight = 24;
Styles.AssetButtonsStyle.fixedWidth = 24;
}
EditorGUILayout.Space();
EditorGUI.indentLevel++;
{
if (_generateAllTextureProp != null)
{
EditorGUILayout.Space();
EditorGUILayout.PropertyField(_generateAllTextureProp, Contents.GenerateAllTexturesText, GUILayout.Width(100));
}
if (_targetResolutionProp != null)
{
EditorGUILayout.Space();
EditorDrawUtilities.DrawResolutionSelection(_targetResolutionProp, Contents.TextureResoltuionText);
}
DrawEngineInfo();
EditorGUILayout.Space();
DrawTextLinksAndAbout();
//DrawAboutText();
}
EditorGUI.indentLevel--;
_editorSettings.ApplyModifiedProperties();
}
private void DrawEngineInfo()
{
string label = PlatformUtils.IsCPU() ? "CPU" : "GPU";
var content = new GUIContent($"Computing textures with {label}", "Engine used for rendering textures");
EditorGUILayout.Space();
EditorGUILayout.BeginHorizontal();
EditorGUILayout.LabelField(content);
EditorGUILayout.EndHorizontal();
}
/// <summary>
/// Draws a hyperlink label that should redirect the user to a URL.
/// </summary>
/// <param name="text">Label text.</param>
/// <param name="url">Redirect URL.</param>
private void DrawClickableText(string text, GUIStyle style, Action callback)
{
var labelRect = EditorGUILayout.GetControlRect();
if (Event.current.type == EventType.MouseUp && labelRect.Contains(Event.current.mousePosition))
callback();
GUI.Label(labelRect, text, style);
}
private void DrawTextLinksAndAbout()
{
var textStyle = new GUIStyle();
textStyle.normal.textColor = new Color(75f / 255f, 122f / 255f, 243f / 255f);
textStyle.alignment = TextAnchor.LowerLeft;
textStyle.fixedWidth = 150;
EditorGUILayout.Space();
GUILayout.BeginHorizontal();
{
EditorGUILayout.Space(10, false);
if (GUILayout.Button(Contents.SubstanceAssetsIcon, Styles.AssetButtonsStyle))
Application.OpenURL(SubstanceAssetURL);
DrawClickableText("Substance 3D assets", textStyle, () => Application.OpenURL(SubstanceAssetURL));
}
GUILayout.EndHorizontal();
EditorGUILayout.Space();
GUILayout.BeginHorizontal();
{
EditorGUILayout.Space(10, false);
if (GUILayout.Button(Contents.SubstanceCommunityIcon, Styles.AssetButtonsStyle))
Application.OpenURL(SubstanceCommunityURL);
DrawClickableText("Substance 3D community assets", textStyle, () => Application.OpenURL(SubstanceCommunityURL));
}
GUILayout.EndHorizontal();
EditorGUILayout.Space();
GUILayout.BeginHorizontal();
{
EditorGUILayout.Space(40, false);
DrawClickableText("About", textStyle, () => Extensions.DrawAboutWindow());
}
GUILayout.EndHorizontal();
}
#region Registration
[SettingsProvider]
public static SettingsProvider CreateSubstanceSettingsProvider()
{
if (SubstanceEditorSettingsSO.IsSettingsAvailable())
{
return new SubstanceEditorSettingsProvider("Project/Adobe Substance 3D", SettingsScope.Project)
{
label = "Adobe Substance 3D",
keywords = GetSearchKeywordsFromGUIContentProperties<Contents>()
};
}
return null;
}
#endregion Registration
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 4e36266595fde1a47b3add3f8a295cfe
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,59 @@
using Adobe.Substance;
using System.IO;
using UnityEditor;
using UnityEngine;
namespace Adobe.SubstanceEditor.ProjectSettings
{
/// <summary>
/// Global editor settings scriptable object
/// </summary>
internal class SubstanceEditorSettingsSO : ScriptableObject
{
private static string _editorSettingsAsset => $"{PathUtils.SubstanceRootPath}/Editor/Settings/SubstanceEditorSettings.asset";
[SerializeField]
private bool _generateAllTexture;
[SerializeField]
private Vector2Int _targetResolution;
internal static SubstanceEditorSettingsSO GetOrCreateSettings()
{
var settings = AssetDatabase.LoadAssetAtPath<SubstanceEditorSettingsSO>(_editorSettingsAsset);
if (settings == null)
{
settings = ScriptableObject.CreateInstance<SubstanceEditorSettingsSO>();
settings._generateAllTexture = false;
settings._targetResolution = new Vector2Int(9, 9);
AssetDatabase.CreateAsset(settings, _editorSettingsAsset);
AssetDatabase.SaveAssets();
}
return settings;
}
public static Vector2Int TextureOutputResultion()
{
var settigns = GetOrCreateSettings();
return settigns._targetResolution;
}
public static bool GenerateAllTextures()
{
var settigns = GetOrCreateSettings();
return settigns._generateAllTexture;
}
public static bool IsSettingsAvailable()
{
return File.Exists(_editorSettingsAsset);
}
internal static SerializedObject GetSerializedSettings()
{
return new SerializedObject(GetOrCreateSettings());
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 715e7c131b0ebdd48b77c2b2741d1dae
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 846e54def7465614e9af6ed817c28f6a
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,61 @@
using Adobe.Substance;
using UnityEditor;
namespace Adobe.SubstanceEditor
{
internal static class SubstanceInputDrawer
{
public static bool DrawInput(SerializedProperty property, SubstanceInputGUIContent content, SubstanceNativeGraph handler, int inputID)
{
switch (content.Description.Type)
{
case SubstanceValueType.Float:
return SubstanceInputDrawerFloat.DrawInput(content.DataProp, content, handler, inputID);
case SubstanceValueType.Float2:
return SubstanceInputDrawerFloat2.DrawInput(content.DataProp, content, handler, inputID);
case SubstanceValueType.Float3:
return SubstanceInputDrawerFloat3.DrawInput(content.DataProp, content, handler, inputID);
case SubstanceValueType.Float4:
return SubstanceInputDrawerFloat4.DrawInput(content.DataProp, content, handler, inputID);
case SubstanceValueType.Int:
return SubstanceInputDrawerInt.DrawInput(content.DataProp, content, handler, inputID);
case SubstanceValueType.Int2:
return SubstanceInputDrawerInt2.DrawInput(content.DataProp, content, handler, inputID);
case SubstanceValueType.Int3:
return SubstanceInputDrawerInt3.DrawInput(content.DataProp, content, handler, inputID); ;
case SubstanceValueType.Int4:
return SubstanceInputDrawerInt4.DrawInput(property, content, handler, inputID);
case SubstanceValueType.Image:
return SubstanceInputDrawerTexture.DrawInput(content.DataProp, content, handler, inputID);
case SubstanceValueType.String:
return SubstanceInputDrawerString.DrawInput(content.DataProp, content, handler, inputID);
default:
return DrawDefault(property, content);
}
}
private static bool DrawDefault(SerializedProperty valueProperty, SubstanceInputGUIContent content)
{
if (content.Description.WidgetType == SubstanceWidgetType.NoWidget)
{
EditorGUILayout.LabelField($"Hidden property.");
return false;
}
EditorGUI.indentLevel++;
EditorGUILayout.LabelField($"Not supported. Value with widget {content.Description.WidgetType}");
EditorGUI.indentLevel--;
return false;
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 291115c592a8ea648812908d840ceb8d
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,67 @@
using UnityEditor;
using UnityEngine;
using Adobe.Substance.Input.Description;
using Adobe.Substance;
namespace Adobe.SubstanceEditor
{
internal static class SubstanceInputDrawerFloat
{
public static bool DrawInput(SerializedProperty valueProperty, SubstanceInputGUIContent content, SubstanceNativeGraph handler, int inputID)
{
float newValue;
bool changed = false;
switch (content.Description.WidgetType)
{
case SubstanceWidgetType.Slider:
changed = DrawSlider(valueProperty, content as SubstanceFloatGUIContent, out newValue);
break;
default:
changed = DrawDefault(valueProperty, content, out newValue);
break;
}
if (changed)
handler.SetInputFloat(inputID, newValue);
return changed;
}
private static bool DrawSlider(SerializedProperty valueProperty, SubstanceFloatGUIContent content, out float newValue)
{
var floatInputDesc = content.NumericalDescription;
var maxValue = floatInputDesc.MaxValue;
var minValue = floatInputDesc.MinValue;
var sliderClamp = maxValue != minValue;
var oldValue = valueProperty.floatValue;
newValue = EditorGUILayout.Slider(content, oldValue, sliderClamp ? minValue : 0, sliderClamp ? maxValue : 50);
if (oldValue != newValue)
{
valueProperty.floatValue = newValue;
return true;
}
return false;
}
private static bool DrawDefault(SerializedProperty valueProperty, SubstanceInputGUIContent content, out float newValue)
{
var oldValue = valueProperty.floatValue;
newValue = EditorGUILayout.FloatField(content, oldValue);
if (oldValue != newValue)
{
valueProperty.floatValue = newValue;
return true;
}
return false;
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 033dbc14304ac024da91e3803dfab82f
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,43 @@
using UnityEditor;
using UnityEngine;
using Adobe.Substance;
namespace Adobe.SubstanceEditor
{
internal static class SubstanceInputDrawerFloat2
{
public static bool DrawInput(SerializedProperty valueProperty, SubstanceInputGUIContent content, SubstanceNativeGraph handler, int inputID)
{
Vector2 newValue;
bool changed;
switch (content.Description.WidgetType)
{
default:
changed = DrawDefault(valueProperty, content, out newValue);
break;
}
if (changed)
handler.SetInputFloat2(inputID, newValue);
return changed;
}
private static bool DrawDefault(SerializedProperty valueProperty, SubstanceInputGUIContent content, out Vector2 newValue)
{
bool result = false;
var previewValue = valueProperty.vector2Value;
newValue = EditorGUILayout.Vector2Field(content, previewValue);
if (newValue != previewValue)
{
valueProperty.vector2Value = newValue;
result = true;
}
return result;
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: a184eff873fa9f04a9cb3c6e7a45656c
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,70 @@
using UnityEditor;
using UnityEngine;
using Adobe.Substance;
namespace Adobe.SubstanceEditor
{
internal static class SubstanceInputDrawerFloat3
{
public static bool DrawInput(SerializedProperty valueProperty, SubstanceInputGUIContent content, SubstanceNativeGraph handler, int inputID)
{
Vector3 newValue;
bool changed;
switch (content.Description.WidgetType)
{
case SubstanceWidgetType.Color:
changed = DrawColorPicker(valueProperty, content, out newValue);
break;
default:
changed = DrawDefault(valueProperty, content, out newValue);
break;
}
if (changed)
handler.SetInputFloat3(inputID, newValue);
return changed;
}
/// <summary>
/// Renders custome GUI for input float3 as color.
/// </summary>
/// <param name="position">GUI position rect.</param>
/// <param name="valueProperty">Value property.</param>
/// <param name="content">GUI content.</param>
/// <param name="description">Description for the target input.</param>
private static bool DrawColorPicker(SerializedProperty valueProperty, SubstanceInputGUIContent content, out Vector3 newValue)
{
var previewValue = valueProperty.vector3Value;
var color = new Color(previewValue.x, previewValue.y, previewValue.z, 1);
var newColor = EditorGUILayout.ColorField(content, color, false, false, false);
newValue = new Vector3();
if (color != newColor)
{
newValue = new Vector4(newColor.r, newColor.g, newColor.b, newColor.a);
valueProperty.vector3Value = newValue;
return true;
}
return false;
}
private static bool DrawDefault(SerializedProperty valueProperty, SubstanceInputGUIContent content, out Vector3 newValue)
{
var previewValue = valueProperty.vector3Value;
newValue = EditorGUILayout.Vector3Field(content, previewValue);
if (newValue != previewValue)
{
valueProperty.vector3Value = newValue;
return true;
}
return false;
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: dd7292cd00c740040b07a24e3f49ac8f
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,68 @@
using UnityEditor;
using UnityEngine;
using Adobe.Substance;
namespace Adobe.SubstanceEditor
{
internal static class SubstanceInputDrawerFloat4
{
public static bool DrawInput(SerializedProperty valueProperty, SubstanceInputGUIContent content, SubstanceNativeGraph handler, int inputID)
{
Vector4 newValyue;
bool changed;
switch (content.Description.WidgetType)
{
case SubstanceWidgetType.Color:
changed = DrawColorPicker(valueProperty, content, out newValyue);
break;
default:
changed = DrawDefault(valueProperty, content, out newValyue);
break;
}
if (changed)
handler.SetInputFloat4(inputID, newValyue);
return changed;
}
/// <summary>
/// Renders custome GUI for input float4 as color.
/// </summary>
/// <param name="position">GUI position rect.</param>
/// <param name="valueProperty">Value property.</param>
/// <param name="content">GUI content.</param>
/// <param name="description">Description for the target input.</param>
private static bool DrawColorPicker(SerializedProperty valueProperty, SubstanceInputGUIContent content, out Vector4 newValue)
{
var previewValue = valueProperty.vector4Value;
var color = new Vector4(previewValue.x, previewValue.y, previewValue.z, previewValue.w);
newValue = EditorGUILayout.ColorField(content, color);
if (color != newValue)
{
valueProperty.vector4Value = new Vector4(newValue.x, newValue.y, newValue.z, newValue.w);
return true;
}
return false;
}
private static bool DrawDefault(SerializedProperty valueProperty, SubstanceInputGUIContent content, out Vector4 newValue)
{
var oldValue = valueProperty.vector4Value;
newValue = EditorGUILayout.Vector4Field(content, oldValue);
if (oldValue != newValue)
{
valueProperty.vector4Value = newValue;
return true;
}
return false;
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: b864e9955ea52c1439bd7a5e691c3e75
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,194 @@
using UnityEditor;
using UnityEngine;
using Adobe.Substance.Input.Description;
using Adobe.Substance;
namespace Adobe.SubstanceEditor
{
/// <summary>
/// Drawer helper for int substance inputs.
/// </summary>
internal static class SubstanceInputDrawerInt
{
/// <summary>
/// Draws int input.
/// </summary>
/// <param name="valueProperty"></param>
/// <param name="content"></param>
/// <returns>True if value changed.</returns>
public static bool DrawInput(SerializedProperty valueProperty, SubstanceInputGUIContent content, SubstanceNativeGraph handler, int inputID)
{
int value;
bool changed;
if (content.Description.Label == "$randomseed")
{
changed = DrawRandomSeedButton(valueProperty, content, out value);
}
else
{
switch (content.Description.WidgetType)
{
case SubstanceWidgetType.ToggleButton:
changed = DrawToggleButton(valueProperty, content as SubstanceIntGUIContent, out value);
break;
case SubstanceWidgetType.Slider:
changed = DrawSlider(valueProperty, content as SubstanceIntGUIContent, out value);
break;
case SubstanceWidgetType.ComboBox:
changed = DrawComboBox(valueProperty, content as SubstanceIntGUIContent, out value);
break;
default:
changed = DrawDefault(valueProperty, content, out value);
break;
}
}
if (changed)
handler.SetInputInt(inputID, value);
return changed;
}
/// <summary>
/// Renders the int input as a toggle button.
/// </summary>
/// <param name="valueProperty">Value property.</param>
/// <param name="content">GUI content.</param>
/// <returns>True if value changed.</returns>
private static bool DrawToggleButton(SerializedProperty valueProperty, SubstanceIntGUIContent content, out int newValue)
{
newValue = 0;
var oldValue = valueProperty.intValue != 0;
var newValueToggle = EditorGUILayout.Toggle(content, oldValue);
if (oldValue != newValueToggle)
{
newValue = newValueToggle ? 1 : 0;
valueProperty.intValue = newValue;
return true;
}
return false;
}
/// <summary>
/// Renders the int input as a slider.
/// </summary>
/// <param name="valueProperty">Value property.</param>
/// <param name="content">GUI content.</param>
/// <returns>True if value changed.</returns>
private static bool DrawSlider(SerializedProperty valueProperty, SubstanceIntGUIContent content, out int newValue)
{
var numDescription = content.NumericalDescription;
var maxValue = numDescription.MaxValue;
var minValue = numDescription.MinValue;
var oldValue = valueProperty.intValue;
newValue = EditorGUILayout.IntSlider(content, oldValue, minValue, maxValue);
if (oldValue != newValue)
{
valueProperty.intValue = newValue;
return true;
}
return false;
}
/// <summary>
/// Renders the int input as combo box.
/// </summary>
/// <param name="valueProperty">Value property.</param>
/// <param name="content">GUI content.</param>
/// <returns>True if value changed.</returns>
private static bool DrawComboBox(SerializedProperty valueProperty, SubstanceIntGUIContent content, out int newValue)
{
var specializedContent = content as SubstanceIntComboBoxGUIContent;
var oldValue = valueProperty.intValue;
newValue = EditorGUILayout.IntPopup(content, oldValue, specializedContent.EnumValuesGUI, specializedContent.EnumValues);
if (oldValue != newValue)
{
valueProperty.intValue = newValue;
return true;
}
return false;
}
/// <summary>
/// Default input render.
/// </summary>
/// <param name="valueProperty">Value property.</param>
/// <param name="content">GUI content.</param>
/// <returns>True if value changed.</returns>
private static bool DrawDefault(SerializedProperty valueProperty, SubstanceInputGUIContent content, out int newValue)
{
var oldValue = valueProperty.intValue;
newValue = EditorGUILayout.IntField(content, oldValue);
if (oldValue != newValue)
{
valueProperty.intValue = newValue;
return true;
}
return false;
}
/// <summary>
/// Draws the random seed button.
/// </summary>
/// <param name="valueProperty">Value property.</param>
/// <param name="content">GUI content.</param>
/// <returns>True if value changed.</returns>
private static bool DrawRandomSeedButton(SerializedProperty valueProperty, SubstanceInputGUIContent content, out int newValue)
{
bool result = false;
newValue = valueProperty.intValue;
int minimum = 0;
int maximum = 10000;
int labelWidth = (int)EditorGUIUtility.labelWidth - 15;
int fieldWidth = 50;
content.text = "Random Seed";
content.tooltip = "$randomseed: the overall random aspect of the texture";
GUILayout.BeginHorizontal();
{
int buttonWidth = (int)EditorGUIUtility.currentViewWidth - labelWidth - fieldWidth - 60;
EditorGUILayout.LabelField(content, GUILayout.Width(labelWidth), GUILayout.ExpandWidth(true));
if (GUILayout.Button("Randomize", GUILayout.Width(buttonWidth)))
{
newValue = UnityEngine.Random.Range(minimum, maximum);
valueProperty.intValue = newValue;
result = true;
}
EditorGUI.BeginChangeCheck();
newValue = EditorGUILayout.IntField(newValue, GUILayout.Width(fieldWidth));
if (EditorGUI.EndChangeCheck())
{
newValue = (newValue < minimum) ? minimum : (newValue > maximum) ? maximum : newValue;
valueProperty.intValue = newValue;
result = true;
}
}
GUILayout.EndHorizontal();
return result;
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 91b2fc4c484fa3f48804e6352c3d51d8
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,95 @@
using UnityEditor;
using UnityEngine;
using Adobe.Substance;
namespace Adobe.SubstanceEditor
{
internal static class SubstanceInputDrawerInt2
{
private static readonly string[] _resulutions = { "256", "512", "1024", "2048", "4096" };
public static bool DrawInput(SerializedProperty valueProperty, SubstanceInputGUIContent content, SubstanceNativeGraph handler, int inputID)
{
Vector2Int newValue;
bool changed;
if (IsSizeAttribute(content))
{
changed = DrawResolutionSelection(valueProperty, content, out newValue);
}
else
{
changed = DrawDefault(valueProperty, content, out newValue);
}
if (changed)
handler.SetInputInt2(inputID, newValue);
return changed;
}
private static bool DrawResolutionSelection(SerializedProperty valueProperty, SubstanceInputGUIContent content, out Vector2Int newValue)
{
Vector2Int oldValue = valueProperty.vector2IntValue;
var currentIndex = GetEnumIndex(oldValue);
int newIndex = EditorGUILayout.Popup("Output Resolution", currentIndex, _resulutions);
if (currentIndex != newIndex)
{
newValue = GetValueFromIndex(newIndex);
valueProperty.vector2IntValue = newValue;
return true;
}
newValue = new Vector2Int();
return false;
}
private static bool DrawDefault(SerializedProperty valueProperty, SubstanceInputGUIContent content, out Vector2Int newValue)
{
Vector2Int oldValue = valueProperty.vector2IntValue;
newValue = EditorGUILayout.Vector2IntField(content, oldValue);
if (newValue != oldValue)
{
valueProperty.vector2IntValue = newValue;
return true;
}
return false;
}
private static bool IsSizeAttribute(GUIContent content)
{
return content.text == "$outputsize";
}
private static int GetEnumIndex(Vector2Int data)
{
switch (data.x)
{
case 8: return 0;
case 9: return 1;
case 10: return 2;
case 11: return 3;
case 12: return 4;
default:
return 0;
}
}
private static Vector2Int GetValueFromIndex(int index)
{
switch (index)
{
case 0: return new Vector2Int(8, 8);
case 1: return new Vector2Int(9, 9);
case 2: return new Vector2Int(10, 10);
case 3: return new Vector2Int(11, 11);
case 4: return new Vector2Int(12, 12);
default:
return new Vector2Int(8, 8);
}
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 17a55141ccae8494f8ea0e29affd3a9b
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,41 @@
using UnityEditor;
using UnityEngine;
using Adobe.Substance;
namespace Adobe.SubstanceEditor
{
internal static class SubstanceInputDrawerInt3
{
public static bool DrawInput(SerializedProperty valueProperty, SubstanceInputGUIContent content, SubstanceNativeGraph handler, int inputID)
{
Vector3Int newValue;
bool changed;
switch (content.Description.WidgetType)
{
default:
changed = DrawDefault(valueProperty, content, out newValue);
break;
}
if (changed)
handler.SetInputInt3(inputID, newValue);
return changed;
}
private static bool DrawDefault(SerializedProperty valueProperty, SubstanceInputGUIContent content, out Vector3Int newValue)
{
var previewValue = valueProperty.vector3IntValue;
newValue = EditorGUILayout.Vector3IntField(content, previewValue);
if (newValue != previewValue)
{
valueProperty.vector3IntValue = newValue;
return true;
}
return false;
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 7c99f44dae8ff5f4097e558c6f881451
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,211 @@
using UnityEditor;
using Adobe.Substance;
using UnityEngine;
using Adobe.Substance.Input.Description;
namespace Adobe.SubstanceEditor
{
internal static class SubstanceInputDrawerInt4
{
public static bool DrawInput(SerializedProperty valueProperty, SubstanceInputGUIContent content, SubstanceNativeGraph handler, int inputID)
{
int value0;
int value1;
int value2;
int value3;
bool changed;
switch (content.Description.WidgetType)
{
case SubstanceWidgetType.Slider:
changed = DrawSliderWidget(valueProperty, content as SubstanceInt4GUIContent, out value0, out value1, out value2, out value3);
break;
case SubstanceWidgetType.Color:
changed = DrawColorWidget(valueProperty, content as SubstanceInt4GUIContent, out value0, out value1, out value2, out value3);
break;
//TODO: Add edge cases here.
default:
changed = DrawDefault(valueProperty, content as SubstanceInt4GUIContent, out value0, out value1, out value2, out value3);
break;
}
if (changed)
handler.SetInputInt4(inputID, value0, value1, value2, value3);
return changed;
}
private static bool DrawDefault(SerializedProperty valueProperty, SubstanceInt4GUIContent content, out int newValue0, out int newValue1, out int newValue2, out int newValue3)
{
bool result = false;
var value0 = valueProperty?.FindPropertyRelative("Data0");
var value1 = valueProperty?.FindPropertyRelative("Data1");
var value2 = valueProperty?.FindPropertyRelative("Data2");
var value3 = valueProperty?.FindPropertyRelative("Data3");
var previewValue0 = value0.intValue;
var previewValue1 = value1.intValue;
var previewValue2 = value2.intValue;
var previewValue3 = value3.intValue;
newValue0 = EditorGUILayout.IntField(content, previewValue0);
newValue1 = EditorGUILayout.IntField(content, previewValue1);
newValue2 = EditorGUILayout.IntField(content, previewValue2);
newValue3 = EditorGUILayout.IntField(content, previewValue3);
if (newValue0 != previewValue0)
{
value0.intValue = newValue0;
result = true;
}
if (newValue1 != previewValue1)
{
value1.intValue = newValue1;
result = true;
}
if (newValue2 != previewValue2)
{
value2.intValue = newValue2;
result = true;
}
if (newValue3 != previewValue3)
{
value3.intValue = newValue3;
result = true;
}
return result;
}
private static bool DrawSliderWidget(SerializedProperty valueProperty, SubstanceInt4GUIContent content, out int newValue0, out int newValue1, out int newValue2, out int newValue3)
{
bool result = false;
int rightValue0 = 100;
int rightValue1 = 100;
int rightValue2 = 100;
int rightValue3 = 100;
int leftValue0 = 0;
int leftValue1 = 0;
int leftValue2 = 0;
int leftValue3 = 0;
var int4NumbericalDescription = content.NumericalDescription;
if (int4NumbericalDescription != null)
{
leftValue0 = int4NumbericalDescription.MinValue0;
leftValue1 = int4NumbericalDescription.MinValue1;
leftValue2 = int4NumbericalDescription.MinValue2;
leftValue3 = int4NumbericalDescription.MinValue3;
rightValue0 = int4NumbericalDescription.MaxValue0;
rightValue1 = int4NumbericalDescription.MaxValue1;
rightValue2 = int4NumbericalDescription.MaxValue2;
rightValue3 = int4NumbericalDescription.MaxValue3;
}
var value0 = valueProperty?.FindPropertyRelative("Data0");
var value1 = valueProperty?.FindPropertyRelative("Data1");
var value2 = valueProperty?.FindPropertyRelative("Data2");
var value3 = valueProperty?.FindPropertyRelative("Data3");
var previewValue0 = value0.intValue;
var previewValue1 = value1.intValue;
var previewValue2 = value2.intValue;
var previewValue3 = value3.intValue;
newValue0 = EditorGUILayout.IntSlider(content, previewValue0, leftValue0, rightValue0);
newValue1 = EditorGUILayout.IntSlider(content, previewValue1, leftValue1, rightValue1);
newValue2 = EditorGUILayout.IntSlider(content, previewValue2, leftValue2, rightValue2);
newValue3 = EditorGUILayout.IntSlider(content, previewValue3, leftValue3, rightValue3);
if (newValue0 != previewValue0)
{
value0.intValue = newValue0;
result = true;
}
if (newValue1 != previewValue1)
{
value1.intValue = newValue1;
result = true;
}
if (newValue2 != previewValue2)
{
value2.intValue = newValue2;
result = true;
}
if (newValue3 != previewValue3)
{
value3.intValue = newValue3;
result = true;
}
return result;
}
private static bool DrawColorWidget(SerializedProperty valueProperty, SubstanceInt4GUIContent content, out int newValue0, out int newValue1, out int newValue2, out int newValue3)
{
bool result = false;
var value0 = valueProperty?.FindPropertyRelative("Data0");
var value1 = valueProperty?.FindPropertyRelative("Data1");
var value2 = valueProperty?.FindPropertyRelative("Data2");
var value3 = valueProperty?.FindPropertyRelative("Data3");
var previewValue0 = value0.intValue;
var previewValue1 = value1.intValue;
var previewValue2 = value2.intValue;
var previewValue3 = value3.intValue;
var floatValue0 = ((float)previewValue0) / 255f;
var floatValue1 = ((float)previewValue1) / 255f;
var floatValue2 = ((float)previewValue2) / 255f;
var floatValue3 = ((float)previewValue3) / 255f;
Color color = new Color(floatValue0, floatValue1, floatValue2, floatValue3);
var newColor = EditorGUILayout.ColorField(content, color);
newValue0 = (int)newColor.r * 255;
newValue1 = (int)newColor.g * 255;
newValue2 = (int)newColor.b * 255;
newValue3 = (int)newColor.a * 255;
if (newValue0 != previewValue0)
{
value0.intValue = newValue0;
result = true;
}
if (newValue1 != previewValue1)
{
value1.intValue = newValue1;
result = true;
}
if (newValue2 != previewValue2)
{
value2.intValue = newValue2;
result = true;
}
if (newValue3 != previewValue3)
{
value3.intValue = newValue3;
result = true;
}
return result;
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 6083c230932064544b0beca8c8228f62
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,35 @@
using UnityEditor;
using Adobe.Substance;
namespace Adobe.SubstanceEditor
{
internal static class SubstanceInputDrawerString
{
public static bool DrawInput(SerializedProperty valueProperty, SubstanceInputGUIContent content, SubstanceNativeGraph handler, int inputID)
{
bool changed;
switch (content.Description.WidgetType)
{
default:
changed = DrawDefault(valueProperty, content);
break;
}
if (changed)
{
var stringValue = valueProperty.stringValue;
handler.SetInputString(inputID, stringValue);
}
return changed;
}
private static bool DrawDefault(SerializedProperty valueProperty, SubstanceInputGUIContent content)
{
EditorGUI.BeginChangeCheck();
EditorGUILayout.PropertyField(valueProperty, content);
return EditorGUI.EndChangeCheck();
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 1ac347008a541b247a481fafa2196b14
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,61 @@
using Adobe.Substance;
using UnityEditor;
using UnityEngine;
namespace Adobe.SubstanceEditor
{
internal static class SubstanceInputDrawerTexture
{
public static bool DrawInput(SerializedProperty valueProperty, SubstanceInputGUIContent content, SubstanceNativeGraph handler, int inputID)
{
Texture2D newValue;
bool changed;
switch (content.Description.WidgetType)
{
default:
changed = DrawDefault(valueProperty, content, out newValue);
break;
}
if (changed)
{
if (newValue != null)
{
var pixels = newValue.GetPixels32();
handler.SetInputTexture2D(inputID, pixels, newValue.width, newValue.height);
}
else
{
handler.SetInputTexture2DNull(inputID);
}
}
return changed;
}
private static bool DrawDefault(SerializedProperty valueProperty, SubstanceInputGUIContent content, out Texture2D newValue)
{
EditorGUI.BeginChangeCheck();
EditorGUILayout.ObjectField(valueProperty, content);
var changed = EditorGUI.EndChangeCheck();
newValue = null;
if (changed)
{
if (valueProperty.objectReferenceValue != null)
{
newValue = valueProperty.objectReferenceValue as Texture2D;
if (newValue != null)
{
if (!newValue.isReadable)
TextureUtils.SetReadableFlag(newValue, true);
}
}
}
return changed;
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 07aca6dfe5b18924ba359d2f53a2ac34
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,824 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using UnityEditor;
using UnityEngine;
using Adobe.Substance.Input;
using Adobe.SubstanceEditor.Importer;
using Adobe.SubstanceEditor.ProjectSettings;
using Adobe.Substance;
using UnityEditor.SceneManagement;
namespace Adobe.SubstanceEditor
{
/// <summary>
/// Editor Singleton to manage interactions with the Substance engine.
/// </summary>
internal sealed class SubstanceEditorEngine : ScriptableSingleton<SubstanceEditorEngine>
{
/// <summary>
/// Substance files currently loaded in the engine.
/// </summary>
private readonly Dictionary<string, SubstanceNativeGraph> _activeSubstanceDictionary = new Dictionary<string, SubstanceNativeGraph>();
/// <summary>
/// Currently active instances.
/// </summary>
private readonly List<SubstanceGraphSO> _managedInstances = new List<SubstanceGraphSO>();
private readonly Queue<string> _delayiedInitilization = new Queue<string>();
/// <summary>
/// Render results generated by the substance engine in a background thread.
/// </summary>
private readonly ConcurrentQueue<RenderResult> _renderResultsQueue = new ConcurrentQueue<RenderResult>();
private readonly List<SubstanceGraphSO> _playmodeObjects = new List<SubstanceGraphSO>();
private readonly Queue<DelayAssetCreationInfo> _creationQueue = new Queue<DelayAssetCreationInfo>();
private class DelayAssetCreationInfo
{
public SubstanceGraphSO InstanceAsset;
public string InstancePath;
public DelayAssetCreationInfo(SubstanceGraphSO instanceAsset, string instancePath)
{
InstanceAsset = instanceAsset;
InstancePath = instancePath;
}
}
internal void DelayAssetCreation(SubstanceGraphSO instanceAsset, string instancePath)
{
_creationQueue.Enqueue(new DelayAssetCreationInfo(instanceAsset, instancePath));
}
private bool _resetAllInputs = false;
/// <summary>
/// Initializer to ensure SubstanceEditorEngine is
/// started consistently on editor load and assembly reload.
///</summary>
[InitializeOnLoad]
private sealed class SubstanceEditorEngineInitializer
{
static SubstanceEditorEngineInitializer()
{
AssemblyReloadEvents.beforeAssemblyReload += OnBeforeAssemblyReload;
AssemblyReloadEvents.afterAssemblyReload += OnAfterAssemblyReload;
}
private static void OnBeforeAssemblyReload()
{
SubstanceEditorEngine.instance.TearDown();
}
private static void OnAfterAssemblyReload()
{
SubstanceEditorEngine.instance.Setup();
}
}
private static Queue<Tuple<string, string>> _delayMoveOperation = new Queue<Tuple<string, string>>();
internal void PushMoveOperation(string from, string to)
{
_delayMoveOperation.Enqueue(new Tuple<string, string>(from, to));
}
private bool _isLoaded;
public bool IsInitialized => _isLoaded;
/// <summary>
/// Initalize substance engine.
/// </summary>
private void Setup()
{
_isLoaded = false;
PluginPipelines.GetCurrentPipelineInUse();
var enginePath = PlatformUtils.GetEnginePath();
var pluginPath = PlatformUtils.GetPluginPath();
Engine.Initialize(pluginPath, enginePath);
EditorApplication.update += Update;
EditorApplication.quitting += OnQuit;
Undo.undoRedoPerformed += UndoCallback;
}
private void UndoCallback()
{
if (Selection.activeObject is SubstanceGraphSO)
{
var target = Selection.activeObject as SubstanceGraphSO;
var managedInstance = _managedInstances.FirstOrDefault((a) => a.GUID == target.GUID);
if (managedInstance != null)
{
managedInstance.RenderTextures = true;
PushAllInputsToUpdate();
}
}
}
/// <summary>
/// Shutdown substance engine.
/// </summary>
private void TearDown()
{
_isLoaded = false;
EditorApplication.update -= Update;
EditorApplication.quitting -= OnQuit;
Undo.undoRedoPerformed -= UndoCallback;
Engine.Shutdown();
}
private void OnQuit()
{
//Check if there are pending updates. If there is, we need to do them synchronously.
foreach (var graph in _managedInstances)
{
if (graph == null)
continue;
if (!TryGetHandlerFromInstance(graph, out SubstanceNativeGraph substanceHandler))
continue;
if (substanceHandler.InRenderWork)
continue;
if (graph.RenderTextures)
{
var renderResult = substanceHandler.Render();
graph.UpdateAssociatedAssets(renderResult, true);
}
}
}
#region Update
/// <summary>
/// Editor update.
/// </summary>
private void Update()
{
if (!_isLoaded)
{
LoadAllSbsarFiles();
_isLoaded = true;
}
_managedInstances.RemoveAll(item => item == null);
CheckDelayedMove();
HandleAssetCreation();
HandleDelayedInitialization();
HandlePlaymode();
CheckUIUpdate();
CheckRenderResultsUpdates();
}
private void CheckDelayedMove()
{
while (_delayMoveOperation.Count != 0)
{
var newMove = _delayMoveOperation.Dequeue();
var substanceInstance = AssetDatabase.LoadAssetAtPath<SubstanceGraphSO>(newMove.Item2);
if (substanceInstance != null)
{
substanceInstance.Move(newMove.Item2);
}
}
}
private void HandleAssetCreation()
{
while (_creationQueue.Count != 0)
{
var creationData = _creationQueue.Dequeue();
if (creationData == null)
continue;
if (creationData.InstanceAsset == null)
continue;
var oldAsset = AssetDatabase.LoadAssetAtPath<SubstanceGraphSO>(creationData.InstancePath);
AssetDatabase.CreateAsset(creationData.InstanceAsset, creationData.InstancePath);
var createAsset = AssetDatabase.LoadAssetAtPath<SubstanceGraphSO>(creationData.InstancePath);
var importer = AssetImporter.GetAtPath(createAsset.AssetPath) as SubstanceImporter;
EditorUtility.SetDirty(importer);
AssetDatabase.Refresh();
SubmitAsyncRenderWorkBatch(new List<SubstanceGraphSO> { creationData.InstanceAsset });
}
}
private void HandleDelayedInitialization()
{
while (_delayiedInitilization.Count != 0)
{
var instancePath = _delayiedInitilization.Dequeue();
var materialInstance = AssetDatabase.LoadAssetAtPath<SubstanceGraphSO>(instancePath);
_managedInstances.Add(materialInstance);
}
}
private bool _onPlaymodeEnterHandled = false;
private void HandlePlaymode()
{
if (EditorApplication.isPlaying && !_onPlaymodeEnterHandled)
{
var runtiemMaterials = GameObject.FindObjectsOfType<Substance.Runtime.SubstanceRuntimeGraph>();
foreach (var graph in _managedInstances)
{
bool isAssigned = runtiemMaterials.FirstOrDefault(a => a.GraphSO == graph) != null;
if (Application.IsPlaying(graph) && (isAssigned || graph.IsRuntimeOnly))
{
if (!_playmodeObjects.Contains(graph))
_playmodeObjects.Add(graph);
}
}
_onPlaymodeEnterHandled = true;
}
if (!EditorApplication.isPlaying && _onPlaymodeEnterHandled)
{
var objectsToRemove = new List<SubstanceGraphSO>();
foreach (var playmodeObject in _playmodeObjects)
{
if (!Application.IsPlaying(playmodeObject))
{
playmodeObject.RenderTextures = true;
objectsToRemove.Add(playmodeObject);
}
}
foreach (var item in objectsToRemove)
_playmodeObjects.Remove(item);
_onPlaymodeEnterHandled = false;
}
}
/// <summary>
/// Updated the state of the SubstanceFileHandlers based on changes made in the graph objects.
/// </summary>
private void CheckUIUpdate()
{
foreach (var graph in _managedInstances)
{
if (graph == null)
continue;
if (graph.RawData == null)
{
var assets = AssetDatabase.LoadAllAssetsAtPath(graph.AssetPath);
if (assets == null)
continue;
var dataObject = assets.FirstOrDefault(a => a is SubstanceFileRawData) as SubstanceFileRawData;
graph.RawData = dataObject;
EditorUtility.SetDirty(graph);
AssetDatabase.Refresh();
}
if (!TryGetHandlerFromInstance(graph, out SubstanceNativeGraph substanceHandler))
continue;
if (substanceHandler.InRenderWork)
continue;
if (graph.IsRuntimeOnly && graph.OutputMaterial != null)
if (graph.OutputMaterial.GetTexture("_MainTex") == null)
MaterialUtils.AssignOutputTexturesToMaterial(graph);
if (HasMaterialShaderChanged(graph))
{
SubmitAsyncRenderWork(substanceHandler, graph, true);
graph.RenderTextures = true;
continue;
}
if (graph.OutputRemaped)
{
graph.OutputRemaped = false;
if (graph.IsRuntimeOnly)
{
DeleteGeneratedTextures(graph);
}
RenderingUtils.UpdateAlphaChannelsAssignment(substanceHandler, graph);
SubmitAsyncRenderWork(substanceHandler, graph, true);
graph.RenderTextures = true;
continue;
}
if (graph.RenderTextures)
{
graph.RenderTextures = false;
if (_resetAllInputs)
{
_resetAllInputs = true;
foreach (var input in graph.Input)
{
input.UpdateNativeHandle(substanceHandler);
}
}
SubmitAsyncRenderWork(substanceHandler, graph);
EditorUtility.SetDirty(graph);
}
}
}
/// <summary>
/// Updated the render results that are finished by the substance engine
/// </summary>
private void CheckRenderResultsUpdates()
{
if (_renderResultsQueue.TryDequeue(out RenderResult renderResult))
{
SubstanceGraphSO graph = _managedInstances.FirstOrDefault(a => a.GUID == renderResult.GUID);
if (graph == null)
return;
if (!TryGetHandlerFromInstance(graph, out SubstanceNativeGraph handler))
return;
var textureReassigned = graph.UpdateAssociatedAssets(renderResult.Result, renderResult.ForceRebuild);
if (textureReassigned)
{
if (!string.IsNullOrEmpty(graph.AssetPath))
{
AssetCreationUtils.CreateMaterialOrUpdateMaterial(graph, graph.Name);
EditorUtility.SetDirty(graph);
EditorUtility.SetDirty(graph.OutputMaterial);
AssetDatabase.Refresh();
}
}
else
{
if (graph.OutputMaterial == null)
{
AssetCreationUtils.CreateMaterialOrUpdateMaterial(graph, graph.Name);
EditorUtility.SetDirty(graph);
EditorUtility.SetDirty(graph.OutputMaterial);
AssetDatabase.Refresh();
}
else
{
EditorUtility.SetDirty(graph.OutputMaterial);
}
}
handler.InRenderWork = false;
}
}
/// <summary>
/// Checks if the shaders assigned to the substance graph generated material has changed. If so, we have to change the default outputs.
/// </summary>
private bool HasMaterialShaderChanged(SubstanceGraphSO graph)
{
if (graph.OutputMaterial == null || string.IsNullOrEmpty(graph.MaterialShader))
return false;
if (graph.OutputMaterial.shader.name == graph.MaterialShader)
return false;
AssetCreationUtils.UpdateMeterialAssignment(graph);
return true;
}
#endregion Update
#region Public methods
#region Instance Management
/// <summary>
/// Loads a sbsar file into the engine. The engine will keep track of this file internally.
/// </summary>
/// <param name="assetPath">Path to a sbsar file.</param>
public void InitializeInstance(SubstanceGraphSO substanceInstance, string instancePath, out SubstanceGraphSO matchingInstance)
{
matchingInstance = null;
if (substanceInstance == null)
return;
if (string.IsNullOrEmpty(substanceInstance.AssetPath))
Debug.LogError("Unable to instantiate substance material with null assetPath.");
matchingInstance = _managedInstances.FirstOrDefault(a => a.OutputPath.Equals(substanceInstance.OutputPath, StringComparison.OrdinalIgnoreCase));
if (!_activeSubstanceDictionary.TryGetValue(substanceInstance.GUID, out SubstanceNativeGraph _))
{
var substanceArchive = Engine.OpenFile(substanceInstance.RawData.FileContent, substanceInstance.GetNativeID());
_activeSubstanceDictionary.Add(substanceInstance.GUID, substanceArchive);
}
if (!string.IsNullOrEmpty(instancePath))
_delayiedInitilization.Enqueue(instancePath);
else
{
_managedInstances.Add(substanceInstance);
}
}
/// <summary>
/// Unloads the target substance from th e substance engine.
/// </summary>
/// <param name="assetPath">Path to a sbsar file.</param>
public void ReleaseInstance(SubstanceGraphSO substanceInstance)
{
if (TryGetHandlerFromInstance(substanceInstance, out SubstanceNativeGraph substanceArchive))
{
_activeSubstanceDictionary.Remove(substanceInstance.GUID);
substanceArchive.Dispose();
var managedInstance = _managedInstances.FirstOrDefault((a) => a.GUID == substanceInstance.GUID);
if (managedInstance != null)
_managedInstances.Remove(managedInstance);
}
}
public string SerializeCurrentState(SubstanceGraphSO substanceInstance)
{
if (TryGetHandlerFromInstance(substanceInstance, out SubstanceNativeGraph substanceArchive))
return substanceArchive.CreatePresetFromCurrentState();
return string.Empty;
}
public void SetStateFromSerializedData(SubstanceGraphSO substanceInstance, string data)
{
if (TryGetHandlerFromInstance(substanceInstance, out SubstanceNativeGraph substanceArchive))
substanceArchive.ApplyPreset(data);
}
#endregion Instance Management
public void PushAllInputsToUpdate()
{
_resetAllInputs = true;
}
/// <summary>
/// Loads the list of substance graphs from a substance file.
/// </summary>
/// <param name="assetPath">Path to the target substance file.</param>
/// <returns>List of substance graph objects.</returns>
public void CreateGraphObject(SubstanceGraphSO instance, SubstanceGraphSO copy = null)
{
if (!TryGetHandlerFromInstance(instance, out SubstanceNativeGraph substanceHandle))
return;
if (copy != null)
{
if (TryGetHandlerFromInstance(copy, out SubstanceNativeGraph copyHandle))
{
var copyPreset = copyHandle.CreatePresetFromCurrentState();
substanceHandle.ApplyPreset(copyPreset); ;
}
}
instance.Input = GetGraphInputs(substanceHandle);
instance.Output = GetGraphOutputs(substanceHandle);
instance.PhysicalSize = substanceHandle.GetPhysicalSize();
instance.HasPhysicalSize = instance.PhysicalSize != Vector3.zero;
RenderingUtils.ConfigureOutputTextures(substanceHandle, instance);
instance.GenerateAllOutputs = SubstanceEditorSettingsSO.GenerateAllTextures();
SetOutputTextureSize(instance, substanceHandle);
instance.DefaultPreset = substanceHandle.CreatePresetFromCurrentState();
var thumbnailData = substanceHandle.GetThumbnail();
if (thumbnailData != null)
{
instance.Thumbnail = thumbnailData;
instance.HasThumbnail = true;
}
}
/// <summary>
/// Renders a substance file using the substance engine.
/// </summary>
/// <param name="assetPath">Path to a sbsar file.</param>
/// <param name="graphID">Target graph index.</param>
/// <returns>Task that will be finished once the rendering is finished.</returns>
public void RenderInstanceAsync(SubstanceGraphSO instances)
{
if (TryGetHandlerFromInstance(instances, out SubstanceNativeGraph substanceArchive))
SubmitAsyncRenderWork(substanceArchive, instances);
}
public void RenderInstanceAsync(IReadOnlyList<SubstanceGraphSO> instances)
{
foreach (var graph in instances)
RenderInstanceAsync(graph);
}
/// <summary>
/// Assigns the substance graph objects inputs to the substance file Handlers associated with them.
/// </summary>
/// <param name="assetPath">Path to the sbsar object.</param>
/// <param name="graphCopy">List of graph objects.</param>
internal void SetSubstanceInput(SubstanceGraphSO instance)
{
if (TryGetHandlerFromInstance(instance, out SubstanceNativeGraph substanceArchive))
{
foreach (var input in instance.Input)
input.UpdateNativeHandle(substanceArchive);
}
}
#region Preset
/// <summary>
/// Get the preset XML document for the current state of the a managed substance object.
/// </summary>
/// <param name="assetPath">Path to the target sbsar file.</param>
/// <param name="graphID">Target graph id. </param>
/// <returns>XML document with the current input states as a preset. </returns>
public string ExportGraphPresetXML(SubstanceGraphSO instance)
{
if (!TryGetHandlerFromInstance(instance, out SubstanceNativeGraph substanceArchive))
return null;
return substanceArchive.CreatePresetFromCurrentState();
}
/// <summary>
/// Loads the inputs from a preset XML document into the target graph of a managed substance file.
/// </summary>
/// <param name="substanceInstancePath">Path to the target sbsar file.</param>
/// <param name="graphID">Target graph id.</param>
/// <param name="presetXML">Preset XML document.</param>
public void LoadPresetsToGraph(SubstanceGraphSO instance, string presetXML)
{
if (TryGetHandlerFromInstance(instance, out SubstanceNativeGraph substanceHandler))
{
substanceHandler.ApplyPreset(presetXML);
instance.Input = GetGraphInputs(substanceHandler);
instance.RenderTextures = true;
EditorUtility.SetDirty(instance);
}
}
#endregion Preset
#endregion Public methods
public bool TryGetHandlerFromInstance(SubstanceGraphSO substanceInstance, out SubstanceNativeGraph substanceHandler)
{
substanceHandler = null;
if (substanceInstance == null)
return false;
if (!_activeSubstanceDictionary.TryGetValue(substanceInstance.GUID, out substanceHandler))
{
return false;
}
return true;
}
public bool TryGetHandlerFromGUI(string guid, out SubstanceNativeGraph substanceHandler)
{
if (!_activeSubstanceDictionary.TryGetValue(guid, out substanceHandler))
return false;
return true;
}
/// <summary>
/// Loads all sbsar files currently in the project.
/// </summary>
private void LoadAllSbsarFiles()
{
string[] files = Directory.GetFiles(Application.dataPath, "*.sbsar", SearchOption.AllDirectories);
foreach (string filePath in files)
{
if (filePath.StartsWith(Application.dataPath))
{
var assetPath = "Assets" + filePath.Substring(Application.dataPath.Length);
assetPath = assetPath.Replace(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar);
if (!File.Exists(assetPath))
continue;
SubstanceImporter importer = AssetImporter.GetAtPath(assetPath) as SubstanceImporter;
if (importer == null)
continue;
foreach (var substanceInstance in importer._fileAsset.GetGraphs())
{
if (!substanceInstance.IsRuntimeOnly)
return;
InitializeInstance(substanceInstance, AssetDatabase.GetAssetPath(substanceInstance), out SubstanceGraphSO _);
if (TryGetHandlerFromInstance(substanceInstance, out SubstanceNativeGraph fileHandler))
{
if (substanceInstance == null)
continue;
substanceInstance.RuntimeInitialize(fileHandler, substanceInstance.IsRuntimeOnly);
}
}
}
}
}
public void RefreshActiveInstances()
{
foreach (var substanceInstance in _managedInstances)
{
substanceInstance.RenderTextures = true;
}
}
private List<ISubstanceInput> GetGraphInputs(SubstanceNativeGraph substanceFileHandler)
{
var inputs = new List<ISubstanceInput>();
var graphInputCount = substanceFileHandler.GetInputCount();
for (int j = 0; j < graphInputCount; j++)
{
SubstanceInputBase graphInput = substanceFileHandler.GetInputObject(j);
inputs.Add(graphInput);
}
return inputs;
}
private List<SubstanceOutputTexture> GetGraphOutputs(SubstanceNativeGraph substanceFileHandler)
{
var outputs = new List<SubstanceOutputTexture>();
var graphOutputCount = substanceFileHandler.GetOutputCount();
for (int j = 0; j < graphOutputCount; j++)
{
var outputDescription = substanceFileHandler.GetOutputDescription(j);
var unityTextureName = MaterialUtils.GetUnityTextureName(outputDescription);
SubstanceOutputTexture graphData = new SubstanceOutputTexture(outputDescription, unityTextureName);
if (graphData.IsBaseColor() ||
graphData.IsDiffuse() ||
graphData.IsSpecular() ||
graphData.IsHightMap() ||
graphData.IsEmissive())
{
graphData.sRGB = true;
}
else
{
graphData.sRGB = false;
}
outputs.Add(graphData);
}
return outputs;
}
private void SetOutputTextureSize(SubstanceGraphSO graph, SubstanceNativeGraph substanceFileHandler)
{
var outputSize = graph.Input.FirstOrDefault(a => a.Description.Label == "$outputsize");
if (outputSize == null)
return;
if (outputSize is SubstanceInputInt2 outputSizeInput)
{
outputSizeInput.Data = SubstanceEditorSettingsSO.TextureOutputResultion();
outputSizeInput.UpdateNativeHandle(substanceFileHandler);
}
}
#region Rendering
public void SubmitAsyncRenderWork(SubstanceNativeGraph substanceArchive, SubstanceGraphSO instanceKey, bool forceRebuild = false)
{
if (substanceArchive.InRenderWork)
return;
substanceArchive.InRenderWork = true;
var renderResut = new RenderResult()
{
SubstanceArchive = substanceArchive,
ForceRebuild = forceRebuild,
GUID = instanceKey.GUID
};
Task.Run(() =>
{
try
{
renderResut.Result = substanceArchive.Render();
_renderResultsQueue.Enqueue(renderResut);
}
catch (Exception e)
{
substanceArchive.InRenderWork = false;
Debug.LogException(e);
}
});
}
public void SubmitAsyncRenderWorkBatch(IReadOnlyList<SubstanceGraphSO> instanceKey)
{
var guildList = instanceKey.Select(a => a.GUID).ToArray();
Task.Run(() =>
{
foreach (var guid in guildList)
{
if (!TryGetHandlerFromGUI(guid, out SubstanceNativeGraph substanceArchive))
continue;
if (substanceArchive.InRenderWork)
continue;
substanceArchive.InRenderWork = true;
var renderResut = new RenderResult()
{
SubstanceArchive = substanceArchive,
ForceRebuild = false,
GUID = guid
};
try
{
renderResut.Result = substanceArchive.Render();
_renderResultsQueue.Enqueue(renderResut);
}
catch (Exception e)
{
substanceArchive.InRenderWork = false;
Debug.LogException(e);
}
}
});
}
private void DeleteGeneratedTextures(SubstanceGraphSO graph)
{
foreach (var output in graph.Output)
{
if (output.OutputTexture != null)
{
var texturePath = AssetDatabase.GetAssetPath(output.OutputTexture);
if (!string.IsNullOrEmpty(texturePath))
AssetDatabase.DeleteAsset(texturePath);
}
}
}
private struct RenderResult
{
public SubstanceNativeGraph SubstanceArchive;
public IntPtr Result;
public bool ForceRebuild;
public string GUID;
}
#endregion Rendering
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 83f5cf3931946de4da5f6a2138701f02
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,142 @@
using UnityEngine;
using UnityEditor;
using System.IO;
using Adobe.Substance;
using Adobe.SubstanceEditor.Importer;
using UnityEditor.SceneManagement;
namespace Adobe.SubstanceEditor
{
[CustomEditor(typeof(SubstanceFileSO))]
[CanEditMultipleObjects]
public class SubstanceFileEditor : UnityEditor.Editor
{
private SubstanceFileSO _target;
public void OnEnable()
{
_target = serializedObject.targetObject as SubstanceFileSO;
}
/// <summary>
/// Callback for GUI events to block substance files from been duplicated.
/// </summary>
/// <param name="guid">Asset guid.</param>
/// <param name="rt">GUI rect.</param>
protected static void OnHierarchyWindowItemOnGUI(string guid, Rect rt)
{
var currentEvent = Event.current;
if ("Duplicate" == currentEvent.commandName && currentEvent.type == EventType.ExecuteCommand)
{
var assetPath = AssetDatabase.GUIDToAssetPath(guid);
if (Path.GetExtension(assetPath) == ".sbsar")
{
Debug.LogWarning("Substance graph can not be manually duplicated.");
currentEvent.Use();
}
}
}
public override Texture2D RenderStaticPreview(string assetPath, UnityEngine.Object[] subAssets, int width, int height)
{
if (_target == null)
return null;
var importer = AssetImporter.GetAtPath(AssetDatabase.GetAssetPath(_target)) as SubstanceImporter;
if (importer == null)
return null;
var defaultGraph = importer.GetDefaultGraph();
if (defaultGraph == null)
return null;
if (defaultGraph.HasThumbnail)
{
var thumbnailTexture = defaultGraph.GetThumbnailTexture();
return thumbnailTexture;
}
else
{
var icon = UnityPackageInfo.GetSubstanceIcon(width, height);
if (icon != null)
{
Texture2D tex = new Texture2D(width, height);
EditorUtility.CopySerialized(icon, tex);
return tex;
}
}
return base.RenderStaticPreview(assetPath, subAssets, width, height);
}
#region Scene Drag
public void OnSceneDrag(SceneView sceneView, int index)
{
Event evt = Event.current;
if (evt.type == EventType.Repaint)
return;
var materialIndex = -1;
var go = HandleUtility.PickGameObject(evt.mousePosition, out materialIndex);
var importer = AssetImporter.GetAtPath(AssetDatabase.GetAssetPath(_target)) as SubstanceImporter;
if (importer != null)
{
var defaultGraph = importer.GetDefaultGraph();
if (defaultGraph != null && defaultGraph.OutputMaterial != null)
{
if (go && go.GetComponent<Renderer>())
{
HandleRenderer(go.GetComponent<Renderer>(), materialIndex, defaultGraph.OutputMaterial, evt.type, evt.alt);
EditorSceneManager.MarkSceneDirty(EditorSceneManager.GetActiveScene());
}
}
}
}
internal static void HandleRenderer(Renderer r, int materialIndex, Material dragMaterial, EventType eventType, bool alt)
{
var applyMaterial = false;
switch (eventType)
{
case EventType.DragUpdated:
DragAndDrop.visualMode = DragAndDropVisualMode.Copy;
applyMaterial = true;
break;
case EventType.DragPerform:
DragAndDrop.AcceptDrag();
applyMaterial = true;
break;
}
if (applyMaterial)
{
var materials = r.sharedMaterials;
bool isValidMaterialIndex = (materialIndex >= 0 && materialIndex < r.sharedMaterials.Length);
if (!alt && isValidMaterialIndex)
{
materials[materialIndex] = dragMaterial;
}
else
{
for (int q = 0; q < materials.Length; ++q)
materials[q] = dragMaterial;
}
r.sharedMaterials = materials;
}
}
#endregion Scene Drag
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 812ebf7eca2a66e4c989c54c5e8b9e8f
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,967 @@
using Adobe.Substance;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using UnityEditor;
using UnityEditor.SceneManagement;
using UnityEngine;
namespace Adobe.SubstanceEditor
{
[CustomEditor(typeof(SubstanceGraphSO))]
public class SubstanceGraphSOEditor : UnityEditor.Editor
{
private GraphInputsGroupingHelper _inputGroupingHelper;
private GraphOutputAlphaChannelsHelper _outputChannelsHelper;
private bool _propertiesChanged = false;
private bool _showOutput = true;
private bool _showExportPresentationHandler = false;
private bool _showPhysicalSize = false;
private SubstanceGraphSO _target = null;
private SubstanceNativeGraph _nativeGraph = null;
// Scrollview handling:
private Rect lastRect;
private Texture2D _backgroundImage;
private MaterialEditor _materialPreviewEditor;
private Vector2 _textureOutputScrollView;
private SerializedProperty _generateAllOutputsProperty;
private SerializedProperty _generateAllMipmapsProperty;
private SerializedProperty _runtimeOnlyProperty;
private SerializedProperty _outputRemapedProperty;
private SerializedProperty _graphOutputs;
private SerializedProperty _presetProperty;
private SerializedProperty _physicalSizelProperty;
private SerializedProperty _hasPhysicalSizeProperty;
private SerializedProperty _enablePhysicalSizeProperty;
private IReadOnlyList<SerializedProperty> _outputProperties;
public void OnEnable()
{
if (!IsSerializedObjectReady())
return;
_target = serializedObject.targetObject as SubstanceGraphSO;
_textureOutputScrollView = Vector2.zero;
_propertiesChanged = false;
if (_inputGroupingHelper == null)
_inputGroupingHelper = new GraphInputsGroupingHelper(_target, serializedObject);
if (_outputChannelsHelper == null)
_outputChannelsHelper = new GraphOutputAlphaChannelsHelper(_target);
float c = (EditorGUIUtility.isProSkin) ? 0.35f : 0.65f;
if (_backgroundImage == null)
_backgroundImage = Globals.CreateColoredTexture(16, 16, new Color(c, c, c, 1));
EditorApplication.projectWindowItemOnGUI += OnHierarchyWindowItemOnGUI;
_generateAllOutputsProperty = serializedObject.FindProperty("GenerateAllOutputs");
_generateAllMipmapsProperty = serializedObject.FindProperty("GenerateAllMipmaps");
_runtimeOnlyProperty = serializedObject.FindProperty("IsRuntimeOnly");
_outputRemapedProperty = serializedObject.FindProperty("OutputRemaped");
_graphOutputs = serializedObject.FindProperty("Output");
_presetProperty = serializedObject.FindProperty("CurrentStatePreset");
_physicalSizelProperty = serializedObject.FindProperty("PhysicalSize");
_hasPhysicalSizeProperty = serializedObject.FindProperty("HasPhysicalSize");
_enablePhysicalSizeProperty = serializedObject.FindProperty("EnablePhysicalSize");
if (!SubstanceEditorEngine.instance.TryGetHandlerFromInstance(_target, out _nativeGraph))
{
if (!SubstanceEditorEngine.instance.IsInitialized)
return;
SubstanceEditorEngine.instance.InitializeInstance(_target, null, out SubstanceGraphSO _);
if (SubstanceEditorEngine.instance.TryGetHandlerFromInstance(_target, out _nativeGraph))
_target.RuntimeInitialize(_nativeGraph, _target.IsRuntimeOnly);
}
var outputList = serializedObject.FindProperty("Output");
var list = new List<SerializedProperty>();
for (int i = 0; i < outputList.arraySize; i++)
{
var output = outputList.GetArrayElementAtIndex(i);
var textureTarget = output.FindPropertyRelative("MaterialTextureTarget");
list.Add(textureTarget);
}
_outputProperties = list;
}
private void GetShaderInputTextures(Shader shader)
{
_shaderInputTextures.Add("none");
for (int i = 0; i < ShaderUtil.GetPropertyCount(shader); i++)
{
if (ShaderUtil.GetPropertyType(shader, i) == ShaderUtil.ShaderPropertyType.TexEnv)
{
_shaderInputTextures.Add(ShaderUtil.GetPropertyName(shader, i));
}
}
}
public void OnDisable()
{
if (_materialPreviewEditor != null)
{
_materialPreviewEditor.OnDisable();
_materialPreviewEditor = null;
}
SaveEditorChanges();
EditorApplication.projectWindowItemOnGUI -= OnHierarchyWindowItemOnGUI;
}
public void SaveEditorChanges()
{
if (_propertiesChanged)
{
SaveTGAFiles();
UpdateGraphMaterialLabel();
AssetDatabase.Refresh();
}
_propertiesChanged = false;
}
public override void OnInspectorGUI()
{
if (serializedObject.targetObject == null)
{
return;
}
if (_nativeGraph == null)
{
if (!SubstanceEditorEngine.instance.TryGetHandlerFromInstance(_target, out _nativeGraph))
{
return;
}
}
if (_materialPreviewEditor == null)
{
var material = _target.OutputMaterial;
if (material != null)
_materialPreviewEditor = MaterialEditor.CreateEditor(material) as MaterialEditor;
}
serializedObject.Update();
if (DrawGraph())
{
serializedObject.ApplyModifiedProperties();
_propertiesChanged = true;
}
}
/// <summary>
/// Callback for GUI events to block substance files from been duplicated.
/// </summary>
/// <param name="guid">Asset guid.</param>
/// <param name="rt">GUI rect.</param>
protected static void OnHierarchyWindowItemOnGUI(string guid, Rect rt)
{
var currentEvent = Event.current;
if ("Duplicate" == currentEvent.commandName && currentEvent.type == EventType.ExecuteCommand)
{
var assetPath = AssetDatabase.GUIDToAssetPath(guid);
var instanceObject = AssetDatabase.LoadAssetAtPath<SubstanceGraphSO>(assetPath);
if (instanceObject != null)
{
Debug.LogWarning("Substance graph can not be manually duplicated.");
currentEvent.Use();
}
}
}
#region Material Preview
public override bool HasPreviewGUI()
{
return _materialPreviewEditor != null;
}
public override GUIContent GetPreviewTitle()
{
return new GUIContent("Material", null, "");
}
public override void OnPreviewSettings()
{
if (_materialPreviewEditor)
_materialPreviewEditor.OnPreviewSettings();
}
public override void OnPreviewGUI(Rect r, GUIStyle background)
{
if (_materialPreviewEditor)
_materialPreviewEditor.OnPreviewGUI(r, background);
}
#endregion Material Preview
#region Draw
private bool DrawGraph()
{
bool valuesChanged = false;
if (DrawTextureGenerationSettings(_generateAllOutputsProperty, _generateAllMipmapsProperty, _runtimeOnlyProperty))
{
_outputRemapedProperty.boolValue = true;
valuesChanged = true;
}
GUILayout.Space(8);
DrawInputs(out bool serializedObject, out bool renderGraph);
if (renderGraph)
{
var newPreset = _nativeGraph.CreatePresetFromCurrentState();
_presetProperty.stringValue = newPreset;
SubstanceEditorEngine.instance.SubmitAsyncRenderWork(_nativeGraph, _target);
valuesChanged = true;
}
if (serializedObject)
valuesChanged = true;
DrawPresentExport(_target);
EditorGUILayout.Space();
_showOutput = EditorGUILayout.Foldout(_showOutput, "Generated textures");
if (_showOutput)
{
if (DrawAdvanceSettings())
{
MaterialUtils.EnableEmissionIfAssigned(_target.OutputMaterial);
_outputRemapedProperty.boolValue = true;
valuesChanged = true;
}
if (DrawGeneratedTextures(_graphOutputs, _generateAllOutputsProperty.boolValue))
{
_outputRemapedProperty.boolValue = true;
valuesChanged = true;
}
}
return valuesChanged;
}
#region Texture Generation Settings
private bool DrawTextureGenerationSettings(SerializedProperty generateAllOutputsProperty, SerializedProperty generateAllMipmapsProperty, SerializedProperty runtimeOnlyProperty)
{
bool changed = false;
GUILayout.Space(4);
var boxWidth = EditorGUIUtility.currentViewWidth;
var boxHeight = (3 * EditorGUIUtility.singleLineHeight) + 16;
var padding = 16;
DrawHighlightBox(boxWidth, boxHeight, padding);
if (DrawGenerateAllOutputs(generateAllOutputsProperty) ||
DrawGenerateAllMipmaps(generateAllMipmapsProperty) ||
DrawRuntimeOnlyToggle(runtimeOnlyProperty))
{
changed = true;
}
return changed;
}
private static readonly GUIContent _GenerateAllOutputsGUI = new GUIContent("Generate All Outputs", "Force the generation of all Substance outputs");
private bool DrawGenerateAllOutputs(SerializedProperty generateAllOutputsProperty)
{
var oldValue = generateAllOutputsProperty.boolValue;
generateAllOutputsProperty.boolValue = EditorGUILayout.Toggle(_GenerateAllOutputsGUI, generateAllOutputsProperty.boolValue);
return oldValue != generateAllOutputsProperty.boolValue;
}
private static readonly GUIContent _GenerateAllMipMapsGUI = new GUIContent("Generate Mip Maps", "Enable MipMaps when generating textures");
private bool DrawGenerateAllMipmaps(SerializedProperty generateAllMipmapsProperty)
{
var oldValue = generateAllMipmapsProperty.boolValue;
generateAllMipmapsProperty.boolValue = EditorGUILayout.Toggle(_GenerateAllMipMapsGUI, generateAllMipmapsProperty.boolValue);
return oldValue != generateAllMipmapsProperty.boolValue;
}
private static readonly GUIContent _RuntimeOnlyGUI = new GUIContent("Runtime only", "If checked this instance will not generate TGA texture files");
private bool DrawRuntimeOnlyToggle(SerializedProperty runtimeOnlyProperty)
{
var oldValue = runtimeOnlyProperty.boolValue;
runtimeOnlyProperty.boolValue = EditorGUILayout.Toggle(_RuntimeOnlyGUI, runtimeOnlyProperty.boolValue);
return oldValue != runtimeOnlyProperty.boolValue;
}
#endregion Texture Generation Settings
#region Physical size
private bool DrawPhysicalSize()
{
if (!_hasPhysicalSizeProperty.boolValue)
return false;
_showPhysicalSize = EditorGUILayout.Foldout(_showPhysicalSize, "Physical Size");
bool valueChanged = false;
if (_showPhysicalSize)
{
var currentValue = _physicalSizelProperty.vector3Value;
var enablePhysicaSize = _enablePhysicalSizeProperty.boolValue;
if (EditorGUILayout.Toggle("Use Physical Size", enablePhysicaSize) != enablePhysicaSize)
{
_enablePhysicalSizeProperty.boolValue = !enablePhysicaSize;
valueChanged = true;
}
var newValue = new Vector3();
newValue.x = EditorGUILayout.FloatField("X:", currentValue.x);
newValue.y = EditorGUILayout.FloatField("Y:", currentValue.y);
newValue.z = EditorGUILayout.FloatField("Z:", currentValue.z);
if ((newValue - currentValue).sqrMagnitude >= 0.01f)
{
_physicalSizelProperty.vector3Value = newValue;
valueChanged = true;
}
if (_target.OutputMaterial != null)
{
DrawPhysicalSizeOffsets(_target.OutputMaterial);
}
}
return valueChanged;
}
private static bool _showPhysicalSizePositionOffset = false;
private void DrawPhysicalSizeOffsets(Material material)
{
EditorGUI.indentLevel++;
_showPhysicalSizePositionOffset = EditorGUILayout.Foldout(_showPhysicalSizePositionOffset, "Position Offset (In %)");
if (_showPhysicalSizePositionOffset)
{
Vector2 offset = MaterialUtils.GetPhysicalSizePositionOffset(material);
Vector2 newOffset = offset;
newOffset.x = EditorGUILayout.FloatField("X:", offset.x * 100.0f) / 100.0f;
newOffset.y = EditorGUILayout.FloatField("Y:", offset.y * 100.0f) / 100.0f;
if ((newOffset - offset).sqrMagnitude >= 0.0000001f)
{
MaterialUtils.SetPhysicalSizePositionOffset(material, newOffset);
}
}
EditorGUI.indentLevel--;
}
#endregion Physical size
#region Input draw
/// <summary>
/// Draws substance file inputs.
/// </summary>
/// <param name="serializeObject">True if object properties have changed.</param>
/// <param name="renderGraph">True if substance graph must be re rendered.</param>
private void DrawInputs(out bool serializeObject, out bool renderGraph)
{
renderGraph = false;
serializeObject = false;
EditorGUILayout.Space();
if (DrawGrouplessInputs(_inputGroupingHelper.GrouplessInputs))
{
renderGraph = true;
serializeObject = true;
}
EditorGUILayout.Space();
if (PhysicalSizeExtension.IsSupported())
{
if (DrawPhysicalSize())
{
renderGraph = true;
serializeObject = true;
MaterialUtils.ApplyPhysicalSize(_target.OutputMaterial, _physicalSizelProperty.vector3Value, _enablePhysicalSizeProperty.boolValue);
UpdateGraphMaterialLabel();
}
}
EditorGUILayout.Space();
foreach (var groupInfo in _inputGroupingHelper.InputGroups)
{
if (DrawInputGroup(groupInfo))
{
renderGraph = true;
serializeObject = true;
}
EditorGUILayout.Space();
}
}
/// <summary>
/// Draws the inputs that are not part of any input group.
/// </summary>
/// <param name="inputsInfo">Inputs info</param>
/// <returns>True if any input has changed.</returns>
private bool DrawGrouplessInputs(SubstanceInputGroupCachedInfo inputsInfo)
{
var indexArray = inputsInfo.Inputs;
bool changed = false;
for (int i = 0; i < indexArray.Count; i++)
{
var property = indexArray[i].InputProperty;
var guiContent = indexArray[i].GUIContent;
var index = indexArray[i].Index;
if (_nativeGraph.IsInputVisible(index))
{
if (SubstanceInputDrawer.DrawInput(property, guiContent, _nativeGraph, index))
changed = true;
}
}
return changed;
}
/// <summary>
/// Draws inputs from a input group.
/// </summary>
/// <param name="groupInfo"></param>
/// <returns></returns>
private bool DrawInputGroup(SubstanceInputGroupCachedInfo groupInfo)
{
var groupName = groupInfo.Name;
var indexArray = groupInfo.Inputs;
groupInfo.ShowGroup = EditorGUILayout.Foldout(groupInfo.ShowGroup, groupName);
if (!groupInfo.ShowGroup)
return false;
bool changed = false;
for (int i = 0; i < indexArray.Count; i++)
{
EditorGUI.indentLevel++;
var property = indexArray[i].InputProperty;
var guiContent = indexArray[i].GUIContent;
var index = indexArray[i].Index;
if (_nativeGraph.IsInputVisible(index))
{
if (SubstanceInputDrawer.DrawInput(property, guiContent, _nativeGraph, index))
changed = true;
}
EditorGUI.indentLevel--;
}
return changed;
}
#endregion Input draw
#region Output draw
private static readonly GUIContent _GeneratedTextureGUI = new GUIContent();
private bool DrawGeneratedTextures(SerializedProperty outputList, bool generateAllTextures)
{
bool valueChanged = false;
EditorGUILayout.Space(4);
using (var scrollViewScope = new EditorGUILayout.ScrollViewScope(_textureOutputScrollView, false, false))
{
scrollViewScope.handleScrollWheel = false;
_textureOutputScrollView = scrollViewScope.scrollPosition;
EditorGUILayout.BeginHorizontal();
{
var outputsCount = outputList.arraySize;
for (int i = 0; i < outputsCount; i++)
{
var outputProperty = outputList.GetArrayElementAtIndex(i);
var outputTexture = _target.Output[i];
if (generateAllTextures || outputTexture.IsStandardOutput(_target.OutputMaterial))
valueChanged |= DrawOutputTexture(outputProperty, _GeneratedTextureGUI, outputTexture);
}
}
EditorGUILayout.EndHorizontal();
}
return valueChanged;
}
private bool DrawOutputTexture(SerializedProperty output, GUIContent content, SubstanceOutputTexture substanceOutput)
{
var valueChanged = false;
EditorGUILayout.BeginVertical(GUILayout.Width(120));
{
var texture = output.FindPropertyRelative("OutputTexture").objectReferenceValue as Texture2D;
var label = output.FindPropertyRelative("Description.Channel").stringValue;
var sRGB = output.FindPropertyRelative("sRGB");
var alpha = output.FindPropertyRelative("AlphaChannel");
var inverAlpha = output.FindPropertyRelative("InvertAssignedAlpha");
var isAlphaAssignable = output.FindPropertyRelative("IsAlphaAssignable").boolValue;
//Draw texture preview.
if (texture != null)
{
if (texture != null)
{
content.text = null;
var thumbnail = EditorUtility.IsDirty(texture) ? AssetPreview.GetMiniThumbnail(texture) : AssetPreview.GetAssetPreview(texture);
if (thumbnail == null)
thumbnail = AssetPreview.GetAssetPreview(texture);
content.image = thumbnail;
content.tooltip = texture.name;
if (GUILayout.Button(content, //style,
GUILayout.Width(70),
GUILayout.Height(70)))
{
// Highlight object in project browser:
EditorGUIUtility.PingObject(texture);
}
}
}
GUILayout.Label(label);
if (substanceOutput.IsBaseColor() || substanceOutput.IsDiffuse() || substanceOutput.IsEmissive())
{
var oldsRGB = sRGB.boolValue;
var newsRGB = GUILayout.Toggle(oldsRGB, "sRGB");
if (newsRGB != oldsRGB)
{
sRGB.boolValue = newsRGB;
valueChanged = true;
}
}
//Draw alpha remapping.
EditorGUILayout.BeginHorizontal(GUILayout.Width(80), GUILayout.Height(EditorGUIUtility.singleLineHeight));
{
if (isAlphaAssignable)
{
var option = _outputChannelsHelper.GetAlphaChannels(label);
var index = 0;
if (!string.IsNullOrEmpty(alpha.stringValue))
index = Array.IndexOf(option, alpha.stringValue);
EditorGUILayout.LabelField("A", GUILayout.Width(10));
var newIndex = EditorGUILayout.Popup(index, option, GUILayout.Width(70));
if (newIndex != index)
{
alpha.stringValue = newIndex != 0 ? option[newIndex] : string.Empty;
valueChanged = true;
}
}
}
EditorGUILayout.EndHorizontal();
//Draw inver alpha.
EditorGUILayout.BeginHorizontal(GUILayout.Width(80), GUILayout.Height(EditorGUIUtility.singleLineHeight));
{
if (!string.IsNullOrEmpty(alpha.stringValue))
{
var oldValue = inverAlpha.boolValue;
var newValue = GUILayout.Toggle(oldValue, "Invert alpha");
if (newValue != oldValue)
{
inverAlpha.boolValue = newValue;
valueChanged = true;
}
}
}
EditorGUILayout.EndHorizontal();
}
EditorGUILayout.EndVertical();
return valueChanged;
}
private static bool _showAdvanceSettings = false;
private readonly List<string> _shaderInputTextures = new List<string>();
private string _shaderName = string.Empty;
private bool DrawAdvanceSettings()
{
EditorGUILayout.Space();
EditorGUI.indentLevel++;
_showAdvanceSettings = EditorGUILayout.Foldout(_showAdvanceSettings, "Output Textures Mapping");
EditorGUILayout.Space();
bool result = false;
if (_showAdvanceSettings)
{
EditorGUI.indentLevel++;
if (_target.OutputMaterial != null)
{
if (!_shaderName.Equals(_target.OutputMaterial.shader.name, StringComparison.OrdinalIgnoreCase))
{
_shaderInputTextures.Clear();
GetShaderInputTextures(_target.OutputMaterial.shader);
}
for (int i = 0; i < _outputProperties.Count; i++)
{
EditorGUILayout.BeginHorizontal();
GUILayout.TextField(_target.Output[i].Description.Channel, GUILayout.Width(200));
var oldstring = _outputProperties[i].stringValue;
var oldSelected = _shaderInputTextures.FindIndex(0, _shaderInputTextures.Count, (value) => { return oldstring.Equals(value, StringComparison.OrdinalIgnoreCase); });
if (oldSelected == -1)
oldSelected = 0;
var newSelectedIndex = EditorGUILayout.Popup("", oldSelected, _shaderInputTextures.ToArray());
if (newSelectedIndex != oldSelected)
{
result = true;
var updateString = newSelectedIndex == 0 ? string.Empty : _shaderInputTextures[newSelectedIndex];
_outputProperties[i].stringValue = updateString;
MaterialUtils.UpdateTextureTarget(_target.OutputMaterial, _target.Output[i].OutputTexture, oldstring, updateString);
//Clean other outputs that have the same assignment.
for (int j = 0; j < _outputProperties.Count; j++)
{
if (string.Equals(_outputProperties[j].stringValue, updateString) && i != j)
{
_outputProperties[j].stringValue = string.Empty;
}
}
}
EditorGUILayout.EndHorizontal();
}
}
EditorGUI.indentLevel--;
}
EditorGUI.indentLevel--;
return result;
}
#endregion Output draw
#region Presets draw
private static readonly GUIContent _PresetExportGUIContent = new GUIContent("Export Preset...", "Save Preset");
private static readonly GUIContent _PresetImportGUIContent = new GUIContent("Import Preset...", "Fetch Preset");
private static readonly GUIContent _PresetResetGUIContent = new GUIContent("Reset Preset to Default", "Restore input defaults");
private void DrawPresentExport(SubstanceGraphSO graph)
{
int labelWidth = (int)EditorGUIUtility.labelWidth - 15;
_showExportPresentationHandler = EditorGUILayout.Foldout(_showExportPresentationHandler, "Preset Handling", true);
if (_showExportPresentationHandler)
{
EditorGUILayout.BeginHorizontal();
{
EditorGUILayout.LabelField(" ", GUILayout.Width(labelWidth)); // Used to position the next button
if (GUILayout.Button(_PresetExportGUIContent))
HandleExportPresets(graph);
}
EditorGUILayout.EndHorizontal();
EditorGUILayout.BeginHorizontal();
{
EditorGUILayout.LabelField(" ", GUILayout.Width(labelWidth)); // Used to position the next button
if (GUILayout.Button(_PresetImportGUIContent))
HandleImportPresets(graph);
}
EditorGUILayout.EndHorizontal();
GUILayout.Space(6);
EditorGUILayout.BeginHorizontal();
{
EditorGUILayout.LabelField(" ", GUILayout.Width(labelWidth)); // Used to position the next button
if (GUILayout.Button(_PresetResetGUIContent))
HandleResetPresets(graph);
}
EditorGUILayout.EndHorizontal();
}
}
private void HandleExportPresets(SubstanceGraphSO graph)
{
string savePath = EditorUtility.SaveFilePanel("Save Preset as...", graph.AssetPath, graph.GetAssetFileName(), "sbsprs");
if (savePath != "")
{
string savePreset = "<sbspresets count=\"1\" formatversion=\"1.1\">\n "; //formatting line needed by other integrations
savePreset += SubstanceEditorEngine.instance.ExportGraphPresetXML(graph);
savePreset += "</sbspresets>";
File.WriteAllText(savePath, savePreset);
}
}
private void HandleImportPresets(SubstanceGraphSO graph)
{
string loadPath = EditorUtility.OpenFilePanel("Select Preset", graph.AssetPath, "sbsprs");
if (loadPath != "")
{
string presetFile = System.IO.File.ReadAllText(loadPath);
int startIndex = presetFile.IndexOf("<sbspreset ");
int endIndex = presetFile.IndexOf("sbspreset>") + 10;
var presetXML = presetFile.Substring(startIndex, endIndex - startIndex);
SubstanceEditorEngine.instance.LoadPresetsToGraph(graph, presetXML);
}
}
private void HandleResetPresets(SubstanceGraphSO graph)
{
SubstanceEditorEngine.instance.LoadPresetsToGraph(graph, graph.DefaultPreset);
}
#endregion Presets draw
#region Thumbnail preview
public override Texture2D RenderStaticPreview(string assetPath, UnityEngine.Object[] subAssets, int width, int height)
{
if (_target.HasThumbnail)
return _target.GetThumbnailTexture();
var icon = UnityPackageInfo.GetSubstanceIcon(width, height);
if (icon != null)
{
Texture2D tex = new Texture2D(width, height);
EditorUtility.CopySerialized(icon, tex);
return tex;
}
return base.RenderStaticPreview(assetPath, subAssets, width, height);
}
#endregion Thumbnail preview
#endregion Draw
#region Scene Drag
public void OnSceneDrag(SceneView sceneView, int index)
{
Event evt = Event.current;
if (evt.type == EventType.Repaint)
return;
var materialIndex = -1;
var go = HandleUtility.PickGameObject(evt.mousePosition, out materialIndex);
if (_target.OutputMaterial != null)
{
if (go && go.GetComponent<Renderer>())
{
HandleRenderer(go.GetComponent<Renderer>(), materialIndex, _target.OutputMaterial, evt.type, evt.alt);
EditorSceneManager.MarkSceneDirty(EditorSceneManager.GetActiveScene());
if (_target.IsRuntimeOnly)
{
var runtimeComponent = go.GetComponent<Adobe.Substance.Runtime.SubstanceRuntimeGraph>();
if (runtimeComponent == null)
runtimeComponent = go.AddComponent<Adobe.Substance.Runtime.SubstanceRuntimeGraph>();
runtimeComponent.AttachGraph(_target);
}
}
}
}
internal static void HandleRenderer(Renderer r, int materialIndex, Material dragMaterial, EventType eventType, bool alt)
{
var applyMaterial = false;
switch (eventType)
{
case EventType.DragUpdated:
DragAndDrop.visualMode = DragAndDropVisualMode.Copy;
applyMaterial = true;
break;
case EventType.DragPerform:
DragAndDrop.AcceptDrag();
applyMaterial = true;
break;
}
if (applyMaterial)
{
var materials = r.sharedMaterials;
bool isValidMaterialIndex = (materialIndex >= 0 && materialIndex < r.sharedMaterials.Length);
if (!alt && isValidMaterialIndex)
{
materials[materialIndex] = dragMaterial;
}
else
{
for (int q = 0; q < materials.Length; ++q)
materials[q] = dragMaterial;
}
r.sharedMaterials = materials;
}
}
#endregion Scene Drag
#region Utilities
private void SaveTGAFiles()
{
if (_target == null)
return;
if (_target.IsRuntimeOnly)
return;
_target.OutputRemaped = true;
_target.RenderTextures = true;
EditorUtility.SetDirty(_target);
}
private Rect DrawHighlightBox(float width, float height, float xPadding)
{
float bx, by, bw, bh;
bx = xPadding;
by = GetPosition();
bw = width - xPadding;
bh = height;
var boxRect = new Rect(bx, by, bw, bh);
var backgroundStyle = new GUIStyle();
backgroundStyle.normal.background = _backgroundImage;
GUI.Box(boxRect, GUIContent.none, backgroundStyle);
return boxRect;
}
private int GetPosition()
{
Rect rect = GUILayoutUtility.GetLastRect();
if ((rect.x != 0) || (rect.y != 0))
lastRect = rect;
return (int)lastRect.y;
}
/// This is a workaround a bug in the Unity asset database for generating materials previews.
/// It basically generated a previews image whenever a property changes in the material, but it is now considering changes in the
/// textures assign to the material itself. By adding a random label we ensure that the asset preview image will be updated.
private void UpdateGraphMaterialLabel()
{
if (_target == null)
return;
const string tagPrefix = "sb_";
var material = _target.OutputMaterial;
if (material != null)
{
var labels = AssetDatabase.GetLabels(material);
var newLabels = labels.Where(a => !a.Contains(tagPrefix)).ToList();
newLabels.Add($"{tagPrefix}{Guid.NewGuid().ToString("N")}");
AssetDatabase.SetLabels(material, newLabels.ToArray());
}
}
#endregion Utilities
/// Work around Unity SerializedObjectNotCreatableException during script compilation.
private bool IsSerializedObjectReady()
{
try
{
if (serializedObject.targetObject == null)
return false;
}
catch (Exception)
{
return false;
}
return true;
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 94692ae502378e0469eba20cb87193c4
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 09746064b4df14847928695cb7c68c07
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,66 @@
using Adobe.Substance;
using System.IO;
using UnityEditor;
using UnityEngine;
namespace Adobe.SubstanceEditor
{
/// <summary>
/// General utilites for material creating and output texture assignment.
/// </summary>
internal static class AssetCreationUtils
{
/// <summary>
/// Creates a Unity material and set its textures according to the currently in use Unity render pipeline.//
/// </summary>
/// <param name="graph"></param>
/// <returns></returns>
public static void CreateMaterialOrUpdateMaterial(SubstanceGraphSO graph, string instanceName)
{
var materialOutput = graph.GetAssociatedAssetPath($"{instanceName}_material", "mat");
var oldMaterial = AssetDatabase.LoadAssetAtPath<Material>(materialOutput);
if (oldMaterial != null)
{
graph.OutputMaterial = oldMaterial;
}
bool createMaterial = graph.OutputMaterial == null;
if (createMaterial)
{
graph.OutputMaterial = new Material(MaterialUtils.GetStandardShader())
{
name = Path.GetFileNameWithoutExtension(materialOutput)
};
}
MaterialUtils.AssignOutputTexturesToMaterial(graph);
if (createMaterial)
AssetDatabase.CreateAsset(graph.OutputMaterial, materialOutput);
else
EditorUtility.SetDirty(graph.OutputMaterial);
graph.MaterialShader = graph.OutputMaterial.shader.name;
}
public static void UpdateMeterialAssignment(SubstanceGraphSO graph)
{
graph.MaterialShader = graph.OutputMaterial.shader.name;
foreach (var output in graph.Output)
{
if (!output.IsStandardOutput(graph.OutputMaterial) && (!graph.GenerateAllOutputs))
{
var texturePath = AssetDatabase.GetAssetPath(output.OutputTexture);
if (File.Exists(texturePath))
AssetDatabase.DeleteAsset(texturePath);
output.OutputTexture = null;
}
}
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: dc9df0d9c52231245aa194e418a9c115
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,67 @@
using System.Collections;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
namespace Adobe.SubstanceEditor
{
/// <summary>
/// Static class with utility methods commonly used to draw substance properties and UI.
/// </summary>
internal static class EditorDrawUtilities
{
private static readonly string[] _resolutions = { "256", "512", "1024", "2048", "4096" };
public static void DrawResolutionSelection(SerializedProperty property, GUIContent content, params GUILayoutOption[] options)
{
Vector2Int oldValue = property.vector2IntValue;
var currentIndex = GetEnumIndex(oldValue);
int newIndex = EditorGUILayout.Popup(content, currentIndex, _resolutions, options);
if (currentIndex != newIndex)
{
Vector2Int newValue = GetValueFromIndex(newIndex);
property.vector2IntValue = newValue;
}
}
private static int GetEnumIndex(Vector2Int data)
{
switch (data.x)
{
case 8:
return 0;
case 9:
return 1;
case 10:
return 2;
case 11:
return 3;
case 12:
return 4;
default:
return 0;
}
}
private static Vector2Int GetValueFromIndex(int index)
{
switch (index)
{
case 0: return new Vector2Int(8, 8);
case 1: return new Vector2Int(9, 9);
case 2: return new Vector2Int(10, 10);
case 3: return new Vector2Int(11, 11);
case 4: return new Vector2Int(12, 12);
default:
return new Vector2Int(8, 8);
}
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 7fb287e26604e6d4493b8ad922d4b790
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,353 @@
using Adobe.Substance;
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using UnityEditor;
using UnityEngine;
namespace Adobe.SubstanceEditor
{
internal static class EditorTools
{
/// <summary>
/// Makes an object editable. (Usefull for object managed by Importers)
/// </summary>
/// <param name="pObject"></param>
public static void OverrideReadOnlyFlag(UnityEngine.Object unityObject)
{
unityObject.hideFlags &= ~HideFlags.NotEditable;
}
public static SubstanceGraphSO CreateSubstanceInstance(string assetPath, SubstanceFileRawData fileData, string name, int index, string guid, bool isRoot = false, SubstanceGraphSO copy = null)
{
var instanceAsset = ScriptableObject.CreateInstance<SubstanceGraphSO>();
instanceAsset.AssetPath = assetPath;
instanceAsset.RawData = fileData;
instanceAsset.Name = name;
instanceAsset.IsRoot = isRoot;
instanceAsset.GUID = guid;
instanceAsset.OutputPath = CreateGraphFolder(assetPath, name);
instanceAsset.SetNativeID(index);
instanceAsset.GenerateAllMipmaps = true;
var instancePath = MakeRootGraphAssetPath(instanceAsset);
SubstanceEditorEngine.instance.InitializeInstance(instanceAsset, instancePath, out SubstanceGraphSO _);
SubstanceEditorEngine.instance.CreateGraphObject(instanceAsset, copy);
AssetDatabase.CreateAsset(instanceAsset, instancePath);
return instanceAsset;
}
public static void UpdateSubstanceInstance(SubstanceGraphSO instanceAsset, SubstanceFileRawData newFileData)
{
instanceAsset.RawData = newFileData;
var inputState = SubstanceEditorEngine.instance.SerializeCurrentState(instanceAsset);
SubstanceEditorEngine.instance.ReleaseInstance(instanceAsset);
SubstanceEditorEngine.instance.InitializeInstance(instanceAsset, null, out SubstanceGraphSO _);
SubstanceEditorEngine.instance.SetStateFromSerializedData(instanceAsset, inputState);
SubstanceEditorEngine.instance.CreateGraphObject(instanceAsset, null);
}
public class SubstanceInstanceInfo
{
public string Name { get; set; }
public int Index { get; set; }
public string GUID { get; set; }
public bool IsRoot { get; set; }
public SubstanceInstanceInfo()
{
}
}
public static void CreateSubstanceInstanceAsync(string assetPath, SubstanceFileRawData fileData, IEnumerable<SubstanceInstanceInfo> infos)
{
var instances = new List<Tuple<SubstanceGraphSO, string>>();
foreach (var item in infos)
{
var instanceAsset = ScriptableObject.CreateInstance<SubstanceGraphSO>();
instanceAsset.AssetPath = assetPath;
instanceAsset.RawData = fileData;
instanceAsset.Name = item.Name;
instanceAsset.IsRoot = item.IsRoot;
instanceAsset.GUID = item.GUID;
instanceAsset.OutputPath = CreateGraphFolder(assetPath, item.Name);
instanceAsset.SetNativeID(item.Index);
instanceAsset.GenerateAllMipmaps = true;
var instancePath = MakeRootGraphAssetPath(instanceAsset);
SubstanceEditorEngine.instance.InitializeInstance(instanceAsset, instancePath, out SubstanceGraphSO matchingInstance);
SubstanceEditorEngine.instance.CreateGraphObject(instanceAsset, matchingInstance);
instances.Add(new Tuple<SubstanceGraphSO, string>(instanceAsset, instancePath));
}
foreach (var instance in instances)
{
SubstanceEditorEngine.instance.DelayAssetCreation(instance.Item1, instance.Item2);
}
}
public static void Rename(this SubstanceGraphSO substanceMaterial, string name)
{
var oldFolder = substanceMaterial.OutputPath;
if (substanceMaterial.Name == name)
return;
substanceMaterial.Name = name;
var dir = Path.GetDirectoryName(substanceMaterial.AssetPath);
var assetName = Path.GetFileNameWithoutExtension(substanceMaterial.AssetPath);
var newFolder = Path.Combine(dir, $"{assetName}_{name}");
substanceMaterial.OutputPath = newFolder;
FileUtil.MoveFileOrDirectory(oldFolder, substanceMaterial.OutputPath);
File.Delete($"{oldFolder}.meta");
EditorUtility.SetDirty(substanceMaterial);
AssetDatabase.Refresh();
var oldPath = AssetDatabase.GetAssetPath(substanceMaterial);
var error = AssetDatabase.RenameAsset(oldPath, $"{name}.asset");
if (!string.IsNullOrEmpty(error))
Debug.LogError(error);
var materialOldName = AssetDatabase.GetAssetPath(substanceMaterial.OutputMaterial);
var materialNewName = Path.GetFileName(substanceMaterial.GetAssociatedAssetPath($"{name}_material", "mat"));
error = AssetDatabase.RenameAsset(materialOldName, materialNewName);
EditorUtility.SetDirty(substanceMaterial.OutputMaterial);
if (!string.IsNullOrEmpty(error))
Debug.LogError(error);
AssetDatabase.Refresh();
}
public static void Move(this SubstanceGraphSO substanceMaterial, string to)
{
substanceMaterial.OutputPath = Path.GetDirectoryName(to);
var oldMaterialPath = AssetDatabase.GetAssetPath(substanceMaterial.OutputMaterial);
AssetDatabase.MoveAsset(oldMaterialPath, Path.Combine(substanceMaterial.OutputPath, Path.GetFileName(oldMaterialPath)));
foreach (var output in substanceMaterial.Output)
{
var textureAssetPath = AssetDatabase.GetAssetPath(output.OutputTexture);
var textureFileName = Path.GetFileName(textureAssetPath);
var newTexturePath = Path.Combine(substanceMaterial.OutputPath, textureFileName);
AssetDatabase.MoveAsset(textureAssetPath, newTexturePath);
}
EditorUtility.SetDirty(substanceMaterial);
AssetDatabase.Refresh();
}
private static string CreateGraphFolder(string assetPath, string graphName)
{
var dir = Path.GetDirectoryName(assetPath);
var assetName = Path.GetFileNameWithoutExtension(assetPath);
var newFolder = Path.Combine(dir, $"{assetName}_{graphName}");
if (Directory.Exists(newFolder))
return newFolder;
string guid = AssetDatabase.CreateFolder(dir, $"{assetName}_{graphName}");
return AssetDatabase.GUIDToAssetPath(guid);
}
private static string MakeRootGraphAssetPath(SubstanceGraphSO substanceMaterial)
{
return Path.Combine(substanceMaterial.OutputPath, $"{substanceMaterial.Name}.asset");
}
}
public static class SubstanceEditorTools
{
public static void SetGraphFloatInput(SubstanceGraphSO graph, int inputId, float value)
{
var so = new SerializedObject(graph);
var graphInputs = so.FindProperty("Input");
var graphInput = graphInputs.GetArrayElementAtIndex(inputId);
var dataProp = graphInput.FindPropertyRelative("Data");
dataProp.floatValue = value;
so.ApplyModifiedProperties();
UpdateNativeInput(graph, inputId);
}
public static void SetGraphFloat2Input(SubstanceGraphSO graph, int inputId, Vector2 value)
{
var so = new SerializedObject(graph);
var graphInputs = so.FindProperty("Input");
var graphInput = graphInputs.GetArrayElementAtIndex(inputId);
var dataProp = graphInput.FindPropertyRelative("Data");
dataProp.vector2Value = value;
so.ApplyModifiedProperties();
UpdateNativeInput(graph, inputId);
}
public static void SetGraphFloat3Input(SubstanceGraphSO graph, int inputId, Vector3 value)
{
var so = new SerializedObject(graph);
var graphInputs = so.FindProperty("Input");
var graphInput = graphInputs.GetArrayElementAtIndex(inputId);
var dataProp = graphInput.FindPropertyRelative("Data");
dataProp.vector3Value = value;
so.ApplyModifiedProperties();
UpdateNativeInput(graph, inputId);
}
public static void SetGraphFloat4Input(SubstanceGraphSO graph, int inputId, Vector3 value)
{
var so = new SerializedObject(graph);
var graphInputs = so.FindProperty("Input");
var graphInput = graphInputs.GetArrayElementAtIndex(inputId);
var dataProp = graphInput.FindPropertyRelative("Data");
dataProp.vector4Value = value;
so.ApplyModifiedProperties();
UpdateNativeInput(graph, inputId);
}
public static void SetGraphIntInput(SubstanceGraphSO graph, int inputId, int value)
{
var so = new SerializedObject(graph);
var graphInputs = so.FindProperty("Input");
var graphInput = graphInputs.GetArrayElementAtIndex(inputId);
var dataProp = graphInput.FindPropertyRelative("Data");
dataProp.intValue = value;
so.ApplyModifiedProperties();
UpdateNativeInput(graph, inputId);
}
public static void SetGraphInt2Input(SubstanceGraphSO graph, int inputId, Vector2Int value)
{
var so = new SerializedObject(graph);
var graphInputs = so.FindProperty("Input");
var graphInput = graphInputs.GetArrayElementAtIndex(inputId);
var dataProp = graphInput.FindPropertyRelative("Data");
dataProp.vector2IntValue = value;
so.ApplyModifiedProperties();
UpdateNativeInput(graph, inputId);
}
public static void SetGraphInt3Input(SubstanceGraphSO graph, int inputId, Vector3Int value)
{
var so = new SerializedObject(graph);
var graphInputs = so.FindProperty("Input");
var graphInput = graphInputs.GetArrayElementAtIndex(inputId);
var dataProp = graphInput.FindPropertyRelative("Data");
dataProp.vector3IntValue = value;
so.ApplyModifiedProperties();
UpdateNativeInput(graph, inputId);
}
public static void SetGraphInt4Input(SubstanceGraphSO graph, int inputId, int value0, int value1, int value2, int value3)
{
var so = new SerializedObject(graph);
var graphInputs = so.FindProperty("Input");
var graphInput = graphInputs.GetArrayElementAtIndex(inputId);
var dataProp0 = graphInput.FindPropertyRelative("Data0");
var dataProp1 = graphInput.FindPropertyRelative("Data1");
var dataProp2 = graphInput.FindPropertyRelative("Data2");
var dataProp3 = graphInput.FindPropertyRelative("Data3");
dataProp0.intValue = value0;
dataProp1.intValue = value1;
dataProp2.intValue = value2;
dataProp3.intValue = value3;
so.ApplyModifiedProperties();
UpdateNativeInput(graph, inputId);
}
public static void SetGraphInputString(SubstanceGraphSO graph, int inputId, string value)
{
var so = new SerializedObject(graph);
var graphInputs = so.FindProperty("Input");
var graphInput = graphInputs.GetArrayElementAtIndex(inputId);
var dataProp = graphInput.FindPropertyRelative("Data");
dataProp.stringValue = value;
so.ApplyModifiedProperties();
UpdateNativeInput(graph, inputId);
}
public static void SetGraphInputTexture(SubstanceGraphSO graph, int inputId, Texture2D value)
{
var so = new SerializedObject(graph);
var graphInputs = so.FindProperty("Input");
var graphInput = graphInputs.GetArrayElementAtIndex(inputId);
var dataProp = graphInput.FindPropertyRelative("Data");
dataProp.objectReferenceValue = value;
so.ApplyModifiedProperties();
UpdateNativeInput(graph, inputId);
}
private static void UpdateNativeInput(SubstanceGraphSO graph, int inputId)
{
if (!SubstanceEditorEngine.instance.TryGetHandlerFromInstance(graph, out SubstanceNativeGraph _nativeGraph))
{
if (!SubstanceEditorEngine.instance.IsInitialized)
return;
SubstanceEditorEngine.instance.InitializeInstance(graph, null, out SubstanceGraphSO _);
}
if (SubstanceEditorEngine.instance.TryGetHandlerFromInstance(graph, out _nativeGraph))
graph.RuntimeInitialize(_nativeGraph, graph.IsRuntimeOnly);
graph.Input[inputId].UpdateNativeHandle(_nativeGraph);
}
public static void RenderGraph(SubstanceGraphSO graph)
{
if (!SubstanceEditorEngine.instance.TryGetHandlerFromInstance(graph, out SubstanceNativeGraph _nativeGraph))
{
if (!SubstanceEditorEngine.instance.IsInitialized)
return;
SubstanceEditorEngine.instance.InitializeInstance(graph, null, out SubstanceGraphSO _);
}
if (SubstanceEditorEngine.instance.TryGetHandlerFromInstance(graph, out _nativeGraph))
{
SubstanceEditorEngine.instance.SubmitAsyncRenderWork(_nativeGraph, graph);
}
}
public static string CreatePresetFromCurrentState(SubstanceGraphSO graph)
{
if (!SubstanceEditorEngine.instance.TryGetHandlerFromInstance(graph, out SubstanceNativeGraph _nativeGraph))
{
if (!SubstanceEditorEngine.instance.IsInitialized)
return string.Empty;
SubstanceEditorEngine.instance.InitializeInstance(graph, null, out SubstanceGraphSO _);
}
if (SubstanceEditorEngine.instance.TryGetHandlerFromInstance(graph, out _nativeGraph))
graph.RuntimeInitialize(_nativeGraph, graph.IsRuntimeOnly);
return _nativeGraph.CreatePresetFromCurrentState();
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 9501269c4defb994083f07343a9de616
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,165 @@
using Adobe.Substance;
using Adobe.Substance.Input.Description;
using System.Collections.Generic;
using System.Linq;
using UnityEditor;
namespace Adobe.SubstanceEditor
{
/// <summary>
/// Cached info for a substance input. Allow drawing UI without having to query description values from the SerializedProperty object.
/// </summary>
internal class SubstanceInputCachedInfo
{
/// <summary>
/// Input serialized property.
/// </summary>
public SerializedProperty InputProperty { get; }
/// <summary>
/// GUIContent for the drawing the input.
/// </summary>
public SubstanceInputGUIContent GUIContent { get; }
public int Index { get; }
public SubstanceInputCachedInfo(SerializedProperty inputProperty, SubstanceInputGUIContent GUIContent, int index)
{
this.InputProperty = inputProperty;
this.GUIContent = GUIContent;
Index = index;
}
}
internal class SubstanceInputGroupCachedInfo
{
public string Name { get; }
public List<SubstanceInputCachedInfo> Inputs { get; }
public bool ShowGroup { get; set; }
public SubstanceInputGroupCachedInfo(string groupName)
{
Name = groupName;
Inputs = new List<SubstanceInputCachedInfo>();
ShowGroup = false;
}
}
/// <summary>
/// Helper class for caching grouping information for inputs so we don't have to query them every UI draw.
/// </summary>
internal class GraphInputsGroupingHelper
{
/// <summary>
/// Readonly list with all input groups and their elements info.
/// </summary>
public IReadOnlyList<SubstanceInputGroupCachedInfo> InputGroups { get; }
/// <summary>
/// Groups with inputs that don't have grouping information.
/// </summary>
public SubstanceInputGroupCachedInfo GrouplessInputs { get; }
public GraphInputsGroupingHelper(SubstanceGraphSO graph, SerializedObject targetObject)
{
var GUIgroups = new List<SubstanceInputGroupCachedInfo>();
var graphInputs = targetObject.FindProperty("Input");
var groups = graph.Input.Select(a => a.Description.GuiGroup).Distinct();
foreach (var group in groups)
{
var groupInfo = new SubstanceInputGroupCachedInfo(group);
for (int i = 0; i < graph.Input.Count; i++)
{
var target = graph.Input[i];
if (target.Description.GuiGroup == group)
{
SubstanceInputGUIContent guiContent;
var graphInput = graphInputs.GetArrayElementAtIndex(i);
var dataProp = graphInput.FindPropertyRelative("Data");
target.TryGetNumericalDescription(out ISubstanceInputDescNumerical descNumerical);
switch (target.Description.Type)
{
case SubstanceValueType.Float:
guiContent = new SubstanceFloatGUIContent(target.Description, dataProp, descNumerical as SubstanceInputDescNumericalFloat);
break;
case SubstanceValueType.Float2:
guiContent = new SubstanceFloat2GUIContent(target.Description, dataProp, descNumerical as SubstanceInputDescNumericalFloat2);
break;
case SubstanceValueType.Float3:
guiContent = new SubstanceFloat3GUIContent(target.Description, dataProp, descNumerical as SubstanceInputDescNumericalFloat3);
break;
case SubstanceValueType.Float4:
guiContent = new SubstanceFloat4GUIContent(target.Description, dataProp, descNumerical as SubstanceInputDescNumericalFloat4);
break;
case SubstanceValueType.Int:
guiContent = (target.Description.WidgetType == SubstanceWidgetType.ComboBox) ? new SubstanceIntComboBoxGUIContent(target.Description, descNumerical as SubstanceInputDescNumericalInt, dataProp) : new SubstanceIntGUIContent(target.Description, dataProp, descNumerical as SubstanceInputDescNumericalInt);
break;
case SubstanceValueType.Int2:
guiContent = new SubstanceInt2GUIContent(target.Description, dataProp, descNumerical as SubstanceInputDescNumericalInt2);
break;
case SubstanceValueType.Int3:
guiContent = new SubstanceInt3GUIContent(target.Description, dataProp, descNumerical as SubstanceInputDescNumericalInt3);
break;
case SubstanceValueType.Int4:
guiContent = new SubstanceInt4GUIContent(target.Description, dataProp, descNumerical as SubstanceInputDescNumericalInt4);
break;
default:
guiContent = new SubstanceInputGUIContent(target.Description, dataProp);
break;
}
groupInfo.Inputs.Add(new SubstanceInputCachedInfo(graphInput, guiContent, i));
}
}
if (string.IsNullOrEmpty(groupInfo.Name))
{
if (GrouplessInputs == null)
GrouplessInputs = groupInfo;
else
GrouplessInputs.Inputs.AddRange(groupInfo.Inputs);
}
else
{
GUIgroups.Add(groupInfo);
}
}
InputGroups = GUIgroups;
}
}
internal class GraphOutputAlphaChannelsHelper
{
private readonly List<string> _channels;
public GraphOutputAlphaChannelsHelper(SubstanceGraphSO graph)
{
_channels = new List<string> { "source" };
foreach (var item in graph.Output.Where(a => a.IsAlphaAssignable).Select(b => b.Description.Label))
_channels.Add(item);
}
public string[] GetAlphaChannels(string label)
{
return _channels.Where(a => a != label).ToArray();
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 613ef46be9575f44fbcb243ab26b4db4
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,19 @@
using Adobe.Substance;
using System.IO;
namespace Adobe.SubstanceEditor
{
public static class NamingExtensions
{
public static string GetAssociatedAssetPath(this SubstanceGraphSO graph, string name, string extension)
{
var fileName = Path.GetFileNameWithoutExtension(graph.AssetPath);
return Path.Combine(graph.OutputPath, $"{fileName}_{name}.{extension}");
}
public static string GetAssetFileName(this SubstanceGraphSO graph)
{
return Path.GetFileNameWithoutExtension(graph.AssetPath);
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 5bf22d7a02c9e744483e24b116112bb8
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,68 @@
using UnityEditor;
using UnityEditor.Callbacks;
using System.IO;
using Adobe.Substance;
namespace Adobe.SubstanceEditor
{
public class SubstanceBuildUtils
{
[PostProcessBuildAttribute(1)]
public static void OnPostprocessBuild(BuildTarget target, string pathToBuiltProject)
{
if (target == BuildTarget.StandaloneLinux64)
OnPostprocessBuildLinux(pathToBuiltProject);
else if (target == BuildTarget.StandaloneOSX)
OnPostprocessBuildMac(pathToBuiltProject);
SubstanceEditorEngine.instance.RefreshActiveInstances();
}
private static void OnPostprocessBuildLinux(string pathToBuiltProject)
{
var dataPath = pathToBuiltProject.Replace(".x86_64", "_Data");
var pluginsPath = Path.Combine(dataPath, "Plugins");
if (!Directory.Exists(pluginsPath))
Directory.CreateDirectory(pluginsPath);
var enginePath = Path.GetDirectoryName(PlatformUtils.GetEnginePath());
string[] files = Directory.GetFiles(enginePath);
foreach (string s in files)
{
var extension = Path.GetExtension(s);
if (string.Equals(extension, ".so") || string.Equals(extension, ".1"))
{
var fileName = Path.GetFileName(s);
var testination = Path.Combine(pluginsPath, fileName);
File.Copy(s, testination, true);
}
}
}
private static void OnPostprocessBuildMac(string pathToBuiltProject)
{
var pluginsPath = Path.Combine(Path.Combine(pathToBuiltProject, "Contents"), "PlugIns");
if (!Directory.Exists(pluginsPath))
Directory.CreateDirectory(pluginsPath);
var enginePath = Path.GetDirectoryName(PlatformUtils.GetEnginePath());
string[] files = Directory.GetFiles(enginePath);
foreach (string s in files)
{
var extension = Path.GetExtension(s);
if (string.Equals(extension, ".dylib") || string.Equals(extension, ".1"))
{
var fileName = Path.GetFileName(s);
var testination = Path.Combine(pluginsPath, fileName);
File.Copy(s, testination, true);
}
}
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 18d63636c53acdc4680dd008426e63f6
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,39 @@
using Adobe.Substance;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using UnityEditor;
using UnityEngine;
namespace Adobe.SubstanceEditor
{
internal static class SubstanceFileExtensions
{
internal static List<SubstanceGraphSO> GetGraphs(this SubstanceFileSO fileSO)
{
var result = new List<SubstanceGraphSO>();
var path = AssetDatabase.GetAssetPath(fileSO);
string[] guids = AssetDatabase.FindAssets(string.Format("t:{0}", typeof(SubstanceGraphSO)));
for (int i = 0; i < guids.Length; i++)
{
string assetPath = AssetDatabase.GUIDToAssetPath(guids[i]);
SubstanceGraphSO graph = AssetDatabase.LoadAssetAtPath<SubstanceGraphSO>(assetPath);
if (graph != null)
{
var filePath = AssetDatabase.GetAssetPath(graph.RawData);
if (filePath.Equals(path, System.StringComparison.OrdinalIgnoreCase))
{
result.Add(graph);
}
}
}
return result;
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: d88a151702771984bb824b8639661be1
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,121 @@
using System.Collections;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
namespace Adobe.SubstanceEditor
{
internal static class TextureUtils
{
public static bool IsCompressed(this TextureFormat unityFormat)
{
switch (unityFormat)
{
case TextureFormat.RGBA32:
case TextureFormat.RGBA64:
case TextureFormat.RGB24:
case TextureFormat.BGRA32:
case TextureFormat.R8:
case TextureFormat.R16:
case TextureFormat.RFloat:
return false;
default:
return true;
}
}
public static Texture2D SetReadableFlag(Texture2D pTexture, bool pReadable)
{
Texture2D texture = pTexture;
if (pTexture == null)
return null;
string assetPath = AssetDatabase.GetAssetPath(pTexture);
var textureImporter = AssetImporter.GetAtPath(assetPath) as TextureImporter;
if (textureImporter != null)
{
if (textureImporter.isReadable == pReadable)
return pTexture;
textureImporter.isReadable = pReadable;
Debug.LogWarning(string.Format("Setting {0}'s 'Read/Write Enabled' flag to {1}",
pTexture.name, (pReadable ? "true" : "false")));
EditorUtility.SetDirty(textureImporter);
AssetDatabase.ImportAsset(assetPath);
AssetDatabase.Refresh();
texture = AssetDatabase.LoadMainAssetAtPath(assetPath) as Texture2D;
}
return texture;
}
public static Texture2D EnsureTextureCorrectness(Texture2D pTexture, bool ensureRGBA, bool enableMipMaps)
{
Texture2D texture = pTexture;
if (pTexture == null)
return null;
string assetPath = AssetDatabase.GetAssetPath(pTexture);
var textureImporter = AssetImporter.GetAtPath(assetPath) as TextureImporter;
if (textureImporter != null)
{
bool changed = false;
if (textureImporter.textureCompression != TextureImporterCompression.Uncompressed)
{
textureImporter.textureCompression = TextureImporterCompression.Uncompressed;
Debug.LogWarning(string.Format("Setting {0}'s 'Compression' flag to Uncompressed", pTexture.name));
changed = true;
}
if (textureImporter.isReadable != true)
{
textureImporter.isReadable = true;
Debug.LogWarning(string.Format("Setting {0}'s 'Read/Write Enabled' flag to {1}",
pTexture.name, (true ? "true" : "false")));
changed = true;
}
if (textureImporter.maxTextureSize < 4096)
{
textureImporter.maxTextureSize = 4096;
changed = true;
}
if (enableMipMaps != textureImporter.mipmapEnabled)
{
textureImporter.mipmapEnabled = enableMipMaps;
changed = true;
}
if (ensureRGBA)
{
var defaultSettings = textureImporter.GetDefaultPlatformTextureSettings();
if (defaultSettings.format != TextureImporterFormat.RGBA32)
{
defaultSettings.format = TextureImporterFormat.RGBA32;
textureImporter.SetPlatformTextureSettings(defaultSettings);
changed = true;
}
}
if (changed)
{
AssetDatabase.ImportAsset(assetPath);
texture = AssetDatabase.LoadMainAssetAtPath(assetPath) as Texture2D;
}
}
return texture;
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 3f50142101ff97a409b03240bd12d0b6
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 78d21cfe470a7ec4b9caa5d8b89c89b4
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,17 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 715e7c131b0ebdd48b77c2b2741d1dae, type: 3}
m_Name: SubstanceEditorSettings
m_EditorClassIdentifier:
_generateAllTexture: 0
_targetResolution: {x: 10, y: 10}
_targetTextureEngine: 0

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: e686586dac7adb5429a0ca6dd01af373
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:

Binary file not shown.

View File

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 30801c657a078c941a2391ec38ae57c4
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,14 @@
Adobe Substance 3D for Unity
=====
The Substance 3D plugin enables you to load, apply, and tweak Substance parametric materials directly in Unity. <br /><br />
Whether you are working on games, architectural visualization, virtual reality and or deploying across mobile, desktop or XR, Substance 3D delivers a unique experience with optimized features for enhanced productivity. <br/><br/>
**Work faster, be more productive**: Substance 3D parameters allow for real-time texture updates in editor or at runtime. <br/><br/>
Substance 3D for Unity contains the plugin for the **Substance Engine** <br/><br/>
Import and customize physically-based Substance materials created in Substance Designer with support for Unity Standard/Standard (specular) shader and HDRP. <br /><br />
Please refer to the HOWTO guide to get started using Substance 3D for Unity. More detailed information will be available soon on our official documentation. <br /><br />
Support or Questions? Email us at contact@substance3d.com. <br/><br/>
Known Issues:
- Height map does not render correctly when using scanned Substance data with fixed output size
- The documentation on https://substance3d.adobe.com/documentation/integrations/unity-170459323.html is outdated
- Currently there is no option to toggle between CPU and GPU

View File

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 9f965ac7ac1ee7d4da311dd8525582d2
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: e7d6d85ed61bf2a4f8f6fd772abefb65
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

Some files were not shown because too many files have changed in this diff Show More