diff --git a/BrowEdit3.vcxproj b/BrowEdit3.vcxproj
index 28af1ebd..1e06bcbf 100644
--- a/BrowEdit3.vcxproj
+++ b/BrowEdit3.vcxproj
@@ -32,6 +32,7 @@
+
@@ -72,6 +73,7 @@
+
@@ -105,6 +107,7 @@
+
@@ -163,6 +166,7 @@
+
@@ -205,6 +209,7 @@
+
@@ -266,6 +271,8 @@
+
+
@@ -371,8 +378,8 @@
MSVCRT
- xcopy /y $(TargetPath) $(SolutionDir)
-xcopy /y $(OutDir)BrowEdit3.pdb $(SolutionDir)
+ xcopy /y "$(TargetPath)" "$(SolutionDir)"
+xcopy /y "$(OutDir)BrowEdit3.pdb" "$(SolutionDir)"
diff --git a/BrowEdit3.vcxproj.filters b/BrowEdit3.vcxproj.filters
index 082a1184..11e953c8 100644
--- a/BrowEdit3.vcxproj.filters
+++ b/BrowEdit3.vcxproj.filters
@@ -451,6 +451,15 @@
lib\glad
+
+ browedit\windows
+
+
+ browedit
+
+
+ browedit\actions
+
@@ -780,6 +789,12 @@
lib\lzma
+
+ browedit\actions
+
+
+ browedit\shaders
+
@@ -853,6 +868,12 @@
docs\formats
+
+ data\shaders
+
+
+ data\shaders
+
diff --git a/browedit/BrowEdit.cpp b/browedit/BrowEdit.cpp
index 94dfc492..f8e5caf7 100644
--- a/browedit/BrowEdit.cpp
+++ b/browedit/BrowEdit.cpp
@@ -276,6 +276,8 @@ void BrowEdit::run()
showShadowEditWindow();
if (editMode == EditMode::Cinematic)
showCinematicModeWindow();
+ if (editMode == EditMode::Water)
+ showWaterEditWindow();
if (windowData.hotkeyEditWindowVisible)
showHotkeyEditorWindow();
@@ -595,6 +597,8 @@ void BrowEdit::showMapWindow(MapView& mapView, float deltaTime)
mapView.postRenderShadowMode(this);
else if (editMode == EditMode::Cinematic)
mapView.postRenderCinematicMode(this);
+ else if (editMode == EditMode::Water)
+ mapView.postRenderWaterMode(this);
}
diff --git a/browedit/BrowEdit.h b/browedit/BrowEdit.h
index 40a876c8..bcd33026 100644
--- a/browedit/BrowEdit.h
+++ b/browedit/BrowEdit.h
@@ -227,6 +227,7 @@ class BrowEdit
Shadow,
Sprite,
Cinematic,
+ Water,
} editMode = EditMode::Gat;
enum class SelectTool
@@ -352,6 +353,7 @@ class BrowEdit
void showColorEditWindow();
void showShadowEditWindow();
void showCinematicModeWindow();
+ void showWaterEditWindow();
void copyTiles();
void copyGat();
diff --git a/browedit/HotkeyActions.cpp b/browedit/HotkeyActions.cpp
index dd2d2550..8e872888 100644
--- a/browedit/HotkeyActions.cpp
+++ b/browedit/HotkeyActions.cpp
@@ -218,6 +218,7 @@ void BrowEdit::registerActions()
HotkeyRegistry::registerAction(HotkeyAction::EditMode_Shadow, [this]() { editMode = EditMode::Shadow; });
HotkeyRegistry::registerAction(HotkeyAction::EditMode_Sprite, [this]() { editMode = EditMode::Sprite; });
HotkeyRegistry::registerAction(HotkeyAction::EditMode_Cinematic, [this]() { editMode = EditMode::Cinematic; });
+ HotkeyRegistry::registerAction(HotkeyAction::EditMode_Water, [this]() { editMode = EditMode::Water; });
HotkeyRegistry::registerAction(HotkeyAction::View_ShadowMap, [this]() { activeMapView->viewLightmapShadow = !activeMapView->viewLightmapShadow; }, hasActiveMapView);
HotkeyRegistry::registerAction(HotkeyAction::View_ColorMap, [this]() { activeMapView->viewLightmapColor = !activeMapView->viewLightmapColor; }, hasActiveMapView);
diff --git a/browedit/HotkeyRegistry.h b/browedit/HotkeyRegistry.h
index 8042ddc6..7fe9b707 100644
--- a/browedit/HotkeyRegistry.h
+++ b/browedit/HotkeyRegistry.h
@@ -128,6 +128,7 @@ enum class HotkeyAction
EditMode_Shadow,
EditMode_Sprite,
EditMode_Cinematic,
+ EditMode_Water,
View_ShadowMap,
View_ColorMap,
diff --git a/browedit/Map.h b/browedit/Map.h
index ae3d3fcd..ac3dc01b 100644
--- a/browedit/Map.h
+++ b/browedit/Map.h
@@ -21,6 +21,7 @@ class Map
std::vector selectedNodes;
std::vector tileSelection;
std::vector gatSelection;
+ std::vector waterSelection;
bool changed = false;
bool mapHasNoGnd = false; // This variable is meant to prevent the console from being filled with the "map has no gnd" error
diff --git a/browedit/MapView.Gatmode.cpp b/browedit/MapView.Gatmode.cpp
index 3c00790d..3734c1d3 100644
--- a/browedit/MapView.Gatmode.cpp
+++ b/browedit/MapView.Gatmode.cpp
@@ -402,7 +402,7 @@ void MapView::postRenderGatMode(BrowEdit* browEdit)
if (gadgetHeight[i].axisClicked)
{
dragIndex = i;
- mouseDragPlane.normal = glm::normalize(glm::vec3(nodeRenderContext.viewMatrix * glm::vec4(0, 0, 1, 1)) - glm::vec3(nodeRenderContext.viewMatrix * glm::vec4(0, 0, 0, 1)));
+ mouseDragPlane.normal = -mouseRay.dir;
mouseDragPlane.normal.y = 0;
mouseDragPlane.normal = glm::normalize(mouseDragPlane.normal);
mouseDragPlane.D = -glm::dot(pos[i], mouseDragPlane.normal);
diff --git a/browedit/MapView.Heightmode.cpp b/browedit/MapView.Heightmode.cpp
index f954e20b..63888530 100644
--- a/browedit/MapView.Heightmode.cpp
+++ b/browedit/MapView.Heightmode.cpp
@@ -498,7 +498,7 @@ void MapView::postRenderHeightMode(BrowEdit* browEdit)
if (gadgetHeight[i].axisClicked)
{
dragIndex = i;
- mouseDragPlane.normal = glm::normalize(glm::vec3(nodeRenderContext.viewMatrix * glm::vec4(0, 0, 1, 1)) - glm::vec3(nodeRenderContext.viewMatrix * glm::vec4(0, 0, 0, 1)));
+ mouseDragPlane.normal = -mouseRay.dir;
mouseDragPlane.normal.y = 0;
mouseDragPlane.normal = glm::normalize(mouseDragPlane.normal);
mouseDragPlane.D = -glm::dot(pos[i], mouseDragPlane.normal);
diff --git a/browedit/MapView.Watermode.cpp b/browedit/MapView.Watermode.cpp
new file mode 100644
index 00000000..ebe20c8d
--- /dev/null
+++ b/browedit/MapView.Watermode.cpp
@@ -0,0 +1,496 @@
+#define IMGUI_DEFINE_MATH_OPERATORS
+#include
+#include "MapView.h"
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include
+
+void MapView::rebuildWaterGrid(Rsw* rsw, Gnd* gnd, bool forced)
+{
+ std::vector verts;
+ glm::vec3 n(0, -1, 0);
+
+ if (waterGridDirty || forced || rsw->water.splitHeight * rsw->water.splitWidth * 8 != waterGridVbo->size()) {
+ for (int x = 0; x < rsw->water.splitWidth; x++)
+ {
+ for (int y = 0; y < rsw->water.splitHeight; y++)
+ {
+ int xmin, xmax, ymin, ymax;
+ auto zone = &rsw->water.zones[x][y];
+
+ rsw->water.getBoundsFromGnd(x, y, gnd, &xmin, &xmax, &ymin, &ymax);
+
+ VertexP3 v0 = VertexP3(glm::vec3(10 * xmin, -zone->height, 10 * gnd->height - 10 * ymin));
+ VertexP3 v1 = VertexP3(glm::vec3(10 * xmax, -zone->height, 10 * gnd->height - 10 * ymax));
+ VertexP3 v2 = VertexP3(glm::vec3(10 * xmax, -zone->height, 10 * gnd->height - 10 * ymin));
+ VertexP3 v3 = VertexP3(glm::vec3(10 * xmin, -zone->height, 10 * gnd->height - 10 * ymax));
+
+ verts.push_back(v0); verts.push_back(v2);
+ verts.push_back(v0); verts.push_back(v3);
+ verts.push_back(v1); verts.push_back(v3);
+ verts.push_back(v1); verts.push_back(v2);
+ }
+ }
+
+ waterGridVbo->setData(verts, GL_STATIC_DRAW);
+ waterGridDirty = false;
+ }
+}
+
+void MapView::postRenderWaterMode(BrowEdit* browEdit)
+{
+ float gridSize = gridSizeTranslate;
+ float gridOffset = gridOffsetTranslate;
+ static bool isDragged = false;
+
+ auto rsw = map->rootNode->getComponent();
+ auto gnd = map->rootNode->getComponent();
+ auto gndRenderer = map->rootNode->getComponent();
+ auto waterRenderer = map->rootNode->getComponent();
+
+ if (!rsw || !gnd || !gndRenderer || !waterRenderer)
+ return;
+
+ glUseProgram(0);
+ glMatrixMode(GL_PROJECTION);
+ glLoadMatrixf(glm::value_ptr(nodeRenderContext.projectionMatrix));
+ glMatrixMode(GL_MODELVIEW);
+ glLoadMatrixf(glm::value_ptr(nodeRenderContext.viewMatrix));
+ glColor3f(1, 0, 0);
+ fbo->bind();
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
+ glDisable(GL_TEXTURE_2D);
+
+ simpleShader->use();
+ simpleShader->setUniform(SimpleShader::Uniforms::projectionMatrix, nodeRenderContext.projectionMatrix);
+ simpleShader->setUniform(SimpleShader::Uniforms::viewMatrix, nodeRenderContext.viewMatrix);
+ simpleShader->setUniform(SimpleShader::Uniforms::modelMatrix, glm::mat4(1.0f));
+ simpleShader->setUniform(SimpleShader::Uniforms::textureFac, 0.0f);
+ glEnable(GL_BLEND);
+ glDepthMask(0);
+ bool canSelect = true;
+ glm::vec3 n(0, -1, 0);
+
+ auto mouse3D = rsw->rayCastWater(mouseRay, gnd, viewEmptyTiles);
+ glm::ivec2 tileHovered;
+
+ if (mouse3D == glm::vec3(std::numeric_limits::max())) {
+ tileHovered = glm::ivec2(-1, -1);
+ }
+ else {
+ rsw->water.getIndexFromGnd((int)glm::floor(mouse3D.x / 10), gnd->height - (int)glm::floor(mouse3D.z) / 10, gnd, &tileHovered.x, &tileHovered.y);
+ }
+
+ ImGui::Begin("Statusbar");
+ ImGui::SetNextItemWidth(100.0f);
+ ImGui::Text("Water zone: %d,%d", tileHovered.x, tileHovered.y);
+ ImGui::SameLine();
+ ImGui::End();
+
+ bool snap = snapToGrid;
+ if (ImGui::GetIO().KeyShift)
+ snap = !snap;
+
+ int perWidth = glm::max(1, gnd->width / glm::max(1, rsw->water.splitWidth));
+ int perHeight = glm::max(1, gnd->height / glm::max(1, rsw->water.splitHeight));
+
+ std::vector waterSelection;
+
+ // Always select the entire water if there are no split zones
+ if (gnd->version < 0x108 && map->waterSelection.size() == 0)
+ map->waterSelection.push_back(glm::ivec2(0, 0));
+
+ for (auto& tile : map->waterSelection)
+ {
+ // The selection can become invalid if the split zones count was changed
+ if (tile.x < 0 || tile.x >= rsw->water.splitWidth ||
+ tile.y < 0 || tile.y >= rsw->water.splitHeight)
+ continue;
+
+ waterSelection.push_back(tile);
+ }
+
+ // Draw the white water grid, it's hard to see the zones otherwise
+ rebuildWaterGrid(rsw, gnd);
+
+ if (showWaterGrid && !isDragged && waterGridVbo->size() > 0) {
+ waterGridVbo->bind();
+ glEnableVertexAttribArray(0);
+ glDisableVertexAttribArray(1);
+ glDisableVertexAttribArray(2);
+ glDisableVertexAttribArray(3);
+ glDisableVertexAttribArray(4);
+ glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
+ simpleShader->setUniform(SimpleShader::Uniforms::color, glm::vec4(1, 1, 1, 0.25f));
+ glVertexAttribPointer(0, 3, GL_FLOAT, false, sizeof(VertexP3), (void*)(0 * sizeof(float)));
+ glDrawArrays(GL_LINES, 0, (int)waterGridVbo->size());
+ glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
+ waterGridVbo->unBind();
+ }
+
+ if (waterSelection.size() > 0 && canSelect)
+ {
+ std::vector verts;
+ std::vector vertsGrid;
+
+ for (auto& tile : waterSelection)
+ {
+ int xmin, xmax, ymin, ymax;
+ auto zone = &rsw->water.zones[tile.x][tile.y];
+
+ rsw->water.getBoundsFromGnd(tile.x, tile.y, gnd, &xmin, &xmax, &ymin, &ymax);
+
+ VertexP3 v0 = VertexP3(glm::vec3(10 * xmin, -zone->height, 10 * gnd->height - 10 * ymin));
+ VertexP3 v1 = VertexP3(glm::vec3(10 * xmax, -zone->height, 10 * gnd->height - 10 * ymax));
+ VertexP3 v2 = VertexP3(glm::vec3(10 * xmax, -zone->height, 10 * gnd->height - 10 * ymin));
+ VertexP3 v3 = VertexP3(glm::vec3(10 * xmin, -zone->height, 10 * gnd->height - 10 * ymax));
+
+ verts.push_back(v0);
+ verts.push_back(v1);
+ verts.push_back(v2);
+
+ verts.push_back(v3);
+ verts.push_back(v0);
+ verts.push_back(v1);
+
+ vertsGrid.push_back(v0); vertsGrid.push_back(v2);
+ vertsGrid.push_back(v0); vertsGrid.push_back(v3);
+ vertsGrid.push_back(v1); vertsGrid.push_back(v3);
+ vertsGrid.push_back(v1); vertsGrid.push_back(v2);
+ }
+
+ if (vertsGrid.size() > 0) {
+ glEnableVertexAttribArray(0);
+ glDisableVertexAttribArray(1);
+ glDisableVertexAttribArray(2);
+ glDisableVertexAttribArray(3);
+ glDisableVertexAttribArray(4);
+ glVertexAttribPointer(0, 3, GL_FLOAT, false, sizeof(VertexP3), verts[0].data);
+
+ glLineWidth(1.0f);
+ simpleShader->setUniform(SimpleShader::Uniforms::color, glm::vec4(1, 0, 0, (gnd->version >= 0x108 && showWaterSelectedOverlay) ? 0.15f : 0.0f));
+ glDrawArrays(GL_TRIANGLES, 0, (int)verts.size());
+ glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
+ simpleShader->setUniform(SimpleShader::Uniforms::color, glm::vec4(1, 0, 0, 1.0f));
+ glVertexAttribPointer(0, 3, GL_FLOAT, false, sizeof(VertexP3), vertsGrid[0].data);
+ glDrawArrays(GL_LINES, 0, (int)vertsGrid.size());
+ glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
+ }
+
+ Gadget::setMatrices(nodeRenderContext.projectionMatrix, nodeRenderContext.viewMatrix);
+ glDepthMask(1);
+
+ glm::ivec2 maxValues(-99999, -99999), minValues(999999, 9999999);
+
+ for (auto& tile : waterSelection)
+ {
+ maxValues = glm::max(maxValues, tile);
+ minValues = glm::min(minValues, tile);
+ }
+
+ static std::map originalValues;
+ static glm::vec3 clickedPos;
+
+ int xmin = perWidth * minValues.x;
+ int xmax = perWidth * (maxValues.x + 1);
+
+ if (maxValues.x == rsw->water.splitWidth - 1)
+ xmax = gnd->width;
+
+ int ymin = perHeight * minValues.y - 1;
+ int ymax = perHeight * (maxValues.y + 1) - 1;
+
+ if (maxValues.y == rsw->water.splitHeight - 1)
+ ymax = gnd->height - 1;
+
+ glm::vec3 pos[5];
+ glm::vec3 posGadget[5];
+ posGadget[0] = pos[0] = glm::vec3(10 * xmin, -rsw->water.zones[minValues.x][minValues.y].height, 10 * gnd->height - 10 * ymin);
+ posGadget[1] = pos[1] = glm::vec3(10 * xmax, -rsw->water.zones[maxValues.x][minValues.y].height, 10 * gnd->height - 10 * ymin);
+ posGadget[2] = pos[2] = glm::vec3(10 * xmin, -rsw->water.zones[minValues.x][maxValues.y].height, 10 * gnd->height - 10 * ymax);
+ posGadget[3] = pos[3] = glm::vec3(10 * xmax, -rsw->water.zones[maxValues.x][maxValues.y].height, 10 * gnd->height - 10 * ymax);
+
+ glm::vec3 center = (pos[0] + pos[1] + pos[2] + pos[3]) / 4.0f;
+
+ int cx = (maxValues.x - minValues.x) / 2 + minValues.x;
+ int cy = (maxValues.y - minValues.y) / 2 + minValues.y;
+ center.y = -rsw->water.zones[cx][cy].height;
+
+ posGadget[4] = pos[4] = center;
+
+ glm::vec3 cameraPos = glm::inverse(nodeRenderContext.viewMatrix) * glm::vec4(0, 0, 0, 1);
+ int order[5] = { 0, 1, 2, 3, 4 };
+ std::sort(std::begin(order), std::end(order), [&](int a, int b)
+ {
+ return glm::distance(cameraPos, pos[a]) < glm::distance(cameraPos, pos[b]);
+ });
+
+ static int dragIndex = -1;
+ for (int ii = 0; ii < 5; ii++)
+ {
+ if (gadgetScale == 0)
+ continue;
+ int i = order[ii];
+
+ if (!browEdit->windowData.heightEdit.showCornerArrows && i < 4)
+ continue;
+ if (!browEdit->windowData.heightEdit.showCenterArrow && i == 4)
+ continue;
+ if (!browEdit->windowData.heightEdit.showEdgeArrows && i > 4)
+ continue;
+ glm::mat4 mat = glm::translate(glm::mat4(1.0f), posGadget[i]);
+
+ if (dragIndex == -1)
+ gadgetHeight[i].disabled = false;
+ else
+ gadgetHeight[i].disabled = dragIndex != i;
+
+ gadgetHeight[i].draw(mouseRay, mat);
+
+ if (gadgetHeight[i].axisClicked)
+ {
+ dragIndex = i;
+ mouseDragPlane.normal = -mouseRay.dir;
+ mouseDragPlane.normal.y = 0;
+ mouseDragPlane.normal = glm::normalize(mouseDragPlane.normal);
+ mouseDragPlane.D = -glm::dot(pos[i], mouseDragPlane.normal);
+
+ float f;
+ mouseRay.planeIntersection(mouseDragPlane, f);
+ mouseDragStart = mouseRay.origin + f * mouseRay.dir;
+
+ originalValues.clear();
+ for (auto& t : waterSelection)
+ originalValues[&rsw->water.zones[t.x][t.y]] = rsw->water.zones[t.x][t.y].height;
+
+ clickedPos = pos[i];
+
+ canSelect = false;
+ waterRenderer->renderFullWater = true;
+ waterRenderer->setDirty();
+ }
+ else if (gadgetHeight[i].axisReleased)
+ {
+ dragIndex = -1;
+ canSelect = false;
+ isDragged = false;
+
+ std::map newValues;
+ for (auto& t : originalValues)
+ newValues[t.first] = t.first->height;
+ map->doAction(new WaterHeightChangeAction(originalValues, newValues, waterSelection), browEdit);
+ waterRenderer->renderFullWater = false;
+ waterRenderer->setDirty();
+
+ rebuildWaterGrid(rsw, gnd, true);
+ }
+ else if (gadgetHeight[i].axisDragged)
+ {
+ isDragged = true;
+
+ if (snap)
+ {
+ glm::mat4 mat(1.0f);
+
+ glm::vec3 rounded = pos[i];
+ if (!gridLocal)
+ rounded.y = glm::floor(rounded.y / gridSize) * gridSize;
+
+ mat = glm::translate(mat, rounded);
+ mat = glm::rotate(mat, glm::radians(90.0f), glm::vec3(1, 0, 0));
+ simpleShader->setUniform(SimpleShader::Uniforms::modelMatrix, mat);
+ simpleShader->setUniform(SimpleShader::Uniforms::color, glm::vec4(0, 0, 0, 1));
+ glLineWidth(2);
+ gridVbo->bind();
+ glEnableVertexAttribArray(0);
+ glDisableVertexAttribArray(1);
+ glDisableVertexAttribArray(2);
+ glDisableVertexAttribArray(3);
+ glDisableVertexAttribArray(4);
+ glVertexAttribPointer(0, 3, GL_FLOAT, false, sizeof(VertexP3), (void*)(0 * sizeof(float)));
+ glDrawArrays(GL_LINES, 0, (int)gridVbo->size());
+ gridVbo->unBind();
+ }
+
+ float f;
+ mouseRay.planeIntersection(mouseDragPlane, f);
+ glm::vec3 mouseOffset = (mouseRay.origin + f * mouseRay.dir) - mouseDragStart;
+
+ if (snap && gridLocal)
+ mouseOffset = glm::round(mouseOffset / (float)gridSize) * (float)gridSize;
+
+ canSelect = false;
+
+ float offsetY = clickedPos.y - mouseOffset.y;
+ if (snap && !gridLocal)
+ {
+ offsetY += 2 * mouseOffset.y;
+ offsetY = glm::round(offsetY / gridSize) * gridSize;
+ offsetY = clickedPos.y + (clickedPos.y - offsetY);
+ }
+
+ for (auto& t : waterSelection)
+ {
+ rsw->water.zones[t.x][t.y].height = originalValues[&rsw->water.zones[t.x][t.y]] + offsetY - clickedPos.y;
+ }
+ }
+ }
+
+ simpleShader->setUniform(SimpleShader::Uniforms::modelMatrix, glm::mat4(1.0f));
+ glDepthMask(0);
+ }
+
+ if (hovered && canSelect)
+ {
+ if (ImGui::IsMouseDown(0))
+ {
+ if (!mouseDown)
+ mouseDragStart = mouse3D;
+ mouseDown = true;
+ }
+
+ if (ImGui::IsMouseReleased(0) && hovered)
+ {
+ auto mouseDragEnd = mouse3D;
+ mouseDown = false;
+
+ std::map newSelection;
+ if (ImGui::GetIO().KeyShift || ImGui::GetIO().KeyCtrl) {
+ for (auto& tile : waterSelection) {
+ newSelection[tile.x + rsw->water.splitWidth * tile.y] = tile;
+ }
+ }
+ if (browEdit->selectTool == BrowEdit::SelectTool::Rectangle)
+ {
+ int waterMinX, waterMinY, waterMaxX, waterMaxY;
+
+ rsw->water.getIndexFromGnd(
+ (int)glm::floor(glm::min(mouseDragStart.x, mouseDragEnd.x) / 10),
+ gnd->height - (int)glm::floor(glm::max(mouseDragStart.z, mouseDragEnd.z) / 10),
+ gnd,
+ &waterMinX,
+ &waterMinY);
+
+ rsw->water.getIndexFromGnd(
+ (int)glm::floor(glm::max(mouseDragStart.x, mouseDragEnd.x) / 10),
+ gnd->height - (int)glm::floor(glm::min(mouseDragStart.z, mouseDragEnd.z) / 10),
+ gnd,
+ &waterMaxX,
+ &waterMaxY);
+
+ if (waterMinX >= 0 && waterMaxX < rsw->water.splitWidth && waterMinY >= 0 && waterMaxY < rsw->water.splitHeight)
+ for (int x = waterMinX; x < waterMaxX + 1; x++)
+ {
+ for (int y = waterMinY; y < waterMaxY + 1; y++)
+ {
+ if (ImGui::GetIO().KeyCtrl)
+ {
+ if (newSelection.contains(x + rsw->water.splitWidth * y))
+ newSelection.erase(x + rsw->water.splitWidth * y);
+ }
+ else if (!newSelection.contains(x + rsw->water.splitWidth * y))
+ newSelection[x + rsw->water.splitWidth * y] = glm::ivec2(x, y);
+ }
+ }
+ }
+
+ std::vector newSelection2;
+
+ for (const auto& entry : newSelection) {
+ newSelection2.push_back(entry.second);
+ }
+
+ if (waterSelection != newSelection2)
+ {
+ map->doAction(new WaterZoneSelectAction(map, newSelection2), browEdit);
+ }
+ }
+ }
+
+ if (mouseDown)
+ {
+ auto mouseDragEnd = mouse3D;
+ std::vector verts;
+ float dist = 0.002f * cameraDistance;
+ int waterMinX, waterMinY, waterMaxX, waterMaxY;
+
+ rsw->water.getIndexFromGnd(
+ (int)glm::floor(glm::min(mouseDragStart.x, mouseDragEnd.x) / 10),
+ gnd->height - (int)glm::floor(glm::max(mouseDragStart.z, mouseDragEnd.z) / 10),
+ gnd,
+ &waterMinX,
+ &waterMinY);
+
+ rsw->water.getIndexFromGnd(
+ (int)glm::floor(glm::max(mouseDragStart.x, mouseDragEnd.x) / 10),
+ gnd->height - (int)glm::floor(glm::min(mouseDragStart.z, mouseDragEnd.z) / 10),
+ gnd,
+ &waterMaxX,
+ &waterMaxY);
+
+ ImGui::Begin("Statusbar");
+ ImGui::Text("Selection: (%d,%d) - (%d,%d)", waterMinX, waterMinY, waterMaxX, waterMaxY);
+ ImGui::End();
+
+ //if (browEdit->selectTool == BrowEdit::SelectTool::Rectangle)
+ //{
+ if (waterMinX >= 0 && waterMaxX < rsw->water.splitWidth && waterMinY >= 0 && waterMaxY < rsw->water.splitHeight)
+ for (int x = waterMinX; x < waterMaxX + 1; x++)
+ {
+ for (int y = waterMinY; y < waterMaxY + 1; y++)
+ {
+ int xmin, xmax, ymin, ymax;
+ auto zone = &rsw->water.zones[x][y];
+
+ rsw->water.getBoundsFromGnd(x, y, gnd, &xmin, &xmax, &ymin, &ymax);
+
+ verts.push_back(VertexP3(glm::vec3(10 * xmin, -zone->height + dist, 10 * gnd->height - 10 * ymin)));
+ verts.push_back(VertexP3(glm::vec3(10 * xmax, -zone->height + dist, 10 * gnd->height - 10 * ymax)));
+ verts.push_back(VertexP3(glm::vec3(10 * xmax, -zone->height + dist, 10 * gnd->height - 10 * ymin)));
+
+ verts.push_back(VertexP3(glm::vec3(10 * xmin, -zone->height + dist, 10 * gnd->height - 10 * ymax)));
+ verts.push_back(VertexP3(glm::vec3(10 * xmin, -zone->height + dist, 10 * gnd->height - 10 * ymin)));
+ verts.push_back(VertexP3(glm::vec3(10 * xmax, -zone->height + dist, 10 * gnd->height - 10 * ymax)));
+ }
+ }
+
+ if (verts.size() > 0)
+ {
+ glEnableVertexAttribArray(0);
+ glDisableVertexAttribArray(1);
+ glDisableVertexAttribArray(2);
+ glDisableVertexAttribArray(3);
+ glDisableVertexAttribArray(4);
+ glVertexAttribPointer(0, 3, GL_FLOAT, false, sizeof(VertexP3), verts[0].data);
+ simpleShader->setUniform(SimpleShader::Uniforms::color, glm::vec4(1, 0, 0, 0.5f));
+ glDrawArrays(GL_TRIANGLES, 0, (int)verts.size());
+ }
+ //}
+ }
+
+ glDepthMask(1);
+ fbo->unbind();
+}
\ No newline at end of file
diff --git a/browedit/MapView.cpp b/browedit/MapView.cpp
index a9ea8fa4..9df6f628 100644
--- a/browedit/MapView.cpp
+++ b/browedit/MapView.cpp
@@ -27,6 +27,7 @@
#include
#include
#include
+#include
#include
#include
@@ -48,7 +49,9 @@ extern float timeSelected;
MapView::MapView(Map* map, const std::string &viewName) : map(map), viewName(viewName), mouseRay(glm::vec3(0,0,0), glm::vec3(0,0,0))
{
- fbo = new gl::FBO(1920, 1080, true);
+ fbo = new gl::FBO(1920, 1080, false, 1, true);
+ outlineFbo = new gl::FBO(1920, 1080, false, 1, true);
+
//shader = new TestShader();
auto gnd = map->rootNode->getComponent();
@@ -73,6 +76,8 @@ MapView::MapView(Map* map, const std::string &viewName) : map(map), viewName(vie
skyDomeMesh.init();
if (!simpleShader)
simpleShader = util::ResourceManager::load();
+ if (!outlineShader)
+ outlineShader = util::ResourceManager::load();
for(int i = 0; i < 9; i++)
gadgetHeight[i].mode = Gadget::Mode::TranslateY;
@@ -80,8 +85,11 @@ MapView::MapView(Map* map, const std::string &viewName) : map(map), viewName(vie
gridVbo = new gl::VBO();
rotateGridVbo = new gl::VBO();
textureGridVbo = new gl::VBO();
+ waterGridVbo = new gl::VBO();
+ outlineVbo = new gl::VBO();
rebuildObjectModeGrid();
rebuildObjectRotateModeGrid();
+ rebuildOutlineVbo();
}
void MapView::toolbar(BrowEdit* browEdit)
@@ -335,6 +343,11 @@ void MapView::toolbar(BrowEdit* browEdit)
void MapView::render(BrowEdit* browEdit)
{
+ outlineFbo->bind();
+ glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+ outlineFbo->unbind();
+
fbo->bind();
glViewport(0, 0, fbo->getWidth(), fbo->getHeight());
glClearColor(browEdit->config.backgroundColor.r, browEdit->config.backgroundColor.g, browEdit->config.backgroundColor.b, 1.0f);
@@ -487,8 +500,46 @@ void MapView::render(BrowEdit* browEdit)
}
map->rootNode->getComponent()->enabled = viewWater;
+ nodeRenderContext.mapView = this;
+ nodeRenderContext.time = (float)glfwGetTime();
+ nodeRenderContext.fbo = fbo;
+ nodeRenderContext.outlineFbo = outlineFbo;
NodeRenderer::render(map->rootNode, nodeRenderContext);
+ // Draw selection outline
+ outlineShader->use();
+ outlineShader->setUniform(OutlineShader::Uniforms::iResolution, glm::vec2(outlineFbo->getWidth(), outlineFbo->getHeight()));
+ outlineShader->setUniform(OutlineShader::Uniforms::selectionOverlay, glm::vec4(1, 0, 0, 0.15f));
+ outlineShader->setUniform(OutlineShader::Uniforms::selectionOutline, glm::vec4(1, 0, 0, 1));
+ outlineShader->setUniform(OutlineShader::Uniforms::selectionOccludedOverlay, glm::vec4(1, 1, 1, 0));
+ outlineShader->setUniform(OutlineShader::Uniforms::selectionOccludedOutline, glm::vec4(1, 1, 1, 0.5f));
+
+ glActiveTexture(GL_TEXTURE1);
+ glBindTexture(GL_TEXTURE_2D, fbo->depthTexture);
+ glUniform1i(glGetUniformLocation(outlineShader->programId, "s_depth_fbo"), 1);
+ glActiveTexture(GL_TEXTURE2);
+ glBindTexture(GL_TEXTURE_2D, outlineFbo->depthTexture);
+ glUniform1i(glGetUniformLocation(outlineShader->programId, "s_depth_outline"), 2);
+ glActiveTexture(GL_TEXTURE0);
+
+ outlineVbo->bind();
+ glDisable(GL_DEPTH_TEST);
+ glEnable(GL_BLEND);
+ glBindTexture(GL_TEXTURE_2D, outlineFbo->texid[0]);
+
+ glEnableVertexAttribArray(0);
+ glEnableVertexAttribArray(1);
+ glDisableVertexAttribArray(2);
+ glDisableVertexAttribArray(3);
+ glDisableVertexAttribArray(4);
+
+ glVertexAttribPointer(0, 3, GL_FLOAT, false, sizeof(VertexP3T2), (void*)(0 * sizeof(float)));
+ glVertexAttribPointer(1, 2, GL_FLOAT, false, sizeof(VertexP3T2), (void*)(3 * sizeof(float)));
+
+ glDrawArrays(GL_TRIANGLES, 0, (int)outlineVbo->size());
+ glEnable(GL_DEPTH_TEST);
+ outlineVbo->unBind();
+
// position and draw the new nodes
if (browEdit->newNodes.size() > 0 && hovered)
{
@@ -626,6 +677,8 @@ void MapView::render(BrowEdit* browEdit)
//update just fixes the camera
void MapView::update(BrowEdit* browEdit, const ImVec2 &size, float deltaTime)
{
+ if (outlineFbo->getWidth() != (int)size.x || outlineFbo->getHeight() != (int)size.y)
+ outlineFbo->resize((int)size.x, (int)size.y);
if (fbo->getWidth() != (int)size.x || fbo->getHeight() != (int)size.y)
fbo->resize((int)size.x, (int)size.y);
mouseState.position = glm::vec2(ImGui::GetMousePos().x - ImGui::GetCursorScreenPos().x, ImGui::GetMousePos().y - ImGui::GetCursorScreenPos().y);
@@ -1024,9 +1077,17 @@ void MapView::drawLight(Node* n)
}
}
-
-
-
+void MapView::rebuildOutlineVbo()
+{
+ std::vector verts;
+ verts.push_back(VertexP3T2(glm::vec3(-1, -1, 0), glm::vec2(0, 0)));
+ verts.push_back(VertexP3T2(glm::vec3(1, -1, 0), glm::vec2(1, 0)));
+ verts.push_back(VertexP3T2(glm::vec3(1, 1, 0), glm::vec2(1, 1)));
+ verts.push_back(VertexP3T2(glm::vec3(1, 1, 0), glm::vec2(1, 1)));
+ verts.push_back(VertexP3T2(glm::vec3(-1, 1, 0), glm::vec2(0, 1)));
+ verts.push_back(VertexP3T2(glm::vec3(-1, -1, 0), glm::vec2(0, 0)));
+ outlineVbo->setData(verts, GL_STATIC_DRAW);
+}
void MapView::CubeMesh::buildVertices(std::vector &verts)
{//https://dotnetfiddle.net/tPcv8S
diff --git a/browedit/MapView.h b/browedit/MapView.h
index 961839ea..5575c128 100644
--- a/browedit/MapView.h
+++ b/browedit/MapView.h
@@ -10,6 +10,7 @@
#include
class BrowEdit;
class Map;
+class OutlineShader;
struct ImVec2;
namespace gl
@@ -61,12 +62,15 @@ class MapView
gl::VBO* gridVbo = nullptr;
gl::VBO* rotateGridVbo = nullptr;
+ gl::VBO* waterGridVbo = nullptr;
+ gl::VBO* outlineVbo = nullptr;
NodeRenderContext nodeRenderContext;
Gadget gadget;
Gadget gadgetHeight[9];
BillboardRenderer::BillboardShader* billboardShader;
SimpleShader* simpleShader = nullptr;
+ OutlineShader* outlineShader = nullptr;
static inline SphereMesh sphereMesh;
static inline CubeMesh cubeMesh;
static inline SkyBoxMesh skyBoxMesh;
@@ -81,6 +85,7 @@ class MapView
bool opened = true;
gl::FBO* fbo;
+ gl::FBO* outlineFbo;
bool showViewOptions = false;
bool ortho = false;
@@ -122,6 +127,7 @@ class MapView
gl::VBO* textureGridVbo = nullptr;
bool textureGridSmart = true;
bool textureGridDirty = true;
+ bool waterGridDirty = true;
int textureSelected = 0;
glm::vec2 textureEditUv1 = glm::vec2(0.0f, 0.0f);
@@ -192,6 +198,7 @@ class MapView
void postRenderColorMode(BrowEdit* browEdit);
void postRenderShadowMode(BrowEdit* browEdit);
void postRenderCinematicMode(BrowEdit* browEdit);
+ void postRenderWaterMode(BrowEdit* browEdit);
void gatEdit_adjustToGround(BrowEdit* browEdit);
@@ -200,6 +207,8 @@ class MapView
void rebuildObjectModeGrid();
void rebuildObjectRotateModeGrid();
+ void rebuildWaterGrid(Rsw* rsw, Gnd* gnd, bool forced = false);
+ void rebuildOutlineVbo();
//todo, move this to a struct for better organisation
bool viewLightmapShadow = true;
@@ -224,6 +233,8 @@ class MapView
bool enableLightQuickPreview = false;
bool hideOtherLightmaps = false;
+ bool showWaterGrid = true;
+ bool showWaterSelectedOverlay = true;
void focusSelection();
void drawLight(Node* n);
diff --git a/browedit/ModelEditor.cpp b/browedit/ModelEditor.cpp
index 9e0e12f2..11bd2617 100644
--- a/browedit/ModelEditor.cpp
+++ b/browedit/ModelEditor.cpp
@@ -169,8 +169,6 @@ void ModelEditor::run(BrowEdit* browEdit)
if (m.fbo->getWidth() != size.x || m.fbo->getHeight() != size.y)
m.fbo->resize((int)size.x, (int)size.y);
- rsmRenderer->time = fmod(timeSelected/1000.0f, rsm->animLen/1000.0f);
-
if (size.x > 0 && size.y > 0)
{
m.fbo->bind();
@@ -196,6 +194,8 @@ void ModelEditor::run(BrowEdit* browEdit)
nodeRenderContext.viewMatrix = glm::translate(nodeRenderContext.viewMatrix, glm::vec3(0, rsm->bbrange.y, 0));
else
nodeRenderContext.viewMatrix = glm::translate(nodeRenderContext.viewMatrix, glm::vec3(0, -rsm->bbrange.y, 0));
+ nodeRenderContext.time = fmod(timeSelected / 1000.0f, rsm->animLen / 1000.0f);
+ nodeRenderContext.fbo = m.fbo;
// draw grid
simpleShader->use();
diff --git a/browedit/NodeRenderer.cpp b/browedit/NodeRenderer.cpp
index 71feaf87..b9555633 100644
--- a/browedit/NodeRenderer.cpp
+++ b/browedit/NodeRenderer.cpp
@@ -6,6 +6,8 @@
#include
#include "Node.h"
+class NodeRenderContext;
+
void NodeRenderer::begin()
{
@@ -45,11 +47,11 @@ void NodeRenderer::render(Node* rootNode, NodeRenderContext& context)
{
for (int phase = 0; phase < r->phases; phase++) {
r->phase = phase;
- r->preFrame(rootNode, context.projectionMatrix, context.viewMatrix);
+ r->preFrame(rootNode, context);
for (auto renderer : renderers[r])
if (renderer->enabled && renderer->shouldRender(phase))
- renderer->render();
- r->postFrame();
+ renderer->render(context);
+ r->postFrame(context);
}
}
}
\ No newline at end of file
diff --git a/browedit/NodeRenderer.h b/browedit/NodeRenderer.h
index bf711c86..b599edb6 100644
--- a/browedit/NodeRenderer.h
+++ b/browedit/NodeRenderer.h
@@ -4,8 +4,14 @@
#include