123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367 |
- using UnityEngine;
- using System.Collections;
- using System.Collections.Specialized;
- using System;
- using System.Collections.Generic;
- using System.Text;
- using DigitalOpus.MB.Core;
- namespace DigitalOpus.MB.Core
- {
- public partial class MB3_MeshCombinerSingle : MB3_MeshCombiner
- {
- public class UVAdjuster_Atlas
- {
- MB2_TextureBakeResults textureBakeResults;
- MB2_LogLevel LOG_LEVEL;
- int[] numTimesMatAppearsInAtlas;
- MB_MaterialAndUVRect[] matsAndSrcUVRect;
- public UVAdjuster_Atlas(MB2_TextureBakeResults tbr, MB2_LogLevel ll)
- {
- textureBakeResults = tbr;
- LOG_LEVEL = ll;
- matsAndSrcUVRect = tbr.materialsAndUVRects;
- //count the number of times a material appears in the atlas. used for fast lookup
- numTimesMatAppearsInAtlas = new int[matsAndSrcUVRect.Length];
- for (int i = 0; i < matsAndSrcUVRect.Length; i++)
- {
- if (numTimesMatAppearsInAtlas[i] > 1)
- {
- continue;
- }
- int count = 1;
- for (int j = i + 1; j < matsAndSrcUVRect.Length; j++)
- {
- if (matsAndSrcUVRect[i].material == matsAndSrcUVRect[j].material)
- {
- count++;
- }
- }
- numTimesMatAppearsInAtlas[i] = count;
- if (count > 1)
- {
- //allMatsAreUnique = false;
- for (int j = i + 1; j < matsAndSrcUVRect.Length; j++)
- {
- if (matsAndSrcUVRect[i].material == matsAndSrcUVRect[j].material)
- {
- numTimesMatAppearsInAtlas[j] = count;
- }
- }
- }
- }
- }
- 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));
- }
- /// <summary>
- /// A material can appear more than once in an atlas if using fixOutOfBoundsUVs.
- /// in this case you need to use the UV rect of the mesh to find the correct rectangle.
- /// If the all properties on the mat use the same tiling then
- /// encapsulatingRect can be larger and will include baked UV and material tiling
- /// If mat uses different tiling for different maps then encapsulatingRect is the uvs of
- /// source mesh used to bake atlas and sourceMaterialTilingOut is 0,0,1,1. This works because
- /// material tiling was baked into the atlas.
- /// </summary>
- public bool TryMapMaterialToUVRect(Material mat, Mesh m, int submeshIdx, int idxInResultMats,
- MB3_MeshCombinerSingle.MeshChannelsCache meshChannelCache,
- Dictionary<int, MB_Utility.MeshAnalysisResult[]> meshAnalysisCache,
- out MB_TextureTilingTreatment tilingTreatment,
- out Rect rectInAtlas,
- out Rect encapsulatingRectOut,
- out Rect sourceMaterialTilingOut,
- out int sliceIdx,
- ref String errorMsg,
- MB2_LogLevel logLevel)
- {
- if (textureBakeResults.version < MB2_TextureBakeResults.VERSION)
- {
- textureBakeResults.UpgradeToCurrentVersion(textureBakeResults);
- }
- tilingTreatment = MB_TextureTilingTreatment.unknown;
- if (textureBakeResults.materialsAndUVRects.Length == 0)
- {
- errorMsg = "The 'Texture Bake Result' needs to be re-baked to be compatible with this version of Mesh Baker. Please re-bake using the MB3_TextureBaker.";
- rectInAtlas = new Rect();
- encapsulatingRectOut = new Rect();
- sourceMaterialTilingOut = new Rect();
- sliceIdx = -1;
- return false;
- }
- if (mat == null)
- {
- rectInAtlas = new Rect();
- encapsulatingRectOut = new Rect();
- sourceMaterialTilingOut = new Rect();
- sliceIdx = -1;
- errorMsg = String.Format("Mesh {0} Had no material on submesh {1} cannot map to a material in the atlas", m.name, submeshIdx);
- return false;
- }
- if (submeshIdx >= m.subMeshCount)
- {
- errorMsg = "Submesh index is greater than the number of submeshes";
- rectInAtlas = new Rect();
- encapsulatingRectOut = new Rect();
- sourceMaterialTilingOut = new Rect();
- sliceIdx = -1;
- return false;
- }
- //find the first index of this material
- int idx = -1;
- for (int i = 0; i < matsAndSrcUVRect.Length; i++)
- {
- if (mat == matsAndSrcUVRect[i].material)
- {
- idx = i;
- break;
- }
- }
- // if couldn't find material
- if (idx == -1)
- {
- rectInAtlas = new Rect();
- encapsulatingRectOut = new Rect();
- sourceMaterialTilingOut = new Rect();
- sliceIdx = -1;
- errorMsg = String.Format("Material {0} could not be found in the Texture Bake Result", mat.name);
- return false;
- }
- bool considerUVs = textureBakeResults.GetConsiderMeshUVs(idxInResultMats, mat);
- if (!considerUVs)
- {
- if (numTimesMatAppearsInAtlas[idx] != 1)
- {
- Debug.LogError("There is a problem with this TextureBakeResults. FixOutOfBoundsUVs is false and a material appears more than once: " + matsAndSrcUVRect[idx].material + " appears: " + numTimesMatAppearsInAtlas[idx]);
- }
- MB_MaterialAndUVRect mr = matsAndSrcUVRect[idx];
- rectInAtlas = mr.atlasRect;
- tilingTreatment = mr.tilingTreatment;
- encapsulatingRectOut = mr.GetEncapsulatingRect();
- sourceMaterialTilingOut = mr.GetMaterialTilingRect();
- sliceIdx = mr.textureArraySliceIdx;
- return true;
- }
- else
- {
- //todo what if no UVs
- //Find UV rect in source mesh
- MB_Utility.MeshAnalysisResult[] mar;
- if (!meshAnalysisCache.TryGetValue(m.GetInstanceID(), out mar))
- {
- mar = new MB_Utility.MeshAnalysisResult[m.subMeshCount];
- for (int j = 0; j < m.subMeshCount; j++)
- {
- Vector2[] uvss = meshChannelCache.GetUv0Raw(m);
- MB_Utility.hasOutOfBoundsUVs(uvss, m, ref mar[j], j);
- }
- meshAnalysisCache.Add(m.GetInstanceID(), mar);
- }
- //this could be a mesh that was not used in the texture baking that has huge UV tiling too big for the rect that was baked
- //find a record that has an atlas uvRect capable of containing this
- bool found = false;
- Rect encapsulatingRect = new Rect(0, 0, 0, 0);
- Rect sourceMaterialTiling = new Rect(0, 0, 0, 0);
- if (logLevel >= MB2_LogLevel.trace)
- {
- Debug.Log(String.Format("Trying to find a rectangle in atlas capable of holding tiled sampling rect for mesh {0} using material {1} meshUVrect={2}", m, mat, mar[submeshIdx].uvRect.ToString("f5")));
- }
- for (int i = idx; i < matsAndSrcUVRect.Length; i++)
- {
- MB_MaterialAndUVRect matAndUVrect = matsAndSrcUVRect[i];
- if (matAndUVrect.material == mat)
- {
- if (matAndUVrect.allPropsUseSameTiling)
- {
- encapsulatingRect = matAndUVrect.allPropsUseSameTiling_samplingEncapsulatinRect;
- sourceMaterialTiling = matAndUVrect.allPropsUseSameTiling_sourceMaterialTiling;
- }
- else
- {
- encapsulatingRect = matAndUVrect.propsUseDifferntTiling_srcUVsamplingRect;
- sourceMaterialTiling = new Rect(0, 0, 1, 1);
- }
- if (MB2_TextureBakeResults.IsMeshAndMaterialRectEnclosedByAtlasRect(
- matAndUVrect.tilingTreatment,
- mar[submeshIdx].uvRect,
- sourceMaterialTiling,
- encapsulatingRect,
- logLevel))
- {
- if (logLevel >= MB2_LogLevel.trace)
- {
- Debug.Log("Found rect in atlas capable of containing tiled sampling rect for mesh " + m + " at idx=" + i);
- }
- idx = i;
- found = true;
- break;
- }
- }
- }
- if (found)
- {
- MB_MaterialAndUVRect mr = matsAndSrcUVRect[idx];
- rectInAtlas = mr.atlasRect;
- tilingTreatment = mr.tilingTreatment;
- encapsulatingRectOut = mr.GetEncapsulatingRect();
- sourceMaterialTilingOut = mr.GetMaterialTilingRect();
- sliceIdx = mr.textureArraySliceIdx;
- return true;
- }
- else
- {
- rectInAtlas = new Rect();
- encapsulatingRectOut = new Rect();
- sourceMaterialTilingOut = new Rect();
- sliceIdx = -1;
- errorMsg = String.Format("Could not find a tiled rectangle in the atlas capable of containing the uv and material tiling on mesh {0} for material {1}. Was this mesh included when atlases were baked?", m.name, mat);
- return false;
- }
- }
- }
- }
- }
- }
|