private void CalStrokeUpperAndLowerPoint(StrokePoint[] spAry) { Vector3 widthVec = new Vector3(0.0f, 0.0f, 1.0f); Vector3 forward = new Vector3(1.0f, 0.0f, 0.0f); Vector3 upward = new Vector3(0.0f, 1.0f, 0.0f); // The first of the point forward = spAry[1].centerPoint - spAry[0].centerPoint; widthVec = Vector3.Cross(upward, forward).normalized; spAry[0].upperPoint = spAry[0].centerPoint - spAry[0].weight * widthVec; spAry[0].lowerPoint = spAry[0].centerPoint + spAry[0].weight * widthVec; for (int i = 1; i < spAry.Length - 1; i++) { StrokePoint prevPoint = spAry[i - 1]; StrokePoint currPoint = spAry[i]; StrokePoint nextPoint = spAry[i + 1]; forward = nextPoint.centerPoint - prevPoint.centerPoint; widthVec = Vector3.Cross(upward, forward).normalized; spAry[i].upperPoint = currPoint.centerPoint - currPoint.weight * widthVec; spAry[i].lowerPoint = currPoint.centerPoint + currPoint.weight * widthVec; } // The last of the point spAry[spAry.Length - 1].upperPoint = spAry[spAry.Length - 1].centerPoint - spAry[spAry.Length - 1].weight * widthVec; spAry[spAry.Length - 1].lowerPoint = spAry[spAry.Length - 1].centerPoint + spAry[spAry.Length - 1].weight * widthVec; }
public void AddSkeletonVertex(Vector3 _targetPoint, float _weight) { StrokePoint sp = new StrokePoint(); sp.centerPoint = _targetPoint; sp.weight = _weight; m_StrokeSkeletonPointList.Add(sp); }
/// <summary> /// Called once per rendered frame. /// </summary> /// void Update() { if (actionSave.IsActivated()) { // save to file string baseFilename = "StrokeExport_" + System.DateTime.Now.ToString("yyyyMMdd_HHmmss"); SaveStrokeData(baseFilename); Debug.Log("Exported strokes to " + baseFilename); } if (actionReset.IsActivated()) { // clear all strokes ClearStrokes(); } if ((strokeMesh == null) && actionUndo.IsActivated()) { // only allow undo when not painting UndoLastStroke(); } // read size/colour buttons float sizeChange = actionSize.GetValue(); float colourChange = actionColour.GetValue(); // only allow one of the actions if (Mathf.Abs(sizeChange) > Mathf.Abs(colourChange)) { colourChange = 0; } else { sizeChange = 0; } // process changing colour if (colourChange != 0) { strokeColourIdx += colourChange * Time.deltaTime * colourChangeSpeed; strokeColourIdx = Mathf.Repeat(strokeColourIdx, 1); // force creation of new material strokeMaterials = null; if (colourIndicator != null) { colourIndicator.gameObject.SetActive(true); Quaternion q = Quaternion.AngleAxis(-strokeColourIdx * 360, Vector3.forward); colourIndicator.localRotation = q; strokeIndicatorTimer = Time.time + 1; // hide indicator 1s from now } } else { // hide colour indicator after timeout if ((colourIndicator != null) && (Time.time > strokeIndicatorTimer)) { colourIndicator.gameObject.SetActive(false); } } strokeColour = colourPalette.GetPixelBilinear(strokeColourIdx, 0.5f); // process size change if (sizeChange != 0) { // change brush size sizeFactor += sizeChange * Time.deltaTime; sizeFactor = Mathf.Clamp(sizeFactor, 0, 1); } strokeWidth = Mathf.Lerp(strokeWidthMinimum, strokeWidthMaximum, sizeFactor); if (trailRenderer != null) { trailRenderer.startWidth = strokeWidth; foreach (Material m in trailRenderer.materials) { m.SetColor("_Color", strokeColour); m.SetColor("_TintColor", strokeColour); m.SetColor("_EmissionColor", strokeColour); } } if (colourIndicatorMesh != null) { // is there a mesh rendering the active colour? colourIndicatorMesh.material.color = strokeColour; } if (brushObject != null) { brushObject.localScale = new Vector3(brushObject.localScale.x, strokeWidth, brushObject.localScale.z); Renderer renderer = brushObject.GetComponent <Renderer>(); foreach (Material m in renderer.materials) { m.SetColor("_Color", strokeColour); m.SetColor("_TintColor", strokeColour); m.SetColor("_EmissionColor", strokeColour); } } // handle paint button bool painting = actionPaint.IsActive(); if (painting && (strokeMesh == null)) { // start data structure activeStroke = new Stroke(); activeStroke.startTime = Time.time; // start new mesh for the stroke and add to common container activeStroke.gameObject = new GameObject(); activeStroke.gameObject.name = string.Format("Stroke_{0:D3}", strokeIndex); activeStroke.gameObject.transform.parent = strokeContainer.transform; // create new material? if (strokeMaterials == null) { Material mat = Instantiate <Material>(strokeBaseMaterial); mat.name = string.Format("StrokeMaterial_{0:D3}", strokeMaterialIndex); mat.color = strokeColour; strokeMaterials = new Material[2]; strokeMaterials[0] = mat; strokeMaterials[1] = mat; strokeMaterialIndex++; } // create renderer MeshRenderer renderer = activeStroke.gameObject.AddComponent <MeshRenderer>(); renderer.receiveShadows = true; renderer.shadowCastingMode = UnityEngine.Rendering.ShadowCastingMode.TwoSided; renderer.materials = strokeMaterials; // create mesh strokeMesh = activeStroke.gameObject.AddComponent <MeshFilter>().mesh; strokeMesh.MarkDynamic(); // this is going to change a lot strokeMesh.subMeshCount = 2; strokePoints = new List <Vector3>(); vertices = new List <Vector3>(); normals = new List <Vector3>(); //uvCoords = new List<Vector2>(); topology = new List <int>(); topologyBack = new List <int>(); lastPoint = new Vector3(); strokeIndex++; if (spraySound != null) { spraySound.loop = true; spraySound.Play(); } } else if (!painting && (strokeMesh != null)) { // stop painting strokeMesh = null; // save last stroke activeStroke.endTime = Time.time; strokeList.Add(activeStroke); activeStroke = null; if (spraySound != null) { spraySound.Stop(); } } // stroke is being constructed: handle last point if (strokeMesh != null) { Vector3 tipPos = this.transform.position; if (Vector3.Distance(lastPoint, tipPos) > minimumSegmentSize) { AddPoint(tipPos, this.transform.up); lastPoint = tipPos; } else { SetLastPoint(tipPos, this.transform.up); } } // record stroke data if (activeStroke != null) { StrokePoint p = new StrokePoint(); p.position = this.transform.position; p.orientation = this.transform.rotation; p.strokeSize = strokeWidth; p.strokeColour = strokeColour; p.timestamp = Time.time; activeStroke.points.Add(p); } }
public MeshRenderer CreateStrokeMesh(ref Stroke stroke, ref GameObject go, int stepSize) { // create renderer MeshRenderer renderer = go.AddComponent <MeshRenderer>(); // create mesh Mesh strokeMesh = go.AddComponent <MeshFilter>().mesh; strokeMesh.subMeshCount = 2; List <Vector3> vertices = new List <Vector3>(); List <Vector3> normals = new List <Vector3>(); //uvCoords = new List<Vector2>(); List <int> topology = new List <int>(); List <int> topologyBack = new List <int>(); int length = stroke.points.Count; int lIdx = 0; Vector3 lastPos = Vector3.zero; for (int pIdx = 0; pIdx < length + stepSize; pIdx += stepSize) { StrokePoint p = stroke.points[Mathf.Min(pIdx, length - 1)]; Vector3 pos = p.position; // avoid very short (or zero length) segments if (stepSize == 1) { Vector3 delta = pos - lastPos; if (delta.magnitude < minimumSegmentSize) { continue; } lastPos = pos; } // clauclate normal Vector3 up = p.orientation * Vector3.up; Vector3 dir = (pIdx > 0) ? (pos - stroke.points[pIdx - stepSize].position) : pos; dir.Normalize(); Vector3 normal = Vector3.Cross(up, dir); // add top/bottom vertices for front/back up *= p.strokeSize * 0.5f; vertices.Add(pos + up); vertices.Add(pos + up); vertices.Add(pos - up); vertices.Add(pos - up); // add two normals each for front/back (but not for first point) normals.Add(normal); normals.Add(-normal); normals.Add(normal); normals.Add(-normal); if (lIdx == 1) { // add normals for first point = equal to second point normals[0] = normal; normals[1] = -normal; normals[2] = normal; normals[3] = -normal; } if (lIdx > 0) { // more than one point > start building topology // Front: 2 6 Back: 3 7 // x x x x // 0 4 1 5 int idx = (lIdx + 1) * 4; // two points > idx = 8 topology.Add(idx - 8); topology.Add(idx - 4); topology.Add(idx - 6); // 0 > 4 > 2 topology.Add(idx - 6); topology.Add(idx - 4); topology.Add(idx - 2); // 2 > 4 > 6 // backface > reverse direction topologyBack.Add(idx - 7); topologyBack.Add(idx - 5); topologyBack.Add(idx - 3); // 1 > 3 > 5 topologyBack.Add(idx - 5); topologyBack.Add(idx - 1); topologyBack.Add(idx - 3); // 3 > 7 > 5 } lIdx++; } strokeMesh.SetVertices(vertices); strokeMesh.SetNormals(normals); strokeMesh.SetTriangles(topology, 0); strokeMesh.SetTriangles(topologyBack, 1); strokeMesh.RecalculateBounds(); return(renderer); }
private void updateMeshRepresentation() { // Index + Thumb points -> StrokePoints in a StrokeObject. if (_strokeObj == null) { _strokeObj = gameObject.AddComponent <StrokeObject>(); } // Get color via Ucon wiring. var brushColor = colorChannelIn.Get(); _strokeObj.Clear(); for (int i = 1; i < indexPoints.Count; i++) { var avgPos = (indexPoints[i] + thumbPoints[i]) * 0.5f; var prevAvgPos = (indexPoints[i - 1] + thumbPoints[i - 1]) * 0.5f; var right = (indexPoints[i] - thumbPoints[i]).normalized; var forward = (avgPos - prevAvgPos).normalized; if ((avgPos - prevAvgPos).sqrMagnitude < 1e-7f) { continue; } var up = Vector3.Cross(right, forward).normalized; var rot = Quaternion.LookRotation(forward, up); var radius = (indexPoints[i] - thumbPoints[i]).magnitude * 0.3f; var strokePoint = new StrokePoint() { pose = new Pose(avgPos, rot), color = brushColor, radius = radius, temp_refFrame = Matrix4x4.identity }; _strokeObj.Add(strokePoint); } // StrokeObjects -> PolyMesh. fillPolyMeshObject.polyMesh.Clear(); var positions = Pool <List <Vector3> > .Spawn(); positions.Clear(); var polygons = Pool <List <Polygon> > .Spawn(); polygons.Clear(); var smoothEdges = Pool <List <Edge> > .Spawn(); smoothEdges.Clear(); var colors = Pool <List <Color> > .Spawn(); colors.Clear(); try { strokePolyMesher.FillPolyMeshData(_strokeObj, positions, polygons, smoothEdges, colors); fillPolyMeshObject.polyMesh.Fill(positions, polygons, smoothEdges, colors); } finally { positions.Clear(); Pool <List <Vector3> > .Recycle(positions); polygons.Clear(); Pool <List <Polygon> > .Recycle(polygons); smoothEdges.Clear(); Pool <List <Edge> > .Recycle(smoothEdges); colors.Clear(); Pool <List <Color> > .Recycle(colors); } // Refresh the Unity mesh representation of the PolyMeshObject. fillPolyMeshObject.RefreshUnityMesh(); }
public bool LoadStrokeData(string baseFilename) { bool success = false; if (!baseFilename.Contains(".csv")) { baseFilename = baseFilename + ".csv"; } using (StreamReader sr = new StreamReader(baseFilename)) { // read header string header = sr.ReadLine(); success = header.Equals("time,strokeIdx,pointIdx,posX,posY,posZ,rotX,rotY,rotZ,rotW,width,colR,colG,colB,colA"); if (success) { int strokeIdx = 0; string line; Stroke stroke = null; average = Vector3.zero; int pointCount = 0; this.Clear(); while ((line = sr.ReadLine()) != null) { string[] parts = line.Split(','); // check stroke index int sIdx = int.Parse(parts[1]); if (sIdx != strokeIdx) { // new stroke starts if (stroke != null) { stroke.startTime = stroke.points[0].timestamp; stroke.endTime = stroke.points[stroke.points.Count - 1].timestamp; this.Add(stroke); } strokeIdx = sIdx; stroke = new Stroke(); } // read point data StrokePoint p = new StrokePoint(); p.timestamp = float.Parse(parts[0]); // [1] stroke idx // [2] point idx p.position.x = float.Parse(parts[3]); p.position.y = float.Parse(parts[4]); p.position.z = float.Parse(parts[5]); p.orientation.x = float.Parse(parts[6]); p.orientation.y = float.Parse(parts[7]); p.orientation.z = float.Parse(parts[8]); p.orientation.w = float.Parse(parts[9]); p.strokeSize = float.Parse(parts[10]); p.strokeColour.r = float.Parse(parts[11]); p.strokeColour.g = float.Parse(parts[12]); p.strokeColour.b = float.Parse(parts[13]); p.strokeColour.a = float.Parse(parts[14]); stroke.points.Add(p); bounds.Encapsulate(p.position); average += p.position; pointCount++; } // add last stroke if (stroke != null) { stroke.startTime = stroke.points[0].timestamp; stroke.endTime = stroke.points[stroke.points.Count - 1].timestamp; this.Add(stroke); } average /= pointCount; } } return(success); }
void Update() { if (Input.GetMouseButtonDown(0)) { currentStroke = StartNewStroke(); strokes.Add(currentStroke); GameObject goStrokeMesh = Instantiate(strokeMeshPrefab); Mesh strokeMesh = new Mesh(); strokeMesh.MarkDynamic(); goStrokeMesh.GetComponent<MeshFilter>().mesh = strokeMesh; strokeMeshes.Add(strokeMesh); } else if (Input.GetMouseButtonUp(0)) { EndStroke(); currentStroke = null; } if (Input.GetMouseButton(0) && Input.mousePosition != prevMousePosition) { Vector3 screenPoint = Input.mousePosition; screenPoint.z = Camera.main.nearClipPlane; StrokePoint sp = new StrokePoint(Camera.main.ScreenToWorldPoint(screenPoint), Time.realtimeSinceStartup); currentStroke.AddPoint(sp); //Debug.Log (Input.mousePosition); //Debug.Log(Camera.main.ScreenToWorldPoint(screenPoint)); // Rebuild mesh for this stroke. Mesh strokeMesh = (Mesh)strokeMeshes[strokeMeshes.Count - 1]; strokeMesh.Clear (); Vector3[] vertices = new Vector3[currentStroke.CountPoints]; Vector2[] uvs = new Vector2[currentStroke.CountPoints]; int[] indices = new int[currentStroke.CountPoints]; for (int i = 0; i < currentStroke.CountPoints; ++ i) { vertices[i] = currentStroke.getPoint(i); uvs[i] = Vector2.zero; indices[i] = i; } strokeMesh.vertices = vertices; strokeMesh.uv = uvs; strokeMesh.SetIndices(indices, MeshTopology.LineStrip, 0); } foreach (Stroke s in strokes) { for (int iSegment = 0; iSegment < s.CountSegments; ++iSegment) { Vector3 p0; Vector3 p1; s.getSegmentPoints(iSegment, out p0, out p1); //Debug.DrawLine(p0, p1, Color.black); } } prevMousePosition = Input.mousePosition; }
public void AddPoint(StrokePoint sp) { points.Add(sp); }