Commit 04fb3a44 authored by Alexander Kreibich's avatar Alexander Kreibich

Full code of the loop generating a long guidewire

parents
using UnityEngine;
using UnityEngine.UI;
public class CameraSwitcherGUI : MonoBehaviour
{
public Camera camera1;
public Camera camera2;
public Button switchButton; // Reference to the UI button
private void Start()
{
// Initialize camera states
camera1.enabled = true;
camera2.enabled = false;
// Add a click listener to the button
switchButton.onClick.AddListener(SwitchCameras);
}
public void SwitchCameras()
{
camera1.enabled = !camera1.enabled;
camera2.enabled = !camera2.enabled;
}
}
fileFormatVersion: 2
guid: d8499b1ef342bc95586efa57c5ce1cee
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Assertions;
namespace GuidewireSim
{
/**
* This class is responsible for tracking collisions of the object it is attached to. Attach this component only to sphere objects
* of the guidewire.
*/
public class CollisionDetectionPrimitive : MonoBehaviour
{
SimulationLoop simulationLoop; //!< The SimulationLoop component in the Simulation GameObject
CollisionHandler collisionHandler; //!< The CollisionHandler component in the Simulation GameObject
public int sphereID; /**< The unique ID of the sphere that this component is attached to. Matches the position in @p spheres in #SimulationLoop.
* @note Should also match the position in @p spherePositions, @p sphereVelocities, @p sphereExternalForces,
* @p spherePositionPredictions in #SimulationLoop.
*/
private void Awake()
{
simulationLoop = FindObjectOfType<SimulationLoop>();
Assert.IsNotNull(simulationLoop);
collisionHandler = FindObjectOfType<CollisionHandler>();
Assert.IsNotNull(collisionHandler);
}
private void Start()
{
AssignSphereID();
}
/**
* Assigns the unique ID of the object sphere it is attached to to #sphereID.
*/
private void AssignSphereID()
{
GameObject thisSphere = this.transform.gameObject;
for (int sphereIndex = 0; sphereIndex < simulationLoop.SpheresCount; sphereIndex++)
{
if (thisSphere == simulationLoop.spheres[sphereIndex])
{
sphereID = sphereIndex;
return;
}
}
Debug.LogWarning("No sphereID could be assigned.");
}
/**
* Registers a collision that Unity's collision detection detected.
*/
private void OnCollisionEnter(Collision other)
{
ContactPoint collisionContact = other.GetContact(0);
Vector3 contactPoint = collisionContact.point;
Vector3 collisionNormal = collisionContact.normal;
collisionHandler.RegisterCollision(this.transform, sphereID, contactPoint, collisionNormal);
}
/**
* Registers a collision that Unity's collision detection detected.
*/
private void OnCollisionStay(Collision other)
{
ContactPoint collisionContact = other.GetContact(0);
Vector3 contactPoint = collisionContact.point;
Vector3 collisionNormal = collisionContact.normal;
collisionHandler.RegisterCollision(this.transform, sphereID, contactPoint, collisionNormal);
}
}
}
\ No newline at end of file
fileFormatVersion: 2
guid: 22ca87514938f7140bded0cf819e6cfa
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 1300
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Assertions;
namespace GuidewireSim
{
/**
* Similarly to CollisionDetectionPrimitive, this class is responsible for tracking collisions of the object it is attached to.
* Attach this component only to blood vessel objects.
*/
public class CollisionDetectionVessel : MonoBehaviour
{
SimulationLoop simulationLoop; //!< The SimulationLoop component in the Simulation GameObject
CollisionHandler collisionHandler; //!< The CollisionHandler component in the Simulation GameObject
private void Awake()
{
simulationLoop = FindObjectOfType<SimulationLoop>();
Assert.IsNotNull(simulationLoop);
collisionHandler = FindObjectOfType<CollisionHandler>();
Assert.IsNotNull(collisionHandler);
}
// private void OnCollisionEnter(Collision other)
// {
// // ContactPoint contactPoint = other.GetContact(0);
// // DebugExtension.DrawPoint(contactPoint.point, Color.black);
// int sphereID = 1;
// collisionHandler.RegisterCollision(other.transform, other, sphereID);
// }
// private void OnCollisionStay(Collision other)
// {
// // ContactPoint contactPoint = other.GetContact(0);
// // DebugExtension.DrawPoint(contactPoint.point, Color.black);
// int sphereID = 1;
// collisionHandler.RegisterCollision(other.transform, other, sphereID);
// }
private int YieldSphereID(Transform sphereTransform)
{
GameObject sphereGO = sphereTransform.gameObject;
for (int sphereIndex = 0; sphereIndex < simulationLoop.SpheresCount; sphereIndex++)
{
if (sphereGO == simulationLoop.spheres[sphereIndex])
{
return sphereIndex;
}
}
Debug.LogWarning("No sphereID could be assigned.");
return 0;
}
}
}
\ No newline at end of file
fileFormatVersion: 2
guid: bb0903acb76666a4a9257caa76e71835
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Assertions;
namespace GuidewireSim
{
/**
* This class manages all collisions that should be resolved, i.e. the collisions of the last frame.
*/
public class CollisionHandler : MonoBehaviour
{
public List<CollisionPair> registeredCollisions; //!< All collisions that occured between the last and the current frame in OnTriggerEnter.
public SphereCollider[] sphereColliders; /**< Each element stores a reference to the SpherCollider of the respective element in @p spheres
* in SimulationLoop.
* @exampletext The second element in this list is the SphereCollider corresponding to the
* sphere GameObject that is referenced in the second element of @p spheres in SimulationLoop.
*/
float sphereRadius = 5f; //!< The radius of the sphere elements of the guidewire.
private void Start()
{
registeredCollisions = new List<CollisionPair>();
}
/**
* Registers a collision by adding it to #registeredCollisions.
* @param sphere The sphere of the guidewire that collided.
* @param sphereID The unique ID of @p sphere.
* @param contactPoint The contact point of the collision.
* @param collisionNormal The normal of the collision.
*/
public void RegisterCollision(Transform sphere, int sphereID, Vector3 contactPoint, Vector3 collisionNormal)
{
CollisionPair registeredCollision = new CollisionPair(sphere, sphereID, contactPoint, collisionNormal);
registeredCollisions.Add(registeredCollision);
}
/**
* Clears the list of all registered collisions.
*/
public void ResetRegisteredCollisions()
{
registeredCollisions.Clear();
}
/**
* Sets the position of the collider of each sphere to the sphere's position prediction.
* @note The position of the collider is implicitly set by setting the colliders center argument.
* @param spheresCount The count of all spheres of the guidewire. Equals the length of @p spherePositionPredictions.
* @param spherePositionPredictions The prediction of the position at the current frame of each sphere (in this case of the last frame).
* @param spherePositions The position at the current frame of each sphere.
*/
public void SetCollidersToPredictions(int spheresCount, Vector3[] spherePositionPredictions, Vector3[] spherePositions)
{
for (int sphereIndex = 0; sphereIndex < spheresCount; sphereIndex++)
{
Vector3 centerPosition = (spherePositionPredictions[sphereIndex] - spherePositions[sphereIndex]) / (2 * sphereRadius);
sphereColliders[sphereIndex].center = centerPosition;
}
}
}
}
\ No newline at end of file
fileFormatVersion: 2
guid: 9f7f474e600043f489e35ce209adf4b9
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace GuidewireSim
{
/**
* Carries all information of a collision that occured.
*/
public struct CollisionPair
{
public Transform sphere; //!< The sphere object of the guidewire that was part of the collision.
public Vector3 contactPoint; //!< The contact point of the collision.
public Vector3 collisionNormal; //!< The normal of the collision.
public int sphereID; //!< The ID of the sphere object of the guidewire that was part of the collision.
public CollisionPair(Transform sphere, int sphereID, Vector3 contactPoint, Vector3 collisionNormal)
{
this.sphere = sphere;
this.sphereID = sphereID;
this.contactPoint = contactPoint;
this.collisionNormal = collisionNormal;
}
}
}
\ No newline at end of file
fileFormatVersion: 2
guid: e348a30119928ff4eba43e7678df2244
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Assertions;
namespace GuidewireSim
{
/**
* This class implements the collision solving that is part of the constraint solving step.
*/
public class CollisionSolvingStep : MonoBehaviour
{
MathHelper mathHelper; //!< The component MathHelper that provides math related helper functions.
CollisionHandler collisionHandler; //!< The component CollisionHandler that tracks all collisions.
Vector3 deltaPosition = new Vector3(); //!< The correction of @p spherePositionPrediction in method SolveCollisionConstraint().
Vector3 initialPositionPrediction = new Vector3();
float sphereRadius = 5f; //!< The radius of a sphere of the guidewire.
[SerializeField] float collisionMargin = 0.1f; /**< A margin by which a colliding element of the guidewire is set away from the object
* colliding with in the direction of the normal.
* @note Without this margin, the colliding element of the guidewire (e.g. a sphere) is
* corrected such that its surface exactly touches the object colliding with, which
* results in the guidewire still penetrating the object.
*/
[Range(0.00001f, 1f)] float collisionStiffness = 0.001f; //!< The collision constraint stiffness parameter.
private void Awake()
{
mathHelper = GetComponent<MathHelper>();
Assert.IsNotNull(mathHelper);
collisionHandler = GetComponent<CollisionHandler>();
Assert.IsNotNull(collisionHandler);
}
/**
* Is responsible for executing one iteration of the constraint solving step for the collision constraint
* of each collision of this frame.
* @param spherePositionPredictions The prediction of the position at the current frame of each sphere (in this case of the last frame).
* @param solverStep The current iteration of the constraint solving step.
* @param constraintSolverSteps The total number of solver steps of the constraint solving step.
*/
public void SolveCollisionConstraints(Vector3[] spherePositionPredictions, int solverStep, int constraintSolverSteps)
{
for (int collisionIndex = 0; collisionIndex < collisionHandler.registeredCollisions.Count; collisionIndex++)
{
CollisionPair collisionPair = collisionHandler.registeredCollisions[collisionIndex];
int sphereID = collisionPair.sphereID;
Vector3 spherePositionPrediction = spherePositionPredictions[sphereID];
SolveCollisionConstraint(spherePositionPrediction, collisionPair.contactPoint, collisionPair.collisionNormal,
solverStep, out deltaPosition);
CorrectCollisionPredictions(sphereID, spherePositionPredictions, solverStep, constraintSolverSteps);
}
}
/**
* Solves the collision constraint for one collision that occured this frame.
* @param spherePositionPredictions The prediction of the position at the current frame of each sphere (in this case of the last frame).
* @param contactPoint The contact point of the collision.
* @param collisionNormal The normal of the collision.
* @param solverStep The current iteration of the constraint solving step.
* @attention Current calculation of the normal only works for spheres.
*/
private void SolveCollisionConstraint(Vector3 spherePositionPrediction, Vector3 contactPoint, Vector3 collisionNormal,
int solverStep, out Vector3 deltaPosition)
{
if (solverStep == 0)
{
DrawCollisionInformation(spherePositionPrediction, contactPoint, collisionNormal);
}
deltaPosition = CalculateDeltaPosition(spherePositionPrediction, contactPoint, collisionNormal);
}
/**
* Draws the contact point, collision normal, and displacement corrections into the scene of the collision that occured.
* @param spherePositionPrediction The position prediction of the sphere that collided.
* @param contactPoint The contact point of the collision.
* @param collisionNormal The normal of the collision.
*/
private void DrawCollisionInformation(Vector3 spherePositionPrediction, Vector3 contactPoint, Vector3 collisionNormal)
{
Debug.DrawLine(contactPoint, contactPoint + 10f * collisionNormal, Color.blue, 2f);
DebugExtension.DrawPoint(spherePositionPrediction, Color.white);
DebugExtension.DrawPoint(contactPoint, Color.yellow);
}
/**
* Calculates the displacement of the collision constraint.
* @param spherePositionPredictions The prediction of the position at the current frame of each sphere (in this case of the last frame).
* @param closestSurfacePoint The contact point of the collision.
* @param normalVector The collision normal.
* @return The delta position, i.e. the calculated displacement.
*/
private Vector3 CalculateDeltaPosition(Vector3 spherePositionPrediction, Vector3 closestSurfacePoint, Vector3 normalVector)
{
return - (spherePositionPrediction - sphereRadius * normalVector - closestSurfacePoint - collisionMargin * normalVector);
}
/**
* Corrects the position prediction of the sphere of @p sphereIndex with the calculated displacement.
* @param sphereIndex The sphere ID of the colliding sphere.
* @param spherePositionPredictions The prediction of the position at the current frame of each sphere (in this case of the last frame).
* @param solverStep The current iteration of the constraint solving step.
* @param constraintSolverSteps The total number of solver steps of the constraint solving step.
*/
private void CorrectCollisionPredictions(int sphereIndex, Vector3[] spherePositionPredictions, int solverStep, int constraintSolverSteps)
{
Assert.IsTrue(sphereIndex >= 0);
if (solverStep == 0)
{
initialPositionPrediction = spherePositionPredictions[sphereIndex];
}
spherePositionPredictions[sphereIndex] += collisionStiffness * deltaPosition;
if (solverStep == constraintSolverSteps - 1)
{
DebugExtension.DrawPoint(spherePositionPredictions[sphereIndex], Color.red);
}
}
}
}
\ No newline at end of file
fileFormatVersion: 2
guid: 70b3fde2a73919f49b9b11e9deeeef0d
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Assertions;
namespace GuidewireSim
{
public class CollisionTestPerformer : MonoBehaviour
{
SimulationLoop simulationLoop; //!< The SimulationLoop component that executes all steps of the simulation loop.
[SerializeField] Vector3 pullForce = new Vector3(0f, 0f, 5f); //!< External force that is applied in Force Test Three.
[SerializeField] bool doCollisionTestOne = false;
[SerializeField] bool doCollisionTestTwo = false;
[SerializeField] bool doCollisionTestThree = false;
[SerializeField] bool doCollisionTestFour = false;
float startTime = 0f;
private void Awake()
{
simulationLoop = GetComponent<SimulationLoop>();
Assert.IsNotNull(simulationLoop);
}
private void Start()
{
startTime = Time.time;
PerformCollisionTests();
}
/**
* Performs each Torque Test whose respective serialized boolean is set to true in the Unity inspector.
*/
private void PerformCollisionTests()
{
if (doCollisionTestOne) PerformCollisionTestOne();
else if (doCollisionTestTwo) StartCoroutine(PerformCollisionTestTwo());
else if (doCollisionTestThree) StartCoroutine(PerformCollisionTestThree());
else if (doCollisionTestFour) StartCoroutine(PerformCollisionTestFour());
}
/**
* Performs torque test one. This test applies an external force to one end of the guidewire.
*/
private void PerformCollisionTestOne()
{
for (int sphereIndex = 0; sphereIndex < (simulationLoop.SpheresCount - 1); sphereIndex++)
{
simulationLoop.sphereExternalForces[sphereIndex] = Vector3.zero;
}
simulationLoop.sphereExternalForces[simulationLoop.SpheresCount - 1] = pullForce;
}
// force gets applied for a fixed time
private IEnumerator PerformCollisionTestTwo(float applyForceTime = 1.5f)
{
for (int sphereIndex = 0; sphereIndex < (simulationLoop.SpheresCount - 1); sphereIndex++)
{
simulationLoop.sphereExternalForces[sphereIndex] = Vector3.zero;
}
simulationLoop.sphereExternalForces[simulationLoop.SpheresCount - 1] = pullForce;
yield return new WaitForSeconds(applyForceTime);
simulationLoop.sphereExternalForces[simulationLoop.SpheresCount - 1] = Vector3.zero;
float timeDiff = Time.time - startTime;
Debug.Log("Elapsed time of collision test: " + timeDiff);
Debug.Log("Velocity at test end: " + simulationLoop.sphereVelocities[1].ToString("e2"));
Debug.Log("End of Pull Phase of Collision Test Two");
}
// force gets applied until a fixed velocity is reached
private IEnumerator PerformCollisionTestThree(float exitVelocity = 4f)
{
for (int sphereIndex = 0; sphereIndex < (simulationLoop.SpheresCount - 1); sphereIndex++)
{
simulationLoop.sphereExternalForces[sphereIndex] = Vector3.zero;
}
simulationLoop.sphereExternalForces[simulationLoop.SpheresCount - 1] = pullForce;
yield return new WaitUntil(() => simulationLoop.sphereVelocities[simulationLoop.SpheresCount - 1].z >= exitVelocity);
simulationLoop.sphereExternalForces[simulationLoop.SpheresCount - 1] = Vector3.zero;
float timeDiff = Time.time - startTime;
Debug.Log("Elapsed time of collision test: " + timeDiff);
Debug.Log("Velocity at test end: " + simulationLoop.sphereVelocities[1].ToString("e2"));
Debug.Log("End of Pull Phase of Collision Test Three");
}
// force gets applied for the whole time
private IEnumerator PerformCollisionTestFour(float pullForceFactor = 0.3f)
{
float appliedPullForce = pullForceFactor * pullForce.z;
Debug.Log("Start of Collision Test Four");
Debug.Log("Pull Force: " + appliedPullForce);
for (int sphereIndex = 0; sphereIndex < (simulationLoop.SpheresCount - 1); sphereIndex++)
{
simulationLoop.sphereExternalForces[sphereIndex] = Vector3.zero;
}
simulationLoop.sphereExternalForces[simulationLoop.SpheresCount - 1] = pullForceFactor * pullForce;
yield return null;
}
}
}
fileFormatVersion: 2
guid: ad1da93e65d612f4bb67479904956eca
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 1200
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
This diff is collapsed.
fileFormatVersion: 2
guid: 44f385d20ea9f8d49a21d6275cc8dafe
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
using UnityEngine;
using System.IO;
using GuidewireSim;
public class CreationScript : MonoBehaviour
{
public GameObject spherePrefab;
public GameObject cylinderPrefab;
private int spheresCount;
private int cylinderCount;
private GameObject[] spheres;
private GameObject[] cylinders;
private SimulationLoop simulationLoop;
void Start()
{
simulationLoop = GameObject.Find("Simulation").GetComponent<SimulationLoop>();
}
void FixedUpdate()
{
SavePositionsToFile();
}
public void CreateGuidewire(int numberElements)
{
spheres = new GameObject[numberElements];
cylinders = new GameObject[numberElements - 1];
float rEL = simulationLoop.GetRodElementLength();
for (int i = 0; i < numberElements; ++i)
{
GameObject sphere = Instantiate(spherePrefab);
sphere.transform.position = new Vector3(0, 7, -1000 + i * rEL);
sphere.transform.parent = this.transform;
spheres[i] = sphere;
if (i < numberElements - 1)
{
GameObject cylinder = Instantiate(cylinderPrefab);
cylinder.layer = 6;
cylinder.transform.parent = this.transform;
cylinders[i] = cylinder;
}
}
spheresCount = numberElements;
simulationLoop.SetSpheres(spheres);
simulationLoop.SetCylinders(cylinders);
}
public void SavePositionsToFile()
{
string path = "/home/akreibich/TestRobinCode22/PositionTest22_2.txt";
StreamWriter writer = new StreamWriter(path, true);
if (spheresCount > 0)
{
Vector3 firstSpherePosition = spheres[0].transform.position;
writer.WriteLine("First Sphere: " + firstSpherePosition.x + "," + firstSpherePosition.y + "," + firstSpherePosition.z);
if (spheresCount > 1)
{
Vector3 lastSpherePosition = spheres[spheresCount - 1].transform.position;
writer.WriteLine("Last Sphere: " + lastSpherePosition.x + "," + lastSpherePosition.y + "," + lastSpherePosition.z);
}
}
writer.Close();
}
public GameObject GetLastSphere()
{
if (spheres != null && spheres.Length > 0)
{
return spheres[spheres.Length - 1];
}
return null;
}
public GameObject[] GetSpheres()
{
return spheres;
}
public GameObject[] GetCylinders()
{
return cylinders;
}
}
fileFormatVersion: 2
guid: 0db5d8d9e5b58013aada8da779d68927
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
This diff is collapsed.
fileFormatVersion: 2
guid: 54785b64fd02b0e429cca9e59cdcb28b
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Assertions;
namespace GuidewireSim
{
/**
* This class represents each orientation by drawing all of its directors as arrows in each frame.
*/
public class DirectorsDrawer : MonoBehaviour
{
SimulationLoop simulationLoop; //!< The component SimulationLoop.
[SerializeField] [Range(0.1f, 50f)] float scaleFactor; //!< The scale factor that gets multiplied to the length of the respective director.
[SerializeField] [Range(1f, 90f)] float arrowHeadAngle; //!< The angle spread of the arrow head.
[SerializeField] [Range(0.1f, 1f)] float arrowHeadPercentage; //!< The percentage of the length of the arrow that the arrow head covers.
Color directorOneColor = Color.green; //!< The color that the lines representing the first director are drawn with.
Color directorTwoColor = Color.blue; //!< The color that the lines representing the second director are drawn with.
Color directorThreeColor = Color.red; //!< The color that the lines representing the third director are drawn with.
Color[] directorColors = new Color[3] {Color.red, Color.green, Color.blue}; /**< The color that the lines representing the
* three directors are drawn with.
* @note The i-th director is drawn in the i-th Color.
*/
private void Awake()
{
simulationLoop = GetComponent<SimulationLoop>();
Assert.IsNotNull(simulationLoop);
}
void Update()
{
DrawDirectors(simulationLoop.cylinderPositions, simulationLoop.directors);
}
/**
* Draws the director basis of each orientation element as arrows.
* @param cylinderPositions The center of mass of each cylinder, i.e. the position of each orientation element.
* @param directors The orthonormal basis of each orientation element / cylinder, also called directors.
*/
private void DrawDirectors(Vector3[] cylinderPositions, Vector3[][] directors)
{
// iterates over each orientation element
for (int cylinderIndex = 0; cylinderIndex < cylinderPositions.Length; cylinderIndex++)
{
Vector3 startPosition = cylinderPositions[cylinderIndex];
// iterates over each director
for (int directorIndex = 0; directorIndex < 3; directorIndex++)
{
Vector3 endPosition = startPosition + scaleFactor * directors[directorIndex][cylinderIndex];
// Draws a line between the cylinder's center of mass and the director's end position.
Debug.DrawLine(startPosition, endPosition, directorColors[directorIndex]);
Vector3[] arrowHeadPositions = CalculateArrowHeadPositions(startPosition, endPosition);
DrawArrowHeadLines(directorIndex, endPosition, arrowHeadPositions);
DrawArrowHeadConnectionLines(directorIndex, arrowHeadPositions);
}
}
}
/**
* Calculates the end position of each line of each arrow head. E.g. an arrow head consists of four lines, each of them starting at
* @p endPosition and spreading in different directions to form the shape of an arrow tip.
* @param startPosition The start position of the director, i.e. the position of the orientation.
* @param endPosition The position of the tip of the arrow head.
* @return The end positions of the four lines that form the arrow head.
* @req @p arrowHeadPositions has a length of 4.
*/
private Vector3[] CalculateArrowHeadPositions(Vector3 startPosition, Vector3 endPosition)
{
Vector3[] arrowHeadPositions = new Vector3[4];
Vector3 direction = endPosition - startPosition;
arrowHeadPositions[0] = endPosition + Quaternion.LookRotation(direction) * Quaternion.Euler(-arrowHeadAngle, 0, 0) * Vector3.back * arrowHeadPercentage;
arrowHeadPositions[1] = endPosition + Quaternion.LookRotation(direction) * Quaternion.Euler(0, arrowHeadAngle, 0) * Vector3.back * arrowHeadPercentage;
arrowHeadPositions[2] = endPosition + Quaternion.LookRotation(direction) * Quaternion.Euler(arrowHeadAngle, 0, 0) * Vector3.back * arrowHeadPercentage;
arrowHeadPositions[3] = endPosition + Quaternion.LookRotation(direction) * Quaternion.Euler(0, -arrowHeadAngle, 0) * Vector3.back * arrowHeadPercentage;
Assert.IsTrue(arrowHeadPositions.Length == 4);
return arrowHeadPositions;
}
/**
* Draws the four lines that form the arrow head for the director that corresponds to @p directorIndex.
* @param directorIndex The index of the director under consideration.
* @param endPosition The position of the tip of the arrow head.
* @param arrowHeadPositions The end positions of the four lines that form the arrow head.
* @req @p arrowHeadPositions has a length of 4.
*/
private void DrawArrowHeadLines(int directorIndex, Vector3 endPosition, Vector3[] arrowHeadPositions)
{
Assert.IsTrue(arrowHeadPositions.Length == 4);
for (int positionIndex = 0; positionIndex < arrowHeadPositions.Length; positionIndex++)
{
Debug.DrawLine(endPosition, arrowHeadPositions[positionIndex], directorColors[directorIndex]);
}
}
/**
* Draws the four lines that connect the arrow head tips with each other. E.g. draws the line from @p arrowHeadPositions 0 and @p arrowHeadPositions 1.
* @param directorIndex The index of the director under consideration.
* @param arrowHeadPositions The end positions of the four lines that form the arrow head.
*/
private void DrawArrowHeadConnectionLines(int directorIndex, Vector3[] arrowHeadPositions)
{
Debug.DrawLine(arrowHeadPositions[0], arrowHeadPositions[1], directorColors[directorIndex]);
Debug.DrawLine(arrowHeadPositions[1], arrowHeadPositions[2], directorColors[directorIndex]);
Debug.DrawLine(arrowHeadPositions[2], arrowHeadPositions[3], directorColors[directorIndex]);
Debug.DrawLine(arrowHeadPositions[3], arrowHeadPositions[0], directorColors[directorIndex]);
}
}
}
\ No newline at end of file
fileFormatVersion: 2
guid: 58982b2c5257b7d4baa434eefb1460fb
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Assertions;
namespace GuidewireSim
{
/**
* This class enables the user to test the impact of external forces with one button within the Unity inspector.
*/
public class ForceTestPerformer : MonoBehaviour
{
SimulationLoop simulationLoop; //!< The SimulationLoop component that executes all steps of the simulation loop.
[Tooltip("Applies gravity to all spheres.")]
[SerializeField] bool doForceTestOne = false; //!< Whether to run Force Test One. This test applies gravity to all spheres.
[Tooltip("Applies an external force to one end of the guidewire.")]
[SerializeField] bool doForceTestTwo = false; //!< Whether to run Force Test Two. This test applies an external force to one end of the guidewire.
[Tooltip("Applies an external force to one end of the guidewire for a fixed amount of time and then the opposite force"
+ "at the same sphere for the same amount of time.")]
[SerializeField] bool doForceTestThree = false; /**< Whether to run Force Test Three. This test applies an external force to one end of the guidewire
* for a fixed amount of time and then the opposite force at the same sphere for the same amount of time.
*/
[Tooltip("Applies an external force to one end of the guidewire and the opposite force at the other end of the guidewire.")]
[SerializeField] bool doForceTestFour = false; /**< Whether to run Force Test Four. This test applies an external force to one end of the guidewire
* and the opposite force at the other end of the guidewire.
*/
[Tooltip("Shifts one end of the guidewire and runs the simulation for exactly one loop iteration to test constraint solving.")]
[SerializeField] bool doSingleLoopTest = false; /**< Whether to run the Single Loop Test. This test shifts one end of the guidewire and
* runs the simulation for exactly one loop iteration to test constraint solving.
*/
[Tooltip("External force that is applied in Force Test Three.")]
[SerializeField] Vector3 pullForceTestThree = new Vector3(0f, 3f, 0f); //!< External force that is applied in Force Test Three.
private void Awake()
{
simulationLoop = GetComponent<SimulationLoop>();
Assert.IsNotNull(simulationLoop);
}
private void Start()
{
PerformForceTests();
}
/**
* Performs each Force Test whose respective serialized boolean is set to true in the Unity inspector.
*/
private void PerformForceTests()
{
if (doForceTestOne) PerformForceTestOne();
else if (doForceTestTwo) PerformForceTestTwo();
else if (doForceTestThree) StartCoroutine(PerformForceTestThree(pullForceTestThree));
else if (doForceTestFour) PerformForceTestFour();
else if (doSingleLoopTest) PerformSingleLoopTest();
}
/**
* Performs force test one. This test applies gravity to all spheres.
*/
private void PerformForceTestOne()
{
Vector3 gravity = new Vector3(0f, -9.81f, 0f);
for (int sphereIndex = 0; sphereIndex < simulationLoop.SpheresCount; sphereIndex++)
{
simulationLoop.sphereExternalForces[sphereIndex] = gravity;
}
}
/**
* Performs force test two. This test applies an external force to one end of the guidewire.
*/
private void PerformForceTestTwo()
{
Vector3 pullForce = new Vector3(0f, 0.3f, 0f);
for (int sphereIndex = 0; sphereIndex < (simulationLoop.SpheresCount - 1); sphereIndex++)
{
simulationLoop.sphereExternalForces[sphereIndex] = Vector3.zero;
}
simulationLoop.sphereExternalForces[simulationLoop.SpheresCount - 1] = pullForce;
}
/**
* Performs force test three. This test applies an external force to one end of the guidewire
* for a fixed amount of time and then the opposite force at the same sphere for the same amount of time.
* @param applyForceTime For how many seconds to apply the force to the particles.
*/
private IEnumerator PerformForceTestThree(Vector3 pullForce, float applyForceTime = 1f)
{
for (int sphereIndex = 0; sphereIndex < (simulationLoop.SpheresCount - 1); sphereIndex++)
{
simulationLoop.sphereExternalForces[sphereIndex] = Vector3.zero;
}
simulationLoop.sphereExternalForces[simulationLoop.SpheresCount - 1] = pullForce;
yield return new WaitForSeconds(applyForceTime);
simulationLoop.sphereExternalForces[simulationLoop.SpheresCount - 1] = -pullForce;
yield return new WaitForSeconds(applyForceTime);
simulationLoop.sphereExternalForces[simulationLoop.SpheresCount - 1] = Vector3.zero;
Debug.Log("End of Force Test Three");
}
/**
* Performs force test four. This test applies an external force to one end of the guidewire
* and the opposite force at the other end of the guidewire.
*/
private void PerformForceTestFour()
{
Vector3 pullForce = new Vector3(0f, 1f, 0f);
simulationLoop.sphereExternalForces[0] = -pullForce;
if (simulationLoop.SpheresCount > 2)
{
for (int sphereIndex = 1; sphereIndex < (simulationLoop.SpheresCount - 1); sphereIndex++)
{
simulationLoop.sphereExternalForces[sphereIndex] = Vector3.zero;
}
}
simulationLoop.sphereExternalForces[simulationLoop.SpheresCount - 1] = pullForce;
}
/**
* Performs the single loop test. This test shifts one end of the guidewire and runs the simulation for exactly
* one loop iteration to test constraint solving.
* @note Position of particle one stays at (0, 0, 0), while the section particle shifts to about (10, 2, 0). Expected result is that
* both particles move a bit towards each other and reestablish a distance of 10 between them.
*/
private void PerformSingleLoopTest()
{
Assert.IsTrue(simulationLoop.sphereVelocities.Length >= 2);
// ARRANGE
simulationLoop.ExecuteSingleLoopTest = true;
simulationLoop.ConstraintSolverSteps = 1000;
simulationLoop.sphereVelocities[simulationLoop.SpheresCount - 1] = new Vector3(0f, 100f, 0f);
Debug.Log("Executing Single Loop Test.");
Debug.Log("Constraint Solving Steps: " + simulationLoop.ConstraintSolverSteps);
Debug.Log("The last sphere is displaced by velocity " + simulationLoop.sphereVelocities[simulationLoop.SpheresCount - 1]);
Debug.Log("The distance between both spheres at rest state is "
+ Vector3.Distance(simulationLoop.spherePositions[0], simulationLoop.spherePositions[simulationLoop.SpheresCount - 1]));
// ACT
simulationLoop.PerformSimulationLoop();
// ASSERT
Debug.Log("Sphere Positions after Update Step: " + simulationLoop.spherePositions[0] + simulationLoop.spherePositions[simulationLoop.SpheresCount - 1]);
Debug.Log("The distance between both spheres after the update step is "
+ Vector3.Distance(simulationLoop.spherePositions[0], simulationLoop.spherePositions[simulationLoop.SpheresCount - 1]));
}
}
}
\ No newline at end of file
fileFormatVersion: 2
guid: 08d386284a38d8446a35fc059bdcdac7
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 900
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using GuidewireSim;
public class GuidewireCreateManager : MonoBehaviour
{
private CreationScript creationScript;
private SimulationLoop simulationLoop;
public GameObject Simulation;
public int L_0 = 100;
private float REL;
private void Start()
{
// Find the SimulationLoop component from the Simulation GameObject
simulationLoop = Simulation.GetComponent<SimulationLoop>();
if (simulationLoop == null)
{
Debug.LogError("SimulationLoop component not found in the Simulation GameObject!");
return; // Exit if SimulationLoop is not found
}
// Get the Rod Element Length
REL = simulationLoop.GetRodElementLength();
int numberOfElements = (int)(L_0 / REL) + 1; // Calculate the desired number of elements
// Find the CreationScript component in the scene
creationScript = FindObjectOfType<CreationScript>();
if (creationScript != null)
{
creationScript.CreateGuidewire(numberOfElements);
// Get the created spheres and cylinders from the CreationScript
GameObject[] createdSpheres = creationScript.GetSpheres();
GameObject[] createdCylinders = creationScript.GetCylinders();
// Link them to the arrays in the SimulationLoop script
simulationLoop.SetSpheres(createdSpheres);
simulationLoop.SetCylinders(createdCylinders);
}
else
{
Debug.LogError("CreationScript component not found in the scene!");
}
}
}
fileFormatVersion: 2
guid: 55bd9da85a19af534bb6a2238637280b
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
This diff is collapsed.
fileFormatVersion: 2
guid: 51e2964dfbfea2a4a86aa0885301a80c
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
This diff is collapsed.
fileFormatVersion: 2
guid: 247d58bf6cc854e409f370d46a6646f4
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
fileFormatVersion: 2
guid: 9a007ccd78fa34e7d8072ff3761a6868
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.IO;
using System.Text;
namespace Dummiesman {
public class CharWordReader {
public char[] word;
public int wordSize;
public bool endReached;
private StreamReader reader;
private int bufferSize;
private char[] buffer;
public char currentChar;
private int currentPosition = 0;
private int maxPosition = 0;
public CharWordReader(StreamReader reader, int bufferSize) {
this.reader = reader;
this.bufferSize = bufferSize;
this.buffer = new char[this.bufferSize];
this.word = new char[this.bufferSize];
this.MoveNext();
}
public void SkipWhitespaces() {
while (char.IsWhiteSpace(this.currentChar)) {
this.MoveNext();
}
}
public void SkipWhitespaces(out bool newLinePassed) {
newLinePassed = false;
while (char.IsWhiteSpace(this.currentChar)) {
if (this.currentChar == '\r' || this.currentChar == '\n') {
newLinePassed = true;
}
this.MoveNext();
}
}
public void SkipUntilNewLine() {
while (this.currentChar != char.MinValue && this.currentChar != '\n' && this.currentChar != '\r') {
this.MoveNext();
}
this.SkipNewLineSymbols();
}
public void ReadUntilWhiteSpace() {
this.wordSize = 0;
while (this.currentChar != char.MinValue && char.IsWhiteSpace(this.currentChar) == false) {
this.word[this.wordSize] = this.currentChar;
this.wordSize++;
this.MoveNext();
}
}
public void ReadUntilNewLine() {
this.wordSize = 0;
while (this.currentChar != char.MinValue && this.currentChar != '\n' && this.currentChar != '\r') {
this.word[this.wordSize] = this.currentChar;
this.wordSize++;
this.MoveNext();
}
this.SkipNewLineSymbols();
}
public bool Is(string other) {
if (other.Length != this.wordSize) {
return false;
}
for (int i=0; i<this.wordSize; i++) {
if (this.word[i] != other[i]) {
return false;
}
}
return true;
}
public string GetString(int startIndex = 0) {
if (startIndex >= this.wordSize - 1) {
return string.Empty;
}
return new string(this.word, startIndex, this.wordSize - startIndex);
}
public Vector3 ReadVector() {
this.SkipWhitespaces();
float x = this.ReadFloat();
this.SkipWhitespaces();
float y = this.ReadFloat();
this.SkipWhitespaces(out var newLinePassed);
float z = 0f;
if (newLinePassed == false) {
z = this.ReadFloat();
}
return new Vector3(x, y, z);
}
public int ReadInt() {
int result = 0;
bool isNegative = this.currentChar == '-';
if (isNegative == true) {
this.MoveNext();
}
while (this.currentChar >= '0' && this.currentChar <= '9') {
var digit = this.currentChar - '0';
result = result * 10 + digit;
this.MoveNext();
}
return (isNegative == true) ? -result : result;
}
public float ReadFloat() {
bool isNegative = this.currentChar == '-';
if (isNegative) {
this.MoveNext();
}
var num = (float)this.ReadInt();
if (this.currentChar == '.' || this.currentChar == ',') {
this.MoveNext();
num += this.ReadFloatEnd();
if (this.currentChar == 'e' || this.currentChar == 'E') {
this.MoveNext();
var exp = this.ReadInt();
num = num * Mathf.Pow(10f, exp);
}
}
if (isNegative == true) {
num = -num;
}
return num;
}
private float ReadFloatEnd() {
float result = 0f;
var exp = 0.1f;
while (this.currentChar >= '0' && this.currentChar <= '9') {
var digit = this.currentChar - '0';
result += digit * exp;
exp *= 0.1f;
this.MoveNext();
}
return result;
}
private void SkipNewLineSymbols() {
while (this.currentChar == '\n' || this.currentChar == '\r') {
this.MoveNext();
}
}
public void MoveNext() {
this.currentPosition++;
if (this.currentPosition >= this.maxPosition) {
if (this.reader.EndOfStream == true) {
this.currentChar = char.MinValue;
this.endReached = true;
return;
}
this.currentPosition = 0;
this.maxPosition = this.reader.Read(this.buffer, 0, this.bufferSize);
}
this.currentChar = this.buffer[this.currentPosition];
}
}
}
\ No newline at end of file
fileFormatVersion: 2
guid: 37ed5e34a98d98669ac1a63bf547afa3
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
This diff is collapsed.
fileFormatVersion: 2
guid: 11b3883c0720fb8409b86b54877b58ea
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
This diff is collapsed.
fileFormatVersion: 2
guid: 9951190d0789ee64599dc374c1f81ce5
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
using System.Globalization;
using UnityEngine;
namespace Dummiesman
{
public static class OBJLoaderHelper
{
/// <summary>
/// Enables transparency mode on standard materials
/// </summary>
public static void EnableMaterialTransparency(Material mtl)
{
mtl.SetFloat("_Mode", 3f);
mtl.SetInt("_SrcBlend", (int)UnityEngine.Rendering.BlendMode.SrcAlpha);
mtl.SetInt("_DstBlend", (int)UnityEngine.Rendering.BlendMode.OneMinusSrcAlpha);
mtl.SetInt("_ZWrite", 0);
mtl.DisableKeyword("_ALPHATEST_ON");
mtl.EnableKeyword("_ALPHABLEND_ON");
mtl.DisableKeyword("_ALPHAPREMULTIPLY_ON");
mtl.renderQueue = 3000;
}
/// <summary>
/// Modified from https://codereview.stackexchange.com/a/76891. Faster than float.Parse
/// </summary>
public static float FastFloatParse(string input)
{
if (input.Contains("e") || input.Contains("E"))
return float.Parse(input, CultureInfo.InvariantCulture);
float result = 0;
int pos = 0;
int len = input.Length;
if (len == 0) return float.NaN;
char c = input[0];
float sign = 1;
if (c == '-')
{
sign = -1;
++pos;
if (pos >= len) return float.NaN;
}
while (true) // breaks inside on pos >= len or non-digit character
{
if (pos >= len) return sign * result;
c = input[pos++];
if (c < '0' || c > '9') break;
result = (result * 10.0f) + (c - '0');
}
if (c != '.' && c != ',') return float.NaN;
float exp = 0.1f;
while (pos < len)
{
c = input[pos++];
if (c < '0' || c > '9') return float.NaN;
result += (c - '0') * exp;
exp *= 0.1f;
}
return sign * result;
}
/// <summary>
/// Modified from http://cc.davelozinski.com/c-sharp/fastest-way-to-convert-a-string-to-an-int. Faster than int.Parse
/// </summary>
public static int FastIntParse(string input)
{
int result = 0;
bool isNegative = (input[0] == '-');
for (int i = (isNegative) ? 1 : 0; i < input.Length; i++)
result = result * 10 + (input[i] - '0');
return (isNegative) ? -result : result;
}
public static Material CreateNullMaterial()
{
return new Material(Shader.Find("Standard (Specular setup)"));
}
public static Vector3 VectorFromStrArray(string[] cmps)
{
float x = FastFloatParse(cmps[1]);
float y = FastFloatParse(cmps[2]);
if (cmps.Length == 4)
{
float z = FastFloatParse(cmps[3]);
return new Vector3(x, y, z);
}
return new Vector2(x, y);
}
public static Color ColorFromStrArray(string[] cmps, float scalar = 1.0f)
{
float Kr = FastFloatParse(cmps[1]) * scalar;
float Kg = FastFloatParse(cmps[2]) * scalar;
float Kb = FastFloatParse(cmps[3]) * scalar;
return new Color(Kr, Kg, Kb);
}
}
}
\ No newline at end of file
fileFormatVersion: 2
guid: 6083f989375f6104aafb31a9e82ee153
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
/*
* Copyright (c) 2019 Dummiesman
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*/
using Dummiesman;
using System.Collections.Generic;
using UnityEngine;
namespace Dummiesman {
public class OBJObjectBuilder {
//
public int PushedFaceCount { get; private set; } = 0;
//stuff passed in by ctor
private OBJLoader _loader;
private string _name;
private Dictionary<ObjLoopHash, int> _globalIndexRemap = new Dictionary<ObjLoopHash, int>();
private Dictionary<string, List<int>> _materialIndices = new Dictionary<string, List<int>>();
private List<int> _currentIndexList;
private string _lastMaterial = null;
//our local vert/normal/uv
private List<Vector3> _vertices = new List<Vector3>();
private List<Vector3> _normals = new List<Vector3>();
private List<Vector2> _uvs = new List<Vector2>();
//this will be set if the model has no normals or missing normal info
private bool recalculateNormals = false;
/// <summary>
/// Loop hasher helper class
/// </summary>
private class ObjLoopHash {
public int vertexIndex;
public int normalIndex;
public int uvIndex;
public override bool Equals(object obj) {
if (!(obj is ObjLoopHash))
return false;
var hash = obj as ObjLoopHash;
return (hash.vertexIndex == vertexIndex) && (hash.uvIndex == uvIndex) && (hash.normalIndex == normalIndex);
}
public override int GetHashCode() {
int hc = 3;
hc = unchecked(hc * 314159 + vertexIndex);
hc = unchecked(hc * 314159 + normalIndex);
hc = unchecked(hc * 314159 + uvIndex);
return hc;
}
}
public GameObject Build() {
var go = new GameObject(_name);
//add meshrenderer
var mr = go.AddComponent<MeshRenderer>();
int submesh = 0;
//locate the material for each submesh
Material[] materialArray = new Material[_materialIndices.Count];
foreach (var kvp in _materialIndices) {
Material material = null;
if (_loader.Materials == null) {
material = OBJLoaderHelper.CreateNullMaterial();
material.name = kvp.Key;
} else {
if (!_loader.Materials.TryGetValue(kvp.Key, out material)) {
material = OBJLoaderHelper.CreateNullMaterial();
material.name = kvp.Key;
_loader.Materials[kvp.Key] = material;
}
}
materialArray[submesh] = material;
submesh++;
}
mr.sharedMaterials = materialArray;
//add meshfilter
var mf = go.AddComponent<MeshFilter>();
submesh = 0;
var msh = new Mesh() {
name = _name,
indexFormat = (_vertices.Count > 65535) ? UnityEngine.Rendering.IndexFormat.UInt32 : UnityEngine.Rendering.IndexFormat.UInt16,
subMeshCount = _materialIndices.Count
};
//set vertex data
msh.SetVertices(_vertices);
msh.SetNormals(_normals);
msh.SetUVs(0, _uvs);
//set faces
foreach (var kvp in _materialIndices) {
msh.SetTriangles(kvp.Value, submesh);
submesh++;
}
//recalculations
if (recalculateNormals)
msh.RecalculateNormals();
msh.RecalculateTangents();
msh.RecalculateBounds();
mf.sharedMesh = msh;
//
return go;
}
public void SetMaterial(string name) {
if (!_materialIndices.TryGetValue(name, out _currentIndexList))
{
_currentIndexList = new List<int>();
_materialIndices[name] = _currentIndexList;
}
}
public void PushFace(string material, List<int> vertexIndices, List<int> normalIndices, List<int> uvIndices) {
//invalid face size?
if (vertexIndices.Count < 3) {
return;
}
//set material
if (material != _lastMaterial) {
SetMaterial(material);
_lastMaterial = material;
}
//remap
int[] indexRemap = new int[vertexIndices.Count];
for (int i = 0; i < vertexIndices.Count; i++) {
int vertexIndex = vertexIndices[i];
int normalIndex = normalIndices[i];
int uvIndex = uvIndices[i];
var hashObj = new ObjLoopHash() {
vertexIndex = vertexIndex,
normalIndex = normalIndex,
uvIndex = uvIndex
};
int remap = -1;
if (!_globalIndexRemap.TryGetValue(hashObj, out remap)) {
//add to dict
_globalIndexRemap.Add(hashObj, _vertices.Count);
remap = _vertices.Count;
//add new verts and what not
_vertices.Add((vertexIndex >= 0 && vertexIndex < _loader.Vertices.Count) ? _loader.Vertices[vertexIndex] : Vector3.zero);
_normals.Add((normalIndex >= 0 && normalIndex < _loader.Normals.Count) ? _loader.Normals[normalIndex] : Vector3.zero);
_uvs.Add((uvIndex >= 0 && uvIndex < _loader.UVs.Count) ? _loader.UVs[uvIndex] : Vector2.zero);
//mark recalc flag
if (normalIndex < 0)
recalculateNormals = true;
}
indexRemap[i] = remap;
}
//add face to our mesh list
if (indexRemap.Length == 3) {
_currentIndexList.AddRange(new int[] { indexRemap[0], indexRemap[1], indexRemap[2] });
} else if (indexRemap.Length == 4) {
_currentIndexList.AddRange(new int[] { indexRemap[0], indexRemap[1], indexRemap[2] });
_currentIndexList.AddRange(new int[] { indexRemap[2], indexRemap[3], indexRemap[0] });
} else if (indexRemap.Length > 4) {
for (int i = indexRemap.Length - 1; i >= 2; i--) {
_currentIndexList.AddRange(new int[] { indexRemap[0], indexRemap[i - 1], indexRemap[i] });
}
}
PushedFaceCount++;
}
public OBJObjectBuilder(string name, OBJLoader loader) {
_name = name;
_loader = loader;
}
}
}
\ No newline at end of file
fileFormatVersion: 2
guid: 8242329223027ef40a531fc4dc2efd1c
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
<!DOCTYPE html>
<html>
<head>
<title>Dummiesman OBJ Importer Readme</title>
</head>
<body>
<h2>Dummiesman's OBJ Importer</h2>
<p>Works during runtime*, and in editor! A couple of code samples are provided in the Samples folder.</p>
<br />
<h2>*Runtime Import Note</h2>
<p>You must go to the "Graphics" tab in your project, and add "Standard (Specular Setup)" to the always included shaders. This will make your next build take a while.</p>
<br />
<h2>Features</h2>
<ul>
<li>Material file loading</li>
<li>Runtime texture library supporting BMP, TGA, JPG, PNG, DDS, and CRN textures.</li>
<li>Decent import speed (~750k triangles in about 10 seconds)</li>
<li>Support for loading obj and mtl from stream, as well as overridable callback for texture loads</li>
<li>Works in editor, and during runtime</li>
<li>Loads triangles, quads, and ngons</li>
</ul>
</body>
</html>
\ No newline at end of file
fileFormatVersion: 2
guid: 127e58e7a0d489046a128e511967dea8
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
fileFormatVersion: 2
guid: 1e292531b556307479437e7763c8b31f
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
This diff is collapsed.
fileFormatVersion: 2
guid: df0f3cf90c6b7f24b99437ae8c8dc721
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
using Dummiesman;
using System.IO;
using UnityEngine;
public class ObjFromFile : MonoBehaviour
{
string objPath = string.Empty;
string error = string.Empty;
GameObject loadedObject;
void OnGUI() {
objPath = GUI.TextField(new Rect(0, 0, 256, 32), objPath);
GUI.Label(new Rect(0, 0, 256, 32), "Obj Path:");
if(GUI.Button(new Rect(256, 32, 64, 32), "Load File"))
{
//file path
if (!File.Exists(objPath))
{
error = "File doesn't exist.";
}else{
if(loadedObject != null)
Destroy(loadedObject);
loadedObject = new OBJLoader().Load(objPath);
error = string.Empty;
}
}
if(!string.IsNullOrWhiteSpace(error))
{
GUI.color = Color.red;
GUI.Box(new Rect(0, 64, 256 + 64, 32), error);
GUI.color = Color.white;
}
}
}
fileFormatVersion: 2
guid: e668e28abcfb0ae439f4ead584aba85b
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
This diff is collapsed.
fileFormatVersion: 2
guid: e96535515ee47d04589da7981f6650a2
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
using Dummiesman;
using System.IO;
using System.Text;
using UnityEngine;
public class ObjFromStream : MonoBehaviour {
void Start () {
//make www
var www = new WWW("https://people.sc.fsu.edu/~jburkardt/data/obj/lamp.obj");
while (!www.isDone)
System.Threading.Thread.Sleep(1);
//create stream and load
var textStream = new MemoryStream(Encoding.UTF8.GetBytes(www.text));
var loadedObj = new OBJLoader().Load(textStream);
}
}
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment