You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

241 lines
9.3 KiB

// Amplify Shader Editor - Visual Shader Editing Tool
// Copyright (c) Amplify Creations, Lda <info@amplify.pt>
// Billboard based on:
// https://gist.github.com/renaudbedard/7a90ec4a5a7359712202
using System;
using UnityEngine;
using System.Collections.Generic;
namespace AmplifyShaderEditor
{
public enum BillboardType
{
Cylindrical,
Spherical
}
[Serializable]
public class BillboardOpHelper
{
public static readonly string BillboardTitleStr = " Billboard";
public static readonly string BillboardTypeStr = "Type";
public static readonly string BillboardRotIndStr = "Ignore Rotation";
public static readonly string[] BillboardCylindricalInstructions = { "//Calculate new billboard vertex position and normal",
"float3 upCamVec = float3( 0, 1, 0 )"};
public static readonly string[] BillboardSphericalInstructions = { "//Calculate new billboard vertex position and normal",
"float3 upCamVec = normalize ( UNITY_MATRIX_V._m10_m11_m12 )"};
public static readonly string[] BillboardCommonInstructions = { "float3 forwardCamVec = -normalize ( UNITY_MATRIX_V._m20_m21_m22 )",
"float3 rightCamVec = normalize( UNITY_MATRIX_V._m00_m01_m02 )",
"float4x4 rotationCamMatrix = float4x4( rightCamVec, 0, upCamVec, 0, forwardCamVec, 0, 0, 0, 0, 1 )",
"{0} = normalize( mul( float4( {0} , 0 ), rotationCamMatrix ))"};
public static readonly string[] BillboardRotDependent = { "//This unfortunately must be made to take non-uniform scaling into account",
"//Transform to world coords, apply rotation and transform back to local",
"{0} = mul( {0} , unity_ObjectToWorld ){1}",
"{0} = mul( {0} , rotationCamMatrix ){1}",
"{0} = mul( {0} , unity_WorldToObject ){1}"};
public static readonly string[] BillboardRotIndependent = { "{0}.x *= length( unity_ObjectToWorld._m00_m10_m20 )",
"{0}.y *= length( unity_ObjectToWorld._m01_m11_m21 )",
"{0}.z *= length( unity_ObjectToWorld._m02_m12_m22 )",
"{0} = mul( {0}, rotationCamMatrix )",
"{0}.xyz += unity_ObjectToWorld._m03_m13_m23",
"//Need to nullify rotation inserted by generated surface shader",
"{0} = mul( unity_WorldToObject, {0} )"};
public static readonly string[] BillboardHDRotDependent = { "//This unfortunately must be made to take non-uniform scaling into account",
"//Transform to world coords, apply rotation and transform back to local",
"{0} = mul( {0} , GetObjectToWorldMatrix() ){1}",
"{0} = mul( {0} , rotationCamMatrix ){1}",
"{0} = mul( {0} , GetWorldToObjectMatrix() ){1}"};
public static readonly string[] BillboardHDRotIndependent = { "{0}.x *= length( GetObjectToWorldMatrix()._m00_m10_m20 )",
"{0}.y *= length( GetObjectToWorldMatrix()._m01_m11_m21 )",
"{0}.z *= length( GetObjectToWorldMatrix()._m02_m12_m22 )",
"{0} = mul( {0}, rotationCamMatrix )",
"{0}.xyz += GetObjectToWorldMatrix()._m03_m13_m23",
"//Need to nullify rotation inserted by generated surface shader",
"{0} = mul( GetWorldToObjectMatrix(), {0} )"};
[SerializeField]
private bool m_isBillboard = false;
[SerializeField]
private BillboardType m_billboardType = BillboardType.Cylindrical;
[SerializeField]
private bool m_rotationIndependent = false;
public void Draw( ParentNode owner )
{
bool visible = owner.ContainerGraph.ParentWindow.InnerWindowVariables.ExpandedVertexOptions;
bool enabled = m_isBillboard;
NodeUtils.DrawPropertyGroup( owner, ref visible, ref m_isBillboard, BillboardTitleStr, () =>
{
m_billboardType = (BillboardType)owner.EditorGUILayoutEnumPopup( BillboardTypeStr, m_billboardType );
m_rotationIndependent = owner.EditorGUILayoutToggle( BillboardRotIndStr, m_rotationIndependent );
} );
owner.ContainerGraph.ParentWindow.InnerWindowVariables.ExpandedVertexOptions = visible;
if( m_isBillboard != enabled )
{
UIUtils.RequestSave();
}
}
public void FillDataCollectorWithInternalData( ref MasterNodeDataCollector dataCollector )
{
if( m_isBillboard )
{
FillDataCollector( ref dataCollector, m_billboardType, m_rotationIndependent, "v.vertex", "v.normal" , false );
}
}
// This should be called after the Vertex Offset and Vertex Normal ports are analised
public static void FillDataCollector( ref MasterNodeDataCollector dataCollector, BillboardType billboardType, bool rotationIndependent, string vertexPosValue, string vertexNormalValue, bool vertexIsFloat3 )
{
switch( billboardType )
{
case BillboardType.Cylindrical:
{
for( int i = 0; i < BillboardCylindricalInstructions.Length; i++ )
{
dataCollector.AddVertexInstruction( BillboardCylindricalInstructions[ i ] + ( dataCollector.IsTemplate ? ";" : string.Empty ), -1, true );
}
}
break;
case BillboardType.Spherical:
{
for( int i = 0; i < BillboardCylindricalInstructions.Length; i++ )
{
dataCollector.AddVertexInstruction( BillboardSphericalInstructions[ i ] + ( dataCollector.IsTemplate ? ";" : string.Empty ), -1, true );
}
}
break;
}
for( int i = 0; i < BillboardCommonInstructions.Length; i++ )
{
string value = ( i == 3 ) ? string.Format( BillboardCommonInstructions[ i ], vertexNormalValue ) : BillboardCommonInstructions[ i ];
dataCollector.AddVertexInstruction( value + ( dataCollector.IsTemplate ? ";" : string.Empty ), -1, true );
}
if( rotationIndependent )
{
for( int i = 0; i < BillboardRotIndependent.Length; i++ )
{
string value = string.Empty;
if( dataCollector.IsTemplate && dataCollector.TemplateDataCollectorInstance.CurrentSRPType == TemplateSRPType.HD )
{
value = ( i != 5 ) ? string.Format( BillboardHDRotIndependent[ i ], vertexPosValue ) : BillboardHDRotIndependent[ i ];
}
else
{
value = ( i != 5 ) ? string.Format( BillboardRotIndependent[ i ], vertexPosValue ) : BillboardRotIndependent[ i ];
}
dataCollector.AddVertexInstruction( value + ( dataCollector.IsTemplate ? ";" : string.Empty ), -1, true );
}
}
else
{
for( int i = 0; i < BillboardRotDependent.Length; i++ )
{
string value = string.Empty;
if( dataCollector.IsTemplate && dataCollector.TemplateDataCollectorInstance.CurrentSRPType == TemplateSRPType.HD )
{
value = ( i > 1 ) ? string.Format( BillboardHDRotDependent[ i ], vertexPosValue, ( vertexIsFloat3 ? ".xyz" : string.Empty ) ) : BillboardHDRotDependent[ i ];
}
else
{
value = ( i > 1 ) ? string.Format( BillboardRotDependent[ i ], vertexPosValue, ( vertexIsFloat3 ? ".xyz" : string.Empty ) ) : BillboardRotDependent[ i ];
}
dataCollector.AddVertexInstruction( value + ( dataCollector.IsTemplate ? ";" : string.Empty ), -1, true );
}
}
}
public string[] GetInternalMultilineInstructions()
{
// This method is only used on Surface ... no HD variation is needed
return GetMultilineInstructions( m_billboardType, m_rotationIndependent, "v.vertex", "v.normal" );
}
public static string[] GetMultilineInstructions( BillboardType billboardType, bool rotationIndependent, string vertexPosValue, string vertexNormalValue )
{
// This method is only used on Surface ... no HD variation is needed
List<string> body = new List<string>();
switch( billboardType )
{
case BillboardType.Cylindrical:
{
for( int i = 0; i < BillboardCylindricalInstructions.Length; i++ )
{
body.Add( BillboardCylindricalInstructions[ i ] );
}
}
break;
case BillboardType.Spherical:
{
for( int i = 0; i < BillboardCylindricalInstructions.Length; i++ )
{
body.Add( BillboardSphericalInstructions[ i ] );
}
}
break;
}
for( int i = 0; i < BillboardCommonInstructions.Length; i++ )
{
string value = ( i == 3 ) ? string.Format( BillboardCommonInstructions[ i ], vertexNormalValue ) : BillboardCommonInstructions[ i ];
body.Add( value );
}
if( rotationIndependent )
{
for( int i = 0; i < BillboardRotIndependent.Length; i++ )
{
string value = ( i != 5 ) ? string.Format( BillboardRotIndependent[ i ], vertexPosValue ) : BillboardRotIndependent[ i ];
body.Add( value );
}
}
else
{
for( int i = 0; i < BillboardRotDependent.Length; i++ )
{
string value = ( i > 1 ) ? string.Format( BillboardRotDependent[ i ], vertexPosValue ) : BillboardRotDependent[ i ];
body.Add( value );
}
}
return body.ToArray();
}
public void ReadFromString( ref uint index, ref string[] nodeParams )
{
m_isBillboard = Convert.ToBoolean( nodeParams[ index++ ] );
m_billboardType = (BillboardType)Enum.Parse( typeof( BillboardType ), nodeParams[ index++ ] );
if( UIUtils.CurrentShaderVersion() > 11007 )
{
m_rotationIndependent = Convert.ToBoolean( nodeParams[ index++ ] );
}
}
public void WriteToString( ref string nodeInfo )
{
IOUtils.AddFieldValueToString( ref nodeInfo, m_isBillboard );
IOUtils.AddFieldValueToString( ref nodeInfo, m_billboardType );
IOUtils.AddFieldValueToString( ref nodeInfo, m_rotationIndependent );
}
public bool IsBillboard { get { return m_isBillboard; } }
}
}