public void CalculateVerticesLoop(IvyParameters ivyParameters, RTIvyContainer rtIvyContainer, GameObject ivyGO) { float angle = 0f; if (!ivyParameters.halfgeom) { angle = Mathf.Rad2Deg * 2 * Mathf.PI / ivyParameters.sides; } else { angle = Mathf.Rad2Deg * 2 * Mathf.PI / ivyParameters.sides / 2; } Vector3 vertex = Vector3.zero; Vector3 normal = Vector3.zero; Vector2 uv = Vector2.zero; Quaternion quat = Quaternion.identity; Vector3 direction = Vector3.zero; Quaternion inverseIvyGORotation = Quaternion.Inverse(ivyGO.transform.rotation); for (int i = 0; i < ivyParameters.sides + 1; i++) { quat = Quaternion.AngleAxis(angle * i, axis); direction = quat * firstVector; if (ivyParameters.halfgeom && ivyParameters.sides == 1) { normal = -grabVector; } else { normal = direction; } normal = inverseIvyGORotation * normal; vertex = direction * radius + point; vertex -= ivyGO.transform.position; vertex = inverseIvyGORotation * vertex; uv = new Vector2(length * ivyParameters.uvScale.y + ivyParameters.uvOffset.y - ivyParameters.stepSize, 1f / ivyParameters.sides * i * ivyParameters.uvScale.x + ivyParameters.uvOffset.x); verticesLoop[i] = new RTVertexData(vertex, normal, uv, Vector2.zero, Color.black); } }
public void BuildGeometry() { if (leavesDataInitialized) { //Lo primero inicializar Initialize(); //Estos contadores nos servirán para saber por dónde vamos calculándo vértices y triángulos, ya q lo calcularemos todo a buco, sin ir por ramas int vertCount = 0; int triBranchesCount = 0; //Recorremos cada rama y definimos el primer vértice que tenemos que escribir del array, recogido del vertcount actualizado en la iteración anterior for (int b = 0; b < infoPool.ivyContainer.branches.Count; b++) { int firstVertex = vertCount; Random.InitState(b + infoPool.ivyParameters.randomSeed); if (infoPool.ivyContainer.branches [b].branchPoints.Count > 1) { //En este contador guardaremos cuántos vértices tiene la rama actual, para en la siguiente tenerlo en cuenta y saber qué vértices hay que //escribir int lastVertCount = 0; //Recorremos cada punto de la rama hasta el penúltimo for (int p = 0; p < infoPool.ivyContainer.branches [b].branchPoints.Count; p++) { //Si no es el último punto, calculamos el ring de vértices BranchPoint branchPoint = infoPool.ivyContainer.branches[b].branchPoints[p]; branchPoint.verticesLoop = new List <RTVertexData>(); Vector3 centerVertexPosition = (branchPoint.point - infoPool.ivyContainer.ivyGO.transform.position); centerVertexPosition = Quaternion.Inverse(infoPool.ivyContainer.ivyGO.transform.rotation) * centerVertexPosition; float radius = CalculateRadius(branchPoint.length, infoPool.ivyContainer.branches[b].totalLenght); branchPoint.radius = radius; if (p != infoPool.ivyContainer.branches [b].branchPoints.Count - 1) { //En este array, el método nos mete en el index 0 el firstvector, y en el index 1 el axis de rotación del ring Vector3[] vectors = CalculateVectors(infoPool.ivyContainer.branches [b].branchPoints [p].point, p, b); branchPoint.firstVector = vectors[0]; branchPoint.axis = vectors[1]; for (int v = 0; v < infoPool.ivyParameters.sides + 1; v++) { if (infoPool.ivyParameters.generateBranches) { //BranchPoint branchPoint = infoPool.ivyContainer.branches[b].branchPoints[p]; float tipInfluence = GetTipInfluence(branchPoint.length, infoPool.ivyContainer.branches[b].totalLenght); infoPool.ivyContainer.branches[b].branchPoints[p].radius = radius; Quaternion quat = Quaternion.AngleAxis(angle * v, vectors[1]); Vector3 direction = quat * vectors[0]; //Excepción para el cálculo de normales si tenemos el caso de media geometría y 1 lado if (infoPool.ivyParameters.halfgeom && infoPool.ivyParameters.sides == 1) { normals[vertCount] = -infoPool.ivyContainer.branches[b].branchPoints[p].grabVector; } else { normals[vertCount] = direction; } Vector3 vertexForRuntime = (direction * radius) + centerVertexPosition; verts[vertCount] = (direction * radius * tipInfluence) + infoPool.ivyContainer.branches[b].branchPoints[p].point; verts[vertCount] -= infoPool.ivyContainer.ivyGO.transform.position; verts[vertCount] = Quaternion.Inverse(infoPool.ivyContainer.ivyGO.transform.rotation) * verts[vertCount]; uvs[vertCount] = new Vector2(branchPoint.length * infoPool.ivyParameters.uvScale.y + infoPool.ivyParameters.uvOffset.y - infoPool.ivyParameters.stepSize, 1f / infoPool.ivyParameters.sides * v * infoPool.ivyParameters.uvScale.x + infoPool.ivyParameters.uvOffset.x); normals[vertCount] = Quaternion.Inverse(infoPool.ivyContainer.ivyGO.transform.rotation) * normals[vertCount]; RTVertexData vertexData = new RTVertexData(vertexForRuntime, normals[vertCount], uvs[vertCount], Vector2.zero, vColor[vertCount]); branchPoint.verticesLoop.Add(vertexData); //Vamos actualizando estos contadores para en la siguiente pasada saber por dónde íbamos escribiendo en el array vertCount++; lastVertCount++; } } } //Si es el último punto, en lugar de calcular el ring, usamos el último punto para escribir el último vértice de esta rama else { if (infoPool.ivyParameters.generateBranches) { verts[vertCount] = (infoPool.ivyContainer.branches[b].branchPoints[p].point); //Corrección de espacio local verts[vertCount] -= infoPool.ivyContainer.ivyGO.transform.position; verts[vertCount] = Quaternion.Inverse(infoPool.ivyContainer.ivyGO.transform.rotation) * verts[vertCount]; //verts[vertCount] = centerVertexPosition; //Excepción para las normales en el caso de media geometría y 1 solo lado if (infoPool.ivyParameters.halfgeom && infoPool.ivyParameters.sides == 1) { normals[vertCount] = -infoPool.ivyContainer.branches[b].branchPoints[p].grabVector; } else { normals[vertCount] = Vector3.Normalize(infoPool.ivyContainer.branches[b].branchPoints[p].point - infoPool.ivyContainer.branches[b].branchPoints[p - 1].point); } uvs[vertCount] = new Vector2(infoPool.ivyContainer.branches[b].totalLenght * infoPool.ivyParameters.uvScale.y + infoPool.ivyParameters.uvOffset.y, 0.5f * infoPool.ivyParameters.uvScale.x + infoPool.ivyParameters.uvOffset.x); normals[vertCount] = Quaternion.Inverse(infoPool.ivyContainer.ivyGO.transform.rotation) * normals[vertCount]; Vector3 vertexForRuntime = centerVertexPosition; RTVertexData vertexData = new RTVertexData(vertexForRuntime, normals[vertCount], uvs[vertCount], Vector2.zero, vColor[vertCount]); branchPoint.verticesLoop.Add(vertexData); //Vamos actualizando estos contadores para en la siguiente pasada saber por dónde íbamos escribiendo en el array vertCount++; lastVertCount++; //Y después de poner el último vértice, triangulamos TriangulateBranch(b, ref triBranchesCount, vertCount, lastVertCount); } } } } //escribimos en el diccionario el index por donde nos hemos quedado para después poder //transformar las uvs de cada elemento acorde a su dimensión real int[] fromTo = new int[2] { firstVertex, vertCount - 1 }; branchesLeavesIndices.Add(branchesLeavesIndices.Count, fromTo); if (infoPool.ivyParameters.generateLeaves) { //infoPool.ivyContainer.branches[b].ClearRuntimeVerticesLeaves(); BuildLeaves(b, ref vertCount); } } //Y pasamos los vértices y tris a la malla ivyMesh.vertices = verts; ivyMesh.normals = normals; ivyMesh.uv = uvs; ivyMesh.colors = vColor; ivyMesh.SetTriangles(trisBranches, 0); //Por cada material, metemos los triángulos de hojas al submesh correspondiente for (int i = 0; i < leavesMaterials.Count; i++) { ivyMesh.SetTriangles(trisLeaves [i], i + 1); } ivyMesh.RecalculateTangents(); ivyMesh.RecalculateBounds(); } }
//Aquí se construyen las hojas, este método es llamado rama a rama void BuildLeaves(int b, ref int vertCount) { Mesh chosenLeaveMesh; //Se recorren los materiales for (int i = 0; i < leavesMaterials.Count; i++) { Random.InitState(b + infoPool.ivyParameters.randomSeed + i); for (int j = 0; j < infoPool.ivyContainer.branches[b].leaves.Count; j++) { LeafPoint currentLeaf = infoPool.ivyContainer.branches[b].leaves[j]; //Ahora vemos si para el material que estamos iterando, le corresponde el tipo de hoja que tenemos en este punto if (typesByMat[i].Contains(currentLeaf.chosenLeave)) { currentLeaf.verticesLeaves = new List <RTVertexData>(); //Y vemos qué tipo de hoja corresponde a cada punto a cogemos esa malla chosenLeaveMesh = infoPool.ivyParameters.leavesPrefabs[currentLeaf.chosenLeave].GetComponent <MeshFilter>().sharedMesh; //definimos el vértice por el que tenemos que empezar a escribir en el array int firstVertex = vertCount; Vector3 left, forward; Quaternion quat; //Aquí cálculos de orientación en función de las opciones de rotación if (!infoPool.ivyParameters.globalOrientation) { forward = currentLeaf.lpForward; left = currentLeaf.left; //left = Vector3.Cross(currentLeaf.lpForward, currentLeaf.lpUpward); } else { forward = infoPool.ivyParameters.globalRotation; left = Vector3.Normalize(Vector3.Cross(infoPool.ivyParameters.globalRotation, currentLeaf.lpUpward)); } //Y aplicamos la rotación quat = Quaternion.LookRotation(currentLeaf.lpUpward, forward); quat = Quaternion.AngleAxis(infoPool.ivyParameters.rotation.x, left) * Quaternion.AngleAxis(infoPool.ivyParameters.rotation.y, currentLeaf.lpUpward) * Quaternion.AngleAxis(infoPool.ivyParameters.rotation.z, forward) * quat; quat = Quaternion.AngleAxis(Random.Range(-infoPool.ivyParameters.randomRotation.x, infoPool.ivyParameters.randomRotation.x), left) * Quaternion.AngleAxis(Random.Range(-infoPool.ivyParameters.randomRotation.y, infoPool.ivyParameters.randomRotation.y), currentLeaf.lpUpward) * Quaternion.AngleAxis(Random.Range(-infoPool.ivyParameters.randomRotation.z, infoPool.ivyParameters.randomRotation.z), forward) * quat; quat = currentLeaf.forwarRot * quat; //Aquí la escala, que es facilita, incluyendo el tip influence float scale = Random.Range(infoPool.ivyParameters.minScale, infoPool.ivyParameters.maxScale); currentLeaf.leafScale = scale; scale *= Mathf.InverseLerp(infoPool.ivyContainer.branches[b].totalLenght, infoPool.ivyContainer.branches[b].totalLenght - infoPool.ivyParameters.tipInfluence, currentLeaf.lpLength); /*******************/ currentLeaf.leafRotation = quat; currentLeaf.dstScale = scale; /*******************/ //Metemos los triángulos correspondientes en el array correspondiente al material que estamos iterando for (int t = 0; t < chosenLeaveMesh.triangles.Length; t++) { int triangle = chosenLeaveMesh.triangles[t] + vertCount; trisLeaves[i].Add(triangle); } //ylos vértices, normales y uvs, aplicando las transformaciones pertinentes, actualizando el contador para la siguiente iteración saber por dónde vamos for (int v = 0; v < chosenLeaveMesh.vertexCount; v++) { Vector3 offset = left * infoPool.ivyParameters.offset.x + currentLeaf.lpUpward * infoPool.ivyParameters.offset.y + currentLeaf.lpForward * infoPool.ivyParameters.offset.z; verts[vertCount] = quat * chosenLeaveMesh.vertices[v] * scale + currentLeaf.point + offset; normals[vertCount] = quat * chosenLeaveMesh.normals[v]; uvs[vertCount] = chosenLeaveMesh.uv[v]; vColor[vertCount] = chosenLeaveMesh.colors[v]; normals[vertCount] = Quaternion.Inverse(infoPool.ivyContainer.ivyGO.transform.rotation) * normals[vertCount]; verts[vertCount] -= infoPool.ivyContainer.ivyGO.transform.position; verts[vertCount] = Quaternion.Inverse(infoPool.ivyContainer.ivyGO.transform.rotation) * verts[vertCount]; RTVertexData vertexData = new RTVertexData(verts[vertCount], normals[vertCount], uvs[vertCount], Vector2.zero, vColor[vertCount]); currentLeaf.verticesLeaves.Add(vertexData); currentLeaf.leafCenter = currentLeaf.point - infoPool.ivyContainer.ivyGO.transform.position; currentLeaf.leafCenter = Quaternion.Inverse(infoPool.ivyContainer.ivyGO.transform.rotation) * currentLeaf.leafCenter; vertCount++; } //escribimos en el diccionario el index por donde nos hemos quedado para después poder //transformar las uvs de cada elemento acorde a su dimensión real int[] fromTo = new int[2] { firstVertex, vertCount - 1 }; branchesLeavesIndices.Add(branchesLeavesIndices.Count, fromTo); } //for (int p = 0; p < currentBranchPoint.leaves.Count; p++) //{ //} } /*//Después por cada material, recorremos los puntos de la rama * for (int j = 0; j < infoPool.ivyContainer.branches[b].branchPoints.Count; j++) { * * * * * * * }*/ } }
public void CopyToFixedMesh(int branchIndex, int initSegmentIdx, int endSegmentIdx, RTBranchContainer branchContainer, RTBranchContainer bakedBranchContainer) { int numVerticesPerLoop = ivyParameters.sides + 1; int numTrianglesPerLoop = ivyParameters.sides * 6; int numLoopsToProcess = 1; int onlyBranchVertices = (vertCountsPerBranch[branchIndex] - vertCountLeavesPerBranch[branchIndex]); int vertexOffset = 0; for (int i = 1; i <= branchIndex; i++) { vertexOffset += vertCountsPerBranch[branchIndex]; } if (processedBranchesVerticesIndicesPerBranch[branchIndex].Count <= 0) { numLoopsToProcess = 2; } else { numLoopsToProcess = 1; vertexOffset += numVerticesPerLoop; } for (int i = numLoopsToProcess - 1; i >= 0; i--) { int index = branchContainer.branchPoints.Count - backtrackingPoints - i; RTBranchPoint rtBranchPoint = branchContainer.branchPoints[index]; for (int j = 0; j < rtBranchPoint.verticesLoop.Length; j++) { RTVertexData vertexData = rtBranchPoint.verticesLoop[j]; processedMeshData.AddVertex(vertexData.vertex, vertexData.normal, vertexData.uv, vertexData.color); processedBranchesVerticesIndicesPerBranch[branchIndex].Add(processedMeshData.VertexCount() - 1); } } int triangleIndexOffset = 0; if (branchIndex > 0) { triangleIndexOffset = lastTriangleIndexPerBranch[branchIndex]; } if (processedBranchesVerticesIndicesPerBranch[branchIndex].Count >= numVerticesPerLoop * 2) { int initIdx = processedBranchesVerticesIndicesPerBranch[branchIndex].Count - (numVerticesPerLoop * 2); for (int i = 0; i < ivyParameters.sides; i++) { int v0 = processedBranchesVerticesIndicesPerBranch[branchIndex][i + initIdx]; int v1 = processedBranchesVerticesIndicesPerBranch[branchIndex][i + 1 + initIdx]; int v2 = processedBranchesVerticesIndicesPerBranch[branchIndex][i + ivyParameters.sides + 1 + initIdx]; int v3 = processedBranchesVerticesIndicesPerBranch[branchIndex][i + 1 + initIdx]; int v4 = processedBranchesVerticesIndicesPerBranch[branchIndex][i + ivyParameters.sides + 2 + initIdx]; int v5 = processedBranchesVerticesIndicesPerBranch[branchIndex][i + ivyParameters.sides + 1 + initIdx]; processedMeshData.AddTriangle(0, v0); processedMeshData.AddTriangle(0, v1); processedMeshData.AddTriangle(0, v2); processedMeshData.AddTriangle(0, v3); processedMeshData.AddTriangle(0, v4); processedMeshData.AddTriangle(0, v5); } } if (ivyParameters.generateLeaves) { int lastVertexLeafProcessed = processedMeshData.VertexCount(); int numLeavesProcessed = 0; for (int i = initSegmentIdx; i < endSegmentIdx; i++) { RTLeafPoint[] leaves = bakedBranchContainer.leavesOrderedByInitSegment[i]; for (int j = 0; j < leaves.Length; j++) { RTLeafPoint currentLeaf = leaves[j]; if (currentLeaf == null) { continue; } RTMeshData chosenLeaveMeshData = leavesMeshesByChosenLeaf[currentLeaf.chosenLeave]; int submesh = submeshByChoseLeaf[currentLeaf.chosenLeave]; for (int t = 0; t < chosenLeaveMeshData.triangles[0].Length; t++) { int triangleValue = chosenLeaveMeshData.triangles[0][t] + lastVertexLeafProcessed; processedMeshData.AddTriangle(submesh, triangleValue); } for (int v = 0; v < currentLeaf.vertices.Length; v++) { RTVertexData vertexData = currentLeaf.vertices[v]; processedMeshData.AddVertex(vertexData.vertex, vertexData.normal, vertexData.uv, vertexData.color); processedVerticesIndicesPerBranch[branchIndex].Add(processedMeshData.VertexCount() - 1); lastVertexLeafProcessed++; } numLeavesProcessed++; } } } }
public void CreateVertices(IvyParameters ivyParameters, RTMeshData leafMeshData, GameObject ivyGO) { Vector3 left, forward; Quaternion quat; //Aquí cálculos de orientación en función de las opciones de rotación if (!ivyParameters.globalOrientation) { forward = lpForward; left = this.left; //left = Vector3.Cross(currentLeaf.lpForward, currentLeaf.lpUpward); } else { forward = ivyParameters.globalRotation; left = Vector3.Normalize(Vector3.Cross(ivyParameters.globalRotation, this.lpUpward)); } //Y aplicamos la rotación quat = Quaternion.LookRotation(this.lpUpward, forward); quat = Quaternion.AngleAxis(ivyParameters.rotation.x, left) * Quaternion.AngleAxis(ivyParameters.rotation.y, this.lpUpward) * Quaternion.AngleAxis(ivyParameters.rotation.z, forward) * quat; quat = Quaternion.AngleAxis(Random.Range(-ivyParameters.randomRotation.x, ivyParameters.randomRotation.x), left) * Quaternion.AngleAxis(Random.Range(-ivyParameters.randomRotation.y, ivyParameters.randomRotation.y), this.lpUpward) * Quaternion.AngleAxis(Random.Range(-ivyParameters.randomRotation.z, ivyParameters.randomRotation.z), forward) * quat; quat = this.forwarRot * quat; //Aquí la escala, que es facilita, incluyendo el tip influence float scale = Random.Range(ivyParameters.minScale, ivyParameters.maxScale); //scale *= Mathf.InverseLerp(infoPool.ivyContainer.branches[b].totalLenght, infoPool.ivyContainer.branches[b].totalLenght - infoPool.ivyParameters.tipInfluence, currentLeaf.lpLength); /*******************/ this.leafRotation = quat; //currentLeaf.dstScale = scale; /*******************/ this.leafCenter = this.point - ivyGO.transform.position; this.leafCenter = Quaternion.Inverse(ivyGO.transform.rotation) * this.leafCenter; if (this.verticesLeaves == null) { this.verticesLeaves = new List <RTVertexData>(4); } //this.verticesLeaves.Clear(); Vector3 vertex = Vector3.zero; Vector3 normal = Vector3.zero; Vector2 uv = Vector2.zero; Color vertexColor = Color.black; Quaternion ivyGOInverseRotation = Quaternion.Inverse(ivyGO.transform.rotation); for (int v = 0; v < leafMeshData.vertices.Length; v++) { Vector3 offset = left * ivyParameters.offset.x + this.lpUpward * ivyParameters.offset.y + this.lpForward * ivyParameters.offset.z; vertex = quat * leafMeshData.vertices[v] * scale + leafCenter + offset; normal = quat * leafMeshData.normals[v]; normal = ivyGOInverseRotation * normal; uv = leafMeshData.uv[v]; vertexColor = leafMeshData.colors[v]; RTVertexData vertexData = new RTVertexData(vertex, normal, uv, Vector2.zero, vertexColor); this.verticesLeaves.Add(vertexData); } }