squash commits

This commit is contained in:
2025-01-07 18:54:46 +02:00
parent 855639487b
commit 62c0a21987
3632 changed files with 708443 additions and 999 deletions
+128
View File
@@ -0,0 +1,128 @@
#if Projected_Decals
// List<RendererFragment>
// TODO: Calculate slot vertex offset in mesh, store in materialfragment
// TODO: Generate DecalChunk at runtime. Pull the triangles that contain the
// captured vertexes from the sample scene.
// Make sure we can do this all at runtime.
public class Decal
{
private class RendererFragment
{
// public GeneratedMaterial generatedMaterial;
public SkinnedMeshRenderer renderer;
public List<DecalChunk> chunks;
public RendererFragment(SkinnedMeshRenderer ren)
{
renderer = ren;
chunks = new List<DecalChunk>();
}
}
private class MaterialPacket
{
public MaterialFragment materialFragment;
public GeneratedMaterial generatedMaterial;
public MaterialPacket(GeneratedMaterial gMat, MaterialFragment mFrag)
{
materialFragment = mFrag;
generatedMaterial = gMat;
}
}
public string uniqueName;
public List<DecalChunk> DecalChunks;
public Material DecalMaterial; // Decal textures must be clamped.
private void ApplyChunks(RendererFragment renderFragment)
{
SkinnedMeshRenderer smr = renderFragment.renderer;
int MatIndex = -1;
for (int i = 0; i < smr.materials.Length; i++)
{
Material m = smr.materials[i];
if (m.name == uniqueName)
{
MatIndex = i;
break;
}
}
if (MatIndex == -1)
{
Material newMaterial = new Material(DecalMaterial);
newMaterial.name = uniqueName;
List<Material> mats = new List<Material>();
smr.GetMaterials(mats);
mats.Add(newMaterial);
MatIndex = mats.Count - 1;
smr.sharedMesh.subMeshCount++;
}
List<int> decalTris = new List<int>();
foreach(DecalChunk chunk in DecalChunks)
{
decalTris.AddRange(chunk.TriangleList);
}
smr.sharedMesh.SetTriangles(decalTris, MatIndex);
}
public void ApplySubmesh(UMAData uMAData)
{
Dictionary<string, MaterialPacket> SlotsToPacket = new Dictionary<string, MaterialPacket>();
Dictionary<string, RendererFragment> RendererFragments = new Dictionary<string, RendererFragment>();
// todo: build a dictionary of the slots, and the material fragment.
foreach(var mat in uMAData.generatedMaterials.materials)
{
foreach(var slotmat in mat.materialFragments)
{
SlotsToPacket.Add(slotmat.slotData.slotName,new MaterialPacket(mat,slotmat));
}
}
foreach(DecalChunk chunk in DecalChunks)
{
if (SlotsToPacket.ContainsKey(chunk.slotName))
{
var materialPacket = SlotsToPacket[chunk.slotName];
chunk.fragment = materialPacket.materialFragment;
string rendererName = materialPacket.generatedMaterial.skinnedMeshRenderer.name;
if (!RendererFragments.ContainsKey(rendererName))
{
RendererFragments.Add(rendererName, new RendererFragment(materialPacket.generatedMaterial.skinnedMeshRenderer));
}
RendererFragments[rendererName].chunks.Add(chunk);
}
}
foreach(RendererFragment rf in RendererFragments.Values)
{
ApplyChunks(rf);
}
}
}
public class DecalChunk
{
public string slotName;
public int[] TriangleList;
public MaterialFragment fragment;
// index of vertexes in the slot.
// These need to be translate d to the mesh index after the build is complete.
// To do this, we will need to track for each slot in the UMAData (during the build process)
// What SMR the slot is actually in, in case there are multiples
// what vertex position the slot starts at in the SMR
}
#endif
@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: deba20c3855a436478ceaf5a0b2db7ef
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 35611
packageName: UMA 2
packageVersion: 2.13
assetPath: Assets/UMA/Core/Decals/Scripts/Decal.cs
uploadId: 679826
@@ -0,0 +1,271 @@
using System.Collections.Generic;
using UMA;
using Unity.Collections;
using UnityEngine;
public class DecalDefinition
{
public string Name;
public int InitialIndex;
public GameObject DecalMeshObject;
public Mesh bakedMesh;
public Plane[] planesInWorldSpace;
public int VertexNumber;
public Vector3 WorldImpactPoint;
public Vector3 LocalImpactPoint;
public Material material;
public float offset; // zbias or offset for rendering. vertex = vertex + normal * offset.
List<DecalInstance> Instances = new List<DecalInstance>();
public GameObject InstantiateSimpleDecal(GameObject umaParent, SkinnedMeshRenderer baseRenderer)
{
// instantiate a new one here???
GameObject newDecal = GameObject.Instantiate(DecalMeshObject,umaParent.transform);
SkinnedMeshRenderer smr = newDecal.GetComponent<SkinnedMeshRenderer>();
if (smr == null)
{
Debug.LogWarning("Unable to instantiate decal - no SMR");
return null;
}
// Copy bindposes from main.
// copy bones from main.
smr.sharedMesh.bindposes = baseRenderer.sharedMesh.bindposes;
smr.bones = baseRenderer.bones;
return newDecal;
}
public void AddInstance(UMAData umaData, List<int> Vertexes)
{
}
public void AddSubmesh(SkinnedMeshRenderer smr)
{
// we can't reuse the vertexes because the UV coordinates are different...
if (Instances == null)
{
return;
}
if (Instances.Count == 0)
{
return;
}
List<Material> mats = new List<Material>();
smr.GetMaterials(mats);
mats.Add(material);
// read old data
List<Vector3> Vertexes = new List<Vector3>();
List<Vector3> Normals = new List<Vector3>();
List<Vector4> Tangents = new List<Vector4>();
List<Color32> Colors = new List<Color32>();
List<Vector2> UV = new List<Vector2>();
List<Vector2> UV2 = new List<Vector2>();
List<Vector2> UV3 = new List<Vector2>();
List<Vector2> UV4 = new List<Vector2>();
List<int> Tris = new List<int>();
// TODO: these should be NativeArray. Size to mesh size + all DI sizes.
List<byte> bonesPerVertex = new List<byte>();
List<BoneWeight1> boneWeights = new List<BoneWeight1>();
Mesh mesh = smr.sharedMesh;
mesh.GetVertices(Vertexes);
mesh.GetNormals(Normals);
mesh.GetTangents(Tangents);
mesh.GetColors(Colors);
mesh.GetUVs(0, UV);
mesh.GetUVs(1, UV2);
mesh.GetUVs(2, UV3);
mesh.GetUVs(3, UV4);
bonesPerVertex.AddRange(mesh.GetBonesPerVertex());
boneWeights.AddRange(mesh.GetAllBoneWeights());
// boneweights
// bonespervertex
// blendshapes <-- later?
int baseVertex = Vertexes.Count;
foreach(DecalInstance di in Instances)
{
Vertexes.AddRange(di.vertexes);
// add the triangles
for(int i=0;i<di.TriangleList.Length;i++)
{
Tris.Add(di.TriangleList[i] + baseVertex);
}
// add the boneweights
for(int i=0;i<di.boneWeights.Length;i++)
{
BoneWeight1 bw = di.boneWeights[i];
bw.boneIndex += baseVertex;
boneWeights.Add(bw);
}
bonesPerVertex.AddRange(di.bonesPerVertex);
// go to next DecalInstance
baseVertex += di.vertexes.Length;
}
mesh.SetVertices(Vertexes);
mesh.SetNormals(Normals);
mesh.SetTangents(Tangents);
mesh.SetColors(Colors);
mesh.SetUVs(0, UV);
mesh.SetUVs(1, UV2);
mesh.SetUVs(2, UV3);
mesh.SetUVs(3, UV4);
// Set the submesh
mesh.subMeshCount++;
int newSubmesh = mesh.subMeshCount - 1;
mesh.SetIndices(Tris, MeshTopology.Triangles, newSubmesh);
// todo: these should not use temp NativeArrays, but always be NativeArrays.
var unityBonesPerVertex = new NativeArray<byte>(bonesPerVertex.ToArray(), Allocator.Persistent);
var unityBoneWeights = new NativeArray<BoneWeight1>(boneWeights.ToArray(), Allocator.Persistent);
mesh.SetBoneWeights(unityBonesPerVertex, unityBoneWeights);
unityBonesPerVertex.Dispose();
unityBoneWeights.Dispose();
}
}
public struct faceData
{
int oldFaceNumber;
Vector3 Normal;
}
public class DecalInstance
{
float offset; // z bias, or add to vertexes?
public Vector3[] vertexes; // copied from slot(s)
public Vector3[] normals; // copied from slot(s)
public Vector4[] tangents; // copied from slot(s)
public Color32[] colors32; // copied from slot(s)
public Vector2[] uv; // calculated at capture time by projecting to plane
public int[] TriangleList; // calculated at capture time (each triangle found is translated to local triangles and added to list).
public byte[] bonesPerVertex;
public BoneWeight1[] boneWeights;
/// <summary>
/// Creates a skinned decal
/// </summary>
/// <param name="t">t is the transform of the meshes game object in the scene.</param>
/// <param name="m">m is the mesh as it is *right now* in the scene, captured at the current frame.
/// It's used in place of the meshdata, and is used for everything *except* as the vertex position
/// when creating the skinned mesh!!!</param>
/// <param name="RayOrigin">RayOrigin is the origin of the ray. Used to determine if a face is "facing" the origin of the decal.</param>
/// <param name="meshData"> This is the UMAMeshData that holds all the data. This is the information
/// that is pre-bound to the rig. It's used for constructing the submesh</param>
/// <param name="planes">planes is the list of planes in world space that define the bounds</param>
/// <returns></returns>
public bool Create(Transform t, Mesh m, Vector3 RayOrigin, UMAMeshData meshData, Plane[] planes)
{
List<Vector3> newVerts = new List<Vector3>();
List<Vector3> newNormals = new List<Vector3>();
List<Vector4> newTangents = new List<Vector4>();
List<Color32> newColors32 = new List<Color32>();
List<Vector2> newUv = new List<Vector2>();
List<int> newTriangleList = new List<int>();
List<byte> newBonesPerVertex = new List<byte>();
List<BoneWeight1> newBoneWeights = new List<BoneWeight1>();
List<int> oldVertexNumbers = new List<int>();
HashSet<int> vertHash = new HashSet<int>();
// calculate the face normals for each vertex.
// int = the source vertex number from the mesh.
// List<Vector3> = the list of normals for this vertex (one from each face it's connected to).
//Dictionary<int, List<faceData>> oldVertexnumberFaceNormals = CalculateFaceNormals(m, meshData);
List<Vector3> meshVerts = new List<Vector3>();
m.GetVertices(meshVerts);
for (int i=0;i<m.vertexCount;i++)
{
// this vertex is in world space.
Vector3 vert = t.TransformPoint(meshVerts[i]);
if (OnRight(vert,planes))
{
oldVertexNumbers.Add(i);
vertHash.Add(i);
}
}
// now look through every face that has the vertexes in it.
// if the face is facing the origin, then add it.
Plane p = new Plane();
for(int i=0;i<m.subMeshCount;i++)
{
var smd = m.GetSubMesh(i);
// UMA only creates triangle lists, so if this fails, then something has changed...
if (smd.topology == MeshTopology.Triangles)
{
int[] submeshtris = m.GetIndices(i);
for (int v=0;v<smd.indexCount;v+= 3)
{
int i1 = submeshtris[v];
int i2 = submeshtris[v + 1];
int i3 = submeshtris[v + 2];
p.Set3Points(meshVerts[i1], meshVerts[i2], meshVerts[i3]);
// Does the triangle face the origin?
if (p.GetDistanceToPoint(RayOrigin) >= 0.0f)
{
if (vertHash.Contains(i1) || vertHash.Contains(i2) || vertHash.Contains(i3))
{
// Add this triangle.
// add meshVerts[i1], meshVerts[i2], meshVerts[i3] to new triangle list.
// add translation for i1,i2,i3 to lookup (new, old)
// Calculate UV for each one based on distance to UV planes
}
}
}
}
}
return false;
}
// Return true if vertex is inside plane group.
private bool OnRight(Vector3 vert, Plane[] planes)
{
foreach (Plane p in planes)
{
if (p.GetDistanceToPoint(vert) <= 0.0f)
{
return false;
}
}
return true;
}
private Dictionary<int, List<faceData>> CalculateFaceNormals(Mesh m, UMAMeshData meshData)
{
Dictionary<int, List<faceData>> retval = new Dictionary<int, List<faceData>>();
return retval;
}
// index of vertexes in the slot.
// These need to be translate d to the mesh index after the build is complete.
// To do this, we will need to track for each slot in the UMAData (during the build process)
// What SMR the slot is actually in, in case there are multiples
// what vertex position the slot starts at in the SMR
}
@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: a5960693ae28da44993cd40ca719a2a9
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 35611
packageName: UMA 2
packageVersion: 2.13
assetPath: Assets/UMA/Core/Decals/Scripts/DecalDefinition.cs
uploadId: 679826
@@ -0,0 +1,55 @@
using System;
using UnityEngine;
public class DecalIndicator : MonoBehaviour
{
#if UNITY_EDITOR
public Ray Ray;
public Vector3 LocalEuler;
public GameObject visualPlane;
public GameObject visualCube;
public Plane UVPlane;
public GameObject U1;
public GameObject U2;
public GameObject V1;
public GameObject V2;
public GameObject Front;
public GameObject Back;
// Start is called before the first frame update
void Start()
{
UVPlane = CalculatePlane(visualPlane);
}
// Update is called once per frame
void Update()
{
}
Plane CalculatePlane(GameObject go)
{
MeshFilter mf = go.GetComponent<MeshFilter>();
if (mf != null)
{
int vertcount = mf.sharedMesh.vertexCount;
int size = Convert.ToInt32(Mathf.Sqrt(vertcount));
Vector3 v1 = mf.sharedMesh.vertices[0];
Vector3 v2 = mf.sharedMesh.vertices[1];
Vector3 v3 = mf.sharedMesh.vertices[size + 1];
v1 = gameObject.transform.localToWorldMatrix * v1;
v2 = gameObject.transform.localToWorldMatrix * v2;
v3 = gameObject.transform.localToWorldMatrix * v3;
Plane p = new Plane(v1, v2, v3);
return p;
}
throw new Exception("Unable to calc plane.");
}
#endif
}
@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 59b2ea4fb624d594ca0b363db59b1b15
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 35611
packageName: UMA 2
packageVersion: 2.13
assetPath: Assets/UMA/Core/Decals/Scripts/DecalIndicator.cs
uploadId: 679826
@@ -0,0 +1,7 @@
using System.Collections.Generic;
using UnityEngine;
public class DecalManager : MonoBehaviour
{
public List<DecalDefinition> Decals = new List<DecalDefinition>();
}
@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 4aedd86eec0d63a408d954c29100a02d
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 35611
packageName: UMA 2
packageVersion: 2.13
assetPath: Assets/UMA/Core/Decals/Scripts/DecalManager.cs
uploadId: 679826
Binary file not shown.
@@ -0,0 +1,135 @@
fileFormatVersion: 2
guid: 170273e2f246de342a674afcef7c9b34
TextureImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 11
mipmaps:
mipMapMode: 0
enableMipMap: 1
sRGBTexture: 0
linearTexture: 0
fadeOut: 0
borderMipMap: 0
mipMapsPreserveCoverage: 0
alphaTestReferenceValue: 0.5
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 1
externalNormalMap: 0
heightScale: 0.0293
normalMapFilter: 1
isReadable: 0
streamingMipmaps: 0
streamingMipmapsPriority: 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: 1
nPOTScale: 1
lightmap: 0
compressionQuality: 50
spriteMode: 0
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: 1
textureShape: 1
singleChannelComponent: 0
maxTextureSizeSet: 0
compressionQualitySet: 0
textureFormatSet: 0
applyGammaDecoding: 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: Android
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:
internalID: 0
vertices: []
indices:
edges: []
weights: []
secondaryTextures: []
spritePackingTag:
pSDRemoveMatte: 0
pSDShowRemoveMatteOption: 1
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 35611
packageName: UMA 2
packageVersion: 2.13
assetPath: Assets/UMA/Core/Decals/Scripts/DecalNormal.psd
uploadId: 679826
@@ -0,0 +1,139 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &-2356353477197238579
MonoBehaviour:
m_ObjectHideFlags: 11
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: d0353a89b1f911e48b9e16bdc9f2e058, type: 3}
m_Name:
m_EditorClassIdentifier:
version: 9
--- !u!21 &2100000
Material:
serializedVersion: 8
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name: DemoDecal
m_Shader: {fileID: 4800000, guid: 933532a4fcc9baf4fa0491de14d08ed7, type: 3}
m_Parent: {fileID: 0}
m_ModifiedSerializedProperties: 0
m_ValidKeywords:
- _NORMALMAP
m_InvalidKeywords: []
m_LightmapFlags: 4
m_EnableInstancingVariants: 0
m_DoubleSidedGI: 0
m_CustomRenderQueue: -1
stringTagMap:
RenderType: Opaque
disabledShaderPasses:
- MOTIONVECTORS
m_LockedProperties:
m_SavedProperties:
serializedVersion: 3
m_TexEnvs:
- _BaseMap:
m_Texture: {fileID: 2800000, guid: dbbeae2754888b04080fa3e6711086fe, type: 3}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _BumpMap:
m_Texture: {fileID: 2800000, guid: 170273e2f246de342a674afcef7c9b34, type: 3}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _DetailAlbedoMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _DetailMask:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _DetailNormalMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _EmissionMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _MainTex:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _MetallicGlossMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _OcclusionMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _ParallaxMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _SpecGlossMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- unity_Lightmaps:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- unity_LightmapsInd:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- unity_ShadowMasks:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
m_Ints: []
m_Floats:
- _AddPrecomputedVelocity: 0
- _AlphaClip: 0
- _AlphaToMask: 0
- _Blend: 0
- _BlendModePreserveSpecular: 1
- _BumpScale: 1
- _ClearCoatMask: 0
- _ClearCoatSmoothness: 0
- _Cull: 2
- _Cutoff: 0.5
- _DetailAlbedoMapScale: 1
- _DetailNormalMapScale: 1
- _DstBlend: 0
- _DstBlendAlpha: 0
- _EnvironmentReflections: 1
- _GlossMapScale: 1
- _Glossiness: 0
- _GlossyReflections: 1
- _Metallic: 0
- _Mode: 1
- _OcclusionStrength: 1
- _Parallax: 0.02
- _QueueOffset: 0
- _ReceiveShadows: 1
- _Smoothness: 0
- _SmoothnessTextureChannel: 0
- _SpecularHighlights: 1
- _SrcBlend: 1
- _SrcBlendAlpha: 1
- _Surface: 0
- _UVSec: 0
- _WorkflowMode: 1
- _ZWrite: 1
m_Colors:
- _BaseColor: {r: 1, g: 1, b: 1, a: 1}
- _Color: {r: 1, g: 1, b: 1, a: 1}
- _EmissionColor: {r: 0, g: 0, b: 0, a: 1}
- _SpecColor: {r: 0.19999996, g: 0.19999996, b: 0.19999996, a: 1}
m_BuildTextureStacks: []
m_AllowLocking: 1
@@ -0,0 +1,15 @@
fileFormatVersion: 2
guid: f456e456d024652418f3bbb8411f239c
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 2100000
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 35611
packageName: UMA 2
packageVersion: 2.13
assetPath: Assets/UMA/Core/Decals/Scripts/DemoDecal.mat
uploadId: 679826
Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

