Skip to content

Instantly share code, notes, and snippets.

@cabbibo
Created July 26, 2017 02:07
Show Gist options
  • Save cabbibo/6101abe1c30788c940913f25c4221180 to your computer and use it in GitHub Desktop.
Save cabbibo/6101abe1c30788c940913f25c4221180 to your computer and use it in GitHub Desktop.
using UnityEngine;
using System.Collections;
public class Cloak : MonoBehaviour {
//public HumanBuffer humanBuffer;
//
//public GameObject hand1;
//public GameObject hand2;
public float Radius;
public float StartTime;
public Color color;
public float gravity;
public float attractToHand;
public float repelFromHand;
// How the donut looks
public Shader shader;
public Shader debugShader;
// How the donut feels
public ComputeShader constraintPass;
public ComputeShader normalPass;
public ComputeShader forcePass;
public Texture2D normalMap;
public Cubemap cubeMap;
public float clothSize = 1;
public float startingHeight = 1;
private Vector3 v1;
private Vector3 v2;
private Vector3 bodyPos;
public ComputeBuffer _vertBuffer;
public ComputeBuffer _transformBuffer;
private ComputeBuffer _upLinkBuffer;
private ComputeBuffer _rightLinkBuffer;
private ComputeBuffer _diagonalDownLinkBuffer;
private ComputeBuffer _diagonalUpLinkBuffer;
public Material material;
private Material debugMaterial;
private const int threadX = 4;
private const int threadY = 4;
private const int threadZ = 4;
private const int strideX = 4;
private const int strideY = 4;
private const int strideZ = 4;
private int gridX { get { return threadX * strideX; } }
private int gridY { get { return threadY * strideY; } }
private int gridZ { get { return threadZ * strideZ; } }
private int vertexCount { get { return gridX * gridY * gridZ; } }
public int ribbonWidth = 64;
public int ribbonLength { get { return (int)Mathf.Floor( (float)vertexCount / ribbonWidth ); } }
private int _kernelforce;
private int _kernelconstraint;
private int _kernelnormal;
private float[] inValues;
private float[] transformValues;
private float oTime = 0;
struct Vert{
public Vector3 pos;
public Vector3 oPos;
public Vector3 ogPos;
public Vector3 norm;
public Vector2 uv;
public float mass;
public float[] ids;
public Vector3 debug;
};
struct Link{
public float id1;
public float id2;
public float distance;
public float stiffness;
}
private int VertStructSize = 3 + 3 + 3 + 3 + 2 + 1 + 8 + 3;
private int LinkStructSize = 1 + 1 + 1 + 1;
// private float oTime;
// Use this for initialization
void Start () {
//HumanBuffer = GameObject.Find("HumanBuffer");
//EventManager.OnTriggerDown += OnTriggerDown;
oTime = Time.time;
transformValues = new float[ 16 ];
createBuffers();
createMaterial();
_kernelforce = forcePass.FindKernel("CSMain");
_kernelnormal = normalPass.FindKernel("CSMain");
_kernelconstraint = constraintPass.FindKernel("CSMain");
forcePass.SetInt( "_Reset" , 0 );
forcePass.SetInt( "_Ended" , 0 );
// Dispatch();
OnTriggerDown(gameObject);
Camera.onPostRender += Render;
}
public void OnTriggerDown( GameObject g ){
forcePass.SetInt( "_Reset" , 1 );
Dispatch();
//Dispatch();
//Dispatch();
forcePass.SetInt( "_Reset" , 0);
}
public void FixedUpdate(){
Dispatch();
}
//When this GameObject is disabled we must release the buffers or else Unity complains.
private void OnDisable(){
Camera.onPostRender -= Render;
ReleaseBuffer();
}
//For some reason I made this method to create a material from the attached shader.
private void createMaterial(){
material = new Material( shader );
debugMaterial = new Material( debugShader );
}
//Remember to release buffers and destroy the material when play has been stopped.
void ReleaseBuffer(){
_vertBuffer.Release();
_transformBuffer.Release();
_upLinkBuffer.Release();
_rightLinkBuffer.Release();
_diagonalUpLinkBuffer.Release();
_diagonalDownLinkBuffer.Release();
DestroyImmediate( material );
DestroyImmediate( debugMaterial );
}
//After all rendering is complete we dispatch the compute shader and then set the material before drawing with DrawProcedural
//this just draws the "mesh" as a set of points
public void Render(Camera camera) {
int numVertsTotal = (ribbonWidth) * 3 * 2 * (ribbonLength-1);
material.SetPass(0);
material.SetInt("_Large" , 0 );
material.SetBuffer("buf_Points", _vertBuffer);
material.SetColor("_Color", color);
//material.SetBuffer("shapeBuffer", PF.pillows._shapeBuffer);
//
//material.SetInt( "_NumberHands" , PF.handBufferInfo.GetComponent<HandBuffer>().numberHands );
//material.SetBuffer( "handBuffer" , PF.handBufferInfo.GetComponent<HandBuffer>()._handBuffer );
//material.SetFloat( "_FullEnd" , PF.fullEnd );
//material.SetFloat("_ClothDown" , PF.clothDown );
material.SetInt( "_RibbonWidth" , ribbonWidth );
material.SetInt( "_RibbonLength" , ribbonLength );
material.SetInt( "_TotalVerts" , vertexCount );
//material.SetInt( "_NumShapes" , PF.pillows.Shapes.Length );
//material.SetFloat("_Height" , PF.pillows.tallestPoint );
//material.SetFloat("_Width" , PF.pillows.widestPoint );
//material.SetFloat("_Special" , PF.special );
material.SetTexture( "_NormalMap" , normalMap);
material.SetTexture( "_CubeMap" , cubeMap );
material.SetMatrix("worldMat", transform.localToWorldMatrix);
material.SetMatrix("invWorldMat", transform.worldToLocalMatrix);
Graphics.DrawProcedural(MeshTopology.Triangles, numVertsTotal);
}
private Vector3 getVertPosition( float col , float row ){
float uvX = col / ribbonWidth;
float uvY = row / ribbonLength;
float angle = uvX * 2 * Mathf.PI;
float radius = (Mathf.Pow( uvY , .5f )+.2f) * Radius;
float x = Mathf.Cos( angle ) * radius;
float y = Mathf.Sin( angle ) * radius;
return new Vector3( x * clothSize , 0 , y * clothSize );
}
private void createBuffers() {
_transformBuffer = new ComputeBuffer( 1 , 16* sizeof(float));
_vertBuffer = new ComputeBuffer( vertexCount , VertStructSize * sizeof(float));
_upLinkBuffer = new ComputeBuffer( vertexCount / 2 , LinkStructSize * sizeof(float));
_rightLinkBuffer = new ComputeBuffer( vertexCount / 2 , LinkStructSize * sizeof(float));
_diagonalDownLinkBuffer = new ComputeBuffer( vertexCount / 2 , LinkStructSize * sizeof(float));
_diagonalUpLinkBuffer = new ComputeBuffer( vertexCount / 2 , LinkStructSize * sizeof(float));
float lRight = clothSize / (float)ribbonWidth;
float lUp = clothSize / (float)ribbonLength;
Vector2 n = new Vector2( lRight , lUp );
float lDia = n.magnitude;
inValues = new float[ VertStructSize * vertexCount];
float[] upLinkValues = new float[ LinkStructSize * vertexCount / 2 ];
float[] rightLinkValues = new float[ LinkStructSize * vertexCount / 2 ];
float[] diagonalDownLinkValues = new float[ LinkStructSize * vertexCount / 2 ];
float[] diagonalUpLinkValues = new float[ LinkStructSize * vertexCount / 2 ];
// Used for assigning to our buffer;
int index = 0;
int indexOG = 0;
int li1= 0;
int li2= 0;
int li3= 0;
int li4= 0;
/* // second rite up here
u dU x . r
. . // third rite down here
x . r x . r
.
dD
*/
for (int z = 0; z < gridZ; z++) {
for (int y = 0; y < gridY; y++) {
for (int x = 0; x < gridX; x++) {
int id = x + y * gridX + z * gridX * gridY;
float col = (float)(id % ribbonWidth );
float row = Mathf.Floor( ((float)id +0.01f) / ribbonWidth);
float uvX = col / ribbonWidth;
float uvY = row / ribbonLength;
float stiffness = uvY;
Vector3 fVec = getVertPosition( col , row );
if( row % 2 == 0 ){
Vector3 pos1; Vector3 pos2;
pos1 = fVec;
pos2 = getVertPosition( col + 0 , row +1 );
stiffness = (row + .5f ) / ribbonLength;
upLinkValues[li1++] = id;
upLinkValues[li1++] = convertToID( col + 0 , row + 1 );
upLinkValues[li1++] = ( pos1 - pos2 ).magnitude;
upLinkValues[li1++] = stiffness;
// Because of the way the right links
// are made, we need to alternate them,
// and flip flop them back and forth
// so they are not writing to the same
// positions during the same path!
float id1 , id2;
float fDist;
if( col % 2 == 0 ){
id1 = id;
id2 = convertToID( col + 1 , row + 0 );
pos1 = fVec;
pos2 = getVertPosition( col + 1 , row +0 );
stiffness = (row + 0 ) / ribbonLength;
}else{
id1 = convertToID( col + 0 , row + 1 );
id2 = convertToID( col + 1 , row + 1 );
pos1 = getVertPosition( col + 0 , row +1 );
pos2 = getVertPosition( col + 1 , row +1 );
stiffness = (row+1 ) / ribbonLength;
}
rightLinkValues[li2++] = id1;
rightLinkValues[li2++] = id2;
rightLinkValues[li2++] = ( pos1 - pos2 ).magnitude;
rightLinkValues[li2++] = stiffness;
pos1 = fVec;
pos2 = getVertPosition( col -1 , row -1 );
stiffness = (row-.5f) / ribbonLength;
diagonalDownLinkValues[li3++] = id;
diagonalDownLinkValues[li3++] = convertToID( col - 1 , row - 1 );
diagonalDownLinkValues[li3++] = ( pos1 - pos2 ).magnitude;
diagonalDownLinkValues[li3++] = stiffness;
pos1 = fVec;
pos2 = getVertPosition( col +1 , row +1 );
stiffness = (row+.5f) / ribbonLength;
diagonalUpLinkValues[li4++] = id;
diagonalUpLinkValues[li4++] = convertToID( col + 1 , row + 1 );
diagonalUpLinkValues[li4++] = ( pos1 - pos2 ).magnitude;
diagonalUpLinkValues[li4++] = stiffness;
}
Vert vert = new Vert();
float stuck = 0;
if( fVec.magnitude < .2f ){
stuck = 1;
fVec += new Vector3( 0, .2f - fVec.magnitude , 0 );
fVec = fVec.normalized * .2f;
//print( fVec );
}
vert.pos = fVec * 1.000001f;
vert.oPos = fVec- new Vector3( 0 , 0 , 0 );
vert.ogPos = fVec ;
vert.norm = new Vector3( 0 , 1 , 0 );
vert.uv = new Vector2( uvX , uvY );
vert.mass = 0.3f;
if( col == 0 || col == ribbonWidth || row == 0 || row == ribbonLength ){
vert.mass = 2.0f;
}
vert.ids = new float[8];
vert.ids[0] = convertToID( col + 1 , row + 0 );
vert.ids[1] = convertToID( col + 1 , row - 1 );
vert.ids[2] = convertToID( col + 0 , row - 1 );
vert.ids[3] = convertToID( col - 1 , row - 1 );
vert.ids[4] = convertToID( col - 1 , row - 0 );
vert.ids[5] = convertToID( col - 1 , row + 1 );
vert.ids[6] = convertToID( col - 0 , row + 1 );
vert.ids[7] = convertToID( col + 1 , row + 1 );
vert.debug = new Vector3(stuck,1,0);
//Vector2 d = new Vector2(vert.uv.x-.5f,vert.uv.y-.5f);
//
//if(d.magnitude < .02 ){
// vert.debug= new Vector3(1,1,0);
//}
inValues[index++] = vert.pos.x;
inValues[index++] = vert.pos.y;
inValues[index++] = vert.pos.z;
inValues[index++] = vert.oPos.x;
inValues[index++] = vert.oPos.y;
inValues[index++] = vert.oPos.z;
inValues[index++] = vert.ogPos.x;
inValues[index++] = vert.ogPos.y;
inValues[index++] = vert.ogPos.z;
inValues[index++] = vert.norm.x;
inValues[index++] = vert.norm.y;
inValues[index++] = vert.norm.z;
inValues[index++] = vert.uv.x;
inValues[index++] = vert.uv.y;
inValues[index++] = 0;
inValues[index++] = vert.ids[0];
inValues[index++] = vert.ids[1];
inValues[index++] = vert.ids[2];
inValues[index++] = vert.ids[3];
inValues[index++] = vert.ids[4];
inValues[index++] = vert.ids[5];
inValues[index++] = vert.ids[6];
inValues[index++] = vert.ids[7];
inValues[index++] = vert.debug.x;
inValues[index++] = vert.debug.y;
inValues[index++] = vert.debug.z;
}
}
}
_vertBuffer.SetData(inValues);
_upLinkBuffer.SetData(upLinkValues);
_rightLinkBuffer.SetData(rightLinkValues);
_diagonalUpLinkBuffer.SetData(diagonalUpLinkValues);
_diagonalDownLinkBuffer.SetData(diagonalDownLinkValues);
}
public float convertToID( float col , float row ){
float id;
if( col >= ribbonWidth ){ col -= ribbonWidth;}
if( col < 0 ){ col += ribbonWidth; }
// Instead of returning negative numbers,
// we are going to connect to other side of cloth
// to make it a tube!
if( row >= ribbonLength ){ return -10; }
if( row < 0 ){ return -20; }
id = row * ribbonWidth + col;
return id;
}
private void doConstraint( float v , int offset , ComputeBuffer b ){
// Which link in compute are we doing
constraintPass.SetInt("_Offset" , offset );
constraintPass.SetFloat("_Multiplier" , v );
constraintPass.SetBuffer( _kernelconstraint , "linkBuffer" , b );
//TODO: only need to dispatch for 1/9th of the buffer size!
constraintPass.Dispatch( _kernelconstraint , strideX / 2 , strideY , strideZ );
}
private void assignTransform(){
Matrix4x4 m = transform.localToWorldMatrix;
float[] matrixFloats = new float[]
{
m[0,0], m[1, 0], m[2, 0], m[3, 0],
m[0,1], m[1, 1], m[2, 1], m[3, 1],
m[0,2], m[1, 2], m[2, 2], m[3, 2],
m[0,3], m[1, 3], m[2, 3], m[3, 3]
};
forcePass.SetFloats("transform", matrixFloats);
}
private void Dispatch() {
v1 = transform.TransformPoint(new Vector3( 0 , -.1f , -.2f ));
v2 = new Vector3( 0 , v1.y * .15f , 0 );
v1 = v1 - v2;
bodyPos = v1;//transform.TransformPoint( v1 );
assignTransform();
forcePass.SetFloat( "_DeltaTime" , Time.time - oTime );
forcePass.SetFloat( "_Time" , Time.time + StartTime );
//forcePass.SetFloat( "_Reset" , );
//forcePass.SetVector( "_ChestPosition" , bodyPos );
//forcePass.SetVector( "_Hand1" , hand1.transform.position );
//forcePass.SetVector( "_Hand2" , hand2.transform.position );
forcePass.SetFloat("_AttractToHand", attractToHand);
forcePass.SetFloat("_RepelFromHand", repelFromHand);
forcePass.SetFloat("_Gravity", gravity);
///oTime = Time.time;
forcePass.SetInt( "_RibbonWidth" , ribbonWidth );
forcePass.SetInt( "_RibbonLength" , ribbonLength );
// print( PF.pillows);
//forcePass.SetInt( "_NumShapes" , PF.pillows.Shapes.Length );
//forcePass.SetInt( "_NumberHands" , PF.handBufferInfo.GetComponent<HandBuffer>().numberHands );
//print( HumanBuffer.GetComponent<HumanBuffer>().numberHumans );
//forcePass.SetInt( "_NumberHumans" , humanBuffer.numberHumans );
//forcePass.SetBuffer( _kernelforce , "humanBuffer" , humanBuffer._buffer );
forcePass.SetBuffer( _kernelforce , "vertBuffer" , _vertBuffer );
forcePass.SetBuffer( _kernelforce , "transformBuffer" , _transformBuffer );
//if( PF.pillows._shapeBuffer != null ){
// forcePass.SetBuffer( _kernelforce , "shapeBuffer" , PF.pillows._shapeBuffer );
//}
//forcePass.SetBuffer( _kernelforce , "handBuffer" , PF.handBufferInfo.GetComponent<HandBuffer>()._handBuffer );
forcePass.Dispatch( _kernelforce , strideX , strideY , strideZ );
constraintPass.SetInt( "_RibbonWidth" , ribbonWidth );
constraintPass.SetInt( "_RibbonLength" , ribbonLength );
constraintPass.SetBuffer( _kernelconstraint , "vertBuffer" , _vertBuffer );
doConstraint( 1 , 1 , _upLinkBuffer );
doConstraint( 1 , 1 , _rightLinkBuffer );
doConstraint( 1 , 1 , _diagonalDownLinkBuffer );
doConstraint( 1 , 1 , _diagonalUpLinkBuffer );
doConstraint( 1 , 0 , _upLinkBuffer );
doConstraint( 1 , 0 , _rightLinkBuffer );
doConstraint( 1 , 0 , _diagonalDownLinkBuffer );
doConstraint( 1 , 0 , _diagonalUpLinkBuffer );
//calculate our normals
normalPass.SetBuffer( _kernelnormal , "vertBuffer" , _vertBuffer );
normalPass.Dispatch( _kernelnormal , strideX , strideY , strideZ );
oTime = Time.time;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment