Files
ichni_Creator_Studio/Assets/Dreamteck/Utilities/MeshUtility.cs

743 lines
29 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using System;
namespace Dreamteck
{
public class MeshUtility
{
private static Vector3[] tan1 = new Vector3[0];
private static Vector3[] tan2 = new Vector3[0];
public static int[] GeneratePlaneTriangles(int x, int z, bool flip, int startTriangleIndex = 0, int startVertex = 0)
{
int nbFaces = x * (z - 1);
int[] triangles = new int[nbFaces * 6];
GeneratePlaneTriangles(ref triangles, x, z, flip);
return triangles;
}
public static int[] GeneratePlaneTriangles(ref int[] triangles, int x, int z, bool flip, int startTriangleIndex = 0, int startVertex = 0, bool reallocateArray = false)
{
int nbFaces = x * (z - 1);
if (reallocateArray && triangles.Length != nbFaces * 6)
{
if (startTriangleIndex > 0)
{
int[] newTris = new int[startTriangleIndex + nbFaces * 6];
for (int i = 0; i < startTriangleIndex; i++) newTris[i] = triangles[i];
triangles = newTris;
}
else triangles = new int[nbFaces * 6];
}
int g = x + 1;
int t = startTriangleIndex;
// for (int face = 0; face < nbFaces + z - 2; face++)
// {
// if ((float)(face + 1) % (float)g == 0f && face != 0) face++;
// if (flip)
// {
// triangles[t++] = face + x + 1 + startVertex;
// triangles[t++] = face + 1 + startVertex;
// triangles[t++] = face + startVertex;
// triangles[t++] = face + x + 1 + startVertex;
// triangles[t++] = face + x + 2 + startVertex;
// triangles[t++] = face + 1 + startVertex;
// }
// else
// {
// triangles[t++] = face + startVertex;
// triangles[t++] = face + 1 + startVertex;
// triangles[t++] = face + x + 1 + startVertex;
// triangles[t++] = face + 1 + startVertex;
// triangles[t++] = face + x + 2 + startVertex;
// triangles[t++] = face + x + 1 + startVertex;
// }
// }
int index = 0;
for (int row = 0; row < z - 1; row++)
{
for (int col = 0; col < x; col++)
{
int i = row * (x + 1) + col;
if (flip)
{
triangles[index++] = i + x + 1;
triangles[index++] = i + 1;
triangles[index++] = i;
triangles[index++] = i + x + 1;
triangles[index++] = i + x + 2;
triangles[index++] = i + 1;
}
else
{
triangles[index++] = i + startVertex;
triangles[index++] = i + 1 + startVertex;
triangles[index++] = i + x + 1 + startVertex;
triangles[index++] = i + 1 + startVertex;
triangles[index++] = i + x + 2 + startVertex;
triangles[index++] = i + x + 1 + startVertex;
}
}
}
return triangles;
}
public static void CalculateTangents(TS_Mesh mesh)
{
int vertexCount = mesh.vertexCount;
int triangleCount = mesh.triangles.Length / 3;
// 优化1数组重用与按需分配
if (tan1.Length < vertexCount)
{
tan1 = new Vector3[vertexCount];
tan2 = new Vector3[vertexCount];
}
else
{
// 重用数组但需要清零
Array.Clear(tan1, 0, vertexCount);
Array.Clear(tan2, 0, vertexCount);
}
// 优化2避免重复访问属性
Vector3[] vertices = mesh.vertices;
Vector2[] uvs = mesh.uv;
Vector3[] normals = mesh.normals;
int[] triangles = mesh.triangles;
// 优化3预先检查数组长度
if (uvs.Length < vertexCount || normals.Length < vertexCount)
{
Debug.LogError("UV or Normal array size mismatch");
return;
}
// 优化4缓存变量减少重复计算
Vector3 edge1 = Vector3.zero;
Vector3 edge2 = Vector3.zero;
Vector2 uvEdge1 = Vector2.zero;
Vector2 uvEdge2 = Vector2.zero;
// 优化5循环展开减少分支预测失败
for (int i = 0; i < triangleCount; i++)
{
int triIndex = i * 3;
int i1 = triangles[triIndex];
int i2 = triangles[triIndex + 1];
int i3 = triangles[triIndex + 2];
// 计算边向量
edge1.x = vertices[i2].x - vertices[i1].x;
edge1.y = vertices[i2].y - vertices[i1].y;
edge1.z = vertices[i2].z - vertices[i1].z;
edge2.x = vertices[i3].x - vertices[i1].x;
edge2.y = vertices[i3].y - vertices[i1].y;
edge2.z = vertices[i3].z - vertices[i1].z;
// 计算UV差
uvEdge1.x = uvs[i2].x - uvs[i1].x;
uvEdge1.y = uvs[i2].y - uvs[i1].y;
uvEdge2.x = uvs[i3].x - uvs[i1].x;
uvEdge2.y = uvs[i3].y - uvs[i1].y;
// 优化6避免除零检查的分支预测
float div = uvEdge1.x * uvEdge2.y - uvEdge2.x * uvEdge1.y;
float r = Mathf.Abs(div) < Mathf.Epsilon ? 0f : 1f / div;
// 计算切线向量
Vector3 sdir = new Vector3(
(uvEdge2.y * edge1.x - uvEdge1.y * edge2.x) * r,
(uvEdge2.y * edge1.y - uvEdge1.y * edge2.y) * r,
(uvEdge2.y * edge1.z - uvEdge1.y * edge2.z) * r
);
Vector3 tdir = new Vector3(
(uvEdge1.x * edge2.x - uvEdge2.x * edge1.x) * r,
(uvEdge1.x * edge2.y - uvEdge2.x * edge1.y) * r,
(uvEdge1.x * edge2.z - uvEdge2.x * edge1.z) * r
);
// 累加到顶点
tan1[i1] += sdir;
tan1[i2] += sdir;
tan1[i3] += sdir;
tan2[i1] += tdir;
tan2[i2] += tdir;
tan2[i3] += tdir;
}
// 优化7确保切线数组已分配
if (mesh.tangents == null || mesh.tangents.Length != vertexCount)
{
mesh.tangents = new Vector4[vertexCount];
}
// 优化8手动Gram-Schmidt正交化
for (int i = 0; i < vertexCount; i++)
{
Vector3 n = normals[i];
Vector3 t = tan1[i];
// 手动正交化 (比Vector3.OrthoNormalize更高效)
t = (t - n * Vector3.Dot(n, t)).normalized;
// 计算副法线方向
Vector3 b = Vector3.Cross(n, t);
float w = (Vector3.Dot(b, tan2[i]) < 0.0f) ? -1.0f : 1.0f;
mesh.tangents[i] = new Vector4(t.x, t.y, t.z, w);
}
}
public static void MakeDoublesided(Mesh input)
{
Vector3[] vertices = input.vertices;
Vector3[] normals = input.normals;
Vector2[] uvs = input.uv;
Color[] colors = input.colors;
int[] triangles = input.triangles;
List<int[]> submeshes = new List<int[]>();
for (int i = 0; i < input.subMeshCount; i++) submeshes.Add(input.GetTriangles(i));
Vector3[] newVertices = new Vector3[vertices.Length * 2];
Vector3[] newNormals = new Vector3[normals.Length * 2];
Vector2[] newUvs = new Vector2[uvs.Length * 2];
Color[] newColors = new Color[colors.Length * 2];
int[] newTris = new int[triangles.Length * 2];
List<int[]> newSubmeshes = new List<int[]>();
for (int i = 0; i < submeshes.Count; i++)
{
newSubmeshes.Add(new int[submeshes[i].Length * 2]);
submeshes[i].CopyTo(newSubmeshes[i], 0);
}
for (int i = 0; i < vertices.Length; i++)
{
newVertices[i] = vertices[i];
newNormals[i] = normals[i];
newUvs[i] = uvs[i];
if (colors.Length > i) newColors[i] = colors[i];
newVertices[i + vertices.Length] = vertices[i];
newNormals[i + vertices.Length] = -normals[i];
newUvs[i + vertices.Length] = uvs[i];
if (colors.Length > i) newColors[i + vertices.Length] = colors[i];
}
for (int i = 0; i < triangles.Length; i += 3)
{
int index1 = triangles[i];
int index2 = triangles[i + 1];
int index3 = triangles[i + 2];
newTris[i] = index1;
newTris[i + 1] = index2;
newTris[i + 2] = index3;
newTris[i + triangles.Length] = index3 + vertices.Length;
newTris[i + triangles.Length + 1] = index2 + vertices.Length;
newTris[i + triangles.Length + 2] = index1 + vertices.Length;
}
for (int i = 0; i < submeshes.Count; i++)
{
for (int n = 0; n < submeshes[i].Length; n += 3)
{
int index1 = submeshes[i][n];
int index2 = submeshes[i][n + 1];
int index3 = submeshes[i][n + 2];
newSubmeshes[i][n] = index1;
newSubmeshes[i][n + 1] = index2;
newSubmeshes[i][n + 2] = index3;
newSubmeshes[i][n + submeshes[i].Length] = index3 + vertices.Length;
newSubmeshes[i][n + submeshes[i].Length + 1] = index2 + vertices.Length;
newSubmeshes[i][n + submeshes[i].Length + 2] = index1 + vertices.Length;
}
}
input.vertices = newVertices;
input.normals = newNormals;
input.uv = newUvs;
input.colors = newColors;
input.triangles = newTris;
for (int i = 0; i < newSubmeshes.Count; i++) input.SetTriangles(newSubmeshes[i], i);
}
public static void MakeDoublesided(TS_Mesh input)
{
Vector3[] vertices = input.vertices;
Vector3[] normals = input.normals;
Vector2[] uvs = input.uv;
Color[] colors = input.colors;
int[] triangles = input.triangles;
List<int[]> submeshes = input.subMeshes;
Vector3[] newVertices = new Vector3[vertices.Length * 2];
Vector3[] newNormals = new Vector3[normals.Length * 2];
Vector2[] newUvs = new Vector2[uvs.Length * 2];
Color[] newColors = new Color[colors.Length * 2];
int[] newTris = new int[triangles.Length * 2];
List<int[]> newSubmeshes = new List<int[]>();
for (int i = 0; i < submeshes.Count; i++)
{
newSubmeshes.Add(new int[submeshes[i].Length * 2]);
submeshes[i].CopyTo(newSubmeshes[i], 0);
}
for (int i = 0; i < vertices.Length; i++)
{
newVertices[i] = vertices[i];
newNormals[i] = normals[i];
newUvs[i] = uvs[i];
if (colors.Length > i) newColors[i] = colors[i];
newVertices[i + vertices.Length] = vertices[i];
newNormals[i + vertices.Length] = -normals[i];
newUvs[i + vertices.Length] = uvs[i];
if (colors.Length > i) newColors[i + vertices.Length] = colors[i];
}
for (int i = 0; i < triangles.Length; i += 3)
{
int index1 = triangles[i];
int index2 = triangles[i + 1];
int index3 = triangles[i + 2];
newTris[i] = index1;
newTris[i + 1] = index2;
newTris[i + 2] = index3;
newTris[i + triangles.Length] = index3 + vertices.Length;
newTris[i + triangles.Length + 1] = index2 + vertices.Length;
newTris[i + triangles.Length + 2] = index1 + vertices.Length;
}
for (int i = 0; i < submeshes.Count; i++)
{
for (int n = 0; n < submeshes[i].Length; n += 3)
{
int index1 = submeshes[i][n];
int index2 = submeshes[i][n + 1];
int index3 = submeshes[i][n + 2];
newSubmeshes[i][n] = index1;
newSubmeshes[i][n + 1] = index2;
newSubmeshes[i][n + 2] = index3;
newSubmeshes[i][n + submeshes[i].Length] = index3 + vertices.Length;
newSubmeshes[i][n + submeshes[i].Length + 1] = index2 + vertices.Length;
newSubmeshes[i][n + submeshes[i].Length + 2] = index1 + vertices.Length;
}
}
input.vertices = newVertices;
input.normals = newNormals;
input.uv = newUvs;
input.colors = newColors;
input.triangles = newTris;
input.subMeshes = newSubmeshes;
}
public static void MakeDoublesidedHalf(TS_Mesh input)
{
int vertexHalf = input.vertices.Length / 2;
int trisHalf = input.triangles.Length / 2;
for (int i = 0; i < vertexHalf; i++)
{
input.vertices[i + vertexHalf] = input.vertices[i];
if (input.normals.Length > i) input.normals[i + vertexHalf] = -input.normals[i];
if (input.tangents.Length > i) input.tangents[i + vertexHalf] = input.tangents[i];
if (input.uv.Length > i) input.uv[i + vertexHalf] = input.uv[i];
if (input.uv2.Length > i) input.uv2[i + vertexHalf] = input.uv2[i];
if (input.uv3.Length > i) input.uv3[i + vertexHalf] = input.uv3[i];
if (input.uv4.Length > i) input.uv4[i + vertexHalf] = input.uv4[i];
if (input.colors.Length > i) input.colors[i + vertexHalf] = input.colors[i];
}
for (int i = 0; i < trisHalf; i += 3)
{
input.triangles[i + trisHalf + 2] = input.triangles[i] + vertexHalf;
input.triangles[i + trisHalf + 1] = input.triangles[i + 1] + vertexHalf;
input.triangles[i + trisHalf] = input.triangles[i + 2] + vertexHalf;
}
for (int i = 0; i < input.subMeshes.Count; i++)
{
trisHalf = input.subMeshes[i].Length / 2;
for (int n = 0; n < trisHalf; n += 3)
{
input.subMeshes[i][n + trisHalf + 2] = input.subMeshes[i][n] + vertexHalf;
input.subMeshes[i][n + trisHalf + 1] = input.subMeshes[i][n + 1] + vertexHalf;
input.subMeshes[i][n + trisHalf] = input.subMeshes[i][n + 2] + vertexHalf;
}
}
}
public static void TransformMesh(TS_Mesh input, Matrix4x4 matrix)
{
if (input.vertices == null || input.normals == null) return;
for (int i = 0; i < input.vertices.Length; i++)
{
input.vertices[i] = matrix.MultiplyPoint3x4(input.vertices[i]);
input.normals[i] = matrix.MultiplyVector(input.normals[i]);
}
}
public static void TransformMesh(Mesh input, Matrix4x4 matrix)
{
Vector3[] vertices = input.vertices;
Vector3[] normals = input.vertices;
if (input.vertices == null || input.normals == null) return;
for (int i = 0; i < input.vertices.Length; i++)
{
vertices[i] = matrix.MultiplyPoint3x4(vertices[i]);
normals[i] = matrix.MultiplyVector(normals[i]);
}
input.vertices = vertices;
input.normals = normals;
}
public static void TransformVertices(Vector3[] vertices, Matrix4x4 matrix)
{
for (int i = 0; i < vertices.Length; i++)
{
vertices[i] = matrix.MultiplyPoint3x4(vertices[i]);
}
}
public static void TransformNormals(Vector3[] normals, Matrix4x4 matrix)
{
for (int i = 0; i < normals.Length; i++)
{
normals[i] = matrix.MultiplyVector(normals[i]);
}
}
public static string ToOBJString(Mesh mesh, Material[] materials)
{
int numVertices = 0;
if (mesh == null)
{
return "####Error####";
}
StringBuilder sb = new StringBuilder();
sb.Append("g " + mesh.name + "\n");
foreach (Vector3 v in mesh.vertices)
{
numVertices++;
sb.Append(string.Format("v {0} {1} {2}\n", -v.x, v.y, v.z));
}
sb.Append("\n");
foreach (Vector3 n in mesh.normals)
{
sb.Append(string.Format("vn {0} {1} {2}\n", -n.x, n.y, n.z));
}
sb.Append("\n");
foreach (Vector3 v in mesh.uv)
{
sb.Append(string.Format("vt {0} {1}\n", v.x, v.y));
}
sb.Append("\n");
foreach (Vector2 v in mesh.uv2)
{
sb.Append(string.Format("vt2 {0} {1}\n", v.x, v.y));
}
sb.Append("\n");
foreach (Vector2 v in mesh.uv3)
{
sb.Append(string.Format("vt2 {0} {1}\n", v.x, v.y));
}
sb.Append("\n");
foreach (Color c in mesh.colors)
{
sb.Append(string.Format("vc {0} {1} {2} {3}\n", c.r, c.g, c.b, c.a));
}
for (int material = 0; material < mesh.subMeshCount; material++)
{
sb.Append("\n");
sb.Append("usemtl ").Append(materials[material].name).Append("\n");
sb.Append("usemap ").Append(materials[material].name).Append("\n");
int[] triangles = mesh.GetTriangles(material);
for (int i = 0; i < triangles.Length; i += 3)
{
sb.Append(string.Format("f {2}/{2}/{2} {1}/{1}/{1} {0}/{0}/{0}\n",
triangles[i] + 1, triangles[i + 1] + 1, triangles[i + 2] + 1));
}
}
return sb.ToString().Replace(',', '.');
}
public static Mesh Copy(Mesh input)
{
Mesh copy = new Mesh();
copy.name = input.name;
copy.vertices = input.vertices;
copy.normals = input.normals;
copy.colors = input.colors;
copy.uv = input.uv;
copy.uv2 = input.uv2;
copy.uv3 = input.uv3;
copy.uv4 = input.uv4;
copy.tangents = input.tangents;
copy.boneWeights = input.boneWeights;
copy.bindposes = input.bindposes;
copy.triangles = input.triangles;
copy.subMeshCount = input.subMeshCount;
for (int i = 0; i < input.subMeshCount; i++)
{
copy.SetTriangles(input.GetTriangles(i), i);
}
return copy;
}
public static void Triangulate(Vector2[] points, ref int[] output)
{
List<int> indices = new List<int>();
int pointsLength = points.Length;
if (pointsLength < 3)
{
output = new int[0];
return;
}
int[] V = new int[pointsLength];
if (Area(points, pointsLength) > 0)
{
for (int v = 0; v < pointsLength; v++)
V[v] = v;
}
else
{
for (int v = 0; v < pointsLength; v++)
V[v] = (pointsLength - 1) - v;
}
int nv = pointsLength;
int count = 2 * nv;
for (int m = 0, v = nv - 1; nv > 2;)
{
if ((count--) <= 0)
{
if (output.Length != indices.Count) output = new int[indices.Count];
indices.CopyTo(output, 0);
return;
}
int u = v;
if (nv <= u)
u = 0;
v = u + 1;
if (nv <= v)
v = 0;
int w = v + 1;
if (nv <= w)
w = 0;
if (Snip(points, u, v, w, nv, V))
{
int a, b, c, s, t;
a = V[u];
b = V[v];
c = V[w];
indices.Add(c);
indices.Add(b);
indices.Add(a);
m++;
for (s = v, t = v + 1; t < nv; s++, t++)
V[s] = V[t];
nv--;
count = 2 * nv;
}
}
indices.Reverse();
if (output.Length != indices.Count) output = new int[indices.Count];
indices.CopyTo(output, 0);
}
public static void FlipTriangles(ref int[] triangles)
{
for (int i = 0; i < triangles.Length; i += 3)
{
int temp = triangles[i];
triangles[i] = triangles[i + 2];
triangles[i + 2] = temp;
}
}
public static void FlipFaces(TS_Mesh input)
{
for (int i = 0; i < input.subMeshes.Count; i++)
{
int[] array = input.subMeshes[i];
FlipTriangles(ref array);
}
FlipTriangles(ref input.triangles);
for (int i = 0; i < input.normals.Length; i++)
{
input.normals[i] *= -1f;
}
}
public static void BreakMesh(Mesh input, bool keepNormals = true)
{
Vector3[] newVertices = new Vector3[input.triangles.Length];
Vector3[] newNormals = new Vector3[newVertices.Length];
Vector2[] newUVs = new Vector2[newVertices.Length];
Vector4[] newTangents = new Vector4[newVertices.Length];
Color[] newColors = new Color[newVertices.Length];
BoneWeight[] newBoneWeights = new BoneWeight[newVertices.Length];
Vector3[] oldVertices = input.vertices;
Vector2[] oldUvs = input.uv;
Vector3[] oldNormals = input.normals;
Vector4[] oldTangents = input.tangents;
Color[] oldColors = input.colors;
BoneWeight[] oldBoneWeights = input.boneWeights;
if (oldColors.Length != oldVertices.Length)
{
oldColors = new Color[oldVertices.Length];
for (int i = 0; i < oldColors.Length; i++) oldColors[i] = Color.white;
}
List<int[]> submeshList = new List<int[]>();
int submeshes = input.subMeshCount;
int vertIndex = 0;
for (int i = 0; i < submeshes; i++)
{
int[] submesh = input.GetTriangles(i);
for (int n = 0; n < submesh.Length; n += 3)
{
newVertices[vertIndex] = oldVertices[submesh[n]];
newVertices[vertIndex + 1] = oldVertices[submesh[n + 1]];
newVertices[vertIndex + 2] = oldVertices[submesh[n + 2]];
if (oldNormals.Length > submesh[n + 2])
{
if (!keepNormals)
{
newNormals[vertIndex] = newNormals[vertIndex + 1] = newNormals[vertIndex + 2] = (oldNormals[submesh[n]] + oldNormals[submesh[n + 1]] + oldNormals[submesh[n + 2]]).normalized;
}
else
{
newNormals[vertIndex] = oldNormals[submesh[n]];
newNormals[vertIndex + 1] = oldNormals[submesh[n + 1]];
newNormals[vertIndex + 2] = oldNormals[submesh[n + 2]];
}
}
if (oldColors.Length > submesh[n + 2])
newColors[vertIndex] = newColors[vertIndex + 1] = newColors[vertIndex + 2] = (oldColors[submesh[n]] + oldColors[submesh[n + 1]] + oldColors[submesh[n + 2]]) / 3f;
if (oldUvs.Length > submesh[n + 2])
{
newUVs[vertIndex] = oldUvs[submesh[n]];
newUVs[vertIndex + 1] = oldUvs[submesh[n + 1]];
newUVs[vertIndex + 2] = oldUvs[submesh[n + 2]];
}
if (oldTangents.Length > submesh[n + 2])
{
newTangents[vertIndex] = oldTangents[submesh[n]];
newTangents[vertIndex + 1] = oldTangents[submesh[n + 1]];
newTangents[vertIndex + 2] = oldTangents[submesh[n + 2]];
}
if (oldBoneWeights.Length > submesh[n + 2])
{
newBoneWeights[vertIndex] = oldBoneWeights[submesh[n]];
newBoneWeights[vertIndex + 1] = oldBoneWeights[submesh[n + 1]];
newBoneWeights[vertIndex + 2] = oldBoneWeights[submesh[n + 2]];
}
submesh[n] = vertIndex;
submesh[n + 1] = vertIndex + 1;
submesh[n + 2] = vertIndex + 2;
vertIndex += 3;
}
submeshList.Add(submesh);
}
input.vertices = newVertices;
input.normals = newNormals;
input.colors = newColors;
input.uv = newUVs;
input.tangents = newTangents;
input.subMeshCount = submeshList.Count;
input.boneWeights = newBoneWeights;
for (int i = 0; i < submeshList.Count; i++)
{
input.SetTriangles(submeshList[i], i);
}
}
private static float Area(Vector2[] points, int maxCount)
{
float A = 0.0f;
for (int p = maxCount - 1, q = 0; q < maxCount; p = q++)
{
Vector2 pval = points[p];
Vector2 qval = points[q];
A += pval.x * qval.y - qval.x * pval.y;
}
return (A * 0.5f);
}
private static bool Snip(Vector2[] points, int u, int v, int w, int n, int[] V)
{
int p;
Vector2 A = points[V[u]];
Vector2 B = points[V[v]];
Vector2 C = points[V[w]];
if (Mathf.Epsilon > (((B.x - A.x) * (C.y - A.y)) - ((B.y - A.y) * (C.x - A.x))))
return false;
for (p = 0; p < n; p++)
{
if ((p == u) || (p == v) || (p == w))
continue;
Vector2 P = points[V[p]];
if (InsideTriangle(A, B, C, P))
return false;
}
return true;
}
private static bool InsideTriangle(Vector2 A, Vector2 B, Vector2 C, Vector2 P)
{
float ax, ay, bx, by, cx, cy, apx, apy, bpx, bpy, cpx, cpy;
float cCROSSap, bCROSScp, aCROSSbp;
ax = C.x - B.x; ay = C.y - B.y;
bx = A.x - C.x; by = A.y - C.y;
cx = B.x - A.x; cy = B.y - A.y;
apx = P.x - A.x; apy = P.y - A.y;
bpx = P.x - B.x; bpy = P.y - B.y;
cpx = P.x - C.x; cpy = P.y - C.y;
aCROSSbp = ax * bpy - ay * bpx;
cCROSSap = cx * apy - cy * apx;
bCROSScp = bx * cpy - by * cpx;
return ((aCROSSbp >= 0.0f) && (bCROSScp >= 0.0f) && (cCROSSap >= 0.0f));
}
}
}