From 943ad7a355826b047a5703851a56614071a5a756 Mon Sep 17 00:00:00 2001 From: Peter Vaiko Date: Wed, 1 Jan 2025 09:41:55 -0500 Subject: [PATCH] feat: use islands detected by Giants --- .../CourseGeneratorInterface.lua | 21 +++++++++--- scripts/courseGenerator/Field.lua | 5 +++ scripts/courseGenerator/Island.lua | 9 +++++ scripts/field/FieldBoundaryDetector.lua | 33 +++++++++++++++---- scripts/specializations/CpCourseGenerator.lua | 26 ++++++++++----- 5 files changed, 74 insertions(+), 20 deletions(-) diff --git a/scripts/courseGenerator/CourseGeneratorInterface.lua b/scripts/courseGenerator/CourseGeneratorInterface.lua index f0b8b105b..1b01aa04b 100644 --- a/scripts/courseGenerator/CourseGeneratorInterface.lua +++ b/scripts/courseGenerator/CourseGeneratorInterface.lua @@ -11,10 +11,12 @@ CourseGeneratorInterface.generatedCourse = nil ---@param startPosition table {x, z} ---@param vehicle table ---@param settings CpCourseGeneratorSettings +---@param islandPolygons|nil table [[{x, z}]] island polygons, if not given, we'll attempt to find islands function CourseGeneratorInterface.generate(fieldPolygon, startPosition, vehicle, - settings + settings, + islandPolygons ) CourseGenerator.clearDebugObjects() local field = CourseGenerator.Field('', 0, CpMathUtil.pointsFromGame(fieldPolygon)) @@ -54,8 +56,17 @@ function CourseGeneratorInterface.generate(fieldPolygon, context:setIslandHeadlands(settings.nIslandHeadlands:getValue()) context:setIslandHeadlandClockwise(settings.islandHeadlandClockwise:getValue()) if settings.bypassIslands:getValue() then - context.field:findIslands() - context.field:setupIslands() + if islandPolygons then + -- islands were detected already, create them from the polygons and add to the field + for i, islandPolygon in ipairs(islandPolygons) do + context.field:addIsland(CourseGenerator.Island.createFromBoundary(i, + Polygon(CpMathUtil.pointsFromGame(islandPolygon)))) + end + else + -- detect islands ourselves + context.field:findIslands() + context.field:setupIslands() + end end local status @@ -197,7 +208,7 @@ function CourseGeneratorInterface.generateDefaultCourse() vehicle:cpDetectFieldBoundary(x, z, nil, CourseGeneratorInterface.onFieldDetectionFinished) end -function CourseGeneratorInterface.onFieldDetectionFinished(vehicle, fieldPolygon) +function CourseGeneratorInterface.onFieldDetectionFinished(vehicle, fieldPolygon, islandPolygons) if fieldPolygon == nil then CpUtil.infoVehicle(vehicle, "Not on a field, can't generate") return @@ -210,7 +221,7 @@ function CourseGeneratorInterface.onFieldDetectionFinished(vehicle, fieldPolygon settings.sharpenCorners:setValue(true) CpUtil.infoVehicle(vehicle, "Generating default course with %d headlands", settings.numberOfHeadlands:getValue()) local x, _, z = getWorldTranslation(vehicle.rootNode) - local ok, course = CourseGeneratorInterface.generate(fieldPolygon, {x = x, z = z}, vehicle, settings) + local ok, course = CourseGeneratorInterface.generate(fieldPolygon, {x = x, z = z}, vehicle, settings, islandPolygons) if ok then CourseGeneratorInterface.setCourse(vehicle, course) end diff --git a/scripts/courseGenerator/Field.lua b/scripts/courseGenerator/Field.lua index 98bb6feb1..9e2e8f680 100644 --- a/scripts/courseGenerator/Field.lua +++ b/scripts/courseGenerator/Field.lua @@ -111,6 +111,11 @@ function Field:getUnpackedVertices() return self.unpackedVertices end +---@param island CourseGenerator.Island +function Field:addIsland(island) + table.insert(self.islands, island) +end + -- Find islands (when running in the game) function Field:findIslands() if self.islandPoints == nil then diff --git a/scripts/courseGenerator/Island.lua b/scripts/courseGenerator/Island.lua index be237a978..c9a6ae800 100644 --- a/scripts/courseGenerator/Island.lua +++ b/scripts/courseGenerator/Island.lua @@ -17,6 +17,15 @@ function Island:init(id, perimeterPoints) self:createFromPerimeterPoints(perimeterPoints) end +---@param id number +---@param boundary Polygon +---@return CourseGenerator.Island +function Island.createFromBoundary(id, boundary) + local island = CourseGenerator.Island(id, {}) + island.boundary = boundary + return island +end + function Island:getId() return self.id end diff --git a/scripts/field/FieldBoundaryDetector.lua b/scripts/field/FieldBoundaryDetector.lua index fa3f6d30a..b7205d0b0 100644 --- a/scripts/field/FieldBoundaryDetector.lua +++ b/scripts/field/FieldBoundaryDetector.lua @@ -25,7 +25,8 @@ function FieldBoundaryDetector:init(x, z, vehicle) local fieldCourseSettings, implementData = FieldCourseSettings.generate(vehicle) self.courseField = FieldCourseField.generateAtPosition(x, z, fieldCourseSettings, function(courseField, success) if success then - self:info('Field boundary detection successful') + self:info('Field boundary detection successful, %d boundary points and %d islands', + #courseField.fieldRootBoundary.boundaryLine, #courseField.islands) else self:info('Field boundary detection failed') end @@ -43,14 +44,32 @@ function FieldBoundaryDetector:update(dt) end end ----@return table|nil [{x, y, z}] field polygon vertices +---@return table|nil [{x, y, z}] field polygon with game vertices function FieldBoundaryDetector:getFieldPolygon() - local vertices = {} if self.success then - for _, point in ipairs(self.result.fieldRootBoundary.boundaryLine) do - local x, z = point[1], point[2] - table.insert(vertices, { x = x, y = getTerrainHeightAtWorldPos(g_currentMission.terrainRootNode, x, 1, z), z = z }) + return self:getAsVertices(self.result.fieldRootBoundary.boundaryLine) + end +end + +---@return table|nil [[{x, y, z}]] array of island polygons with game vertices (x, y, z) +function FieldBoundaryDetector:getIslandPolygons() + local islandPolygons = {} + if self.success then + for i, island in ipairs(self.result.islands) do + local islandBoundary = self:getAsVertices(island.rootBoundary.boundaryLine) + table.insert(islandPolygons, islandBoundary) end end + return islandPolygons +end + +---@param boundaryLine table [[x, z]] array of arrays as the Giants functions return the field boundary +---@return table [{x, z}] array of vertices as the course generator needs it +function FieldBoundaryDetector:getAsVertices(boundaryLine) + local vertices = {} + for _, point in ipairs(boundaryLine) do + local x, z = point[1], point[2] + table.insert(vertices, { x = x, y = getTerrainHeightAtWorldPos(g_currentMission.terrainRootNode, x, 1, z), z = z }) + end return vertices -end \ No newline at end of file +end diff --git a/scripts/specializations/CpCourseGenerator.lua b/scripts/specializations/CpCourseGenerator.lua index a41e2ccf7..0a3fa014f 100644 --- a/scripts/specializations/CpCourseGenerator.lua +++ b/scripts/specializations/CpCourseGenerator.lua @@ -35,7 +35,8 @@ end ---@param x number world X coordinate to start the detection at ---@param z number world Z coordinate to start the detection at ----@param object table|nil optional object +---@param object table|nil optional object with callback +---@param onFinishedFunc function callback function to call when finished: onFinishedFunc([object,] fieldPolygon, islandPolygons) function CpCourseGenerator:cpDetectFieldBoundary(x, z, object, onFinishedFunc) local spec = self.spec_cpCourseGenerator spec.fieldBoundaryDetector = FieldBoundaryDetector(x, z, self) @@ -49,11 +50,12 @@ function CpCourseGenerator:onUpdate(dt) if not spec.fieldBoundaryDetector:update(dt) then -- done spec.fieldPolygon = spec.fieldBoundaryDetector:getFieldPolygon() + spec.islandPolygons = spec.fieldBoundaryDetector:getIslandPolygons() spec.fieldBoundaryDetector = nil if spec.object then - spec.onFinishedFunc(spec.object, self, spec.fieldPolygon) + spec.onFinishedFunc(spec.object, self, spec.fieldPolygon, spec.islandPolygons) else - spec.onFinishedFunc(self, spec.fieldPolygon) + spec.onFinishedFunc(self, spec.fieldPolygon, spec.islandPolygons) end end end @@ -63,14 +65,22 @@ function CpCourseGenerator:cpGetFieldPolygon() return self.spec_cpCourseGenerator.fieldPolygon end --- For debug, if there is a field polygon, draw it +-- For debug, if there is a field polygon or island polygons, draw them function CpCourseGenerator:cpDrawFieldPolygon() local spec = self.spec_cpCourseGenerator - if spec.fieldPolygon then - for i = 2, #spec.fieldPolygon do - local p, n = spec.fieldPolygon[i - 1], spec.fieldPolygon[i] + local function drawPolygon(polygon) + for i = 2, #polygon do + local p, n = polygon[i - 1], polygon[i] Utils.renderTextAtWorldPosition(p.x, p.y + 1.2, p.z, tostring(i - 1), getCorrectTextSize(0.012), 0) DebugUtil.drawDebugLine(p.x, p.y + 1, p.z, n.x, n.y + 1, n.z, 0, 1, 0) end end -end \ No newline at end of file + if spec.fieldPolygon then + drawPolygon(spec.fieldPolygon) + end + if spec.islandPolygons then + for _, p in ipairs(spec.islandPolygons) do + drawPolygon(p) + end + end +end