2106 lines
72 KiB
C#
2106 lines
72 KiB
C#
// Amplify Impostors
|
|
// Copyright (c) Amplify Creations, Lda <info@amplify.pt>
|
|
|
|
//#define AI_DEBUG_MODE
|
|
|
|
using System;
|
|
using System.Runtime.InteropServices;
|
|
using System.Collections.Generic;
|
|
using UnityEngine;
|
|
using UnityEngine.Rendering;
|
|
using UnityEngine.Experimental.Rendering;
|
|
using Unity.Collections;
|
|
using System.IO;
|
|
|
|
#if UNITY_EDITOR
|
|
using UnityEditor;
|
|
using Unity.Collections.LowLevel.Unsafe;
|
|
#endif
|
|
|
|
namespace AmplifyImpostors
|
|
{
|
|
public enum LODReplacement
|
|
{
|
|
DoNothing = 0,
|
|
ReplaceCulled = 1,
|
|
ReplaceLast = 2,
|
|
ReplaceAllExceptFirst = 3,
|
|
ReplaceSpecific = 4,
|
|
ReplaceAfterSpecific = 5,
|
|
InsertAfter = 6
|
|
}
|
|
|
|
public enum CutMode
|
|
{
|
|
Automatic = 0,
|
|
Manual = 1
|
|
}
|
|
|
|
public enum FolderMode
|
|
{
|
|
RelativeToPrefab = 0,
|
|
Global = 1
|
|
}
|
|
|
|
public enum RenderPipelineInUse
|
|
{
|
|
None = 0,
|
|
HDRP = 1,
|
|
URP = 2,
|
|
Custom = 3
|
|
}
|
|
|
|
//[ExecuteInEditMode]
|
|
[HelpURL( "https://wiki.amplify.pt/index.php?title=Unity_Products:Amplify_Impostors/Manual" )]
|
|
public class AmplifyImpostor : MonoBehaviour
|
|
{
|
|
public const string DefaultPreset = "e4786beb7716da54dbb02a632681cc37";
|
|
|
|
public const string ShaderBiRP = "e82933f4c0eb9ba42aab0739f48efe21";
|
|
public const string ShaderOctaBiRP = "572f9be5706148142b8da6e9de53acdb";
|
|
|
|
public const string ShaderHDRP = "175c951fec709c44fa2f26b8ab78b8dd";
|
|
public const string ShaderOctaHDRP = "56236dc63ad9b7949b63a27f0ad180b3";
|
|
|
|
public const string ShaderURP = "da79d698f4bf0164e910ad798d07efdf";
|
|
public const string ShaderOctaURP = "83dd8de9a5c14874884f9012def4fdcc";
|
|
|
|
public const string DilateGUID = "57c23892d43bc9f458360024c5985405";
|
|
public const string PackerGUID = "31bd3cd74692f384a916d9d7ea87710d";
|
|
public const string GBufferToOutputGUID = "9587d58ea8f1dac478d1adbf2a63d31f";
|
|
|
|
private const string GlobalShaderVariablesQualifiedNameHDRP = "UnityEngine.Rendering.HighDefinition.ShaderVariablesGlobal, Unity.RenderPipelines.HighDefinition.Runtime, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null";
|
|
|
|
public readonly static int _DetailNormalMap_PID = Shader.PropertyToID( "_DetailNormalMap" );
|
|
|
|
[SerializeField]
|
|
private AmplifyImpostorAsset m_data;
|
|
public AmplifyImpostorAsset Data { get { return m_data; } set { m_data = value; } }
|
|
|
|
[SerializeField]
|
|
private Transform m_rootTransform;
|
|
public Transform RootTransform { get { return m_rootTransform; } set { m_rootTransform = value; } }
|
|
|
|
[SerializeField]
|
|
private LODGroup m_lodGroup;
|
|
public LODGroup LodGroup { get { return m_lodGroup; } set { m_lodGroup = value; } }
|
|
|
|
[SerializeField]
|
|
private Renderer[] m_renderers;
|
|
public Renderer[] Renderers { get { return m_renderers; } set { m_renderers = value; } }
|
|
|
|
public LODReplacement m_lodReplacement = LODReplacement.ReplaceLast;
|
|
|
|
[SerializeField]
|
|
public RenderPipelineInUse m_renderPipelineInUse = RenderPipelineInUse.None;
|
|
|
|
public int m_insertIndex = 1;
|
|
|
|
[SerializeField]
|
|
public GameObject m_lastImpostor;
|
|
|
|
[SerializeField]
|
|
public string m_folderPath;
|
|
|
|
[NonSerialized]
|
|
public string m_impostorName = string.Empty;
|
|
|
|
[SerializeField]
|
|
public CutMode m_cutMode = CutMode.Automatic;
|
|
|
|
[NonSerialized]
|
|
private const float StartXRotation = -90;
|
|
[NonSerialized]
|
|
private const float StartYRotation = 90;
|
|
[NonSerialized]
|
|
private const int MinAlphaResolution = 256;
|
|
[NonSerialized]
|
|
private RenderTexture[] m_rtGBuffers;
|
|
[NonSerialized]
|
|
private RenderTexture[] m_outBuffers;
|
|
[NonSerialized]
|
|
private RenderTexture[] m_alphaGBuffers;
|
|
[NonSerialized]
|
|
private RenderTexture m_trueDepth;
|
|
[NonSerialized]
|
|
public Texture2D m_alphaTex;
|
|
[NonSerialized]
|
|
private float m_xyFitSize = 0;
|
|
[NonSerialized]
|
|
private float m_depthFitSize = 0;
|
|
[NonSerialized]
|
|
private Vector2 m_pixelOffset = Vector2.zero;
|
|
|
|
[NonSerialized]
|
|
private Bounds m_originalBound = new Bounds();
|
|
|
|
[NonSerialized]
|
|
private Vector3 m_oriPos = Vector3.zero;
|
|
|
|
[NonSerialized]
|
|
private Quaternion m_oriRot = Quaternion.identity;
|
|
|
|
[NonSerialized]
|
|
private Vector3 m_oriSca = Vector3.one;
|
|
|
|
[NonSerialized]
|
|
private const int BlockSize = 65536;
|
|
|
|
#if UNITY_EDITOR
|
|
[NonSerialized]
|
|
private readonly string[] m_propertyNames = { "_Albedo", "_Normals", "_Specular", "_Occlusion", "_Emission", "_Position" };
|
|
|
|
[NonSerialized]
|
|
private string[] m_standardFileNamesOld = { string.Empty, string.Empty, string.Empty, string.Empty, string.Empty, string.Empty };
|
|
|
|
[NonSerialized]
|
|
private string[] m_standardFileNames = { string.Empty, string.Empty, string.Empty, string.Empty, string.Empty, string.Empty };
|
|
|
|
[NonSerialized]
|
|
private string[] m_fileNames = { string.Empty, string.Empty, string.Empty, string.Empty, string.Empty, string.Empty };
|
|
|
|
[Serializable]
|
|
private class TextureData
|
|
{
|
|
public bool SRGB = true;
|
|
public bool Alpha = true;
|
|
public TextureCompression Compression = TextureCompression.Normal;
|
|
public int MaxSize = -1;
|
|
public TextureData() { }
|
|
public TextureData( bool sRGB, TextureCompression compression, bool alpha, int maxSize )
|
|
{
|
|
SRGB = sRGB;
|
|
Compression = compression;
|
|
Alpha = alpha;
|
|
MaxSize = maxSize;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
[NonSerialized]
|
|
private Matrix4x4[] m_cameraInvViewProjPerFrame = null;
|
|
|
|
#if AI_DEBUG_MODE
|
|
[SerializeField]
|
|
private string m_renderInfo = string.Empty;
|
|
public string RenderInfo { get { return m_renderInfo; } set { m_renderInfo = value; } }
|
|
public bool m_createGameobject = true;
|
|
public bool m_generateQuad = true;
|
|
#endif
|
|
|
|
private enum RenderImpostorMode
|
|
{
|
|
Alpha = 0,
|
|
Normal = 1
|
|
}
|
|
|
|
private void GenerateTextures( List<TextureOutput> outputList, bool standardRendering )
|
|
{
|
|
m_outBuffers = new RenderTexture[ outputList.Count ];
|
|
for ( int i = 0; i < outputList.Count; i++ )
|
|
{
|
|
m_outBuffers[ i ] = new RenderTexture( ( int )m_data.TexSize.x, ( int )m_data.TexSize.y, 16, outputList[ i ].SRGB ? RenderTextureFormat.ARGB32 : RenderTextureFormat.ARGBHalf );
|
|
m_outBuffers[ i ].Create();
|
|
}
|
|
|
|
if ( standardRendering )
|
|
{
|
|
m_rtGBuffers = new RenderTexture[ 4 ];
|
|
|
|
if ( m_renderPipelineInUse == RenderPipelineInUse.HDRP )
|
|
{
|
|
// Albedo (RGB) Occlusion (A) [A => occlusion(7) / isLightmap(1)]
|
|
m_rtGBuffers[ 0 ] = new RenderTexture( ( int )m_data.TexSize.x, ( int )m_data.TexSize.y, 16, GraphicsFormat.R8G8B8A8_SRGB );
|
|
m_rtGBuffers[ 0 ].Create();
|
|
|
|
// Normals (RGB) PerceptualRoughness (A) [RGB => X:Y / 12:12 / Octa]
|
|
m_rtGBuffers[ 1 ] = new RenderTexture( ( int )m_data.TexSize.x, ( int )m_data.TexSize.y, 16, GraphicsFormat.R8G8B8A8_UNorm );
|
|
m_rtGBuffers[ 1 ].Create();
|
|
|
|
// Specular (RGB)
|
|
m_rtGBuffers[ 2 ] = new RenderTexture( ( int )m_data.TexSize.x, ( int )m_data.TexSize.y, 16, GraphicsFormat.R8G8B8A8_UNorm );
|
|
m_rtGBuffers[ 2 ].Create();
|
|
|
|
// Emission (RGB) Alpha (A)
|
|
m_rtGBuffers[ 3 ] = new RenderTexture( ( int )m_data.TexSize.x, ( int )m_data.TexSize.y, 16, GraphicsFormat.R16G16B16A16_SFloat );
|
|
m_rtGBuffers[ 3 ].Create();
|
|
}
|
|
else if ( m_renderPipelineInUse == RenderPipelineInUse.URP )
|
|
{
|
|
// Albedo (RGB) MaterialFlags (A) [A => miscFlags(7) / specularSetup(1)]
|
|
m_rtGBuffers[ 0 ] = new RenderTexture( ( int )m_data.TexSize.x, ( int )m_data.TexSize.y, 16, GraphicsFormat.R8G8B8A8_SRGB );
|
|
m_rtGBuffers[ 0 ].Create();
|
|
|
|
// Specular (RGB) Occlusion (A) [specularSetup ? R => Metallic : RGB => Specular ]
|
|
m_rtGBuffers[ 1 ] = new RenderTexture( ( int )m_data.TexSize.x, ( int )m_data.TexSize.y, 16, GraphicsFormat.R8G8B8A8_UNorm );
|
|
m_rtGBuffers[ 1 ].Create();
|
|
|
|
// Normals (RGB) Smoothness (A)
|
|
m_rtGBuffers[ 2 ] = new RenderTexture( ( int )m_data.TexSize.x, ( int )m_data.TexSize.y, 16, GraphicsFormat.R8G8B8A8_SNorm );
|
|
m_rtGBuffers[ 2 ].Create();
|
|
|
|
// Emission (RGB) Alpha (A)
|
|
m_rtGBuffers[ 3 ] = new RenderTexture( ( int )m_data.TexSize.x, ( int )m_data.TexSize.y, 16, GraphicsFormat.R16G16B16A16_SFloat );
|
|
m_rtGBuffers[ 3 ].Create();
|
|
}
|
|
else
|
|
{
|
|
// Albedo (RGB) Occlusion (A)
|
|
m_rtGBuffers[ 0 ] = new RenderTexture( ( int )m_data.TexSize.x, ( int )m_data.TexSize.y, 16, GraphicsFormat.R8G8B8A8_SRGB );
|
|
m_rtGBuffers[ 0 ].Create();
|
|
|
|
// Specular (RGB) Smoothness (A)
|
|
m_rtGBuffers[ 1 ] = new RenderTexture( ( int )m_data.TexSize.x, ( int )m_data.TexSize.y, 16, GraphicsFormat.R8G8B8A8_SRGB );
|
|
m_rtGBuffers[ 1 ].Create();
|
|
|
|
// Normals (RGB)
|
|
m_rtGBuffers[ 2 ] = new RenderTexture( ( int )m_data.TexSize.x, ( int )m_data.TexSize.y, 16, GraphicsFormat.A2B10G10R10_UNormPack32 );
|
|
m_rtGBuffers[ 2 ].Create();
|
|
|
|
// Emission (RGB) Alpha (A)
|
|
m_rtGBuffers[ 3 ] = new RenderTexture( ( int )m_data.TexSize.x, ( int )m_data.TexSize.y, 16, GraphicsFormat.R16G16B16A16_SFloat );
|
|
m_rtGBuffers[ 3 ].Create();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
m_rtGBuffers = m_outBuffers;
|
|
}
|
|
|
|
m_trueDepth = new RenderTexture( (int)m_data.TexSize.x, (int)m_data.TexSize.y, 16, RenderTextureFormat.Depth );
|
|
m_trueDepth.Create();
|
|
}
|
|
|
|
private void GenerateAlphaTextures( int targetAmount )
|
|
{
|
|
m_alphaGBuffers = new RenderTexture[ targetAmount ];
|
|
|
|
for( int i = 0; i < m_alphaGBuffers.Length; ++i )
|
|
{
|
|
m_alphaGBuffers[ i ] = new RenderTexture( MinAlphaResolution, MinAlphaResolution, 16, RenderTextureFormat.ARGB32 );
|
|
m_alphaGBuffers[ i ].Create();
|
|
}
|
|
|
|
m_trueDepth = new RenderTexture( MinAlphaResolution, MinAlphaResolution, 16, RenderTextureFormat.Depth );
|
|
m_trueDepth.Create();
|
|
}
|
|
|
|
private void ClearBuffers()
|
|
{
|
|
RenderTexture.active = null;
|
|
|
|
foreach( var rt in m_rtGBuffers )
|
|
{
|
|
rt.Release();
|
|
}
|
|
m_rtGBuffers = null;
|
|
|
|
foreach ( var rt in m_outBuffers )
|
|
{
|
|
rt.Release();
|
|
}
|
|
m_outBuffers = null;
|
|
}
|
|
|
|
private void ClearAlphaBuffers( )
|
|
{
|
|
RenderTexture.active = null;
|
|
foreach( var rt in m_alphaGBuffers )
|
|
{
|
|
rt.Release();
|
|
}
|
|
m_alphaGBuffers = null;
|
|
}
|
|
|
|
public static void GetFrameInfo( AmplifyImpostorAsset data, out int hframes, out int vframes )
|
|
{
|
|
hframes = data.HorizontalFrames;
|
|
vframes = ( data.ImpostorType == ImpostorType.Spherical && data.DecoupleAxisFrames ) ? data.VerticalFrames : data.HorizontalFrames;
|
|
}
|
|
|
|
#if UNITY_EDITOR
|
|
private static readonly List<KeyValuePair<string, int>> m_hdrpStencilCheck = new List<KeyValuePair<string, int>> {
|
|
new KeyValuePair<string,int>( "_StencilForwardRef", 0 ),
|
|
new KeyValuePair<string,int>( "_StencilForwardMask", 6 ),
|
|
new KeyValuePair<string,int>( "_StencilMotionRef", 40 ),
|
|
new KeyValuePair<string,int>( "_StencilMotionMask", 40 ),
|
|
new KeyValuePair<string,int>( "_StencilDepthRef", 8 ),
|
|
new KeyValuePair<string,int>( "_StencilDepthMask", 8 ),
|
|
new KeyValuePair<string,int>( "_StencilGBufferRef", 10 ),
|
|
new KeyValuePair<string,int>( "_StencilGBufferMask", 14 )
|
|
};
|
|
|
|
public void CheckHDRPMaterial()
|
|
{
|
|
if( m_renderPipelineInUse != RenderPipelineInUse.HDRP )
|
|
return;
|
|
|
|
if( m_data == null || m_data.Preset == null || m_data.Material == null )
|
|
return;
|
|
|
|
foreach ( var pair in m_hdrpStencilCheck )
|
|
{
|
|
if ( m_data.Material.HasProperty( pair.Key ) && m_data.Material.GetInt( pair.Key ) != pair.Value )
|
|
{
|
|
m_data.Material.SetInt( pair.Key, pair.Value );
|
|
}
|
|
}
|
|
}
|
|
|
|
public void SaveDebugRenderTexturePNG( ref RenderTexture rtex, string path )
|
|
{
|
|
SaveTexture( ref rtex, path, ImageFormat.PNG, 1, TextureChannels.RGBA, rtex.width, rtex.height );
|
|
}
|
|
|
|
public void SaveTexture( ref RenderTexture tex, string path, ImageFormat imageFormat, int resizeScale, TextureChannels channels, int width = 0, int height = 0 )
|
|
{
|
|
width = ( width != 0 ) ? width : ( int )m_data.TexSize.x;
|
|
height = ( height != 0 ) ? height : ( int )m_data.TexSize.y;
|
|
|
|
Texture2D outfile = AssetDatabase.LoadAssetAtPath<Texture2D>( path );
|
|
if( imageFormat == ImageFormat.EXR )
|
|
outfile = new Texture2D( (int)width / resizeScale, (int)height / resizeScale, TextureFormat.RGBAFloat, false );
|
|
else
|
|
outfile = new Texture2D( (int)width / resizeScale, (int)height / resizeScale, channels == TextureChannels.RGB ? TextureFormat.RGB24 : TextureFormat.RGBA32, true );
|
|
|
|
outfile.name = Path.GetFileNameWithoutExtension( path );
|
|
RenderTexture temp = RenderTexture.active;
|
|
RenderTexture.active = tex;
|
|
outfile.ReadPixels( new Rect( 0, 0, (int)width / resizeScale, (int)height / resizeScale ), 0, 0 );
|
|
|
|
RenderTexture.active = temp;
|
|
outfile.Apply();
|
|
byte[] bytes;
|
|
switch( imageFormat )
|
|
{
|
|
case ImageFormat.PNG:
|
|
bytes = outfile.EncodeToPNG();
|
|
break;
|
|
default:
|
|
case ImageFormat.TGA:
|
|
bytes = outfile.EncodeToTGA( Texture2DEx.Compression.RLE );
|
|
break;
|
|
case ImageFormat.EXR:
|
|
bytes = ImageConversion.EncodeToEXR( outfile, Texture2D.EXRFlags.CompressZIP );
|
|
break;
|
|
}
|
|
|
|
if( imageFormat == ImageFormat.EXR )
|
|
{
|
|
File.WriteAllBytes( path, bytes );
|
|
DestroyImmediate( outfile );
|
|
}
|
|
else
|
|
{
|
|
int BytesToWrite, BufIndex;
|
|
int bytesLength = bytes.Length;
|
|
FileStream FSFile = new FileStream( path, FileMode.Create, FileAccess.Write, FileShare.None, BlockSize, false );
|
|
BufIndex = 0;
|
|
do
|
|
{
|
|
BytesToWrite = Math.Min( BlockSize, bytesLength - BufIndex );
|
|
FSFile.Write( bytes, BufIndex, BytesToWrite );
|
|
BufIndex += BytesToWrite;
|
|
} while( BufIndex < bytesLength );
|
|
FSFile.Close();
|
|
FSFile.Dispose();
|
|
DestroyImmediate( outfile );
|
|
}
|
|
}
|
|
|
|
public void ChangeTextureImporter( ref RenderTexture tex, string path, bool sRGB = true, bool changeResolution = false, TextureCompression compression = TextureCompression.Normal, bool alpha = true )
|
|
{
|
|
Texture2D outfile = AssetDatabase.LoadAssetAtPath<Texture2D>( path );
|
|
TextureImporter tImporter = AssetImporter.GetAtPath( path ) as TextureImporter;
|
|
if( tImporter != null )
|
|
{
|
|
if( (tImporter.alphaSource == TextureImporterAlphaSource.FromInput && !alpha) ||
|
|
( tImporter.textureCompression != (TextureImporterCompression)compression ) ||
|
|
tImporter.sRGBTexture != sRGB ||
|
|
( changeResolution && tImporter.maxTextureSize != (int)m_data.TexSize.x ) )
|
|
{
|
|
tImporter.sRGBTexture = sRGB;
|
|
tImporter.alphaSource = alpha ? TextureImporterAlphaSource.FromInput : TextureImporterAlphaSource.None;
|
|
tImporter.textureCompression = (TextureImporterCompression)compression;
|
|
if( changeResolution )
|
|
tImporter.maxTextureSize = (int)m_data.TexSize.x;
|
|
|
|
EditorUtility.SetDirty( tImporter );
|
|
EditorUtility.SetDirty( outfile );
|
|
tImporter.SaveAndReimport();
|
|
}
|
|
}
|
|
}
|
|
|
|
public void CalculateSheetBounds()
|
|
{
|
|
m_xyFitSize = 0;
|
|
m_depthFitSize = 0;
|
|
|
|
GetFrameInfo( m_data, out int hframes, out int vframes );
|
|
|
|
for( int x = 0; x < hframes; x++ )
|
|
{
|
|
for( int y = 0; y < vframes; y++ )
|
|
{
|
|
Bounds frameBounds = new Bounds();
|
|
Matrix4x4 camMatrixRot = GetCameraRotationMatrix( m_data.ImpostorType, hframes, vframes, x, y );
|
|
|
|
for( int i = 0; i < Renderers.Length; i++ )
|
|
{
|
|
if( Renderers[ i ] == null || !Renderers[ i ].enabled || Renderers[ i ].shadowCastingMode == ShadowCastingMode.ShadowsOnly )
|
|
continue;
|
|
|
|
MeshFilter mf = Renderers[ i ].GetComponent<MeshFilter>();
|
|
if( mf == null || mf.sharedMesh == null )
|
|
continue;
|
|
|
|
if( frameBounds.size == Vector3.zero )
|
|
frameBounds = mf.sharedMesh.bounds.Transform( m_rootTransform.worldToLocalMatrix * Renderers[ i ].localToWorldMatrix );
|
|
else
|
|
frameBounds.Encapsulate( mf.sharedMesh.bounds.Transform( m_rootTransform.worldToLocalMatrix * Renderers[ i ].localToWorldMatrix ) );
|
|
}
|
|
|
|
if( x == 0 && y == 0 )
|
|
m_originalBound = frameBounds;
|
|
|
|
frameBounds = frameBounds.Transform( camMatrixRot );
|
|
m_xyFitSize = Mathf.Max( m_xyFitSize, frameBounds.size.x, frameBounds.size.y );
|
|
|
|
const float depthFitCorrection = 1.0f; // @diogo: cube with a smaller cube on top was intersecting near plane
|
|
|
|
m_depthFitSize = Mathf.Max( m_depthFitSize, frameBounds.size.z * depthFitCorrection );
|
|
}
|
|
}
|
|
|
|
#if AI_DEBUG_MODE
|
|
m_renderInfo = "";
|
|
m_renderInfo += "\nXY fit:\t" + m_trueFitsize;
|
|
m_renderInfo += "\nDepth:\t" + m_depthFitsize;
|
|
#endif
|
|
}
|
|
|
|
public void DilateRenderTextureUsingMask( ref RenderTexture mainTex, ref RenderTexture maskTex, int pixelBleed, bool alpha, Material dilateMat = null )
|
|
{
|
|
if( pixelBleed == 0 )
|
|
return;
|
|
|
|
bool destroyMaterial = false;
|
|
if( dilateMat == null )
|
|
{
|
|
destroyMaterial = true;
|
|
Shader dilateShader = AssetDatabase.LoadAssetAtPath<Shader>( AssetDatabase.GUIDToAssetPath( DilateGUID ) );
|
|
dilateMat = new Material( dilateShader );
|
|
}
|
|
|
|
RenderTexture tempTex = RenderTexture.GetTemporary( mainTex.width, mainTex.height, mainTex.depth, mainTex.format );
|
|
RenderTexture tempMask = RenderTexture.GetTemporary( maskTex.width, maskTex.height, maskTex.depth, maskTex.format );
|
|
RenderTexture dilatedMask = RenderTexture.GetTemporary( maskTex.width, maskTex.height, maskTex.depth, maskTex.format );
|
|
|
|
Graphics.Blit( maskTex, dilatedMask );
|
|
|
|
for( int i = 0; i < pixelBleed; i++ )
|
|
{
|
|
dilateMat.SetTexture( "_MaskTex", dilatedMask );
|
|
|
|
Graphics.Blit( mainTex, tempTex, dilateMat, alpha ? 1 : 0 );
|
|
Graphics.Blit( tempTex, mainTex );
|
|
|
|
Graphics.Blit( dilatedMask, tempMask, dilateMat, 1 );
|
|
Graphics.Blit( tempMask, dilatedMask );
|
|
}
|
|
|
|
RenderTexture.ReleaseTemporary( tempTex );
|
|
RenderTexture.ReleaseTemporary( tempMask );
|
|
RenderTexture.ReleaseTemporary( dilatedMask );
|
|
|
|
if( destroyMaterial )
|
|
{
|
|
DestroyImmediate( dilateMat );
|
|
dilateMat = null;
|
|
}
|
|
}
|
|
|
|
public void PackingRemapping( ref RenderTexture src, ref RenderTexture dst, int passIndex, Material packerMat = null, Texture extraTex = null, string texName = null )
|
|
{
|
|
bool destroyMaterial = false;
|
|
if( packerMat == null )
|
|
{
|
|
destroyMaterial = true;
|
|
Shader packerShader = AssetDatabase.LoadAssetAtPath<Shader>( AssetDatabase.GUIDToAssetPath( PackerGUID ) );
|
|
packerMat = new Material( packerShader );
|
|
}
|
|
|
|
if( extraTex != null )
|
|
{
|
|
if( string.IsNullOrEmpty( texName ) )
|
|
packerMat.SetTexture( "_A", extraTex );
|
|
else
|
|
packerMat.SetTexture( texName, extraTex );
|
|
}
|
|
|
|
if( src == dst )
|
|
{
|
|
int width = src.width;
|
|
int height = src.height;
|
|
int depth = src.depth;
|
|
RenderTextureFormat format = src.format;
|
|
|
|
RenderTexture tempTex = RenderTexture.GetTemporary( width, height, depth, format );
|
|
Graphics.Blit( src, tempTex, packerMat, passIndex );
|
|
Graphics.Blit( tempTex, dst );
|
|
RenderTexture.ReleaseTemporary( tempTex );
|
|
}
|
|
else
|
|
{
|
|
Graphics.Blit( src, dst, packerMat, passIndex );
|
|
}
|
|
|
|
if( destroyMaterial )
|
|
{
|
|
DestroyImmediate( packerMat );
|
|
packerMat = null;
|
|
}
|
|
}
|
|
|
|
private void CopyTransform()
|
|
{
|
|
m_oriPos = RootTransform.position;
|
|
m_oriRot = RootTransform.rotation;
|
|
m_oriSca = RootTransform.localScale;
|
|
RootTransform.position = Vector3.zero;
|
|
RootTransform.rotation = Quaternion.identity;
|
|
RootTransform.localScale = Vector3.one;
|
|
}
|
|
|
|
private void PasteTransform()
|
|
{
|
|
RootTransform.position = m_oriPos;
|
|
RootTransform.rotation = m_oriRot;
|
|
RootTransform.localScale = m_oriSca;
|
|
}
|
|
|
|
public void CalculatePixelBounds( int targetAmount )
|
|
{
|
|
bool sRGBcache = GL.sRGBWrite;
|
|
|
|
CalculateSheetBounds();
|
|
GenerateAlphaTextures( targetAmount );
|
|
|
|
GL.sRGBWrite = true;
|
|
|
|
m_pixelOffset = Vector2.zero;
|
|
|
|
// TODO: remove this temporary solution
|
|
CopyTransform();
|
|
try
|
|
{
|
|
RenderImpostor( targetAmount, RenderImpostorMode.Alpha, true, m_data.Preset.BakeShader );
|
|
PasteTransform();
|
|
}
|
|
finally
|
|
{
|
|
PasteTransform();
|
|
EditorUtility.ClearProgressBar();
|
|
}
|
|
|
|
GL.sRGBWrite = sRGBcache;
|
|
|
|
bool standardRendering = ( m_data.Preset.BakeShader == null );
|
|
int alphaIndex = standardRendering ? 3 : m_data.Preset.AlphaIndex;
|
|
|
|
Shader packerShader = AssetDatabase.LoadAssetAtPath<Shader>( AssetDatabase.GUIDToAssetPath( PackerGUID ) );
|
|
Material packerMat = new Material( packerShader );
|
|
|
|
// Generate Alpha using Depth
|
|
RenderTexture tempTex2 = RenderTextureEx.GetTemporary( m_alphaGBuffers[ alphaIndex ] );
|
|
Graphics.Blit( m_alphaGBuffers[ alphaIndex ], tempTex2 );
|
|
packerMat.SetTexture( "_A", tempTex2 );
|
|
Graphics.Blit( m_trueDepth, m_alphaGBuffers[ alphaIndex ], packerMat, 11 );
|
|
RenderTexture.ReleaseTemporary( tempTex2 );
|
|
|
|
m_trueDepth.Release();
|
|
m_trueDepth = null;
|
|
|
|
// Render just alpha
|
|
RenderTexture combinedAlphaTexture = RenderTexture.GetTemporary( MinAlphaResolution, MinAlphaResolution, m_alphaGBuffers[ alphaIndex ].depth, m_alphaGBuffers[ alphaIndex ].format );
|
|
PackingRemapping( ref m_alphaGBuffers[ alphaIndex ], ref combinedAlphaTexture, 8, packerMat );
|
|
|
|
DestroyImmediate( packerMat );
|
|
packerMat = null;
|
|
|
|
ClearAlphaBuffers();
|
|
|
|
RenderTexture.active = combinedAlphaTexture;
|
|
Texture2D tempTex = new Texture2D( combinedAlphaTexture.width, combinedAlphaTexture.height, TextureFormat.RGBA32, false );
|
|
tempTex.ReadPixels( new Rect( 0, 0, combinedAlphaTexture.width, combinedAlphaTexture.height ), 0, 0 );
|
|
tempTex.Apply();
|
|
RenderTexture.active = null;
|
|
|
|
RenderTexture.ReleaseTemporary( combinedAlphaTexture );
|
|
|
|
Rect testRect = new Rect( 0, 0, tempTex.width, tempTex.height );
|
|
Vector2[][] paths;
|
|
SpriteUtilityEx.GenerateOutline( tempTex, testRect, 0.2f, 0, false, out paths );
|
|
int sum = 0;
|
|
for( int i = 0; i < paths.Length; i++ )
|
|
{
|
|
sum += paths[ i ].Length;
|
|
}
|
|
|
|
Vector2[] minMaxPoints = new Vector2[ sum ];
|
|
int index = 0;
|
|
for( int i = 0; i < paths.Length; i++ )
|
|
{
|
|
for( int j = 0; j < paths[ i ].Length; j++ )
|
|
{
|
|
minMaxPoints[ index ] = (Vector2)( paths[ i ][ j ] ) + ( new Vector2( tempTex.width * 0.5f, tempTex.height * 0.5f ) );
|
|
minMaxPoints[ index ] = Vector2.Scale( minMaxPoints[ index ], new Vector2( 1.0f / tempTex.width, 1.0f / tempTex.height ) );
|
|
index++;
|
|
}
|
|
}
|
|
|
|
Vector2 mins = Vector2.one;
|
|
Vector2 maxs = Vector2.zero;
|
|
|
|
for( int i = 0; i < minMaxPoints.Length; i++ )
|
|
{
|
|
mins.x = Mathf.Min( minMaxPoints[ i ].x, mins.x );
|
|
mins.y = Mathf.Min( minMaxPoints[ i ].y, mins.y );
|
|
maxs.x = Mathf.Max( minMaxPoints[ i ].x, maxs.x );
|
|
maxs.y = Mathf.Max( minMaxPoints[ i ].y, maxs.y );
|
|
}
|
|
|
|
Vector2 height = ( maxs - mins );
|
|
float maxBound = Mathf.Max( height.x, height.y );
|
|
Vector2 center = mins + ( height * 0.5f );
|
|
m_pixelOffset = ( center - ( Vector2.one * 0.5f ) ) * m_xyFitSize;
|
|
//Debug.Log( m_pixelOffset.ToString( "N5" ) );
|
|
//Debug.Log( height.ToString( "N5" ) );
|
|
m_xyFitSize *= maxBound;
|
|
m_depthFitSize *= maxBound;
|
|
}
|
|
|
|
// For inspector
|
|
public void RenderCombinedAlpha( AmplifyImpostorAsset data = null )
|
|
{
|
|
AmplifyImpostorAsset tempData = m_data;
|
|
if( data != null )
|
|
m_data = data;
|
|
|
|
bool standardRendering = ( m_data.Preset.BakeShader == null );
|
|
int targetAmount = standardRendering ? 4 : m_data.Preset.Output.Count;
|
|
|
|
CalculatePixelBounds( targetAmount );
|
|
GenerateAlphaTextures( targetAmount );
|
|
|
|
bool sRGBcache = GL.sRGBWrite;
|
|
GL.sRGBWrite = true;
|
|
|
|
// TODO: remove this temporary solution
|
|
CopyTransform();
|
|
try
|
|
{
|
|
RenderImpostor( targetAmount, RenderImpostorMode.Alpha, false, m_data.Preset.BakeShader );
|
|
PasteTransform();
|
|
}
|
|
finally
|
|
{
|
|
PasteTransform();
|
|
EditorUtility.ClearProgressBar();
|
|
}
|
|
|
|
GL.sRGBWrite = sRGBcache;
|
|
|
|
int alphaIndex = standardRendering ? 3 : m_data.Preset.AlphaIndex;
|
|
|
|
Shader packerShader = AssetDatabase.LoadAssetAtPath<Shader>( AssetDatabase.GUIDToAssetPath( PackerGUID ) );
|
|
Material packerMat = new Material( packerShader );
|
|
|
|
// Generate Alpha using Depth
|
|
RenderTexture tempTex = RenderTextureEx.GetTemporary( m_alphaGBuffers[ alphaIndex ] );
|
|
Graphics.Blit( m_alphaGBuffers[ alphaIndex ], tempTex );
|
|
packerMat.SetTexture( "_A", tempTex );
|
|
Graphics.Blit( m_trueDepth, m_alphaGBuffers[ alphaIndex ], packerMat, 11 );
|
|
RenderTexture.ReleaseTemporary( tempTex );
|
|
|
|
m_trueDepth.Release();
|
|
m_trueDepth = null;
|
|
|
|
// Render just alpha
|
|
RenderTexture combinedAlphaTexture = RenderTexture.GetTemporary( MinAlphaResolution, MinAlphaResolution, m_alphaGBuffers[ alphaIndex ].depth, m_alphaGBuffers[ alphaIndex ].format );
|
|
PackingRemapping( ref m_alphaGBuffers[ alphaIndex ], ref combinedAlphaTexture, 8, packerMat );
|
|
|
|
DestroyImmediate( packerMat );
|
|
packerMat = null;
|
|
|
|
ClearAlphaBuffers();
|
|
|
|
RenderTexture.active = combinedAlphaTexture;
|
|
m_alphaTex = new Texture2D( combinedAlphaTexture.width, combinedAlphaTexture.height, TextureFormat.RGBAFloat, false );
|
|
m_alphaTex.ReadPixels( new Rect( 0, 0, combinedAlphaTexture.width, combinedAlphaTexture.height ), 0, 0 );
|
|
m_alphaTex.Apply();
|
|
RenderTexture.active = null;
|
|
|
|
RenderTexture.ReleaseTemporary( combinedAlphaTexture );
|
|
|
|
m_data = tempData;
|
|
}
|
|
|
|
public void CreateAssetFile( AmplifyImpostorAsset data = null )
|
|
{
|
|
string folderPath = this.OpenFolderForImpostor();
|
|
|
|
if( string.IsNullOrEmpty( folderPath ) )
|
|
return;
|
|
|
|
string fileName = m_impostorName;
|
|
|
|
if( string.IsNullOrEmpty( fileName ) )
|
|
fileName = m_rootTransform.name + "_Impostor";
|
|
|
|
folderPath = folderPath.TrimEnd( new char[] { '/', '*', '.', ' ' } );
|
|
folderPath += "/";
|
|
folderPath = folderPath.TrimStart( new char[] { '/', '*', '.', ' ' } );
|
|
|
|
if( m_data == null )
|
|
{
|
|
Undo.RegisterCompleteObjectUndo( this, "Create Impostor Asset" );
|
|
AmplifyImpostorAsset existingAsset = AssetDatabase.LoadAssetAtPath<AmplifyImpostorAsset>( folderPath + fileName + ".asset" );
|
|
if( existingAsset != null )
|
|
{
|
|
m_data = existingAsset;
|
|
}
|
|
else
|
|
{
|
|
m_data = ScriptableObject.CreateInstance<AmplifyImpostorAsset>();
|
|
AssetDatabase.CreateAsset( m_data, folderPath + fileName + ".asset" );
|
|
}
|
|
}
|
|
}
|
|
|
|
private void DisplayProgress( float progress, string message )
|
|
{
|
|
#if UNITY_EDITOR
|
|
if( !Application.isPlaying )
|
|
{
|
|
EditorUtility.DisplayProgressBar( "Baking Impostor", message, progress );
|
|
|
|
if( progress >= 1.0f )
|
|
EditorUtility.ClearProgressBar();
|
|
}
|
|
#endif
|
|
}
|
|
|
|
public void DetectRenderPipeline()
|
|
{
|
|
string pipelineName = string.Empty;
|
|
try
|
|
{
|
|
pipelineName = UnityEngine.Rendering.RenderPipelineManager.currentPipeline.ToString();
|
|
}
|
|
catch( Exception )
|
|
{
|
|
pipelineName = "";
|
|
}
|
|
|
|
if( pipelineName.Contains( "UniversalRenderPipeline" ) )
|
|
{
|
|
m_renderPipelineInUse = RenderPipelineInUse.URP;
|
|
}
|
|
else if( pipelineName.Contains( "HDRenderPipeline" ) )
|
|
{
|
|
m_renderPipelineInUse = RenderPipelineInUse.HDRP;
|
|
}
|
|
else if( pipelineName.Equals( "" ) )
|
|
{
|
|
m_renderPipelineInUse = RenderPipelineInUse.None;
|
|
}
|
|
else
|
|
{
|
|
m_renderPipelineInUse = RenderPipelineInUse.Custom;
|
|
}
|
|
}
|
|
|
|
public static void UpdateKeywords( Material material, AmplifyImpostorBakePreset preset )
|
|
{
|
|
foreach ( var outout in preset.Output )
|
|
{
|
|
if ( material.HasProperty( outout.Name ) )
|
|
{
|
|
if ( !outout.Active )
|
|
{
|
|
material.SetTexture( outout.Name, null );
|
|
}
|
|
material.EnsureKeywordState( outout.Name.ToUpper() + "MAP", outout.Active );
|
|
}
|
|
}
|
|
}
|
|
|
|
private void PostProcessTextures( Dictionary<string, TextureData> textureImportData )
|
|
{
|
|
foreach ( var pair in textureImportData )
|
|
{
|
|
TextureData desc = pair.Value;
|
|
TextureImporter textureImporter = ( TextureImporter )AssetImporter.GetAtPath( pair.Key );
|
|
|
|
var desiredSRGB = desc.SRGB;
|
|
var desiredAlphaSource = desc.Alpha ? TextureImporterAlphaSource.FromInput : TextureImporterAlphaSource.None;
|
|
var desiredCompression = ( TextureImporterCompression )desc.Compression;
|
|
var desiredMaxSize = ( desc.MaxSize > -1 ) ? desc.MaxSize : textureImporter.maxTextureSize;
|
|
|
|
if ( textureImporter.sRGBTexture != desiredSRGB ||
|
|
textureImporter.alphaSource != desiredAlphaSource ||
|
|
textureImporter.textureCompression != desiredCompression ||
|
|
textureImporter.maxTextureSize != desiredMaxSize )
|
|
{
|
|
textureImporter.sRGBTexture = desiredSRGB;
|
|
textureImporter.alphaSource = desiredAlphaSource;
|
|
textureImporter.textureCompression = desiredCompression;
|
|
textureImporter.maxTextureSize = desiredMaxSize;
|
|
textureImporter.SaveAndReimport();
|
|
}
|
|
}
|
|
}
|
|
|
|
public void RenderAllDeferredGroups( AmplifyImpostorAsset data = null )
|
|
{
|
|
string folderPath = m_folderPath;
|
|
if( m_data == null )
|
|
{
|
|
folderPath = this.OpenFolderForImpostor();
|
|
}
|
|
else
|
|
{
|
|
m_impostorName = m_data.name;
|
|
folderPath = Path.GetDirectoryName( AssetDatabase.GetAssetPath( m_data ) ).Replace( "\\", "/" ) + "/";
|
|
}
|
|
if( string.IsNullOrEmpty( folderPath ) )
|
|
return;
|
|
|
|
DisplayProgress( 0, "Please Wait... Setting up" );
|
|
string fileName = m_impostorName;
|
|
|
|
if( string.IsNullOrEmpty( fileName ) )
|
|
fileName = m_rootTransform.name + "_Impostor";
|
|
|
|
m_folderPath = folderPath;
|
|
folderPath = folderPath.TrimEnd( new char[] { '/', '*', '.', ' ' } );
|
|
folderPath += "/";
|
|
folderPath = folderPath.TrimStart( new char[] { '/', '*', '.', ' ' } );
|
|
m_impostorName = fileName;
|
|
|
|
Undo.RegisterCompleteObjectUndo( this, "Create Impostor" );
|
|
|
|
DetectRenderPipeline();
|
|
|
|
if( m_data == null )
|
|
{
|
|
AmplifyImpostorAsset existingAsset = AssetDatabase.LoadAssetAtPath<AmplifyImpostorAsset>( folderPath + fileName + ".asset" );
|
|
|
|
m_data = ScriptableObject.CreateInstance<AmplifyImpostorAsset>();
|
|
AssetDatabase.CreateAsset( m_data, folderPath + fileName + ".asset" );
|
|
|
|
if( data != null )
|
|
{
|
|
m_data.ShapePoints = data.ShapePoints;
|
|
}
|
|
}
|
|
else
|
|
if( data != null )
|
|
{
|
|
m_data = data;
|
|
}
|
|
bool chache = GL.sRGBWrite;
|
|
GL.sRGBWrite = true;
|
|
|
|
if( !m_data.DecoupleAxisFrames )
|
|
m_data.HorizontalFrames = m_data.VerticalFrames;
|
|
|
|
if( m_data.Preset == null )
|
|
{
|
|
m_data.Preset = AssetDatabase.LoadAssetAtPath<AmplifyImpostorBakePreset>( AssetDatabase.GUIDToAssetPath( DefaultPreset ) );
|
|
}
|
|
|
|
bool standardRendering = ( m_data.Preset.BakeShader == null );
|
|
if ( standardRendering && m_data.Preset.Output.Count != AmplifyImpostorBakePreset.DefaultOutputCount )
|
|
{
|
|
Debug.LogError( "[AmplifyImpostor] Detected an error in Bake Preset " + m_data.Preset.name + ". It uses a default/null baking shader but the outputs are non-default." );
|
|
return;
|
|
}
|
|
|
|
List<TextureOutput> outputList = new List<TextureOutput>();
|
|
for( int i = 0; i < m_data.Preset.Output.Count; i++ )
|
|
outputList.Add( m_data.Preset.Output[ i ].Clone() );
|
|
|
|
for( int i = 0; i < m_data.OverrideOutput.Count && i < m_data.Preset.Output.Count; i++ )
|
|
{
|
|
if( ( m_data.OverrideOutput[ i ].OverrideMask & OverrideMask.OutputToggle ) == OverrideMask.OutputToggle )
|
|
outputList[ m_data.OverrideOutput[ i ].Index ].Active = m_data.OverrideOutput[ i ].Active;
|
|
if( ( m_data.OverrideOutput[ i ].OverrideMask & OverrideMask.NameSuffix ) == OverrideMask.NameSuffix )
|
|
outputList[ m_data.OverrideOutput[ i ].Index ].Name = m_data.OverrideOutput[ i ].Name;
|
|
if( ( m_data.OverrideOutput[ i ].OverrideMask & OverrideMask.RelativeScale ) == OverrideMask.RelativeScale )
|
|
outputList[ m_data.OverrideOutput[ i ].Index ].Scale = m_data.OverrideOutput[ i ].Scale;
|
|
if( ( m_data.OverrideOutput[ i ].OverrideMask & OverrideMask.ColorSpace ) == OverrideMask.ColorSpace )
|
|
outputList[ m_data.OverrideOutput[ i ].Index ].SRGB = m_data.OverrideOutput[ i ].SRGB;
|
|
if( ( m_data.OverrideOutput[ i ].OverrideMask & OverrideMask.QualityCompression ) == OverrideMask.QualityCompression )
|
|
outputList[ m_data.OverrideOutput[ i ].Index ].Compression = m_data.OverrideOutput[ i ].Compression;
|
|
if( ( m_data.OverrideOutput[ i ].OverrideMask & OverrideMask.FileFormat ) == OverrideMask.FileFormat )
|
|
outputList[ m_data.OverrideOutput[ i ].Index ].ImageFormat = m_data.OverrideOutput[ i ].ImageFormat;
|
|
}
|
|
m_fileNames = new string[ outputList.Count ];
|
|
|
|
string guid = string.Empty;
|
|
if( m_renderPipelineInUse == RenderPipelineInUse.HDRP )
|
|
guid = m_data.ImpostorType == ImpostorType.Spherical ? ShaderHDRP : ShaderOctaHDRP;
|
|
else if( m_renderPipelineInUse == RenderPipelineInUse.URP )
|
|
guid = m_data.ImpostorType == ImpostorType.Spherical ? ShaderURP : ShaderOctaURP;
|
|
else
|
|
guid = m_data.ImpostorType == ImpostorType.Spherical ? ShaderBiRP : ShaderOctaBiRP;
|
|
|
|
int targetAmount = standardRendering ? 4 : outputList.Count;
|
|
|
|
CalculatePixelBounds( targetAmount );
|
|
DisplayProgress( 0.1f, "Please Wait... Allocating Resources" );
|
|
|
|
GenerateTextures( outputList, standardRendering );
|
|
DisplayProgress( 0.2f, "Please Wait... Baking" );
|
|
|
|
// TODO: remove this temporary solution
|
|
CopyTransform();
|
|
|
|
GetFrameInfo( m_data, out int hframes, out int vframes );
|
|
m_cameraInvViewProjPerFrame = new Matrix4x4[ hframes * vframes ];
|
|
|
|
try
|
|
{
|
|
RenderImpostor( targetAmount, RenderImpostorMode.Normal, true, m_data.Preset.BakeShader );
|
|
PasteTransform();
|
|
}
|
|
finally
|
|
{
|
|
PasteTransform();
|
|
EditorUtility.ClearProgressBar();
|
|
}
|
|
|
|
DisplayProgress( 0.5f, "Please Wait... Remapping" );
|
|
|
|
Shader packerShader = AssetDatabase.LoadAssetAtPath<Shader>( AssetDatabase.GUIDToAssetPath( PackerGUID ) );
|
|
Material packerMat = new Material( packerShader );
|
|
|
|
Shader gbufferToOutputShader = AssetDatabase.LoadAssetAtPath<Shader>( AssetDatabase.GUIDToAssetPath( GBufferToOutputGUID ) );
|
|
Material gbufferToOutputMat = new Material( gbufferToOutputShader );
|
|
|
|
int alphaIndex = m_data.Preset.AlphaIndex;
|
|
if ( standardRendering )
|
|
{
|
|
gbufferToOutputMat.SetInt( "_RenderPipeline", ( int )m_renderPipelineInUse );
|
|
gbufferToOutputMat.SetTexture( "_GBuffer0", m_rtGBuffers[ 0 ] );
|
|
gbufferToOutputMat.SetTexture( "_GBuffer1", m_rtGBuffers[ 1 ] );
|
|
gbufferToOutputMat.SetTexture( "_GBuffer2", m_rtGBuffers[ 2 ] );
|
|
gbufferToOutputMat.SetTexture( "_GBuffer3", m_rtGBuffers[ 3 ] );
|
|
gbufferToOutputMat.SetTexture( "_Depth", m_trueDepth );
|
|
|
|
var invViewProjBuffer = new ComputeBuffer( m_cameraInvViewProjPerFrame.Length, sizeof( float ) * 16, ComputeBufferType.Structured );
|
|
invViewProjBuffer.SetData( m_cameraInvViewProjPerFrame );
|
|
|
|
gbufferToOutputMat.SetBuffer( "_CameraInvViewProjPerFrame", invViewProjBuffer );
|
|
gbufferToOutputMat.SetVector( "_FrameCount", new Vector2( hframes, vframes ) );
|
|
gbufferToOutputMat.SetFloat( "_DepthSize", m_depthFitSize );
|
|
gbufferToOutputMat.SetVector( "_BoundsMin", m_originalBound.min );
|
|
gbufferToOutputMat.SetVector( "_BoundsSize", m_originalBound.size );
|
|
|
|
for ( int i = 0; i < m_outBuffers.Length; i++ )
|
|
{
|
|
Graphics.Blit( null, m_outBuffers[ i ], gbufferToOutputMat, i );
|
|
}
|
|
|
|
invViewProjBuffer.Release();
|
|
|
|
m_trueDepth.Release();
|
|
m_trueDepth = null;
|
|
}
|
|
|
|
m_cameraInvViewProjPerFrame = null;
|
|
|
|
// TGA
|
|
for ( int i = 0; i < outputList.Count; i++ )
|
|
{
|
|
if( outputList[ i ].ImageFormat == ImageFormat.TGA )
|
|
PackingRemapping( ref m_outBuffers[ i ], ref m_outBuffers[ i ], 6, packerMat );
|
|
}
|
|
|
|
if( m_data.PixelPadding > 0 )
|
|
DisplayProgress( 0.55f, "Please Wait... Dilating" );
|
|
|
|
Shader dilateShader = AssetDatabase.LoadAssetAtPath<Shader>( AssetDatabase.GUIDToAssetPath( DilateGUID ) );
|
|
Material dilateMat = new Material( dilateShader );
|
|
|
|
// Dilation
|
|
for( int i = 0; i < outputList.Count; i++ )
|
|
{
|
|
if( outputList[ i ].Active )
|
|
DilateRenderTextureUsingMask( ref m_outBuffers[ i ], ref m_outBuffers[ alphaIndex ], m_data.PixelPadding, alphaIndex != i, dilateMat );
|
|
}
|
|
|
|
DestroyImmediate( dilateMat );
|
|
dilateMat = null;
|
|
|
|
DisplayProgress( 0.575f, "Please Wait... Resizing" );
|
|
|
|
// Resize Final Textures
|
|
for( int i = 0; i < outputList.Count; i++ )
|
|
{
|
|
if( outputList[ i ].Scale != TextureScale.Full )
|
|
{
|
|
RenderTexture resTex = RenderTexture.GetTemporary( m_outBuffers[ i ].width / (int)outputList[ i ].Scale, m_outBuffers[ i ].height / (int)outputList[ i ].Scale, m_outBuffers[ i ].depth, m_outBuffers[ i ].graphicsFormat );
|
|
Graphics.Blit( m_outBuffers[ i ], resTex );
|
|
m_outBuffers[ i ].Release();
|
|
m_outBuffers[ i ] = new RenderTexture( resTex.width, resTex.height, m_outBuffers[ i ].depth, m_outBuffers[ i ].graphicsFormat );
|
|
m_outBuffers[ i ].Create();
|
|
Graphics.Blit( resTex, m_outBuffers[ i ] );
|
|
RenderTexture.ReleaseTemporary( resTex );
|
|
}
|
|
}
|
|
|
|
DestroyImmediate( packerMat );
|
|
packerMat = null;
|
|
|
|
DestroyImmediate( gbufferToOutputMat );
|
|
gbufferToOutputMat = null;
|
|
|
|
DisplayProgress( 0.6f, "Please Wait... Creating Asset and Textures" );
|
|
|
|
bool isPrefab = false;
|
|
|
|
if( PrefabUtility.GetPrefabAssetType( this.gameObject ) == PrefabAssetType.Regular && PrefabUtility.GetPrefabInstanceHandle( this.gameObject ) == null )
|
|
isPrefab = true;
|
|
|
|
// Create billboard
|
|
Shader defaultShader = null;
|
|
if( m_data.Preset.RuntimeShader != null )
|
|
{
|
|
defaultShader = m_data.Preset.RuntimeShader;
|
|
}
|
|
else
|
|
{
|
|
defaultShader = AssetDatabase.LoadAssetAtPath<Shader>( AssetDatabase.GUIDToAssetPath( guid ) );
|
|
}
|
|
Material material = m_data.Material;
|
|
if( material == null )
|
|
{
|
|
material = new Material( defaultShader );
|
|
material.name = fileName;
|
|
material.enableInstancing = true;
|
|
AssetDatabase.AddObjectToAsset( material, m_data );
|
|
m_data.Material = material;
|
|
EditorUtility.SetDirty( material );
|
|
}
|
|
else
|
|
{
|
|
material.shader = defaultShader;
|
|
material.name = fileName;
|
|
EditorUtility.SetDirty( material );
|
|
}
|
|
|
|
Texture2D tex = null;
|
|
bool hasDifferentResolution = false;
|
|
|
|
// Construct file names
|
|
m_standardFileNamesOld[ 0 ] = "_AlbedoAlpha";
|
|
m_standardFileNamesOld[ 1 ] = "_NormalDepth";
|
|
m_standardFileNamesOld[ 2 ] = "_SpecularSmoothness";
|
|
m_standardFileNamesOld[ 3 ] = "_EmissionOcclusion";
|
|
|
|
m_standardFileNames[ 0 ] = Preferences.GlobalAlbedo;
|
|
m_standardFileNames[ 1 ] = Preferences.GlobalNormals;
|
|
m_standardFileNames[ 2 ] = Preferences.GlobalSpecular;
|
|
m_standardFileNames[ 3 ] = Preferences.GlobalOcclusion;
|
|
m_standardFileNames[ 4 ] = Preferences.GlobalEmission;
|
|
|
|
for( int i = 0; i < outputList.Count; i++ )
|
|
{
|
|
tex = null;
|
|
m_fileNames[ i ] = string.Empty;
|
|
if( material.HasProperty( outputList[ i ].Name ) )
|
|
tex = material.GetTexture( outputList[ i ].Name ) as Texture2D;
|
|
if( tex != null )
|
|
{
|
|
m_fileNames[ i ] = AssetDatabase.GetAssetPath( tex );
|
|
//m_fileNames[ i ] = Path.GetDirectoryName( AssetDatabase.GetAssetPath( tex ) ).Replace( "\\", "/" ) + "/";
|
|
if( tex.width != (int)m_data.TexSize.x / (int)outputList[ i ].Scale )
|
|
hasDifferentResolution = true;
|
|
}
|
|
else
|
|
{
|
|
m_fileNames[ i ] = folderPath;
|
|
m_fileNames[ i ] += fileName + outputList[ i ].Name + "." + outputList[ i ].ImageFormat.ToString().ToLower();
|
|
}
|
|
}
|
|
|
|
for( int i = 0; i < m_propertyNames.Length; i++ )
|
|
{
|
|
tex = null;
|
|
if( material.HasProperty( m_propertyNames[ i ] ) )
|
|
{
|
|
tex = material.GetTexture( m_propertyNames[ i ] ) as Texture2D;
|
|
if( tex != null )
|
|
{
|
|
int indexFound = outputList.FindIndex( x => x.Name == m_standardFileNames[ i ] );
|
|
if( indexFound > -1 )
|
|
{
|
|
m_fileNames[ indexFound ] = AssetDatabase.GetAssetPath( tex );
|
|
//m_fileNames[ indexFound ] = Path.GetDirectoryName( AssetDatabase.GetAssetPath( tex ) ).Replace( "\\", "/" ) + "/";
|
|
//m_fileNames[ indexFound ] += fileName + outputList[ indexFound ].Name + "." + outputList[ indexFound ].ImageFormat.ToString().ToLower();
|
|
|
|
if( tex.width != (int)m_data.TexSize.x / (int)outputList[ indexFound ].Scale )
|
|
hasDifferentResolution = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
int activeCount = 0;
|
|
int missingCount = 0;
|
|
for ( int i = 0; i < outputList.Count; i++ )
|
|
{
|
|
activeCount += outputList[ i ].Active ? 1 : 0;
|
|
missingCount += string.IsNullOrEmpty( AssetDatabase.AssetPathToGUID( m_fileNames[ i ], AssetPathToGUIDOptions.OnlyExistingAssets ) ) ? 1 : 0;
|
|
}
|
|
|
|
bool resizeTextures = false;
|
|
if ( missingCount == activeCount )
|
|
{
|
|
// @diogo: they're all missing; always resize in this case.
|
|
resizeTextures = true;
|
|
}
|
|
else
|
|
{
|
|
if ( hasDifferentResolution && EditorPrefs.GetInt( Preferences.PrefGlobalTexImport, 0 ) == 0 )
|
|
resizeTextures = EditorUtility.DisplayDialog( "Resize Textures?", "Do you wish to override the Texture Import settings to match the provided Impostor Texture Size?", "Yes", "No" );
|
|
else if ( EditorPrefs.GetInt( Preferences.PrefGlobalTexImport, 0 ) == 1 )
|
|
resizeTextures = true;
|
|
else
|
|
resizeTextures = false;
|
|
}
|
|
|
|
// save to texture files
|
|
var textureImportData = new Dictionary<string, TextureData>();
|
|
if( !Application.isPlaying )
|
|
{
|
|
for( int i = 0; i < outputList.Count; i++ )
|
|
{
|
|
if( outputList[ i ].Active )
|
|
{
|
|
SaveTexture( ref m_outBuffers[ i ], m_fileNames[ i ], outputList[ i ].ImageFormat, (int)outputList[ i ].Scale, outputList[ i ].Channels );
|
|
textureImportData.Add( m_fileNames[ i ], new TextureData( outputList[ i ].SRGB, outputList[ i ].Compression, outputList[ i ].Channels == TextureChannels.RGBA, resizeTextures ? (int)m_data.TexSize.x / (int)outputList[ i ].Scale : -1 ) );
|
|
}
|
|
}
|
|
}
|
|
|
|
GL.sRGBWrite = chache;
|
|
|
|
GameObject impostorObject = null;
|
|
DisplayProgress( 0.65f, "Please Wait... Generating Mesh and Material" );
|
|
//RenderCombinedAlpha();
|
|
Vector4 offsetCalc = /*transform.worldToLocalMatrix **/ new Vector4( m_originalBound.center.x, m_originalBound.center.y, m_originalBound.center.z, 1 );
|
|
Vector4 offset = new Vector4( offsetCalc.x, offsetCalc.y, offsetCalc.z, -m_pixelOffset.y / m_xyFitSize/*(-pixelOffset.y / m_data.VerticalFrames) * ( m_trueFitsize / m_data.VerticalFrames )*/ );
|
|
Vector4 sizeOffset = new Vector4( m_xyFitSize, m_depthFitSize, (m_pixelOffset.x / m_xyFitSize) / (float)m_data.HorizontalFrames, (m_pixelOffset.y / m_xyFitSize) / (float)m_data.VerticalFrames );
|
|
//offset.y += pixelOffset.y;
|
|
bool justCreated = false;
|
|
UnityEngine.Object targetPrefab = null;
|
|
GameObject tempGO = null;
|
|
|
|
|
|
Mesh mesh = m_data.Mesh;
|
|
if( mesh == null )
|
|
{
|
|
mesh = GenerateMesh( m_data.ShapePoints, offset, m_xyFitSize, m_xyFitSize, true );
|
|
mesh.name = fileName;
|
|
AssetDatabase.AddObjectToAsset( mesh, m_data );
|
|
m_data.Mesh = mesh;
|
|
EditorUtility.SetDirty( mesh );
|
|
}
|
|
else
|
|
{
|
|
Mesh tempmesh = GenerateMesh( m_data.ShapePoints, offset, m_xyFitSize, m_xyFitSize, true );
|
|
EditorUtility.CopySerialized( tempmesh, mesh );
|
|
mesh.vertices = tempmesh.vertices;
|
|
mesh.triangles = tempmesh.triangles;
|
|
mesh.uv = tempmesh.uv;
|
|
mesh.normals = tempmesh.normals;
|
|
mesh.bounds = tempmesh.bounds;
|
|
mesh.name = fileName;
|
|
EditorUtility.SetDirty( mesh );
|
|
}
|
|
|
|
if( isPrefab )
|
|
{
|
|
if( m_lastImpostor != null && PrefabUtility.GetPrefabAssetType( m_lastImpostor ) == PrefabAssetType.Regular )
|
|
{
|
|
impostorObject = m_lastImpostor;
|
|
}
|
|
else
|
|
{
|
|
GameObject mainGO = new GameObject( "Impostor", new Type[] { typeof( MeshFilter ), typeof( MeshRenderer ) } );
|
|
impostorObject = mainGO;
|
|
justCreated = true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if( m_lastImpostor != null )
|
|
{
|
|
impostorObject = m_lastImpostor;
|
|
//impostorObject.transform.position = m_rootTransform.position;
|
|
//impostorObject.transform.rotation = m_rootTransform.rotation;
|
|
}
|
|
else
|
|
{
|
|
impostorObject = new GameObject( "Impostor", new Type[] { typeof( MeshFilter ), typeof( MeshRenderer ) } );
|
|
Undo.RegisterCreatedObjectUndo( impostorObject, "Create Impostor" );
|
|
impostorObject.transform.position = m_rootTransform.position;
|
|
impostorObject.transform.rotation = m_rootTransform.rotation;
|
|
|
|
justCreated = true;
|
|
}
|
|
}
|
|
m_lastImpostor = impostorObject;
|
|
impostorObject.transform.localScale = Vector3.one;
|
|
impostorObject.GetComponent<MeshFilter>().sharedMesh = mesh;
|
|
|
|
if( justCreated )
|
|
{
|
|
if( LodGroup != null )
|
|
{
|
|
if( isPrefab )
|
|
{
|
|
targetPrefab = PrefabUtility.GetPrefabInstanceHandle( ( Selection.activeObject as GameObject ).transform.root.gameObject );
|
|
GameObject targetGO = AssetDatabase.LoadAssetAtPath( folderPath + ( Selection.activeObject as GameObject ).transform.root.gameObject.name + ".prefab", typeof( GameObject ) ) as GameObject;
|
|
UnityEngine.Object inst = PrefabUtility.InstantiatePrefab( targetGO );
|
|
tempGO = inst as GameObject;
|
|
AmplifyImpostor ai = tempGO.GetComponentInChildren<AmplifyImpostor>();
|
|
impostorObject.transform.SetParent( ai.LodGroup.transform );
|
|
ai.m_lastImpostor = impostorObject;
|
|
PrefabUtility.SaveAsPrefabAssetAndConnect( tempGO, AssetDatabase.GetAssetPath( targetPrefab ), InteractionMode.AutomatedAction );
|
|
ai = targetGO.GetComponentInChildren<AmplifyImpostor>();
|
|
impostorObject = ai.m_lastImpostor;
|
|
DestroyImmediate( tempGO );
|
|
}
|
|
else
|
|
{
|
|
impostorObject.transform.SetParent( LodGroup.transform, true );
|
|
impostorObject.transform.localScale = Vector3.one;
|
|
}
|
|
|
|
switch( m_lodReplacement )
|
|
{
|
|
default:
|
|
case LODReplacement.DoNothing:
|
|
break;
|
|
case LODReplacement.ReplaceCulled:
|
|
{
|
|
LOD[] lods = LodGroup.GetLODs();
|
|
Array.Resize( ref lods, lods.Length + 1 );
|
|
LOD lastLOD = new LOD();
|
|
lastLOD.screenRelativeTransitionHeight = 0;
|
|
lastLOD.renderers = impostorObject.GetComponents<Renderer>();
|
|
lods[ lods.Length - 1 ] = lastLOD;
|
|
LodGroup.SetLODs( lods );
|
|
}
|
|
break;
|
|
case LODReplacement.ReplaceLast:
|
|
{
|
|
LOD[] lods = LodGroup.GetLODs();
|
|
|
|
foreach( Renderer item in lods[ lods.Length - 1 ].renderers )
|
|
if( item )
|
|
item.enabled = false;
|
|
|
|
lods[ lods.Length - 1 ].renderers = impostorObject.GetComponents<Renderer>();
|
|
LodGroup.SetLODs( lods );
|
|
}
|
|
break;
|
|
case LODReplacement.ReplaceAllExceptFirst:
|
|
{
|
|
LOD[] lods = LodGroup.GetLODs();
|
|
for( int i = lods.Length - 1; i > 0; i-- )
|
|
{
|
|
foreach( Renderer item in lods[ i ].renderers )
|
|
if( item )
|
|
item.enabled = false;
|
|
}
|
|
float lastTransition = lods[ lods.Length - 1 ].screenRelativeTransitionHeight;
|
|
Array.Resize( ref lods, 2 );
|
|
lods[ lods.Length - 1 ].screenRelativeTransitionHeight = lastTransition;
|
|
lods[ lods.Length - 1 ].renderers = impostorObject.GetComponents<Renderer>();
|
|
LodGroup.SetLODs( lods );
|
|
}
|
|
break;
|
|
case LODReplacement.ReplaceSpecific:
|
|
{
|
|
LOD[] lods = LodGroup.GetLODs();
|
|
foreach( Renderer item in lods[ m_insertIndex ].renderers )
|
|
if( item )
|
|
item.enabled = false;
|
|
|
|
lods[ m_insertIndex ].renderers = impostorObject.GetComponents<Renderer>();
|
|
LodGroup.SetLODs( lods );
|
|
}
|
|
break;
|
|
case LODReplacement.ReplaceAfterSpecific:
|
|
{
|
|
LOD[] lods = LodGroup.GetLODs();
|
|
for( int i = lods.Length - 1; i > m_insertIndex; i-- )
|
|
{
|
|
foreach( Renderer item in lods[ i ].renderers )
|
|
if( item )
|
|
item.enabled = false;
|
|
}
|
|
float lastTransition = lods[ lods.Length - 1 ].screenRelativeTransitionHeight;
|
|
if( m_insertIndex == lods.Length - 1 )
|
|
lastTransition = 0;
|
|
Array.Resize( ref lods, 2 + m_insertIndex );
|
|
lods[ lods.Length - 1 ].screenRelativeTransitionHeight = lastTransition;
|
|
lods[ lods.Length - 1 ].renderers = impostorObject.GetComponents<Renderer>();
|
|
LodGroup.SetLODs( lods );
|
|
}
|
|
break;
|
|
case LODReplacement.InsertAfter:
|
|
{
|
|
LOD[] lods = LodGroup.GetLODs();
|
|
Array.Resize( ref lods, lods.Length + 1 );
|
|
for( int i = lods.Length - 1; i > m_insertIndex; i-- )
|
|
{
|
|
lods[ i ].screenRelativeTransitionHeight = lods[ i - 1 ].screenRelativeTransitionHeight;
|
|
lods[ i ].fadeTransitionWidth = lods[ i - 1 ].fadeTransitionWidth;
|
|
lods[ i ].renderers = lods[ i - 1 ].renderers;
|
|
}
|
|
|
|
float firstTransition = 1;
|
|
if( m_insertIndex > 0 )
|
|
firstTransition = lods[ m_insertIndex - 1 ].screenRelativeTransitionHeight;
|
|
|
|
lods[ m_insertIndex + 1 ].renderers = impostorObject.GetComponents<Renderer>();
|
|
lods[ m_insertIndex ].screenRelativeTransitionHeight = ( lods[ m_insertIndex + 1 ].screenRelativeTransitionHeight + firstTransition ) * 0.5f;
|
|
LodGroup.SetLODs( lods );
|
|
}
|
|
break;
|
|
}
|
|
Undo.RegisterCompleteObjectUndo( LodGroup, "Create Impostor" );
|
|
}
|
|
else if( !isPrefab )
|
|
{
|
|
impostorObject.transform.SetParent( m_rootTransform.parent );
|
|
int sibIndex = m_rootTransform.GetSiblingIndex();
|
|
impostorObject.transform.SetSiblingIndex( sibIndex + 1 );
|
|
m_rootTransform.SetSiblingIndex( sibIndex );
|
|
impostorObject.transform.localScale = Vector3.one;
|
|
}
|
|
}
|
|
|
|
if( LodGroup == null )
|
|
{
|
|
Transform par = impostorObject.transform.parent;
|
|
int sibIndex = impostorObject.transform.GetSiblingIndex();
|
|
if ( !isPrefab )
|
|
{
|
|
impostorObject.transform.SetParent( m_rootTransform, true );
|
|
}
|
|
impostorObject.transform.localScale = Vector3.one;
|
|
impostorObject.transform.SetParent( par, true );
|
|
impostorObject.transform.SetSiblingIndex( sibIndex );
|
|
}
|
|
|
|
|
|
EditorUtility.SetDirty( m_data );
|
|
if( m_lastImpostor == null )
|
|
impostorObject.name = fileName;
|
|
impostorObject.GetComponent<Renderer>().sharedMaterial = material;
|
|
EditorUtility.SetDirty( impostorObject );
|
|
|
|
DisplayProgress( 0.7f, "Please Wait... Saving and Importing" );
|
|
|
|
// saving and refreshing to make sure textures can be set properly into the material
|
|
AssetDatabase.SaveAssets();
|
|
AssetDatabase.Refresh();
|
|
|
|
PostProcessTextures( textureImportData );
|
|
|
|
DisplayProgress( 0.8f, "Please Wait... Changing Texture Import Settings" );
|
|
|
|
hasDifferentResolution = false;
|
|
tex = null;
|
|
|
|
if( standardRendering )
|
|
{
|
|
for( int i = 0; i < outputList.Count; i++ )
|
|
{
|
|
tex = null;
|
|
if( outputList[ i ].Active )
|
|
{
|
|
if( material.HasProperty( m_propertyNames[ i ] ) )
|
|
tex = material.GetTexture( m_propertyNames[ i ] ) as Texture2D;
|
|
if( tex == null )
|
|
tex = AssetDatabase.LoadAssetAtPath<Texture2D>( m_fileNames[ i ] );
|
|
if( tex != null )
|
|
material.SetTexture( m_propertyNames[ i ], tex );
|
|
|
|
if( tex != null && tex.width != m_data.TexSize.x / (int)outputList[ i ].Scale )
|
|
hasDifferentResolution = true;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for( int i = 0; i < outputList.Count; i++ )
|
|
{
|
|
tex = null;
|
|
if( outputList[ i ].Active )
|
|
{
|
|
if( material.HasProperty( outputList[ i ].Name ) )
|
|
tex = material.GetTexture( outputList[ i ].Name ) as Texture2D;
|
|
if( tex == null )
|
|
tex = AssetDatabase.LoadAssetAtPath<Texture2D>( m_fileNames[ i ] );
|
|
if( tex != null )
|
|
material.SetTexture( outputList[ i ].Name, tex );
|
|
|
|
if( tex != null && tex.width != m_data.TexSize.x / (int)outputList[ i ].Scale )
|
|
hasDifferentResolution = true;
|
|
}
|
|
}
|
|
|
|
for( int i = 0; i < m_propertyNames.Length; i++ )
|
|
{
|
|
tex = null;
|
|
if( material.HasProperty( m_propertyNames[ i ] ) )
|
|
{
|
|
tex = material.GetTexture( m_propertyNames[ i ] ) as Texture2D;
|
|
if ( tex == null )
|
|
{
|
|
string filen = folderPath + fileName + m_standardFileNames[ i ] + ".tga";
|
|
tex = AssetDatabase.LoadAssetAtPath<Texture2D>( filen );
|
|
}
|
|
if ( tex == null )
|
|
{
|
|
string filen = folderPath + fileName + m_standardFileNames[ i ] + ".png";
|
|
tex = AssetDatabase.LoadAssetAtPath<Texture2D>( filen );
|
|
}
|
|
if ( tex == null )
|
|
{
|
|
string filen = folderPath + fileName + m_standardFileNames[ i ] + ".exr";
|
|
tex = AssetDatabase.LoadAssetAtPath<Texture2D>( filen );
|
|
}
|
|
if ( tex == null )
|
|
{
|
|
string filen = folderPath + fileName + m_standardFileNamesOld[ i ] + ".tga";
|
|
tex = AssetDatabase.LoadAssetAtPath<Texture2D>( filen );
|
|
}
|
|
if ( tex == null )
|
|
{
|
|
string filen = folderPath + fileName + m_standardFileNamesOld[ i ] + ".png";
|
|
tex = AssetDatabase.LoadAssetAtPath<Texture2D>( filen );
|
|
}
|
|
if ( tex == null )
|
|
{
|
|
string filen = folderPath + fileName + m_standardFileNamesOld[ i ] + ".exr";
|
|
tex = AssetDatabase.LoadAssetAtPath<Texture2D>( filen );
|
|
}
|
|
if ( tex != null )
|
|
material.SetTexture( m_propertyNames[ i ], tex );
|
|
|
|
if ( tex != null )
|
|
{
|
|
int indexFound = outputList.FindIndex( x => x.Name == m_standardFileNames[ i ] );
|
|
if ( indexFound > -1 && tex.width != ( ( int )m_data.TexSize.x / ( int )outputList[ indexFound ].Scale ) )
|
|
hasDifferentResolution = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if( m_data.ImpostorType == ImpostorType.HemiOctahedron )
|
|
{
|
|
material.SetFloat( "_Hemi", 1 );
|
|
material.SetFloat( "_AI_Hemi", 1 );
|
|
material.EnableKeyword( "_HEMI_ON" );
|
|
}
|
|
else
|
|
{
|
|
material.SetFloat( "_Hemi", 0 );
|
|
material.SetFloat( "_AI_Hemi", 0 );
|
|
material.DisableKeyword( "_HEMI_ON" );
|
|
}
|
|
|
|
material.SetFloat( "_Frames", m_data.HorizontalFrames );
|
|
material.SetFloat( "_ImpostorSize", m_xyFitSize );
|
|
material.SetVector( "_Offset", offset );
|
|
material.SetFloat( "_DepthSize", m_depthFitSize );
|
|
material.SetFloat( "_FramesX", m_data.HorizontalFrames );
|
|
material.SetFloat( "_FramesY", m_data.VerticalFrames );
|
|
material.SetVector( "_BoundsMin", m_originalBound.min );
|
|
material.SetVector( "_BoundsSize", m_originalBound.size );
|
|
|
|
material.SetFloat( "_AI_Frames", m_data.HorizontalFrames );
|
|
material.SetFloat( "_AI_ImpostorSize", m_xyFitSize );
|
|
material.SetVector( "_AI_Offset", offset );
|
|
material.SetVector( "_AI_SizeOffset", sizeOffset );
|
|
material.SetFloat( "_AI_DepthSize", m_depthFitSize );
|
|
material.SetFloat( "_AI_FramesX", m_data.HorizontalFrames );
|
|
material.SetFloat( "_AI_FramesY", m_data.VerticalFrames );
|
|
material.SetVector( "_AI_BoundsMin", m_originalBound.min );
|
|
material.SetVector( "_AI_BoundsSize", m_originalBound.size );
|
|
|
|
UpdateKeywords( material, m_data.Preset );
|
|
|
|
CheckHDRPMaterial();
|
|
|
|
//if( standardRendering && m_renderPipelineInUse == RenderPipelineInUse.HDRP )
|
|
// material.SetShaderPassEnabled( "MotionVectors", true );
|
|
|
|
EditorUtility.SetDirty( material );
|
|
|
|
if( hasDifferentResolution && resizeTextures )
|
|
resizeTextures = true;
|
|
else
|
|
resizeTextures = false;
|
|
|
|
DisplayProgress( 1f, "Complete!" );
|
|
|
|
for( int i = 0; i < outputList.Count; i++ )
|
|
{
|
|
if( outputList[ i ].Active )
|
|
ChangeTextureImporter( ref m_outBuffers[ i ], m_fileNames[ i ], outputList[i].SRGB, resizeTextures, outputList[ i ].Compression, outputList[ i ].Channels == TextureChannels.RGBA );
|
|
}
|
|
ClearBuffers();
|
|
|
|
Data.Version = VersionInfo.FullNumber;
|
|
}
|
|
#endif
|
|
|
|
private Cubemap CreateBlackCubemap()
|
|
{
|
|
var blackCubemap = new Cubemap( 1, TextureFormat.RGBA32, false );
|
|
blackCubemap.name = "BlackCube";
|
|
blackCubemap.SetPixel( CubemapFace.PositiveX, 1, 1, Color.black );
|
|
blackCubemap.SetPixel( CubemapFace.NegativeX, 1, 1, Color.black );
|
|
blackCubemap.SetPixel( CubemapFace.PositiveY, 1, 1, Color.black );
|
|
blackCubemap.SetPixel( CubemapFace.NegativeY, 1, 1, Color.black );
|
|
blackCubemap.SetPixel( CubemapFace.PositiveZ, 1, 1, Color.black );
|
|
blackCubemap.SetPixel( CubemapFace.NegativeZ, 1, 1, Color.black );
|
|
blackCubemap.Apply( false, true );
|
|
return blackCubemap;
|
|
}
|
|
|
|
private void CopyConstantStructToArray( object constants, Vector4[] array, int stride )
|
|
{
|
|
// @diogo: I know this looks dirty but couldn't find another way to do it as HighDefinition.ShaderVariablesGlobal
|
|
// is internal. Should be safe, however. This avoids having to duplicate the struct on our side and all
|
|
// the different versions/sizes for different versions of HDRP, while saving us any kind of maintenance.
|
|
GCHandle handle = GCHandle.Alloc( constants, GCHandleType.Pinned );
|
|
try
|
|
{
|
|
IntPtr src = handle.AddrOfPinnedObject();
|
|
unsafe
|
|
{
|
|
fixed ( Vector4* dst = array )
|
|
{
|
|
Buffer.MemoryCopy( ( void* )src, dst, stride, stride );
|
|
}
|
|
}
|
|
}
|
|
finally
|
|
{
|
|
// Always free the handle
|
|
handle.Free();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Renders Impostors maps to render textures
|
|
/// </summary>
|
|
/// <param name="impostorType"></param>
|
|
/// <param name="impostorMaps">set to true to render all selected maps</param>
|
|
/// <param name="combinedAlphas">set to true to render the combined alpha map which is used to generate the mesh</param>
|
|
private void RenderImpostor( int targetAmount, RenderImpostorMode mode, bool useMinResolution = false, Shader customShader = null )
|
|
{
|
|
if( targetAmount <= 0 )
|
|
return;
|
|
|
|
bool standardRendering = customShader == null;
|
|
Dictionary<Material, Material> bakeMats = new Dictionary<Material, Material>();
|
|
|
|
CommandBuffer commandBuffer = new CommandBuffer();
|
|
|
|
if ( mode == RenderImpostorMode.Normal )
|
|
{
|
|
commandBuffer.name = "GBufferCatcher";
|
|
RenderTargetIdentifier[] rtIDs = new RenderTargetIdentifier[ targetAmount ];
|
|
for( int i = 0; i < targetAmount; i++ )
|
|
{
|
|
rtIDs[ i ] = m_rtGBuffers[ i ];
|
|
}
|
|
commandBuffer.SetRenderTarget( rtIDs, m_trueDepth );
|
|
commandBuffer.ClearRenderTarget( true, true, Color.clear, 1 );
|
|
}
|
|
|
|
if ( mode == RenderImpostorMode.Alpha )
|
|
{
|
|
commandBuffer.name = "DepthAlphaCatcher";
|
|
RenderTargetIdentifier[] rtIDsAlpha = new RenderTargetIdentifier[ targetAmount ];
|
|
for( int i = 0; i < targetAmount; i++ )
|
|
{
|
|
rtIDsAlpha[ i ] = m_alphaGBuffers[ i ];
|
|
}
|
|
commandBuffer.SetRenderTarget( rtIDsAlpha, m_trueDepth );
|
|
commandBuffer.ClearRenderTarget( true, true, Color.clear, 1 );
|
|
}
|
|
|
|
GetFrameInfo( m_data, out int hframes, out int vframes );
|
|
|
|
List<MeshFilter> validMeshes = new List<MeshFilter>();
|
|
for( int i = 0; i < Renderers.Length; i++ )
|
|
{
|
|
// only allow for renderers that are enabled and not marked as shadow only
|
|
if( Renderers[ i ] == null || !Renderers[ i ].enabled || Renderers[ i ].shadowCastingMode == ShadowCastingMode.ShadowsOnly )
|
|
{
|
|
validMeshes.Add( null );
|
|
continue;
|
|
}
|
|
|
|
// skip non-meshes, for now
|
|
MeshFilter mf = Renderers[ i ].GetComponent<MeshFilter>();
|
|
if( mf == null || mf.sharedMesh == null )
|
|
{
|
|
validMeshes.Add( null );
|
|
continue;
|
|
}
|
|
|
|
validMeshes.Add( mf );
|
|
}
|
|
|
|
int validMeshesCount = validMeshes.Count;
|
|
|
|
Type constantsType = null;
|
|
ComputeBuffer constantsBuffer = null;
|
|
object constants = null;
|
|
Vector4[] constantsArray = null;
|
|
|
|
if ( m_renderPipelineInUse == RenderPipelineInUse.HDRP )
|
|
{
|
|
constantsType = Type.GetType( GlobalShaderVariablesQualifiedNameHDRP );
|
|
if ( constantsType != null )
|
|
{
|
|
constantsBuffer = new ComputeBuffer( 1, Marshal.SizeOf( constantsType ), ComputeBufferType.Constant );
|
|
constantsArray = new Vector4 [ constantsBuffer.stride / Marshal.SizeOf<Vector4>() ];
|
|
constants = Activator.CreateInstance( constantsType, nonPublic: true );
|
|
}
|
|
}
|
|
|
|
var blackCubemap = CreateBlackCubemap();
|
|
|
|
Bounds rendererBounds = new Bounds();
|
|
for ( int i = 0; i < validMeshesCount; i++ )
|
|
{
|
|
if ( validMeshes[ i ] == null )
|
|
continue;
|
|
|
|
if ( rendererBounds.size == Vector3.zero )
|
|
rendererBounds = validMeshes[ i ].sharedMesh.bounds.Transform( m_rootTransform.worldToLocalMatrix * Renderers[ i ].localToWorldMatrix );
|
|
else
|
|
rendererBounds.Encapsulate( validMeshes[ i ].sharedMesh.bounds.Transform( m_rootTransform.worldToLocalMatrix * Renderers[ i ].localToWorldMatrix ) );
|
|
}
|
|
m_originalBound = rendererBounds;
|
|
|
|
for ( int x = 0; x < hframes; x++ )
|
|
{
|
|
for( int y = 0; y < vframes; y++ )
|
|
{
|
|
int frameIndex = y * hframes + x;
|
|
|
|
float fitSize = m_xyFitSize * 0.5f;
|
|
|
|
Matrix4x4 camMatrixRot = GetCameraRotationMatrix( m_data.ImpostorType, hframes, vframes, x, y );
|
|
Bounds frameBounds = rendererBounds.Transform( camMatrixRot );
|
|
|
|
Matrix4x4 matrixP = Matrix4x4.Ortho( -fitSize + m_pixelOffset.x, fitSize + m_pixelOffset.x, -fitSize + m_pixelOffset.y, fitSize + m_pixelOffset.y, 0, -m_depthFitSize );
|
|
Matrix4x4 matrixV = Matrix4x4.Inverse( camMatrixRot ) * Matrix4x4.LookAt( frameBounds.center - new Vector3( 0, 0, m_depthFitSize * 0.5f ), frameBounds.center, Vector3.up );
|
|
matrixV = Matrix4x4.Inverse( matrixV ) * m_rootTransform.worldToLocalMatrix;
|
|
|
|
commandBuffer.SetViewProjectionMatrices( matrixV, matrixP );
|
|
|
|
matrixP = GL.GetGPUProjectionMatrix( matrixP, true );
|
|
Matrix4x4 matrixVP = matrixP * matrixV;
|
|
Matrix4x4 matrixInvVP = Matrix4x4.Inverse( matrixVP );
|
|
|
|
if ( m_renderPipelineInUse == RenderPipelineInUse.HDRP )
|
|
{
|
|
if ( constants != null )
|
|
{
|
|
constantsType.GetField( "_ViewMatrix" ).SetValue( constants, matrixV );
|
|
constantsType.GetField( "_CameraViewMatrix" ).SetValue( constants, matrixV );
|
|
constantsType.GetField( "_InvViewMatrix" ).SetValue( constants, Matrix4x4.Inverse( matrixV ) );
|
|
constantsType.GetField( "_ProjMatrix" ).SetValue( constants, matrixP );
|
|
constantsType.GetField( "_InvProjMatrix" ).SetValue( constants, Matrix4x4.Inverse( matrixP ) );
|
|
constantsType.GetField( "_ViewProjMatrix" ).SetValue( constants, matrixVP );
|
|
constantsType.GetField( "_CameraViewProjMatrix" ).SetValue( constants, matrixVP );
|
|
constantsType.GetField( "_InvViewProjMatrix" ).SetValue( constants, matrixInvVP );
|
|
constantsType.GetField( "_ProbeExposureScale" ).SetValue( constants, 1 );
|
|
}
|
|
|
|
CopyConstantStructToArray( constants, constantsArray, constantsBuffer.stride );
|
|
commandBuffer.SetBufferData( constantsBuffer, constantsArray );
|
|
commandBuffer.SetGlobalConstantBuffer( constantsBuffer, "ShaderVariablesGlobal", 0, constantsBuffer.stride );
|
|
}
|
|
else if ( m_renderPipelineInUse == RenderPipelineInUse.URP )
|
|
{
|
|
commandBuffer.SetGlobalTexture( "_GlossyEnvironmentCubeMap", blackCubemap );
|
|
}
|
|
|
|
if ( mode == RenderImpostorMode.Normal && m_cameraInvViewProjPerFrame != null )
|
|
{
|
|
m_cameraInvViewProjPerFrame[ y * hframes + x ] = Matrix4x4.Transpose( matrixInvVP );
|
|
}
|
|
|
|
commandBuffer.SetGlobalVector( "unity_SHAr", Vector4.zero );
|
|
commandBuffer.SetGlobalVector( "unity_SHAg", Vector4.zero );
|
|
commandBuffer.SetGlobalVector( "unity_SHAb", Vector4.zero );
|
|
commandBuffer.SetGlobalVector( "unity_SHBr", Vector4.zero );
|
|
commandBuffer.SetGlobalVector( "unity_SHBg", Vector4.zero );
|
|
commandBuffer.SetGlobalVector( "unity_SHBb", Vector4.zero );
|
|
commandBuffer.SetGlobalVector( "unity_SHC", Vector4.zero );
|
|
|
|
commandBuffer.SetGlobalTexture( "unity_SpecCube0", blackCubemap );
|
|
commandBuffer.SetGlobalTexture( "unity_SpecCube1", blackCubemap );
|
|
|
|
commandBuffer.SetGlobalVector( "unity_SpecCube0_HDR", Vector4.zero );
|
|
commandBuffer.SetGlobalVector( "unity_SpecCube1_HDR", Vector4.zero );
|
|
|
|
commandBuffer.SetGlobalVector( "_AI_BoundsMin", m_originalBound.min );
|
|
commandBuffer.SetGlobalVector( "_AI_BoundsSize", m_originalBound.size );
|
|
|
|
if ( mode == RenderImpostorMode.Normal )
|
|
{
|
|
float targetWidth = m_data.TexSize.x / m_data.HorizontalFrames;
|
|
float targetHeight = m_data.TexSize.y / m_data.VerticalFrames;
|
|
|
|
commandBuffer.SetViewport( new Rect( ( m_data.TexSize.x / hframes ) * x, ( m_data.TexSize.y / vframes ) * y, targetWidth, targetHeight ) );
|
|
}
|
|
else if ( mode == RenderImpostorMode.Alpha )
|
|
{
|
|
float targetWidth = MinAlphaResolution;
|
|
float targetHeight = MinAlphaResolution;
|
|
|
|
commandBuffer.SetViewport( new Rect( 0, 0, targetWidth, targetHeight ) );
|
|
}
|
|
|
|
for ( int j = 0; j < validMeshesCount; j++ )
|
|
{
|
|
if( validMeshes[ j ] == null )
|
|
continue;
|
|
|
|
// Renderer shares array position with validMesh
|
|
Material[] meshMaterials = Renderers[ j ].sharedMaterials;
|
|
|
|
// Draw Mesh
|
|
for( int k = 0; k < meshMaterials.Length; k++ )
|
|
{
|
|
Material renderMaterial = null;
|
|
Mesh mesh = validMeshes[ j ].sharedMesh;
|
|
int pass = 0;
|
|
int prePass = 0;
|
|
if( standardRendering )
|
|
{
|
|
renderMaterial = meshMaterials[ k ];
|
|
|
|
pass = renderMaterial.FindPass( "DEFERRED" );
|
|
if( pass == -1 )
|
|
pass = renderMaterial.FindPass( "Deferred" );
|
|
if( pass == -1 )
|
|
pass = renderMaterial.FindPass( "GBuffer" );
|
|
|
|
prePass = renderMaterial.FindPass( "DepthOnly" );
|
|
|
|
if( pass == -1 ) // last resort fallback
|
|
{
|
|
pass = 0;
|
|
for( int sp = 0; sp < renderMaterial.passCount; sp++ )
|
|
{
|
|
string lightmode = renderMaterial.GetTag( "LightMode", true );
|
|
if( lightmode.Equals( "Deferred" ) )
|
|
{
|
|
pass = sp;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
// Only useful for 2017.1 and 2017.2
|
|
commandBuffer.EnableShaderKeyword( "UNITY_HDR_ON" );
|
|
}
|
|
else
|
|
{
|
|
prePass = -1;
|
|
if( !bakeMats.TryGetValue( meshMaterials[ k ], out renderMaterial ) )
|
|
{
|
|
|
|
renderMaterial = new Material( customShader ) { hideFlags = HideFlags.HideAndDontSave };
|
|
renderMaterial.CopyPropertiesFromMaterial( meshMaterials[ k ] );
|
|
|
|
// @diogo: workaround for a Unity bug; not assigning default detail normal map
|
|
if ( m_renderPipelineInUse == RenderPipelineInUse.URP && meshMaterials[ k ].HasProperty( _DetailNormalMap_PID ) )
|
|
{
|
|
if ( meshMaterials[ k ].GetTexture( _DetailNormalMap_PID ) == null )
|
|
{
|
|
renderMaterial.SetTexture( _DetailNormalMap_PID, Texture2D.normalTexture );
|
|
}
|
|
}
|
|
|
|
bakeMats.Add( meshMaterials[ k ], renderMaterial );
|
|
}
|
|
}
|
|
|
|
// Setup Lightmap keywords and values
|
|
bool isUsingBakedGI = Renderers[ j ].lightmapIndex > -1;
|
|
bool isUsingRealtimeGI = Renderers[ j ].realtimeLightmapIndex > -1;
|
|
if(( isUsingBakedGI || isUsingRealtimeGI) && !standardRendering )
|
|
{
|
|
commandBuffer.EnableShaderKeyword( "LIGHTMAP_ON" );
|
|
if( isUsingBakedGI )
|
|
{
|
|
commandBuffer.SetGlobalVector( "unity_LightmapST", Renderers[ j ].lightmapScaleOffset );
|
|
}
|
|
|
|
if( isUsingRealtimeGI )
|
|
{
|
|
commandBuffer.EnableShaderKeyword( "DYNAMICLIGHTMAP_ON" );
|
|
commandBuffer.SetGlobalVector( "unity_DynamicLightmapST", Renderers[ j ].realtimeLightmapScaleOffset );
|
|
}
|
|
else
|
|
{
|
|
commandBuffer.DisableShaderKeyword( "DYNAMICLIGHTMAP_ON" );
|
|
}
|
|
|
|
if( isUsingBakedGI && isUsingRealtimeGI )
|
|
{
|
|
commandBuffer.EnableShaderKeyword( "DIRLIGHTMAP_COMBINED" );
|
|
}
|
|
else
|
|
{
|
|
commandBuffer.DisableShaderKeyword( "DIRLIGHTMAP_COMBINED" );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
commandBuffer.DisableShaderKeyword( "LIGHTMAP_ON" );
|
|
commandBuffer.DisableShaderKeyword( "DYNAMICLIGHTMAP_ON" );
|
|
commandBuffer.DisableShaderKeyword( "DIRLIGHTMAP_COMBINED" );
|
|
}
|
|
|
|
commandBuffer.DisableShaderKeyword( "LIGHTPROBE_SH" );
|
|
commandBuffer.DisableShaderKeyword( "USING_STEREO_MATRICES" );
|
|
commandBuffer.DisableShaderKeyword( "SHADEROPTIONS_CAMERA_RELATIVE_RENDERING" );
|
|
commandBuffer.DisableShaderKeyword( "WRITE_DECAL_BUFFER" );
|
|
|
|
if ( prePass > -1 )
|
|
{
|
|
commandBuffer.DrawRenderer( Renderers[ j ], renderMaterial, k, prePass );
|
|
}
|
|
|
|
commandBuffer.DrawRenderer( Renderers[ j ], renderMaterial, k, pass );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
Graphics.ExecuteCommandBuffer( commandBuffer );
|
|
|
|
if ( constantsBuffer != null )
|
|
{
|
|
constantsBuffer.Release();
|
|
}
|
|
|
|
validMeshes.Clear();
|
|
|
|
Cubemap.DestroyImmediate( blackCubemap );
|
|
|
|
foreach ( var pair in bakeMats )
|
|
{
|
|
Material bakeMat = pair.Value;
|
|
if( bakeMat != null )
|
|
{
|
|
if( !Application.isPlaying )
|
|
DestroyImmediate( bakeMat );
|
|
bakeMat = null;
|
|
}
|
|
}
|
|
bakeMats.Clear();
|
|
|
|
commandBuffer.Release();
|
|
commandBuffer = null;
|
|
}
|
|
|
|
//public void Update()
|
|
//{
|
|
// RenderAllDeferredGroups( m_data );
|
|
//}
|
|
|
|
private static Matrix4x4 GetCameraRotationMatrix( ImpostorType impostorType, int hframes, int vframes, int x, int y )
|
|
{
|
|
Matrix4x4 camMatrixRot = Matrix4x4.identity;
|
|
if( impostorType == ImpostorType.Spherical ) //SPHERICAL
|
|
{
|
|
float fractionY = 0;
|
|
if( vframes > 0 )
|
|
fractionY = -( 180.0f / ( ( float )vframes - 1 ) );
|
|
Quaternion hRot = Quaternion.Euler( fractionY * y + StartYRotation, 0, 0 );
|
|
Quaternion vRot = Quaternion.Euler( 0, ( 360.0f / hframes ) * x + StartXRotation, 0 );
|
|
camMatrixRot = Matrix4x4.Rotate( hRot * vRot );
|
|
|
|
}
|
|
else if( impostorType == ImpostorType.Octahedron ) //OCTAHEDRON
|
|
{
|
|
Vector3 forw = OctahedronToVector( ( (float)( x ) / ( (float)hframes - 1 ) ) * 2f - 1f, ( (float)( y ) / ( (float)vframes - 1 ) ) * 2f - 1f );
|
|
Quaternion octa = Quaternion.LookRotation( new Vector3( forw.x * -1, forw.z * -1, forw.y * -1 ), Vector3.up );
|
|
camMatrixRot = Matrix4x4.Rotate( octa ).inverse;
|
|
}
|
|
else if( impostorType == ImpostorType.HemiOctahedron ) //HEMIOCTAHEDRON
|
|
{
|
|
Vector3 forw = HemiOctahedronToVector( ( (float)( x ) / ( (float)hframes - 1 ) ) * 2f - 1f, ( (float)( y ) / ( (float)vframes - 1 ) ) * 2f - 1f );
|
|
Quaternion octa = Quaternion.LookRotation( new Vector3( forw.x * -1, forw.z * -1, forw.y * -1 ), Vector3.up );
|
|
camMatrixRot = Matrix4x4.Rotate( octa ).inverse;
|
|
}
|
|
return camMatrixRot;
|
|
}
|
|
|
|
private static Vector3 OctahedronToVector( Vector2 oct )
|
|
{
|
|
Vector3 N = new Vector3( oct.x, oct.y, 1.0f - Mathf.Abs( oct.x ) - Mathf.Abs( oct.y ) );
|
|
float t = Mathf.Clamp01( -N.z );
|
|
N.Set( N.x + ( N.x >= 0.0f ? -t : t ), N.y + ( N.y >= 0.0f ? -t : t ), N.z );
|
|
N = Vector3.Normalize( N );
|
|
return N;
|
|
}
|
|
|
|
private static Vector3 OctahedronToVector( float x, float y )
|
|
{
|
|
Vector3 N = new Vector3( x, y, 1.0f - Mathf.Abs( x ) - Mathf.Abs( y ) );
|
|
float t = Mathf.Clamp01( -N.z );
|
|
N.Set( N.x + ( N.x >= 0.0f ? -t : t ), N.y + ( N.y >= 0.0f ? -t : t ), N.z );
|
|
N = Vector3.Normalize( N );
|
|
return N;
|
|
}
|
|
|
|
private static Vector3 HemiOctahedronToVector( float x, float y )
|
|
{
|
|
float tempx = x;
|
|
float tempy = y;
|
|
|
|
x = ( tempx + tempy ) * 0.5f;
|
|
y = ( tempx - tempy ) * 0.5f;
|
|
Vector3 N = new Vector3( x, y, 1.0f - Mathf.Abs( x ) - Mathf.Abs( y ) );
|
|
N = Vector3.Normalize( N );
|
|
return N;
|
|
}
|
|
|
|
public void GenerateAutomaticMesh( AmplifyImpostorAsset data )
|
|
{
|
|
// create a 2d texture for calculations
|
|
Rect testRect = new Rect( 0, 0, m_alphaTex.width, m_alphaTex.height );
|
|
Vector2[][] paths;
|
|
SpriteUtilityEx.GenerateOutline( m_alphaTex, testRect, data.Tolerance, 254, false, out paths );
|
|
int sum = 0;
|
|
for( int i = 0; i < paths.Length; i++ )
|
|
{
|
|
sum += paths[ i ].Length;
|
|
}
|
|
|
|
data.ShapePoints = new Vector2[ sum ];
|
|
int index = 0;
|
|
for( int i = 0; i < paths.Length; i++ )
|
|
{
|
|
for( int j = 0; j < paths[ i ].Length; j++ )
|
|
{
|
|
data.ShapePoints[ index ] = (Vector2)( paths[ i ][ j ] ) + ( new Vector2( m_alphaTex.width * 0.5f, m_alphaTex.height * 0.5f ) );
|
|
data.ShapePoints[ index ] = Vector2.Scale( data.ShapePoints[ index ], new Vector2( 1.0f / m_alphaTex.width, 1.0f / m_alphaTex.height ) );
|
|
index++;
|
|
}
|
|
}
|
|
|
|
// make it convex hull
|
|
data.ShapePoints = Vector2Ex.ConvexHull( data.ShapePoints );
|
|
|
|
// reduce vertices
|
|
data.ShapePoints = Vector2Ex.ReduceVertices( data.ShapePoints, data.MaxVertices );
|
|
|
|
// Resize the mesh using calculated normals
|
|
data.ShapePoints = Vector2Ex.ScaleAlongNormals( data.ShapePoints, data.NormalScale );
|
|
|
|
// clamp to box (needs a cut algorithm)
|
|
for( int i = 0; i < data.ShapePoints.Length; i++ )
|
|
{
|
|
data.ShapePoints[ i ].x = Mathf.Clamp01( data.ShapePoints[ i ].x );
|
|
data.ShapePoints[ i ].y = Mathf.Clamp01( data.ShapePoints[ i ].y );
|
|
}
|
|
|
|
// make it convex hull gain to clean edges
|
|
data.ShapePoints = Vector2Ex.ConvexHull( data.ShapePoints );
|
|
|
|
// invert Y
|
|
for( int i = 0; i < data.ShapePoints.Length; i++ )
|
|
{
|
|
data.ShapePoints[ i ] = new Vector2( data.ShapePoints[ i ].x, 1 - data.ShapePoints[ i ].y );
|
|
}
|
|
}
|
|
|
|
public Mesh GenerateMesh( Vector2[] points, Vector3 offset, float width = 1, float height = 1, bool invertY = true )
|
|
{
|
|
Vector2[] newPoints = new Vector2[ points.Length ];
|
|
Vector2[] UVs = new Vector2[ points.Length ];
|
|
Array.Copy( points, newPoints, points.Length );
|
|
float halfWidth = width * 0.5f;
|
|
float halfHeight = height * 0.5f;
|
|
|
|
if( invertY )
|
|
{
|
|
for( int i = 0; i < newPoints.Length; i++ )
|
|
{
|
|
newPoints[ i ] = new Vector2( newPoints[ i ].x, 1 - newPoints[ i ].y );
|
|
}
|
|
}
|
|
|
|
Array.Copy( newPoints, UVs, newPoints.Length );
|
|
|
|
for( int i = 0; i < newPoints.Length; i++ )
|
|
{
|
|
newPoints[ i ] = new Vector2( newPoints[ i ].x * width - halfWidth + m_pixelOffset.x, newPoints[ i ].y * height - halfHeight + m_pixelOffset.y );
|
|
}
|
|
|
|
Triangulator tr = new Triangulator( newPoints );
|
|
int[] indices = tr.Triangulate();
|
|
|
|
Vector3[] vertices = new Vector3[ tr.Points.Count ];
|
|
for( int i = 0; i < vertices.Length; i++ )
|
|
{
|
|
vertices[ i ] = new Vector3( tr.Points[ i ].x, tr.Points[ i ].y, 0 );
|
|
}
|
|
|
|
//Vector4[] tangents = new Vector4[ tr.Points.Count ];
|
|
//for( int i = 0; i < vertices.Length; i++ )
|
|
//{
|
|
// tangents[ i ] = new Vector4( 1, 0, 0, 1 );
|
|
//}
|
|
|
|
Mesh mesh = new Mesh();
|
|
mesh.vertices = vertices;
|
|
mesh.uv = UVs;
|
|
//mesh.tangents = tangents;
|
|
|
|
mesh.triangles = indices;
|
|
mesh.RecalculateNormals();
|
|
mesh.bounds = new Bounds( offset, m_originalBound.size );
|
|
|
|
return mesh;
|
|
}
|
|
}
|
|
}
|