// PROBLEM: If multiple intermediate levels of the hierarchy have the same name // then children will be randomly assigned. This could happen if Av0 has children, // Av1 does not and is added, but then Av2 has children, which could be parented to // either Av1 or Av0. Or, if the original model uses a name multiple times. // TODO: Identify problem and warn. static void Merge(Transform mergeFrom, Transform mergeTo) { // When names match, merge var mergeChildren = new List <Transform>(); foreach (var childFrom in mergeFrom.Children()) { var childTo = mergeTo.NameFindInChildren(childFrom.name); if (childTo.Length == 0) { // ChildFrom is not in the hierarchy mergeChildren.Add(childFrom); continue; } if (PrefabUtility.GetPrefabAssetType(childFrom) != PrefabAssetType.NotAPrefab) { // ChildFrom is a Prefab and is already present in hierarchy since childTo != null continue; } if (childFrom.transform.childCount == 0) { // ChildFrom is a distinct instance even if childTo is present mergeChildren.Add(childFrom); continue; } // ChildFrom and ChildTo match, so merge children instead Merge(childFrom, childTo[0].transform); } foreach (var childFrom in mergeChildren) { EP.SetParent(childFrom, mergeTo); } }
// PROBLEM: Light source meshes are generally small and could use a lower level of detail // in most cases. Prefabs with custom meshes could address this for sphere and cylinder // OPTION: Provide a component to monitor the light intensity and update the emissive material // accordingly - both static and dynamic. This could also tag actual child light sources. public static GameObject CreatePrimitiveSource(Light light, PrimitiveType primitiveType) { var source = GameObject.CreatePrimitive(primitiveType); source.SetActive(light.enabled); source.layer = light.gameObject.layer; // Make source constitent with search source.name = ConfigureName(light.name); EP.SetParent(source.transform, light.transform); // Position source around light source.transform.localPosition = Vector3.zero; source.transform.localRotation = Quaternion.identity; // Source scale depends on light type Object.DestroyImmediate(source.GetComponent <Collider>()); if (light.gameObject.isStatic) { var staticFlags = (StaticEditorFlags) ~0; staticFlags &= ~StaticEditorFlags.OccluderStatic; staticFlags &= ~StaticEditorFlags.ContributeGI; GameObjectUtility.SetStaticEditorFlags(source, staticFlags); } else { source.isStatic = false; } LightSourceMeshRenderer(light, source.GetComponent <MeshRenderer>()); Undo.RegisterCreatedObjectUndo(source, "Create Primitive Light Source"); return(source); }
// Model export generates meshes in world coordinates // In order to retain information, each prefab is replaced with a transformed tetrahedron static void ConfigurePrefab(Transform placeholder, CachedPrefab cached) { var prefab = (PrefabUtility.InstantiatePrefab(cached.prefab) as GameObject).transform; EP.SetParent(prefab, placeholder.parent); prefab.localPosition = placeholder.localPosition; prefab.localRotation = placeholder.localRotation; prefab.localScale = placeholder.localScale; prefab.name = placeholder.name; }
// TODO: This, like a grid, is a standard layout // IDEA: When TransformData is included, provide utilities to generate layouts, and to replicate gameobjects over them static GameObject[] RotateCopies(GameObject original, Quaternion rotation, int count) { var copyList = new GameObject[count]; copyList[0] = original; for (int c = 1; c < count; ++c) { var copy = EP.Instantiate(original); EP.SetParent(copy.transform, original.transform.parent); copy.transform.localPosition = rotation * copyList[c - 1].transform.localPosition; copy.transform.localRotation = rotation * copyList[c - 1].transform.localRotation; copyList[c] = copy; } return(copyList); }
static void CreateLinearYSource(Light light) { // Create a self-illuminated cylinder var source = CreatePrimitiveSource(light, PrimitiveType.Cylinder); source.transform.localScale = new Vector3(pointDiameter, light.areaSize.y / 2f, pointDiameter); // Create planes covering all emission directions var side0 = MakeAreaCopy(light, new Vector2(pointDiameter, light.areaSize.y)); EP.SetParent(side0.transform, light.transform); var sideList = RotateCopies(side0, Quaternion.Euler(360f / linearSources, 0f, 0f), linearSources); for (int s = 0; s < sideList.Length; ++s) { sideList[s].name = light.name + "_Side" + s; } }
/// <summary> /// Make a copy of light source as area light, with equivalent illumination /// </summary> /// <remarks> /// Intensity is scaled relative to the area of the light /// </remarks> public static GameObject MakeAreaCopy(Light light, Vector2 areaSize) { var gameObject = EP.Instantiate(); GameObjectUtility.SetStaticEditorFlags(gameObject, (StaticEditorFlags) ~0); EP.SetParent(gameObject.transform, light.transform.parent); gameObject.transform.localPosition = light.transform.localPosition; gameObject.transform.localRotation = light.transform.localRotation; var areaLight = EP.AddComponent <Light>(gameObject); areaLight.lightmapBakeType = LightmapBakeType.Baked; areaLight.type = LightType.Rectangle; areaLight.areaSize = areaSize; areaLight.intensity = light.intensity / (areaSize.x * areaSize.y); areaLight.color = light.color; areaLight.range = light.range; return(gameObject); }
static void MergeGroup(string pathName, List <GameObject> group) { // Gather LODGroup and Renderers LODGroup lodGroup = null; var renderers = new List <RendererSort>(); foreach (var gameObject in group) { var renderer = gameObject.GetComponent <Renderer>(); if (renderer) { renderers.Add(new RendererSort(renderer)); } var isGroup = gameObject.GetComponent <LODGroup>(); if (isGroup) { var lods = isGroup.GetLODs(); foreach (var lod in lods) { foreach (var lodRenderer in lod.renderers) { // Renderers must begin as siblings of LODGroup EP.SetParent(lodRenderer.transform, isGroup.transform.parent); renderers.Add(new RendererSort(lodRenderer)); } } if (!!lodGroup || !!renderer) { // LODGroup manager cannot be duplicated, and cannot have renderer component Debug.LogWarning($"Removing LODGroup found on {gameObject.Path()}"); EP.Destroy(isGroup); continue; } lodGroup = isGroup; } } if (!lodGroup) { lodGroup = EP.AddComponent <LODGroup>(EP.Instantiate()); } // renderers[0] has the lowest vertex count renderers.Sort((l, m) => l.vertexCount - m.vertexCount); // Remove missing meshes and duplicate levels of detail var vertexCount = 0; var removeRenderers = new List <RendererSort>(); foreach (var renderer in renderers) { if (vertexCount == renderer.vertexCount) { removeRenderers.Add(renderer); continue; } vertexCount = renderer.vertexCount; } foreach (var renderer in removeRenderers) { renderers.Remove(renderer); EP.Destroy(renderer.renderer.gameObject); // NOTE: Duplicate mesh asset could be removed } if (renderers.Count == 0) { EP.Destroy(lodGroup.gameObject); return; } // renderers[0] has the highest vertrex count renderers.Reverse(); // Configure manager in hierarchy lodGroup.gameObject.name = pathName.Substring(pathName.LastIndexOf('/') + 1); EP.SetParent(lodGroup.transform, renderers[0].renderer.transform.parent); lodGroup.transform.localPosition = renderers[0].renderer.transform.localPosition; lodGroup.transform.localRotation = renderers[0].renderer.transform.localRotation; lodGroup.transform.localScale = renderers[0].renderer.transform.localScale; for (var r = 0; r < renderers.Count; ++r) { var renderer = renderers[r].renderer; // TODO: Used PathNameExtension for this! var lodIndex = renderer.gameObject.name.LastIndexOf(lodSuffix); if (lodIndex >= 0) { renderer.gameObject.name = renderer.gameObject.name.Substring(0, lodIndex); } renderer.gameObject.name += lodSuffix + r.ToString(); EP.SetParent(renderer.transform, lodGroup.transform); } // Configure the group var lodList = new LOD[renderers.Count]; for (var r = 0; r < renderers.Count; ++r) { lodList[r].renderers = new[] { renderers[r].renderer } } ; ConfigureLODGroup(lodGroup, lodList); // Configure the renderers and materials foreach (var lod in lodGroup.GetLODs()) { foreach (var renderer in lod.renderers) { SetLightmapScale(renderer); foreach (var material in renderer.sharedMaterials) { UseFadingShader(material); } } } }