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 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 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)); } }
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); * } */ }