修改水
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Obi
|
||||
@@ -12,11 +13,9 @@ namespace Obi
|
||||
public int firstChild;
|
||||
|
||||
// add 12 bytes of padding to ensure correct memory alignment:
|
||||
#pragma warning disable 0414
|
||||
private int pad0;
|
||||
private int pad1;
|
||||
private int pad2;
|
||||
#pragma warning restore 0414
|
||||
|
||||
public DFNode(Vector4 center)
|
||||
{
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Obi
|
||||
{
|
||||
@@ -20,46 +19,16 @@ namespace Obi
|
||||
this.scale = scale;
|
||||
}
|
||||
|
||||
public void FromTransform3D(Transform source, ObiRigidbody rb)
|
||||
public void FromTransform(Transform source, bool is2D = false)
|
||||
{
|
||||
if (rb != null && rb.unityRigidbody != null)
|
||||
{
|
||||
translation = source.position - rb.unityRigidbody.transform.position + rb.position;
|
||||
rotation = (source.rotation * Quaternion.Inverse(rb.unityRigidbody.transform.rotation)) * rb.rotation;
|
||||
}
|
||||
else
|
||||
{
|
||||
translation = source.position;
|
||||
rotation = source.rotation;
|
||||
}
|
||||
|
||||
translation = source.position;
|
||||
rotation = source.rotation;
|
||||
scale = source.lossyScale;
|
||||
}
|
||||
|
||||
public void FromTransform2D(Transform source, ObiRigidbody2D rb)
|
||||
{
|
||||
if (rb != null && rb.unityRigidbody != null)
|
||||
if (is2D)
|
||||
{
|
||||
translation = source.position - rb.unityRigidbody.transform.position + (Vector3)rb.position;
|
||||
rotation = (source.rotation * Quaternion.Inverse(rb.unityRigidbody.transform.rotation)) * Quaternion.AngleAxis(rb.rotation, Vector3.forward);
|
||||
translation[2] = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
translation = source.position;
|
||||
rotation = source.rotation;
|
||||
}
|
||||
|
||||
scale = source.lossyScale;
|
||||
translation[2] = 0;
|
||||
}
|
||||
|
||||
public AffineTransform Inverse()
|
||||
{
|
||||
var conj = Quaternion.Inverse(rotation);
|
||||
var invScale = new Vector3(1 / scale.x, 1 / scale.y, 1 / scale.z);
|
||||
return new AffineTransform(conj * Vector3.Scale(translation , -invScale),
|
||||
conj,
|
||||
invScale);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,8 +10,8 @@ namespace Obi
|
||||
public Vector4 angularVelocity;
|
||||
public Vector4 com;
|
||||
public float inverseMass;
|
||||
|
||||
public int constraintCount;
|
||||
|
||||
private int pad0;
|
||||
private int pad1;
|
||||
private int pad2;
|
||||
|
||||
@@ -20,7 +20,7 @@ namespace Obi
|
||||
bool kinematic = !Application.isPlaying || rb.unityRigidbody.isKinematic || rb.kinematicForParticles;
|
||||
|
||||
//rotation = source.rotation;
|
||||
velocity = rb.kinematicForParticles ? Vector3.zero : rb.linearVelocity + (rb.unityRigidbody.useGravity ? Physics.gravity * Time.fixedDeltaTime : Vector3.zero);
|
||||
velocity = rb.kinematicForParticles ? Vector3.zero : rb.linearVelocity;
|
||||
angularVelocity = rb.kinematicForParticles ? Vector3.zero : rb.angularVelocity;
|
||||
|
||||
// center of mass in unity is affected by local rotation and position, but not scale. We need it expressed in world space:
|
||||
|
||||
@@ -22,34 +22,13 @@ namespace Obi
|
||||
heightmap: width (x axis), height (y axis) and depth (z axis) in world units.*/
|
||||
public ShapeType type;
|
||||
public float contactOffset;
|
||||
|
||||
public int dataIndex;
|
||||
public int rigidbodyIndex; // index of the associated rigidbody in the collision world.
|
||||
public int materialIndex; // index of the associated material in the collision world.
|
||||
public int forceZoneIndex; // index of the associated force zone in the collision world.
|
||||
|
||||
public int filter; // bitwise category/mask.
|
||||
public int flags; // first bit whether the collider is 2D (1) or 3D (0), second bit whether it's a trigger (1) or regular collider (0),
|
||||
// third bit (sign) determines whether shape is inverted or not.
|
||||
|
||||
public bool is2D
|
||||
{
|
||||
get => (flags & 1) != 0;
|
||||
set => flags |= value ? 1 : 0;
|
||||
}
|
||||
public bool isTrigger
|
||||
{
|
||||
get => ((flags & 1 << 1) != 0) || forceZoneIndex >= 0;
|
||||
set => flags |= value ? 1 << 1 : 0;
|
||||
}
|
||||
public float sign
|
||||
{
|
||||
get => (flags & 1 << 2) != 0 ? -1 : 1;
|
||||
}
|
||||
|
||||
public void SetSign(bool inverted)
|
||||
{
|
||||
if (inverted) flags |= 1 << 2;
|
||||
else flags &= ~(1 << 2);
|
||||
}
|
||||
|
||||
public int flags; // for now, only used for trigger (1) or regular collider (0).
|
||||
public int is2D; // whether the collider is 2D (1) or 3D (0).
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
using UnityEngine;
|
||||
using System.Collections;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Obi
|
||||
{
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
|
||||
public struct CollisionMaterial
|
||||
{
|
||||
public float dynamicFriction;
|
||||
|
||||
@@ -1,93 +0,0 @@
|
||||
#if (OBI_BURST && OBI_MATHEMATICS && OBI_COLLECTIONS)
|
||||
using Unity.Mathematics;
|
||||
#endif
|
||||
|
||||
namespace Obi
|
||||
{
|
||||
public struct ContactEffectiveMasses
|
||||
{
|
||||
public float normalInvMassA;
|
||||
public float tangentInvMassA;
|
||||
public float bitangentInvMassA;
|
||||
|
||||
public float normalInvMassB;
|
||||
public float tangentInvMassB;
|
||||
public float bitangentInvMassB;
|
||||
|
||||
public float TotalNormalInvMass => normalInvMassA + normalInvMassB;
|
||||
public float TotalTangentInvMass => tangentInvMassA + tangentInvMassB;
|
||||
public float TotalBitangentInvMass => bitangentInvMassA + bitangentInvMassB;
|
||||
|
||||
public void ClearContactMassesA()
|
||||
{
|
||||
normalInvMassA = tangentInvMassA = bitangentInvMassA = 0;
|
||||
}
|
||||
|
||||
public void ClearContactMassesB()
|
||||
{
|
||||
normalInvMassB = tangentInvMassB = bitangentInvMassB = 0;
|
||||
}
|
||||
|
||||
#if (OBI_BURST && OBI_MATHEMATICS && OBI_COLLECTIONS)
|
||||
public void CalculateContactMassesA(float invMass,
|
||||
float4 inverseInertiaTensor,
|
||||
float4 position,
|
||||
quaternion orientation,
|
||||
float4 contactPoint,
|
||||
float4 normal,
|
||||
float4 tangent,
|
||||
float4 bitangent,
|
||||
bool rollingContacts)
|
||||
{
|
||||
// initialize inverse linear masses:
|
||||
normalInvMassA = tangentInvMassA = bitangentInvMassA = invMass;
|
||||
|
||||
if (rollingContacts)
|
||||
{
|
||||
float4 rA = contactPoint - position;
|
||||
float4x4 solverInertiaA = BurstMath.TransformInertiaTensor(inverseInertiaTensor, orientation);
|
||||
|
||||
normalInvMassA += BurstMath.RotationalInvMass(solverInertiaA, rA, normal);
|
||||
tangentInvMassA += BurstMath.RotationalInvMass(solverInertiaA, rA, tangent);
|
||||
bitangentInvMassA += BurstMath.RotationalInvMass(solverInertiaA, rA, bitangent);
|
||||
}
|
||||
}
|
||||
|
||||
public void CalculateContactMassesB(float invMass,
|
||||
float4 inverseInertiaTensor,
|
||||
float4 position,
|
||||
quaternion orientation,
|
||||
float4 contactPoint,
|
||||
float4 normal,
|
||||
float4 tangent,
|
||||
float4 bitangent,
|
||||
bool rollingContacts)
|
||||
{
|
||||
// initialize inverse linear masses:
|
||||
normalInvMassB = tangentInvMassB = bitangentInvMassB = invMass;
|
||||
|
||||
if (rollingContacts)
|
||||
{
|
||||
float4 rB = contactPoint - position;
|
||||
float4x4 solverInertiaB = BurstMath.TransformInertiaTensor(inverseInertiaTensor, orientation);
|
||||
|
||||
normalInvMassB += BurstMath.RotationalInvMass(solverInertiaB, rB, normal);
|
||||
tangentInvMassB += BurstMath.RotationalInvMass(solverInertiaB, rB, tangent);
|
||||
bitangentInvMassB += BurstMath.RotationalInvMass(solverInertiaB, rB, bitangent);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void CalculateContactMassesB(in BurstRigidbody rigidbody, in BurstAffineTransform solver2World, float4 pointB, float4 normal, float4 tangent, float4 bitangent)
|
||||
{
|
||||
float4 rB = solver2World.TransformPoint(pointB) - rigidbody.com;
|
||||
|
||||
// initialize inverse linear masses:
|
||||
normalInvMassB = tangentInvMassB = bitangentInvMassB = rigidbody.inverseMass;
|
||||
normalInvMassB += BurstMath.RotationalInvMass(rigidbody.inverseInertiaTensor, rB, normal);
|
||||
tangentInvMassB += BurstMath.RotationalInvMass(rigidbody.inverseInertiaTensor, rB, tangent);
|
||||
bitangentInvMassB += BurstMath.RotationalInvMass(rigidbody.inverseInertiaTensor, rB, bitangent);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7b11c7db3414246668eb72ef55dd737f
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,40 +0,0 @@
|
||||
using UnityEngine;
|
||||
using System;
|
||||
|
||||
namespace Obi
|
||||
{
|
||||
[Serializable]
|
||||
public struct EmitPoint
|
||||
{
|
||||
public Vector4 position;
|
||||
public Vector4 direction;
|
||||
public Vector4 velocity;
|
||||
public Color color;
|
||||
|
||||
public EmitPoint(Vector3 position, Vector3 direction)
|
||||
{
|
||||
this.position = position;
|
||||
this.direction = direction;
|
||||
this.velocity = Vector4.zero;
|
||||
this.color = Color.white;
|
||||
}
|
||||
|
||||
public EmitPoint(Vector3 position, Vector3 direction, Color color)
|
||||
{
|
||||
this.position = position;
|
||||
this.direction = direction;
|
||||
this.velocity = Vector4.zero;
|
||||
this.color = color;
|
||||
}
|
||||
|
||||
public EmitPoint GetTransformed(Matrix4x4 transform, Matrix4x4 prevTransform, Color multiplyColor, float deltaTime)
|
||||
{
|
||||
var ep = new EmitPoint(transform.MultiplyPoint3x4(position),
|
||||
transform.MultiplyVector(direction),
|
||||
color * multiplyColor);
|
||||
|
||||
ep.velocity = deltaTime > 0 ? ((Vector3)ep.position - prevTransform.MultiplyPoint3x4(position)) / deltaTime : Vector3.zero;
|
||||
return ep;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d0c9d9016e8744477adde7f1afe28798
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,16 +0,0 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace Obi
|
||||
{
|
||||
public struct EmittedParticleData
|
||||
{
|
||||
public Vector4 fluidMaterial;
|
||||
public Vector4 fluidMaterial2;
|
||||
public Vector4 fluidInterface;
|
||||
public Vector4 userData;
|
||||
public int phase;
|
||||
public float invMass;
|
||||
public float radius;
|
||||
public float volume;
|
||||
}
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
using UnityEngine;
|
||||
using System;
|
||||
|
||||
namespace Obi
|
||||
{
|
||||
[Serializable]
|
||||
public struct ForceZone
|
||||
{
|
||||
public enum ForceMode
|
||||
{
|
||||
Force,
|
||||
Acceleration,
|
||||
Wind,
|
||||
}
|
||||
|
||||
public enum ZoneType
|
||||
{
|
||||
Directional,
|
||||
Radial,
|
||||
Vortex,
|
||||
Void
|
||||
}
|
||||
|
||||
public enum DampingDirection
|
||||
{
|
||||
All, // damps motion in all directions
|
||||
ForceDirection, // damps motion in the direction of force.
|
||||
SurfaceDirection // damps motion toward/away from the surface of the zone.
|
||||
}
|
||||
|
||||
public Color color;
|
||||
public ZoneType type;
|
||||
public ForceMode mode;
|
||||
public DampingDirection dampingDir;
|
||||
public float intensity;
|
||||
public float minDistance;
|
||||
public float maxDistance;
|
||||
public float falloffPower;
|
||||
public float damping;
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5c189f8dc8ade4e14a960f3b6eb11f14
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,56 +0,0 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace Obi
|
||||
{
|
||||
public struct InertialFrame
|
||||
{
|
||||
public AffineTransform frame;
|
||||
public AffineTransform prevFrame;
|
||||
|
||||
public Vector4 velocity;
|
||||
public Vector4 angularVelocity;
|
||||
|
||||
public Vector4 acceleration;
|
||||
public Vector4 angularAcceleration;
|
||||
|
||||
public InertialFrame(Vector4 position, Vector4 scale, Quaternion rotation)
|
||||
{
|
||||
this.frame = new AffineTransform(position, rotation, scale);
|
||||
this.prevFrame = frame;
|
||||
|
||||
velocity = Vector4.zero;
|
||||
angularVelocity = Vector4.zero;
|
||||
acceleration = Vector4.zero;
|
||||
angularAcceleration = Vector4.zero;
|
||||
}
|
||||
|
||||
public InertialFrame(AffineTransform frame)
|
||||
{
|
||||
this.frame = frame;
|
||||
this.prevFrame = frame;
|
||||
|
||||
velocity = Vector4.zero;
|
||||
angularVelocity = Vector4.zero;
|
||||
acceleration = Vector4.zero;
|
||||
angularAcceleration = Vector4.zero;
|
||||
}
|
||||
|
||||
public void Update(Vector4 position, Vector4 scale, Quaternion rotation, float dt)
|
||||
{
|
||||
prevFrame = frame;
|
||||
Vector4 prevVelocity = velocity;
|
||||
Vector4 prevAngularVelocity = angularVelocity;
|
||||
|
||||
frame.translation = position;
|
||||
frame.rotation = rotation;
|
||||
frame.scale = scale;
|
||||
|
||||
velocity = ObiIntegration.DifferentiateLinear(frame.translation, prevFrame.translation, dt);
|
||||
angularVelocity = ObiIntegration.DifferentiateAngular(frame.rotation, prevFrame.rotation, dt);
|
||||
|
||||
acceleration = ObiIntegration.DifferentiateLinear(velocity, prevVelocity, dt);
|
||||
angularAcceleration = ObiIntegration.DifferentiateLinear(angularVelocity, prevAngularVelocity, dt);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7420f38eb63f04112a965d884f75d176
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -12,6 +12,11 @@ namespace Obi
|
||||
for (int i = 0; i < capacity; ++i)
|
||||
this[i] = Color.white;
|
||||
}
|
||||
public ObiNativeColorList(int capacity, int alignment, Color defaultValue) : base(capacity, alignment)
|
||||
{
|
||||
for (int i = 0; i < capacity; ++i)
|
||||
this[i] = defaultValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f377d76dadb3945b39cfac818a54d1be
|
||||
guid: 391ffe5ba25364d2393afabc3083daa1
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Obi
|
||||
{
|
||||
[Serializable]
|
||||
public class ObiNativeContactList : ObiNativeList<Oni.Contact>
|
||||
{
|
||||
public ObiNativeContactList() { }
|
||||
public ObiNativeContactList(int capacity = 8, int alignment = 16) : base(capacity, alignment)
|
||||
{
|
||||
for (int i = 0; i < capacity; ++i)
|
||||
this[i] = new Oni.Contact();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: aa58238d45b934704aede27415ce4242
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,17 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Obi
|
||||
{
|
||||
[Serializable]
|
||||
public class ObiNativeContactShapeList : ObiNativeList<Oni.Contact>
|
||||
{
|
||||
public ObiNativeContactShapeList() { }
|
||||
public ObiNativeContactShapeList(int capacity = 8, int alignment = 16) : base(capacity, alignment)
|
||||
{
|
||||
for (int i = 0; i < capacity; ++i)
|
||||
this[i] = new Oni.Contact();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 032673eb10713468fb72d04ad5cc6b26
|
||||
guid: 5ac88e1dd7e0249b5b361d60655d8d88
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
@@ -1,17 +0,0 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Obi
|
||||
{
|
||||
[Serializable]
|
||||
public class ObiNativeEffectiveMassesList : ObiNativeList<ContactEffectiveMasses>
|
||||
{
|
||||
public ObiNativeEffectiveMassesList() { }
|
||||
public ObiNativeEffectiveMassesList(int capacity = 8, int alignment = 16) : base(capacity, alignment)
|
||||
{
|
||||
for (int i = 0; i < capacity; ++i)
|
||||
this[i] = new ContactEffectiveMasses();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: fc8b00cd716564eff82fc19de0c458a8
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,17 +0,0 @@
|
||||
using System;
|
||||
|
||||
namespace Obi
|
||||
{
|
||||
[Serializable]
|
||||
public class ObiNativeEmitPointList : ObiNativeList<EmitPoint>
|
||||
{
|
||||
public ObiNativeEmitPointList() { }
|
||||
public ObiNativeEmitPointList(int capacity = 8, int alignment = 16) : base(capacity, alignment)
|
||||
{
|
||||
for (int i = 0; i < capacity; ++i)
|
||||
this[i] = new EmitPoint();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3cbd83091221b46c9a724f3275281d2e
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,18 +0,0 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Obi
|
||||
{
|
||||
[Serializable]
|
||||
public class ObiNativeForceZoneList : ObiNativeList<ForceZone>
|
||||
{
|
||||
public ObiNativeForceZoneList() { }
|
||||
public ObiNativeForceZoneList(int capacity = 8, int alignment = 16) : base(capacity, alignment)
|
||||
{
|
||||
for (int i = 0; i < capacity; ++i)
|
||||
this[i] = new ForceZone();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2815feee105f648149e0f5b1b22954bf
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Obi
|
||||
{
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Obi
|
||||
{
|
||||
|
||||
@@ -5,12 +5,10 @@ using UnityEngine;
|
||||
using Unity.Collections;
|
||||
using Unity.Collections.LowLevel.Unsafe;
|
||||
using System.Collections;
|
||||
using UnityEngine.Rendering;
|
||||
using System.Linq;
|
||||
|
||||
namespace Obi
|
||||
{
|
||||
public unsafe class ObiNativeList<T> : IEnumerable<T>, IDisposable, ISerializationCallbackReceiver where T : struct
|
||||
public unsafe abstract class ObiNativeList<T> : IEnumerable<T>, IDisposable, ISerializationCallbackReceiver where T : struct
|
||||
{
|
||||
public T[] serializedContents;
|
||||
protected void* m_AlignedPtr = null;
|
||||
@@ -23,13 +21,7 @@ namespace Obi
|
||||
#if ENABLE_UNITY_COLLECTIONS_CHECKS
|
||||
protected AtomicSafetyHandle m_SafetyHandle;
|
||||
#endif
|
||||
|
||||
protected GraphicsBuffer.Target m_ComputeBufferType;
|
||||
protected GraphicsBuffer m_ComputeBuffer;
|
||||
protected GraphicsBuffer m_CountBuffer; // used to hold the counter value in case m_ComputeBufferType is Counter.
|
||||
protected bool computeBufferDirty = false;
|
||||
protected AsyncGPUReadbackRequest m_AsyncRequest;
|
||||
protected AsyncGPUReadbackRequest m_CounterAsyncRequest;
|
||||
protected ComputeBuffer m_ComputeBuffer;
|
||||
|
||||
public int count
|
||||
{
|
||||
@@ -41,9 +33,6 @@ namespace Obi
|
||||
// we first ensure we can hold the previous count, and then set the new one.
|
||||
EnsureCapacity(m_Count);
|
||||
m_Count = Mathf.Min(m_Capacity, value);
|
||||
|
||||
if (m_ComputeBuffer != null && m_ComputeBuffer.IsValid() && m_ComputeBufferType == GraphicsBuffer.Target.Counter)
|
||||
m_ComputeBuffer.SetCounterValue((uint)m_Count);
|
||||
}
|
||||
}
|
||||
get { return m_Count; }
|
||||
@@ -51,61 +40,37 @@ namespace Obi
|
||||
|
||||
public int capacity
|
||||
{
|
||||
set
|
||||
{
|
||||
if (value != m_Capacity)
|
||||
ChangeCapacity(value);
|
||||
}
|
||||
get { return m_Capacity; }
|
||||
}
|
||||
|
||||
public int stride
|
||||
{
|
||||
get { return m_Stride; }
|
||||
}
|
||||
|
||||
public bool isCreated
|
||||
{
|
||||
get { return m_AlignedPtr != null; }
|
||||
}
|
||||
|
||||
public bool noReadbackInFlight
|
||||
{
|
||||
get { return m_AsyncRequest.done && (m_ComputeBufferType != GraphicsBuffer.Target.Counter || m_CounterAsyncRequest.done); }
|
||||
}
|
||||
|
||||
// Returns the current compute buffer representation of this list. Will return null if AsComputeBuffer() hasn't been called yet,
|
||||
// or if the list has been disposed of.
|
||||
public GraphicsBuffer computeBuffer
|
||||
{
|
||||
get { return m_ComputeBuffer; }
|
||||
}
|
||||
|
||||
public T this[int index]
|
||||
{
|
||||
get
|
||||
{
|
||||
#if ENABLE_UNITY_COLLECTIONS_CHECKS
|
||||
if (index < 0 || index >= m_Capacity)
|
||||
{
|
||||
throw new IndexOutOfRangeException($"Reading from index {index} is out of range of '{m_Capacity}' Capacity.");
|
||||
}
|
||||
#endif
|
||||
return UnsafeUtility.ReadArrayElementWithStride<T>(m_AlignedPtr, index, m_Stride);
|
||||
}
|
||||
set
|
||||
{
|
||||
#if ENABLE_UNITY_COLLECTIONS_CHECKS
|
||||
if (index < 0 || index >= m_Capacity)
|
||||
{
|
||||
throw new IndexOutOfRangeException($"Writing to index {index} is out of range of '{m_Capacity}' Capacity.");
|
||||
}
|
||||
#endif
|
||||
UnsafeUtility.WriteArrayElementWithStride<T>(m_AlignedPtr, index, m_Stride, value);
|
||||
computeBufferDirty = true;
|
||||
|
||||
if (m_ComputeBuffer != null)
|
||||
m_ComputeBuffer.SetData(AsNativeArray<T>(), index, index, 1);
|
||||
}
|
||||
}
|
||||
|
||||
// Declare parameterless constructor, called by Unity upon deserialization.
|
||||
protected ObiNativeList()
|
||||
{
|
||||
m_Stride = UnsafeUtility.SizeOf<T>();
|
||||
|
||||
#if ENABLE_UNITY_COLLECTIONS_CHECKS
|
||||
m_SafetyHandle = AtomicSafetyHandle.Create();
|
||||
#endif
|
||||
@@ -113,8 +78,6 @@ namespace Obi
|
||||
|
||||
public ObiNativeList(int capacity = 8, int alignment = 16)
|
||||
{
|
||||
m_Stride = UnsafeUtility.SizeOf<T>();
|
||||
|
||||
#if ENABLE_UNITY_COLLECTIONS_CHECKS
|
||||
m_SafetyHandle = AtomicSafetyHandle.Create();
|
||||
#endif
|
||||
@@ -129,21 +92,24 @@ namespace Obi
|
||||
|
||||
protected void Dispose(bool disposing)
|
||||
{
|
||||
DisposeOfComputeBuffer();
|
||||
|
||||
if (isCreated)
|
||||
{
|
||||
|
||||
// dispose of compuse buffer representation:
|
||||
if (m_ComputeBuffer != null)
|
||||
{
|
||||
m_ComputeBuffer.Dispose();
|
||||
}
|
||||
|
||||
// free unmanaged memory buffer:
|
||||
UnsafeUtility.Free(m_AlignedPtr, Allocator.Persistent);
|
||||
m_AlignedPtr = null;
|
||||
m_Count = m_Capacity = 0;
|
||||
|
||||
#if ENABLE_UNITY_COLLECTIONS_CHECKS
|
||||
// dispose of atomic safety handle:
|
||||
AtomicSafetyHandle.CheckDeallocateAndThrow(m_SafetyHandle);
|
||||
AtomicSafetyHandle.Release(m_SafetyHandle);
|
||||
#endif
|
||||
}
|
||||
#if ENABLE_UNITY_COLLECTIONS_CHECKS
|
||||
// dispose of atomic safety handle:
|
||||
AtomicSafetyHandle.CheckDeallocateAndThrow(m_SafetyHandle);
|
||||
AtomicSafetyHandle.Release(m_SafetyHandle);
|
||||
#endif
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
@@ -151,26 +117,6 @@ namespace Obi
|
||||
Dispose(true);
|
||||
}
|
||||
|
||||
public void DisposeOfComputeBuffer()
|
||||
{
|
||||
// dispose of compute buffer representation:
|
||||
if (m_ComputeBuffer != null)
|
||||
{
|
||||
// if there's any pending async readback, finalize it.
|
||||
// otherwise we pull the rug from under the readbacks' feet and that's no good.
|
||||
WaitForReadback();
|
||||
|
||||
m_ComputeBuffer.Dispose();
|
||||
m_ComputeBuffer = null;
|
||||
}
|
||||
|
||||
if (m_CountBuffer != null)
|
||||
{
|
||||
m_CountBuffer.Dispose();
|
||||
m_CountBuffer = null;
|
||||
}
|
||||
}
|
||||
|
||||
public void OnBeforeSerialize()
|
||||
{
|
||||
if (isCreated)
|
||||
@@ -215,144 +161,38 @@ namespace Obi
|
||||
return AsNativeArray<U>(m_Count);
|
||||
}
|
||||
|
||||
public NativeArray<T> AsNativeArray()
|
||||
{
|
||||
return AsNativeArray<T>(m_Count);
|
||||
}
|
||||
|
||||
// Reinterprets the data in the list as a native array of the given length, up to the list's capacity.
|
||||
// Reinterprets the data in the list as a native array.
|
||||
public NativeArray<U> AsNativeArray<U>(int arrayLength) where U : struct
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
NativeArray<U> array = NativeArrayUnsafeUtility.ConvertExistingDataToNativeArray<U>(m_AlignedPtr, Mathf.Min(arrayLength, m_Capacity), Allocator.None);
|
||||
NativeArray<U> array = NativeArrayUnsafeUtility.ConvertExistingDataToNativeArray<U>(m_AlignedPtr, arrayLength, Allocator.None);
|
||||
|
||||
#if ENABLE_UNITY_COLLECTIONS_CHECKS
|
||||
NativeArrayUnsafeUtility.SetAtomicSafetyHandle(ref array, m_SafetyHandle);
|
||||
#endif
|
||||
// assume the NativeArray will write new data, so we'll need to update the computeBuffer upon Upload().
|
||||
computeBufferDirty = true;
|
||||
return array;
|
||||
}
|
||||
}
|
||||
|
||||
// Reinterprets the data in the list as a compute buffer, in case of an empty list it returns a buffer of size 1 with uninitialized content.
|
||||
public GraphicsBuffer SafeAsComputeBuffer<U>(GraphicsBuffer.Target bufferType = GraphicsBuffer.Target.Structured) where U : struct
|
||||
// Reinterprets the data in the list as a compute buffer. Note: This also calls AsNativeArray() internally, to be able to pass the raw pointer to the compute buffer
|
||||
public ComputeBuffer AsComputeBuffer<U>() where U : struct
|
||||
{
|
||||
return AsComputeBuffer<U>(Mathf.Max(1,m_Count), bufferType);
|
||||
}
|
||||
|
||||
// Reinterprets the data in the list as a compute buffer.
|
||||
public GraphicsBuffer AsComputeBuffer<U>(GraphicsBuffer.Target bufferType = GraphicsBuffer.Target.Structured) where U : struct
|
||||
{
|
||||
return AsComputeBuffer<U>(m_Count, bufferType);
|
||||
}
|
||||
|
||||
// Reinterprets the data in the list as a compute buffer of the given length. Returns null if the list is empty.
|
||||
public GraphicsBuffer AsComputeBuffer<U>(int arrayLength, GraphicsBuffer.Target bufferType = GraphicsBuffer.Target.Structured) where U : struct
|
||||
{
|
||||
DisposeOfComputeBuffer();
|
||||
|
||||
if (arrayLength > 0)
|
||||
{
|
||||
m_ComputeBufferType = bufferType;
|
||||
m_ComputeBuffer = new GraphicsBuffer(bufferType, arrayLength, UnsafeUtility.SizeOf<U>());
|
||||
m_ComputeBuffer.SetData(AsNativeArray<U>(arrayLength));
|
||||
|
||||
if (bufferType == GraphicsBuffer.Target.Counter)
|
||||
{
|
||||
// initialize count to zero, since counter buffers always start empty:
|
||||
m_Count = 0;
|
||||
m_ComputeBuffer.SetCounterValue((uint)m_Count);
|
||||
m_CountBuffer = new GraphicsBuffer(GraphicsBuffer.Target.IndirectArguments, 1, 4);
|
||||
GraphicsBuffer.CopyCount(m_ComputeBuffer, m_CountBuffer, 0);
|
||||
}
|
||||
|
||||
return m_ComputeBuffer;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// Kicks a GPU readback request, to bring compute buffer data to this list.
|
||||
public void Readback<U>(int readcount, bool async) where U : struct
|
||||
{
|
||||
if (m_ComputeBuffer != null && m_ComputeBuffer.IsValid() && noReadbackInFlight)
|
||||
{
|
||||
var nativeArray = AsNativeArray<U>(readcount);
|
||||
|
||||
// When using SafeAsComputeBuffer, we'll get a compute buffer of size 1 even if the list (and the NativeArray) is empty.
|
||||
// Guard against trying to readback into a smaller NativeArray. Also guard against requesting zero items.
|
||||
if (nativeArray.Length >= readcount && readcount > 0)
|
||||
m_AsyncRequest = AsyncGPUReadback.RequestIntoNativeArray(ref nativeArray, m_ComputeBuffer, readcount * UnsafeUtility.SizeOf<U>(), 0);
|
||||
|
||||
// For counter buffers, request the counter value too:
|
||||
if (m_ComputeBufferType == GraphicsBuffer.Target.Counter)
|
||||
{
|
||||
GraphicsBuffer.CopyCount(m_ComputeBuffer, m_CountBuffer, 0);
|
||||
m_CounterAsyncRequest = AsyncGPUReadback.Request(m_CountBuffer, m_CountBuffer.stride, 0, (AsyncGPUReadbackRequest request)=>
|
||||
{
|
||||
if (!request.hasError)
|
||||
m_Count = Mathf.Min(m_Capacity, request.GetData<int>()[0]);
|
||||
});
|
||||
}
|
||||
|
||||
if (!async)
|
||||
WaitForReadback();
|
||||
}
|
||||
}
|
||||
|
||||
public void Readback(bool async = true)
|
||||
{
|
||||
// On counter buffers, we shouldn't read data up to m_Count and then update m_Count with the compute buffer's counter value *afterwards*.
|
||||
// This would lead to reading back less data than we should, so we need to request the entire compute buffer.
|
||||
if (m_ComputeBuffer != null)
|
||||
Readback<T>(m_ComputeBuffer.count, async);
|
||||
}
|
||||
|
||||
public void Readback(int readcount ,bool async = true)
|
||||
{
|
||||
Readback<T>(readcount, async);
|
||||
}
|
||||
|
||||
// Makes sure any pending changes by the CPU are sent to the GPU.
|
||||
// If the list data has been changed on the CPU since the last time Unmap() was called and there's a compute buffer associated to it,
|
||||
// will write the current contents of the list to the compute buffer.
|
||||
public void Upload<U>(int length, bool force = false) where U : struct
|
||||
{
|
||||
if ((computeBufferDirty || force) && m_ComputeBuffer != null && m_ComputeBuffer.IsValid())
|
||||
m_ComputeBuffer.SetData(AsNativeArray<U>(length));
|
||||
|
||||
computeBufferDirty = false;
|
||||
}
|
||||
|
||||
public void Upload(bool force = false)
|
||||
{
|
||||
Upload<T>(m_Count,force);
|
||||
}
|
||||
|
||||
public void UploadFullCapacity()
|
||||
{
|
||||
Upload<T>(m_Capacity, true);
|
||||
}
|
||||
|
||||
// Waits for the last readback request to be complete, this brings back data from the GPU to the CPU:
|
||||
public void WaitForReadback()
|
||||
{
|
||||
if (isCreated)
|
||||
{
|
||||
m_AsyncRequest.WaitForCompletion();
|
||||
m_CounterAsyncRequest.WaitForCompletion();
|
||||
m_ComputeBuffer.Dispose();
|
||||
}
|
||||
|
||||
m_ComputeBuffer = new ComputeBuffer(m_Count, m_Stride);
|
||||
m_ComputeBuffer.SetData(AsNativeArray<U>());
|
||||
return m_ComputeBuffer;
|
||||
}
|
||||
|
||||
protected void ChangeCapacity(int newCapacity)
|
||||
{
|
||||
// invalidate compute buffer:
|
||||
DisposeOfComputeBuffer();
|
||||
|
||||
// allocate a new buffer:
|
||||
m_Stride = UnsafeUtility.SizeOf<T>();
|
||||
var newAlignedPtr = UnsafeUtility.Malloc(newCapacity * m_Stride, m_AlignBytes, Allocator.Persistent);
|
||||
var newAlignedPtr = UnsafeUtility.Malloc(newCapacity * m_Stride, 16, Allocator.Persistent);
|
||||
|
||||
// if there was a previous allocation:
|
||||
if (isCreated)
|
||||
@@ -409,45 +249,7 @@ namespace Obi
|
||||
void* sourceAddress = source.AddressOfElement(sourceIndex);
|
||||
void* destAddress = AddressOfElement(destIndex);
|
||||
UnsafeUtility.MemCpy(destAddress, sourceAddress, length * m_Stride);
|
||||
}
|
||||
|
||||
public void CopyFrom<U>(NativeArray<U> source, int sourceIndex, int destIndex, int length) where U : struct
|
||||
{
|
||||
if (!isCreated || !source.IsCreated || UnsafeUtility.SizeOf<U>() != m_Stride)
|
||||
throw new ArgumentNullException();
|
||||
|
||||
if (length <= 0 || source.Length == 0)
|
||||
return;
|
||||
|
||||
if (sourceIndex >= source.Length || sourceIndex < 0 || destIndex >= m_Count || destIndex < 0 ||
|
||||
sourceIndex + length > source.Length || destIndex + length > m_Count)
|
||||
throw new ArgumentOutOfRangeException();
|
||||
|
||||
void* sourceAddress = (byte*)source.GetUnsafePtr() + sourceIndex * m_Stride;
|
||||
void* destAddress = AddressOfElement(destIndex);
|
||||
UnsafeUtility.MemCpy(destAddress, sourceAddress, length * m_Stride);
|
||||
}
|
||||
|
||||
public void CopyFrom(T[] source, int sourceIndex, int destIndex, int length)
|
||||
{
|
||||
if (source == null || !isCreated)
|
||||
throw new ArgumentNullException();
|
||||
|
||||
if (length <= 0 || source.Length == 0)
|
||||
return;
|
||||
|
||||
if (sourceIndex < 0 || destIndex < 0 ||
|
||||
sourceIndex + length > source.Length || destIndex + length > m_Count)
|
||||
throw new ArgumentOutOfRangeException();
|
||||
|
||||
// pin the managed array and get its address:
|
||||
ulong sourceHandle;
|
||||
void* sourceAddress = UnsafeUtility.PinGCArrayAndGetDataAddress(source, out sourceHandle);
|
||||
void* destAddress = UnsafeUtility.AddressOf(ref UnsafeUtility.ArrayElementAsRef<T>(m_AlignedPtr, destIndex));
|
||||
UnsafeUtility.MemCpy(destAddress, sourceAddress, length * m_Stride);
|
||||
|
||||
// unpin the managed array:
|
||||
UnsafeUtility.ReleaseGCObject(sourceHandle);
|
||||
}
|
||||
|
||||
public void CopyReplicate(T value, int destIndex, int length)
|
||||
@@ -491,53 +293,9 @@ namespace Obi
|
||||
public void Add(T item)
|
||||
{
|
||||
EnsureCapacity(m_Count + 1);
|
||||
computeBufferDirty = true;
|
||||
this[m_Count++] = item;
|
||||
}
|
||||
|
||||
public void AddReplicate(T value, int times)
|
||||
{
|
||||
int appendAt = m_Count;
|
||||
ResizeUninitialized(m_Count + times);
|
||||
CopyReplicate(value, appendAt, times);
|
||||
}
|
||||
|
||||
public void AddRange(T[] array)
|
||||
{
|
||||
AddRange(array, array.Length);
|
||||
}
|
||||
|
||||
public void AddRange(T[] array, int length)
|
||||
{
|
||||
AddRange(array, 0, length);
|
||||
}
|
||||
|
||||
public void AddRange(T[] array, int start, int length)
|
||||
{
|
||||
int appendAt = m_Count;
|
||||
ResizeUninitialized(m_Count + length);
|
||||
CopyFrom(array, start, appendAt, length);
|
||||
}
|
||||
|
||||
public void AddRange(ObiNativeList<T> array, int length)
|
||||
{
|
||||
int appendAt = m_Count;
|
||||
ResizeUninitialized(m_Count + length);
|
||||
CopyFrom(array, 0, appendAt, length);
|
||||
}
|
||||
|
||||
public void AddRange(ObiNativeList<T> array, int start, int length)
|
||||
{
|
||||
int appendAt = m_Count;
|
||||
ResizeUninitialized(m_Count + length);
|
||||
CopyFrom(array, start, appendAt, length);
|
||||
}
|
||||
|
||||
public void AddRange(ObiNativeList<T> array)
|
||||
{
|
||||
AddRange(array, array.count);
|
||||
}
|
||||
|
||||
public void AddRange(IEnumerable<T> enumerable)
|
||||
{
|
||||
ICollection<T> collection = enumerable as ICollection<T>;
|
||||
@@ -585,9 +343,7 @@ namespace Obi
|
||||
{
|
||||
newCount = Mathf.Max(0, newCount);
|
||||
bool realloc = EnsureCapacity(newCount);
|
||||
|
||||
m_Count = newCount;
|
||||
|
||||
return realloc;
|
||||
}
|
||||
|
||||
@@ -625,25 +381,7 @@ namespace Obi
|
||||
unsafe
|
||||
{
|
||||
if (isCreated)
|
||||
{
|
||||
UnsafeUtility.MemClear(m_AlignedPtr, count * m_Stride);
|
||||
|
||||
computeBufferDirty = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void WipeToValue(T value)
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
if (isCreated)
|
||||
{
|
||||
void* sourceAddress = UnsafeUtility.AddressOf(ref value);
|
||||
UnsafeUtility.MemCpyReplicate(m_AlignedPtr, sourceAddress, m_Stride, count);
|
||||
|
||||
computeBufferDirty = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -665,14 +403,10 @@ namespace Obi
|
||||
|
||||
public void* AddressOfElement(int index)
|
||||
{
|
||||
// UnsafeUtility.AddressOf(ref UnsafeUtilityEx.ArrayElementAsRef<T>(m_AlignedPtr, m_Count));
|
||||
return (void*) ((byte*)m_AlignedPtr + m_Stride * index);
|
||||
}
|
||||
|
||||
public NativeReference<int> GetCountReference(Allocator alloc)
|
||||
{
|
||||
return new NativeReference<int>(m_Count, alloc);
|
||||
}
|
||||
|
||||
public IntPtr GetIntPtr()
|
||||
{
|
||||
if (isCreated)
|
||||
|
||||
@@ -6,18 +6,11 @@ namespace Obi
|
||||
[Serializable]
|
||||
public class ObiNativeMatrix4x4List : ObiNativeList<Matrix4x4>
|
||||
{
|
||||
public ObiNativeMatrix4x4List() { } // TODO: WTF???? why does this prevent a crash?
|
||||
public ObiNativeMatrix4x4List(int capacity = 8, int alignment = 16) : base(capacity, alignment)
|
||||
{
|
||||
for (int i = 0; i < capacity; ++i)
|
||||
this[i] = Matrix4x4.identity;
|
||||
}
|
||||
|
||||
public ObiNativeMatrix4x4List(int capacity, int alignment, Matrix4x4 defaultValue) : base(capacity, alignment)
|
||||
{
|
||||
for (int i = 0; i < capacity; ++i)
|
||||
this[i] = defaultValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 57e079b439b8f4d71966919ca4fa4838
|
||||
guid: f6b7295a6ebb4475fb050c4c5b3770cc
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Obi
|
||||
{
|
||||
[Serializable]
|
||||
public class ObiNativeUIntList : ObiNativeList<uint>
|
||||
{
|
||||
|
||||
public ObiNativeUIntList(int capacity = 8, int alignment = 16) : base(capacity, alignment)
|
||||
{
|
||||
for (int i = 0; i < capacity; ++i)
|
||||
this[i] = 0;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: cee276900c5ce412aafe4e23112537eb
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -110,7 +110,7 @@ namespace Obi
|
||||
|
||||
EnsureCapacity(++count);
|
||||
|
||||
for (int i = count-1; i > index; --i)
|
||||
for (int i = count-1; i > index; ++i)
|
||||
{
|
||||
data[i] = data[i-1];
|
||||
}
|
||||
|
||||
@@ -6,12 +6,11 @@ namespace Obi
|
||||
[StructLayout(LayoutKind.Sequential, Size = 64)]
|
||||
public struct QueryResult
|
||||
{
|
||||
public Vector4 simplexBary; /**< Barycentric coords of nearest point in simplex */
|
||||
public Vector4 queryPoint; /**< Nearest point in query shape*/
|
||||
public Vector4 normal; /**< Closest direction between simplex and query shape. */
|
||||
public float distance; /**< Distance between simplex and query shape.*/
|
||||
public float distanceAlongRay; /**< For ray queries, distance along the ray.*/
|
||||
public int simplexIndex; /**< Index of the simplex in the solver.*/
|
||||
public int queryIndex; /**< Index of the query that spawned this result.*/
|
||||
public Vector4 simplexBary; /**< Barycentric coords of nearest point in simplex */
|
||||
public Vector4 queryPoint; /**< Nearest point in query shape*/
|
||||
public Vector4 normal; /**< Closest direction between simplex and query shape. */
|
||||
public float distance; /**< Distance between simplex and query shape.*/
|
||||
public int simplexIndex; /**< Index of the simplex in the solver.*/
|
||||
public int queryIndex; /**< Index of the query that spawned this result.*/
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2c4a05fed09dd4e9e9d346fa3f1ccd55
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,106 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Obi
|
||||
{
|
||||
public class RegularGrid<T>
|
||||
{
|
||||
private Dictionary<Vector3Int, List<T>> gridMap = new Dictionary<Vector3Int, List<T>>();
|
||||
private float cellSize;
|
||||
private Func<T, Vector3> getPosition;
|
||||
|
||||
public RegularGrid(float cellSize, Func<T, Vector3> getPosition)
|
||||
{
|
||||
this.cellSize = cellSize;
|
||||
|
||||
if (getPosition != null)
|
||||
this.getPosition = getPosition;
|
||||
else
|
||||
getPosition = (x) => { return Vector3.zero; };
|
||||
}
|
||||
|
||||
public Vector3Int GetCellCoords(Vector3 pos)
|
||||
{
|
||||
return new Vector3Int(Mathf.FloorToInt(pos.x / cellSize),
|
||||
Mathf.FloorToInt(pos.y / cellSize),
|
||||
Mathf.FloorToInt(pos.z / cellSize));
|
||||
}
|
||||
|
||||
public void AddElement(T elm)
|
||||
{
|
||||
var coords = GetCellCoords(getPosition(elm));
|
||||
if (gridMap.TryGetValue(coords, out List<T> cell))
|
||||
cell.Add(elm);
|
||||
else
|
||||
gridMap[coords] = new List<T> { elm };
|
||||
}
|
||||
|
||||
public bool RemoveElement(T elm)
|
||||
{
|
||||
var coords = GetCellCoords(getPosition(elm));
|
||||
if (gridMap.TryGetValue(coords, out List<T> cell))
|
||||
{
|
||||
return cell.Remove(elm);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public IEnumerable<T> GetNeighborsEnumerator(T elm)
|
||||
{
|
||||
// if cells are infinitesimaly small,
|
||||
// elements should have no neighbors.
|
||||
if (cellSize < ObiUtils.epsilon)
|
||||
yield break;
|
||||
|
||||
var position = getPosition(elm);
|
||||
var coords = GetCellCoords(position);
|
||||
List<T> cell;
|
||||
|
||||
for (int x = -1; x <= 1; ++x)
|
||||
for (int y = -1; y <= 1; ++y)
|
||||
for (int z = -1; z <= 1; ++z)
|
||||
{
|
||||
if (gridMap.TryGetValue(coords + new Vector3Int(x,y,z), out cell))
|
||||
{
|
||||
foreach(T n in cell)
|
||||
{
|
||||
if (n.Equals(elm))
|
||||
continue;
|
||||
|
||||
float dist = Vector3.Distance(position, getPosition(n));
|
||||
if (dist <= cellSize)
|
||||
yield return n;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: single call passing position and element to ignore.
|
||||
public IEnumerable<T> GetNeighborsEnumerator(Vector3 position)
|
||||
{
|
||||
// if cells are infinitesimaly small,
|
||||
// elements should have no neighbors.
|
||||
if (cellSize < ObiUtils.epsilon)
|
||||
yield break;
|
||||
|
||||
var coords = GetCellCoords(position);
|
||||
List<T> cell;
|
||||
|
||||
for (int x = -1; x <= 1; ++x)
|
||||
for (int y = -1; y <= 1; ++y)
|
||||
for (int z = -1; z <= 1; ++z)
|
||||
{
|
||||
if (gridMap.TryGetValue(coords + new Vector3Int(x, y, z), out cell))
|
||||
{
|
||||
foreach (T n in cell)
|
||||
{
|
||||
float dist = Vector3.Distance(position, getPosition(n));
|
||||
if (dist <= cellSize)
|
||||
yield return n;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9506e58093a7c491f944e8e74529bf74
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -24,20 +24,21 @@ namespace Obi
|
||||
|
||||
public int GetSimplexStartAndSize(int index, out int size)
|
||||
{
|
||||
if (index < triangleCount)
|
||||
if (index < pointCount)
|
||||
{
|
||||
size = 3;
|
||||
return index * 3;
|
||||
size = 1;
|
||||
return index;
|
||||
}
|
||||
else if (index < triangleCount + edgeCount)
|
||||
else if (index < pointCount + edgeCount)
|
||||
{
|
||||
size = 2;
|
||||
return triangleCount * 3 + (index - triangleCount) * 2;
|
||||
return pointCount + (index - pointCount) * 2;
|
||||
}
|
||||
else if (index < simplexCount)
|
||||
{
|
||||
size = 1;
|
||||
return triangleCount * 3 + edgeCount * 2 + (index - triangleCount - edgeCount);
|
||||
size = 3;
|
||||
int triStart = pointCount + edgeCount * 2;
|
||||
return triStart + (index - pointCount - edgeCount) * 3;
|
||||
}
|
||||
size = 0;
|
||||
return 0;
|
||||
|
||||
@@ -1,11 +1,8 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
#if (OBI_MATHEMATICS)
|
||||
using Unity.Mathematics;
|
||||
#endif
|
||||
|
||||
|
||||
namespace Obi
|
||||
{
|
||||
[Serializable]
|
||||
@@ -32,10 +29,5 @@ namespace Obi
|
||||
this.z = x;
|
||||
this.w = x;
|
||||
}
|
||||
|
||||
#if (OBI_MATHEMATICS)
|
||||
public static implicit operator VInt4(int4 i) => new VInt4(i.x, i.y, i.z, i.w);
|
||||
public static implicit operator int4(VInt4 i) => new int4(i.x, i.y, i.z, i.w);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@@ -80,7 +80,7 @@ namespace Obi
|
||||
};
|
||||
|
||||
public readonly static Vector3Int[] faceNeighborhood =
|
||||
{
|
||||
{
|
||||
new Vector3Int(-1,0,0),
|
||||
new Vector3Int(1,0,0),
|
||||
new Vector3Int(0,-1,0),
|
||||
@@ -226,15 +226,15 @@ namespace Obi
|
||||
}
|
||||
|
||||
|
||||
public IEnumerator Voxelize(Matrix4x4 transform, Vector3Int axisMask, bool generateTriangleIndices = false)
|
||||
public IEnumerator Voxelize(Matrix4x4 transform, bool generateTriangleIndices = false)
|
||||
{
|
||||
voxelSize = Mathf.Max(0.0001f, voxelSize);
|
||||
|
||||
var xfBounds = input.bounds.Transform(transform);
|
||||
|
||||
// Calculate min and max voxels, adding a 1-voxel margin.
|
||||
origin = GetPointVoxel(Vector3.Scale(xfBounds.min, axisMask)) - axisMask;
|
||||
Vector3Int max = GetPointVoxel(Vector3.Scale(xfBounds.max, axisMask)) + axisMask;
|
||||
origin = GetPointVoxel(xfBounds.min) - new Vector3Int(1, 1, 1);
|
||||
Vector3Int max = GetPointVoxel(xfBounds.max) + new Vector3Int(1, 1, 1);
|
||||
|
||||
resolution = new Vector3Int(max.x - origin.x + 1, max.y - origin.y + 1, max.z - origin.z + 1);
|
||||
|
||||
@@ -263,9 +263,9 @@ namespace Obi
|
||||
// Generate surface voxels:
|
||||
for (int i = 0; i < triIndices.Length; i += 3)
|
||||
{
|
||||
Vector3 v1 = Vector3.Scale(transform.MultiplyPoint3x4(vertices[triIndices[i]]), axisMask);
|
||||
Vector3 v2 = Vector3.Scale(transform.MultiplyPoint3x4(vertices[triIndices[i + 1]]), axisMask);
|
||||
Vector3 v3 = Vector3.Scale(transform.MultiplyPoint3x4(vertices[triIndices[i + 2]]), axisMask);
|
||||
Vector3 v1 = transform.MultiplyPoint3x4(vertices[triIndices[i]]);
|
||||
Vector3 v2 = transform.MultiplyPoint3x4(vertices[triIndices[i + 1]]);
|
||||
Vector3 v3 = transform.MultiplyPoint3x4(vertices[triIndices[i + 2]]);
|
||||
|
||||
Bounds triBounds = GetTriangleBounds(v1, v2, v3);
|
||||
|
||||
@@ -281,7 +281,6 @@ namespace Obi
|
||||
yield return fillCoroutine.Current;
|
||||
}
|
||||
|
||||
// Ensures boundary is only one voxel thick.
|
||||
public void BoundaryThinning()
|
||||
{
|
||||
for (int x = 0; x < resolution.x; ++x)
|
||||
@@ -309,212 +308,6 @@ namespace Obi
|
||||
}
|
||||
}
|
||||
|
||||
// Ensures boundary voxels are 2D.
|
||||
public void MakeBoundary2D()
|
||||
{
|
||||
for (int x = 0; x < resolution.x; ++x)
|
||||
for (int y = 0; y < resolution.y; ++y)
|
||||
for (int z = 0; z < resolution.z; ++z)
|
||||
if (this[x, y, z] == Voxel.Boundary)
|
||||
this[x, y, z] = Voxel.Inside;
|
||||
|
||||
for (int x = 0; x < resolution.x; ++x)
|
||||
for (int y = 0; y < resolution.y; ++y)
|
||||
for (int z = 0; z < resolution.z; ++z)
|
||||
{
|
||||
int sum = 0;
|
||||
for (int j = 0; j < faceNeighborhood.Length; ++j)
|
||||
{
|
||||
var index = faceNeighborhood[j];
|
||||
if (VoxelExists(index.x + x, index.y + y, index.z + z) && this[index.x + x, index.y + y, index.z + z] != Voxel.Outside)
|
||||
{
|
||||
sum++;
|
||||
}
|
||||
}
|
||||
|
||||
if (sum <= 3 && this[x, y, z] == Voxel.Inside)
|
||||
this[x, y, z] = Voxel.Boundary;
|
||||
}
|
||||
}
|
||||
|
||||
public void CreateMesh(ref Mesh mesh, int smoothingIterations)
|
||||
{
|
||||
if (mesh == null)
|
||||
mesh = new Mesh();
|
||||
|
||||
mesh.indexFormat = UnityEngine.Rendering.IndexFormat.UInt32;
|
||||
mesh.Clear();
|
||||
List<Vector3> vertices = new List<Vector3>();
|
||||
List<Vector3> vertices2 = new List<Vector3>();
|
||||
List<int> tris = new List<int>();
|
||||
vertices.Clear();
|
||||
vertices2.Clear();
|
||||
tris.Clear();
|
||||
|
||||
int[] vtxIndex = new int[voxelCount];
|
||||
for (int i = 0; i < vtxIndex.Length; ++i)
|
||||
vtxIndex[i] = -1;
|
||||
|
||||
// create vertices:
|
||||
for (int x = 0; x < resolution.x; ++x)
|
||||
for (int y = 0; y < resolution.y; ++y)
|
||||
for (int z = 0; z < resolution.z; ++z)
|
||||
if (this[x, y, z] == Voxel.Boundary)
|
||||
{
|
||||
vtxIndex[GetVoxelIndex(x, y, z)] = vertices.Count;
|
||||
var vtx = new Vector3(Origin.x + x + 0.5f, Origin.y + y + 0.5f, Origin.z + z + 0.5f) * voxelSize;
|
||||
vertices.Add(vtx);
|
||||
vertices2.Add(vtx);
|
||||
}
|
||||
|
||||
List<Vector3> inputVertices = vertices;
|
||||
List<Vector3> outputVertices = vertices2;
|
||||
for (int i = 0; i < smoothingIterations; ++i)
|
||||
{
|
||||
for (int x = 0; x < resolution.x; ++x)
|
||||
for (int y = 0; y < resolution.y; ++y)
|
||||
for (int z = 0; z < resolution.z; ++z)
|
||||
if (this[x, y, z] == Voxel.Boundary)
|
||||
{
|
||||
Vector3 avg = Vector3.zero;
|
||||
|
||||
int count = 0;
|
||||
for (int j = 0; j < faceNeighborhood.Length; ++j)
|
||||
{
|
||||
var index = faceNeighborhood[j];
|
||||
if (VoxelExists(index.x + x, index.y + y, index.z + z) && this[index.x + x, index.y + y, index.z + z] == Voxel.Boundary)
|
||||
{
|
||||
avg += inputVertices[vtxIndex[GetVoxelIndex(index.x + x, index.y + y, index.z + z)]];
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
if (count > 0)
|
||||
outputVertices[vtxIndex[GetVoxelIndex(x, y, z)]] = avg / count;
|
||||
}
|
||||
|
||||
var aux = inputVertices;
|
||||
inputVertices = outputVertices;
|
||||
outputVertices = aux;
|
||||
}
|
||||
|
||||
// triangulate
|
||||
for (int x = 0; x < resolution.x; ++x)
|
||||
for (int y = 0; y < resolution.y; ++y)
|
||||
for (int z = 0; z < resolution.z; ++z)
|
||||
if (this[x, y, z] == Voxel.Boundary)
|
||||
{
|
||||
int x0y0z0 = GetVoxelIndex(x, y, z);
|
||||
|
||||
int x1y0z0 = VoxelExists(x + 1, y, z) ? GetVoxelIndex(x+1, y, z) : -1;
|
||||
int x1y1z0 = VoxelExists(x + 1, y + 1, z) ? GetVoxelIndex(x+1, y+1, z) : -1;
|
||||
int x0y1z0 = VoxelExists(x, y + 1, z) ? GetVoxelIndex(x, y+1, z) : -1;
|
||||
|
||||
int x0y0z1 = VoxelExists(x, y, z + 1) ? GetVoxelIndex(x, y , z + 1) : -1;
|
||||
int x0y1z1 = VoxelExists(x, y + 1, z + 1) ? GetVoxelIndex(x, y + 1, z + 1) : -1;
|
||||
int x1y0z1 = VoxelExists(x + 1, y, z + 1) ? GetVoxelIndex(x+1, y, z + 1) : -1;
|
||||
|
||||
int x1y1z1 = VoxelExists(x + 1, y + 1, z + 1) ? GetVoxelIndex(x + 1, y + 1, z + 1) : -1;
|
||||
|
||||
// XY plane
|
||||
if (x1y0z0 >= 0 && x1y1z0 >= 0 && x0y1z0 >= 0 &&
|
||||
voxels[x1y0z0] == Voxel.Boundary &&
|
||||
voxels[x1y1z0] == Voxel.Boundary &&
|
||||
voxels[x0y1z0] == Voxel.Boundary)
|
||||
{
|
||||
if (x0y1z1 < 0 || voxels[x0y1z1] == Voxel.Outside ||
|
||||
x0y0z1 < 0 || voxels[x0y0z1] == Voxel.Outside ||
|
||||
x1y0z1 < 0 || voxels[x1y0z1] == Voxel.Outside ||
|
||||
x1y1z1 < 0 || voxels[x1y1z1] == Voxel.Outside)
|
||||
{
|
||||
tris.Add(vtxIndex[x0y0z0]);
|
||||
tris.Add(vtxIndex[x1y0z0]);
|
||||
tris.Add(vtxIndex[x0y1z0]);
|
||||
|
||||
tris.Add(vtxIndex[x0y1z0]);
|
||||
tris.Add(vtxIndex[x1y0z0]);
|
||||
tris.Add(vtxIndex[x1y1z0]);
|
||||
}
|
||||
else
|
||||
{
|
||||
tris.Add(vtxIndex[x1y0z0]);
|
||||
tris.Add(vtxIndex[x0y0z0]);
|
||||
tris.Add(vtxIndex[x0y1z0]);
|
||||
|
||||
tris.Add(vtxIndex[x1y0z0]);
|
||||
tris.Add(vtxIndex[x0y1z0]);
|
||||
tris.Add(vtxIndex[x1y1z0]);
|
||||
}
|
||||
}
|
||||
|
||||
// XZ plane
|
||||
if (x1y0z0 >= 0 && x1y0z1 >= 0 && x0y0z1 >= 0 &&
|
||||
voxels[x1y0z0] == Voxel.Boundary &&
|
||||
voxels[x1y0z1] == Voxel.Boundary &&
|
||||
voxels[x0y0z1] == Voxel.Boundary)
|
||||
{
|
||||
if (x0y1z0 < 0 || voxels[x0y1z0] == Voxel.Outside ||
|
||||
x0y1z1 < 0 || voxels[x0y1z1] == Voxel.Outside ||
|
||||
x1y1z0 < 0 || voxels[x1y1z0] == Voxel.Outside ||
|
||||
x1y1z1 < 0 || voxels[x1y1z1] == Voxel.Outside)
|
||||
{
|
||||
tris.Add(vtxIndex[x1y0z0]);
|
||||
tris.Add(vtxIndex[x0y0z0]);
|
||||
tris.Add(vtxIndex[x0y0z1]);
|
||||
|
||||
tris.Add(vtxIndex[x1y0z0]);
|
||||
tris.Add(vtxIndex[x0y0z1]);
|
||||
tris.Add(vtxIndex[x1y0z1]);
|
||||
}
|
||||
else
|
||||
{
|
||||
tris.Add(vtxIndex[x0y0z0]);
|
||||
tris.Add(vtxIndex[x1y0z0]);
|
||||
tris.Add(vtxIndex[x0y0z1]);
|
||||
|
||||
tris.Add(vtxIndex[x0y0z1]);
|
||||
tris.Add(vtxIndex[x1y0z0]);
|
||||
tris.Add(vtxIndex[x1y0z1]);
|
||||
}
|
||||
}
|
||||
|
||||
// XY plane
|
||||
if (x0y0z1 >= 0 && x0y1z1 >= 0 && x0y1z0 >= 0 &&
|
||||
voxels[x0y0z1] == Voxel.Boundary &&
|
||||
voxels[x0y1z1] == Voxel.Boundary &&
|
||||
voxels[x0y1z0] == Voxel.Boundary)
|
||||
{
|
||||
if (x1y0z0 < 0 || voxels[x1y0z0] == Voxel.Outside ||
|
||||
x1y0z1 < 0 || voxels[x1y0z1] == Voxel.Outside ||
|
||||
x1y1z0 < 0 || voxels[x1y1z0] == Voxel.Outside ||
|
||||
x1y1z1 < 0 || voxels[x1y1z1] == Voxel.Outside)
|
||||
{
|
||||
tris.Add(vtxIndex[x0y0z1]);
|
||||
tris.Add(vtxIndex[x0y0z0]);
|
||||
tris.Add(vtxIndex[x0y1z0]);
|
||||
|
||||
tris.Add(vtxIndex[x0y0z1]);
|
||||
tris.Add(vtxIndex[x0y1z0]);
|
||||
tris.Add(vtxIndex[x0y1z1]);
|
||||
}
|
||||
else
|
||||
{
|
||||
tris.Add(vtxIndex[x0y0z0]);
|
||||
tris.Add(vtxIndex[x0y0z1]);
|
||||
tris.Add(vtxIndex[x0y1z0]);
|
||||
|
||||
tris.Add(vtxIndex[x0y1z0]);
|
||||
tris.Add(vtxIndex[x0y0z1]);
|
||||
tris.Add(vtxIndex[x0y1z1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mesh.SetVertices(outputVertices);
|
||||
mesh.SetIndices(tris, MeshTopology.Triangles, 0);
|
||||
mesh.RecalculateNormals();
|
||||
}
|
||||
|
||||
private IEnumerator FloodFill()
|
||||
{
|
||||
Queue<Vector3Int> queue = new Queue<Vector3Int>();
|
||||
@@ -631,5 +424,6 @@ namespace Obi
|
||||
|
||||
return !(Mathf.Max(-maxP, minP) > r);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,4 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Collections;
|
||||
using System.Collections;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Obi
|
||||
@@ -10,7 +9,7 @@ namespace Obi
|
||||
*/
|
||||
public class VoxelDistanceField
|
||||
{
|
||||
public Vector3[,,] distanceField; // for each coordinate, stores coordinates of closest surface voxel.
|
||||
public Vector3Int[,,] distanceField; // for each coordinate, stores coordinates of closest surface voxel.
|
||||
|
||||
private MeshVoxelizer voxelizer;
|
||||
|
||||
@@ -19,159 +18,115 @@ namespace Obi
|
||||
this.voxelizer = voxelizer;
|
||||
}
|
||||
|
||||
public Vector4 SampleUnfiltered(int x, int y, int z)
|
||||
public float SampleUnfiltered(int x, int y, int z)
|
||||
{
|
||||
x = Mathf.Clamp(x, 0, voxelizer.resolution.x - 1);
|
||||
y = Mathf.Clamp(y, 0, voxelizer.resolution.y - 1);
|
||||
z = Mathf.Clamp(z, 0, voxelizer.resolution.z - 1);
|
||||
if (!voxelizer.VoxelExists(x, y, z)) return float.PositiveInfinity;
|
||||
|
||||
var grad = distanceField[x, y, z];
|
||||
float dist = grad.magnitude;
|
||||
grad.Normalize();
|
||||
float dist = Vector3.Distance(voxelizer.GetVoxelCenter(distanceField[x, y, z]),
|
||||
voxelizer.GetVoxelCenter(new Vector3Int(x, y, z)));
|
||||
|
||||
return new Vector4(grad.x, grad.y, grad.z, -dist);
|
||||
if (voxelizer[x, y, z] == MeshVoxelizer.Voxel.Inside)
|
||||
return -dist;
|
||||
return dist;
|
||||
}
|
||||
|
||||
public Vector4 SampleFiltered(float x, float y, float z, Vector3Int axisMask)
|
||||
public Vector4 SampleFiltered(float x, float y, float z)
|
||||
{
|
||||
var pos = new Vector3(x, y, z);
|
||||
|
||||
// clamp position inside the distance field:
|
||||
var min = voxelizer.GetVoxelCenter(new Vector3Int(0, 0, 0));
|
||||
var min = voxelizer.GetVoxelCenter(new Vector3Int(0, 0, 0));
|
||||
var max = voxelizer.GetVoxelCenter(new Vector3Int(voxelizer.resolution.x - 1, voxelizer.resolution.y - 1, voxelizer.resolution.z - 1));
|
||||
pos.x = Mathf.Clamp(pos.x, min.x, max.x);
|
||||
pos.y = Mathf.Clamp(pos.y, min.y, max.y);
|
||||
pos.z = Mathf.Clamp(pos.z, min.z, max.z);
|
||||
pos.x = Mathf.Clamp(pos.x, min.x, max.x - voxelizer.voxelSize * 0.05f);
|
||||
pos.y = Mathf.Clamp(pos.y, min.y, max.y - voxelizer.voxelSize * 0.05f);
|
||||
pos.z = Mathf.Clamp(pos.z, min.z, max.z - voxelizer.voxelSize * 0.05f);
|
||||
|
||||
var voxel = voxelizer.GetPointVoxel(pos - Vector3.one * voxelizer.voxelSize * 0.5f) - voxelizer.Origin;
|
||||
|
||||
var voxel = voxelizer.GetPointVoxel(pos - (Vector3)axisMask * voxelizer.voxelSize * 0.5f) - voxelizer.Origin;
|
||||
var voxelCenter = voxelizer.GetVoxelCenter(voxel);
|
||||
var norm = Vector3.Scale((pos - voxelCenter) / voxelizer.voxelSize, axisMask);
|
||||
var norm = (pos - voxelCenter) / voxelizer.voxelSize;
|
||||
|
||||
var xz00 = SampleUnfiltered(voxel.x, voxel.y, voxel.z);
|
||||
var xz01 = SampleUnfiltered(voxel.x, voxel.y, voxel.z + 1);
|
||||
var xz10 = SampleUnfiltered(voxel.x + 1, voxel.y, voxel.z);
|
||||
var xz11 = SampleUnfiltered(voxel.x + 1, voxel.y, voxel.z + 1);
|
||||
float xz00 = SampleUnfiltered(voxel.x, voxel.y, voxel.z);
|
||||
float xz01 = SampleUnfiltered(voxel.x, voxel.y, voxel.z + 1);
|
||||
float xz10 = SampleUnfiltered(voxel.x + 1, voxel.y, voxel.z);
|
||||
float xz11 = SampleUnfiltered(voxel.x + 1, voxel.y, voxel.z + 1);
|
||||
|
||||
var yz00 = SampleUnfiltered(voxel.x, voxel.y + 1, voxel.z);
|
||||
var yz01 = SampleUnfiltered(voxel.x, voxel.y + 1, voxel.z + 1);
|
||||
var yz10 = SampleUnfiltered(voxel.x + 1, voxel.y + 1, voxel.z);
|
||||
var yz11 = SampleUnfiltered(voxel.x + 1, voxel.y + 1, voxel.z + 1);
|
||||
float yz00 = SampleUnfiltered(voxel.x, voxel.y + 1, voxel.z);
|
||||
float yz01 = SampleUnfiltered(voxel.x, voxel.y + 1, voxel.z + 1);
|
||||
float yz10 = SampleUnfiltered(voxel.x + 1, voxel.y + 1, voxel.z);
|
||||
float yz11 = SampleUnfiltered(voxel.x + 1, voxel.y + 1, voxel.z + 1);
|
||||
|
||||
var X1 = Vector4.Lerp(xz00, xz10, norm.x);
|
||||
var X2 = Vector4.Lerp(xz01, xz11, norm.x);
|
||||
var X3 = Vector4.Lerp(yz00, yz10, norm.x);
|
||||
var X4 = Vector4.Lerp(yz01, yz11, norm.x);
|
||||
float X1 = Mathf.Lerp(xz00, xz10, norm.x);
|
||||
float X2 = Mathf.Lerp(xz01, xz11, norm.x);
|
||||
float X3 = Mathf.Lerp(yz00, yz10, norm.x);
|
||||
float X4 = Mathf.Lerp(yz01, yz11, norm.x);
|
||||
|
||||
var Y1 = Vector4.Lerp(X1, X2, norm.z);
|
||||
var Y2 = Vector4.Lerp(X3, X4, norm.z);
|
||||
float Y1 = Mathf.Lerp(X1, X2, norm.z);
|
||||
float Y2 = Mathf.Lerp(X3, X4, norm.z);
|
||||
|
||||
return Vector4.Lerp(Y1, Y2, norm.y);
|
||||
}
|
||||
float R = Mathf.Lerp(Mathf.Lerp(xz10, xz11, norm.z), Mathf.Lerp(yz10, yz11, norm.z), norm.y);
|
||||
float L = Mathf.Lerp(Mathf.Lerp(xz00, xz01, norm.z), Mathf.Lerp(yz00, yz01, norm.z), norm.y);
|
||||
|
||||
public void Smooth()
|
||||
{
|
||||
// create output buffer for ping-pong.
|
||||
Vector3[,,] smoothed = new Vector3[voxelizer.resolution.x,
|
||||
voxelizer.resolution.y,
|
||||
voxelizer.resolution.z];
|
||||
float F = Mathf.Lerp(X2, X4, norm.y);
|
||||
float B = Mathf.Lerp(X1, X3, norm.y);
|
||||
|
||||
for (int x = 0; x < distanceField.GetLength(0); ++x)
|
||||
for (int y = 0; y < distanceField.GetLength(1); ++y)
|
||||
for (int z = 0; z < distanceField.GetLength(2); ++z)
|
||||
{
|
||||
if (voxelizer[x, y, z] != MeshVoxelizer.Voxel.Outside)
|
||||
{
|
||||
var p = new Vector3Int(x, y, z);
|
||||
Vector3 df = distanceField[x, y, z];
|
||||
int count = 1;
|
||||
foreach (var o in MeshVoxelizer.faceNeighborhood)
|
||||
{
|
||||
// offset voxel to get neighbor:
|
||||
var n = p + o;
|
||||
if (voxelizer.VoxelExists(n.x, n.y, n.z) && voxelizer[n.x, n.y, n.z] != MeshVoxelizer.Voxel.Outside)
|
||||
{
|
||||
df += distanceField[n.x, n.y, n.z];
|
||||
count++;
|
||||
}
|
||||
}
|
||||
df /= count;
|
||||
smoothed[x, y, z] = df;
|
||||
}
|
||||
}
|
||||
|
||||
distanceField = smoothed;
|
||||
}
|
||||
|
||||
private void CalculateGradientsAndDistances(Vector3Int[,,] buffer1)
|
||||
{
|
||||
distanceField = new Vector3[voxelizer.resolution.x,
|
||||
voxelizer.resolution.y,
|
||||
voxelizer.resolution.z];
|
||||
|
||||
for (int x = 0; x < buffer1.GetLength(0); ++x)
|
||||
for (int y = 0; y < buffer1.GetLength(1); ++y)
|
||||
for (int z = 0; z < buffer1.GetLength(2); ++z)
|
||||
{
|
||||
if (voxelizer[x, y, z] != MeshVoxelizer.Voxel.Outside)
|
||||
{
|
||||
distanceField[x, y, z] = voxelizer.GetVoxelCenter(buffer1[x, y, z]) -
|
||||
voxelizer.GetVoxelCenter(new Vector3Int(x, y, z));
|
||||
}
|
||||
else
|
||||
distanceField[x, y, z] = Vector3.zero;
|
||||
}
|
||||
return new Vector4((R - L) / voxelizer.voxelSize,
|
||||
(Y2 - Y1) / voxelizer.voxelSize,
|
||||
(F - B) / voxelizer.voxelSize,
|
||||
Mathf.Lerp(Y1, Y2, norm.y));
|
||||
}
|
||||
|
||||
public IEnumerator JumpFlood()
|
||||
{
|
||||
// create two buffers for ping-ponging:
|
||||
Vector3Int[,,] buffer1 = new Vector3Int[voxelizer.resolution.x,
|
||||
voxelizer.resolution.y,
|
||||
voxelizer.resolution.z];
|
||||
|
||||
Vector3Int[,,] buffer2 = new Vector3Int[voxelizer.resolution.x,
|
||||
// create and initialize distance field:
|
||||
distanceField = new Vector3Int[voxelizer.resolution.x,
|
||||
voxelizer.resolution.y,
|
||||
voxelizer.resolution.z];
|
||||
|
||||
// create auxiliar buffer for ping-pong.
|
||||
Vector3Int[,,] auxBuffer = new Vector3Int[voxelizer.resolution.x,
|
||||
voxelizer.resolution.y,
|
||||
voxelizer.resolution.z];
|
||||
|
||||
// initialize distance field:
|
||||
for (int x = 0; x < buffer1.GetLength(0); ++x)
|
||||
for (int y = 0; y < buffer1.GetLength(1); ++y)
|
||||
for (int z = 0; z < buffer1.GetLength(2); ++z)
|
||||
for (int x = 0; x < distanceField.GetLength(0); ++x)
|
||||
for (int y = 0; y < distanceField.GetLength(1); ++y)
|
||||
for (int z = 0; z < distanceField.GetLength(2); ++z)
|
||||
{
|
||||
if (voxelizer[x, y, z] == MeshVoxelizer.Voxel.Outside)
|
||||
buffer1[x, y, z] = new Vector3Int(x, y, z);
|
||||
if (voxelizer[x, y, z] == MeshVoxelizer.Voxel.Boundary)
|
||||
distanceField[x, y, z] = new Vector3Int(x, y, z);
|
||||
else
|
||||
buffer1[x, y, z] = new Vector3Int(-1, -1, -1);
|
||||
distanceField[x, y, z] = new Vector3Int(-1, -1, -1);
|
||||
}
|
||||
|
||||
// calculate the maximum size of the buffer:
|
||||
int size = Mathf.Max(buffer1.GetLength(0),
|
||||
buffer1.GetLength(1),
|
||||
buffer1.GetLength(2));
|
||||
int size = Mathf.Max(distanceField.GetLength(0),
|
||||
distanceField.GetLength(1),
|
||||
distanceField.GetLength(2));
|
||||
int step = (int)(size / 2.0f);
|
||||
|
||||
yield return new CoroutineJob.ProgressInfo("Generating voxel distance field...", 0);
|
||||
yield return new CoroutineJob.ProgressInfo("Generating voxel distance field...",0);
|
||||
|
||||
float numPasses = (int)Mathf.Log(size, 2);
|
||||
float numPasses = (int) Mathf.Log(size, 2);
|
||||
int i = 0;
|
||||
|
||||
// jump flood passes:
|
||||
while (step >= 1)
|
||||
{
|
||||
JumpFloodPass(step, buffer1, buffer2);
|
||||
JumpFloodPass(step, distanceField, auxBuffer);
|
||||
|
||||
// halve step:
|
||||
step /= 2;
|
||||
|
||||
// swap buffers:
|
||||
Vector3Int[,,] temp = buffer1;
|
||||
buffer1 = buffer2;
|
||||
buffer2 = temp;
|
||||
Vector3Int[,,] temp = distanceField;
|
||||
distanceField = auxBuffer;
|
||||
auxBuffer = temp;
|
||||
|
||||
yield return new CoroutineJob.ProgressInfo("Generating voxel distance field...", ++i / numPasses);
|
||||
}
|
||||
|
||||
CalculateGradientsAndDistances(buffer1);
|
||||
}
|
||||
|
||||
private void JumpFloodPass(int stride, Vector3Int[,,] input, Vector3Int[,,] output)
|
||||
@@ -200,30 +155,34 @@ namespace Obi
|
||||
dist = (s - p).sqrMagnitude;
|
||||
|
||||
// for each neighbor voxel:
|
||||
foreach (var o in MeshVoxelizer.fullNeighborhood)
|
||||
{
|
||||
// offset voxel to get neighbor:
|
||||
var n = p + o * stride;
|
||||
|
||||
if (voxelizer.VoxelExists(n.x, n.y, n.z))
|
||||
{
|
||||
// neighbors' closest seed.
|
||||
Vector3Int nc = input[n.x, n.y, n.z];
|
||||
|
||||
if (nc.x >= 0)
|
||||
for (int nx = -1; nx <= 1; ++nx)
|
||||
for (int ny = -1; ny <= 1; ++ny)
|
||||
for (int nz = -1; nz <= 1; ++nz)
|
||||
{
|
||||
// distance to neighbor's closest seed:
|
||||
float newDist = (nc - p).sqrMagnitude;
|
||||
// neighbor's position:
|
||||
int px = x + nx * stride;
|
||||
int py = y + ny * stride;
|
||||
int pz = z + nz * stride;
|
||||
|
||||
// if the distance to the neighbor's closest seed is smaller than the distance to ours:
|
||||
if (newDist < dist)
|
||||
if (voxelizer.VoxelExists(px,py,pz))
|
||||
{
|
||||
output[x, y, z] = nc;
|
||||
dist = newDist;
|
||||
// neighbors' closest seed.
|
||||
Vector3Int n = input[px,py,pz];
|
||||
|
||||
if (n.x >= 0)
|
||||
{
|
||||
// distance to neighbor's closest seed:
|
||||
float newDist = (n - p).sqrMagnitude;
|
||||
|
||||
// if the distance to the neighbor's closest seed is smaller than the distance to ours:
|
||||
if (newDist < dist)
|
||||
{
|
||||
output[x, y, z] = n;
|
||||
dist = newDist;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user