initial commit
This commit is contained in:
parent
6715289efe
commit
788c3389af
37645 changed files with 2526849 additions and 80 deletions
|
@ -0,0 +1,506 @@
|
|||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using Unity.Collections;
|
||||
using Unity.Collections.LowLevel.Unsafe;
|
||||
using Unity.Burst;
|
||||
using Unity.Jobs;
|
||||
using Unity.Mathematics;
|
||||
using static Unity.Mathematics.math;
|
||||
using UnityEngine;
|
||||
using System.Runtime.CompilerServices;
|
||||
using UnityEngine.UIElements;
|
||||
using System;
|
||||
|
||||
|
||||
namespace SLZ.CustomStaticBatching
|
||||
{
|
||||
public unsafe class TransferVtxDummyCompileGeneric
|
||||
{
|
||||
static IntPtr DummyCompileGeneric(NativeArray<byte> dummy)
|
||||
{
|
||||
IntPtr a = (IntPtr)NativeArrayUnsafeUtility.GetUnsafeBufferPointerWithoutChecks<byte>(dummy);
|
||||
throw new InvalidOperationException("DummyCompileGeneric is not a real method, exists solely to force compilation of a generic method");
|
||||
return a;
|
||||
}
|
||||
}
|
||||
|
||||
[BurstCompile]
|
||||
|
||||
public unsafe struct TransferVtxBuffer : IJobParallelFor
|
||||
{
|
||||
[NativeDisableUnsafePtrRestriction]
|
||||
[ReadOnly]
|
||||
public NativeArray<byte> vertIn;
|
||||
|
||||
[NativeDisableUnsafePtrRestriction]
|
||||
[ReadOnly]
|
||||
public NativeArray<byte> vertIn2;
|
||||
|
||||
[NativeDisableUnsafePtrRestriction]
|
||||
[NativeDisableParallelForRestriction]
|
||||
[WriteOnly]
|
||||
public NativeArray<byte> vertOut;
|
||||
|
||||
[NativeDisableUnsafePtrRestriction]
|
||||
[NativeDisableParallelForRestriction]
|
||||
[WriteOnly]
|
||||
public NativeArray<byte> vertOut2;
|
||||
|
||||
[ReadOnly]
|
||||
public float4x4 ObjectToWorld;
|
||||
[ReadOnly]
|
||||
public float4x4 WorldToObject;
|
||||
[ReadOnly]
|
||||
public float4 lightmapScaleOffset;
|
||||
[ReadOnly]
|
||||
public float4 dynLightmapScaleOffset;
|
||||
[ReadOnly]
|
||||
public int4 offset_strideIn_offset2_strideIn2; // Sign of the tangent is stored in the sign of strideIn2. strideIn2 should be set to 1/-1 if there is no second input buffer.
|
||||
[ReadOnly]
|
||||
public NativeArray<PackedChannel> inPackedChannelInfo;
|
||||
|
||||
[ReadOnly]
|
||||
public int4 strideOut;
|
||||
[ReadOnly]
|
||||
public NativeArray<PackedChannel> outPackedChannelInfo;
|
||||
[ReadOnly]
|
||||
public FixedList32Bytes<uint> formatToBytes;
|
||||
|
||||
public void Execute(int i)
|
||||
{
|
||||
uint id = (uint)i;
|
||||
uint vertInAdr = (id * asuint(offset_strideIn_offset2_strideIn2.y));
|
||||
uint vertOutAdr = (id * asuint(strideOut.x) + asuint(offset_strideIn_offset2_strideIn2.x));
|
||||
uint vertIn2Adr = (id * asuint(abs(offset_strideIn_offset2_strideIn2.w)));
|
||||
uint vertOut2Adr = (id * asuint(strideOut.y) + asuint(offset_strideIn_offset2_strideIn2.z));
|
||||
|
||||
WritePositionChannel(inPackedChannelInfo[0].packedData, outPackedChannelInfo[0].packedData, vertInAdr, vertOutAdr);
|
||||
WriteNormalChannel(inPackedChannelInfo[1].packedData, outPackedChannelInfo[1].packedData, vertInAdr, vertOutAdr, vertIn2Adr, vertOut2Adr);
|
||||
WriteTangentChannel(inPackedChannelInfo[2].packedData, outPackedChannelInfo[2].packedData, vertInAdr, vertOutAdr, vertIn2Adr, vertOut2Adr);
|
||||
WriteColorChannel(inPackedChannelInfo[3].packedData, outPackedChannelInfo[3].packedData, vertInAdr, vertOutAdr, vertIn2Adr, vertOut2Adr);
|
||||
|
||||
for (int r = 4; r < 12; r++)
|
||||
{
|
||||
WriteUVChannel(inPackedChannelInfo[r].packedData, outPackedChannelInfo[r].packedData, vertInAdr, vertOutAdr, vertIn2Adr, vertOut2Adr, (r == 5), (r == 6));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const int
|
||||
FORMAT_FLOAT = 4, // 0 - float
|
||||
FORMAT_HALF = 3, // 1 - half
|
||||
FORMAT_SNORM = 2, // 2 - snorm
|
||||
FORMAT_UNORM = 1; // 3 - unorm
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
static uint GetDimension(uint packedInfo)
|
||||
{
|
||||
return (packedInfo & 0x00000ffu);
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
static uint GetFormat(uint packedInfo)
|
||||
{
|
||||
return (packedInfo >> 8) & 0x00000ffu;
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
static uint GetOffset(uint packedInfo)
|
||||
{
|
||||
return (packedInfo >> 16) & 0x000000ffu;
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
static uint GetStream(uint packedInfo)
|
||||
{
|
||||
return (packedInfo >> 24);
|
||||
}
|
||||
|
||||
// UNorm to Float -------------------------------------------------------------
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
static float4 ConvertUNorm4ToFloat4(uint packedValue)
|
||||
{
|
||||
uint4 unpackedVal = new uint4(0x000000FF & packedValue, 0x000000FF & (packedValue >> 8), 0x000000FF & (packedValue >> 16), packedValue >> 24);
|
||||
float4 floatVal = (1.0f / 255.0f) * new float4(unpackedVal);
|
||||
return floatVal;
|
||||
}
|
||||
|
||||
// Float To UNorm -------------------------------------------------------------
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
static uint ConvertFloat4ToUNorm4(float4 value)
|
||||
{
|
||||
uint4 int3Val = new uint4(round(255.0f * saturate(value)));
|
||||
//int3Val = int3Val & 0x000000FFu;
|
||||
uint intVal = int3Val.x | (int3Val.y << 8) | (int3Val.z << 16) | (int3Val.w << 24);
|
||||
return intVal;
|
||||
}
|
||||
|
||||
|
||||
// SNorm to Float -------------------------------------------------------------
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
static float4 ConvertSNorm4ToFloat4(uint packedValue)
|
||||
{
|
||||
uint4 unpackedVal = new uint4(packedValue << 24, 0xFF000000 & (packedValue << 16), 0xFF000000 & (packedValue << 8), 0xFF000000 & packedValue);
|
||||
int4 unpackedSVal = asint(unpackedVal) / 0x1000000;
|
||||
float4 floatVal = max((1.0f / 127.0f) * new float4(unpackedSVal), -1.0f);
|
||||
return floatVal;
|
||||
}
|
||||
|
||||
// Float To SNorm -------------------------------------------------------------
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
static uint ConvertFloat4ToSNorm4(float4 value)
|
||||
{
|
||||
int4 intVal = new int4(round(clamp(value, -1.0f, 1.0f) * 127.0f)) * 0x1000000; // multiply by 2^24 to shift the non-sign bits 24 bits to the right, so we have an 8 bit signed int at the end of the 32-bit int
|
||||
uint4 uintVal = asuint(intVal);
|
||||
uint composite = uintVal.x >> 24 | (uintVal.y >> 16) | (uintVal.z >> 8) | (uintVal.w);
|
||||
return composite;
|
||||
}
|
||||
|
||||
// Half to Float --------------------------------------------------------------
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
static float4 ConvertHalf4toFloat4(uint2 packedValue)
|
||||
{
|
||||
uint4 unpackedInt = new uint4(packedValue.x, packedValue.x >> 16, packedValue.y, packedValue.y >> 16);
|
||||
return f16tof32(unpackedInt);
|
||||
}
|
||||
|
||||
// Float to Half --------------------------------------------------------------
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
static uint2 ConvertFloat4ToHalf4(float4 value)
|
||||
{
|
||||
uint4 halfInt = f32tof16(value);
|
||||
return new uint2(halfInt.x | (halfInt.y << 16), halfInt.z | (halfInt.w << 16));
|
||||
}
|
||||
// Write a float, int, half2, short2, unorm4, or snorm4 to the buffer
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
static void Write4Bytes(uint value, uint adr, ref NativeArray<byte> buffer)
|
||||
{
|
||||
uint* ptr = (uint*)((byte*)NativeArrayUnsafeUtility.GetUnsafeBufferPointerWithoutChecks(buffer) + adr);
|
||||
*ptr = value;
|
||||
}
|
||||
|
||||
// write a float2, int2, half4 or short4 to the buffer
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
static void Write8Bytes(uint2 value, uint adr, ref NativeArray<byte> buffer)
|
||||
{
|
||||
uint* ptr = (uint*)((byte*)NativeArrayUnsafeUtility.GetUnsafeBufferPointerWithoutChecks(buffer) + adr);
|
||||
ptr[0] = value.x;
|
||||
ptr[1] = value.y;
|
||||
}
|
||||
|
||||
// write a float3 or int3 to the buffer
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
static void Write12Bytes(uint3 value, uint adr, ref NativeArray<byte> buffer)
|
||||
{
|
||||
uint* ptr = (uint*)((byte*)NativeArrayUnsafeUtility.GetUnsafeBufferPointerWithoutChecks(buffer) + adr);
|
||||
ptr[0] = value.x;
|
||||
ptr[1] = value.y;
|
||||
ptr[2] = value.z;
|
||||
}
|
||||
|
||||
// write a float4 or int4 to the buffer
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
static void Write16Bytes(uint4 value, uint adr, ref NativeArray<byte> buffer)
|
||||
{
|
||||
uint* ptr = (uint*)((byte*)NativeArrayUnsafeUtility.GetUnsafeBufferPointerWithoutChecks(buffer) + adr);
|
||||
ptr[0] = value.x;
|
||||
ptr[1] = value.y;
|
||||
ptr[2] = value.z;
|
||||
ptr[3] = value.w;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
void WriteValue(NativeArray<byte> buffer, uint adr, uint format, uint dimension, uint4 value)
|
||||
{
|
||||
uint byteCount = formatToBytes[asint(format)];
|
||||
uint byteCase = byteCount * dimension;
|
||||
switch (byteCase)
|
||||
{
|
||||
case 4:
|
||||
Write4Bytes(value.x, adr, ref buffer);
|
||||
break;
|
||||
case 8:
|
||||
Write8Bytes(value.xy, adr, ref buffer);
|
||||
break;
|
||||
case 12:
|
||||
Write12Bytes(value.xyz, adr, ref buffer);
|
||||
break;
|
||||
case 16:
|
||||
Write16Bytes(value, adr, ref buffer);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
void WriteValueVtx(NativeArray<byte> buffer, uint adr, uint format, uint dimension, uint4 value)
|
||||
{
|
||||
uint byteCount = formatToBytes[asint(format)];
|
||||
uint byteCase = byteCount * dimension;
|
||||
switch (byteCase)
|
||||
{
|
||||
case 8:
|
||||
Write8Bytes(value.xy, adr, ref buffer);
|
||||
break;
|
||||
case 12:
|
||||
Write12Bytes(value.xyz, adr, ref buffer);
|
||||
break;
|
||||
default:
|
||||
Write12Bytes(uint3(1, 1, 1), adr, ref buffer);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
void WriteValueNorm(NativeArray<byte> buffer, uint adr, uint format, uint dimension, uint4 value)
|
||||
{
|
||||
uint byteCount = formatToBytes[asint(format)];
|
||||
uint byteCase = byteCount * dimension;
|
||||
switch (byteCase)
|
||||
{
|
||||
case 4:
|
||||
Write4Bytes(value.x, adr, ref buffer);
|
||||
break;
|
||||
case 8:
|
||||
Write8Bytes(value.xy, adr, ref buffer);
|
||||
break;
|
||||
case 12:
|
||||
Write12Bytes(value.xyz, adr, ref buffer);
|
||||
break;
|
||||
default:
|
||||
Write8Bytes(new uint2(0xFFFFFFFF, 0xFFFFFFFF), adr, ref buffer);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
void WriteValueTanColor(NativeArray<byte> buffer, uint adr, uint format, uint dimension, uint4 value)
|
||||
{
|
||||
uint byteCount = formatToBytes[asint(format)];
|
||||
uint byteCase = byteCount * dimension;
|
||||
switch (byteCase)
|
||||
{
|
||||
case 4:
|
||||
Write4Bytes(value.x, adr, ref buffer);
|
||||
break;
|
||||
case 8:
|
||||
Write8Bytes(value.xy, adr, ref buffer);
|
||||
break;
|
||||
case 16:
|
||||
Write16Bytes(value, adr, ref buffer);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
static float4 ConvertRawToFloat(uint4 value, uint format)
|
||||
{
|
||||
format = clamp(format, FORMAT_UNORM, FORMAT_FLOAT);
|
||||
switch (format)
|
||||
{
|
||||
case FORMAT_UNORM: // unorm
|
||||
return ConvertUNorm4ToFloat4(value.x);
|
||||
break;
|
||||
case FORMAT_SNORM: // snorm
|
||||
return ConvertSNorm4ToFloat4(value.x);
|
||||
break;
|
||||
case FORMAT_HALF: // half
|
||||
return ConvertHalf4toFloat4(value.xy);
|
||||
break;
|
||||
case FORMAT_FLOAT: // float
|
||||
return asfloat(value);
|
||||
break;
|
||||
default:
|
||||
return float4(0, 0, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
static uint4 ConvertFloatToRaw(float4 value, uint format)
|
||||
{
|
||||
format = clamp(format, FORMAT_UNORM, FORMAT_FLOAT);
|
||||
switch (format)
|
||||
{
|
||||
case FORMAT_FLOAT: // float
|
||||
return asuint(value);
|
||||
break;
|
||||
case FORMAT_HALF: // half
|
||||
return uint4(ConvertFloat4ToHalf4(value), 0, 0);
|
||||
break;
|
||||
case FORMAT_SNORM: // snorm
|
||||
return uint4(ConvertFloat4ToSNorm4(value), 0, 0, 0);
|
||||
break;
|
||||
case FORMAT_UNORM: // unorm
|
||||
return uint4(ConvertFloat4ToUNorm4(value), 0, 0, 0);
|
||||
break;
|
||||
default:
|
||||
return uint4(0, 0, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static uint4 Load4(NativeArray<byte> array, uint address)
|
||||
{
|
||||
byte* baseAddress = (byte*)NativeArrayUnsafeUtility.GetUnsafeBufferPointerWithoutChecks(array);
|
||||
int alignedAddress = (int)(address >> 2); // divide by 4 to get the 4 byte aligned address
|
||||
byte* ptr = baseAddress + (alignedAddress << 2);
|
||||
|
||||
|
||||
int arrayLength = array.Length >> 2;
|
||||
int remainingSize = arrayLength - alignedAddress;
|
||||
|
||||
if (remainingSize >= 4)
|
||||
{
|
||||
uint4* uPtr = (uint4*)ptr;
|
||||
uint4 outp = *uPtr;
|
||||
return outp;
|
||||
}
|
||||
|
||||
switch (remainingSize)
|
||||
{
|
||||
case 3:
|
||||
uint3* uPtr3 = (uint3*)ptr;
|
||||
uint4 outp3 = new uint4(uPtr3->x, uPtr3->y, uPtr3->z, 0u);
|
||||
return outp3;
|
||||
case 2:
|
||||
uint2* uPtr2 = (uint2*)ptr;
|
||||
uint4 outp2 = new uint4(uPtr2->x, uPtr2->y, 0u, 0u);
|
||||
return outp2;
|
||||
case 1:
|
||||
uint* uPtr1 = (uint*)ptr;
|
||||
uint4 outp1 = new uint4(*uPtr1, 0u, 0u, 0u);
|
||||
return outp1;
|
||||
default:
|
||||
return new uint4(1, 2, 3, 4);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void WritePositionChannel(uint inPackedInfo, uint outPackedInfo, uint vertInAdr, uint vertOutAdr)
|
||||
{
|
||||
uint address = vertInAdr; // The vertex position should always be at the start of the struct, don't bother adding the offset
|
||||
uint4 rawData = Load4(vertIn, address);
|
||||
uint inFmt = GetFormat(inPackedInfo);
|
||||
float4 position = ConvertRawToFloat(rawData, inFmt);
|
||||
position.w = 1;
|
||||
position = mul(ObjectToWorld, position);
|
||||
rawData = asuint(position);
|
||||
uint outAddress = vertOutAdr; // The vertex position should always be at the start of the struct, don't bother adding the offset
|
||||
WriteValueVtx(vertOut, outAddress, FORMAT_FLOAT, 3, rawData);
|
||||
}
|
||||
|
||||
void WriteNormalChannel(uint inPackedInfo, uint outPackedInfo, uint vertInAdr, uint vertOutAdr, uint vertInAdr2, uint vertOutAdr2)
|
||||
{
|
||||
|
||||
if (GetDimension(inPackedInfo) > 0u) // output mesh guaranteed to have the channel if an input mesh has it
|
||||
{
|
||||
bool altIn = false;// GetStream(inPackedInfo) > 0;
|
||||
uint address = (altIn ? vertInAdr2 : vertInAdr) + GetOffset(inPackedInfo);
|
||||
uint4 rawData = Load4(altIn ? vertIn2 : vertIn, address);
|
||||
uint inFmt = GetFormat(inPackedInfo);
|
||||
|
||||
float3 normal = ConvertRawToFloat(rawData, inFmt).xyz; // normal is always 3 components, if not wtf are you doing?
|
||||
|
||||
// Transform to world space using the appropriate method for normals, but don't bother normalizing the normal. The shader is going to do that anyways, and this ensures the rendering behavior of the combined mesh matches the indiviudal mesh
|
||||
normal = mul(normal, (float3x3)WorldToObject);
|
||||
|
||||
uint outFmt = GetFormat(outPackedInfo);
|
||||
if (outFmt == FORMAT_SNORM)
|
||||
{
|
||||
normal = normalize(normal);
|
||||
}
|
||||
outFmt = clamp(outFmt, FORMAT_SNORM, FORMAT_FLOAT);
|
||||
rawData = ConvertFloatToRaw(float4(normal.xyz, 0), outFmt);
|
||||
bool altOut = GetStream(outPackedInfo) > 0;
|
||||
uint outAddress = (altOut ? vertOutAdr2 : vertOutAdr) + GetOffset(outPackedInfo);
|
||||
uint outDimension = GetDimension(outPackedInfo);
|
||||
// uint inByteEnum = clamp(GetByteCountEnum(inPackedInfo), BYTECOUNT_3, BYTECOUNT_12); //always 3 components, can be snorm to float
|
||||
|
||||
WriteValueNorm(altOut ? vertOut2 : vertOut, outAddress, outFmt, outDimension, rawData);
|
||||
}
|
||||
}
|
||||
|
||||
void WriteTangentChannel(uint inPackedInfo, uint outPackedInfo, uint vertInAdr, uint vertOutAdr, uint vertInAdr2, uint vertOutAdr2)
|
||||
{
|
||||
if (GetDimension(inPackedInfo) > 0u) // output mesh guaranteed to have the channel if an input mesh has it
|
||||
{
|
||||
bool altIn = GetStream(inPackedInfo) > 0;
|
||||
uint address = (altIn ? vertInAdr2 : vertInAdr) + GetOffset(inPackedInfo);
|
||||
uint4 rawData = Load4(altIn ? vertIn2 : vertIn, address);
|
||||
uint inFmt = GetFormat(inPackedInfo);
|
||||
|
||||
float4 tangent = ConvertRawToFloat(rawData, inFmt); // tangent is always 4 components, if not wtf are you doing?
|
||||
|
||||
// Transform to world space, and flip the direction if the mesh is scaled negatively.
|
||||
tangent.xyz = mul((float3x3)ObjectToWorld, tangent.xyz);
|
||||
tangent.w *= offset_strideIn_offset2_strideIn2.w < 0 ? -1 : 1; // sign of tanget stored in sign of strideIn2
|
||||
|
||||
uint outFmt = GetFormat(outPackedInfo);
|
||||
outFmt = clamp(outFmt, FORMAT_SNORM, FORMAT_FLOAT);
|
||||
if (outFmt == FORMAT_SNORM)
|
||||
{
|
||||
tangent.xyz = normalize(tangent.xyz);
|
||||
}
|
||||
rawData = ConvertFloatToRaw(tangent, outFmt);
|
||||
bool altOut = GetStream(outPackedInfo) > 0;
|
||||
uint outAddress = (altOut ? vertOutAdr2 : vertOutAdr) + GetOffset(outPackedInfo);
|
||||
//uint inByteEnum = clamp(GetByteCountEnum(inPackedInfo), BYTECOUNT_4, BYTECOUNT_16); //always 4 components, can be snorm to float
|
||||
WriteValueTanColor(altOut ? vertOut2 : vertOut, outAddress, outFmt, 4, rawData);
|
||||
}
|
||||
}
|
||||
|
||||
void WriteColorChannel(uint inPackedInfo, uint outPackedInfo, uint vertInAdr, uint vertOutAdr, uint vertInAdr2, uint vertOutAdr2)
|
||||
{
|
||||
if ((outPackedInfo & 0x0000000fu) > 0u) // input mesh not guaranteed to have color even if the output does, so we have to initialize the color to 1,1,1,1 if the input is missing the attribute
|
||||
{
|
||||
float4 color;
|
||||
uint4 rawData;
|
||||
uint inDimension = GetDimension(inPackedInfo);
|
||||
if (inDimension > 0u)
|
||||
{
|
||||
bool altIn = GetStream(inPackedInfo) > 0;
|
||||
uint address = (altIn ? vertInAdr2 : vertInAdr) + GetOffset(inPackedInfo);
|
||||
rawData = Load4(altIn ? vertIn2 : vertIn, address);
|
||||
uint inFmt = GetFormat(inPackedInfo);
|
||||
color = ConvertRawToFloat(rawData, inFmt);
|
||||
}
|
||||
else
|
||||
{
|
||||
color = float4(1, 1, 1, 1);
|
||||
}
|
||||
|
||||
uint outFmt = GetFormat(outPackedInfo);
|
||||
rawData = ConvertFloatToRaw(color, outFmt);
|
||||
bool altOut = GetStream(outPackedInfo) > 0;
|
||||
uint outAddress = (altOut ? vertOutAdr2 : vertOutAdr) + GetOffset(outPackedInfo);
|
||||
uint outDimension = GetDimension(outPackedInfo);
|
||||
//uint inByteEnum = GetByteCountEnum(inPackedInfo);
|
||||
WriteValueTanColor(altOut ? vertOut2 : vertOut, outAddress, outFmt, outDimension, rawData);
|
||||
}
|
||||
}
|
||||
|
||||
void WriteUVChannel(uint inPackedInfo, uint outPackedInfo, uint vertInAdr, uint vertOutAdr, uint vertInAdr2, uint vertOutAdr2, bool lmScaleOffset, bool dynLmScaleOffset)
|
||||
{
|
||||
if (GetDimension(inPackedInfo) > 0u) // output mesh guaranteed to have the channel if an input mesh has it
|
||||
{
|
||||
bool altIn = GetStream(inPackedInfo) > 0;
|
||||
uint address = (altIn ? vertInAdr2 : vertInAdr) + GetOffset(inPackedInfo);
|
||||
uint4 rawData = Load4(altIn ? vertIn2 : vertIn, address);
|
||||
uint inFmt = GetFormat(inPackedInfo);
|
||||
|
||||
float4 UV = ConvertRawToFloat(rawData, inFmt);
|
||||
if (lmScaleOffset)
|
||||
UV.xy = UV.xy * lightmapScaleOffset.xy + lightmapScaleOffset.zw;
|
||||
if (lmScaleOffset)
|
||||
UV.xy = UV.xy * dynLightmapScaleOffset.xy + dynLightmapScaleOffset.zw;
|
||||
uint outFmt = GetFormat(outPackedInfo);
|
||||
rawData = ConvertFloatToRaw(UV, outFmt);
|
||||
bool altOut = GetStream(outPackedInfo) > 0;
|
||||
uint outAddress = (altOut ? vertOutAdr2 : vertOutAdr) + GetOffset(outPackedInfo);
|
||||
uint inDimension = GetDimension(inPackedInfo);
|
||||
//uint inByteEnum = GetByteCountEnum(inPackedInfo);
|
||||
WriteValue(altOut ? vertOut2 : vertOut, outAddress, outFmt, inDimension, rawData);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 16a63a8e8ff53de46b9d0e058e78f824
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,145 @@
|
|||
// Derived from https://github.com/keijiro/Akvfx/blob/master/Packages/jp.keijiro.akvfx/Runtime/Internal/Extensions.cs
|
||||
|
||||
using UnityEngine;
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using UnityEngine.Rendering;
|
||||
|
||||
namespace SLZ.CustomStaticBatching
|
||||
{
|
||||
public static class CSBBufferExt
|
||||
{
|
||||
// SetData with ReadOnlySpan
|
||||
public unsafe static void SetFromSpan<T>
|
||||
(ComputeBuffer buffer, ReadOnlySpan<T> data) where T : unmanaged
|
||||
{
|
||||
fixed (T* pData = &data.GetPinnableReference())
|
||||
SetData(buffer, (IntPtr)pData, data.Length, sizeof(T));
|
||||
}
|
||||
|
||||
public unsafe static void SetFromSpan<T>
|
||||
(ComputeBuffer buffer, Span<T> data) where T : unmanaged
|
||||
{
|
||||
fixed (T* pData = &data.GetPinnableReference())
|
||||
SetData(buffer, (IntPtr)pData, data.Length, sizeof(T));
|
||||
}
|
||||
|
||||
public unsafe static void SetFromSpan<T>
|
||||
(ComputeBuffer buffer, ReadOnlySpan<T> data, int spanStartIndex, int bufferStartIndex, int count) where T : unmanaged
|
||||
{
|
||||
fixed (T* pData = &data.GetPinnableReference())
|
||||
SetCBNativeDelegate(buffer, (IntPtr)pData, spanStartIndex, bufferStartIndex, count, sizeof(T));
|
||||
}
|
||||
|
||||
public unsafe static void SetFromSpan<T>
|
||||
(ComputeBuffer buffer, Span<T> data, int spanStartIndex, int bufferStartIndex, int count) where T : unmanaged
|
||||
{
|
||||
fixed (T* pData = &data.GetPinnableReference())
|
||||
SetCBNativeDelegate(buffer, (IntPtr)pData, spanStartIndex, bufferStartIndex, count, sizeof(T));
|
||||
}
|
||||
|
||||
public unsafe static void CmdSetFromSpan<T>
|
||||
(CommandBuffer cmd, ComputeBuffer buffer, ReadOnlySpan<T> data) where T : unmanaged
|
||||
{
|
||||
fixed (T* pData = &data.GetPinnableReference())
|
||||
CmdSetData(cmd, buffer, (IntPtr)pData, data.Length, sizeof(T));
|
||||
}
|
||||
|
||||
public unsafe static void CmdSetFromSpan<T>
|
||||
(CommandBuffer cmd, ComputeBuffer buffer, Span<T> data) where T : unmanaged
|
||||
{
|
||||
fixed (T* pData = &data.GetPinnableReference())
|
||||
CmdSetData(cmd, buffer, (IntPtr)pData, data.Length, sizeof(T));
|
||||
}
|
||||
|
||||
public unsafe static void CmdSetFromSpan<T>
|
||||
(CommandBuffer cmd, ComputeBuffer buffer, ReadOnlySpan<T> data, int spanStartIndex, int bufferStartIndex, int count) where T : unmanaged
|
||||
{
|
||||
fixed (T* pData = &data.GetPinnableReference())
|
||||
SetCmdNativeDelegate(cmd, buffer, (IntPtr)pData, spanStartIndex, bufferStartIndex, count, sizeof(T));
|
||||
}
|
||||
|
||||
public unsafe static void CmdSetFromSpan<T>
|
||||
(CommandBuffer cmd, ComputeBuffer buffer, Span<T> data, int spanStartIndex, int bufferStartIndex, int count) where T : unmanaged
|
||||
{
|
||||
fixed (T* pData = &data.GetPinnableReference())
|
||||
SetCmdNativeDelegate(cmd, buffer, (IntPtr)pData, spanStartIndex, bufferStartIndex, count, sizeof(T));
|
||||
}
|
||||
|
||||
// Directly load an unmanaged data array to a compute buffer via an
|
||||
// Intptr. This is not a public interface so will be broken one day.
|
||||
// DO NOT TRY AT HOME.
|
||||
static void SetData
|
||||
(ComputeBuffer buffer, IntPtr pointer, int count, int stride)
|
||||
{
|
||||
/*
|
||||
_args5[0] = pointer;
|
||||
_args5[1] = 0; // source offset
|
||||
_args5[2] = 0; // buffer offset
|
||||
_args5[3] = count;
|
||||
_args5[4] = stride;
|
||||
|
||||
SetNativeData.Invoke(buffer, _args5);
|
||||
*/
|
||||
SetCBNativeDelegate(buffer, pointer, 0, 0, count, stride);
|
||||
}
|
||||
|
||||
static void CmdSetData
|
||||
(CommandBuffer cmd, ComputeBuffer buffer, IntPtr pointer, int count, int stride)
|
||||
{
|
||||
/*
|
||||
_args6[0] = buffer;
|
||||
_args6[1] = pointer;
|
||||
_args6[2] = 0; // source offset
|
||||
_args6[3] = 0; // buffer offset
|
||||
_args6[4] = count;
|
||||
_args6[5] = stride;
|
||||
|
||||
CmdSetNativeData.Invoke(cmd, _args6);
|
||||
*/
|
||||
SetCmdNativeDelegate(cmd, buffer, pointer, 0, 0, count, stride);
|
||||
}
|
||||
|
||||
static MethodInfo _setNativeData;
|
||||
static MethodInfo _cmdSetNativeData;
|
||||
|
||||
static MethodInfo SetNativeData
|
||||
=> _setNativeData ?? (_setNativeData = GetSetNativeDataMethod());
|
||||
|
||||
static MethodInfo CmdSetNativeData
|
||||
=> _cmdSetNativeData ?? (_cmdSetNativeData = GetCmdSetNativeDataMethod());
|
||||
|
||||
static MethodInfo GetSetNativeDataMethod()
|
||||
=> typeof(ComputeBuffer).GetMethod("InternalSetNativeData",
|
||||
BindingFlags.InvokeMethod |
|
||||
BindingFlags.NonPublic |
|
||||
BindingFlags.Instance);
|
||||
static MethodInfo GetCmdSetNativeDataMethod()
|
||||
=> typeof(CommandBuffer).GetMethod("InternalSetComputeBufferNativeData",
|
||||
BindingFlags.InvokeMethod |
|
||||
BindingFlags.NonPublic |
|
||||
BindingFlags.Instance);
|
||||
|
||||
static Action<ComputeBuffer, IntPtr, int, int, int, int> _setCBNativeDelegate;
|
||||
static Action<CommandBuffer, ComputeBuffer, IntPtr, int, int, int, int> _setCmdNativeDelegate;
|
||||
|
||||
static Action<ComputeBuffer, IntPtr, int, int, int, int> GetCBNativeDelegate()
|
||||
{
|
||||
MethodInfo method = GetSetNativeDataMethod();
|
||||
return (Action<ComputeBuffer, IntPtr, int, int, int, int>)Delegate.CreateDelegate(typeof(Action<ComputeBuffer, IntPtr, int, int, int, int>), method);
|
||||
}
|
||||
|
||||
static Action<CommandBuffer, ComputeBuffer, IntPtr, int, int, int, int> GetCmdNativeDelegate()
|
||||
{
|
||||
MethodInfo method = GetCmdSetNativeDataMethod();
|
||||
return (Action<CommandBuffer, ComputeBuffer, IntPtr, int, int, int, int>)Delegate.CreateDelegate(typeof(Action<CommandBuffer, ComputeBuffer, IntPtr, int, int, int, int>), method);
|
||||
}
|
||||
|
||||
static Action<ComputeBuffer, IntPtr, int, int, int, int> SetCBNativeDelegate => _setCBNativeDelegate ?? (_setCBNativeDelegate = GetCBNativeDelegate());
|
||||
static Action<CommandBuffer, ComputeBuffer, IntPtr, int, int, int, int> SetCmdNativeDelegate => _setCmdNativeDelegate ?? (_setCmdNativeDelegate = GetCmdNativeDelegate());
|
||||
|
||||
//static object[] _args5 = new object[5];
|
||||
//static object[] _args6 = new object[6];
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 5970ca602db4c0142a9f093446f61387
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,27 @@
|
|||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.CompilerServices;
|
||||
using Unity.Collections.LowLevel.Unsafe;
|
||||
using UnityEngine;
|
||||
|
||||
public unsafe static class CSBListExt
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static T[] GetInternalArray<T>(List<T> list)
|
||||
{
|
||||
if (list == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
ListInternals<T> tListAccess = UnsafeUtility.As<List<T>, ListInternals<T>>(ref list);
|
||||
return tListAccess._items;
|
||||
}
|
||||
|
||||
// Copied from 2023's version of the NoAllocHelpers, names and order are magic and should not be changed
|
||||
private class ListInternals<T>
|
||||
{
|
||||
internal T[] _items;
|
||||
internal int _size;
|
||||
internal int _version;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: aa24622bb4d79604ea1ceebd6e6f6b16
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,261 @@
|
|||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using Unity.Collections;
|
||||
using UnityEngine.Rendering;
|
||||
using Unity.Jobs;
|
||||
using Unity.Burst;
|
||||
using Unity.Profiling;
|
||||
using System.Threading;
|
||||
using Unity.Collections.LowLevel.Unsafe;
|
||||
using Unity.Mathematics;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace SLZ.CustomStaticBatching
|
||||
{
|
||||
public class MeshUtilities
|
||||
{
|
||||
static ProfilerMarker profilerGetSubMesh = new ProfilerMarker("MeshUtilities.GetSplitMesh");
|
||||
static ProfilerMarker profilerGetSubMeshMarkUsed = new ProfilerMarker("MeshUtilities.GetSplitMesh.MarkUsed");
|
||||
static ProfilerMarker profilerGetSubMeshHashMap = new ProfilerMarker("MeshUtilities.GetSplitMesh.HashMap");
|
||||
static ProfilerMarker profilerGetSubMeshInitMesh = new ProfilerMarker("MeshUtilities.GetSplitMesh.InitMesh");
|
||||
static ProfilerMarker profilerSplitBySubMesh = new ProfilerMarker("MeshUtilities.SplitBySubmesh");
|
||||
// Start is called before the first frame update
|
||||
static Mesh GetSplitMesh(
|
||||
Mesh originalMesh,
|
||||
ref SubMeshDescriptor oldSmDesc,
|
||||
ref NativeArray<ushort> indexBuffer,
|
||||
ref NativeArray<int> oldVertexBuffer,
|
||||
ref NativeArray<int> tempVertexBuffer,
|
||||
ref NativeArray<int> hashMap,
|
||||
int vertexByteStride)
|
||||
{
|
||||
profilerGetSubMesh.Begin();
|
||||
int vertexStride = vertexByteStride / sizeof(int); // Each channel in a mesh buffer MUST be a multiple of 4 bytes!
|
||||
NativeArrayClear.Clear(ref hashMap, hashMap.Length);
|
||||
int indexCount = indexBuffer.Length;
|
||||
int maxIndex = 0;
|
||||
profilerGetSubMeshMarkUsed.Begin();
|
||||
|
||||
|
||||
NativeReference<int> maxIdxRef = new NativeReference<int>(Allocator.TempJob);
|
||||
maxIdxRef.Value = 0;
|
||||
FlagUsedVerticesSerial flagJob = new FlagUsedVerticesSerial() { hashMap = hashMap, maxIdx = maxIdxRef, indexBuffer = indexBuffer };
|
||||
flagJob.Run();
|
||||
maxIndex = flagJob.maxIdx.Value;
|
||||
maxIdxRef.Dispose();
|
||||
|
||||
|
||||
profilerGetSubMeshMarkUsed.End();
|
||||
|
||||
profilerGetSubMeshHashMap.Begin();
|
||||
ushort currentVtx = 0;
|
||||
|
||||
NativeReference<int> vtxCountRef = new NativeReference<int>(Allocator.TempJob);
|
||||
vtxCountRef.Value = 0;
|
||||
PopulateVtxBufferSerial popVtxBuffer = new PopulateVtxBufferSerial()
|
||||
{
|
||||
hashMap = hashMap,
|
||||
oldVertexBuffer = oldVertexBuffer,
|
||||
tempVertexBuffer = tempVertexBuffer,
|
||||
vtxCount = vtxCountRef,
|
||||
maxIndex = maxIndex,
|
||||
vertexIntStride = vertexStride
|
||||
};
|
||||
popVtxBuffer.Run();
|
||||
currentVtx = (ushort)vtxCountRef.Value;
|
||||
vtxCountRef.Dispose();
|
||||
profilerGetSubMeshHashMap.End();
|
||||
|
||||
|
||||
ReIndexBuffer16 reindexJob = new ReIndexBuffer16() { indexBuffer = indexBuffer, hashMap = hashMap };
|
||||
JobHandle reindexJobHandle = reindexJob.Schedule(indexCount, 32);
|
||||
reindexJobHandle.Complete();
|
||||
|
||||
profilerGetSubMeshInitMesh.Begin();
|
||||
Mesh splitMesh = new Mesh();
|
||||
|
||||
MeshUpdateFlags noMeshUpdate = MeshUpdateFlags.DontValidateIndices | MeshUpdateFlags.DontResetBoneBounds | MeshUpdateFlags.DontRecalculateBounds | MeshUpdateFlags.DontNotifyMeshUsers;
|
||||
splitMesh.SetVertexBufferParams(currentVtx, originalMesh.GetVertexAttributes());
|
||||
splitMesh.SetVertexBufferData<int>(tempVertexBuffer, 0, 0, currentVtx * vertexStride, 0, noMeshUpdate);
|
||||
splitMesh.SetIndexBufferParams(indexCount, IndexFormat.UInt16);
|
||||
SubMeshDescriptor newSmDesc = new SubMeshDescriptor()
|
||||
{
|
||||
indexStart = 0,
|
||||
baseVertex = 0,
|
||||
firstVertex = 0,
|
||||
vertexCount = oldSmDesc.vertexCount,
|
||||
bounds = oldSmDesc.bounds,
|
||||
indexCount = indexCount,
|
||||
topology = oldSmDesc.topology
|
||||
};
|
||||
|
||||
splitMesh.SetSubMesh(0, newSmDesc, noMeshUpdate);
|
||||
splitMesh.bounds = oldSmDesc.bounds;
|
||||
splitMesh.SetIndexBufferData(indexBuffer, 0, 0, indexCount, noMeshUpdate);
|
||||
profilerGetSubMeshInitMesh.End();
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
profilerGetSubMesh.End();
|
||||
return splitMesh;
|
||||
}
|
||||
[BurstCompile]
|
||||
struct ReIndexBuffer16 : IJobParallelFor
|
||||
{
|
||||
public NativeArray<ushort> indexBuffer;
|
||||
[ReadOnly]
|
||||
public NativeArray<int> hashMap;
|
||||
|
||||
public void Execute(int i)
|
||||
{
|
||||
indexBuffer[i] = (ushort)hashMap[indexBuffer[i]];
|
||||
}
|
||||
}
|
||||
|
||||
[BurstCompile]
|
||||
struct FlagUsedVertices : IJobParallelFor
|
||||
{
|
||||
[NativeDisableParallelForRestriction]
|
||||
public NativeArray<int> hashMap;
|
||||
|
||||
[ReadOnly]
|
||||
public NativeArray<ushort> indexBuffer;
|
||||
|
||||
public void Execute(int i)
|
||||
{
|
||||
|
||||
unsafe {
|
||||
Interlocked.Increment(ref ((int*)hashMap.GetUnsafePtr())[indexBuffer[i]]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[BurstCompile]
|
||||
struct FlagUsedVerticesSerial : IJob
|
||||
{
|
||||
[NativeDisableParallelForRestriction]
|
||||
public NativeArray<int> hashMap;
|
||||
|
||||
[ReadOnly]
|
||||
public NativeArray<ushort> indexBuffer;
|
||||
|
||||
public NativeReference<int> maxIdx;
|
||||
|
||||
public void Execute()
|
||||
{
|
||||
int numIdx = indexBuffer.Length;
|
||||
int maxIdxLocal = 0;
|
||||
for (int i = 0; i < numIdx; i++)
|
||||
{
|
||||
int idx = indexBuffer[i];
|
||||
hashMap[idx] = 1;
|
||||
maxIdxLocal = math.max(maxIdxLocal, idx);
|
||||
}
|
||||
maxIdx.Value = maxIdxLocal;
|
||||
}
|
||||
}
|
||||
|
||||
[BurstCompile]
|
||||
struct PopulateVtxBufferSerial : IJob
|
||||
{
|
||||
[NativeDisableParallelForRestriction]
|
||||
public NativeArray<int> hashMap;
|
||||
|
||||
public NativeArray<int> tempVertexBuffer;
|
||||
[ReadOnly]
|
||||
public NativeArray<int> oldVertexBuffer;
|
||||
|
||||
public NativeReference<int> vtxCount;
|
||||
|
||||
public int vertexIntStride;
|
||||
public int maxIndex;
|
||||
|
||||
public unsafe void Execute()
|
||||
{
|
||||
int vtxCountLocal = 0;
|
||||
int* tempVertexBufferPtr = (int*)NativeArrayUnsafeUtility.GetUnsafeBufferPointerWithoutChecks(tempVertexBuffer);
|
||||
int* oldVertexBufferPtr = (int*)NativeArrayUnsafeUtility.GetUnsafeBufferPointerWithoutChecks(oldVertexBuffer);
|
||||
long vtxStructSize = sizeof(int) * vertexIntStride;
|
||||
for (int i = 0; i <= maxIndex; i++)
|
||||
{
|
||||
if (hashMap[i] > 0)
|
||||
{
|
||||
|
||||
hashMap[i] = vtxCountLocal;
|
||||
//CopyUnsafe<int>(oldVertexBuffer, i * vertexStride, tempVertexBuffer, vtxIdx * vertexStride, vertexStride);
|
||||
UnsafeUtility.MemCpy(tempVertexBufferPtr + vtxCountLocal * vertexIntStride, oldVertexBufferPtr + i * vertexIntStride, vtxStructSize);
|
||||
vtxCountLocal++;
|
||||
}
|
||||
}
|
||||
vtxCount.Value = vtxCountLocal;
|
||||
}
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private unsafe static void CopyUnsafe<T>(NativeArray<T> src, int srcIndex, NativeArray<T> dst, int dstIndex, int length) where T : struct
|
||||
{
|
||||
UnsafeUtility.MemCpy((byte*)NativeArrayUnsafeUtility.GetUnsafeBufferPointerWithoutChecks(dst) + dstIndex * UnsafeUtility.SizeOf<T>(),
|
||||
(byte*)NativeArrayUnsafeUtility.GetUnsafeBufferPointerWithoutChecks(src) + srcIndex * UnsafeUtility.SizeOf<T>(), length * UnsafeUtility.SizeOf<T>());
|
||||
}
|
||||
|
||||
static Mesh[] SplitBySubmesh16(Mesh mesh, Mesh.MeshData meshData)
|
||||
{
|
||||
profilerSplitBySubMesh.Begin();
|
||||
int subMeshCount = meshData.subMeshCount;
|
||||
if (subMeshCount <= 1)
|
||||
{
|
||||
return new Mesh[1] { mesh };
|
||||
}
|
||||
Mesh[] outMeshes = new Mesh[subMeshCount];
|
||||
int vertexBufferStride = meshData.GetVertexBufferStride(0);
|
||||
NativeArray<int> oldVertexBuffer = meshData.GetVertexData<int>(0);
|
||||
NativeArray<int> tempVertexBuffer = new NativeArray<int>(meshData.vertexCount * (vertexBufferStride / sizeof(int)), Allocator.TempJob);
|
||||
NativeArray<int> hashMap = new NativeArray<int>(meshData.vertexCount, Allocator.TempJob);
|
||||
try
|
||||
{
|
||||
for (int smIdx = 0; smIdx < subMeshCount; smIdx++)
|
||||
{
|
||||
SubMeshDescriptor subMeshDescriptor = meshData.GetSubMesh(smIdx);
|
||||
NativeArray<ushort> indexBuffer = new NativeArray<ushort>(subMeshDescriptor.indexCount, Allocator.TempJob);
|
||||
meshData.GetIndices(indexBuffer, smIdx, true);
|
||||
outMeshes[smIdx] = GetSplitMesh(
|
||||
mesh,
|
||||
ref subMeshDescriptor,
|
||||
ref indexBuffer,
|
||||
ref oldVertexBuffer,
|
||||
ref tempVertexBuffer,
|
||||
ref hashMap,
|
||||
vertexBufferStride
|
||||
);
|
||||
indexBuffer.Dispose();
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
tempVertexBuffer.Dispose();
|
||||
hashMap.Dispose();
|
||||
}
|
||||
profilerSplitBySubMesh.End();
|
||||
return outMeshes;
|
||||
}
|
||||
|
||||
public static Mesh[] SplitBySubmesh(Mesh mesh, Mesh.MeshData meshData)
|
||||
{
|
||||
|
||||
if (meshData.indexFormat == IndexFormat.UInt16)
|
||||
{
|
||||
return SplitBySubmesh16(mesh, meshData);
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogError("Could not split mesh, 32 bit index buffer not implemented yet");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: c10da140626dac84594172d21696e0a9
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,19 @@
|
|||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using Unity.Collections;
|
||||
using Unity.Collections.LowLevel.Unsafe;
|
||||
|
||||
namespace SLZ.CustomStaticBatching
|
||||
{
|
||||
public unsafe static class NativeArrayClear
|
||||
{
|
||||
unsafe public static void Clear<T>(ref NativeArray<T> array, long count, long start = 0) where T : struct
|
||||
{
|
||||
UnsafeUtility.MemClear(
|
||||
(byte*)NativeArrayUnsafeUtility.GetUnsafeBufferPointerWithoutChecks(array) + start * UnsafeUtility.SizeOf<T>(),
|
||||
count * UnsafeUtility.SizeOf<T>()
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 62e8b5071153e944485b48cb6f80b125
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,39 @@
|
|||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using Unity.Collections;
|
||||
using Unity.Collections.LowLevel.Unsafe;
|
||||
using UnityEngine;
|
||||
|
||||
namespace SLZ.CustomStaticBatching
|
||||
{
|
||||
public static class CSBNativeArraySpanExt
|
||||
{
|
||||
public unsafe static void Copy<T>(Span<T> src, int srcIndex, NativeArray<T> dst, int dstIndex, int length) where T : unmanaged
|
||||
{
|
||||
fixed (T* pData = &src.GetPinnableReference())
|
||||
{
|
||||
//AtomicSafetyHandle.CheckWriteAndThrow(dst.m_Safety);
|
||||
UnsafeUtility.MemCpy((byte*)dst.GetUnsafePtr<T>() + dstIndex * UnsafeUtility.SizeOf<T>(), (byte*)(void*)pData + srcIndex * UnsafeUtility.SizeOf<T>(), length * UnsafeUtility.SizeOf<T>());
|
||||
}
|
||||
}
|
||||
|
||||
public unsafe static void Copy<T>(ReadOnlySpan<T> src, int srcIndex, NativeArray<T> dst, int dstIndex, int length) where T : unmanaged
|
||||
{
|
||||
fixed (T* pData = &src.GetPinnableReference())
|
||||
{
|
||||
//AtomicSafetyHandle.CheckWriteAndThrow(dst.m_Safety);
|
||||
UnsafeUtility.MemCpy((byte*)dst.GetUnsafePtr<T>() + dstIndex * UnsafeUtility.SizeOf<T>(), (byte*)(void*)pData + srcIndex * UnsafeUtility.SizeOf<T>(), length * UnsafeUtility.SizeOf<T>());
|
||||
}
|
||||
}
|
||||
|
||||
public unsafe static void Copy<T>(NativeArray<T> src, int srcIndex, Span<T> dst, int dstIndex, int length) where T : unmanaged
|
||||
{
|
||||
fixed (T* pData = &dst.GetPinnableReference())
|
||||
{
|
||||
//AtomicSafetyHandle.CheckWriteAndThrow(dst.m_Safety);
|
||||
UnsafeUtility.MemCpy((byte*)(void*)pData + dstIndex * UnsafeUtility.SizeOf<T>(), (byte*)src.GetUnsafePtr<T>() + srcIndex * UnsafeUtility.SizeOf<T>(), length * UnsafeUtility.SizeOf<T>());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 64629eefdeabffc49b82f6587560c82d
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,27 @@
|
|||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Security.Cryptography;
|
||||
using Unity.Collections;
|
||||
using Unity.Collections.LowLevel.Unsafe;
|
||||
using UnityEngine;
|
||||
|
||||
namespace SLZ.CustomStaticBatching
|
||||
{
|
||||
public static unsafe class NativeArraySubArray
|
||||
{
|
||||
public static unsafe NativeArray<T2> GetSubArrayAlias<T, T2>(NativeArray<T> array, int start, int length)
|
||||
where T : unmanaged
|
||||
where T2 : unmanaged
|
||||
{
|
||||
void* dataPointer = ((byte*)array.GetUnsafePtr()) + start * sizeof(T);
|
||||
NativeArray<T2> outp = NativeArrayUnsafeUtility.ConvertExistingDataToNativeArray<T2>(dataPointer, length, Allocator.None);
|
||||
#if ENABLE_UNITY_COLLECTIONS_CHECKS
|
||||
NativeArrayUnsafeUtility.SetAtomicSafetyHandle(ref outp, AtomicSafetyHandle.GetTempMemoryHandle());
|
||||
#endif
|
||||
return outp;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 1a0167058eeee0a49920258bf784a54b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,19 @@
|
|||
{
|
||||
"name": "SLZ.CustomStaticBatching.Unsafe",
|
||||
"rootNamespace": "",
|
||||
"references": [
|
||||
"GUID:e0cd26848372d4e5c891c569017e11f1",
|
||||
"GUID:2665a8d13d1b3f18800f46e256720795",
|
||||
"GUID:d8b63aba1907145bea998dd612889d6b",
|
||||
"GUID:b3170da2b0ad74e4aad542674030756e"
|
||||
],
|
||||
"includePlatforms": [],
|
||||
"excludePlatforms": [],
|
||||
"allowUnsafeCode": true,
|
||||
"overrideReferences": false,
|
||||
"precompiledReferences": [],
|
||||
"autoReferenced": true,
|
||||
"defineConstraints": [],
|
||||
"versionDefines": [],
|
||||
"noEngineReferences": false
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 1a4848c44fae8ec4a9b68efad47aab4e
|
||||
AssemblyDefinitionImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,21 @@
|
|||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using Unity.Collections;
|
||||
using Unity.Collections.LowLevel.Unsafe;
|
||||
using UnityEngine;
|
||||
|
||||
namespace SLZ.CustomStaticBatching
|
||||
{
|
||||
public unsafe static class SpanSortExt
|
||||
{
|
||||
public static void Sort<T>(ref Span<T> span) where T : unmanaged, IComparable<T>
|
||||
{
|
||||
fixed (T* pData = &span.GetPinnableReference())
|
||||
{
|
||||
NativeArray<T> nativeCast = NativeArrayUnsafeUtility.ConvertExistingDataToNativeArray<T>((void*)pData, span.Length, Allocator.None);
|
||||
nativeCast.Sort();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 411179256dc538f41a2a5bfff6f4ab55
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
Loading…
Add table
Add a link
Reference in a new issue