void DrawRegionOutline(EBWorldPainterData.Region region, Color outlineColor) { for (var i = 0; i < region.points.Count; ++i) { EBWorldPainterData.Point p0 = region.points[i]; EBWorldPainterData.Point p1 = region.points[(i + 1) % region.points.Count]; DrawLine(p0, p1, outlineColor); } }
void SwitchEditMode(EditMode em) { editMode = em; lastClickedPoint = null; lastClickedRegion = null; if (em == EditMode.BoundingBoxes) { var renderers = (MeshRenderer[])FindObjectsOfType(typeof(MeshRenderer)); worldPainterData.SetRegionBounds(renderers); } Repaint(); }
void DrawRegionBoundingBox(EBWorldPainterData.Region region, Color outlineColor) { for (var i = 0; i < region.points.Count; ++i) { EBWorldPainterData.Point p0 = new EBWorldPainterData.Point(region.bounds.min.x, region.bounds.min.z); EBWorldPainterData.Point p1 = new EBWorldPainterData.Point(region.bounds.max.x, region.bounds.min.z); EBWorldPainterData.Point p2 = new EBWorldPainterData.Point(region.bounds.max.x, region.bounds.max.z); EBWorldPainterData.Point p3 = new EBWorldPainterData.Point(region.bounds.min.x, region.bounds.max.z); DrawLine(p0, p1, outlineColor); DrawLine(p1, p2, outlineColor); DrawLine(p2, p3, outlineColor); DrawLine(p3, p0, outlineColor); } }
void DrawDataLayer(EBWorldPainterData.Region region) { var point = new Vector3(region.Center().location.x, 0.0f, region.Center().location.y); Vector2 center = cam.WorldToViewportPoint(point) * textureSize; Rect rect = new Rect(center.x - 25.0f, textureSize - center.y - 5.0f, 50.0f, 10.0f); switch (dataLayer) { case EBWorldPainterData.eDATA_LAYER.LightmapSize: region.dataLayers[(int)dataLayer].value = (int)(EBWorldPainterData.eLIGHTMAP_SIZE)EditorGUI.EnumPopup(rect, (EBWorldPainterData.eLIGHTMAP_SIZE)(region.dataLayers[(int)dataLayer].value)); break; case EBWorldPainterData.eDATA_LAYER.RenderSetting: region.dataLayers[(int)dataLayer].value = (int)(EBWorldPainterData.eLIGHTMAP_SIZE)EditorGUI.EnumPopup(rect, (EBWorldPainterData.eRENDER_SETTING)(region.dataLayers[(int)dataLayer].value)); break; } }
private void MergeLightmaps(Partition partition, EBWorldPainterData.Region region, Dictionary <int, Texture2D> lightmaps) { int maxLightMapIndex = 0; foreach (int key in lightmaps.Keys) { maxLightMapIndex = Mathf.Max(maxLightMapIndex, key); } List <Rect>[] lightmapRects = new List <Rect> [maxLightMapIndex + 1]; for (int i = 0; i <= maxLightMapIndex; ++i) { lightmapRects[i] = new List <Rect>(); } //crop each meshes lightmaps out of the partition foreach (var instance in partition.instances) { var renderer = instance.renderer; if (!RendererHasLightmap(renderer)) { continue; } if (!lightmaps.ContainsKey(renderer.lightmapIndex)) { Debug.LogWarning("Some lightmaps weren't loaded that geometry references..." + renderer.lightmapIndex); continue; } var filter = renderer.GetComponent <MeshFilter> (); var lightmapUVs = filter.sharedMesh.uv2; int subMeshCount = filter.sharedMesh.subMeshCount; instance.lightmapInfo = new LightMapInfo[subMeshCount]; for (int i = 0; i < subMeshCount; ++i) { Vector2 uvMin = new Vector2(float.MaxValue, float.MaxValue); Vector2 uvMax = new Vector2(float.MinValue, float.MinValue); var indices = filter.sharedMesh.GetIndices(i); foreach (var index in indices) { Vector2 uv = lightmapUVs [index]; uv.x = uv.x * renderer.lightmapScaleOffset.x + renderer.lightmapScaleOffset.z; uv.y = uv.y * renderer.lightmapScaleOffset.y + renderer.lightmapScaleOffset.w; uvMin = Vector2.Min(uvMin, uv); uvMax = Vector2.Max(uvMax, uv); } var rect = new Rect(uvMin.x, uvMin.y, uvMax.x - uvMin.x, uvMax.y - uvMin.y); lightmapRects[renderer.lightmapIndex].Add(rect); instance.lightmapInfo[i] = new LightMapInfo(); instance.lightmapInfo[i].index = lightmapRects[renderer.lightmapIndex].IndexOf(rect); } } List <KeyValuePair <int, Rect> > allRects = new List <KeyValuePair <int, Rect> > (); for (int lm = 0; lm < lightmaps.Count; ++lm) { int[] remap = new int[lightmapRects [lm].Count]; List <Rect> remappedRects = new List <Rect> (); for (int i = 0; i < lightmapRects[lm].Count; ++i) { var curOrigRect = lightmapRects [lm] [i]; bool remapped = false; for (int j = 0; j < remappedRects.Count; ++j) { var curRemappedRect = remappedRects [j]; if (Intersect(curOrigRect, curRemappedRect)) { float l = Mathf.Min(curOrigRect.xMin, curRemappedRect.xMin); float r = Mathf.Max(curOrigRect.xMax, curRemappedRect.xMax); float t = Mathf.Min(curOrigRect.yMin, curRemappedRect.yMin); float b = Mathf.Max(curOrigRect.yMax, curRemappedRect.yMax); Rect encapsulatedRect = new Rect(l, t, r - l, b - t); remappedRects[j] = encapsulatedRect; remap[i] = j; remapped = true; break; } } if (!remapped) { remappedRects.Add(curOrigRect); remap[i] = remappedRects.IndexOf(curOrigRect); } } for (int i = 0; i < remappedRects.Count; ++i) { Rect rect = remappedRects [i]; Texture2D lightmap = lightmaps [lm]; float lightmapWidth = (float)lightmap.width; float lightmapHeight = (float)lightmap.height; //we need to bring this rect into a whole integer, as we crop on pixel boundaries; float left = Mathf.Clamp01(Mathf.Floor(rect.x * lightmapWidth) / lightmapWidth); float top = Mathf.Clamp01(Mathf.Floor(rect.y * lightmapHeight) / lightmapHeight); float width = Mathf.Clamp01(Mathf.Clamp01(Mathf.Ceil((rect.x + rect.width) * lightmapWidth) / lightmapWidth) - left); float height = Mathf.Clamp01(Mathf.Clamp01(Mathf.Ceil((rect.y + rect.height) * lightmapHeight) / lightmapHeight) - top); remappedRects[i] = new Rect(left, top, width, height); allRects.Add(new KeyValuePair <int, Rect>(lm, remappedRects[i])); } foreach (var instance in partition.instances) { var renderer = instance.renderer; if (!RendererIsMergeable(renderer) || !RendererHasLightmap(renderer) || (renderer.lightmapIndex != lm)) { continue; } if (!lightmaps.ContainsKey(renderer.lightmapIndex)) { Debug.LogError("Lightmap " + renderer.lightmapIndex + " wasn't loaded but some geometry references it - are the lightmaps in the right place?"); continue; } var filter = renderer.GetComponent <MeshFilter> (); int subMeshCount = filter.sharedMesh.subMeshCount; for (int i = 0; i < subMeshCount; ++i) { var rect = remappedRects [remap [instance.lightmapInfo [i].index]]; instance.lightmapInfo[i].index = allRects.IndexOf(new KeyValuePair <int, Rect>(renderer.lightmapIndex, rect)); instance.lightmapInfo[i].localScale = new Vector2(1.0f / rect.width, 1.0f / rect.height); instance.lightmapInfo[i].localOffset = new Vector2(-rect.x / rect.width, -rect.y / rect.height); } } } List <Texture2D> croppedLightmaps = new List <Texture2D> (); foreach (var kv in allRects) { int lm = kv.Key; Rect rect = kv.Value; Texture2D cropped = CropTexture(lightmaps [lm], rect); croppedLightmaps.Add(cropped); } //pack all the cropped lightmaps int lightmapSize = 1024; if (region.dataLayers != null) { int regionLightmapSize = region.dataLayers [(int)EBWorldPainterData.eDATA_LAYER.LightmapSize].value; if (regionLightmapSize != 0) { lightmapSize = regionLightmapSize; } } if (croppedLightmaps.Count == 0) { lightmapSize = 1; } Texture2D packedTexture = new Texture2D(lightmapSize, lightmapSize, TextureFormat.RGB24, true); partition.lightmapRects = packedTexture.PackTextures(croppedLightmaps.ToArray(), 0, lightmapSize, false); packedTexture.Apply(true, false); if ((BuildSettings.Target != BuildTarget.Android) && (BuildSettings.Target != BuildTarget.iOS)) { //multiply by 4 * alpha for (int y = 0; y < packedTexture.height; ++y) { for (int x = 0; x < packedTexture.width; ++x) { var color = packedTexture.GetPixel(x, y); color.r *= 4.0f * color.a; color.g *= 4.0f * color.a; color.b *= 4.0f * color.a; color.a = 1.0f; packedTexture.SetPixel(x, y, color); } } } else { for (int y = 0; y < packedTexture.height; ++y) { for (int x = 0; x < packedTexture.width; ++x) { var color = packedTexture.GetPixel(x, y); color.a = 1.0f; packedTexture.SetPixel(x, y, color); } } } packedTexture.Apply(true, false); string filename = GetMergeOutputPath(partition.name); if (File.Exists(filename)) { //it may be checked into perforce, remove 'read-only' flag File.SetAttributes(filename, FileAttributes.Normal); } File.WriteAllBytes(filename, packedTexture.EncodeToPNG()); AssetDatabase.Refresh(); partition.lightmap = (Texture2D)AssetDatabase.LoadMainAssetAtPath(filename); }
void OnGUI() { if (!inited) { return; } const int offset = 20; Rect textureRect = Rect.MinMaxRect(offset, offset, textureSize + offset, textureSize + offset); Rect texturSize = Rect.MinMaxRect(0, 0, textureSize, textureSize); SetMode(); //Deal with the mouse if ((Event.current != null) && Event.current.isMouse && textureRect.Contains(Event.current.mousePosition)) { Vector3 worldMousePos3 = cam.ViewportToWorldPoint(new Vector3(((float)Event.current.mousePosition.x - textureRect.x) / (float)textureSize, 1.0f - ((float)Event.current.mousePosition.y - textureRect.y) / (float)textureSize, 0.0f)); EBWorldPainterData.Point worldMousePos = new EBWorldPainterData.Point(worldMousePos3.x, worldMousePos3.z); UpdateClosestPoint(worldPainterData.ClosestPoint(worldMousePos)); UpdateClosestPoints(worldPainterData.ClosestLine(worldMousePos)); UpdateCurrentRegions(worldPainterData.RegionsPointIsInside(worldMousePos)); switch (mode) { case (Mode.None): break; case (Mode.AddPoints): { Vector2 point0 = closestPoints[0].location; Vector2 point1 = closestPoints[1].location; float distToPoint0 = Vector2.Distance(point0, worldMousePos.location); float distToPoint1 = Vector2.Distance(point1, worldMousePos.location); float t = distToPoint0 / (distToPoint0 + distToPoint1); pointToAdd = new EBWorldPainterData.Point(Vector2.Lerp(point0, point1, t)); if (Event.current.type == EventType.MouseDown) { var result = worldPainterData.InsertPoint(pointToAdd, closestPoints[0], closestPoints[1]); if (!result) { } Repaint(); } break; } case (Mode.RemovePoints): { if (Event.current.type == EventType.MouseDown) { var result = worldPainterData.RemovePoint(closestPoint); if (!result) { } Repaint(); } break; } case (Mode.MovePoints): { if (Event.current.type == EventType.MouseDown) { lastClickedPoint = closestPoint; Repaint(); } else if (Event.current.type == EventType.MouseDrag && lastClickedPoint != null) { lastClickedPoint.location = worldMousePos.location; Repaint(); } break; } case (Mode.SplitRegions): { if (Event.current.type == EventType.MouseDown) { if (lastClickedPoint == null) { lastClickedPoint = closestPoint; Repaint(); } else { EBWorldPainterData.Point nextClickedPoint = worldPainterData.ClosestPoint(worldMousePos); if (nextClickedPoint != lastClickedPoint) { EBWorldPainterData.Region[] regions = worldPainterData.RegionsContainingPoints(lastClickedPoint, nextClickedPoint); if (regions.Length == 1) { //don't want to split if the points are in more than one region, as they must be on a boundary of some sort, so splitting wouldn't make sense //TODO: don't split things that are sequential worldPainterData.SplitRegionAcrossPoints(regions[0], lastClickedPoint, nextClickedPoint); } } lastClickedPoint = null; Repaint(); } } break; } case (Mode.CombineRegions): { if (Event.current.type == EventType.MouseDown) { worldPainterData.RemoveLine(closestPoints[0], closestPoints[1]); UpdateCurrentRegions(worldPainterData.RegionsPointIsInside(worldMousePos)); Repaint(); } break; } case (Mode.PickVisibleBase): { if (Event.current.type == EventType.MouseDown) { lastClickedRegion = (currentRegions.Length > 0) ? currentRegions[0] : null; Repaint(); } break; } case (Mode.PaintVisible): { if ((lastClickedRegion != null) && (currentRegions.Length > 0) && (Event.current.type == EventType.MouseDown)) { lastClickedRegion.ToggleSeesRegion(currentRegions[0]); Repaint(); } break; } } } GUILayout.BeginHorizontal(); GUILayout.Label("Zoom", GUILayout.Width(100)); orthographicSize = GUILayout.HorizontalSlider(orthographicSize, 100.0f, 4000.0f); GUILayout.EndHorizontal(); GUI.skin = guiskin; GUILayout.BeginHorizontal(); float camPosZ = GUILayout.VerticalSlider(cam.transform.position.z, worldPainterData.WorldBounds().max.z, worldPainterData.WorldBounds().min.z); GUI.BeginGroup(textureRect); GUI.Box(texturSize, rt); GUI.EndGroup(); GUI.BeginGroup(textureRect); switch (editMode) { case EditMode.Tesselate: //Draw all the points for (int i = 0; i < worldPainterData.points.Count; ++i) { EBWorldPainterData.Point point = worldPainterData.points[i]; TextureFills tex = TextureFills.Gray; switch (mode) { case (Mode.AddPoints): if (point == closestPoint) { tex = TextureFills.Blue; } break; case (Mode.RemovePoints): if (point == closestPoint) { tex = TextureFills.Red; } break; case (Mode.MovePoints): if (point == closestPoint) { tex = TextureFills.Green; } break; case (Mode.SplitRegions): if (point == closestPoint) { tex = TextureFills.Green; } if (point == lastClickedPoint) { tex = TextureFills.Blue; } break; } DrawPoint(point, tex); } //Draw the line we may add if we are splitting regions if (mode == Mode.AddPoints) { DrawPoint(pointToAdd, TextureFills.Blue); } //Draw the line we may add if we are splitting regions if (mode == Mode.SplitRegions && lastClickedPoint != null) { DrawLine(lastClickedPoint, closestPoint, Color.blue); } //Draw all the region outlines foreach (EBWorldPainterData.Region region in worldPainterData.regions) { DrawRegionOutline(region, Color.white); } //Draw the line we may remove if we are combining regions if (mode == Mode.CombineRegions) { DrawLine(closestPoints[0], closestPoints[1], Color.red); } break; case EditMode.Visibility: //Draw all the points for (int i = 0; i < worldPainterData.points.Count; ++i) { DrawPoint(worldPainterData.points[i], TextureFills.Gray); } //Draw all the region outlines foreach (EBWorldPainterData.Region region in worldPainterData.regions) { DrawRegionOutline(region, Color.red); } //outline all the regions our current region sees, and our current region if (lastClickedRegion != null) { foreach (EBWorldPainterData.Region region in worldPainterData.regions) { if (lastClickedRegion.SeesRegion(region)) { DrawRegionOutline(region, Color.green); } } DrawRegionOutline(lastClickedRegion, new Color(0.5f, 0.5f, 1.0f)); } break; case EditMode.Hover: //Draw all the region outlines foreach (EBWorldPainterData.Region region in worldPainterData.regions) { DrawRegionOutline(region, Color.white); } //outline the one we are hovering over if (currentRegions.Length > 0) { DrawRegionOutline(currentRegions[0], new Color(0.0f, 0.5f, 0.0f)); } break; case EditMode.BoundingBoxes: //Draw all the region bounding boxes foreach (EBWorldPainterData.Region region in worldPainterData.regions) { DrawRegionBoundingBox(region, Color.white); } break; case EditMode.DataLayer: //Draw all the region outlines foreach (EBWorldPainterData.Region region in worldPainterData.regions) { DrawRegionOutline(region, Color.white); DrawDataLayer(region); } break; } GUI.EndGroup(); GUILayout.EndHorizontal(); GUILayout.BeginHorizontal(); float camPosX = GUILayout.HorizontalSlider(cam.transform.position.x, worldPainterData.WorldBounds().min.x, worldPainterData.WorldBounds().max.x); GUILayout.EndHorizontal(); GUILayout.BeginHorizontal(); foreach (EditMode m in System.Enum.GetValues(typeof(EditMode))) { if (GUILayout.Button(m.ToString())) { SwitchEditMode(m); } } dataLayer = (EBWorldPainterData.eDATA_LAYER)EditorGUILayout.EnumPopup("Data Layer: ", dataLayer); GUILayout.EndHorizontal(); Render(camPosX, camPosZ, orthographicSize); GUI.skin = null; }