|
|
@@ -1,3 +1,4 @@
|
|
|
+
|
|
|
// Urho3D terrain editor
|
|
|
|
|
|
const uint TERRAIN_EDITMODE_RAISELOWERHEIGHT = 0, TERRAIN_EDITMODE_SETHEIGHT = 1, TERRAIN_EDITMODE_SMOOTHHEIGHT = 3,
|
|
|
@@ -7,19 +8,23 @@ funcdef bool TerrainEditorShowCallback();
|
|
|
|
|
|
class TerrainEditor
|
|
|
{
|
|
|
- bool dirty = true;
|
|
|
- uint editMode = 0;
|
|
|
-
|
|
|
- Window@ window;
|
|
|
- UIElement@ toolbar;
|
|
|
- Text@ currentToolDescText;
|
|
|
- Array<Image@> brushes;
|
|
|
- CheckBox@ selectedBrush;
|
|
|
- Image@ selectedBrushImage;
|
|
|
-
|
|
|
- Array<Terrain@> terrainsEdited;
|
|
|
-
|
|
|
- Color targetColor;
|
|
|
+ private bool dirty = true;
|
|
|
+ private uint editMode = 0;
|
|
|
+
|
|
|
+ private Window@ window;
|
|
|
+ private UIElement@ toolbar;
|
|
|
+ private Text@ currentToolDescText;
|
|
|
+ private Array<Image@> brushes;
|
|
|
+ private CheckBox@ selectedBrush;
|
|
|
+ private Image@ selectedBrushImage;
|
|
|
+ private Image@ scaledSelectedBrushImage;
|
|
|
+ private Slider@ brushSizeSlider;
|
|
|
+ private Slider@ brushOpacitySlider;
|
|
|
+ private Slider@ brushHeightSlider;
|
|
|
+
|
|
|
+ private Array<Terrain@> terrainsEdited;
|
|
|
+
|
|
|
+ private Color targetColor;
|
|
|
bool targetColorSelected = false;
|
|
|
|
|
|
// Create the Terrain Editor window and initialize it
|
|
|
@@ -32,73 +37,47 @@ class TerrainEditor
|
|
|
ui.root.AddChild(window);
|
|
|
window.opacity = uiMaxOpacity;
|
|
|
|
|
|
- BorderImage@ currentToolDesc = BorderImage("CurrentToolDesc");
|
|
|
- currentToolDesc.style = "EditorToolBar";
|
|
|
- currentToolDesc.SetLayout(LM_HORIZONTAL);
|
|
|
- currentToolDesc.layoutSpacing = 4;
|
|
|
- currentToolDesc.layoutBorder = IntRect(8, 4, 4, 8);
|
|
|
- currentToolDesc.minHeight = 32;
|
|
|
- currentToolDesc.horizontalAlignment = HA_CENTER;
|
|
|
- currentToolDesc.opacity = uiMaxOpacity;
|
|
|
- currentToolDesc.SetPosition(0, 0);
|
|
|
- window.AddChild(currentToolDesc);
|
|
|
-
|
|
|
- currentToolDescText = Text("CurrentToolDescText");
|
|
|
- currentToolDescText.text = "Raise or lower terrain using the current brush";
|
|
|
- currentToolDescText.SetStyleAuto(uiStyle);
|
|
|
- currentToolDescText.color = Color(1.0f, 1.0, 1.0f, 1.0f);
|
|
|
- currentToolDescText.position = IntVector2(16, 16);
|
|
|
- currentToolDescText.SetFont(cache.GetResource("Font", "Fonts/BlueHighway.ttf"), 10);
|
|
|
- currentToolDesc.AddChild(currentToolDescText);
|
|
|
-
|
|
|
- Text@ brushesText = Text("BrushesText");
|
|
|
- brushesText.text = "Brushes";
|
|
|
- brushesText.SetStyleAuto(uiStyle);
|
|
|
- brushesText.color = Color(1.0f, 1.0, 1.0f, 1.0f);
|
|
|
- brushesText.SetFont(cache.GetResource("Font", "Fonts/BlueHighway.ttf"), 10);
|
|
|
- window.AddChild(brushesText);
|
|
|
-
|
|
|
- ListView@ brushesContainer = ListView("BrushesContainer");
|
|
|
- brushesContainer.defaultStyle = uiStyle;
|
|
|
- brushesContainer.style = "ListView";
|
|
|
- brushesContainer.layoutSpacing = 4;
|
|
|
- brushesContainer.internal = false;
|
|
|
- brushesContainer.layoutBorder = IntRect(8, 4, 4, 8);
|
|
|
- brushesContainer.opacity = uiMaxOpacity;
|
|
|
+ BorderImage@ currentToolDesc = window.GetChild("CurrentToolDesc", true);
|
|
|
+ currentToolDesc.layoutBorder = IntRect(8, 8, 8, 8);
|
|
|
+
|
|
|
+ currentToolDescText = window.GetChild("CurrentToolDescText", true);
|
|
|
+
|
|
|
+ ListView@ brushesContainer = window.GetChild("BrushesContainer", true);
|
|
|
brushesContainer.SetScrollBarsVisible(true, false);
|
|
|
brushesContainer.contentElement.layoutMode = LM_HORIZONTAL;
|
|
|
brushesContainer.SetFixedHeight(84);
|
|
|
- window.AddChild(brushesContainer);
|
|
|
|
|
|
- Text@ settingsText = Text("SettingsText");
|
|
|
- settingsText.text = "Settings";
|
|
|
- settingsText.SetStyleAuto(uiStyle);
|
|
|
- settingsText.color = Color(1.0f, 1.0, 1.0f, 1.0f);
|
|
|
- settingsText.SetFont(cache.GetResource("Font", "Fonts/BlueHighway.ttf"), 10);
|
|
|
- window.AddChild(settingsText);
|
|
|
+ BorderImage@ settingsArea = window.GetChild("SettingsArea", true);
|
|
|
+ settingsArea.layoutBorder = IntRect(8, 8, 8, 8);
|
|
|
+
|
|
|
+ LineEdit@ createTerrainValue = window.GetChild("CreateTerrainValue", true);
|
|
|
+ createTerrainValue.text = "1024";
|
|
|
+
|
|
|
+ brushSizeSlider = window.GetChild("BrushSize", true);
|
|
|
+ brushOpacitySlider = window.GetChild("BrushOpacity", true);
|
|
|
+ brushHeightSlider = window.GetChild("BrushHeight", true);
|
|
|
|
|
|
window.height = 300;
|
|
|
window.SetPosition(ui.root.width - 10 - window.width, attributeInspectorWindow.position.y + attributeInspectorWindow.height + 10);
|
|
|
|
|
|
- SubscribeToEvent(window.GetChild("RaiseLowerHeight", true), "Toggled", "EditModeRaiseLowerHeight");
|
|
|
- SubscribeToEvent(window.GetChild("SetHeight", true), "Toggled", "EditModeSetHeight");
|
|
|
- SubscribeToEvent(window.GetChild("SmoothHeight", true), "Toggled", "EditModeSmoothHeight");
|
|
|
- SubscribeToEvent(window.GetChild("PaintBrush", true), "Toggled", "EditModePaintBrush");
|
|
|
- SubscribeToEvent(window.GetChild("PaintTrees", true), "Toggled", "EditModePaintTrees");
|
|
|
- SubscribeToEvent(window.GetChild("PaintFoliage", true), "Toggled", "EditModePaintFoliage");
|
|
|
+ SubscribeToEvent(window.GetChild("RaiseLowerHeight", true), "Toggled", "OnEditModeSelected");
|
|
|
+ SubscribeToEvent(window.GetChild("SetHeight", true), "Toggled", "OnEditModeSelected");
|
|
|
+ SubscribeToEvent(window.GetChild("SmoothHeight", true), "Toggled", "OnEditModeSelected");
|
|
|
+ //SubscribeToEvent(window.GetChild("PaintBrush", true), "Toggled", "OnEditModeSelected");
|
|
|
+ //SubscribeToEvent(window.GetChild("PaintTrees", true), "Toggled", "OnEditModeSelected");
|
|
|
+ //SubscribeToEvent(window.GetChild("PaintFoliage", true), "Toggled", "OnEditModeSelected");
|
|
|
SubscribeToEvent(window.GetChild("CloseButton", true), "Released", "Hide");
|
|
|
SubscribeToEvent(window.GetChild("CreateTerrainButton", true), "Released", "CreateTerrain");
|
|
|
+ SubscribeToEvent(brushSizeSlider, "DragEnd", "UpdateScaledBrush");
|
|
|
|
|
|
LoadBrushes();
|
|
|
Show();
|
|
|
}
|
|
|
|
|
|
- void CreateTerrain()
|
|
|
+ // Hide the window
|
|
|
+ void Hide()
|
|
|
{
|
|
|
- Node@ node = CreateNode(LOCAL);
|
|
|
- node.position = Vector3(0, 0, 0);
|
|
|
- Component@ comp = node.CreateComponent("Terrain");
|
|
|
- SelectComponent(comp, false);
|
|
|
+ window.visible = false;
|
|
|
}
|
|
|
|
|
|
// Save all the terrains we have edited
|
|
|
@@ -139,36 +118,158 @@ class TerrainEditor
|
|
|
terrainsEdited.Clear();
|
|
|
}
|
|
|
|
|
|
- // Returns a brush based on its name (its filename minus the extension)
|
|
|
- Image@ GetBrushImage(String brushName)
|
|
|
+ // Show the window
|
|
|
+ bool Show()
|
|
|
{
|
|
|
- for (uint i = 0; i < brushes.length; ++i)
|
|
|
+ window.visible = true;
|
|
|
+ window.BringToFront();
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ // Update the UI
|
|
|
+ void UpdateDirty()
|
|
|
+ {
|
|
|
+ if (!dirty)
|
|
|
+ return;
|
|
|
+
|
|
|
+ CheckBox@ raiseLowerHeight = window.GetChild("RaiseLowerHeight", true);
|
|
|
+ CheckBox@ setHeight = window.GetChild("SetHeight", true);
|
|
|
+ CheckBox@ smoothHeight = window.GetChild("SmoothHeight", true);
|
|
|
+ //CheckBox@ paintBrush = window.GetChild("PaintBrush", true);
|
|
|
+ //CheckBox@ paintTrees = window.GetChild("PaintTrees", true);
|
|
|
+ //CheckBox@ paintFoliage = window.GetChild("PaintFoliage", true);
|
|
|
+
|
|
|
+ raiseLowerHeight.checked = (editMode == TERRAIN_EDITMODE_RAISELOWERHEIGHT) ? true : false;
|
|
|
+ setHeight.checked = (editMode == TERRAIN_EDITMODE_SETHEIGHT) ? true : false;
|
|
|
+ smoothHeight.checked = (editMode == TERRAIN_EDITMODE_SMOOTHHEIGHT) ? true : false;
|
|
|
+ //paintBrush.checked = (editMode == TERRAIN_EDITMODE_PAINTBRUSH) ? true : false;
|
|
|
+ //paintTrees.checked = (editMode == TERRAIN_EDITMODE_PAINTTREES) ? true : false;
|
|
|
+ //paintFoliage.checked = (editMode == TERRAIN_EDITMODE_PAINTFOLIAGE) ? true : false;
|
|
|
+
|
|
|
+ raiseLowerHeight.enabled = !raiseLowerHeight.checked;
|
|
|
+ setHeight.enabled = !setHeight.checked;
|
|
|
+ smoothHeight.enabled = !smoothHeight.checked;
|
|
|
+
|
|
|
+ ListView@ terrainBrushes = window.GetChild("BrushesContainer", true);
|
|
|
+
|
|
|
+ for (uint i = 0; i < terrainBrushes.numItems; ++i)
|
|
|
{
|
|
|
- if (brushes[i].name == brushName)
|
|
|
- {
|
|
|
- return brushes[i];
|
|
|
- }
|
|
|
+ CheckBox@ checkbox = cast<CheckBox>(terrainBrushes.items[i]);
|
|
|
+ checkbox.checked = terrainBrushes.items[i] is selectedBrush;
|
|
|
+ checkbox.enabled = !checkbox.checked;
|
|
|
}
|
|
|
|
|
|
- return null;
|
|
|
+ dirty = false;
|
|
|
}
|
|
|
|
|
|
- // Loads all the brushes from a hard-coded folder
|
|
|
- void LoadBrushes()
|
|
|
+ // This gets called when we want to do something to a terrain
|
|
|
+ void Work(Terrain@ terrainComponent, Image@ terrainImage, IntVector2 position)
|
|
|
{
|
|
|
- ListView@ terrainBrushes = window.GetChild("BrushesContainer", true);
|
|
|
+ SetSceneModified();
|
|
|
|
|
|
- String brushesFileLocation = fileSystem.programDir + "Data/Textures/Editor/TerrainBrushes";
|
|
|
- Array<String> files = fileSystem.ScanDir(brushesFileLocation, "*.*", SCAN_FILES, false);
|
|
|
+ // Add that terrain to the terrainsEdited if its not already in there
|
|
|
+ if (terrainsEdited.FindByRef(terrainComponent) == -1)
|
|
|
+ terrainsEdited.Push(terrainComponent);
|
|
|
|
|
|
- for (uint i = 0; i < files.length; ++i)
|
|
|
+ // Only work if a brush is selected
|
|
|
+ if (selectedBrushImage is null || scaledSelectedBrushImage is null)
|
|
|
+ return;
|
|
|
+
|
|
|
+ switch (editMode)
|
|
|
{
|
|
|
- terrainBrushes.AddItem(CreateBrush(brushesFileLocation + "/" + files[i]));
|
|
|
+ case TERRAIN_EDITMODE_RAISELOWERHEIGHT:
|
|
|
+ UpdateTerrainRaiseLower(terrainImage, position);
|
|
|
+ break;
|
|
|
+ case TERRAIN_EDITMODE_SETHEIGHT:
|
|
|
+ UpdateTerrainSetHeight(terrainImage, position);
|
|
|
+ break;
|
|
|
+ case TERRAIN_EDITMODE_SMOOTHHEIGHT:
|
|
|
+ UpdateTerrainSmooth(terrainImage, position);
|
|
|
+ break;
|
|
|
}
|
|
|
+
|
|
|
+ // Apply our changes
|
|
|
+ terrainComponent.ApplyHeightMap();
|
|
|
+ }
|
|
|
+
|
|
|
+ private uint NearestPowerOf2(uint value) {
|
|
|
+ if (value < 2)
|
|
|
+ return 2;
|
|
|
+
|
|
|
+ for (uint i = 1; i <= 2048; i *= 2)
|
|
|
+ {
|
|
|
+ if (value == i)
|
|
|
+ return i;
|
|
|
+
|
|
|
+ if (value < i || value > i * 2)
|
|
|
+ continue;
|
|
|
+
|
|
|
+ return value < (i + (i / 2)) ? i : i * 2;
|
|
|
+ }
|
|
|
+
|
|
|
+ return 2048;
|
|
|
+ }
|
|
|
+
|
|
|
+ private void CreateTerrain()
|
|
|
+ {
|
|
|
+ String fileName = "Textures/heightmap-" + time.timeSinceEpoch + ".png";
|
|
|
+ String fileLocation = fileSystem.programDir + "Data/" + fileName;
|
|
|
+
|
|
|
+ Node@ node = CreateNode(LOCAL);
|
|
|
+ node.position = Vector3(0, 0, 0);
|
|
|
+
|
|
|
+ LineEdit@ lineEdit = window.GetChild("CreateTerrainValue", true);
|
|
|
+
|
|
|
+ uint lineEditLength = lineEdit.text.Trimmed().ToUInt();
|
|
|
+
|
|
|
+ // The parse failed, so use a decent default
|
|
|
+ if (lineEditLength == 0)
|
|
|
+ lineEditLength = 1024;
|
|
|
+
|
|
|
+ Image@ image = Image();
|
|
|
+ uint length = NearestPowerOf2(lineEditLength) + 1;
|
|
|
+ image.SetSize(length, length, 3);
|
|
|
+
|
|
|
+ UpdateTerrainSetConstantHeight(image, 0);
|
|
|
+
|
|
|
+ image.SavePNG(fileLocation);
|
|
|
+
|
|
|
+ Terrain@ terrain = node.CreateComponent("Terrain");
|
|
|
+ terrain.heightMap = image;
|
|
|
+
|
|
|
+ Resource@ res = cache.GetResource("Image", fileLocation);
|
|
|
+
|
|
|
+ ResourceRef ref = ResourceRef();
|
|
|
+ ref.type = res.type;
|
|
|
+ ref.name = fileName;
|
|
|
+ terrain.SetAttribute("Height Map", Variant(ref));
|
|
|
+ terrain.ApplyAttributes();
|
|
|
+
|
|
|
+ SelectComponent(terrain, false);
|
|
|
+ }
|
|
|
+
|
|
|
+ // Utility function, returns the difference of the two numbers
|
|
|
+ private float Difference(float a, float b)
|
|
|
+ {
|
|
|
+ return (a > b) ? a - b : b - a;
|
|
|
+ }
|
|
|
+
|
|
|
+ // Returns a brush based on its name (its filename minus the extension)
|
|
|
+ private Image@ GetBrushImage(String brushName)
|
|
|
+ {
|
|
|
+ for (uint i = 0; i < brushes.length; ++i)
|
|
|
+ {
|
|
|
+ if (brushes[i].name == brushName)
|
|
|
+ {
|
|
|
+ return brushes[i];
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return null;
|
|
|
}
|
|
|
|
|
|
// Creates a brush element
|
|
|
- UIElement@ CreateBrush(String fileLocation)
|
|
|
+ private UIElement@ LoadBrush(String fileLocation)
|
|
|
{
|
|
|
Array<String> chunks = fileLocation.Split('/');
|
|
|
Array<String> parts = chunks[chunks.length - 1].Split('.');
|
|
|
@@ -185,7 +286,7 @@ class TerrainEditor
|
|
|
brush.defaultStyle = uiStyle;
|
|
|
brush.style = "TerrainEditorCheckbox";
|
|
|
brush.SetFixedSize(64, 64);
|
|
|
- SubscribeToEvent(brush, "Toggled", "BrushSelected");
|
|
|
+ SubscribeToEvent(brush, "Toggled", "OnBrushSelected");
|
|
|
|
|
|
BorderImage@ icon = BorderImage("Icon");
|
|
|
icon.defaultStyle = iconStyle;
|
|
|
@@ -197,286 +298,211 @@ class TerrainEditor
|
|
|
return brush;
|
|
|
}
|
|
|
|
|
|
- // Show the window
|
|
|
- bool Show()
|
|
|
+ // Loads all the brushes from a hard-coded folder
|
|
|
+ private void LoadBrushes()
|
|
|
{
|
|
|
- window.visible = true;
|
|
|
- window.BringToFront();
|
|
|
- return true;
|
|
|
+ ListView@ terrainBrushes = window.GetChild("BrushesContainer", true);
|
|
|
+
|
|
|
+ String brushesFileLocation = fileSystem.programDir + "Data/Textures/Editor/TerrainBrushes";
|
|
|
+ Array<String> files = fileSystem.ScanDir(brushesFileLocation, "*.*", SCAN_FILES, false);
|
|
|
+
|
|
|
+ for (uint i = 0; i < files.length; ++i)
|
|
|
+ {
|
|
|
+ terrainBrushes.AddItem(LoadBrush(brushesFileLocation + "/" + files[i]));
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
- // Hide the window
|
|
|
- void Hide()
|
|
|
+ private void OnEditModeSelected(StringHash eventType, VariantMap& eventData)
|
|
|
{
|
|
|
- window.visible = false;
|
|
|
+ CheckBox@ edit = eventData["Element"].GetPtr();
|
|
|
+
|
|
|
+ if (!edit.checked)
|
|
|
+ return;
|
|
|
+
|
|
|
+ if (edit.name == "RaiseLowerHeight")
|
|
|
+ SetEditMode(TERRAIN_EDITMODE_RAISELOWERHEIGHT, "Raise or lower terrain");
|
|
|
+
|
|
|
+ else if (edit.name == "SetHeight")
|
|
|
+ SetEditMode(TERRAIN_EDITMODE_SETHEIGHT, "Set height to specified height");
|
|
|
+
|
|
|
+ else if (edit.name == "SmoothHeight")
|
|
|
+ SetEditMode(TERRAIN_EDITMODE_SMOOTHHEIGHT, "Smooth the terrain");
|
|
|
+
|
|
|
+ else if (edit.name == "PaintBrush")
|
|
|
+ SetEditMode(TERRAIN_EDITMODE_PAINTBRUSH, "Paint textures onto the terrain");
|
|
|
+
|
|
|
+ else if (edit.name == "PaintTrees")
|
|
|
+ SetEditMode(TERRAIN_EDITMODE_PAINTTREES, "Paint trees onto the terrain");
|
|
|
+
|
|
|
+ else if (edit.name == "PaintFoliage")
|
|
|
+ SetEditMode(TERRAIN_EDITMODE_PAINTFOLIAGE, "Paint foliage onto the terrain");
|
|
|
}
|
|
|
|
|
|
- // This gets called when we want to do something to a terrain
|
|
|
- void Work(Terrain@ terrainComponent, Image@ terrainImage, IntVector2 position)
|
|
|
+ private void OnBrushSelected(StringHash eventType, VariantMap& eventData)
|
|
|
{
|
|
|
- SetSceneModified();
|
|
|
+ CheckBox@ checkbox = cast<CheckBox>(eventData["Element"].GetPtr());
|
|
|
+ if (checkbox.checked == false)
|
|
|
+ return;
|
|
|
+ selectedBrush = checkbox;
|
|
|
+ selectedBrushImage = GetBrushImage(selectedBrush.name);
|
|
|
+ UpdateScaledBrush();
|
|
|
+ dirty = true;
|
|
|
+ }
|
|
|
|
|
|
- // Add that terrain to the terrainsEdited if its not already in there
|
|
|
- if (terrainsEdited.FindByRef(terrainComponent) == -1)
|
|
|
- terrainsEdited.Push(terrainComponent);
|
|
|
+ private void SetEditMode(uint mode, String description)
|
|
|
+ {
|
|
|
+ window.GetChild("BrushOpacityLabel", true).visible = (mode == TERRAIN_EDITMODE_RAISELOWERHEIGHT);
|
|
|
+ window.GetChild("BrushHeightLabel", true).visible = (mode == TERRAIN_EDITMODE_SETHEIGHT);
|
|
|
|
|
|
- // Only work if a brush is selected
|
|
|
- if (selectedBrushImage !is null){
|
|
|
- uint brushImageWidth = selectedBrushImage.width;
|
|
|
- uint brushImageHeight = selectedBrushImage.height;
|
|
|
+ window.GetChild("BrushOpacity", true).visible = (mode == TERRAIN_EDITMODE_RAISELOWERHEIGHT);
|
|
|
+ window.GetChild("BrushHeight", true).visible = (mode == TERRAIN_EDITMODE_SETHEIGHT);
|
|
|
|
|
|
- switch (editMode)
|
|
|
- {
|
|
|
- case TERRAIN_EDITMODE_RAISELOWERHEIGHT: {
|
|
|
- // Iterate over the entire brush image
|
|
|
- for (int y = 0; y < brushImageHeight; ++y)
|
|
|
- {
|
|
|
- for (int x = 0; x < brushImageWidth; ++x)
|
|
|
- {
|
|
|
- // Get the current terrain height at that position (centred to the brush's size)
|
|
|
- IntVector2 pos = IntVector2(position.x + x - (brushImageWidth / 2), position.y + y - (brushImageHeight / 2));
|
|
|
- Color newColor = terrainImage.GetPixel(pos.x, pos.y);
|
|
|
- Color brushColor = selectedBrushImage.GetPixel(x, y);
|
|
|
-
|
|
|
- // lower or raise (respectively)
|
|
|
- float modifer = (input.keyDown[KEY_SHIFT]) ? -0.01f : 0.01f;
|
|
|
-
|
|
|
- // Now apply the brush to the terrain (we only use the alpha)
|
|
|
- newColor.r += brushColor.a * modifer;
|
|
|
- newColor.g += brushColor.a * modifer;
|
|
|
- newColor.b += brushColor.a * modifer;
|
|
|
-
|
|
|
- terrainImage.SetPixel(pos.x, pos.y, newColor);
|
|
|
- }
|
|
|
- }
|
|
|
- } break;
|
|
|
-
|
|
|
- case TERRAIN_EDITMODE_SETHEIGHT: {
|
|
|
- // The color we want to set the height to (this stays the same until targetColorSelected is false again)
|
|
|
- if (targetColorSelected == false)
|
|
|
- {
|
|
|
- targetColor = terrainImage.GetPixel(position.x, position.y);
|
|
|
- targetColorSelected = true;
|
|
|
- }
|
|
|
-
|
|
|
- // Iterate over the entire brush image
|
|
|
- for (int y = 0; y < brushImageHeight; ++y)
|
|
|
- {
|
|
|
- for (int x = 0; x < brushImageWidth; ++x)
|
|
|
- {
|
|
|
- // Get the current terrain height at that position (centred to the brush's size)
|
|
|
- IntVector2 pos = IntVector2(position.x + x - (brushImageWidth / 2), position.y + y - (brushImageHeight / 2));
|
|
|
- Color newColor = terrainImage.GetPixel(pos.x, pos.y);
|
|
|
- Color brushColor = selectedBrushImage.GetPixel(x, y);
|
|
|
-
|
|
|
- // Ease the height to the target height (using the brush alpha as the 'speed'), making sure the alpha isn't 0
|
|
|
- newColor.r += (targetColor.r - newColor.r) * brushColor.a;
|
|
|
- newColor.g += (targetColor.g - newColor.g) * brushColor.a;
|
|
|
- newColor.b += (targetColor.b - newColor.b) * brushColor.a;
|
|
|
-
|
|
|
- // Set it to target if its close enough
|
|
|
- if (Difference(targetColor.r, newColor.r) < 0.01f) newColor.r = targetColor.r;
|
|
|
- if (Difference(targetColor.g, newColor.g) < 0.01f) newColor.g = targetColor.g;
|
|
|
- if (Difference(targetColor.b, newColor.b) < 0.01f) newColor.b = targetColor.b;
|
|
|
-
|
|
|
- terrainImage.SetPixel(pos.x, pos.y, newColor);
|
|
|
- }
|
|
|
- }
|
|
|
- } break;
|
|
|
-
|
|
|
- case TERRAIN_EDITMODE_SMOOTHHEIGHT: {
|
|
|
- //IntRect rect = IntRect(position.x - (brushImageWidth / 2), position.y - (brushImageHeight / 2),
|
|
|
- // position.x - (brushImageWidth / 2) + brushImageWidth, position.y - (brushImageHeight / 2) + brushImageHeight);
|
|
|
-
|
|
|
- //Image@ subImage = terrainImage.GetSubimage(rect);
|
|
|
-
|
|
|
- // Iterate over the entire brush image
|
|
|
- for (int y = 0; y < brushImageHeight; ++y)
|
|
|
- {
|
|
|
- for (int x = 0; x < brushImageWidth; ++x)
|
|
|
- {
|
|
|
- // Get the current terrain height at that position (centred to the brush's size)
|
|
|
- IntVector2 pos = IntVector2(position.x + x - (brushImageWidth / 2), position.y + y - (brushImageHeight / 2));
|
|
|
-
|
|
|
- // Make sure the pixel we're working on is atleast one pixel inside the terrainImage
|
|
|
- // as we need an adjacent pixel on all sides for the smoothing algorithm to work
|
|
|
- if (pos.x > 0 && pos.y > 0 && pos.x < terrainImage.width - 1 && pos.y < terrainImage.height - 1)
|
|
|
- {
|
|
|
- Color newColor = terrainImage.GetPixel(pos.x, pos.y);
|
|
|
- Color brushColor = selectedBrushImage.GetPixel(x, y);
|
|
|
-
|
|
|
- Array<Color> adjacentColors;
|
|
|
-
|
|
|
- adjacentColors.Push(terrainImage.GetPixel(pos.x + 1, pos.y + 1));
|
|
|
- adjacentColors.Push(terrainImage.GetPixel(pos.x + 1, pos.y));
|
|
|
- adjacentColors.Push(terrainImage.GetPixel(pos.x + 1, pos.y - 1));
|
|
|
- adjacentColors.Push(terrainImage.GetPixel(pos.x - 1, pos.y + 1));
|
|
|
- adjacentColors.Push(terrainImage.GetPixel(pos.x - 1, pos.y));
|
|
|
- adjacentColors.Push(terrainImage.GetPixel(pos.x - 1, pos.y - 1));
|
|
|
- adjacentColors.Push(terrainImage.GetPixel(pos.x, pos.y + 1));
|
|
|
- adjacentColors.Push(terrainImage.GetPixel(pos.x, pos.y));
|
|
|
- adjacentColors.Push(terrainImage.GetPixel(pos.x, pos.y - 1));
|
|
|
-
|
|
|
- float avgR = 0.0f;
|
|
|
- float avgG = 0.0f;
|
|
|
- float avgB = 0.0f;
|
|
|
-
|
|
|
- // Get the average of those pixels
|
|
|
- for (uint i = 0; i < adjacentColors.length; ++i)
|
|
|
- {
|
|
|
- avgR += adjacentColors[i].r;
|
|
|
- avgG += adjacentColors[i].g;
|
|
|
- avgB += adjacentColors[i].b;
|
|
|
- }
|
|
|
-
|
|
|
- newColor.r = avgR / adjacentColors.length;
|
|
|
- newColor.g = avgG / adjacentColors.length;
|
|
|
- newColor.b = avgB / adjacentColors.length;
|
|
|
-
|
|
|
- Color originalColor = terrainImage.GetPixel(pos.x, pos.y);
|
|
|
-
|
|
|
- newColor.r = (Difference(originalColor.r, newColor.r) * brushColor.a) + Smaller(originalColor.r, newColor.r);
|
|
|
- newColor.g = (Difference(originalColor.g, newColor.g) * brushColor.a) + Smaller(originalColor.g, newColor.g);
|
|
|
- newColor.b = (Difference(originalColor.b, newColor.b) * brushColor.a) + Smaller(originalColor.b, newColor.b);
|
|
|
-
|
|
|
- terrainImage.SetPixel(position.x + x - (brushImageWidth / 2), position.y + y - (brushImageHeight / 2), newColor);
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- // Apply our changes to that actual terrain
|
|
|
- //for (int y = 0; y < subImage.height; ++y)
|
|
|
- //{
|
|
|
- // for (int x = 0; x < subImage.width; ++x)
|
|
|
- // {
|
|
|
- // terrainImage.SetPixel(position.x + x - (brushImageWidth / 2), position.y + y - (brushImageHeight / 2), subImage.GetPixel(x, y));
|
|
|
- // }
|
|
|
- //}
|
|
|
-
|
|
|
- } break;
|
|
|
- }
|
|
|
+ editMode = mode;
|
|
|
+ currentToolDescText.text = description;
|
|
|
+ dirty = true;
|
|
|
|
|
|
- // Apply our changes
|
|
|
- terrainComponent.ApplyHeightMap();
|
|
|
- }
|
|
|
+ // force the window to resize its height to fit its children
|
|
|
+ window.height = 0;
|
|
|
}
|
|
|
|
|
|
// Utility function, returns the smaller of the two numbers
|
|
|
- float Smaller(float a, float b)
|
|
|
+ private float Smaller(float a, float b)
|
|
|
{
|
|
|
return (a > b) ? b : a;
|
|
|
}
|
|
|
|
|
|
- // Utility function, returns the difference of the two numbers
|
|
|
- float Difference(float a, float b)
|
|
|
+ private void UpdateScaledBrush()
|
|
|
{
|
|
|
- return (a > b) ? a - b : b - a;
|
|
|
+ if (selectedBrushImage is null)
|
|
|
+ return;
|
|
|
+ float size = (brushSizeSlider.value / 25) + 0.5;
|
|
|
+ Print(brushSizeSlider.value);
|
|
|
+ Print(size);
|
|
|
+ scaledSelectedBrushImage = selectedBrushImage.GetSubimage(IntRect(0, 0, selectedBrushImage.width, selectedBrushImage.height));
|
|
|
+ scaledSelectedBrushImage.Resize(int(selectedBrushImage.width * size), int(selectedBrushImage.height * size));
|
|
|
}
|
|
|
|
|
|
- // Update the UI
|
|
|
- void UpdateDirty()
|
|
|
+ private void UpdateTerrainRaiseLower(Image@ terrainImage, IntVector2 position)
|
|
|
{
|
|
|
- if (!dirty)
|
|
|
- return;
|
|
|
+ uint brushImageWidth = scaledSelectedBrushImage.width;
|
|
|
+ uint brushImageHeight = scaledSelectedBrushImage.height;
|
|
|
|
|
|
- CheckBox@ raiseLowerHeight = window.GetChild("RaiseLowerHeight", true);
|
|
|
- CheckBox@ setHeight = window.GetChild("SetHeight", true);
|
|
|
- CheckBox@ smoothHeight = window.GetChild("SmoothHeight", true);
|
|
|
- CheckBox@ paintBrush = window.GetChild("PaintBrush", true);
|
|
|
- CheckBox@ paintTrees = window.GetChild("PaintTrees", true);
|
|
|
- CheckBox@ paintFoliage = window.GetChild("PaintFoliage", true);
|
|
|
-
|
|
|
- raiseLowerHeight.checked = (editMode == TERRAIN_EDITMODE_RAISELOWERHEIGHT) ? true : false;
|
|
|
- setHeight.checked = (editMode == TERRAIN_EDITMODE_SETHEIGHT) ? true : false;
|
|
|
- smoothHeight.checked = (editMode == TERRAIN_EDITMODE_SMOOTHHEIGHT) ? true : false;
|
|
|
- paintBrush.checked = (editMode == TERRAIN_EDITMODE_PAINTBRUSH) ? true : false;
|
|
|
- paintTrees.checked = (editMode == TERRAIN_EDITMODE_PAINTTREES) ? true : false;
|
|
|
- paintFoliage.checked = (editMode == TERRAIN_EDITMODE_PAINTFOLIAGE) ? true : false;
|
|
|
+ // lower or raise (respectively), multiply this by the brush opacity
|
|
|
+ float opacity = brushOpacitySlider.value / 25;
|
|
|
+ float modifier = (input.keyDown[KEY_SHIFT] ? -opacity : opacity) * 0.05;
|
|
|
|
|
|
- ListView@ terrainBrushes = window.GetChild("BrushesContainer", true);
|
|
|
-
|
|
|
- for (uint i = 0; i < terrainBrushes.numItems; ++i)
|
|
|
+ // Iterate over the entire brush image
|
|
|
+ for (int y = 0; y < brushImageHeight; ++y)
|
|
|
{
|
|
|
- if (terrainBrushes.items[i] !is selectedBrush)
|
|
|
+ for (int x = 0; x < brushImageWidth; ++x)
|
|
|
{
|
|
|
- CheckBox@ checkbox = cast<CheckBox>(terrainBrushes.items[i]);
|
|
|
- checkbox.checked = false;
|
|
|
+ // Get the current terrain height at that position (centred to the brush's size)
|
|
|
+ IntVector2 pos = IntVector2(position.x + x - (brushImageWidth / 2), position.y + y - (brushImageHeight / 2));
|
|
|
+ Color newColor = terrainImage.GetPixel(pos.x, pos.y);
|
|
|
+ Color brushColor = scaledSelectedBrushImage.GetPixel(x, y);
|
|
|
+ // Now apply the brush to the terrain (we only use the alpha)
|
|
|
+ newColor.r += brushColor.a * modifier;
|
|
|
+ newColor.g += brushColor.a * modifier;
|
|
|
+ newColor.b += brushColor.a * modifier;
|
|
|
+ terrainImage.SetPixel(pos.x, pos.y, newColor);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- dirty = false;
|
|
|
+ // Smooth the terrain a bit
|
|
|
+ UpdateTerrainSmooth(terrainImage, position);
|
|
|
}
|
|
|
|
|
|
- // The next 7 functions are callbacks for the Terrain Editors UI
|
|
|
-
|
|
|
- void EditModeRaiseLowerHeight(StringHash eventType, VariantMap& eventData)
|
|
|
+ private void UpdateTerrainSmooth(Image@ terrainImage, IntVector2 position)
|
|
|
{
|
|
|
- CheckBox@ edit = eventData["Element"].GetPtr();
|
|
|
- if (edit.checked){
|
|
|
- editMode = TERRAIN_EDITMODE_RAISELOWERHEIGHT;
|
|
|
- currentToolDescText.text = "Raise or lower terrain";
|
|
|
- }
|
|
|
- dirty = true;
|
|
|
- }
|
|
|
+ uint brushImageWidth = scaledSelectedBrushImage.width;
|
|
|
+ uint brushImageHeight = scaledSelectedBrushImage.height;
|
|
|
|
|
|
- void EditModeSetHeight(StringHash eventType, VariantMap& eventData)
|
|
|
- {
|
|
|
- CheckBox@ edit = eventData["Element"].GetPtr();
|
|
|
- if (edit.checked){
|
|
|
- editMode = TERRAIN_EDITMODE_SETHEIGHT;
|
|
|
- currentToolDescText.text = "Set height to specified height";
|
|
|
+ // Iterate over the entire brush image
|
|
|
+ for (int y = 0; y < brushImageHeight; ++y)
|
|
|
+ {
|
|
|
+ for (int x = 0; x < brushImageWidth; ++x)
|
|
|
+ {
|
|
|
+ Color brushColor = scaledSelectedBrushImage.GetPixel(x, y);
|
|
|
+
|
|
|
+ // Only take opaque pixels into consideration for now
|
|
|
+ if (brushColor.a == 0)
|
|
|
+ continue;
|
|
|
+
|
|
|
+ // Get the current terrain height at that position (centred to the brush's size)
|
|
|
+ IntVector2 pos = IntVector2(position.x + x - (brushImageWidth / 2), position.y + y - (brushImageHeight / 2));
|
|
|
+
|
|
|
+ // Make sure the pixel we're working on is atleast one pixel inside the terrainImage
|
|
|
+ // as we need an adjacent pixel on all sides for the smoothing algorithm to work
|
|
|
+ if (pos.x < 0 || pos.y < 0 || pos.x > terrainImage.width - 1 || pos.y > terrainImage.height - 1)
|
|
|
+ continue;
|
|
|
+
|
|
|
+ Color brp = terrainImage.GetPixel(pos.x + 1, pos.y + 1); // bottomRightPixel
|
|
|
+ Color rp = terrainImage.GetPixel(pos.x + 1, pos.y); // rightPixel
|
|
|
+ Color trp = terrainImage.GetPixel(pos.x + 1, pos.y - 1); // topRightPixel
|
|
|
+ Color blp = terrainImage.GetPixel(pos.x - 1, pos.y + 1); // bottomLeftPixel
|
|
|
+ Color lp = terrainImage.GetPixel(pos.x - 1, pos.y); // leftPixel
|
|
|
+ Color tlp = terrainImage.GetPixel(pos.x - 1, pos.y - 1); // topLeftPixel
|
|
|
+ Color bp = terrainImage.GetPixel(pos.x, pos.y + 1); // bottomPixel
|
|
|
+ Color cp = terrainImage.GetPixel(pos.x, pos.y); // centerPixel
|
|
|
+ Color tp = terrainImage.GetPixel(pos.x, pos.y - 1); // topPixel
|
|
|
+
|
|
|
+ Color avgColor = Color(
|
|
|
+ ((brp.r + rp.r + trp.r + blp.r + lp.r + tlp.r + bp.r + cp.r + tp.r) / 9),
|
|
|
+ ((brp.g + rp.g + trp.g + blp.g + lp.g + tlp.g + bp.g + cp.g + tp.g) / 9),
|
|
|
+ ((brp.b + rp.b + trp.b + blp.b + lp.b + tlp.b + bp.b + cp.b + tp.b) / 9)
|
|
|
+ );
|
|
|
+
|
|
|
+ Color newColor = Color(
|
|
|
+ (Difference(cp.r, avgColor.r) * brushColor.a) + Smaller(cp.r, avgColor.r),
|
|
|
+ (Difference(cp.g, avgColor.g) * brushColor.a) + Smaller(cp.g, avgColor.g),
|
|
|
+ (Difference(cp.b, avgColor.b) * brushColor.a) + Smaller(cp.b, avgColor.b)
|
|
|
+ );
|
|
|
+
|
|
|
+ terrainImage.SetPixel(position.x + x - (brushImageWidth / 2), position.y + y - (brushImageHeight / 2), avgColor);
|
|
|
+ }
|
|
|
}
|
|
|
- dirty = true;
|
|
|
}
|
|
|
|
|
|
- void EditModeSmoothHeight(StringHash eventType, VariantMap& eventData)
|
|
|
+ private void UpdateTerrainSetHeight(Image@ terrainImage, IntVector2 position)
|
|
|
{
|
|
|
- CheckBox@ edit = eventData["Element"].GetPtr();
|
|
|
- if (edit.checked){
|
|
|
- editMode = TERRAIN_EDITMODE_SMOOTHHEIGHT;
|
|
|
- currentToolDescText.text = "Smooth the terrain";
|
|
|
- }
|
|
|
- dirty = true;
|
|
|
- }
|
|
|
+ uint brushImageWidth = scaledSelectedBrushImage.width;
|
|
|
+ uint brushImageHeight = scaledSelectedBrushImage.height;
|
|
|
|
|
|
- void EditModePaintBrush(StringHash eventType, VariantMap& eventData)
|
|
|
- {
|
|
|
- CheckBox@ edit = eventData["Element"].GetPtr();
|
|
|
- if (edit.checked){
|
|
|
- editMode = TERRAIN_EDITMODE_PAINTBRUSH;
|
|
|
- currentToolDescText.text = "Paint textures onto the terrain";
|
|
|
- }
|
|
|
- dirty = true;
|
|
|
- }
|
|
|
+ float targetHeight = brushHeightSlider.value / 25;
|
|
|
|
|
|
- void EditModePaintTrees(StringHash eventType, VariantMap& eventData)
|
|
|
- {
|
|
|
- CheckBox@ edit = eventData["Element"].GetPtr();
|
|
|
- if (edit.checked){
|
|
|
- editMode = TERRAIN_EDITMODE_PAINTTREES;
|
|
|
- currentToolDescText.text = "Paint trees onto the terrain";
|
|
|
+ // Iterate over the entire brush image
|
|
|
+ for (int y = 0; y < brushImageHeight; ++y)
|
|
|
+ {
|
|
|
+ for (int x = 0; x < brushImageWidth; ++x)
|
|
|
+ {
|
|
|
+ // Get the current terrain height at that position (centred to the brush's size)
|
|
|
+ IntVector2 pos = IntVector2(position.x + x - (brushImageWidth / 2), position.y + y - (brushImageHeight / 2));
|
|
|
+ Color newColor = terrainImage.GetPixel(pos.x, pos.y);
|
|
|
+ Color brushColor = scaledSelectedBrushImage.GetPixel(x, y);
|
|
|
+ // Ease the height to the target height (using the brush alpha as the 'speed'), making sure the alpha isn't 0
|
|
|
+ newColor.r += (targetHeight - newColor.r) * brushColor.a;
|
|
|
+ newColor.g += (targetHeight - newColor.g) * brushColor.a;
|
|
|
+ newColor.b += (targetHeight - newColor.b) * brushColor.a;
|
|
|
+ // Set it to target if its close enough
|
|
|
+ if (Difference(targetHeight, newColor.r) < 0.01f) newColor.r = targetHeight;
|
|
|
+ if (Difference(targetHeight, newColor.g) < 0.01f) newColor.g = targetHeight;
|
|
|
+ if (Difference(targetHeight, newColor.b) < 0.01f) newColor.b = targetHeight;
|
|
|
+ terrainImage.SetPixel(pos.x, pos.y, newColor);
|
|
|
+ }
|
|
|
}
|
|
|
- dirty = true;
|
|
|
}
|
|
|
|
|
|
- void EditModePaintFoliage(StringHash eventType, VariantMap& eventData)
|
|
|
+ private void UpdateTerrainSetConstantHeight(Image@ terrainImage, float height)
|
|
|
{
|
|
|
- CheckBox@ edit = eventData["Element"].GetPtr();
|
|
|
- if (edit.checked){
|
|
|
- editMode = TERRAIN_EDITMODE_PAINTFOLIAGE;
|
|
|
- currentToolDescText.text = "Paint foliage onto the terrain";
|
|
|
+ height = Clamp(height, 0.0, 1.0);
|
|
|
+ Color newColor = Color(height, height, height);
|
|
|
+ // Iterate over the entire brush image
|
|
|
+ for (int y = 0; y < terrainImage.height; ++y)
|
|
|
+ {
|
|
|
+ for (int x = 0; x < terrainImage.width; ++x)
|
|
|
+ {
|
|
|
+ terrainImage.SetPixel(x, y, newColor);
|
|
|
+ }
|
|
|
}
|
|
|
- dirty = true;
|
|
|
- }
|
|
|
-
|
|
|
- void BrushSelected(StringHash eventType, VariantMap& eventData)
|
|
|
- {
|
|
|
- CheckBox@ checkbox = cast<CheckBox>(eventData["Element"].GetPtr());
|
|
|
- if (checkbox.checked == false)
|
|
|
- return;
|
|
|
- selectedBrush = checkbox;
|
|
|
- selectedBrushImage = GetBrushImage(selectedBrush.name);
|
|
|
- Print("Selected Brush: " + selectedBrush.name);
|
|
|
- dirty = true;
|
|
|
}
|
|
|
}
|