BITFALL/Assets/GSpawn - Level Designer/Scripts/Level Design/Object Spawn/TileRuleMask.cs

516 lines
18 KiB
C#

#if UNITY_EDITOR
using UnityEngine;
using System;
namespace GSpawn
{
public enum TileRuleMaskRotation
{
None = 0,
R90,
R180,
R270
}
public enum TileRuleMaskMirrorAxis
{
None = 0,
X,
Z
}
public enum TileRuleBitMaskId
{
ReqOn = 0,
ReqOff
}
// Note: Maps to corresponding number of neighbors.
public enum TileRuleNeighborRadius
{
One = 1,
Two,
[Obsolete]
Three,
}
public struct TileRuleMaskMatchResult
{
public bool matched;
public TileRuleMaskRotation maskRotation;
public TileRuleMaskMirrorAxis maskMirrorAxis;
public TileRuleMaskMatchResult(bool matched)
{
this.matched = matched;
maskRotation = TileRuleMaskRotation.None;
maskMirrorAxis = TileRuleMaskMirrorAxis.None;
}
public TileRuleMaskMatchResult(TileRuleMaskRotation maskRotation)
{
matched = true;
this.maskRotation = maskRotation;
maskMirrorAxis = TileRuleMaskMirrorAxis.None;
}
public TileRuleMaskMatchResult(TileRuleMaskMirrorAxis maskMirrorAxis)
{
matched = true;
maskRotation = TileRuleMaskRotation.None;
this.maskMirrorAxis = maskMirrorAxis;
}
public void reset()
{
matched = false;
maskRotation = TileRuleMaskRotation.None;
maskMirrorAxis = TileRuleMaskMirrorAxis.None;
}
}
[Serializable]
public class TileRuleMask
{
// Note: Maps to TileRuleMaskRotation.
private static Vector3[] _rotationLookAxes = new Vector3[] { Vector3.forward, Vector3.right, Vector3.back, Vector3.left };
private const int _numAvailableBits = 49;
private const int _bitRowSize = 7;
private const int _numBitRows = 7;
private const int _midBitIndex = 24;
private const ulong _defaultReqOnMask = (1ul << 24);
private const ulong _defaultReqOffMask = 0;
[SerializeField]
private ulong _reqOnMask = _defaultReqOnMask;
[SerializeField]
private ulong _rotatedReqOnMask_90 = _defaultReqOnMask;
[SerializeField]
private ulong _rotatedReqOnMask_180 = _defaultReqOnMask;
[SerializeField]
private ulong _rotatedReqOnMask_270 = _defaultReqOnMask;
[SerializeField]
private ulong _mirrXReqOnMask = _defaultReqOnMask;
[SerializeField]
private ulong _mirrZReqOnMask = _defaultReqOnMask;
[SerializeField]
private int _numReqOnBitsSet = 1;
[SerializeField]
private ulong _reqOffMask = _defaultReqOffMask;
[SerializeField]
private ulong _rotatedReqOffMask_90 = _defaultReqOffMask;
[SerializeField]
private ulong _rotatedReqOffMask_180 = _defaultReqOffMask;
[SerializeField]
private ulong _rotatedReqOffMask_270 = _defaultReqOffMask;
[SerializeField]
private ulong _mirrXReqOffMask = _defaultReqOffMask;
[SerializeField]
private ulong _mirrZReqOffMask = _defaultReqOffMask;
[SerializeField]
private int _numReqOffBitsSet = 0;
public ulong reqOnMaskValue
{
get { return _reqOnMask; }
set
{
_reqOnMask = value;
_rotatedReqOnMask_90 = rotateMask_90(_reqOnMask);
_rotatedReqOnMask_180 = rotateMask_180(_reqOnMask);
_rotatedReqOnMask_270 = rotateMask_270(_reqOnMask);
_mirrXReqOnMask = mirrorMaskX(_reqOnMask);
_mirrZReqOnMask = mirrorMaskZ(_reqOnMask);
_numReqOnBitsSet = countNumberOf1Bits(_reqOnMask);
}
}
public int numReqOnBitsSet { get { return _numReqOnBitsSet; } }
public ulong reqOffMaskValue
{
get { return _reqOffMask; }
set
{
_reqOffMask = value;
_rotatedReqOffMask_90 = rotateMask_90(_reqOffMask);
_rotatedReqOffMask_180 = rotateMask_180(_reqOffMask);
_rotatedReqOffMask_270 = rotateMask_270(_reqOffMask);
_mirrXReqOffMask = mirrorMaskX(_reqOffMask);
_mirrZReqOffMask = mirrorMaskZ(_reqOffMask);
_numReqOffBitsSet = countNumberOf1Bits(_reqOffMask);
}
}
public int numReqOffBitsSet { get { return _numReqOffBitsSet; } }
public static int numAvailableBits { get { return _numAvailableBits; } }
public static int numBitRows { get { return _numBitRows; } }
public static int bitRowSize { get { return _bitRowSize; } }
public static int middleBitRow { get { return 3; } }
public static int middleBitCol { get { return 3; } }
public static ulong defaultReqOnMask { get { return _defaultReqOnMask; } }
public static Vector3 maskRotationToLookAxis(TileRuleMaskRotation maskRotation, Quaternion frameRotation)
{
return frameRotation * _rotationLookAxes[(int)maskRotation];
}
public static void debugLog(ulong ruleMask)
{
string binaryString = Convert.ToString((long)ruleMask, 2).PadLeft(64, '0');
binaryString = binaryString.Substring(64 - _numAvailableBits, _numAvailableBits);
Debug.Log("================Rule Mask================");
for (int i = 0; i < _numBitRows; ++i)
Debug.Log("| " + binaryString.Substring(i * _bitRowSize, _bitRowSize) + " |");
}
public static ulong setBit(ulong ruleMask, int row, int col)
{
#pragma warning disable 0612
int bitIndex = rowColToBitIndex(TileRuleNeighborRadius.Three, row, col);
ulong bitMask = 1ul << bitIndex;
return ruleMask | bitMask;
#pragma warning restore 0612
}
public static int countNumberOf1Bits(ulong ruleMask)
{
int count = 0;
for (int i = 0; i < _numAvailableBits; ++i)
{
count += (int)(ruleMask & 0x1);
ruleMask >>= 1;
}
return count;
}
public static int rowColToBitIndex(int row, int col)
{
return 48 - (row * _bitRowSize + col);
}
public static void bitIndexToRowCol(int bitIndex, out int row, out int col)
{
row = (48 - bitIndex) / _numBitRows;
col = (48 - bitIndex) % _bitRowSize;
}
public static int rowColToBitIndex(TileRuleNeighborRadius radius, int row, int col)
{
#pragma warning disable 0612
switch (radius)
{
case TileRuleNeighborRadius.One:
return 32 - (row * _bitRowSize + col);
case TileRuleNeighborRadius.Two:
return 40 - (row * _bitRowSize + col);
case TileRuleNeighborRadius.Three:
return 48 - (row * _bitRowSize + col);
}
#pragma warning restore 0612
return 0;
}
public static bool isMiddleBit(int row, int col)
{
#pragma warning disable 0612
int bitIndex = rowColToBitIndex(TileRuleNeighborRadius.Three, row, col);
return bitIndex == _midBitIndex;
#pragma warning restore 0612
}
public static bool isBitInRadius(TileRuleNeighborRadius radius, int row, int col)
{
#pragma warning disable 0612
switch (radius)
{
case TileRuleNeighborRadius.Three:
return true;
case TileRuleNeighborRadius.Two:
return row >= 1 && row <= 5 && col >= 1 && col <= 5;
case TileRuleNeighborRadius.One:
return row >= 2 && row <= 4 && col >= 2 && col <= 4;
}
#pragma warning restore 0612
return false;
}
// Note: The match function only produces the expected results as long as the tile rules
// have been sorted in descending order of the total number of bits set (req on + req off).
// (i.e from the most specific/restrictive to the least specific/restrictive).
public TileRuleMaskMatchResult match(ulong ruleMask, TileRuleRotationMode rotationMode)
{
// Note: This is common to all rotation modes.
if ((_reqOnMask & ruleMask) == _reqOnMask)
{
if ((_reqOffMask & ruleMask) != 0) return new TileRuleMaskMatchResult(false);
return new TileRuleMaskMatchResult(TileRuleMaskRotation.None);
}
switch (rotationMode)
{
case TileRuleRotationMode.Fixed:
// Note: Handled above.
break;
case TileRuleRotationMode.Rotated:
if ((_rotatedReqOnMask_90 & ruleMask) == _rotatedReqOnMask_90)
{
if ((_rotatedReqOffMask_90 & ruleMask) != 0) return new TileRuleMaskMatchResult(false);
return new TileRuleMaskMatchResult(TileRuleMaskRotation.R90);
}
if ((_rotatedReqOnMask_180 & ruleMask) == _rotatedReqOnMask_180)
{
if ((_rotatedReqOffMask_180 & ruleMask) != 0) return new TileRuleMaskMatchResult(false);
return new TileRuleMaskMatchResult(TileRuleMaskRotation.R180);
}
if ((_rotatedReqOnMask_270 & ruleMask) == _rotatedReqOnMask_270)
{
if ((_rotatedReqOffMask_270 & ruleMask) != 0) return new TileRuleMaskMatchResult(false);
return new TileRuleMaskMatchResult(TileRuleMaskRotation.R270);
}
break;
#pragma warning disable 0612
case TileRuleRotationMode.MirrorX:
if ((_mirrXReqOnMask & ruleMask) == _mirrXReqOnMask)
{
if ((_mirrXReqOffMask & ruleMask) != 0) return new TileRuleMaskMatchResult(false);
return new TileRuleMaskMatchResult(TileRuleMaskMirrorAxis.X);
}
break;
case TileRuleRotationMode.MirrorZ:
if ((_mirrZReqOnMask & ruleMask) == _mirrZReqOnMask)
{
if ((_mirrZReqOffMask & ruleMask) != 0) return new TileRuleMaskMatchResult(false);
return new TileRuleMaskMatchResult(TileRuleMaskMirrorAxis.Z);
}
break;
#pragma warning restore 0612
}
return new TileRuleMaskMatchResult(false);
}
public bool checkBit(int row, int col, TileRuleBitMaskId maskId)
{
#pragma warning disable 0612
int bitIndex = rowColToBitIndex(TileRuleNeighborRadius.Three, row, col);
ulong bitMask = 1ul << bitIndex;
switch (maskId)
{
case TileRuleBitMaskId.ReqOn:
return (_reqOnMask & bitMask) != 0;
case TileRuleBitMaskId.ReqOff:
return (_reqOffMask & bitMask) != 0;
}
#pragma warning restore 0612
return false;
}
public void clearBit(int row, int col, TileRuleBitMaskId maskId)
{
#pragma warning disable 0612
int bitIndex = rowColToBitIndex(TileRuleNeighborRadius.Three, row, col);
ulong bitMask = ~(1ul << bitIndex);
switch (maskId)
{
case TileRuleBitMaskId.ReqOn:
reqOnMaskValue = reqOnMaskValue & bitMask;
break;
case TileRuleBitMaskId.ReqOff:
reqOffMaskValue = reqOffMaskValue & bitMask;
break;
}
#pragma warning restore 0612
}
public void setAllBits(TileRuleBitMaskId maskId)
{
switch (maskId)
{
case TileRuleBitMaskId.ReqOn:
reqOnMaskValue = ~0ul;
reqOffMaskValue = _defaultReqOffMask;
break;
case TileRuleBitMaskId.ReqOff:
reqOffMaskValue = ~0ul;
reqOffMaskValue &= ~((1ul << 24));
reqOnMaskValue = _defaultReqOnMask;
break;
}
}
public void setBit(int row, int col, TileRuleBitMaskId maskId)
{
#pragma warning disable 0612
int bitIndex = rowColToBitIndex(TileRuleNeighborRadius.Three, row, col);
ulong bitMask = 1ul << bitIndex;;
switch (maskId)
{
case TileRuleBitMaskId.ReqOn:
reqOnMaskValue = reqOnMaskValue | bitMask;
reqOffMaskValue = reqOffMaskValue & (~bitMask);
break;
case TileRuleBitMaskId.ReqOff:
reqOffMaskValue = reqOffMaskValue | bitMask;
reqOffMaskValue &= ~((1ul << 24));
reqOnMaskValue = reqOnMaskValue & (~bitMask);
break;
}
#pragma warning restore 0612
}
public void toggleBit(int row, int col, TileRuleBitMaskId maskId)
{
if (checkBit(row, col, maskId)) clearBit(row, col, maskId);
else setBit(row, col, maskId);
}
public void useDefaultValue()
{
reqOnMaskValue = _defaultReqOnMask;
reqOffMaskValue = _defaultReqOffMask;
}
private static ulong mirrorMaskX(ulong ruleMask)
{
int numColumns = _bitRowSize / 2;
for (int r = 0; r < _numBitRows; ++r)
{
for (int c = 0; c < numColumns; ++c)
{
int p0 = rowColToBitIndex(r, c);
int p1 = rowColToBitIndex(r, _bitRowSize - 1 - c);
ruleMask = swapBits(ruleMask, p0, p1);
}
}
return ruleMask;
}
private static ulong mirrorMaskZ(ulong ruleMask)
{
int numRows = _numBitRows / 2;
for (int c = 0; c < _bitRowSize; ++c)
{
for (int r = 0; r < numRows; ++r)
{
int p0 = rowColToBitIndex(r, c);
int p1 = rowColToBitIndex(_numBitRows - 1 - r, c);
ruleMask = swapBits(ruleMask, p0, p1);
}
}
return ruleMask;
}
private static ulong swapBits(ulong ruleMask, int p0, int p1)
{
if (p0 == p1) return ruleMask;
// https://www.geeksforgeeks.org/how-to-swap-two-bits-in-a-given-integer/
ulong bit0 = (ruleMask >> p0) & 1;
ulong bit1 = (ruleMask >> p1) & 1;
ulong xor = (bit0 ^ bit1);
xor = (xor << p0) | (xor << p1);
return ruleMask ^ xor;
}
private static ulong rotateMask_90(ulong ruleMask)
{
ulong r0 = extractMaskRow(ruleMask, 0);
ulong r1 = extractMaskRow(ruleMask, 1);
ulong r2 = extractMaskRow(ruleMask, 2);
ulong r3 = extractMaskRow(ruleMask, 3);
ulong r4 = extractMaskRow(ruleMask, 4);
ulong r5 = extractMaskRow(ruleMask, 5);
ulong r6 = extractMaskRow(ruleMask, 6);
ruleMask = setMaskColumn(ruleMask, 0, r6);
ruleMask = setMaskColumn(ruleMask, 1, r5);
ruleMask = setMaskColumn(ruleMask, 2, r4);
ruleMask = setMaskColumn(ruleMask, 3, r3);
ruleMask = setMaskColumn(ruleMask, 4, r2);
ruleMask = setMaskColumn(ruleMask, 5, r1);
ruleMask = setMaskColumn(ruleMask, 6, r0);
return ruleMask;
}
private static ulong rotateMask_180(ulong ruleMask)
{
ruleMask = rotateMask_90(ruleMask);
return rotateMask_90(ruleMask);
}
private static ulong rotateMask_270(ulong ruleMask)
{
ruleMask = rotateMask_90(ruleMask);
ruleMask = rotateMask_90(ruleMask);
return rotateMask_90(ruleMask);
}
private static ulong setMaskColumn(ulong ruleMask, int col, ulong val_7bit)
{
// Note: High bit in val_7bit maps to top most bit in column. Low bit
// in val_7bit maps to bottom most bit in column.
// Loop through each bit in the 7 bit value (0 = low bit; 7 = high bit)
for (int bitIndex = 0; bitIndex < _bitRowSize; ++bitIndex)
{
// Clear bit in rule mask
ruleMask &= ~((1ul << (_bitRowSize - 1 - col)) << (bitIndex * _bitRowSize));
// Extract bit from 7 bit value
ulong bitMask = 1ul << bitIndex;
ulong bit = (val_7bit & bitMask) >> bitIndex;
// Store the bit in the rule mask
ruleMask |= ((bit << (_bitRowSize - 1 - col)) << (bitIndex * _bitRowSize));
}
return ruleMask;
}
private static ulong extractMaskRow(ulong ruleMask, int row)
{
int shift = (_numBitRows - 1 - row) * _bitRowSize;
ulong bitMask = 0b1111111ul << shift;
return (ruleMask & bitMask) >> shift;
}
}
}
#endif