void ApplyBlendShapeFramesToMeshAndBuildMap() { if (MBVersion.GetMajorVersion() > 5 || (MBVersion.GetMajorVersion() == 5 && MBVersion.GetMinorVersion() >= 3)) { if (blendShapesInCombined.Length != blendShapes.Length) { blendShapesInCombined = new MBBlendShape[blendShapes.Length]; } Vector3[] targVerts = new UnityEngine.Vector3[verts.Length]; Vector3[] targNorms = new UnityEngine.Vector3[verts.Length]; Vector3[] targTans = new UnityEngine.Vector3[verts.Length]; ((SkinnedMeshRenderer)_targetRenderer).sharedMesh = null; MBVersion.ClearBlendShapes(_mesh); for (int bsIdx = 0; bsIdx < blendShapes.Length; bsIdx++) { MBBlendShape blendShape = blendShapes[bsIdx]; MB_DynamicGameObject dgo = instance2Combined_MapGet(blendShape.gameObject); if (dgo != null) { int destIdx = dgo.vertIdx; for (int frmIdx = 0; frmIdx < blendShape.frames.Length; frmIdx++) { MBBlendShapeFrame frame = blendShape.frames[frmIdx]; Array.Copy(frame.vertices, 0, targVerts, destIdx, frame.vertices.Length); Array.Copy(frame.normals, 0, targNorms, destIdx, frame.normals.Length); Array.Copy(frame.tangents, 0, targTans, destIdx, frame.tangents.Length); MBVersion.AddBlendShapeFrame(_mesh, ConvertBlendShapeNameToOutputName(blendShape.name) + blendShape.gameObjectID, frame.frameWeight, targVerts, targNorms, targTans); // We re-use these arrays restore them to zero _ZeroArray(targVerts, destIdx, frame.vertices.Length); _ZeroArray(targNorms, destIdx, frame.normals.Length); _ZeroArray(targTans, destIdx, frame.tangents.Length); } } else { Debug.LogError("InstanceID in blend shape that was not in instance2combinedMap"); } blendShapesInCombined[bsIdx] = blendShape; } //this is necessary to get the renderer to refresh its data about the blendshapes. ((SkinnedMeshRenderer)_targetRenderer).sharedMesh = null; ((SkinnedMeshRenderer)_targetRenderer).sharedMesh = _mesh; // Add the map to the target renderer. if (settings.doBlendShapes) { MB_BlendShape2CombinedMap mapComponent = _targetRenderer.GetComponent <MB_BlendShape2CombinedMap>(); if (mapComponent == null) { mapComponent = _targetRenderer.gameObject.AddComponent <MB_BlendShape2CombinedMap>(); } SerializableSourceBlendShape2Combined map = mapComponent.GetMap(); BuildSrcShape2CombinedMap(map, blendShapes); } } }
internal void CopyVertsNormsTansToBuffers(MB_DynamicGameObject dgo, MB_IMeshBakerSettings settings, int vertsIdx, Vector3[] nnorms, Vector4[] ntangs, Vector3[] nverts, Vector3[] normals, Vector4[] tangents, Vector3[] verts) { bool isMeshRenderer = dgo.gameObject.GetComponent <Renderer>() is MeshRenderer; if (settings.smrNoExtraBonesWhenCombiningMeshRenderers && isMeshRenderer && dgo._tmpSMR_CachedBones[0] != dgo.gameObject.transform // bone may not have a parent ancestor that is a bone ) { // transform all the verticies, norms and tangents into the parent bone's local space (adjusted by the parent bone's bind pose). // there should be only one bone and bind pose for a mesh renderer dgo. // The bone and bind pose should be the parent-bone's NOT the MeshRenderers. Matrix4x4 l2parentMat = dgo._tmpSMR_CachedBindposes[0].inverse * dgo._tmpSMR_CachedBones[0].worldToLocalMatrix * dgo.gameObject.transform.localToWorldMatrix; // Similar to local2world but with translation removed and we are using the inverse transpose. // We use this for normals and tangents because it handles scaling correctly. Matrix4x4 l2parentRotScale = l2parentMat; l2parentRotScale[0, 3] = l2parentRotScale[1, 3] = l2parentRotScale[2, 3] = 0f; l2parentRotScale = l2parentRotScale.inverse.transpose; //can't modify the arrays we get from the cache because they will be modified multiple times if the same mesh is being added multiple times. for (int j = 0; j < nverts.Length; j++) { int vIdx = vertsIdx + j; verts[vertsIdx + j] = l2parentMat.MultiplyPoint3x4(nverts[j]); if (settings.doNorm) { normals[vIdx] = l2parentRotScale.MultiplyPoint3x4(nnorms[j]).normalized; } if (settings.doTan) { float w = ntangs[j].w; //need to preserve the w value tangents[vIdx] = l2parentRotScale.MultiplyPoint3x4(((Vector3)ntangs[j])).normalized; tangents[vIdx].w = w; } } } else { if (settings.doNorm) { nnorms.CopyTo(normals, vertsIdx); } if (settings.doTan) { ntangs.CopyTo(tangents, vertsIdx); } nverts.CopyTo(verts, vertsIdx); } }
bool _collectMaterialTriangles(Mesh m, MB_DynamicGameObject dgo, Material[] sharedMaterials, OrderedDictionary sourceMats2submeshIdx_map) { //everything here applies to the source object being added int numTriMeshes = m.subMeshCount; if (sharedMaterials.Length < numTriMeshes) { numTriMeshes = sharedMaterials.Length; } dgo._submeshTris = new List <int> [numTriMeshes]; dgo.targetSubmeshIdxs = new int[numTriMeshes]; for (int i = 0; i < dgo._submeshTris.Length; i++) { dgo._submeshTris[i] = new List <int>(); } for (int i = 0; i < numTriMeshes; i++) { if (doMultiMaterial) { if (!sourceMats2submeshIdx_map.Contains(sharedMaterials[i])) { Debug.LogError("Object " + dgo.go + " has a material that was not found in the result materials maping. " + sharedMaterials[i]); //todo might need to cleanup return(false); } dgo.targetSubmeshIdxs[i] = (int)sourceMats2submeshIdx_map[sharedMaterials[i]]; } else { dgo.targetSubmeshIdxs[i] = 0; } List <int> targTris = dgo._submeshTris[i]; // add distinct triangles to master list int[] sourceTris = m.GetTriangles(i); for (int j = 0; j < sourceTris.Length; j++) { targTris.Add(sourceTris[j]); } if (VERBOSE) { Debug.Log("Collecting triangles for: " + dgo.go.name + " submesh:" + i + " maps to submesh:" + dgo.targetSubmeshIdxs[i] + " added:" + sourceTris.Length); } } return(true); }
public void FindBonesToDelete(MB_DynamicGameObject dgo) { Debug.Assert(_didSetup); Debug.Assert(combiner.settings.renderType == MB_RenderType.skinnedMeshRenderer); // We could be working with adding and deleting smr body parts from the same rig. Different smrs will share // the same bones. Track if we need to delete a bone or not. for (int j = 0; j < dgo.indexesOfBonesUsed.Length; j++) { int idxOfUsedBone = dgo.indexesOfBonesUsed[j]; List <MB_DynamicGameObject> dgosThatUseBone = boneIdx2dgoMap[idxOfUsedBone]; if (dgosThatUseBone.Contains(dgo)) { dgosThatUseBone.Remove(dgo); if (dgosThatUseBone.Count == 0) { boneIdxsToDelete.Add(idxOfUsedBone); } } } }
bool _collectOutOfBoundsUVRects(Mesh m, MB_DynamicGameObject dgo, Material[] sharedMaterials, OrderedDictionary sourceMats2submeshIdx_map) { int numResultMats = 1; if (doMultiMaterial) { numResultMats = resultMaterials.Length; } dgo.obUVRects = new Rect[numResultMats]; for (int i = 0; i < dgo.obUVRects.Length; i++) { dgo.obUVRects[i] = new Rect(0f, 0f, 1f, 1f); } int numTriMeshes = m.subMeshCount; if (sharedMaterials.Length < numTriMeshes) { numTriMeshes = sharedMaterials.Length; } for (int i = 0; i < numTriMeshes; i++) { int combinedSubmeshIdx = 0; if (doMultiMaterial) { if (!sourceMats2submeshIdx_map.Contains(sharedMaterials[i])) { Debug.LogError("Object " + dgo.go + " has a material in sharedMaterials that was not found in the material to rect map. " + sharedMaterials[i] + " numKeys " + sourceMats2submeshIdx_map.Keys.Count); //todo might need to cleanup return(false); } combinedSubmeshIdx = (int)sourceMats2submeshIdx_map[sharedMaterials[i]]; } Rect r = new Rect(); MB_Utility.hasOutOfBoundsUVs(m, ref r, combinedSubmeshIdx); dgo.obUVRects[dgo.targetSubmeshIdxs[i]] = r; } return(true); }
public bool MapSharedMaterialsToAtlasRects(Material[] sharedMaterials, bool checkTargetSubmeshIdxsFromPreviousBake, Mesh m, MeshChannelsCache meshChannelsCache, Dictionary <int, MB_Utility.MeshAnalysisResult[]> meshAnalysisResultsCache, OrderedDictionary sourceMats2submeshIdx_map, GameObject go, MB_DynamicGameObject dgoOut) { MB_TextureTilingTreatment[] tilingTreatment = new MB_TextureTilingTreatment[sharedMaterials.Length]; Rect[] uvRectsInAtlas = new Rect[sharedMaterials.Length]; Rect[] encapsulatingRect = new Rect[sharedMaterials.Length]; Rect[] sourceMaterialTiling = new Rect[sharedMaterials.Length]; int[] sliceIdx = new int[sharedMaterials.Length]; String errorMsg = ""; for (int srcSubmeshIdx = 0; srcSubmeshIdx < sharedMaterials.Length; srcSubmeshIdx++) { System.Object subIdx = sourceMats2submeshIdx_map[sharedMaterials[srcSubmeshIdx]]; int resMatIdx; if (subIdx == null) { Debug.LogError("Source object " + go.name + " used a material " + sharedMaterials[srcSubmeshIdx] + " that was not in the baked materials."); return(false); } else { resMatIdx = (int)subIdx; if (checkTargetSubmeshIdxsFromPreviousBake) { /* * Possibilities: * Consider a mesh with three submeshes with materials A, B, C that map to * different submeshes in the combined mesh, AA,BB,CC. The user is updating the UVs on a * MeshRenderer so that object 'one' now uses material C => CC instead of A => AA. This will mean that the * triangle buffers will need to be resized. This is not allowed in UpdateGameObjects. * Must map to the same submesh that the old one mapped to. */ if (resMatIdx != dgoOut.targetSubmeshIdxs[srcSubmeshIdx]) { Debug.LogError(String.Format("Update failed for object {0}. Material {1} is mapped to a different submesh in the combined mesh than the previous material. This is not supported. Try using AddDelete.", go.name, sharedMaterials[srcSubmeshIdx])); return(false); } } } if (!TryMapMaterialToUVRect(sharedMaterials[srcSubmeshIdx], m, srcSubmeshIdx, resMatIdx, meshChannelsCache, meshAnalysisResultsCache, out tilingTreatment[srcSubmeshIdx], out uvRectsInAtlas[srcSubmeshIdx], out encapsulatingRect[srcSubmeshIdx], out sourceMaterialTiling[srcSubmeshIdx], out sliceIdx[srcSubmeshIdx], ref errorMsg, LOG_LEVEL)) { Debug.LogError(errorMsg); return(false); } } dgoOut.uvRects = uvRectsInAtlas; dgoOut.encapsulatingRect = encapsulatingRect; dgoOut.sourceMaterialTiling = sourceMaterialTiling; dgoOut.textureArraySliceIdx = sliceIdx; return(true); }
public void _copyAndAdjustUVsFromMesh(MB2_TextureBakeResults tbr, MB_DynamicGameObject dgo, Mesh mesh, int uvChannel, int vertsIdx, Vector2[] uvsOut, float[] uvsSliceIdx, MeshChannelsCache meshChannelsCache) { Debug.Assert(dgo.sourceSharedMaterials != null && dgo.sourceSharedMaterials.Length == dgo.targetSubmeshIdxs.Length, "sourceSharedMaterials array was a different size than the targetSubmeshIdxs. Was this old data that is being updated? " + dgo.sourceSharedMaterials.Length); Vector2[] nuvs = meshChannelsCache.GetUVChannel(uvChannel, mesh); int[] done = new int[nuvs.Length]; //use this to track uvs that have already been adjusted don't adjust twice for (int l = 0; l < done.Length; l++) { done[l] = -1; } bool triangleArraysOverlap = false; //Rect uvRectInSrc = new Rect (0f,0f,1f,1f); //need to address the UVs through the submesh indexes because //each submesh has a different UV index bool doTextureArray = tbr.resultType == MB2_TextureBakeResults.ResultType.textureArray; for (int srcSubmeshIdx = 0; srcSubmeshIdx < dgo.targetSubmeshIdxs.Length; srcSubmeshIdx++) { int[] srcSubTris; if (dgo._tmpSubmeshTris != null) { srcSubTris = dgo._tmpSubmeshTris[srcSubmeshIdx].data; } else { srcSubTris = mesh.GetTriangles(srcSubmeshIdx); } float slice = dgo.textureArraySliceIdx[srcSubmeshIdx]; int resultSubmeshIdx = dgo.targetSubmeshIdxs[srcSubmeshIdx]; if (LOG_LEVEL >= MB2_LogLevel.trace) { Debug.Log(String.Format("Build UV transform for mesh {0} submesh {1} encapsulatingRect {2}", dgo.name, srcSubmeshIdx, dgo.encapsulatingRect[srcSubmeshIdx])); } bool considerUVs = textureBakeResults.GetConsiderMeshUVs(resultSubmeshIdx, dgo.sourceSharedMaterials[srcSubmeshIdx]); Rect rr = MB3_TextureCombinerMerging.BuildTransformMeshUV2AtlasRect( considerUVs, dgo.uvRects[srcSubmeshIdx], dgo.obUVRects == null || dgo.obUVRects.Length == 0 ? new Rect(0, 0, 1, 1) : dgo.obUVRects[srcSubmeshIdx], dgo.sourceMaterialTiling[srcSubmeshIdx], dgo.encapsulatingRect[srcSubmeshIdx]); for (int srcSubTriIdx = 0; srcSubTriIdx < srcSubTris.Length; srcSubTriIdx++) { int srcVertIdx = srcSubTris[srcSubTriIdx]; if (done[srcVertIdx] == -1) { done[srcVertIdx] = srcSubmeshIdx; //prevents a uv from being adjusted twice. Same vert can be on more than one submesh. Vector2 nuv = nuvs[srcVertIdx]; //don't modify nuvs directly because it is cached and we might be re-using //if (textureBakeResults.fixOutOfBoundsUVs) { //uvRectInSrc can be larger than (out of bounds uvs) or smaller than 0..1 //this transforms the uvs so they fit inside the uvRectInSrc sample box // scale, shift to fit in atlas rect nuv.x = rr.x + nuv.x * rr.width; nuv.y = rr.y + nuv.y * rr.height; int idx = vertsIdx + srcVertIdx; uvsOut[idx] = nuv; if (doTextureArray) { uvsSliceIdx[idx] = slice; } } if (done[srcVertIdx] != srcSubmeshIdx) { triangleArraysOverlap = true; } } } if (triangleArraysOverlap) { if (LOG_LEVEL >= MB2_LogLevel.warn) { Debug.LogWarning(dgo.name + "has submeshes which share verticies. Adjusted uvs may not map correctly in combined atlas."); } } if (LOG_LEVEL >= MB2_LogLevel.trace) { Debug.Log(string.Format("_copyAndAdjustUVsFromMesh copied {0} verts", nuvs.Length)); } }
bool _addToCombined(GameObject[] goToAdd, GameObject[] goToDelete, bool disableRendererInSource) { if (goToAdd == null) { goToAdd = empty; } if (goToDelete == null) { goToDelete = empty; } if (_mesh == null) { _mesh = new Mesh(); } if (mat2rect_map.Keys.Count == 0) { _initialize(); } int numResultMats = 1; if (doMultiMaterial) { numResultMats = resultMaterials.Length; } if (VERBOSE) { Debug.Log("_addToCombined objs adding:" + goToAdd.Length + " objs deleting:" + goToDelete.Length + " fixOutOfBounds:" + fixOutOfBoundsUVs + " doMultiMaterial:" + doMultiMaterial); } OrderedDictionary sourceMats2submeshIdx_map = null; if (doMultiMaterial) { //build the sourceMats to submesh index map sourceMats2submeshIdx_map = new OrderedDictionary(); for (int i = 0; i < numResultMats; i++) { MB_MultiMaterial mm = resultMaterials[i]; for (int j = 0; j < mm.sourceMaterials.Count; j++) { if (mm.sourceMaterials[j] == null) { Debug.LogError("Found null material in source materials for combined mesh materials " + i); return(false); } if (!sourceMats2submeshIdx_map.Contains(mm.sourceMaterials[j])) { sourceMats2submeshIdx_map.Add(mm.sourceMaterials[j], i); } } } } if (submeshTris.Length == 0) { submeshTris = new int[numResultMats][]; for (int i = 0; i < submeshTris.Length; i++) { submeshTris[i] = new int[0]; } } //calculate num to delete int totalDeleteVerts = 0; // int totalDeleteTris = 0; int[] totalDeleteSubmeshTris = new int[numResultMats]; for (int i = 0; i < goToDelete.Length; i++) { MB_DynamicGameObject dgo; if (instance2combined_map.TryGetValue(goToDelete[i], out dgo)) { totalDeleteVerts += dgo.numVerts; // totalDeleteTris += dgo.numTris; for (int j = 0; j < dgo.submeshNumTris.Length; j++) { totalDeleteSubmeshTris[j] += dgo.submeshNumTris[j]; } } else { Debug.LogWarning("Trying to delete an object that is not in combined mesh"); } } //now add List <MB_DynamicGameObject> toAddDGOs = new List <MB_DynamicGameObject>(); int totalAddVerts = 0; // int totalAddTris = 0; int[] totalAddSubmeshTris = new int[numResultMats]; for (int i = 0; i < goToAdd.Length; i++) { if (!instance2combined_map.ContainsKey(goToAdd[i])) { MB_DynamicGameObject dgo = new MB_DynamicGameObject(); GameObject go = goToAdd[i]; Material[] sharedMaterials = MB_Utility.GetGOMaterials(go); if (sharedMaterials == null) { Debug.LogError("Object " + go.name + " does not have a Renderer"); goToAdd[i] = null; continue; } Mesh m = MB_Utility.GetMesh(go); if (m == null) { Debug.LogError("Object " + go.name + " MeshFilter or SkinedMeshRenderer had no mesh"); goToAdd[i] = null; } Rect[] uvRects = new Rect[sharedMaterials.Length]; for (int j = 0; j < sharedMaterials.Length; j++) { if (!mat2rect_map.TryGetValue(sharedMaterials[j], out uvRects[j])) { Debug.LogError("Object " + go.name + " has an unknown material " + sharedMaterials[j] + ". Try baking textures"); goToAdd[i] = null; } } if (goToAdd[i] != null) { dgo.go = goToAdd[i]; dgo.uvRects = uvRects; dgo.sharedMesh = m; dgo.numVerts = m.vertexCount; if (!_collectMaterialTriangles(m, dgo, sharedMaterials, sourceMats2submeshIdx_map)) { return(false); } dgo.submeshNumTris = new int[numResultMats]; dgo.submeshTriIdxs = new int[numResultMats]; if (fixOutOfBoundsUVs) { if (!_collectOutOfBoundsUVRects(m, dgo, sharedMaterials, sourceMats2submeshIdx_map)) { return(false); } } toAddDGOs.Add(dgo); totalAddVerts += dgo.numVerts; // totalAddTris += dgo.numTris; for (int j = 0; j < dgo._submeshTris.Length; j++) { totalAddSubmeshTris[dgo.targetSubmeshIdxs[j]] += dgo._submeshTris[j].Count; } } } else { Debug.LogWarning("Object " + goToAdd[i].name + " has already been added to " + name); goToAdd[i] = null; } } for (int i = 0; i < goToAdd.Length; i++) { if (goToAdd[i] != null && disableRendererInSource) { MB_Utility.DisableRendererInSource(goToAdd[i]); } } int newVertSize = verts.Length + totalAddVerts - totalDeleteVerts; // int newTrisSize = tris.Length + totalAddTris - totalDeleteTris; int[] newSubmeshTrisSize = new int[numResultMats]; if (VERBOSE) { Debug.Log("Verts adding:" + totalAddVerts + " deleting:" + totalDeleteVerts); } if (VERBOSE) { Debug.Log("Submeshes:" + newSubmeshTrisSize.Length); } for (int i = 0; i < newSubmeshTrisSize.Length; i++) { newSubmeshTrisSize[i] = submeshTris[i].Length + totalAddSubmeshTris[i] - totalDeleteSubmeshTris[i]; if (VERBOSE) { Debug.Log(" submesh :" + i + " already contains:" + submeshTris[i].Length + " trisAdded:" + totalAddSubmeshTris[i] + " trisDeleted:" + totalDeleteSubmeshTris[i]); } } if (newVertSize > 65534) { Debug.LogError("Cannot add objects. Resulting mesh will have more than 64k vertices ."); return(false); } Vector3[] nverts = new Vector3[newVertSize]; Vector3[] nnormals = new Vector3[newVertSize]; Vector4[] ntangents = new Vector4[newVertSize]; Vector2[] nuvs = new Vector2[newVertSize]; Vector2[] nuv1s = new Vector2[newVertSize]; Vector2[] nuv2s = new Vector2[newVertSize]; Color[] ncolors = new Color[newVertSize]; // int[] ntris = new int[newTrisSize]; int[][] nsubmeshTris = null; nsubmeshTris = new int[numResultMats][]; for (int i = 0; i < nsubmeshTris.Length; i++) { nsubmeshTris[i] = new int[newSubmeshTrisSize[i]]; } for (int i = 0; i < goToDelete.Length; i++) { MB_DynamicGameObject dgo; if (instance2combined_map.TryGetValue(goToDelete[i], out dgo)) { dgo._beingDeleted = true; } } objectsInCombinedMesh.Sort(); //copy existing arrays to narrays gameobj by gameobj omitting deleted ones int targVidx = 0; int[] targSubmeshTidx = new int[numResultMats]; int triangleIdxAdjustment = 0; for (int i = 0; i < objectsInCombinedMesh.Count; i++) { MB_DynamicGameObject dgo = objectsInCombinedMesh[i]; if (!dgo._beingDeleted) { if (VERBOSE) { Debug.Log("Copying obj in combined arrays idx:" + i); } Array.Copy(verts, dgo.vertIdx, nverts, targVidx, dgo.numVerts); Array.Copy(normals, dgo.vertIdx, nnormals, targVidx, dgo.numVerts); Array.Copy(tangents, dgo.vertIdx, ntangents, targVidx, dgo.numVerts); Array.Copy(uvs, dgo.vertIdx, nuvs, targVidx, dgo.numVerts); Array.Copy(uv1s, dgo.vertIdx, nuv1s, targVidx, dgo.numVerts); Array.Copy(uv2s, dgo.vertIdx, nuv2s, targVidx, dgo.numVerts); Array.Copy(colors, dgo.vertIdx, ncolors, targVidx, dgo.numVerts); //adjust triangles, then copy them over for (int subIdx = 0; subIdx < numResultMats; subIdx++) { int[] sTris = submeshTris[subIdx]; int sTriIdx = dgo.submeshTriIdxs[subIdx]; int sNumTris = dgo.submeshNumTris[subIdx]; if (VERBOSE) { Debug.Log(" Adjusting submesh triangles submesh:" + subIdx + " startIdx:" + sTriIdx + " num:" + sNumTris); } for (int j = sTriIdx; j < sTriIdx + sNumTris; j++) { sTris[j] = sTris[j] - triangleIdxAdjustment; } Array.Copy(sTris, sTriIdx, nsubmeshTris[subIdx], targSubmeshTidx[subIdx], sNumTris); } // dgo.triIdx = targTidx; dgo.vertIdx = targVidx; // targTidx += dgo.numTris; for (int j = 0; j < targSubmeshTidx.Length; j++) { dgo.submeshTriIdxs[j] = targSubmeshTidx[j]; targSubmeshTidx[j] += dgo.submeshNumTris[j]; } targVidx += dgo.numVerts; } else { if (VERBOSE) { Debug.Log("Not copying obj: " + i); } triangleIdxAdjustment += dgo.numVerts; } } for (int i = objectsInCombinedMesh.Count - 1; i >= 0; i--) { if (objectsInCombinedMesh[i]._beingDeleted) { instance2combined_map.Remove(objectsInCombinedMesh[i].go); objectsInCombinedMesh.RemoveAt(i); } } verts = nverts; normals = nnormals; tangents = ntangents; uvs = nuvs; uv1s = nuv1s; uv2s = nuv2s; colors = ncolors; // tris = ntris; submeshTris = nsubmeshTris; //add new for (int i = 0; i < toAddDGOs.Count; i++) { MB_DynamicGameObject dgo = toAddDGOs[i]; GameObject go = dgo.go; int vertsIdx = targVidx; // int trisIdx = targTidx; Mesh mesh = dgo.sharedMesh; Matrix4x4 l2wMat = go.transform.localToWorldMatrix; Quaternion l2wQ = go.transform.rotation; nverts = mesh.vertices; Vector3[] nnorms = mesh.normals; Vector4[] ntangs = mesh.tangents; for (int j = 0; j < nverts.Length; j++) { nverts[j] = l2wMat.MultiplyPoint(nverts[j]); nnorms[j] = l2wQ * nnorms[j]; float w = ntangs[j].w; //need to preserve the w value ntangs[j] = l2wQ * ntangs[j]; ntangs[j].w = w; } int numTriSets = mesh.subMeshCount; if (dgo.uvRects.Length < numTriSets) { Debug.LogWarning("Mesh " + dgo.go.name + " has more submeshes than materials"); numTriSets = dgo.uvRects.Length; } else if (dgo.uvRects.Length > numTriSets) { Debug.LogWarning("Mesh " + dgo.go.name + " has fewer submeshes than materials"); } nuvs = mesh.uv; int[] done = new int[nuvs.Length]; //use this to track uvs that have already been adjusted don't adjust twice for (int l = 0; l < done.Length; l++) { done[l] = -1; } bool triangleArraysOverlap = false; Rect obUVRect = new Rect(); for (int k = 0; k < dgo._submeshTris.Length; k++) { List <int> subTris = dgo._submeshTris[k]; Rect uvRect = dgo.uvRects[k]; if (fixOutOfBoundsUVs) { obUVRect = dgo.obUVRects[dgo.targetSubmeshIdxs[k]]; } for (int l = 0; l < subTris.Count; l++) { int vidx = subTris[l]; if (done[vidx] == -1) { done[vidx] = k; //prevents a uv from being adjusted twice if (fixOutOfBoundsUVs) { nuvs[vidx].x = nuvs[vidx].x / obUVRect.width - obUVRect.x / obUVRect.width; nuvs[vidx].y = nuvs[vidx].y / obUVRect.height - obUVRect.y / obUVRect.height; } nuvs[vidx].x = uvRect.x + nuvs[vidx].x * uvRect.width; nuvs[vidx].y = uvRect.y + nuvs[vidx].y * uvRect.height; } if (done[vidx] != k) { triangleArraysOverlap = true; } } } if (triangleArraysOverlap) { Debug.LogWarning(dgo.go.name + "has submeshes which share verticies. Adjusted uvs may not map correctly in combined atlas."); } nverts.CopyTo(verts, vertsIdx); nnorms.CopyTo(normals, vertsIdx); ntangs.CopyTo(tangents, vertsIdx); nuvs.CopyTo(uvs, vertsIdx); mesh.uv1.CopyTo(uv1s, vertsIdx); mesh.uv2.CopyTo(uv2s, vertsIdx); mesh.colors.CopyTo(colors, vertsIdx); // ntris = mesh.triangles; // for (int j = 0; j < ntris.Length; j++){ // ntris[j] = ntris[j] + vertsIdx; // } for (int combinedMeshIdx = 0; combinedMeshIdx < targSubmeshTidx.Length; combinedMeshIdx++) { dgo.submeshTriIdxs[combinedMeshIdx] = targSubmeshTidx[combinedMeshIdx]; } for (int j = 0; j < dgo._submeshTris.Length; j++) { List <int> sts = dgo._submeshTris[j]; for (int k = 0; k < sts.Count; k++) { sts[k] = sts[k] + vertsIdx; } int combinedMeshIdx = dgo.targetSubmeshIdxs[j]; sts.CopyTo(submeshTris[combinedMeshIdx], targSubmeshTidx[combinedMeshIdx]); dgo.submeshNumTris[combinedMeshIdx] += sts.Count; targSubmeshTidx[combinedMeshIdx] += sts.Count; } dgo.vertIdx = targVidx; instance2combined_map.Add(go, dgo); objectsInCombinedMesh.Add(dgo); targVidx += nverts.Length; for (int j = 0; j < dgo._submeshTris.Length; j++) { dgo._submeshTris[j].Clear(); } dgo._submeshTris = null; if (VERBOSE) { Debug.Log("Added to combined:" + dgo.go.name + " verts:" + nverts.Length); } } return(true); }
void ApplyBlendShapeFramesToMeshAndBuildMap_MergeBlendShapesWithTheSameName() { if (MBVersion.GetMajorVersion() > 5 || (MBVersion.GetMajorVersion() == 5 && MBVersion.GetMinorVersion() >= 3)) { Vector3[] targVerts = new UnityEngine.Vector3[verts.Length]; Vector3[] targNorms = new UnityEngine.Vector3[verts.Length]; Vector3[] targTans = new UnityEngine.Vector3[verts.Length]; MBVersion.ClearBlendShapes(_mesh); // Group source that share the same blendShapeName bool numFramesError = false; Dictionary <string, List <MBBlendShape> > shapeName2objs = new Dictionary <string, List <MBBlendShape> >(); { for (int i = 0; i < blendShapes.Length; i++) { MBBlendShape blendShape = blendShapes[i]; string blendShapeName = ConvertBlendShapeNameToOutputName(blendShape.name); List <MBBlendShape> dgosUsingBlendShape; if (!shapeName2objs.TryGetValue(blendShapeName, out dgosUsingBlendShape)) { dgosUsingBlendShape = new List <MBBlendShape>(); shapeName2objs.Add(blendShapeName, dgosUsingBlendShape); } dgosUsingBlendShape.Add(blendShape); if (dgosUsingBlendShape.Count > 1) { if (dgosUsingBlendShape[0].frames.Length != blendShape.frames.Length) { Debug.LogError("BlendShapes with the same name must have the same number of frames."); numFramesError = true; } } } } if (numFramesError) { return; } if (blendShapesInCombined.Length != blendShapes.Length) { blendShapesInCombined = new MBBlendShape[shapeName2objs.Keys.Count]; } int bsInCombinedIdx = 0; foreach (string shapeName in shapeName2objs.Keys) { List <MBBlendShape> groupOfSrcObjs = shapeName2objs[shapeName]; MBBlendShape firstBlendShape = groupOfSrcObjs[0]; int numFrames = firstBlendShape.frames.Length; int db_numVertsAdded = 0; int db_numObjsAdded = 0; string db_vIdx = ""; for (int frmIdx = 0; frmIdx < numFrames; frmIdx++) { float firstFrameWeight = firstBlendShape.frames[frmIdx].frameWeight; for (int dgoIdx = 0; dgoIdx < groupOfSrcObjs.Count; dgoIdx++) { MBBlendShape blendShape = groupOfSrcObjs[dgoIdx]; MB_DynamicGameObject dgo = instance2Combined_MapGet(blendShape.gameObject); int destIdx = dgo.vertIdx; Debug.Assert(blendShape.frames.Length == numFrames); MBBlendShapeFrame frame = blendShape.frames[frmIdx]; Debug.Assert(frame.frameWeight == firstFrameWeight); Array.Copy(frame.vertices, 0, targVerts, destIdx, frame.vertices.Length); Array.Copy(frame.normals, 0, targNorms, destIdx, frame.normals.Length); Array.Copy(frame.tangents, 0, targTans, destIdx, frame.tangents.Length); if (frmIdx == 0) { db_numVertsAdded += frame.vertices.Length; db_vIdx += blendShape.gameObject.name + " " + destIdx + ":" + (destIdx + frame.vertices.Length) + ", "; } } db_numObjsAdded += groupOfSrcObjs.Count; MBVersion.AddBlendShapeFrame(_mesh, shapeName, firstFrameWeight, targVerts, targNorms, targTans); // We re-use these arrays restore them to zero _ZeroArray(targVerts, 0, targVerts.Length); _ZeroArray(targNorms, 0, targNorms.Length); _ZeroArray(targTans, 0, targTans.Length); } blendShapesInCombined[bsInCombinedIdx] = firstBlendShape; bsInCombinedIdx++; } //this is necessary to get the renderer to refresh its data about the blendshapes. ((SkinnedMeshRenderer)_targetRenderer).sharedMesh = null; ((SkinnedMeshRenderer)_targetRenderer).sharedMesh = _mesh; // Add the map to the target renderer. if (settings.doBlendShapes) { MB_BlendShape2CombinedMap mapComponent = _targetRenderer.GetComponent <MB_BlendShape2CombinedMap>(); if (mapComponent == null) { mapComponent = _targetRenderer.gameObject.AddComponent <MB_BlendShape2CombinedMap>(); } SerializableSourceBlendShape2Combined map = mapComponent.GetMap(); BuildSrcShape2CombinedMap(map, blendShapesInCombined); } } }
public bool CollectBonesToAddForDGO(MB_DynamicGameObject dgo, Renderer r, bool noExtraBonesForMeshRenderers, MeshChannelsCache meshChannelCache) { bool success = true; Debug.Assert(_didSetup, "Need to setup first."); Debug.Assert(combiner.settings.renderType == MB_RenderType.skinnedMeshRenderer); // We could be working with adding and deleting smr body parts from the same rig. Different smrs will share // the same bones. //cache the bone data that we will be adding. Matrix4x4[] dgoBindPoses = dgo._tmpSMR_CachedBindposes = meshChannelCache.GetBindposes(r, out dgo.isSkinnedMeshWithBones); BoneWeight[] dgoBoneWeights = dgo._tmpSMR_CachedBoneWeights = meshChannelCache.GetBoneWeights(r, dgo.numVerts, dgo.isSkinnedMeshWithBones); Transform[] dgoBones = dgo._tmpSMR_CachedBones = combiner._getBones(r, dgo.isSkinnedMeshWithBones); for (int i = 0; i < dgoBones.Length; i++) { if (dgoBones[i] == null) { Debug.LogError("Source mesh r had a 'null' bone. Bones must not be null: " + r); success = false; } } if (!success) { return(success); } if (noExtraBonesForMeshRenderers) { if (MB_Utility.GetRenderer(dgo.gameObject) is MeshRenderer) { // We are visiting a single dgo which is a MeshRenderer. // It may be the child decendant of a bone in another skinned mesh that is being baked or is already in the combined mesh. We need to find that bone if it exists. // We need to check our parent ancestors and search the bone lists of the other dgos being added or previously baked looking for bones that may have been added Debug.Assert(dgoBones.Length == 1 && dgoBindPoses.Length == 1); // find and cache the parent bone for this MeshRenderer (it may not be the transform.parent) bool foundBoneParent = false; BoneAndBindpose boneParent = new BoneAndBindpose(); { Transform t = dgo.gameObject.transform.parent; while (t != null) { // Look for parent peviously baked in the combined mesh. foreach (BoneAndBindpose b in boneAndBindPose2idx.Keys) { if (b.bone == t) { boneParent = b; foundBoneParent = true; break; } } // Look for parent in something we are adding. foreach (BoneAndBindpose b in bonesToAdd) { if (b.bone == t) { boneParent = b; foundBoneParent = true; break; } } if (foundBoneParent) { break; } else { t = t.parent; } } } if (foundBoneParent) { dgoBones[0] = boneParent.bone; dgoBindPoses[0] = boneParent.bindPose; } } } // The mesh being added may not use all bones on the rig. Find the bones actually used. int[] usedBoneIdx2srcMeshBoneIdx; { /* * HashSet<int> usedBones = new HashSet<int>(); * for (int j = 0; j < dgoBoneWeights.Length; j++) * { * usedBones.Add(dgoBoneWeights[j].boneIndex0); * usedBones.Add(dgoBoneWeights[j].boneIndex1); * usedBones.Add(dgoBoneWeights[j].boneIndex2); * usedBones.Add(dgoBoneWeights[j].boneIndex3); * } * * usedBoneIdx2srcMeshBoneIdx = new int[usedBones.Count]; * usedBones.CopyTo(usedBoneIdx2srcMeshBoneIdx); */ } { usedBoneIdx2srcMeshBoneIdx = new int[dgoBones.Length]; for (int i = 0; i < usedBoneIdx2srcMeshBoneIdx.Length; i++) { usedBoneIdx2srcMeshBoneIdx[i] = i; } } // For each bone see if it exists in the bones array (with the same bindpose.). // We might be baking several skinned meshes on the same rig. We don't want duplicate bones in the bones array. for (int i = 0; i < dgoBones.Length; i++) { bool foundInBonesList = false; int bidx; int dgoBoneIdx = usedBoneIdx2srcMeshBoneIdx[i]; BoneAndBindpose bb = new BoneAndBindpose(dgoBones[dgoBoneIdx], dgoBindPoses[dgoBoneIdx]); if (boneAndBindPose2idx.TryGetValue(bb, out bidx)) { if (dgoBones[dgoBoneIdx] == combiner.bones[bidx] && !boneIdxsToDelete.Contains(bidx) && dgoBindPoses[dgoBoneIdx] == combiner.bindPoses[bidx]) { foundInBonesList = true; } } if (!foundInBonesList) { if (!bonesToAdd.Contains(bb)) { bonesToAdd.Add(bb); } } } dgo._tmpSMRIndexesOfSourceBonesUsed = usedBoneIdx2srcMeshBoneIdx; return(success); }
public static void AddBonesToNewBonesArrayAndAdjustBWIndexes(MB3_MeshCombinerSingle combiner, MB_DynamicGameObject dgo, Renderer r, int vertsIdx, Transform[] nbones, BoneWeight[] nboneWeights, MeshChannelsCache meshChannelCache) { Transform[] dgoBones = dgo._tmpSMR_CachedBones; Matrix4x4[] dgoBindPoses = dgo._tmpSMR_CachedBindposes; BoneWeight[] dgoBoneWeights = dgo._tmpSMR_CachedBoneWeights; int[] srcIndex2combinedIndexMap = new int[dgoBones.Length]; for (int j = 0; j < dgo._tmpSMRIndexesOfSourceBonesUsed.Length; j++) { int dgoBoneIdx = dgo._tmpSMRIndexesOfSourceBonesUsed[j]; for (int k = 0; k < nbones.Length; k++) { if (dgoBones[dgoBoneIdx] == nbones[k]) { if (dgoBindPoses[dgoBoneIdx] == combiner.bindPoses[k]) { srcIndex2combinedIndexMap[dgoBoneIdx] = k; break; } } } } //remap the bone weights for this dgo //build a list of usedBones, can't trust dgoBones because it contains all bones in the rig for (int j = 0; j < dgoBoneWeights.Length; j++) { int newVertIdx = vertsIdx + j; nboneWeights[newVertIdx].boneIndex0 = srcIndex2combinedIndexMap[dgoBoneWeights[j].boneIndex0]; nboneWeights[newVertIdx].boneIndex1 = srcIndex2combinedIndexMap[dgoBoneWeights[j].boneIndex1]; nboneWeights[newVertIdx].boneIndex2 = srcIndex2combinedIndexMap[dgoBoneWeights[j].boneIndex2]; nboneWeights[newVertIdx].boneIndex3 = srcIndex2combinedIndexMap[dgoBoneWeights[j].boneIndex3]; nboneWeights[newVertIdx].weight0 = dgoBoneWeights[j].weight0; nboneWeights[newVertIdx].weight1 = dgoBoneWeights[j].weight1; nboneWeights[newVertIdx].weight2 = dgoBoneWeights[j].weight2; nboneWeights[newVertIdx].weight3 = dgoBoneWeights[j].weight3; } // repurposing the _tmpIndexesOfSourceBonesUsed since //we don't need it anymore and this saves a memory allocation . remap the indexes that point to source bones to combined bones. for (int j = 0; j < dgo._tmpSMRIndexesOfSourceBonesUsed.Length; j++) { dgo._tmpSMRIndexesOfSourceBonesUsed[j] = srcIndex2combinedIndexMap[dgo._tmpSMRIndexesOfSourceBonesUsed[j]]; } dgo.indexesOfBonesUsed = dgo._tmpSMRIndexesOfSourceBonesUsed; dgo._tmpSMRIndexesOfSourceBonesUsed = null; dgo._tmpSMR_CachedBones = null; dgo._tmpSMR_CachedBindposes = null; dgo._tmpSMR_CachedBoneWeights = null; //check original bones and bindPoses /* * for (int j = 0; j < dgo.indexesOfBonesUsed.Length; j++) { * Transform bone = bones[dgo.indexesOfBonesUsed[j]]; * Matrix4x4 bindpose = bindPoses[dgo.indexesOfBonesUsed[j]]; * bool found = false; * for (int k = 0; k < dgo._originalBones.Length; k++) { * if (dgo._originalBones[k] == bone && dgo._originalBindPoses[k] == bindpose) { * found = true; * } * } * if (!found) Debug.LogError("A Mismatch between original bones and bones array. " + dgo.name); * } */ }
public void CopyBonesWeAreKeepingToNewBonesArrayAndAdjustBWIndexes(Transform[] nbones, Matrix4x4[] nbindPoses, BoneWeight[] nboneWeights, int totalDeleteVerts) { // bones are copied separately because some dgos share bones if (boneIdxsToDelete.Count > 0) { int[] boneIdxsToDel = new int[boneIdxsToDelete.Count]; boneIdxsToDelete.CopyTo(boneIdxsToDel); Array.Sort(boneIdxsToDel); //bones are being moved in bones array so need to do some remapping int[] oldBonesIndex2newBonesIndexMap = new int[combiner.bones.Length]; int newIdx = 0; int indexInDeleteList = 0; //bones were deleted so we need to rebuild bones and bind poses //and build a map of old bone indexes to new bone indexes //do this by copying old to new skipping ones we are deleting for (int i = 0; i < combiner.bones.Length; i++) { if (indexInDeleteList < boneIdxsToDel.Length && boneIdxsToDel[indexInDeleteList] == i) { //we are deleting this bone so skip its index indexInDeleteList++; oldBonesIndex2newBonesIndexMap[i] = -1; } else { oldBonesIndex2newBonesIndexMap[i] = newIdx; nbones[newIdx] = combiner.bones[i]; nbindPoses[newIdx] = combiner.bindPoses[i]; newIdx++; } } //adjust the indexes on the boneWeights int numVertKeeping = combiner.boneWeights.Length - totalDeleteVerts; { for (int i = 0; i < numVertKeeping; i++) { BoneWeight bw = nboneWeights[i]; bw.boneIndex0 = oldBonesIndex2newBonesIndexMap[bw.boneIndex0]; bw.boneIndex1 = oldBonesIndex2newBonesIndexMap[bw.boneIndex1]; bw.boneIndex2 = oldBonesIndex2newBonesIndexMap[bw.boneIndex2]; bw.boneIndex3 = oldBonesIndex2newBonesIndexMap[bw.boneIndex3]; nboneWeights[i] = bw; } } /* * unsafe * { * fixed (BoneWeight* boneWeightFirstPtr = &nboneWeights[0]) * { * BoneWeight* boneWeightPtr = boneWeightFirstPtr; * for (int i = 0; i < numVertKeeping; i++) * { * boneWeightPtr->boneIndex0 = oldBonesIndex2newBonesIndexMap[boneWeightPtr->boneIndex0]; * boneWeightPtr->boneIndex1 = oldBonesIndex2newBonesIndexMap[boneWeightPtr->boneIndex1]; * boneWeightPtr->boneIndex2 = oldBonesIndex2newBonesIndexMap[boneWeightPtr->boneIndex2]; * boneWeightPtr->boneIndex3 = oldBonesIndex2newBonesIndexMap[boneWeightPtr->boneIndex3]; * boneWeightPtr++; * } * } * } */ //adjust the bone indexes on the dgos from old to new for (int i = 0; i < combiner.mbDynamicObjectsInCombinedMesh.Count; i++) { MB_DynamicGameObject dgo = combiner.mbDynamicObjectsInCombinedMesh[i]; for (int j = 0; j < dgo.indexesOfBonesUsed.Length; j++) { dgo.indexesOfBonesUsed[j] = oldBonesIndex2newBonesIndexMap[dgo.indexesOfBonesUsed[j]]; } } } else { //no bones are moving so can simply copy bones from old to new Array.Copy(combiner.bones, nbones, combiner.bones.Length); Array.Copy(combiner.bindPoses, nbindPoses, combiner.bindPoses.Length); } }