@@ -0,0 +1,135 @@
fileFormatVersion: 2
guid: dbbeae2754888b04080fa3e6711086fe
TextureImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 11
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
grayScaleToAlpha: 0
generateCubemap: 6
cubemapConvolution: 0
seamlessCubemap: 0
textureFormat: 1
maxTextureSize: 2048
textureSettings:
serializedVersion: 2
filterMode: 1
aniso: 8
mipBias: 0
wrapU: 1
wrapV: 1
wrapW: 1
nPOTScale: 1
lightmap: 0
compressionQuality: 50
spriteMode: 0
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: 0
textureShape: 1
singleChannelComponent: 0
maxTextureSizeSet: 0
compressionQualitySet: 0
textureFormatSet: 0
applyGammaDecoding: 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: Android
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:
internalID: 0
vertices: []
indices:
edges: []
weights: []
secondaryTextures: []
spritePackingTag:
pSDRemoveMatte: 0
pSDShowRemoveMatteOption: 0
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 35611
packageName: UMA 2
packageVersion: 2.13
assetPath: Assets/UMA/Core/Decals/Scripts/DemoDecal.png
uploadId: 679826
@@ -0,0 +1,11 @@
using UnityEngine;
namespace UMA
{
public interface IUMAEventHookup
{
void HookupEvents(SlotDataAsset slot);
void Begun(UMAData umaData);
void Completed(UMAData umaData,GameObject slotObject);
}
}
@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 28db8065707fe9e448781ae42c713219
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 35611
packageName: UMA 2
packageVersion: 2.13
assetPath: Assets/UMA/Core/Decals/Scripts/IUMAEventHookup.cs
uploadId: 679826
@@ -0,0 +1,642 @@
using System.Collections.Generic;
using Unity.Collections;
using UnityEngine;
using System.IO;
using UnityEngine.SceneManagement;
#if UNITY_EDITOR
using UnityEditor;
#endif
namespace UMA
{
public class SimpleDecal : MonoBehaviour, IUMAEventHookup
{
// These need to be set by the editor.
// ****************************************************************************
public int[] boneHashes; // hash of bone names
public string[] boneNames; // the names of the bones.
public byte[] bonesPerVertex; // 1 per vertex
public BoneWeight1[] capturedBoneWeights; // weight / boneIndex
// ****************************************************************************
public BoneWeight1[] finalBoneWeights = new BoneWeight1[0]; // weight / boneIndex
private Dictionary<int, int> NameToBone = new Dictionary<int, int>();
Vector3[] translated = new Vector3[0];
public UMAMeshData meshData;
public Vector3 Offset = Vector3.zero;
public Vector3 Rotation = Vector3.zero;
// public Quaternion Orientation = Quaternion.identity;
// temp
private GameObject vmarker;
private GameObject sceneRoot;
private Scene editorScene;
private Vector3 InitialSpot;
public void Configure(string[] _boneNames, int[] _boneHashes, byte[] _bonesPerVertex, BoneWeight1[] _boneWeights)
{
// this
boneHashes = _boneHashes;
boneNames = _boneNames;
capturedBoneWeights = _boneWeights;
bonesPerVertex = _bonesPerVertex;
}
#if UNITY_EDITOR
public void SaveDecal(string Name, string newAssetPath, bool addToLibrary, SkinnedMeshRenderer smr, DecalDefinition decal,GameObject marker1, Scene editScene,GameObject Root)
{
if (newAssetPath.StartsWith(Application.dataPath))
{
newAssetPath = "Assets"+ newAssetPath.Substring(Application.dataPath.Length);
}
string meshPath = Path.Combine(newAssetPath, Name + "_Mesh.asset");
string prefabPath = Path.Combine(newAssetPath, Name+".prefab");
string slotPath = Path.Combine(newAssetPath, Name + ".asset");
/*
MeshFilter mf = decal.GetComponent<MeshFilter>();
AssetDatabase.CreateAsset(mf.mesh, meshPath );
AssetDatabase.SaveAssets();
*/
InitialSpot = decal.WorldImpactPoint;
vmarker = marker1;
editScene = editorScene;
sceneRoot = Root;
meshData = new UMAMeshData();
#if USE_TestMesh
MeshFilter mf = this.gameObject.GetComponent<MeshFilter>();
meshData.RetrieveDataFromUnityMesh(mf.sharedMesh);
// local to world (object)
// world to local (root)
Matrix4x4 mat = new Matrix4x4();
Transform root = smr.rootBone;
if (root == null)
{
foreach(Transform t in smr.transform.parent)
{
if (t.name.ToLower() == "root")
{
root = t;
break;
}
}
}
if (root == null)
{
foreach (Transform t in smr.transform.parent)
{
if (t.gameObject.GetComponent<SkinnedMeshRenderer>() == null)
{
// Maybe it's this one?
if (t.childCount > 0)
{
root = t;
break;
}
}
}
}
if (root == null)
{
mat.SetTRS(Vector3.zero, Quaternion.identity, Vector3.one);
}
else
{
mat.SetTRS(Vector3.zero, root.localRotation, Vector3.one);
}
/*
for(int i=0;i<meshData.vertices.Length;i++)
{
meshData.vertices[i] = mat.inverse * meshData.vertices[i];
}
*/
meshData.UpdateBones(smr.rootBone, smr.bones);
meshData.ManagedBoneWeights = this.finalBoneWeights; // Not right
meshData.ManagedBonesPerVertex = this.bonesPerVertex;
meshData.bindPoses = smr.sharedMesh.bindposes;
meshData.SlotName = "Decal";
meshData.clothSkinningSerialized = new Vector2[0];
#else
// get all the triangles inside the radius that face the ray.
meshData.subMeshCount = 1;
meshData.submeshes = new SubMeshTriangles[1];
meshData.submeshes[0].SetTriangles(AccumulateTriangles(decal.bakedMesh,decal.planesInWorldSpace,smr,decal.WorldImpactPoint));
meshData.vertices = smr.sharedMesh.vertices;
meshData.normals = smr.sharedMesh.normals;
meshData.uv = new Vector2[smr.sharedMesh.vertices.Length];
meshData.uv2 = smr.sharedMesh.uv2;
meshData.uv3 = smr.sharedMesh.uv3;
meshData.uv4 = smr.sharedMesh.uv4;
meshData.colors32 = smr.sharedMesh.colors32;
meshData.vertexCount = meshData.vertices.Length;
meshData.tangents = smr.sharedMesh.tangents;
meshData.bindPoses = smr.sharedMesh.bindposes;
meshData.ManagedBoneWeights = smr.sharedMesh.GetAllBoneWeights().ToArray();
meshData.ManagedBonesPerVertex = smr.sharedMesh.GetBonesPerVertex().ToArray();
meshData.SlotName = "Decal";
meshData.clothSkinningSerialized = new Vector2[0];
ProjectUV(meshData, decal.bakedMesh, decal.planesInWorldSpace,smr);
#endif
gameObject.name = Name;
GameObject prefab = PrefabUtility.SaveAsPrefabAsset(this.gameObject, prefabPath);
SlotDataAsset sda = CustomAssetUtility.CreateAsset<SlotDataAsset>(slotPath, false, Name);
sda.slotName = Name;
sda.SlotObject = prefab;
/*
mf = prefab.GetComponent<MeshFilter>();
mf.mesh = AssetDatabase.LoadAssetAtPath<Mesh>(meshPath);
SkinnedMeshRenderer smr = prefab.GetComponent<SkinnedMeshRenderer>();
smr.sharedMesh = mf.mesh;
*/
EditorUtility.SetDirty(sda);
AssetDatabase.SaveAssets();
if (addToLibrary)
{
UMAAssetIndexer.Instance.AddAsset(typeof(SlotDataAsset),Name, slotPath, sda);
EditorUtility.DisplayDialog("UMA", "Decal Created and added to library", "OK");
}
else
{
EditorUtility.DisplayDialog("UMA", "Decal Created. Don't forget to add it to the library", "OK");
}
}
private Vector2 GetUV(Plane[] planesinWS,Vector3 vertinWS)
{
Plane u0 = planesinWS[0];
Plane u1 = planesinWS[1];
Plane v0 = planesinWS[2];
Plane v1 = planesinWS[3];
float ud0 = u0.GetDistanceToPoint(vertinWS);
float ud1 = u1.GetDistanceToPoint(vertinWS);
float vd0 = v0.GetDistanceToPoint(vertinWS);
float vd1 = v1.GetDistanceToPoint(vertinWS);
float U_One = Mathf.Abs((u0.ClosestPointOnPlane(vertinWS) - u1.ClosestPointOnPlane(vertinWS)).magnitude);
float V_One = Mathf.Abs((v0.ClosestPointOnPlane(vertinWS) - v1.ClosestPointOnPlane(vertinWS)).magnitude);
return new Vector2(ud0 / U_One, vd0 / V_One);
}
/// <summary>
/// planes needs to be in worldspace!!!!!
/// </summary>
/// <param name="meshData"></param>
/// <param name="bakedMesh"></param>
/// <param name="planes"></param>
/// <param name="smr"></param>
private void ProjectUV(UMAMeshData meshData, Mesh bakedMesh, Plane[] planes, SkinnedMeshRenderer smr)
{
Matrix4x4 mat = smr.gameObject.transform.localToWorldMatrix;
NativeArray<int> tris = meshData.submeshes[0].GetTriangles();
for (int tri = 0; tri < tris.Length; tri+=3)
{
int v0index = tris[tri];
int v1index = tris[tri+1];
int v2index = tris[tri+2];
Vector3 v0 = mat * meshData.vertices[v0index];
Vector3 v1 = mat * meshData.vertices[v1index];
Vector3 v2 = mat * meshData.vertices[v2index];
Vector2 uv0 = GetUV(planes, v0);
Vector2 uv1 = GetUV(planes, v1);
Vector2 uv2 = GetUV(planes, v2);
meshData.uv[v0index] = uv0;
meshData.uv[v1index] = uv1;
meshData.uv[v2index] = uv2;
}
}
private bool PlanesContains(Plane[] planes,Vector3 vert)
{
foreach(Plane p in planes)
{
float dist = p.GetDistanceToPoint(vert);
if (dist < 0.0f)
{
return false;
}
}
return true;
}
private int[] AccumulateTriangles(Mesh bakedMesh, Plane[] planes, SkinnedMeshRenderer smr,Vector3 worldPoint)
{
//List<int> insideVertexes = new List<int>();
HashSet<int> insideVertexes = new HashSet<int>();
List<int> newTriangles = new List<int>();
/* Vector3 VertMax = new Vector3();
for(int i=0;i<bakedMesh.vertices.Length;i++)
{
Vector3 vert = smr.gameObject.transform.TransformPoint(bakedMesh.vertices[i]);
if (vert.y > VertMax.y)
{
VertMax = vert;
}
if (PlanesContains(planes,vert))
{
insideVertexes.Add(i);
}
}
GameObject.Instantiate(vmarker, VertMax, Quaternion.identity,sceneRoot.transform); */
for (int i=0;i<bakedMesh.subMeshCount;i++)
{
int[] triangles = bakedMesh.GetTriangles(i);
for (int tri = 0; tri < triangles.Length; tri += 3)
{
bool isAffected = false;
if (TriangleIntersects(tri, triangles, bakedMesh, smr, planes, worldPoint))
{
isAffected = true;
}
//if (insideVertexes.Contains(triangles[tri])) isAffected = true;
//if (insideVertexes.Contains(triangles[tri+1])) isAffected = true;
//if (insideVertexes.Contains(triangles[tri+2])) isAffected = true;
// if any indexes are inside the space,
// then we need that triangle.
if (isAffected)
{
newTriangles.Add(triangles[tri]);
newTriangles.Add(triangles[tri+1]);
newTriangles.Add(triangles[tri+2]);
}
}
}
return newTriangles.ToArray();
}
private bool TriangleIntersects(int tri, int[] triangles, Mesh bakedMesh, SkinnedMeshRenderer smr,Plane[] planes, Vector3 worldPoint)
{
// if all vertexes are on outside U1 or U2
// if all vertexes are on outside V1 or V2
// return false
//
// else return true.
Transform transform = smr.gameObject.transform;
int backu1count = 0;
int backu2count = 0;
int backv1count = 0;
int backv2count = 0;
int backf1count = 0;
int backb1count = 0;
Plane u1 = planes[0];
Plane u2 = planes[1];
Plane v1 = planes[2];
Plane v2 = planes[3];
Plane f1 = planes[4];
Plane b1 = planes[5];
for (int i=0;i<3;i++)
{
int vertexnum = triangles[tri + i];
Vector3 worldvert = transform.TransformPoint(bakedMesh.vertices[vertexnum]);
float u1dist = u1.GetDistanceToPoint(worldvert);
float u2dist = u2.GetDistanceToPoint(worldvert);
float v1dist = v1.GetDistanceToPoint(worldvert);
float v2dist = v2.GetDistanceToPoint(worldvert);
float f1dist = f1.GetDistanceToPoint(worldvert);
float b1dist = b1.GetDistanceToPoint(worldvert);
if (u1dist < 0)
{
backu1count++;
}
if (u2dist < 0)
{
backu2count++;
}
if (v1dist < 0)
{
backv1count++;
}
if (v2dist < 0)
{
backv2count++;
}
if (f1dist < 0)
{
backf1count++;
}
if (b1dist < 0)
{
backb1count++;
}
}
// Calculate plane from verts.
// if ALL vertexes are "too far", then do not include
if (backu1count == 3 || backu2count == 3)
{
return false;
}
if (backv1count == 3 || backv2count == 3)
{
return false;
}
if (backb1count == 3 || backf1count == 3)
{
return false;
}
// Get the triangle in world space
Vector3 t1 = transform.TransformPoint(bakedMesh.vertices[triangles[tri]]);
Vector3 t2 = transform.TransformPoint(bakedMesh.vertices[triangles[tri+1]]);
Vector3 t3 = transform.TransformPoint(bakedMesh.vertices[triangles[tri+2]]);
// if it faces away, don't make a decal
Plane TriWorldPlane = new Plane(t1, t2, t3);
if (TriWorldPlane.GetDistanceToPoint(worldPoint) < 0.0f)
{
return false;
}
// in the volume.
// faces the indicator.
return true;
}
#endif
#region runtime
public void UpdateBones(UMAData umaData)
{
Dictionary<int, int> HashToPosition = new Dictionary<int, int>();
SkinnedMeshRenderer renderer = umaData.GetRenderer(0);
// No new bones added
if (boneHashes.Length == renderer.bones.Length)
{
finalBoneWeights = new BoneWeight1[0];
return;
}
// build translation table if needed
// if (NameToBone.Count != renderer.bones.Length)
// {
NameToBone.Clear();
for (int i = 0; i < renderer.bones.Length; i++)
{
Transform t = renderer.bones[i];
NameToBone.Add(UMAUtils.StringToHash(t.name), i);
}
// }
// new bones added... need to translate
finalBoneWeights = new BoneWeight1[capturedBoneWeights.Length];
for (int i = 0; i < capturedBoneWeights.Length; i++)
{
BoneWeight1 oldbw = capturedBoneWeights[i];
int Hash = capturedBoneWeights[i].boneIndex;
if (umaData.skeleton.boneHashData.ContainsKey(Hash))
{
var boneData = umaData.skeleton.boneHashData[Hash];
if (NameToBone.ContainsKey(boneData.boneNameHash))
{
finalBoneWeights[i].boneIndex = NameToBone[boneData.boneNameHash];
finalBoneWeights[i].weight = capturedBoneWeights[i].weight;
//Debug.Log($"UMA Bone Found with hash {boneData.boneNameHash} name {boneData.boneTransform.name}");
}
else
{
Debug.LogError($"UMA Bone not found with hash {boneData.boneNameHash} name {boneData.boneTransform.name}");
}
}
else
{
Debug.LogError($"Decal bone not found with hash {Hash}");
}
/*
int Hash = boneHashes[boneWeights[i].boneIndex];
int Hash = capturedBoneWeights[i].boneIndex;
if (umaData.skeleton.boneHashData.ContainsKey(Hash))
{
var boneData = umaData.skeleton.boneHashData[Hash];
if (NameToBone.ContainsKey(boneData.boneNameHash))
{
finalBoneWeights[i].boneIndex = NameToBone[boneData.boneNameHash];
finalBoneWeights[i].weight = capturedBoneWeights[i].weight;
Debug.Log($"UMA Bone Found with hash {boneData.boneNameHash} name {boneData.boneTransform.name}");
}
else
{
Debug.LogError($"UMA Bone not found with hash {boneData.boneNameHash} name {boneData.boneTransform.name}");
}
}
else
{
Debug.LogError($"Decal bone not found with hash {Hash}");
} */
}
}
public bool invert;
public bool root;
public bool global;
public bool position;
Matrix4x4 GetBoneTransform(UMAData umaData)
{
Matrix4x4 mat = Matrix4x4.identity;
Quaternion rot = Quaternion.Euler(Rotation);
mat.SetTRS(Offset, rot, Vector3.one);
if (position)
{
Transform pos = umaData.skeleton.GetBoneTransform(UMAUtils.StringToHash("Position"));
if (pos != null)
{
Matrix4x4 posMat = new Matrix4x4();
posMat.SetTRS(pos.localPosition, Quaternion.identity /*pos.localRotation*/, Vector3.one);
if (invert)
{
posMat = posMat.inverse;
}
mat = mat * posMat;
}
else
{
Debug.Log("Position bone not found?");
}
}
if (global)
{
Transform global = umaData.skeleton.GetBoneTransform(UMAUtils.StringToHash("Global"));
if (global != null)
{
Matrix4x4 globalMat = new Matrix4x4();
globalMat.SetTRS(global.localPosition, Quaternion.identity/*global.localRotation*/, Vector3.one);
if (invert)
{
globalMat = globalMat.inverse;
}
mat = mat * globalMat;
}
else
{
Debug.Log("Global bone not found?");
}
}
if (root)
{
Transform root = umaData.umaRoot.transform;
if (root != null)
{
Matrix4x4 rootmat = new Matrix4x4();
rootmat.SetTRS(Vector3.zero, root.localRotation, Vector3.one);
if (invert)
{
rootmat = rootmat.inverse;
}
mat = mat * rootmat;
}
else
{
Debug.Log("Root bone not found?");
}
}
return mat;
}
Vector3[] TranslateVertices(Vector3[] verts, UMAData umaData)
{
Vector3[] tverts = new Vector3[verts.Length];
Matrix4x4 mat = GetBoneTransform(umaData);
for(int i=0;i<verts.Length;i++)
{
tverts[i] = mat * (verts[i]+Offset);
}
return tverts;
}
private void ApplyToMesh(Mesh mesh,UMAData umaData)
{
mesh.subMeshCount = 1;
mesh.triangles = new int[0];
mesh.vertices = TranslateVertices(meshData.vertices,umaData);
mesh.normals = meshData.normals;
mesh.tangents = meshData.tangents;
mesh.uv = meshData.uv;
mesh.uv2 = meshData.uv2;
mesh.uv3 = meshData.uv3;
mesh.uv4 = meshData.uv4;
mesh.colors32 = meshData.colors32;
mesh.bindposes = meshData.bindPoses;
var subMeshCount = meshData.submeshes.Length;
mesh.subMeshCount = subMeshCount;
for (int i = 0; i < subMeshCount; i++)
{
mesh.SetIndices(meshData.submeshes[i].GetTriangles(), MeshTopology.Triangles, i);
}
mesh.RecalculateBounds();
}
public GameObject ApplyTo(UMAData umaData, SkinnedMeshRenderer baseRenderer,GameObject slotObject)
{
// instantiate a new one here???
GameObject newDecal = GameObject.Instantiate(slotObject, umaData.transform);
SkinnedMeshRenderer smr = newDecal.GetComponent<SkinnedMeshRenderer>();
if (smr == null)
{
Debug.LogWarning("Unable to instantiate decal - no SMR");
return null;
}
Mesh m = new Mesh();
ApplyToMesh(m,umaData);
// Copy bindposes from main.
// copy bones from main.
smr.sharedMesh = m;
smr.sharedMesh.bindposes = baseRenderer.sharedMesh.bindposes;
smr.bones = baseRenderer.bones;
smr.sharedMesh = m;
smr.rootBone = baseRenderer.rootBone;
/* OLD BANDAGE WAY
NativeArray<byte> bpv = new NativeArray<byte>(bonesPerVertex, Allocator.Temp);
NativeArray<BoneWeight1> bweights;
bweights = new NativeArray<BoneWeight1>(finalBoneWeights, Allocator.Temp);
smr.sharedMesh.SetBoneWeights(bpv, bweights); */
smr.gameObject.SetActive(false);
smr.gameObject.SetActive(true);
return newDecal;
}
public void Begun(UMAData umaData)
{
}
public void Completed(UMAData umaData,GameObject slotObject)
{
UpdateBones(umaData);
ApplyTo(umaData, umaData.GetRenderer(0), slotObject);
}
public void HookupEvents(SlotDataAsset slot)
{
}
#endregion
}
}
@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: d32db59f6e6157442938b43899c62390
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 35611
packageName: UMA 2
packageVersion: 2.13
assetPath: Assets/UMA/Core/Decals/Scripts/SimpleDecal.cs
uploadId: 679826