From be2319e6f8768dd2d95349342467e953e210bbf8 Mon Sep 17 00:00:00 2001 From: Dave-tB <90851364+Dave-tB@users.noreply.github.com> Date: Sun, 5 Dec 2021 16:51:34 -0500 Subject: [PATCH 1/6] Added ZK's useful glVolumes, specifically DrawGroundTriangle --- scen_edit/view/glVolumes.lua | 115 ++++++++++++++++++++++++++++++++--- 1 file changed, 106 insertions(+), 9 deletions(-) diff --git a/scen_edit/view/glVolumes.lua b/scen_edit/view/glVolumes.lua index c909f730..cf342e89 100644 --- a/scen_edit/view/glVolumes.lua +++ b/scen_edit/view/glVolumes.lua @@ -18,9 +18,6 @@ end gl.Utilities = gl.Utilities or {} --------------------------------------------------------------------------------- --------------------------------------------------------------------------------- - local min = math.min local max = math.max local sin = math.sin @@ -37,8 +34,8 @@ GL.INCR = 0x1E02 GL.DECR = 0x1E03 GL.INVERT = 0x150A --- local stencilBit1 = 0x01 --- local stencilBit2 = 0x10 +local stencilBit1 = 0x01 +local stencilBit2 = 0x10 -------------------------------------------------------------------------------- -------------------------------------------------------------------------------- @@ -71,6 +68,29 @@ function gl.Utilities.DrawMyBox(minX,minY,minZ, maxX,maxY,maxZ) end) end +function gl.Utilities.DrawMy3DTriangle(x1,z1, x2, z2, x3,z3, minY, maxY) + gl.BeginEnd(GL.TRIANGLES, function() + --// top + glVertex(x1, maxY, z1) + glVertex(x2, maxY, z2) + glVertex(x3, maxY, z3) + --// bottom + glVertex(x1, minY, z1) + glVertex(x3, minY, z3) + glVertex(x2, minY, z2) + end) + gl.BeginEnd(GL.QUAD_STRIP, function() + --// sides + glVertex(x1, maxY, z1) + glVertex(x1, minY, z1) + glVertex(x2, maxY, z2) + glVertex(x2, minY, z2) + glVertex(x3, maxY, z3) + glVertex(x3, minY, z3) + glVertex(x1, maxY, z1) + glVertex(x1, minY, z1) + end) +end local function CreateSinCosTable(divs) local sinTable = {} @@ -133,6 +153,18 @@ function gl.Utilities.DrawMyCylinder(x,y,z, height,radius,divs) end +function gl.Utilities.DrawMyCircle(x,y,radius,divs) + divs = divs or 25 + local sinTable, cosTable = CreateSinCosTable(divs) + + gl.BeginEnd(GL.LINE_LOOP, function() + for i = #sinTable, 1, -1 do + glVertex(x + radius*sinTable[i], y + radius*cosTable[i], 0) + end + end) +end + + function gl.Utilities.DrawMyHollowCylinder(x,y,z, height,radius,inRadius,divs) divs = divs or 25 local sinTable, cosTable = CreateSinCosTable(divs) @@ -195,6 +227,18 @@ function gl.Utilities.DrawGroundRectangle(x1,z1,x2,z2) gl.PopMatrix() end +local triangles = {} +function gl.Utilities.DrawGroundTriangle(args) + if not triangles[args] then + triangles[args] = gl.CreateList(gl.Utilities.DrawMy3DTriangle, args[1], args[2], args[3], args[4], args[5], args[6], -0.5, 0.5) + end + gl.PushMatrix() + gl.Translate(0, averageGroundHeight, 0) + gl.Scale(1, shapeHeight, 1) + gl.Utilities.DrawVolume(triangles[args]) + gl.PopMatrix() +end + local cylinder = gl.CreateList(gl.Utilities.DrawMyCylinder,0,0,0,1,1,35) function gl.Utilities.DrawGroundCircle(x,z,radius) gl.PushMatrix() @@ -204,6 +248,24 @@ function gl.Utilities.DrawGroundCircle(x,z,radius) gl.PopMatrix() end +local circle = gl.CreateList(gl.Utilities.DrawMyCircle,0,0,1,35) +function gl.Utilities.DrawCircle(x,y,radius) + gl.PushMatrix() + gl.Translate(x, y, 0) + gl.Scale(radius, radius, 1) + gl.Utilities.DrawVolume(circle) + gl.PopMatrix() +end + +-- See comment in DrawMergedVolume +function gl.Utilities.DrawMergedGroundCircle(x,z,radius) + gl.PushMatrix() + gl.Translate(x, averageGroundHeight, z) + gl.Scale(radius, shapeHeight, radius) + gl.Utilities.DrawMergedVolume(cylinder) + gl.PopMatrix() +end + local hollowCylinders = { [ 0 ] = cylinder, @@ -244,8 +306,8 @@ function gl.Utilities.DrawVolume(vol_dlist) gl.ColorMask(false, false, false, false) gl.StencilOp(GL.KEEP, GL.INCR, GL.KEEP) --gl.StencilOp(GL.KEEP, GL.INVERT, GL.KEEP) - gl.StencilMask(0x11) - gl.StencilFunc(GL.ALWAYS, 0, 0) + gl.StencilMask(1) + gl.StencilFunc(GL.ALWAYS, 0, 1) gl.CallList(vol_dlist) @@ -253,16 +315,51 @@ function gl.Utilities.DrawVolume(vol_dlist) gl.DepthTest(false) gl.ColorMask(true, true, true, true) gl.StencilOp(GL.ZERO, GL.ZERO, GL.ZERO) - gl.StencilMask(0x11) - gl.StencilFunc(GL.NOTEQUAL, 0, 0+1) + gl.StencilMask(1) + gl.StencilFunc(GL.NOTEQUAL, 0, 1) gl.CallList(vol_dlist) if (gl.DepthClamp) then gl.DepthClamp(false) end gl.StencilTest(false) + -- gl.DepthTest(true) + gl.Culling(false) +end + +-- Make sure that you start with a clear stencil and that you +-- clear it using gl.Clear(GL.STENCIL_BUFFER_BIT, 0) +-- after finishing all the merged volumes +function gl.Utilities.DrawMergedVolume(vol_dlist) + gl.DepthMask(false) + if (gl.DepthClamp) then gl.DepthClamp(true) end + gl.StencilTest(true) + + gl.Culling(false) gl.DepthTest(true) + gl.ColorMask(false, false, false, false) + gl.StencilOp(GL.KEEP, GL.INVERT, GL.KEEP) + --gl.StencilOp(GL.KEEP, GL.INVERT, GL.KEEP) + gl.StencilMask(1) + gl.StencilFunc(GL.ALWAYS, 0, 1) + + gl.CallList(vol_dlist) + + gl.Culling(GL.FRONT) + gl.DepthTest(false) + gl.ColorMask(true, true, true, true) + gl.StencilOp(GL.KEEP, GL.INCR, GL.INCR) + gl.StencilMask(3) + gl.StencilFunc(GL.EQUAL, 1, 3) + + gl.CallList(vol_dlist) + + if (gl.DepthClamp) then gl.DepthClamp(false) end + gl.StencilTest(false) + -- gl.DepthTest(true) gl.Culling(false) end -------------------------------------------------------------------------------- -------------------------------------------------------------------------------- +-------------------------------------------------------------------------------- +-------------------------------------------------------------------------------- From 81ddb96222a2e9b483d021311718a40734108c93 Mon Sep 17 00:00:00 2001 From: Dave-tB <90851364+Dave-tB@users.noreply.github.com> Date: Sun, 5 Dec 2021 16:59:35 -0500 Subject: [PATCH 2/6] Added name parameter for Groupfield, Added RemoveButtonfield. Both Changes are needed to implement a populate function that works on groupfields. --- scen_edit/view/fields/group_field.lua | 8 +- scen_edit/view/fields/remove_button_field.lua | 88 +++++++++++++++++++ 2 files changed, 94 insertions(+), 2 deletions(-) create mode 100644 scen_edit/view/fields/remove_button_field.lua diff --git a/scen_edit/view/fields/group_field.lua b/scen_edit/view/fields/group_field.lua index 58bb7652..18868cd6 100644 --- a/scen_edit/view/fields/group_field.lua +++ b/scen_edit/view/fields/group_field.lua @@ -27,11 +27,15 @@ local _GROUP_INDEX = 0 -- width = 70, -- }), -- }) -function GroupField:init(fields) +function GroupField:init(fields, param) Field.init(self, {}) _GROUP_INDEX = _GROUP_INDEX + 1 - self.name = "_groupField" .. tostring(_GROUP_INDEX) + if param then + self.name = param.name + else + self.name = "_groupField" .. tostring(_GROUP_INDEX) + end self.fields = fields self.components = {} self.autoSize = true diff --git a/scen_edit/view/fields/remove_button_field.lua b/scen_edit/view/fields/remove_button_field.lua new file mode 100644 index 00000000..b91545d3 --- /dev/null +++ b/scen_edit/view/fields/remove_button_field.lua @@ -0,0 +1,88 @@ +SB.Include(Path.Join(SB.DIRS.SRC, 'view/fields/field.lua')) + +--- RemoveButtonField module. + + +--- RemoveButtonField class. +-- @type RemoveButtonField +RemoveButtonField = Field:extends{} + +function RemoveButtonField:Update(source) + -- if source ~= self.checkBox then + -- if self.checkBox.checked ~= self.value then + -- self.checkBox:Toggle() + -- end + -- self.checkBox:Invalidate() + -- end + if source ~= self.toggleButton then + self.toggleButton.checked = self.value + self.toggleButton:Invalidate() + end +end + +--- RemoveButtonField constructor. +-- @function RemoveButtonField() +-- @see field.Field +-- @tparam table opts Table +-- @tparam string opts.title Title. +-- @usage +-- RemoveButtonField({ +-- name = "myRemoveButtonField", +-- value = true, +-- title = "My field", +-- }) +function RemoveButtonField:init(field) + self:__SetDefault("width", 200) + self:__SetDefault("value", false) + + Field.init(self, field) + + -- self.checkBox = Checkbox:New { + -- caption = self.title or "", + -- width = self.width, + -- height = self.height, + -- checked = self.value, + -- tooltip = self.tooltip, + -- OnChange = { + -- function(_, checked) + -- self:Set(checked, self.checkBox) + -- end + -- } + -- } + -- self.components = { + -- self.checkBox, + -- } + + self.toggleButton = Button:New { + caption = self.title or "", + width = self.height, + height = self.height, + checked = self.value, + tooltip = self.tooltip, + classname = "negative_button", + padding = {2, 2, 2, 2}, + children = { + Image:New { + file = Path.Join(SB.DIRS.IMG, 'cancel.png'), + height = 21, + width = 21, + x = "10%", + y = "10%" + }, + }, + OnClick = { + function() + self:__Toggle() + end + } + } + self.components = { + self.toggleButton, + } +end + +function RemoveButtonField:__Toggle() + self.toggleButton.checked = not self.toggleButton.checked + self.toggleButton:Invalidate() + self:Set(self.toggleButton.checked, self.toggleButton) +end From b3f6fa3070cff7af8a2e4bef6fc5794f68b13ec5 Mon Sep 17 00:00:00 2001 From: Dave-tB <90851364+Dave-tB@users.noreply.github.com> Date: Sun, 5 Dec 2021 17:10:33 -0500 Subject: [PATCH 3/6] Added Editing and Viewing for ZK-style metal spots. --- scen_edit/model/mex_manager.lua | 109 +++++++ scen_edit/state/add_metal_state.lua | 83 ++++++ scen_edit/state/view_metal_state.lua | 91 ++++++ scen_edit/view/general/metal_spot_editor.lua | 294 +++++++++++++++++++ 4 files changed, 577 insertions(+) create mode 100644 scen_edit/model/mex_manager.lua create mode 100644 scen_edit/state/add_metal_state.lua create mode 100644 scen_edit/state/view_metal_state.lua create mode 100644 scen_edit/view/general/metal_spot_editor.lua diff --git a/scen_edit/model/mex_manager.lua b/scen_edit/model/mex_manager.lua new file mode 100644 index 00000000..dc5a65be --- /dev/null +++ b/scen_edit/model/mex_manager.lua @@ -0,0 +1,109 @@ +MexManager = Observable:extends{} + +function MexManager:init() + self:super('init') + self.mexIDCount = 0 + self.mexes = {} +end + +function MexManager:addMex(mex, mexID) + if mexID == nil then + mexID = self.mexIDCount + 1 + end + self.mexIDCount = mexID + if mex.xmirror == nil then + mex.xmirror, mex.zmirror = false, false + end + self.mexes[mexID] = mex + self:callListeners("onMexAdded", mexID, mex) + return mexID +end + +function MexManager:getMexIDCount(mexID) + if mexID == nil then + mexID = self.mexIDCount + 1 + end + self.mexIDCount = mexID + return self.mexIDCount +end + +function MexManager:getAllMexes() + local metalspots = {} + for mexID, mex in pairs(self.mexes) do + metalspots[mexID] = mex + end + return metalspots +end + +function MexManager:getAllMexIDs() + local metalspots = {} + for mexID, mex in pairs(self.mexes) do + table.insert(metalspots, mexID) + end + return metalspots +end + +function MexManager:removeMex(mexID) + if self.mexes[mexID] ~= nil then + local mex = self.mexes[mexID] + self.mexes[mexID] = nil + self:callListeners("onMexRemoved", mexID, mex) + end +end + +function MexManager:setMex(mexID, partialObject) + assert(self.mexes[mexID]) + local obj = partialObject + local partialmex = {} + for key, _ in pairs(self.mexes[mexID]) do + if obj[key] ~= nil then + partialmex[key] = self.mexes[mexID][key] + self.mexes[mexID][key] = obj[key] + end + end + self:callListeners("onMexChange", mexID, partialmex) +end + +function MexManager:getMex(mexID) + return self.mexes[mexID] +end +-- Utility functions +local function DistSq(x1, z1, x2, z2) + return (x1 - x2)*(x1 - x2) + (z1 - z2)*(z1 - z2) +end + +function MexManager:getMexIn(x, z) + local x2, z2 = x, z + local selected, dragDiffX, dragDiffZ + for mexID, mex in pairs(self.mexes) do + local xpos, zpos = mex.x, mex.z + if DistSq(xpos, zpos, x2, z2) < 1600 then + selected = mexID + dragDiffX = mex.x - x2 + dragDiffZ = mex.z - z2 + end + end + return selected, dragDiffX, dragDiffZ +end + +function MexManager:getPos(mexID) + assert(self.mexes[mexID]) + local y = Spring.GetGroundHeight(self.mexes[mexID].x, self.mexes[mexID].z) + return {x = self.mexes[mexID].x,y = y, z = self.mexes[mexID].z} +end +------------------------------------------------ +-- Listener definition +------------------------------------------------ +MexManagerListener = LCS.class.abstract{} + +function MexManagerListener:onMexAdded(mexID, mex) +end + +function MexManagerListener:onMexRemoved(mexID, mex) +end + +function MexManagerListener:onMexChange(mexID, partmex, partobj) +end +------------------------------------------------ +-- End listener definition +------------------------------------------------ diff --git a/scen_edit/state/add_metal_state.lua b/scen_edit/state/add_metal_state.lua new file mode 100644 index 00000000..3d9ea266 --- /dev/null +++ b/scen_edit/state/add_metal_state.lua @@ -0,0 +1,83 @@ +AddMetalState = AbstractState:extends{} + +function AddMetalState:init(editorView) + AbstractState.init(self, editorView) + self.params = {} + self.params.metal = editorView.fields["defaultmetal"].value + self.params.xmirror = editorView.fields["defaultxmirror"].value + self.params.zmirror = editorView.fields["defaultzmirror"].value + self.params.x, self.params.z = 0, 0 + self.ev = editorView +end + +function AddMetalState:enterState() + AbstractState.enterState(self) +end + +function AddMetalState:leaveState() + AbstractState.leaveState(self) +end + +function AddMetalState:MousePress(mx, my, button) + if button == 1 then + local result, coords = Spring.TraceScreenRay(mx, my, true) + if result == "ground" then + self.params.x, _, self.params.z = math.floor(coords[1]), coords[2], math.floor(coords[3]) + local objectID = SB.model.mexManager:addMex(self.params) + return true + end + elseif button == 3 then + SB.stateManager:SetState(ViewMetalState(self.ev)) + end +end + +local function DrawSpot(x, z, metal, mirror) + local y = 0 + local r, g, b = 0, 0, 0 + + if mirror then r, g, b = 1, 1, 1 end + + local r2, g2, b2 = (r + 1) % 2, (g + 1) % 2, (b + 1) % 2 + + if x then y = Spring.GetGroundHeight(x,z) end + if (y < 0 or y == nil) then y = 0 end + gl.PushMatrix() + gl.DepthTest(true) + gl.Color(r,g,b,0.7) + gl.LineWidth(6) + gl.DrawGroundCircle(x, 1, z, 40, 21) + gl.Color(r2,g2,b2,0.7) + gl.LineWidth(2) + gl.DrawGroundCircle(x, 1, z, 40, 21) + gl.PopMatrix() + gl.PushMatrix() + gl.Translate(x, y, z) + gl.Rotate(-90, 1, 0, 0) + gl.Translate(0,-40, 0) + gl.Text(metal, 0.0, 0.0, 40, "cno") + gl.PopMatrix() +end + +function AddMetalState:DrawWorld() + for ID, params in pairs(SB.model.mexManager:getAllMexes()) do + local x = params.x + local z = params.z + local metal = "+" .. string.format("%.2f", params.metal) + DrawSpot(x, z, metal) + if params.zmirror and params.xmirror then + x = Game.mapSizeX-x + z = Game.mapSizeZ-z + DrawSpot(x, z, metal, true) + elseif params.xmirror then + x = Game.mapSizeX-x + DrawSpot(x, z, metal, true) + elseif params.zmirror then + z = Game.mapSizeZ-z + DrawSpot(x, z, metal, true) + end + end +end + +function AddMetalState:MouseRelease(...) + SB.stateManager:SetState(ViewMetalState(self.ev)) +end \ No newline at end of file diff --git a/scen_edit/state/view_metal_state.lua b/scen_edit/state/view_metal_state.lua new file mode 100644 index 00000000..1feef9e9 --- /dev/null +++ b/scen_edit/state/view_metal_state.lua @@ -0,0 +1,91 @@ +ViewMetalState = AbstractState:extends{} + +function ViewMetalState:init(editorView) + AbstractState.init(self, editorView) + self.params = {} + self.params.metal = editorView.fields["defaultmetal"].value + self.params.xmirror = editorView.fields["defaultxmirror"].value + self.params.zmirror = editorView.fields["defaultzmirror"].value + self.params.x, self.params.z = 0, 0 + self.ev = editorView +end + +function ViewMetalState:enterState() + AbstractState.enterState(self) +end + +function ViewMetalState:leaveState() + AbstractState.leaveState(self) +end + +function ViewMetalState:MousePress(mx, my, button) + if button == 1 then + local result, coords = Spring.TraceScreenRay(mx, my, true) + if dragID then + local metalspot = SB.model.mexManager:setMex(dragID, {x = coords[1], z = coords[3]}) + SB.SetMouseCursor() + dragID = nil + else + dragID = SB.model.mexManager:getMexIn(coords[1], coords[3]) + end + if dragID then SB.SetMouseCursor ("drag") end + elseif button == 3 then + local result, coords = Spring.TraceScreenRay(mx, my, true) + local x, z = coords[1], coords[3] + local ID = SB.model.mexManager:getMexIn(x, z) + if ID then + SB.model.mexManager:removeMex(ID) + end + end +end + +local function DrawSpot(x, z, metal, mirror) + local y = 0 + local r, g, b = 0, 0, 0 + + if mirror then r, g, b = 1, 1, 1 end + + local r2, g2, b2 = (r + 1) % 2, (g + 1) % 2, (b + 1) % 2 + + if x then y = Spring.GetGroundHeight(x,z) end + if (y < 0 or y == nil) then y = 0 end + gl.PushMatrix() + gl.DepthTest(true) + gl.Color(r,g,b,0.7) + gl.LineWidth(6) + gl.DrawGroundCircle(x, 1, z, 40, 21) + gl.Color(r2,g2,b2,0.7) + gl.LineWidth(2) + gl.DrawGroundCircle(x, 1, z, 40, 21) + gl.PopMatrix() + gl.PushMatrix() + gl.Translate(x, y, z) + gl.Rotate(-90, 1, 0, 0) + gl.Translate(0,-40, 0) + gl.Text(metal, 0.0, 0.0, 40, "cno") + gl.PopMatrix() +end + +function ViewMetalState:DrawWorld() + for ID, params in pairs(SB.model.mexManager:getAllMexes()) do + local x = params.x + local z = params.z + local metal = "+" .. string.format("%.2f", params.metal) + DrawSpot(x, z, metal) + if params.zmirror and params.xmirror then + x = Game.mapSizeX-x + z = Game.mapSizeZ-z + DrawSpot(x, z, metal, true) + elseif params.xmirror then + x = Game.mapSizeX-x + DrawSpot(x, z, metal, true) + elseif params.zmirror then + z = Game.mapSizeZ-z + DrawSpot(x, z, metal, true) + end + end +end + +function ViewMetalState:MouseRelease(...) + SB.stateManager:SetState(DefaultState()) +end \ No newline at end of file diff --git a/scen_edit/view/general/metal_spot_editor.lua b/scen_edit/view/general/metal_spot_editor.lua new file mode 100644 index 00000000..852411f0 --- /dev/null +++ b/scen_edit/view/general/metal_spot_editor.lua @@ -0,0 +1,294 @@ +SB.Include(Path.Join(SB.DIRS.SRC, 'view/editor.lua')) +MetalSpotEditor = Editor:extends{} +MetalSpotEditor:Register({ + name = "MetalSpotEditor", + tab = "Misc", + caption = "Metal-ZK", + tooltip = "Edit metal map (ZK)", + image = Path.Join(SB.DIRS.IMG, 'minerals.png'), + order = 3, +}) + +function MetalSpotEditor:init() + self:super("init") + self.btnAddMetal = TabbedPanelButton({ + x = 10, + y = 0, + tooltip = "Add Metal Spots by clicking on the map", + children = { + TabbedPanelImage({ file = Path.Join(SB.DIRS.IMG, 'metal-add.png') }), + TabbedPanelLabel({ caption = "Add" }), + }, + OnClick = { + function() + self.type = "add" + SB.stateManager:SetState(AddMetalState(self)) + end + }, + }) + self:AddField(GroupField({ + NumericField({ + name = "defaultmetal", + title = "Metal:", + tooltip = "Amount of Metal in the spot", + value = 0, + step = .01, + width = 100, + decimals = 2, + }), + BooleanField({ + name = "defaultxmirror", + title = "X-Mirror", + tooltip = "Mirror X-coordinate over Z-Axis", + width = 100, + value = false, + }), + BooleanField({ + name = "defaultzmirror", + title = "Z-Mirror", + tooltip = "Mirror Z-coordinate over X-Axis", + width = 100, + value = false, + }), + }, + {name = "defaultgroup"} + )) + self:AddControl("default" .. "index", { + Line:New { + x = 0, + y = 0, + width = 480, + }, + }) + self:AddDefaultKeybinding({ + self.btnAddMetal + }) + + local children = { + self.btnAddMetal, + } + + table.insert(children, + ScrollPanel:New { + x = 0, + y = "8%", + bottom = 30, + right = 0, + borderColor = {0,0,0,0}, + horizontalScrollbar = false, + children = { self.stackPanel }, + } + ) + + local mexManagerListener = MexManagerListenerWidget(self) + SB.model.mexManager:addListener(mexManagerListener) + self:Finalize(children) +end + +function MetalSpotEditor:__OnShow() + SB.stateManager:SetState(ViewMetalState(self)) +end + +function MetalSpotEditor:AddSpot(objectID, params) + self:AddControl("metalspot" .. objectID, { + Line:New { + x = 200, + width = self.VALUE_POS, + }, + Label:New { + caption = ("Metal Spot ID:" .. objectID), + }, + Label:New { + x = 325, + caption = ("Mirror?"), + }, + }) + self:AddField(GroupField({ + NumericField({ + name = "x" .. objectID, + title = "X:", + tooltip = "metal", + value = params.x, + minValue = 0, + maxValue = Game.mapSizeX, + step = 1, + width = 75, + decimals = 0, + }), + NumericField({ + name = "z" .. objectID, + title = "Z:", + tooltip = "Position (z)", + value = params.z, + minValue = 0, + maxValue = Game.mapSizeZ, + step = 1, + width = 75, + decimals = 0, + }), + NumericField({ + name = "metal" .. objectID, + title = "Metal:", + tooltip = "Amount of Metal in the spot", + value = params.metal, + step = .01, + width = 95, + decimals = 2, + }), + BooleanField({ + name = "xmirror" .. objectID, + title = "X", + tooltip = "Mirror X-coordinate", + width = 60, + value = params.xmirror, + }), + BooleanField({ + name = "zmirror" .. objectID, + title = "Z", + tooltip = "Mirror Z-coordinate", + width = 60, + value = params.zmirror, + }), + RemoveButtonField({ + name = "Remove" .. objectID, + --title = "\255\255\1\1(X)\255\255\255\255", + width = 50, + tooltip = "Remove Metal Spot", + value = false, + }), + }, + {name = "group"..objectID} + )) +end + +function MetalSpotEditor:RemoveSpot(objectID) + for field, _ in pairs(self.fields) do + local ID, _ = field:gsub('(%a+)', "") + ID = tonumber(ID) + if ID == objectID then + self:RemoveField(field) + end + end +end + +function MetalSpotEditor:OnEndChange(name) + self:SetField(name.value) +end + +function MetalSpotEditor:OnFieldChange(name, values) + if not name:find("default") then + if name:find("Remove") then + local objectID, _ = name:gsub('(%a+)', "") + SB.model.mexManager:removeMex(tonumber(objectID)) + self:RemoveSpot(tonumber(objectID)) + else + local key, _ = name:gsub("(%d+)", "") + local objectID, _ = name:gsub('(%a+)', "") + objectID = tonumber(objectID) + local partialObject = {} + partialObject[key] = values + SB.model.mexManager:setMex(objectID, partialObject) + end + end +end + +function MetalSpotEditor:Populate() + for field, _ in pairs(self.fields) do + self:RemoveField(field) + end + self:AddField(GroupField({ + NumericField({ + name = "defaultmetal", + title = "Metal:", + tooltip = "Amount of Metal in the spot", + value = 0, + step = .01, + width = 100, + decimals = 2, + }), + BooleanField({ + name = "defaultxmirror", + title = "X-Mirror", + tooltip = "Mirror X-coordinate over Z-Axis", + width = 100, + value = false, + }), + BooleanField({ + name = "defaultzmirror", + title = "Z-Mirror", + tooltip = "Mirror Z-coordinate over X-Axis", + width = 100, + value = false, + }), + }, + {name = "defaultgroup"} + )) + self:AddControl("default" .. "index", { + Line:New { + x = 0, + y = 0, + width = 480, + }, + }) + for ID, params in pairs(SB.model.mexManager:getAllMexes()) do + self:AddSpot(ID, params) + end +end + +function MetalSpotEditor:IsValidState(state) + return (state:is_A(AddMetalState)) +end + +function MetalSpotEditor:OnLeaveState(state) + if state:is_A(AddMetalState) then + for _, btn in pairs({self.btnAddMetal}) do + btn:SetPressedState(false) + end + end +end + +function MetalSpotEditor:OnEnterState(state) + if state:is_A(AddMetalState) then + for _, btn in pairs({self.btnAddMetal}) do + btn:SetPressedState(true) + end + end +end + +MexManagerListenerWidget = MexManagerListener:extends{} + +function MexManagerListenerWidget:init(mexEditor) + self.ev = mexEditor +end + +function MexManagerListenerWidget:onMexAdded(ID, mex) + SB.delay(function() + SB.delay(function() + SB.delay(function() + self.ev:AddSpot(ID, mex)end)end)end) + -- local visible_spots = Spring.GetFeaturesInCylinder(mex.x, mex.z,25) + -- if #visible_spots ~= 0 then return end + -- local cmd = AddObjectCommand(featureBridge.name, { + -- defName = "map_metal_spot1", + -- pos = { x = mex.x, y = Spring.GetGroundHeight(mex.x,mex.z) , z = mex.z }, + -- dir = { x = 1, y = 0, z = 1 }, + -- team = 2, + -- }) + -- SB.commandManager:execute(cmd) +end + +function MexManagerListenerWidget:onMexRemoved(ID, mex) + SB.delay(function() + SB.delay(function() + SB.delay(function() + self.ev:RemoveSpot(ID)end)end)end) + -- local visible_spots = Spring.GetFeaturesInCylinder(mex.x, mex.z,25) + -- for _, spot in pairs(visible_spots) do + -- local modelID = featureBridge.getObjectModelID(spot) + -- local cmd = RemoveObjectCommand("feature", modelID) + -- SB.commandManager:execute(cmd) + -- end +end + +function MexManagerListenerWidget:onMexChange(ID, mex) +end \ No newline at end of file From 31766d1263a3d82893dc75af869a437b6d7c9f36 Mon Sep 17 00:00:00 2001 From: Dave-tB <90851364+Dave-tB@users.noreply.github.com> Date: Sun, 5 Dec 2021 17:16:53 -0500 Subject: [PATCH 4/6] Added StartBox adding, viewing, removing and editing. nameShort field added to player window. mex, startbox added to model.lua. --- scen_edit/model/model.lua | 2 + scen_edit/model/startbox_manager.lua | 176 +++++++++++++ scen_edit/state/add_startbox_state.lua | 228 ++++++++++++++++ scen_edit/state/view_startbox_state.lua | 243 ++++++++++++++++++ scen_edit/view/general/player_window.lua | 9 +- .../view/general/startbox_team_window.lua | 39 +++ scen_edit/view/general/startboxes_window.lua | 187 ++++++++++++++ 7 files changed, 883 insertions(+), 1 deletion(-) create mode 100644 scen_edit/model/startbox_manager.lua create mode 100644 scen_edit/state/add_startbox_state.lua create mode 100644 scen_edit/state/view_startbox_state.lua create mode 100644 scen_edit/view/general/startbox_team_window.lua create mode 100644 scen_edit/view/general/startboxes_window.lua diff --git a/scen_edit/model/model.lua b/scen_edit/model/model.lua index f4ccc93f..f17fba53 100644 --- a/scen_edit/model/model.lua +++ b/scen_edit/model/model.lua @@ -4,9 +4,11 @@ SB.IncludeDir(Path.Join(SB.DIRS.SRC, 'model/object')) function Model:init() self.areaManager = AreaManager() + self.mexManager = MexManager() self.variableManager = VariableManager() self.triggerManager = TriggerManager() self.teamManager = TeamManager() + self.startboxManager = StartBoxManager() self.scenarioInfo = ScenarioInfo() self.terrainManager = TerrainManager() if Script.GetName() == "LuaUI" then diff --git a/scen_edit/model/startbox_manager.lua b/scen_edit/model/startbox_manager.lua new file mode 100644 index 00000000..a6ccf67e --- /dev/null +++ b/scen_edit/model/startbox_manager.lua @@ -0,0 +1,176 @@ +StartBoxManager = Observable:extends{} + +function StartBoxManager:init() + self:super('init') + self.boxIDCount = 0 + self.boxes = {} + self.team = {} +end + +local function smallbuild(tbl) + local matrix = {} + count = 1 + for i = 1, #tbl, 2 do + value = {tbl[i], tbl[i+1]} + matrix[count] = value + value = {} + count = count + 1 + end + return matrix +end + +local function build2d(bigtbl) + local value = {} + local matrixlist = {} + for index, tbl in pairs(bigtbl) do + local matrix = {} + count = 1 + for i = 1, #tbl, 2 do + value = {tbl[i], tbl[i+1]} + matrix[count] = value + value = {} + count = count + 1 + end + matrixlist[index] = matrix + end + return matrixlist +end + +local function unbuild(matrix) + local tbl = {} + for i, j in pairs (matrix) do + table.insert(tbl, matrix[i][1]) + table.insert(tbl, matrix[i][2]) + end + return tbl +end + +function StartBoxManager:addBox(box, boxID) + if boxID == nil then + boxID = self.boxIDCount + 1 + end + self.boxIDCount = boxID + self.boxes[boxID] = unbuild(box) + self:callListeners("onBoxAdded", boxID) + return boxID +end + +function StartBoxManager:addMirroredBox(boxID) + local sboxes = build2d(self.boxes) + local sbox = sboxes[boxID] + local areaNew = {} + for i, j in pairs(sbox) do + table.insert(areaNew, {Game.mapSizeX - sbox[i][1], Game.mapSizeZ - sbox[i][2]}) + end + local ID = self.boxIDCount + 1 + self.boxIDCount = ID + self.boxes[ID] = unbuild(areaNew) + self:callListeners("onBoxAdded", boxID) + return ID +end + +function StartBoxManager:getStartBoxIDCount(boxID) + if boxID == nil then + boxID = self.boxIDCount + 1 + end + self.boxIDCount = boxID + return self.boxIDCount +end + +function StartBoxManager:getBoxPos(boxID) + local sboxes = build2d(self.boxes) + local sbox = sboxes[boxID] + local x, z = 0, 0 + for i, _ in pairs(sbox) do + x = x + sbox[i][1] + z = z + sbox[i][2] + end + x, z = x / #sbox, z / #sbox + local y = Spring.GetGroundHeight(x, z) + return {x = x, y = y, z = z} +end + +function StartBoxManager:setBoxPos(boxID, value) + local x, z = self:getBoxPos(boxID).x, self:getBoxPos(boxID).z + local deltaX, deltaZ = value.x - x, value.z - z + local sbox = build2d(self.boxes)[boxID] + areaNew = {} + for i, j in pairs(sbox) do + table.insert(areaNew, {deltaX + sbox[i][1], deltaZ + sbox[i][2]}) + end + areaNew = unbuild(areaNew) + self.boxes[boxID] = areaNew +end + +function StartBoxManager:getAllStartBoxes() + local sboxes = build2d(self.boxes) + return sboxes +end + +function StartBoxManager:getAllRawStartBoxes() + return self.boxes +end + +function StartBoxManager:getAllStartBoxIDs() + local startboxes = {} + for boxID, box in pairs(self.boxes) do + table.insert(startboxes, boxID) + end + return startboxes +end + +function StartBoxManager:removeBox(boxID) + if self.boxes[boxID] ~= nil then + self.boxes[boxID] = nil + self:callListeners("onBoxRemoved", boxID) + end +end + +function StartBoxManager:setTeam(boxID, teamID) + if teamID then + self.team[boxID] = teamID + return teamID + end + self:callListeners("onBoxChange", boxID) +end + +function StartBoxManager:getTeam(boxID) + return self.team[boxID] +end + +function StartBoxManager:getBox(boxID) + return smallbuild(self.boxes[boxID]) +end + +local function DistSq(x1, z1, x2, z2) + return (x1 - x2)*(x1 - x2) + (z1 - z2)*(z1 - z2) +end + +function StartBoxManager:getBoxIn(x, z) + local selected, dragDiffX, dragDiffZ + for boxID, box in pairs(build2d(self.boxes)) do + local pos = self:getBoxPos(boxID) + if DistSq(pos.x, pos.z, x, z) < 2500 then + selected = boxID + dragDiffX = pos.x - x + dragDiffZ = pos.z - z + end + end + return selected, dragDiffX, dragDiffZ +end +------------------------------------------------ +-- Listener definition +------------------------------------------------ +StartBoxManagerListener = LCS.class.abstract{} + +function StartBoxManagerListener:onBoxAdded(boxID) +end + +function StartBoxManagerListener:onBoxRemoved(boxID) +end + +function StartBoxManagerListener:onBoxChange(boxID, box) +end +------------------------------------------------ +-- End listener definition +------------------------------------------------ \ No newline at end of file diff --git a/scen_edit/state/add_startbox_state.lua b/scen_edit/state/add_startbox_state.lua new file mode 100644 index 00000000..7566f297 --- /dev/null +++ b/scen_edit/state/add_startbox_state.lua @@ -0,0 +1,228 @@ +AddStartBoxState = AbstractState:extends{} + +function AddStartBoxState:init(editorView) + AbstractState.init(self, editorView) + self.params = {} + self.lines = {} + self.params.box = {} + self.ev = editorView +end + +function AddStartBoxState:enterState() + AbstractState.enterState(self) + + SB.SetGlobalRenderingFunction(function(...) + self:__DrawInfo(...) + end) +end + +function AddStartBoxState:leaveState() + AbstractState.leaveState(self) + + SB.SetGlobalRenderingFunction(nil) +end + +local function DistSq(x1, z1, x2, z2) + return (x1 - x2)*(x1 - x2) + (z1 - z2)*(z1 - z2) +end + +local function GetBoundedLineIntersection(line1, line2) + local x1, y1, x2, y2 = line1[1][1], line1[1][2], line1[2][1], line1[2][2] + local x3, y3, x4, y4 = line2[1][1], line2[1][2], line2[2][1], line2[2][2] + + local denominator = ((x1 - x2)*(y3 - y4) - (y1 - y2)*(x3 - x4)) + if denominator == 0 then + return false + end + local first = ((x1 - x3)*(y3 - y4) - (y1 - y3)*(x3 - x4))/denominator + local second = -1*((x1 - x2)*(y1 - y3) - (y1 - y2)*(x1 - x3))/denominator + + if first < 0 or first > 1 or (second < 0 or second > 1) then + return false + end + + local px = x1 + first*(x2 - x1) + local py = y1 + first*(y2 - y1) + + return {px, py} +end + +local function AssessVertexValidity(lineList, activeLine) + if #lineList == 0 then return true end + for _, line in pairs(lineList) do + if next(lineList,_) == nil then + for _, j in pairs(line) do + if DistSq(activeLine[1][1], activeLine[1][2], j[1], j[2]) < 1 then + return false + else + return true + end + end + end + if GetBoundedLineIntersection(line, activeLine) then + return false + end + end + return true +end + +function AddStartBoxState:MousePress(mx, my, button) + if button == 1 then + self.params.box = self.params.box or {} + local result, coords = Spring.TraceScreenRay(mx, my, true) + if result == "ground" then + self.params.x, _, self.params.z = math.floor(coords[1]), coords[2], math.floor(coords[3]) + if #self.params.box > 2 then + if DistSq(self.params.box[1][1], self.params.box[1][2], coords[1], coords[3]) < 400 then + local ID = SB.model.startboxManager:addBox(self.params.box) + SB.stateManager:SetState(ViewStartBoxState(self.ev)) + return true + end + end + if #self.params.box ~= 0 then + local line = {self.params.box[#self.params.box],{coords[1], coords[3]}} + if #self.lines ~= 0 then + if not AssessVertexValidity(self.lines, line) then + return false + end + end + table.insert(self.lines, line) + end + table.insert(self.params.box, {coords[1], coords[3]}) + end + elseif button == 3 then + SB.stateManager:SetState(ViewStartBoxState(self.ev)) + end +end + +function AddStartBoxState:MouseMove(...) +end + +function AddStartBoxState:MouseRelease(...) +end + +local function DrawFirstPoint(x,z) + gl.PushMatrix() + gl.DepthTest(true) + gl.Color(.8, .8, .8, .9) + gl.LineWidth(6) + gl.DrawGroundCircle(x, 1, z, 10, 21) + gl.Color(1, 1, 1, 1) + gl.PopMatrix() +end + +local function MakeTriLine(line) + local x, z, x0, z0 = line[1][1], line[1][2], line[2][1], line[2][2] + local dist = math.sqrt(DistSq(x, z, x0, z0)) + local xslope, zslope = (x0 - x), (z0 - z) + local x1, z1 = x + (zslope*3/dist), z - (xslope*3/dist) + local x2, z2 = x - (zslope*3/dist), z + (xslope*3/dist) + local x3, z3 = x0 + (zslope*3/dist), z0 - (xslope*3/dist) + local x4, z4 = x0 - (zslope*3/dist), z0 + (xslope*3/dist) + return {{x1, z1, x2, z2, x3, z3}, {x2, z2, x3, z3, x4, z4}} +end + +local function DrawLine(line, alpha, isNotValid) + local x1, z1, x2, z2 = line[1][1], line[1][2], line[2][1], line[2][2] + local r, g = 0, 1 + if isNotValid then + r, g = 1, 0 + end + if #line == 0 then return end + local triline = MakeTriLine(line) + for _, triangle in pairs(triline) do + gl.PushMatrix() + gl.Color(r, g, 0, alpha) + gl.Utilities.DrawGroundTriangle(triangle) + gl.PopMatrix() + end +end + +local function DrawSpot(x, z, metal, mirror) + local y = 0 + local r, g, b = 0, 0, 0 + + if mirror then r, g, b = 1, 1, 1 end + + local r2, g2, b2 = (r + 1) % 2, (g + 1) % 2, (b + 1) % 2 + + if x then y = Spring.GetGroundHeight(x,z) end + if (y < 0 or y == nil) then y = 0 end + gl.PushMatrix() + gl.DepthTest(true) + gl.Color(r,g,b,0.7) + gl.LineWidth(6) + gl.DrawGroundCircle(x, 1, z, 40, 21) + gl.Color(r2,g2,b2,0.7) + gl.LineWidth(2) + gl.DrawGroundCircle(x, 1, z, 40, 21) + gl.PopMatrix() + gl.PushMatrix() + gl.Translate(x, y, z) + gl.Rotate(-90, 1, 0, 0) + gl.Translate(0,-40, 0) + gl.Text(metal, 0.0, 0.0, 40, "cno") + gl.PopMatrix() +end + +function AddStartBoxState:DrawWorld() + for ID, params in pairs(SB.model.mexManager:getAllMexes()) do + local x = params.x + local z = params.z + local metal = "+" .. string.format("%.2f", params.metal) + DrawSpot(x, z, metal) + if params.zmirror and params.xmirror then + x = Game.mapSizeX-x + z = Game.mapSizeZ-z + DrawSpot(x, z, metal, true) + elseif params.xmirror then + x = Game.mapSizeX-x + DrawSpot(x, z, metal, true) + elseif params.zmirror then + z = Game.mapSizeZ-z + DrawSpot(x, z, metal, true) + end + end + if (#self.params.box == 0) then return end + DrawFirstPoint(self.params.box[1][1], self.params.box[1][2]) + local mx,my = Spring.GetMouseState() + local result, coords = Spring.TraceScreenRay(mx, my, true) + local activeLine ={{coords[1], coords[3]}, {self.params.box[#self.params.box][1], self.params.box[#self.params.box][2]}} + local isNotValid = not AssessVertexValidity(self.lines, activeLine) + DrawLine(activeLine, 0.2, isNotValid) + if #self.lines ~= 0 then + for _, line in pairs(self.lines) do + DrawLine(line, 0.4) + end + end +end + +function AddStartBoxState:__GetInfoText() + return "Add StartBox" +end + +local _displayColor = {1.0, 0.7, 0.1, 0.8} +function AddStartBoxState:__DrawInfo() + if not self.__displayFont then + self.__displayFont = Chili.Font:New { + size = 12, + color = _displayColor, + outline = true, + } + end + + local mx, my, _, _, _, outsideSpring = Spring.GetMouseState() + -- Don't draw if outside Spring + if outsideSpring then + return true + end + + local _, vsy = Spring.GetViewGeometry() + + local x = mx + local y = vsy - my - 30 + self.__displayFont:Draw(self:__GetInfoText(), x, y) + + -- return true to keep redrawing + return true +end \ No newline at end of file diff --git a/scen_edit/state/view_startbox_state.lua b/scen_edit/state/view_startbox_state.lua new file mode 100644 index 00000000..e7ebaeb2 --- /dev/null +++ b/scen_edit/state/view_startbox_state.lua @@ -0,0 +1,243 @@ +ViewStartBoxState = AbstractState:extends{} + +local box = {} +local dragID = nil + +function ViewStartBoxState:init(editorView) + AbstractState.init(self, editorView) + self.params = {} + self.params.box = {} + self.ev = editorView +end + +function ViewStartBoxState:enterState() + AbstractState.enterState(self) +end + +function ViewStartBoxState:leaveState() + AbstractState.leaveState(self) + + SB.SetGlobalRenderingFunction(nil) +end + +function ViewStartBoxState:MousePress(mx, my, button) + if button == 1 then + local result, coords = Spring.TraceScreenRay(mx, my, true) + local x, z = coords[1], coords[3] + if dragID then + local pos = SB.model.startboxManager:setBoxPos(dragID, {x = coords[1], z = coords[3]}) + SB.SetMouseCursor() + dragID = nil + else + dragID = SB.model.startboxManager:getBoxIn(x, z) + end + if dragID then SB.SetMouseCursor ("drag") end + elseif button == 3 then + local result, coords = Spring.TraceScreenRay(mx, my, true) + local x, z = coords[1], coords[3] + local ID = SB.model.startboxManager:getBoxIn(x, z) + if ID then + SB.model.startboxManager:removeBox(ID) + self.ev:Populate() + end + end +end + +function ViewStartBoxState:MouseMove(mx, my, button) +end + +function ViewStartBoxState:MouseRelease(mx, my, button) + if button == 1 then + local result, coords = Spring.TraceScreenRay(mx, my, true) + if dragID then + local pos = SB.model.startboxManager:setBoxPos(dragID, {x = coords[1], z = coords[3]}) + dragID = nil + end + end +end + +local function DrawSpot(x, z, metal, mirror) + local y = 0 + local r, g, b = 0, 0, 0 + + if mirror then r, g, b = 1, 1, 1 end + + local r2, g2, b2 = (r + 1) % 2, (g + 1) % 2, (b + 1) % 2 + + if x then y = Spring.GetGroundHeight(x,z) end + if (y < 0 or y == nil) then y = 0 end + gl.PushMatrix() + gl.DepthTest(true) + gl.Color(r,g,b,0.7) + gl.LineWidth(6) + gl.DrawGroundCircle(x, 1, z, 40, 21) + gl.Color(r2,g2,b2,0.7) + gl.LineWidth(2) + gl.DrawGroundCircle(x, 1, z, 40, 21) + gl.PopMatrix() + gl.PushMatrix() + gl.Translate(x, y, z) + gl.Rotate(-90, 1, 0, 0) + gl.Translate(0,-40, 0) + gl.Text(metal, 0.0, 0.0, 40, "cno") + gl.PopMatrix() +end + +local function cross_product(px, pz, ax, az, bx, bz) + return ((px - bx)*(az - bz) - (ax - bx)*(pz - bz)) +end + +local function triangulate(polies) + local triangles = {} + for ID, box in pairs(polies) do + local polygon = box + + -- find out clockwisdom + polygon[#polygon+1] = polygon[1] + local clockwise = 0 + for i = 2, #polygon do + clockwise = clockwise + (polygon[i-1][1] * polygon[i][2]) - (polygon[i-1][2] * polygon[i][1]) + end + polygon[#polygon] = nil + clockwise = (clockwise < 0) + + -- the van gogh concave polygon triangulation algorithm: cuts off ears + -- is pretty shitty at O(V^3) but was easy to code and it's typically only done once anyway + while (#polygon > 2) do + + -- get a candidate ear + local triangle + local c0, c1, c2 = 0 + local candidate_ok = false + while not candidate_ok do + + c0 = c0 + 1 + c1, c2 = c0+1, c0+2 + if c1 > #polygon then c1 = c1 - #polygon end + if c2 > #polygon then c2 = c2 - #polygon end + triangle = { + polygon[c0][1], polygon[c0][2], + polygon[c1][1], polygon[c1][2], + polygon[c2][1], polygon[c2][2], + } + + -- make sure the ear is of proper rotation but then make it counter-clockwise + local dir = cross_product(triangle[5], triangle[6], triangle[1], triangle[2], triangle[3], triangle[4]) + if ((dir < 0) == clockwise) then + if dir > 0 then + local temp = triangle[5] + triangle[5] = triangle[3] + triangle[3] = temp + temp = triangle[6] + triangle[6] = triangle[4] + triangle[4] = temp + end + + -- check if no point lies inside the triangle + candidate_ok = true + for i = 1, #polygon do + if (i ~= c0 and i ~= c1 and i ~= c2) then + local current_pt = polygon[i] + if (cross_product(current_pt[1], current_pt[2], triangle[1], triangle[2], triangle[3], triangle[4]) < 0) + and (cross_product(current_pt[1], current_pt[2], triangle[3], triangle[4], triangle[5], triangle[6]) < 0) + and (cross_product(current_pt[1], current_pt[2], triangle[5], triangle[6], triangle[1], triangle[2]) < 0) + then + candidate_ok = false + end + end + end + end + end + + -- cut off ear + triangles[#triangles+1] = triangle + table.remove(polygon, c1) + end + end + return triangles +end + +local function DrawSpot(x, z, metal, mirror) + local y = 0 + local r, g, b = 0, 0, 0 + + if mirror then r, g, b = 1, 1, 1 end + + local r2, g2, b2 = (r + 1) % 2, (g + 1) % 2, (b + 1) % 2 + + if x then y = Spring.GetGroundHeight(x,z) end + if (y < 0 or y == nil) then y = 0 end + gl.PushMatrix() + gl.DepthTest(true) + gl.Color(r,g,b,0.7) + gl.LineWidth(6) + gl.DrawGroundCircle(x, 1, z, 40, 21) + gl.Color(r2,g2,b2,0.7) + gl.LineWidth(2) + gl.DrawGroundCircle(x, 1, z, 40, 21) + gl.PopMatrix() + gl.PushMatrix() + gl.Translate(x, y, z) + gl.Rotate(-90, 1, 0, 0) + gl.Translate(0,-40, 0) + gl.Text(metal, 0.0, 0.0, 40, "cno") + gl.PopMatrix() +end + +function DrawText(boxID) + local center = SB.model.startboxManager:getBoxPos(boxID) + local y = Spring.GetGroundHeight(center.x, center.z) + gl.PushMatrix() + gl.Rotate(90, 1, 0, 0) + local fontSize = 58 + local txt = tostring(boxID) + local w = gl.GetTextWidth(txt) * fontSize + local h = gl.GetTextHeight(txt) * fontSize + gl.Translate(center.x-15, center.z+15, -y) + gl.Color(1, 1, 1, 1) + gl.Rotate(180, 0, 0, 1) + gl.Scale(-1, 1, 1) + gl.Text(txt, 0, 0, fontSize) + gl.PopMatrix() + gl.PushMatrix() + local x1, z1, x2, z2 = center.x - 40, center.z - 40, center.x + 40, center.z + 40 + local bt = 4 + gl.Color(1, 1, 1, 0.7) + gl.Utilities.DrawGroundRectangle(x1-bt, z1-bt, x1, z2+bt) + gl.Utilities.DrawGroundRectangle(x2, z1-bt, x2+bt, z2+bt) + gl.Utilities.DrawGroundRectangle(x1, z1-bt, x2, z1) + gl.Utilities.DrawGroundRectangle(x1, z2, x2, z2+bt) + gl.PopMatrix() +end + +function ViewStartBoxState:DrawWorld() + local startboxes = SB.model.startboxManager:getAllStartBoxes() + local triboxes = triangulate(startboxes) + for ID, _ in pairs(startboxes) do + DrawText(ID) + end + for _, box in pairs(triboxes) do + gl.PushMatrix() + gl.DepthTest(true) + gl.Color(0,1,0,0.2) + gl.Utilities.DrawGroundTriangle(box) + gl.PopMatrix() + end + for ID, params in pairs(SB.model.mexManager:getAllMexes()) do + local x = params.x + local z = params.z + local metal = "+" .. string.format("%.2f", params.metal) + DrawSpot(x, z, metal) + if params.zmirror and params.xmirror then + x = Game.mapSizeX-x + z = Game.mapSizeZ-z + DrawSpot(x, z, metal, true) + elseif params.xmirror then + x = Game.mapSizeX-x + DrawSpot(x, z, metal, true) + elseif params.zmirror then + z = Game.mapSizeZ-z + DrawSpot(x, z, metal, true) + end + end +end \ No newline at end of file diff --git a/scen_edit/view/general/player_window.lua b/scen_edit/view/general/player_window.lua index 1ca32eb7..7a906ddf 100644 --- a/scen_edit/view/general/player_window.lua +++ b/scen_edit/view/general/player_window.lua @@ -13,7 +13,13 @@ function PlayerWindow:init(team) tooltip = "Team name", value = team.name, })) - + + self:AddField(StringField({ + name = "short", + title = "Name(Short):", + tooltip = "Team name", + value = team.short or "", + })) self:AddField(BooleanField({ name = "ai", title = "AI:", @@ -123,6 +129,7 @@ function PlayerWindow:init(team) table.insert(self.window.OnDispose, function() local newTeam = Table.DeepCopy(team) newTeam.name = self.fields["name"].value + newTeam.short = self.fields["short"].value local clbColor = self.fields["color"].value newTeam.color.r = clbColor[1] newTeam.color.g = clbColor[2] diff --git a/scen_edit/view/general/startbox_team_window.lua b/scen_edit/view/general/startbox_team_window.lua new file mode 100644 index 00000000..cce15ef2 --- /dev/null +++ b/scen_edit/view/general/startbox_team_window.lua @@ -0,0 +1,39 @@ +SB.Include(Path.Join(SB.DIRS.SRC, 'view/editor.lua')) + +StartBoxTeamWindow = Editor:extends{} + +function StartBoxTeamWindow:init(boxID, editorView) + self:super("init") + + self.ID = boxID + self.ev = editorView + self:AddField(TeamField({ + name = self.ID, + title = "Team: ", + width = 300, + })) + + local children = {} + table.insert(children, + ScrollPanel:New { + x = 0, + y = 0, + bottom = 0, + right = 0, + borderColor = {0,0,0,0}, + horizontalScrollbar = false, + children = { self.stackPanel }, + } + ) + + self:Finalize(children, { + notMainWindow = true, + buttons = { "close" }, + }) + + table.insert(self.window.OnDispose, function() + local newTeam = self.fields[self.ID].value + local teamID = SB.model.startboxManager:setTeam(self.ID, newTeam) + self.ev:Populate() + end) +end \ No newline at end of file diff --git a/scen_edit/view/general/startboxes_window.lua b/scen_edit/view/general/startboxes_window.lua new file mode 100644 index 00000000..cef7a7a1 --- /dev/null +++ b/scen_edit/view/general/startboxes_window.lua @@ -0,0 +1,187 @@ +SB.Include(Path.Join(SB.DIRS.SRC, 'view/editor.lua')) + +StartBoxWindow = Editor:extends{} +StartBoxWindow:Register({ + name = "startboxWindow", + tab = "Misc", + caption = "StartBox", + tooltip = "Edit areas", + image = Path.Join(SB.DIRS.IMG, 'bolivia.png'), + order = 2, +}) + +function StartBoxWindow:init() + self:super("init") + self.btnAddArea = TabbedPanelButton({ + x = 0, + y = 0, + tooltip = "Add area", + children = { + TabbedPanelImage({ file = Path.Join(SB.DIRS.IMG, 'area-add.png') }), + TabbedPanelLabel({ caption = "Add" }), + }, + OnClick = { + function() + self.type = "add" + SB.stateManager:SetState(AddStartBoxState(self)) + end + }, + }) + self:AddDefaultKeybinding({ + self.btnAddArea + }) + + self.areasPanel = StackPanel:New { + itemMargin = {0, 0, 0, 0}, + width = "100%", + autosize = true, + resizeItems = false, + } + local children = { + ScrollPanel:New { + x = 0, + y = 80, + bottom = 30, + right = 0, + borderColor = {0,0,0,0}, + horizontalScrollbar = false, + children = { + self.areasPanel + }, + }, + self.btnAddArea, + } + + self:Populate() + local startboxManagerListener = StartBoxManagerListenerWidget(self) + SB.model.startboxManager:addListener(startboxManagerListener) + self:Finalize(children) +end + +function StartBoxWindow:__OnShow() + SB.stateManager:SetState(ViewStartBoxState(self)) +end + +function StartBoxWindow:Populate() + SB.stateManager:SetState(ViewStartBoxState(self)) + self.areasPanel:ClearChildren() + self:AddField(TeamField({ + name = "team", + title = "Team: ", + width = 300, + })) + local areas = SB.model.startboxManager:getAllRawStartBoxes() + for ID, _ in pairs(areas) do + local teamID = SB.model.startboxManager:getTeam(ID) + if teamID then + local team = SB.model.teamManager:getTeam(teamID) + local fontColor = SB.glToFontColor(team.color) + teamstr = fontColor .. team.name .. "\b" + else + teamstr = "Select Team" + end + local areaStackPanel = MakeComponentPanel(self.areasPanel) + areaStackPanel.areaID = ID + local lblArea = Label:New { + caption = "Area ID: " .. tostring(ID), + right = SB.conf.B_HEIGHT + 10, + x = 1, + height = SB.conf.B_HEIGHT, + _toggle = nil, + parent = areaStackPanel, + } + local btnEditTeam = Button:New { + caption = teamstr, + right = SB.conf.B_HEIGHT + 100, + width = 160, + height = SB.conf.B_HEIGHT, + parent = areaStackPanel, + padding = {0, 0, 0, 0}, + OnClick = { + function() + local startboxteamWindow = StartBoxTeamWindow(ID, self) + startboxteamWindow.window.x = self.window.x + self.window.width + startboxteamWindow.window.y = self.window.y + end + }, + } + local btnMirrorBox = Button:New { + caption = "Mirror", + right = SB.conf.B_HEIGHT + 40, + width = 60, + height = SB.conf.B_HEIGHT, + parent = areaStackPanel, + padding = {0, 0, 0, 0}, + OnClick = { + function() + SB.model.startboxManager:addMirroredBox(ID) + end + }, + } + local btnRemoveArea = Button:New { + caption = "", + right = 0, + width = SB.conf.B_HEIGHT, + height = SB.conf.B_HEIGHT, + parent = areaStackPanel, + padding = {2, 2, 2, 2}, + tooltip = "Remove area", + classname = "negative_button", + children = { + Image:New { + file = Path.Join(SB.DIRS.IMG, 'cancel.png'), + height = "100%", + width = "100%", + }, + }, + OnClick = { + function() + SB.model.startboxManager:removeBox(ID) + end + }, + } + end +end + +function StartBoxWindow:IsValidState(state) + return state:is_A(AddStartBoxState) +end + +function StartBoxWindow:OnLeaveState(state) + if state:is_A(AddStartBoxState) then + for _, btn in pairs({self.btnAddArea}) do + btn:SetPressedState(false) + end + end +end + +function StartBoxWindow:OnEnterState(state) + if state:is_A(AddStartBoxState) then + for _, btn in pairs({self.btnAddArea}) do + btn:SetPressedState(true) + end + end +end + +StartBoxManagerListenerWidget = StartBoxManagerListener:extends{} + +function StartBoxManagerListenerWidget:init(areaWindow) + self.window = areaWindow +end + +function StartBoxManagerListenerWidget:onBoxAdded(areaID) + SB.delay(function() + SB.delay(function() + SB.delay(function() + self.window:Populate()end)end)end) +end + +function StartBoxManagerListenerWidget:onBoxRemoved(areaID) + SB.delay(function() + SB.delay(function() + SB.delay(function() + self.window:Populate()end)end)end) +end + +function StartBoxManagerListenerWidget:onBoxChange(areaID, area) +end From 94cd6b234692998bfcf584e7dd05e88588322dcb Mon Sep 17 00:00:00 2001 From: Dave-tB <90851364+Dave-tB@users.noreply.github.com> Date: Sun, 5 Dec 2021 17:28:43 -0500 Subject: [PATCH 5/6] Loading,Saving,Importing* and Exporting** of map_metal_spots and map_startboxes lua files. Also added an option to only export heightmap. *Done through a button the info tab in misc **done through export menu options, did not yet add to export spring archive option. --- .../export_metal_spot_config_command.lua | 37 +++++++++++ .../export_startbox_config_command.lua | 51 +++++++++++++++ scen_edit/command/load_zkconfig_command.lua | 57 +++++++++++++++++ .../project/load_project_command_widget.lua | 5 ++ scen_edit/command/save_command.lua | 24 ++++++- scen_edit/model/project.lua | 1 + scen_edit/view/actions/export_action.lua | 63 +++++++++++++++++-- scen_edit/view/general/scenario_info_view.lua | 14 ++++- 8 files changed, 244 insertions(+), 8 deletions(-) create mode 100644 scen_edit/command/export_metal_spot_config_command.lua create mode 100644 scen_edit/command/export_startbox_config_command.lua create mode 100644 scen_edit/command/load_zkconfig_command.lua diff --git a/scen_edit/command/export_metal_spot_config_command.lua b/scen_edit/command/export_metal_spot_config_command.lua new file mode 100644 index 00000000..ce56b444 --- /dev/null +++ b/scen_edit/command/export_metal_spot_config_command.lua @@ -0,0 +1,37 @@ +ExportMetalSpotConfigCommand = Command:extends{} +ExportMetalSpotConfigCommand.className = "ExportMetalSpotConfigCommand" + +function ExportMetalSpotConfigCommand:init(path) + self.path = path + if Path.GetExt(self.path) ~= ".lua" then + self.path = self.path .. ".lua" + end +end + +function ExportMetalSpotConfigCommand:GetMetalSpots() + local tbl = {} + local spots = SB.model.mexManager:getAllMexes() + for mexID, params in pairs(spots) do + local x, z, metal = params.x, params.z, params.metal + table.insert(tbl, {x = x, z = z, metal = metal}) + if params.xmirror and params.zmirror then + x, z = Game.mapSizeX-x, Game.mapSizeZ-z + table.insert(tbl, {x = x, z = z, metal = metal}) + elseif params.xmirror then + x = Game.mapSizeX-x + table.insert(tbl, {x = x, z = z, metal = metal}) + elseif params.zmirror then + z = Game.mapSizeZ-z + table.insert(tbl, {x = x, z = z, metal = metal}) + end + end + return tbl +end + +function ExportMetalSpotConfigCommand:execute() + local file = assert(io.open(self.path, "w")) + file:write("local allspots = ") + file:write(table.show(self:GetMetalSpots()):sub(#"return ")) + file:write("\nreturn {spots = allspots}") + file:close() +end diff --git a/scen_edit/command/export_startbox_config_command.lua b/scen_edit/command/export_startbox_config_command.lua new file mode 100644 index 00000000..1167d7b5 --- /dev/null +++ b/scen_edit/command/export_startbox_config_command.lua @@ -0,0 +1,51 @@ +ExportStartBoxConfigCommand = Command:extends{} +ExportStartBoxConfigCommand.className = "ExportStartBoxConfigCommand" + +function ExportStartBoxConfigCommand:init(path) + self.path = path + if Path.GetExt(self.path) ~= ".lua" then + self.path = self.path .. ".lua" + end +end + +function ExportStartBoxConfigCommand:GetStartBoxes() + local teamList = {} + -- teamList.boxes, teamList.x, teamList.z, teamList.name, teamList.short = {}, {}, {}, {}, {} + local boxes = SB.model.startboxManager:getAllStartBoxes() + for boxID, _ in pairs(boxes) do + local teamID = SB.model.startboxManager:getTeam(boxID) + local team = SB.model.teamManager:getTeam(teamID) + local maybePos = SB.model.startboxManager:getBoxPos(boxID) + if team.startPos then + maybePos.x, maybePos.z = team.startPos.x, team.startPos.z + end + if not team.short then + team.short = team.name + end + if not teamList[teamID] then + teamList[teamID] = {name = team.name, short = team.short, x = maybePos.x, z = maybePos.z, boxes = {}} + end + table.insert(teamList[teamID].boxes, boxID) + end + return teamList +end + +function ExportStartBoxConfigCommand:execute() + local file = assert(io.open(self.path, "w")) + local teamList = self:GetStartBoxes() + file:write("local allboxes = {\n") + for ID, team in pairs(teamList) do + file:write("\t["..ID.."] = {\n\t\tstartpoints = {\n\t\t\t {") + file:write(teamList[ID].x..","..teamList[ID].z.."}\n\t\t},\n\t\t") + file:write("boxes = {\n\t\t\t") + for _, boxID in pairs(teamList[ID].boxes) do + local tbl = SB.model.startboxManager:getBox(boxID) + file:write(table.show(tbl):sub(#"return ")..",") + end + file:write("\n\t\t},\n\t\tnameLong = \""..teamList[ID].name.."\",") + file:write("\n\t\tnameShort = \""..teamList[ID].short.."\",\n\t},\n") + end + file:write("\n}\n\n") + file:write("return allboxes") + file:close() +end \ No newline at end of file diff --git a/scen_edit/command/load_zkconfig_command.lua b/scen_edit/command/load_zkconfig_command.lua new file mode 100644 index 00000000..9c8914d0 --- /dev/null +++ b/scen_edit/command/load_zkconfig_command.lua @@ -0,0 +1,57 @@ +LoadZKMapConfigCommand = Command:extends{} +LoadZKMapConfigCommand.className = "LoadZKMapConfigCommand" + +function LoadZKMapConfigCommand:init(ZKmapconfig) + -- Since the introduction of the data packing/unpacking, is much more + -- efficient passing tables than strings + if ZKmapconfig then + self.mapConfig = loadstring(ZKmapconfig)() + else + self.mapConfig = {} + local boxID = 1 + local mapsideBoxes = "mapconfig/map_startboxes.lua" + local mapsideMexes = "mapconfig/map_metal_layout.lua" + if VFS.FileExists (mapsideBoxes) then + self.mapConfig.teamList, self.mapConfig.boxes = {}, {} + local boxConfig = VFS.Include(mapsideBoxes) + for ID, boxdata in pairs(boxConfig) do + local color = { r=math.random(), g=math.random(), b=math.random(), a=1} + self.mapConfig.teamList[ID] = {id = ID, allyTeam = ID, color = color, name = boxdata.nameLong, short = (boxdata.nameShort or boxdata.nameLong), x = boxdata.startpoints[1][1], z = boxdata.startpoints[1][2], boxes = {}} + for _, box in pairs(boxdata.boxes) do + table.insert(self.mapConfig.teamList[ID].boxes, boxID) + table.insert(self.mapConfig.boxes, box) + boxID = boxID + 1 + end + end + end + if VFS.FileExists (mapsideMexes) then + self.mapConfig.mexes = VFS.Include(mapsideMexes).spots + end + end +end + +function LoadZKMapConfigCommand:execute() + --assert(self.mapConfig) + local mexes = self.mapConfig.mexes or {} + local boxes = self.mapConfig.boxes or {} + local teamList = self.mapConfig.teamList or {} + SB.delay(function() + SB.delay(function() + for mexID, mex in pairs(mexes) do + SB.model.mexManager:addMex(mex) + end + for teamID, team in pairs(teamList) do + for _, boxID in pairs(team.boxes) do + local newBoxID = SB.model.startboxManager:addBox(boxes[boxID]) + SB.model.startboxManager:setTeam(newBoxID, teamID) + end + if not SB.model.teamManager:getTeam(teamID) then + local cmd = AddTeamCommand(team.name, team.color, team.allyTeam, Spring.GetSideData(team.allyTeam)) + SB.commandManager:execute(cmd) + end + local cmd = UpdateTeamCommand(team) + SB.commandManager:execute(cmd) + end + end) + end) +end \ No newline at end of file diff --git a/scen_edit/command/project/load_project_command_widget.lua b/scen_edit/command/project/load_project_command_widget.lua index d8dc3d64..59c0821e 100644 --- a/scen_edit/command/project/load_project_command_widget.lua +++ b/scen_edit/command/project/load_project_command_widget.lua @@ -54,6 +54,11 @@ function LoadProjectCommandWidget:_LoadUnsynced() table.insert(cmds, LoadGUIStateCommand(VFS.LoadFile(file, VFS.RAW))) end + local file = Path.Join(SB.project.path, Project.ZKCONFIG_FILE) + if VFS.FileExists(file, VFS.RAW) then + table.insert(cmds, LoadZKMapConfigCommand(VFS.LoadFile(file, VFS.RAW))) + end + local cmd = CompoundCommand(cmds) SB.commandManager:execute(cmd, true) end \ No newline at end of file diff --git a/scen_edit/command/save_command.lua b/scen_edit/command/save_command.lua index 43c0ac7d..53832edd 100644 --- a/scen_edit/command/save_command.lua +++ b/scen_edit/command/save_command.lua @@ -72,6 +72,23 @@ local function GUIStateSave(path) table.save(guiState, path) end +local function ZKconfigSave(path) + local tbl = {} + local teamList = {} + local boxes = SB.model.startboxManager:getAllStartBoxes() + for boxID, _ in pairs(boxes) do + local teamID = SB.model.startboxManager:getTeam(boxID) + local team = SB.model.teamManager:getTeam(teamID) + if not teamList[teamID] then + teamList[teamID] = {name = team.name, short = team.short, x = team.x or nil, z = team.z or nil, boxes = {}} + end + table.insert(teamList[teamID].boxes, boxID) + end + local mexes = SB.model.mexManager:getAllMexes() + tbl.mexes, tbl.boxes, tbl.teamList = mexes, boxes, teamList + table.save(tbl, path) +end + function SaveCommand:execute() local projectDir = self.path @@ -111,7 +128,12 @@ function SaveCommand:execute() end, function(elapsed) Log.Notice(("[%.4fs] Saved GUI state"):format(elapsed)) end) - + + Time.MeasureTime(function() + ZKconfigSave(Path.Join(projectDir, Project.ZKCONFIG_FILE)) + end, function(elapsed) + Log.Notice(("[%.4fs] Saved GUI state"):format(elapsed)) + end) -- Hide the console (FIXME: game agnostic way) -- Spring.SendCommands("console 0") diff --git a/scen_edit/model/project.lua b/scen_edit/model/project.lua index d997f338..a364ae87 100644 --- a/scen_edit/model/project.lua +++ b/scen_edit/model/project.lua @@ -21,6 +21,7 @@ Project.METAL_FILE = Path.Join(Project.FOLDER_PREFIX, "metal.data") Project.SCRIPT_FILE = Path.Join(Project.FOLDER_PREFIX, "script.txt") Project.GUI_FILE = Path.Join(Project.FOLDER_PREFIX, "gui.lua") +Project.ZKCONFIG_FILE = Path.Join(Project.FOLDER_PREFIX, "zkconfig.lua") Project.SCREENSHOT_FILE = Path.Join(Project.FOLDER_PREFIX, "screenshot.jpg") Project.TEXTURES_FOLDER = Path.Join(Project.FOLDER_PREFIX, "textures/") diff --git a/scen_edit/view/actions/export_action.lua b/scen_edit/view/actions/export_action.lua index db862ad1..f942aa30 100644 --- a/scen_edit/view/actions/export_action.lua +++ b/scen_edit/view/actions/export_action.lua @@ -17,18 +17,24 @@ ExportAction.EXPORT_SPRING_ARCHIVE = "Spring archive" ExportAction.EXPORT_MAP_TEXTURES = "Map textures" ExportAction.EXPORT_MAP_INFO = "Map info" ExportAction.EXPORT_S11N = "s11n object format" +ExportAction.EXPORT_HEIGHTMAP = "Heightmap only (fast)" +ExportAction.EXPORT_METALSPOT_CONFIG = "Export Metal Map Config (ZK)" +ExportAction.EXPORT_STARTBOX_CONFIG = "Export Start Box Config (ZK)" local fileTypes = { ExportAction.EXPORT_SPRING_ARCHIVE, ExportAction.EXPORT_MAP_TEXTURES, ExportAction.EXPORT_MAP_INFO, - ExportAction.EXPORT_S11N + ExportAction.EXPORT_S11N, + ExportAction.EXPORT_HEIGHTMAP, + ExportAction.EXPORT_METALSPOT_CONFIG, + ExportAction.EXPORT_STARTBOX_CONFIG } function ExportAction:canExecute() - if Spring.GetGameRulesParam("sb_gameMode") ~= "dev" then - Log.Warning("Cannot export while testing.") - return false - end + -- --if Spring.GetGameRulesParam("sb_gameMode") ~= "dev" then + -- Log.Warning("Cannot export while testing.") + -- return false + -- end if SB.project.path == nil then -- FIXME: this should probably be relaxed for most types of export SB.NotifyWarn("export_warn", "The project must be saved before exporting") @@ -94,6 +100,37 @@ function ExportAction:execute() end) end) return true + elseif fileType == ExportAction.EXPORT_HEIGHTMAP then + if isFile then + return false, "Please select a directory" + end + + if not self:CheckHasSaved() then + return false, "Project files missing. Save before exporting" + end + + local progressID = SB.GenerateNotificationID() + SB.ActionProgress(progressID, 0.1, "Exporting maps textures...") + SB.delay(function() + self:DoExportHeightMap(path, heightmapExtremes):next(function() + SB.ActionProgress(progressID, 1.0, "Exporting Heightmap: Finished") + end) + end) + return true + elseif fileType == ExportAction.EXPORT_METALSPOT_CONFIG then + if isDir then + return false, "Please select a file" + end + + Log.Notice("Exporting map metalspot config...") + exportCommand = ExportMetalSpotConfigCommand(path) + elseif fileType == ExportAction.EXPORT_STARTBOX_CONFIG then + if isDir then + return false, "Please select a file" + end + + Log.Notice("Exporting map startbox config...") + exportCommand = ExportStartBoxConfigCommand(path) elseif fileType == ExportAction.EXPORT_MAP_INFO then if isDir then return false, "Please select a file" @@ -275,6 +312,20 @@ function ExportAction:TryToExportMapTextures(path, heightmapExtremes) }) return false end - return ExportMapsCommand(path, heightmapExtremes):execute() end + +function ExportAction:DoExportHeightMap(path, heightmapExtremes) + -- At least 5x the necessary amount? Super arbitrary... + local wantedTexMemPoolSize = Game.mapSizeX / 1024 * Game.mapSizeZ / 1024 * 3 * 5 + local texMemPoolSize = Spring.GetConfigInt("TextureMemPoolSize", 0) + return ExportHeightmapCommand((path .. ".png"), heightmapExtremes):execute() +end + +function ExportAction:ExportMetalSpotConfig(path) + return ExportMetalSpotConfigCommand((path .. ".lua")):execute() +end + +function ExportAction:ExportStartBoxConfig(path) + return ExportStartBoxConfigCommand((path .. ".lua")):execute() +end \ No newline at end of file diff --git a/scen_edit/view/general/scenario_info_view.lua b/scen_edit/view/general/scenario_info_view.lua index b5c19a39..469fddc7 100644 --- a/scen_edit/view/general/scenario_info_view.lua +++ b/scen_edit/view/general/scenario_info_view.lua @@ -48,7 +48,19 @@ function ScenarioInfoView:init() value = SB.model.scenarioInfo.author, }) ) - + self:AddControl("ZK-Config Import", { + Button:New { + caption = "Import MapConfig(ZK) from Map", + width = 300, + height = 80, + OnClick = { + function() + local cmd = LoadZKMapConfigCommand() + SB.commandManager:execute(cmd, true) + end + } + }, + }) local children = { ScrollPanel:New { x = 0, From 66aa3606dab6df1e361f3f92f532203a3fee4cc1 Mon Sep 17 00:00:00 2001 From: Dave-tB <90851364+Dave-tB@users.noreply.github.com> Date: Sun, 5 Dec 2021 17:38:43 -0500 Subject: [PATCH 6/6] Added custom simplified infoPath for ZK(vehicle = green, bot = yellow, unpathable = red), so that heightmap can be viewed and smoothed more usefully from SpringBoard. --- shaders/GLSL/infoPath.lua | 101 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 101 insertions(+) create mode 100644 shaders/GLSL/infoPath.lua diff --git a/shaders/GLSL/infoPath.lua b/shaders/GLSL/infoPath.lua new file mode 100644 index 00000000..27fceac3 --- /dev/null +++ b/shaders/GLSL/infoPath.lua @@ -0,0 +1,101 @@ +return { + vertex = [[#version 130 + varying vec2 texCoord; + + void main() { + texCoord = gl_MultiTexCoord0.st; + gl_Position = vec4(gl_Vertex.xyz, 1.0); + } + ]], + fragment = [[ + #version 130 + #ifdef HIGH_QUALITY + #extension GL_ARB_texture_query_lod : enable + #endif + uniform sampler2D tex0; + uniform sampler2D tex1; + uniform sampler2D tex2; + + varying vec2 texCoord; + + mat4 COLORMATRIX0 = mat4(0.80,0.00,0.00,1.0, 0.00,0.80,0.20,1.0, 1.0,0.6,0.0,1.0, 0.0,0.0,0.0,1.0); + + const float vehCliff = 0.4546; + const float botCliff = 0.8065; + + + #ifdef HIGH_QUALITY + //! source: http://www.ozone3d.net/blogs/lab/20110427/glsl-random-generator/ + float rand(vec2 n) + { + return fract(sin(dot(n.xy, vec2(12.9898, 78.233)))* 43758.5453); + } + //! source: http://www.iquilezles.org/www/articles/texture/texture.htm + vec4 getTexel(sampler2D tex, vec2 p) + { + int lod = int(textureQueryLOD(tex, p).x); + vec2 texSize = vec2(textureSize(tex, lod)) * 0.5; + p = p * texSize + 0.5; + vec2 i = floor(p); + vec2 f = p - i; + vec2 ff = f*f; + f = ff * f * ((ff * 6.0 - f * 15.0) + 10.0); + p = i + f; + p = (p - 0.5) / texSize; + vec2 off = vec2(0.0); + vec4 c = vec4(0.0); + off = (vec2(rand(p.st + off),rand(p.ts + off)) * 2.0 - 1.0) / texSize; + c += texture2D(tex, p + off * 0.25); + off = (vec2(rand(p.st + off),rand(p.ts + off)) * 2.0 - 1.0) / texSize; + c += texture2D(tex, p + off * 0.25); + off = (vec2(rand(p.st + off),rand(p.ts + off)) * 2.0 - 1.0) / texSize; + c += texture2D(tex, p + off * 0.25); + off = (vec2(rand(p.st + off),rand(p.ts + off)) * 2.0 - 1.0) / texSize; + c += texture2D(tex, p + off * 0.25); + return c * 0.25; + } + #else + #define getTexel texture2D + #endif + + void main() { + + //New slope shader + vec4 norm = texture2D(tex2, texCoord); + vec2 norm2d = vec2(norm.x, norm.a); + float slope = length(norm2d); + if (slope < vehCliff) { + gl_FragColor = vec4(0.0,1.0,0.0,1.0); + } + else if (slope < botCliff) { + gl_FragColor = vec4(1.0,1.0,0.0,1.0); + } + else { + gl_FragColor = vec4(1.0,0.0,0.0,1.0); + } + + //Old path shader + //vec4 null = vec4(0.5,0.5,0.5,1.0); + //vec4 pathData = texture2D(tex0, texCoord); + //gl_FragColor = COLORMATRIX0 * pathData; + //gl_FragColor.r -= smoothstep(0.75, 1.0, pathData.r) * 0.3; + //gl_FragColor.b += smoothstep(0.75, 1.0, pathData.r) * 1.0; + //gl_FragColor = mix(vec4(1.0), gl_FragColor, getTexel(tex1, texCoord).r * 0.35 + 0.7); //Mixing with LoS info texture to brighten up the area inside radar vision. + //gl_FragColor.a = 0.3; + + gl_FragColor.a = 0.3; + } + ]], + + uniformInt = { + tex0 = 0, + tex1 = 1, + tex2 = 2, + }, + textures = { + [0] = "$info:path", + [1] = "$info:los", + [2] = "$normals", + + }, +}