[
  {
    "path": ".gitignore",
    "content": "# Created by https://www.gitignore.io/api/unity\n\n### Unity ###\n/[Ll]ibrary/\n/[Tt]emp/\n/[Oo]bj/\n/[Bb]uild/\n/[Bb]uilds/\n/Assets/AssetStoreTools*\n\n# Autogenerated VS/MD/Consulo solution and project files\nExportedObj/\n.consulo/\n*.csproj\n*.unityproj\n*.sln\n*.suo\n*.tmp\n*.user\n*.userprefs\n*.pidb\n*.booproj\n*.svd\n\n\n# Unity3D generated meta files\n*.pidb.meta\n\n# Unity3D Generated File On Crash Reports\nsysinfo.txt\n\n# Builds\n*.apk\n*.unitypackage\n\n"
  },
  {
    "path": "Assets/CableComponent/Material/CableMaterial.mat",
    "content": "%YAML 1.1\n%TAG !u! tag:unity3d.com,2011:\n--- !u!21 &2100000\nMaterial:\n  serializedVersion: 6\n  m_ObjectHideFlags: 0\n  m_PrefabParentObject: {fileID: 0}\n  m_PrefabInternal: {fileID: 0}\n  m_Name: CableMaterial\n  m_Shader: {fileID: 46, guid: 0000000000000000f000000000000000, type: 0}\n  m_ShaderKeywords: _EMISSION\n  m_LightmapFlags: 1\n  m_CustomRenderQueue: -1\n  stringTagMap: {}\n  m_SavedProperties:\n    serializedVersion: 2\n    m_TexEnvs:\n    - first:\n        name: _BumpMap\n      second:\n        m_Texture: {fileID: 0}\n        m_Scale: {x: 1, y: 1}\n        m_Offset: {x: 0, y: 0}\n    - first:\n        name: _DetailAlbedoMap\n      second:\n        m_Texture: {fileID: 0}\n        m_Scale: {x: 1, y: 1}\n        m_Offset: {x: 0, y: 0}\n    - first:\n        name: _DetailMask\n      second:\n        m_Texture: {fileID: 0}\n        m_Scale: {x: 1, y: 1}\n        m_Offset: {x: 0, y: 0}\n    - first:\n        name: _DetailNormalMap\n      second:\n        m_Texture: {fileID: 0}\n        m_Scale: {x: 1, y: 1}\n        m_Offset: {x: 0, y: 0}\n    - first:\n        name: _EmissionMap\n      second:\n        m_Texture: {fileID: 0}\n        m_Scale: {x: 1, y: 1}\n        m_Offset: {x: 0, y: 0}\n    - first:\n        name: _MainTex\n      second:\n        m_Texture: {fileID: 0}\n        m_Scale: {x: 1, y: 1}\n        m_Offset: {x: 0, y: 0}\n    - first:\n        name: _MetallicGlossMap\n      second:\n        m_Texture: {fileID: 0}\n        m_Scale: {x: 1, y: 1}\n        m_Offset: {x: 0, y: 0}\n    - first:\n        name: _OcclusionMap\n      second:\n        m_Texture: {fileID: 0}\n        m_Scale: {x: 1, y: 1}\n        m_Offset: {x: 0, y: 0}\n    - first:\n        name: _ParallaxMap\n      second:\n        m_Texture: {fileID: 0}\n        m_Scale: {x: 1, y: 1}\n        m_Offset: {x: 0, y: 0}\n    m_Floats:\n    - first:\n        name: _BumpScale\n      second: 1\n    - first:\n        name: _Cutoff\n      second: 0.5\n    - first:\n        name: _DetailNormalMapScale\n      second: 1\n    - first:\n        name: _DstBlend\n      second: 0\n    - first:\n        name: _GlossMapScale\n      second: 1\n    - first:\n        name: _Glossiness\n      second: 0\n    - first:\n        name: _GlossyReflections\n      second: 1\n    - first:\n        name: _Metallic\n      second: 0\n    - first:\n        name: _Mode\n      second: 0\n    - first:\n        name: _OcclusionStrength\n      second: 1\n    - first:\n        name: _Parallax\n      second: 0.02\n    - first:\n        name: _SmoothnessTextureChannel\n      second: 0\n    - first:\n        name: _SpecularHighlights\n      second: 1\n    - first:\n        name: _SrcBlend\n      second: 1\n    - first:\n        name: _UVSec\n      second: 0\n    - first:\n        name: _ZWrite\n      second: 1\n    m_Colors:\n    - first:\n        name: _Color\n      second: {r: 0.20588237, g: 0.20588237, b: 0.20588237, a: 1}\n    - first:\n        name: _EmissionColor\n      second: {r: 0, g: 0, b: 0, a: 1}\n"
  },
  {
    "path": "Assets/CableComponent/Material/CableMaterial.mat.meta",
    "content": "fileFormatVersion: 2\nguid: e20a2186cc0884c4186cd848b087703e\ntimeCreated: 1444228819\nlicenseType: Free\nNativeFormatImporter:\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "Assets/CableComponent/Material/FloorMaterial.mat",
    "content": "%YAML 1.1\n%TAG !u! tag:unity3d.com,2011:\n--- !u!21 &2100000\nMaterial:\n  serializedVersion: 6\n  m_ObjectHideFlags: 0\n  m_PrefabParentObject: {fileID: 0}\n  m_PrefabInternal: {fileID: 0}\n  m_Name: FloorMaterial\n  m_Shader: {fileID: 46, guid: 0000000000000000f000000000000000, type: 0}\n  m_ShaderKeywords: _EMISSION\n  m_LightmapFlags: 1\n  m_CustomRenderQueue: -1\n  stringTagMap: {}\n  m_SavedProperties:\n    serializedVersion: 2\n    m_TexEnvs:\n    - first:\n        name: _BumpMap\n      second:\n        m_Texture: {fileID: 0}\n        m_Scale: {x: 1, y: 1}\n        m_Offset: {x: 0, y: 0}\n    - first:\n        name: _DetailAlbedoMap\n      second:\n        m_Texture: {fileID: 0}\n        m_Scale: {x: 1, y: 1}\n        m_Offset: {x: 0, y: 0}\n    - first:\n        name: _DetailMask\n      second:\n        m_Texture: {fileID: 0}\n        m_Scale: {x: 1, y: 1}\n        m_Offset: {x: 0, y: 0}\n    - first:\n        name: _DetailNormalMap\n      second:\n        m_Texture: {fileID: 0}\n        m_Scale: {x: 1, y: 1}\n        m_Offset: {x: 0, y: 0}\n    - first:\n        name: _EmissionMap\n      second:\n        m_Texture: {fileID: 0}\n        m_Scale: {x: 1, y: 1}\n        m_Offset: {x: 0, y: 0}\n    - first:\n        name: _MainTex\n      second:\n        m_Texture: {fileID: 0}\n        m_Scale: {x: 1, y: 1}\n        m_Offset: {x: 0, y: 0}\n    - first:\n        name: _MetallicGlossMap\n      second:\n        m_Texture: {fileID: 0}\n        m_Scale: {x: 1, y: 1}\n        m_Offset: {x: 0, y: 0}\n    - first:\n        name: _OcclusionMap\n      second:\n        m_Texture: {fileID: 0}\n        m_Scale: {x: 1, y: 1}\n        m_Offset: {x: 0, y: 0}\n    - first:\n        name: _ParallaxMap\n      second:\n        m_Texture: {fileID: 0}\n        m_Scale: {x: 1, y: 1}\n        m_Offset: {x: 0, y: 0}\n    m_Floats:\n    - first:\n        name: _BumpScale\n      second: 1\n    - first:\n        name: _Cutoff\n      second: 0.5\n    - first:\n        name: _DetailNormalMapScale\n      second: 1\n    - first:\n        name: _DstBlend\n      second: 0\n    - first:\n        name: _GlossMapScale\n      second: 1\n    - first:\n        name: _Glossiness\n      second: 0\n    - first:\n        name: _GlossyReflections\n      second: 1\n    - first:\n        name: _Metallic\n      second: 0\n    - first:\n        name: _Mode\n      second: 0\n    - first:\n        name: _OcclusionStrength\n      second: 1\n    - first:\n        name: _Parallax\n      second: 0.02\n    - first:\n        name: _SmoothnessTextureChannel\n      second: 0\n    - first:\n        name: _SpecularHighlights\n      second: 1\n    - first:\n        name: _SrcBlend\n      second: 1\n    - first:\n        name: _UVSec\n      second: 0\n    - first:\n        name: _ZWrite\n      second: 1\n    m_Colors:\n    - first:\n        name: _Color\n      second: {r: 0.29411763, g: 0.29411763, b: 0.29411763, a: 1}\n    - first:\n        name: _EmissionColor\n      second: {r: 0, g: 0, b: 0, a: 1}\n"
  },
  {
    "path": "Assets/CableComponent/Material/FloorMaterial.mat.meta",
    "content": "fileFormatVersion: 2\nguid: 7a2ff32d7b3cb31498d778f8a3b29e1a\ntimeCreated: 1478455142\nlicenseType: Free\nNativeFormatImporter:\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "Assets/CableComponent/Material.meta",
    "content": "fileFormatVersion: 2\nguid: 6d5cbb863b6bf034d94a7b4b35dcd644\nfolderAsset: yes\ntimeCreated: 1444228621\nlicenseType: Free\nDefaultImporter:\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "Assets/CableComponent/README.md",
    "content": "# UnityCableComponent\n\"Cable Component\" for Unity3D based on verlet integration just like in UE4.\n"
  },
  {
    "path": "Assets/CableComponent/README.md.meta",
    "content": "fileFormatVersion: 2\nguid: 6b0a6c9ca9397e741af71e18838577a3\ntimeCreated: 1444252217\nlicenseType: Free\nDefaultImporter:\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "Assets/CableComponent/Scenes/TestScene.unity.meta",
    "content": "fileFormatVersion: 2\nguid: d01135766194e404088cea9fed636792\ntimeCreated: 1444229211\nlicenseType: Free\nDefaultImporter:\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "Assets/CableComponent/Scenes.meta",
    "content": "fileFormatVersion: 2\nguid: 5dd7385a29188264eb039571608a43e3\nfolderAsset: yes\ntimeCreated: 1444228613\nlicenseType: Free\nDefaultImporter:\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "Assets/CableComponent/Scripts/CableComponent.cs",
    "content": "using UnityEngine;\nusing System;\nusing System.Collections;\n\n\npublic class CableComponent : MonoBehaviour\n{\n\t#region Class members\n\n\t[SerializeField] private Transform endPoint;\n\t[SerializeField] private Material cableMaterial;\n\n\t// Cable config\n\t[SerializeField] private float cableLength = 0.5f;\n\t[SerializeField] private int totalSegments = 5;\n\t[SerializeField] private float segmentsPerUnit = 2f;\n\tprivate int segments = 0;\n\t[SerializeField] private float cableWidth = 0.1f;\n\n\t// Solver config\n\t[SerializeField] private int verletIterations = 1;\n\t[SerializeField] private int solverIterations = 1;\n\n\t//[Range(0,3)]\n\t[SerializeField] private float stiffness = 1f;\n\n\tprivate LineRenderer line;\n\tprivate CableParticle[] points;\n\n\t#endregion\n\n\n\t#region Initial setup\n\n\tvoid Start()\n\t{\n\t\tInitCableParticles();\n\t\tInitLineRenderer();\n\t}\n\n\t/**\n\t * Init cable particles\n\t * \n\t * Creates the cable particles along the cable length\n\t * and binds the start and end tips to their respective game objects.\n\t */\n\tvoid InitCableParticles()\n\t{\n\t\t// Calculate segments to use\n\t\tif (totalSegments > 0)\n\t\t\tsegments = totalSegments;\n\t\telse\n\t\t\tsegments = Mathf.CeilToInt (cableLength * segmentsPerUnit);\n\n\t\tVector3 cableDirection = (endPoint.position - transform.position).normalized;\n\t\tfloat initialSegmentLength = cableLength / segments;\n\t\tpoints = new CableParticle[segments + 1];\n\n\t\t// Foreach point\n\t\tfor (int pointIdx = 0; pointIdx <= segments; pointIdx++) {\n\t\t\t// Initial position\n\t\t\tVector3 initialPosition = transform.position + (cableDirection * (initialSegmentLength * pointIdx));\n\t\t\tpoints[pointIdx] = new CableParticle(initialPosition);\n\t\t}\n\n\t\t// Bind start and end particles with their respective gameobjects\n\t\tCableParticle start = points[0];\n\t\tCableParticle end = points[segments];\n\t\tstart.Bind(this.transform);\n\t\tend.Bind(endPoint.transform);\n\t}\n\n\t/**\n\t * Initialized the line renderer\n\t */\n\tvoid InitLineRenderer()\n\t{\n\t\tline = this.gameObject.AddComponent<LineRenderer>();\n\t\tline.SetWidth(cableWidth, cableWidth);\n\t\tline.SetVertexCount(segments + 1);\n\t\tline.material = cableMaterial;\n\t\tline.GetComponent<Renderer>().enabled = true;\n\t}\n\n\t#endregion\n\n\n\t#region Render Pass\n\n\tvoid Update()\n\t{\n\t\tRenderCable();\n\t}\n\n\t/**\n\t * Render Cable\n\t * \n\t * Update every particle position in the line renderer.\n\t */\n\tvoid RenderCable()\n\t{\n\t\tfor (int pointIdx = 0; pointIdx < segments + 1; pointIdx++) \n\t\t{\n\t\t\tline.SetPosition(pointIdx, points [pointIdx].Position);\n\t\t}\n\t}\n\n\t#endregion\n\n\n\t#region Verlet integration & solver pass\n\n\tvoid FixedUpdate()\n\t{\n\t\tfor (int verletIdx = 0; verletIdx < verletIterations; verletIdx++) \n\t\t{\n\t\t\tVerletIntegrate();\n\t\t\tSolveConstraints();\n\t\t}\n\t}\n\n\t/**\n\t * Verler integration pass\n\t * \n\t * In this step every particle updates its position and speed.\n\t */\n\tvoid VerletIntegrate()\n\t{\n\t\tVector3 gravityDisplacement = Time.fixedDeltaTime * Time.fixedDeltaTime * Physics.gravity;\n\t\tforeach (CableParticle particle in points) \n\t\t{\n\t\t\tparticle.UpdateVerlet(gravityDisplacement);\n\t\t}\n\t}\n\n\t/**\n\t * Constrains solver pass\n\t * \n\t * In this step every constraint is addressed in sequence\n\t */\n\tvoid SolveConstraints()\n\t{\n\t\t// For each solver iteration..\n\t\tfor (int iterationIdx = 0; iterationIdx < solverIterations; iterationIdx++) \n\t\t{\n\t\t\tSolveDistanceConstraint();\n\t\t\tSolveStiffnessConstraint();\n\t\t}\n\t}\n\n\t#endregion\n\n\n\t#region Solver Constraints\n\n\t/**\n\t * Distance constraint for each segment / pair of particles\n\t **/\n\tvoid SolveDistanceConstraint()\n\t{\n\t\tfloat segmentLength = cableLength / segments;\n\t\tfor (int SegIdx = 0; SegIdx < segments; SegIdx++) \n\t\t{\n\t\t\tCableParticle particleA = points[SegIdx];\n\t\t\tCableParticle particleB = points[SegIdx + 1];\n\n\t\t\t// Solve for this pair of particles\n\t\t\tSolveDistanceConstraint(particleA, particleB, segmentLength);\n\t\t}\n\t}\n\t\t\n\t/**\n\t * Distance Constraint \n\t * \n\t * This is the main constrains that keeps the cable particles \"tied\" together.\n\t */\n\tvoid SolveDistanceConstraint(CableParticle particleA, CableParticle particleB, float segmentLength)\n\t{\n\t\t// Find current vector between particles\n\t\tVector3 delta = particleB.Position - particleA.Position;\n\t\t// \n\t\tfloat currentDistance = delta.magnitude;\n\t\tfloat errorFactor = (currentDistance - segmentLength) / currentDistance;\n\t\t\n\t\t// Only move free particles to satisfy constraints\n\t\tif (particleA.IsFree() && particleB.IsFree()) \n\t\t{\n\t\t\tparticleA.Position += errorFactor * 0.5f * delta;\n\t\t\tparticleB.Position -= errorFactor * 0.5f * delta;\n\t\t} \n\t\telse if (particleA.IsFree()) \n\t\t{\n\t\t\tparticleA.Position += errorFactor * delta;\n\t\t} \n\t\telse if (particleB.IsFree()) \n\t\t{\n\t\t\tparticleB.Position -= errorFactor * delta;\n\t\t}\n\t}\n\n\t/**\n\t * Stiffness constraint\n\t **/\n\tvoid SolveStiffnessConstraint()\n\t{\n\t\tfloat distance = (points[0].Position - points[segments].Position).magnitude;\n\t\tif (distance > cableLength) \n\t\t{\n\t\t\tforeach (CableParticle particle in points) \n\t\t\t{\n\t\t\t\tSolveStiffnessConstraint(particle, distance);\n\t\t\t}\n\t\t}\t\n\t}\n\n\t/**\n\t * TODO: I'll implement this constraint to reinforce cable stiffness \n\t * \n\t * As the system has more particles, the verlet integration aproach \n\t * may get way too loose cable simulation. This constraint is intended \n\t * to reinforce the cable stiffness.\n\t * // throw new System.NotImplementedException ();\n\t **/\n\tvoid SolveStiffnessConstraint(CableParticle cableParticle, float distance)\n\t{\n\t\n\n\t}\n\n\t#endregion\n}\n"
  },
  {
    "path": "Assets/CableComponent/Scripts/CableComponent.cs.meta",
    "content": "fileFormatVersion: 2\nguid: a262c1cd876da934d86e2a8ee0089db2\ntimeCreated: 1444228668\nlicenseType: Free\nMonoImporter:\n  serializedVersion: 2\n  defaultReferences: []\n  executionOrder: 0\n  icon: {instanceID: 0}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "Assets/CableComponent/Scripts/CableParticle.cs",
    "content": "﻿using UnityEngine;\nusing System.Collections;\n\npublic class CableParticle\n{\n\t#region Class member variables\n\n\tprivate Vector3 _position, _oldPosition;\n\tprivate Transform _boundTo = null;\n\tprivate Rigidbody _boundRigid = null;\n\n\t#endregion\n\n\n\t#region Properties\n\n\tpublic Vector3 Position {\n\t\tget { return _position; }\n\t\tset { _position = value; }\n\t}\n\t\t\n\tpublic Vector3 Velocity {\n\t\tget { return (_position - _oldPosition); }\n\t}\n\n\t#endregion\n\n\n\t#region Constructor\n\n\tpublic CableParticle(Vector3 newPosition)\n\t{\n\t\t_oldPosition = _position = newPosition;\n\t}\n\n\t#endregion\n\n\n\t#region Public functions\n\n\tpublic void UpdateVerlet(Vector3 gravityDisplacement)\n\t{\n\t\tif (this.IsBound())\n\t\t{\n\t\t\tif (_boundRigid == null) {\n\t\t\t\tthis.UpdatePosition(_boundTo.position);\t\t\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tswitch (_boundRigid.interpolation) \n\t\t\t\t{\n\t\t\t\tcase RigidbodyInterpolation.Interpolate:\n\t\t\t\t\tthis.UpdatePosition(_boundRigid.position + (_boundRigid.velocity * Time.fixedDeltaTime) / 2);\n\t\t\t\t\tbreak;\n\t\t\t\tcase RigidbodyInterpolation.None:\n\t\t\t\tdefault:\n\t\t\t\t\tthis.UpdatePosition(_boundRigid.position + _boundRigid.velocity * Time.fixedDeltaTime);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse \n\t\t{\n\t\t\tVector3 newPosition = this.Position + this.Velocity + gravityDisplacement;\n\t\t\tthis.UpdatePosition(newPosition);\n\t\t}\n\t}\n\n\tpublic void UpdatePosition(Vector3 newPos) \n\t{\n\t\t_oldPosition = _position;\n\t\t_position = newPos;\n\t}\n\n\tpublic void Bind(Transform to)\n\t{\n\t\t_boundTo = to;\n\t\t_boundRigid = to.GetComponent<Rigidbody>();\n\t\t_oldPosition = _position = _boundTo.position;\n\t}\n\t\t\n\tpublic void UnBind()\n\t{\n\t\t_boundTo = null;\n\t\t_boundRigid = null;\n\t}\n\t\t\n\tpublic bool IsFree()\n\t{\n\t\treturn (_boundTo == null);\n\t}\n\t\t\n\tpublic bool IsBound()\n\t{\n\t\treturn (_boundTo != null);\n\t}\n\n\t#endregion\n}"
  },
  {
    "path": "Assets/CableComponent/Scripts/CableParticle.cs.meta",
    "content": "fileFormatVersion: 2\nguid: c434c9bd6de363c46b5fae6ed070f2d2\ntimeCreated: 1444522184\nlicenseType: Free\nMonoImporter:\n  serializedVersion: 2\n  defaultReferences: []\n  executionOrder: 0\n  icon: {instanceID: 0}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "Assets/CableComponent/Scripts.meta",
    "content": "fileFormatVersion: 2\nguid: dc842025b760e42469c4ef436fecd3c9\nfolderAsset: yes\ntimeCreated: 1444228637\nlicenseType: Free\nDefaultImporter:\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "Assets/CableComponent.meta",
    "content": "fileFormatVersion: 2\nguid: 6c878aafa9e93a146a8d53329313ea55\nfolderAsset: yes\ntimeCreated: 1444228571\nlicenseType: Free\nDefaultImporter:\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "ProjectSettings/ProjectVersion.txt",
    "content": "m_EditorVersion: 5.4.0f3\nm_StandardAssetsVersion: 0\n"
  },
  {
    "path": "README.md",
    "content": "# Cable-Component\nUnity cable component implementation similar to the Unreal Engine one based on verlet integration.\n\nThis project is a simple and optimized implementation of cable physics in Unity3D, it uses verlet integration[1] to achieve the physics simulation just like UE4 approach[2].\n\nThe rendering part still has some work to be done as it is currently using a simple line renderer. Ideally it should use a procedural cable generation approach but it wasn't main the purpouse of this little experiment.\n\n\n[![Cable Component Video](https://j.gifs.com/GZQ5gQ.gif)](https://www.youtube.com/watch?v=VN21ROvrF2k)\n\n\n[1] https://en.wikipedia.org/wiki/Verlet_integration\n\n[2] https://www.unrealengine.com/blog/cable-component-plugin-for-ue4\n"
  }
]