1295 | function AITurnStrategy:createPointsForSegment(segmentIndex, segmentProgress, positions, posIndex, pointToPointDistance, remainingDistance, remainingPoints) |
1296 | local segment = self.turnSegments[segmentIndex] |
1297 | local curAngle = 0 |
1298 | if segment.isCurve then |
1299 | curAngle = segmentProgress * (segment.endAngle-segment.startAngle) + segment.startAngle |
1300 | local segmentCircleLength = 2 * math.pi * segment.radius |
1301 | |
1302 | while remainingPoints > 0 and remainingDistance > 0 do |
1303 | local nextAngle = curAngle + (pointToPointDistance / segmentCircleLength * (2 * math.pi)) * MathUtil.sign(segment.endAngle-curAngle) |
1304 | if segment.endAngle > segment.startAngle then |
1305 | if nextAngle >= segment.endAngle or nextAngle <= segment.startAngle then |
1306 | break |
1307 | end |
1308 | else |
1309 | if nextAngle <= segment.endAngle or nextAngle >= segment.startAngle then |
1310 | break |
1311 | end |
1312 | end |
1313 | |
1314 | local x1 = segment.radius * math.cos(nextAngle) |
1315 | local z1 = segment.radius * math.sin(nextAngle) |
1316 | positions[posIndex+1], positions[posIndex+2], positions[posIndex+3] = localToWorld(segment.o, x1, 0, z1) |
1317 | positions[posIndex+2] = positions[2] |
1318 | posIndex = posIndex + 3 |
1319 | remainingPoints = remainingPoints - 1 |
1320 | remainingDistance = remainingDistance - pointToPointDistance |
1321 | |
1322 | curAngle = nextAngle |
1323 | end |
1324 | else |
1325 | local segmentLength = MathUtil.vector2Length(segment.startPoint[1]-segment.endPoint[1], segment.startPoint[3]-segment.endPoint[3]) |
1326 | local curAlpha = segmentProgress |
1327 | while remainingPoints > 0 and remainingDistance > 0 do |
1328 | local nextAlpha = curAlpha + pointToPointDistance / segmentLength |
1329 | if nextAlpha < 0 or nextAlpha > 1 then |
1330 | break |
1331 | end |
1332 | |
1333 | local x1, _, z1 = MathUtil.vector3ArrayLerp(segment.startPoint, segment.endPoint, nextAlpha) |
1334 | positions[posIndex+1], positions[posIndex+2], positions[posIndex+3] = x1, positions[2], z1 |
1335 | posIndex = posIndex + 3 |
1336 | remainingPoints = remainingPoints - 1 |
1337 | remainingDistance = remainingDistance - pointToPointDistance |
1338 | |
1339 | curAlpha = nextAlpha |
1340 | end |
1341 | end |
1342 | |
1343 | if remainingDistance > 0 then |
1344 | segmentIndex = segmentIndex + 1 |
1345 | if self.turnSegments[segmentIndex] ~= nil and self.turnSegments[segmentIndex].moveForward then |
1346 | self:createPointsForSegment(segmentIndex, 0, positions, posIndex, pointToPointDistance, remainingDistance, remainingPoints) |
1347 | else |
1348 | if posIndex >= 3 then |
1349 | local dirX, dirY, dirZ |
1350 | if segment.isCurve then |
1351 | local dir = MathUtil.sign(segment.endAngle - segment.startAngle) |
1352 | |
1353 | if dir ~= 0 then |
1354 | local preAngle = curAngle + math.rad(10) * -dir |
1355 | local nextAngle = curAngle + math.rad(10) * dir |
1356 | |
1357 | local x1, y1, z1 = localToWorld(segment.o, segment.radius * math.cos(preAngle), 0, segment.radius * math.sin(preAngle)) |
1358 | local x2, y2, z2 = localToWorld(segment.o, segment.radius * math.cos(nextAngle), 0, segment.radius * math.sin(nextAngle)) |
1359 | dirX, dirY, dirZ = MathUtil.vector3Normalize(x1-x2, y1-y2, z1-z2) |
1360 | else |
1361 | -- start and end angle can be the same depending on the work width |
1362 | -- in this case the segment has a length of 0, so we can skip it |
1363 | -- we fill up the other points into the same direction as the previous ones |
1364 | local x1, y1, z1 = positions[posIndex-5], positions[posIndex-4], positions[posIndex-3] |
1365 | local x2, y2, z2 = positions[posIndex-2], positions[posIndex-1], positions[posIndex-0] |
1366 | dirX, dirY, dirZ = MathUtil.vector3Normalize(x1-x2, y1-y2, z1-z2) |
1367 | end |
1368 | else |
1369 | dirX, dirY, dirZ = MathUtil.vector3Normalize(segment.startPoint[1]-segment.endPoint[1], 0, segment.startPoint[3]-segment.endPoint[3]) |
1370 | end |
1371 | |
1372 | local lx1, ly1, lz1 = positions[posIndex-2], positions[posIndex-1], positions[posIndex-0] |
1373 | local distance = 0 |
1374 | for i=0, remainingPoints-1 do |
1375 | distance = distance + pointToPointDistance |
1376 | positions[posIndex+1], positions[posIndex+2], positions[posIndex+3] = lx1 - dirX * distance, ly1 - dirY * distance, lz1 - dirZ * distance |
1377 | posIndex = posIndex + 3 |
1378 | end |
1379 | end |
1380 | end |
1381 | end |
1382 | end |
983 | function AITurnStrategy.drawTurnSegments(segments) |
984 | for i,segment in pairs(segments) do |
985 | if segment.isCurve == true then |
986 | local oX,oY,oZ = localToWorld(segment.o, 0,2,0) |
987 | local xX,xY,xZ = localToWorld(segment.o, 2,2,0) |
988 | local yX,yY,yZ = localToWorld(segment.o, 0,4,0) |
989 | local zX,zY,zZ = localToWorld(segment.o, 0,2,2) |
990 | |
991 | drawDebugLine(oX,oY,oZ, 1,0,0, xX,xY,xZ, 1,0,0) |
992 | drawDebugLine(oX,oY,oZ, 0,1,0, yX,yY,yZ, 0,1,0) |
993 | drawDebugLine(oX,oY,oZ, 0,0,1, zX,zY,zZ, 0,0,1) |
994 | |
995 | Utils.renderTextAtWorldPosition(yX,yY,yZ, tostring(i), 0.02, 0) |
996 | |
997 | local ts = 20 |
998 | for i=0,ts-1 do |
999 | local x1 = segment.radius * math.cos(segment.startAngle + i*(segment.endAngle-segment.startAngle)/ts) |
1000 | local z1 = segment.radius * math.sin(segment.startAngle + i*(segment.endAngle-segment.startAngle)/ts) |
1001 | local x2 = segment.radius * math.cos(segment.startAngle + (i+1)*(segment.endAngle-segment.startAngle)/ts) |
1002 | local z2 = segment.radius * math.sin(segment.startAngle + (i+1)*(segment.endAngle-segment.startAngle)/ts) |
1003 | local w1X,w1Y,w1Z = localToWorld(segment.o, x1,0,z1) |
1004 | local w2X,w2Y,w2Z = localToWorld(segment.o, x2,0,z2) |
1005 | local w1Y = getTerrainHeightAtWorldPos(g_currentMission.terrainRootNode, w1X,w1Y,w1Z) + 1 |
1006 | local w2Y = getTerrainHeightAtWorldPos(g_currentMission.terrainRootNode, w2X,w2Y,w2Z) + 1 |
1007 | drawDebugLine(w1X,w1Y,w1Z, (ts-i)/ts,i/ts,0, w2X,w2Y,w2Z, (ts-i-1)/ts,(i+1)/ts,0) |
1008 | end |
1009 | else |
1010 | local sY = getTerrainHeightAtWorldPos(g_currentMission.terrainRootNode, unpack(segment.startPoint)) + 1 |
1011 | local eY = getTerrainHeightAtWorldPos(g_currentMission.terrainRootNode, unpack(segment.endPoint)) + 1 |
1012 | drawDebugLine(segment.startPoint[1],sY,segment.startPoint[3], 1,0,0, segment.endPoint[1],eY,segment.endPoint[3], 0,1,0) |
1013 | |
1014 | drawDebugLine(segment.startPoint[1],sY,segment.startPoint[3], 1,1,1, segment.startPoint[1],sY+2,segment.startPoint[3], 1,1,1) |
1015 | drawDebugLine(segment.endPoint[1],sY,segment.endPoint[3], 1,1,1, segment.endPoint[1],sY+2,segment.endPoint[3], 1,1,1) |
1016 | |
1017 | Utils.renderTextAtWorldPosition((segment.startPoint[1]+segment.endPoint[1])/2, (sY+eY)/2, (segment.startPoint[3]+segment.endPoint[3])/2, tostring(i), 0.02, 0) |
1018 | end |
1019 | end |
1020 | end |
1224 | function AITurnStrategy:getActiveTurnSegmentByNode(referenceNode, originX, originY, originZ) |
1225 | local activeSegmentIndex |
1226 | local activeSegmentProgress = 0 |
1227 | local minSegmentDistance = math.huge |
1228 | local activeSegment = self.turnSegments[self.activeTurnSegmentIndex] |
1229 | for i=self.activeTurnSegmentIndex, #self.turnSegments do |
1230 | local segment = self.turnSegments[i] |
1231 | if segment.moveForward and activeSegment.moveForward then |
1232 | if i >= self.activeTurnSegmentIndex and i <= self.activeTurnSegmentIndex + 2 then |
1233 | if segment.isCurve then |
1234 | local curAngle = AITurnStrategy.getAngleInSegment(referenceNode, segment, worldToLocal(referenceNode, originX, originY, originZ)) |
1235 | |
1236 | local x1, _, z1 = localToWorld(segment.o, segment.radius * math.cos(curAngle), 0, segment.radius * math.sin(curAngle)) |
1237 | local distanceToSegment = MathUtil.vector2Length(x1 - originX, z1 - originZ) |
1238 | local prevAngle = curAngle - 2 * math.pi |
1239 | local nextAngle = curAngle + 2 * math.pi |
1240 | local outOfBoundsCur = MathUtil.getIsOutOfBounds(curAngle, segment.startAngle, segment.endAngle) |
1241 | local outOfBoundsPrev = MathUtil.getIsOutOfBounds(prevAngle, segment.startAngle, segment.endAngle) |
1242 | local outOfBoundsNext = MathUtil.getIsOutOfBounds(nextAngle, segment.startAngle, segment.endAngle) |
1243 | if not outOfBoundsCur or not outOfBoundsPrev or not outOfBoundsNext then |
1244 | if distanceToSegment < minSegmentDistance then |
1245 | activeSegmentIndex = i |
1246 | minSegmentDistance = distanceToSegment |
1247 | |
1248 | if not outOfBoundsPrev then |
1249 | curAngle = prevAngle |
1250 | end |
1251 | if not outOfBoundsNext then |
1252 | curAngle = nextAngle |
1253 | end |
1254 | |
1255 | activeSegmentProgress = (curAngle - segment.startAngle) / (segment.endAngle - segment.startAngle) |
1256 | end |
1257 | else |
1258 | x1, _, z1 = localToWorld(segment.o, segment.radius * math.cos(segment.startAngle), 0, segment.radius * math.sin(segment.startAngle)) |
1259 | distanceToSegment = MathUtil.vector2Length(x1 - originX, z1 - originZ) |
1260 | if distanceToSegment < minSegmentDistance then |
1261 | activeSegmentIndex = i |
1262 | minSegmentDistance = distanceToSegment |
1263 | activeSegmentProgress = 0 |
1264 | end |
1265 | |
1266 | x1, _, z1 = localToWorld(segment.o, segment.radius * math.cos(segment.endAngle), 0, segment.radius * math.sin(segment.endAngle)) |
1267 | distanceToSegment = MathUtil.vector2Length(x1 - originX, z1 - originZ) |
1268 | if distanceToSegment < minSegmentDistance then |
1269 | activeSegmentIndex = i |
1270 | minSegmentDistance = distanceToSegment |
1271 | activeSegmentProgress = 1 |
1272 | end |
1273 | end |
1274 | else |
1275 | local x1, _, z1, pos = MathUtil.getClosestPointOnLineSegment(segment.startPoint[1], 0, segment.startPoint[3], segment.endPoint[1], 0, segment.endPoint[3], originX, 0, originZ) |
1276 | local distanceToSegment = MathUtil.vector2Length(x1 - originX, z1 - originZ) |
1277 | if distanceToSegment < minSegmentDistance then |
1278 | activeSegmentIndex = i |
1279 | minSegmentDistance = distanceToSegment |
1280 | |
1281 | activeSegmentProgress = pos |
1282 | end |
1283 | end |
1284 | end |
1285 | else |
1286 | break |
1287 | end |
1288 | end |
1289 | |
1290 | return activeSegmentIndex, activeSegmentProgress |
1291 | end |
481 | function AITurnStrategy:getDistanceToCollision(dt, vX,vY,vZ, turnData, lookAheadDistance) |
482 | local allowLeft = self.usesExtraStraight == turnData.useExtraStraightLeft |
483 | local allowRight = self.usesExtraStraight == turnData.useExtraStraightRight |
484 | |
485 | local distanceToTurn = lookAheadDistance |
486 | |
487 | -- Disable this turn strategy immediately if we are supposed to turn in a direction that is not allwoed |
488 | -- or if the only allowed side does not have any work to be done |
489 | if not allowLeft and not allowRight then |
490 | distanceToTurn = -1 |
491 | elseif self.turnLeft ~= nil then |
492 | if (self.turnLeft and not allowRight) or (not self.turnLeft and not allowLeft) then |
493 | distanceToTurn = -1 |
494 | end |
495 | else |
496 | local allowLeftWithCol = allowLeft and self.collisionEndPosLeft == nil |
497 | local allowRightWithCol = allowRight and self.collisionEndPosRight == nil |
498 | |
499 | -- Turn if not allowed or has collision on one side and the other side has no work to be done |
500 | if not allowLeftWithCol or not allowRightWithCol then |
501 | local leftAreaPercentage, rightAreaPercentage = AIVehicleUtil.getValidityOfTurnDirections(self.vehicle, turnData) |
502 | |
503 | if allowLeftWithCol and leftAreaPercentage <= 3*AIVehicleUtil.VALID_AREA_THRESHOLD then |
504 | distanceToTurn = -1 |
505 | end |
506 | if allowRightWithCol and rightAreaPercentage <= 3*AIVehicleUtil.VALID_AREA_THRESHOLD then |
507 | distanceToTurn = -1 |
508 | end |
509 | end |
510 | end |
511 | |
512 | if self.collisionDetected then |
513 | if self.collisionDetectedPosX ~= nil then |
514 | local dist = MathUtil.vector3Length(vX-self.collisionDetectedPosX, vY-self.collisionDetectedPosY, vZ-self.collisionDetectedPosZ) |
515 | distanceToTurn = math.min(distanceToTurn, lookAheadDistance - dist) |
516 | else |
517 | distanceToTurn = -1 |
518 | end |
519 | end |
520 | |
521 | self.distanceToCollision = distanceToTurn |
522 | |
523 | -- increase the size of the collision check boxes more to the back |
524 | -- this helps to avoid issues if an object is between the field end and the vehicle direction node -> so the boxes cover also 5m behind the direction node |
525 | local boxLookBackDistance = 0 |
526 | if self.parent ~= nil then |
527 | if self.parent.rowStartTranslation ~= nil then |
528 | local x, _, z = getWorldTranslation(self.vehicle:getAIDirectionNode()) |
529 | boxLookBackDistance = math.min(MathUtil.vector2Length(x-self.parent.rowStartTranslation[1], z-self.parent.rowStartTranslation[3]) * 0.5, 5) |
530 | end |
531 | end |
532 | |
533 | -- |
534 | for i=#self.maxTurningSizeBoxes, 1, -1 do |
535 | self.maxTurningSizeBoxes[i] = nil |
536 | end |
537 | |
538 | local collisionHitLeft = false |
539 | local collisionHitRight = false |
540 | |
541 | if (self.turnLeft == nil or self.turnLeft == false) and allowLeft then |
542 | local turnLeft = true |
543 | self:updateTurningSizeBox(self.leftBox, turnLeft, turnData, math.max(0,distanceToTurn)) |
544 | local box = self.leftBox |
545 | box.center[3] = box.center[3]-boxLookBackDistance |
546 | box.size[3] = box.size[3]+boxLookBackDistance |
547 | |
548 | if not self:validateCollisionBox(box) then |
549 | self.vehicle:stopCurrentAIJob(AIMessageErrorUnknown.new()) |
550 | self:debugPrint("Stopping AIVehicle - collision box invalid") |
551 | return distanceToTurn |
552 | end |
553 | |
554 | collisionHitLeft = self:getIsBoxColliding(box) |
555 | |
556 | table.insert(self.maxTurningSizeBoxes, box) |
557 | |
558 | if collisionHitLeft and self.collisionEndPosLeft == nil then |
559 | self.collisionEndPosLeft = { localToWorld(self.vehicleDirectionNode, 0,0,box.size[3]) } |
560 | end |
561 | end |
562 | |
563 | if (self.turnLeft == nil or self.turnLeft == true) and allowRight then |
564 | local turnLeft = false |
565 | self:updateTurningSizeBox(self.rightBox, turnLeft, turnData, math.max(0,distanceToTurn)) |
566 | local box = self.rightBox |
567 | box.center[3] = box.center[3]-boxLookBackDistance |
568 | box.size[3] = box.size[3]+boxLookBackDistance |
569 | |
570 | if not self:validateCollisionBox(box) then |
571 | self.vehicle:stopCurrentAIJob(AIMessageErrorUnknown.new()) |
572 | self:debugPrint("Stopping AIVehicle - collision box invalid 2") |
573 | return distanceToTurn |
574 | end |
575 | |
576 | collisionHitRight = self:getIsBoxColliding(box) |
577 | |
578 | table.insert(self.maxTurningSizeBoxes, box) |
579 | |
580 | if collisionHitRight and self.collisionEndPosRight == nil then |
581 | self.collisionEndPosRight = { localToWorld(self.vehicleDirectionNode, 0,0,box.size[3]) } |
582 | end |
583 | end |
584 | |
585 | self:evaluateCollisionHits(vX,vY,vZ, collisionHitLeft, collisionHitRight, turnData) |
586 | |
587 | return distanceToTurn |
588 | end |
169 | function AITurnStrategy:getDriveData(dt, vX,vY,vZ, turnData) |
170 | if VehicleDebug.state == VehicleDebug.DEBUG_AI then |
171 | self.vehicle:addAIDebugText(string.format("strategy: %s", self.strategyName)) |
172 | end |
173 | |
174 | local tX, tY, tZ |
175 | local maxSpeed = self.vehicle:getSpeedLimit(true) |
176 | maxSpeed = math.min(14, maxSpeed) |
177 | local distanceToStop |
178 | |
179 | local segment = self.turnSegments[self.activeTurnSegmentIndex] |
180 | local segmentIsFinished = false |
181 | |
182 | local moveForwards = segment.moveForward |
183 | |
184 | if segment.isCurve then |
185 | local angleDirSign = MathUtil.sign(segment.endAngle - segment.startAngle) |
186 | |
187 | local curAngle |
188 | if self.reverserDirectionNode ~= nil and not moveForwards then |
189 | curAngle = AITurnStrategy.getAngleInSegment(self.reverserDirectionNode, segment) |
190 | else |
191 | curAngle = AITurnStrategy.getAngleInSegment(self.vehicleAISteeringNode, segment) |
192 | end |
193 | |
194 | local nextAngleDistance = math.max(3, 0.33 * self.vehicle.maxTurningRadius) |
195 | |
196 | local nextAngle = curAngle + angleDirSign * nextAngleDistance / segment.radius |
197 | if nextAngle > math.pi then |
198 | nextAngle = nextAngle - 2*math.pi |
199 | elseif nextAngle < -math.pi then |
200 | nextAngle = nextAngle + 2*math.pi |
201 | end |
202 | |
203 | local endAngle = segment.endAngle |
204 | if endAngle > math.pi then |
205 | endAngle = endAngle - 2*math.pi |
206 | elseif endAngle < -math.pi then |
207 | endAngle = endAngle + 2*math.pi |
208 | end |
209 | |
210 | angleDirSign = MathUtil.sign(segment.endAngle - segment.startAngle) |
211 | |
212 | local curAngleDiff = angleDirSign * (curAngle - endAngle) -- > 0 if after endAngle |
213 | if curAngleDiff > math.rad(10) then |
214 | curAngleDiff = curAngleDiff - 2*math.pi |
215 | elseif curAngleDiff < -2*math.pi + math.rad(10) then |
216 | curAngleDiff = curAngleDiff + 2*math.pi |
217 | end |
218 | |
219 | local nextAngleDiff = angleDirSign * (nextAngle - endAngle) |
220 | if nextAngleDiff > math.rad(10) then |
221 | nextAngleDiff = nextAngleDiff - 2*math.pi |
222 | elseif nextAngleDiff < -2*math.pi + math.rad(10) then |
223 | nextAngleDiff = nextAngleDiff + 2*math.pi |
224 | end |
225 | |
226 | local pX = math.cos(nextAngle) * segment.radius |
227 | local pZ = math.sin(nextAngle) * segment.radius |
228 | tX,tY,tZ = localToWorld(segment.o, pX,0,pZ) |
229 | |
230 | -- condition for segment finish |
231 | distanceToStop = -curAngleDiff * segment.radius |
232 | |
233 | if distanceToStop < 0.01 or (segment.usePredictionToSkipToNextSegment ~= false and nextAngleDiff > 0) then |
234 | segmentIsFinished = true |
235 | end |
236 | |
237 | if segment.checkForSkipToNextSegment then |
238 | local nextSegment = self.turnSegments[self.activeTurnSegmentIndex+1] |
239 | |
240 | local dirX = nextSegment.endPoint[1] - nextSegment.startPoint[1] |
241 | local dirZ = nextSegment.endPoint[3] - nextSegment.startPoint[3] |
242 | local dirLength = MathUtil.vector2Length(dirX, dirZ) |
243 | local dx,_ |
244 | if self.reverserDirectionNode ~= nil and not moveForwards then |
245 | dx,_,_ = worldDirectionToLocal(self.reverserDirectionNode, dirX/dirLength,0,dirZ/dirLength) |
246 | else |
247 | dx,_,_ = worldDirectionToLocal(self.vehicleAISteeringNode, dirX/dirLength,0,dirZ/dirLength) |
248 | end |
249 | |
250 | local l = MathUtil.vector2Length(dirX, dirZ) |
251 | dirX, dirZ = dirX / l, dirZ / l |
252 | pX, pZ = MathUtil.projectOnLine(vX, vZ, nextSegment.startPoint[1], nextSegment.startPoint[3], dirX, dirZ) |
253 | local dist = MathUtil.vector2Length(vX-pX, vZ-pZ) |
254 | |
255 | local distToStart = MathUtil.vector2Length(vX-nextSegment.startPoint[1], vZ-nextSegment.startPoint[3]) |
256 | |
257 | if dist < 1.5 and math.abs(dx) < 0.15 and distToStart < self.vehicle.size.length/2 then |
258 | segmentIsFinished = true |
259 | end |
260 | end |
261 | |
262 | -- |
263 | if not moveForwards and self.reverserDirectionNode ~= nil then |
264 | local x,_,z = worldToLocal(self.reverserDirectionNode, tX,vY,tZ) |
265 | local alpha = Utils.getYRotationBetweenNodes(self.vehicleAISteeringNode, self.reverserDirectionNode) |
266 | local ltX = math.cos(alpha)*x - math.sin(alpha)*z |
267 | local ltZ = math.sin(alpha)*x + math.cos(alpha)*z |
268 | ltX = -ltX |
269 | tX,_,tZ = localToWorld(self.vehicleAISteeringNode, ltX,0,ltZ) |
270 | end |
271 | |
272 | -- just visual debuging |
273 | if VehicleDebug.state == VehicleDebug.DEBUG_AI then |
274 | drawDebugLine(vX,vY+2,vZ, 1,1,0, tX,tY+2,tZ, 1,1,0) |
275 | end |
276 | else |
277 | local toolX,_,toolZ |
278 | if self.reverserDirectionNode ~= nil then |
279 | toolX,_,toolZ = getWorldTranslation(self.reverserDirectionNode) |
280 | end |
281 | |
282 | local dirX = segment.endPoint[1] - segment.startPoint[1] |
283 | local dirZ = segment.endPoint[3] - segment.startPoint[3] |
284 | |
285 | local l = MathUtil.vector2Length(dirX, dirZ) |
286 | dirX, dirZ = dirX / l, dirZ / l |
287 | local pX, pZ |
288 | if self.reverserDirectionNode ~= nil and not moveForwards then |
289 | pX, pZ = MathUtil.projectOnLine(toolX, toolZ, segment.startPoint[1], segment.startPoint[3], dirX, dirZ) |
290 | else |
291 | if self.vehicleAIReverserNode ~= nil and not moveForwards then |
292 | toolX,_,toolZ = getWorldTranslation(self.vehicleAIReverserNode) |
293 | pX, pZ = MathUtil.projectOnLine(toolX, toolZ, segment.startPoint[1], segment.startPoint[3], dirX, dirZ) |
294 | else |
295 | pX, pZ = MathUtil.projectOnLine(vX, vZ, segment.startPoint[1], segment.startPoint[3], dirX, dirZ) |
296 | end |
297 | end |
298 | |
299 | local factor = 1.0 |
300 | tX = pX + (dirX * factor * self.vehicle.maxTurningRadius) |
301 | tZ = pZ + (dirZ * factor * self.vehicle.maxTurningRadius) |
302 | |
303 | if self.reverserDirectionNode ~= nil and not moveForwards then |
304 | local x,_,z = worldToLocal(self.reverserDirectionNode, tX,vY,tZ) |
305 | local alpha = Utils.getYRotationBetweenNodes(self.vehicleAISteeringNode, self.reverserDirectionNode) |
306 | |
307 | local articulatedAxisSpec = self.vehicle.spec_articulatedAxis |
308 | if articulatedAxisSpec ~= nil and articulatedAxisSpec.componentJoint ~= nil then |
309 | local node1 = self.vehicle.components[articulatedAxisSpec.componentJoint.componentIndices[1]].node |
310 | local node2 = self.vehicle.components[articulatedAxisSpec.componentJoint.componentIndices[2]].node |
311 | if articulatedAxisSpec.anchorActor == 1 then |
312 | node1, node2 = node2, node1 |
313 | end |
314 | |
315 | local beta = Utils.getYRotationBetweenNodes(node1, node2) |
316 | alpha = alpha - beta |
317 | end |
318 | |
319 | local ltX = math.cos(alpha)*x - math.sin(alpha)*z |
320 | local ltZ = math.sin(alpha)*x + math.cos(alpha)*z |
321 | ltX = -ltX |
322 | tX,_,tZ = localToWorld(self.vehicleAISteeringNode, ltX,0,ltZ) |
323 | end |
324 | |
325 | distanceToStop = MathUtil.vector3Length(segment.endPoint[1]-vX, segment.endPoint[2]-vY, segment.endPoint[3]-vZ) |
326 | |
327 | |
328 | local _,_,lz = worldToLocal(self.vehicleAISteeringNode, segment.endPoint[1],segment.endPoint[2],segment.endPoint[3]) |
329 | if (segment.moveForward and lz < 0) or (not segment.moveForward and lz > 0) then |
330 | segmentIsFinished = true |
331 | end |
332 | |
333 | -- check during pre final straight, only used by reverse strategies |
334 | if segment.checkAlignmentToSkipSegment then |
335 | local d1x,_,d1z = localDirectionToWorld(self.vehicleAISteeringNode, 0,0,1) |
336 | local l1 = MathUtil.vector2Length(d1x, d1z) |
337 | d1x, d1z = d1x/l1, d1z/l1 |
338 | local a1 = math.acos( d1x * dirX + d1z * dirZ ) |
339 | local dist = MathUtil.vector2Length(vX-pX, vZ-pZ) |
340 | local canSkip = math.deg(a1) < 8 and dist < 0.6 |
341 | |
342 | if self.vehicle.spec_articulatedAxis ~= nil and self.vehicle.spec_articulatedAxis.componentJoint ~= nil then |
343 | for i=1,2 do |
344 | local node = self.vehicle.components[self.vehicle.spec_articulatedAxis.componentJoint.componentIndices[i]].node |
345 | |
346 | d1x,_,d1z = localDirectionToWorld(node, 0,0,1) |
347 | l1 = MathUtil.vector2Length(d1x, d1z) |
348 | d1x, d1z = d1x/l1, d1z/l1 |
349 | local a = math.acos( d1x * dirX + d1z * dirZ ) |
350 | canSkip = canSkip and math.deg(a) < 8 |
351 | end |
352 | end |
353 | |
354 | if self.reverserDirectionNode ~= nil then |
355 | local d2x,_,d2z = localDirectionToWorld(self.reverserDirectionNode, 0,0,1) |
356 | local l2 = MathUtil.vector2Length(d2x, d2z) |
357 | d2x, d2z = d2x/l2, d2z/l2 |
358 | local a2 = math.acos( d2x * dirX + d2z * dirZ ) |
359 | pX, pZ = MathUtil.projectOnLine(toolX,toolZ, segment.startPoint[1],segment.startPoint[3], dirX,dirZ) |
360 | dist = MathUtil.vector2Length(toolX-pX, toolZ-pZ) |
361 | canSkip = canSkip and math.deg(a2) < 6 and dist < 0.6 |
362 | end |
363 | |
364 | local nextSegment = self.turnSegments[self.activeTurnSegmentIndex+1] |
365 | local _,_,sz = worldToLocal(self.vehicleDirectionNode, nextSegment.startPoint[1],nextSegment.startPoint[2],nextSegment.startPoint[3]) |
366 | local _,_,ez = worldToLocal(self.vehicleDirectionNode, nextSegment.endPoint[1],nextSegment.endPoint[2],nextSegment.endPoint[3]) |
367 | canSkip = canSkip and (sz < 0 or ez < 0) |
368 | |
369 | if canSkip then |
370 | segmentIsFinished = true |
371 | end |
372 | end |
373 | |
374 | if VehicleDebug.state == VehicleDebug.DEBUG_AI then |
375 | local sY = getTerrainHeightAtWorldPos(g_currentMission.terrainRootNode, tX,vY,tZ) |
376 | drawDebugLine(vX,vY+2,vZ, 1,1,0, tX,sY+2,tZ, 1,1,0) |
377 | end |
378 | end |
379 | |
380 | if VehicleDebug.state == VehicleDebug.DEBUG_AI then |
381 | self.vehicle:addAIDebugText(string.format("active segment: %d", self.activeTurnSegmentIndex)) |
382 | end |
383 | |
384 | --# check if a tool can already work |
385 | if segment.checkForValidArea then |
386 | local lookAheadDist = 0 |
387 | local lookAheadSize = 1 |
388 | if not moveForwards then |
389 | lookAheadSize = -1 |
390 | end |
391 | if AIVehicleUtil.checkImplementListForValidGround(self.vehicle, lookAheadDist, lookAheadSize) then |
392 | segmentIsFinished = true |
393 | self.activeTurnSegmentIndex = #self.turnSegments |
394 | end |
395 | end |
396 | |
397 | --# |
398 | if segment.findEndOfField then |
399 | local lookAheadDist = 0 |
400 | local lookAheadSize = 1 |
401 | if not moveForwards then |
402 | lookAheadSize = -1 |
403 | end |
404 | if not AIVehicleUtil.checkImplementListForValidGround(self.vehicle, lookAheadDist, lookAheadSize) then |
405 | segmentIsFinished = true |
406 | end |
407 | end |
408 | |
409 | -- activate next segment or stop turn |
410 | if segmentIsFinished or self.requestToEndTurn then |
411 | self.activeTurnSegmentIndex = self.activeTurnSegmentIndex + 1 |
412 | |
413 | if self.turnSegments[self.activeTurnSegmentIndex] == nil then |
414 | self.isTurning = false |
415 | self.requestToEndTurn = false |
416 | return nil |
417 | end |
418 | end |
419 | |
420 | -- calculate turn progress percentage |
421 | local totalSegmentLength = 0 |
422 | local usedSegmentDistance = 0 |
423 | for i, turnSegment in ipairs(self.turnSegments) do |
424 | -- exclude the last straight part(s) since we don't know how far we need to go |
425 | if turnSegment.checkAlignmentToSkipSegment then |
426 | break |
427 | end |
428 | |
429 | local segmentLength |
430 | if turnSegment.isCurve then |
431 | segmentLength = math.abs(turnSegment.endAngle - turnSegment.startAngle) * turnSegment.radius |
432 | else |
433 | segmentLength = math.abs(MathUtil.vector3Length(turnSegment.endPoint[1]-turnSegment.startPoint[1], |
434 | turnSegment.endPoint[2]-turnSegment.startPoint[2], |
435 | turnSegment.endPoint[3]-turnSegment.startPoint[3])) |
436 | end |
437 | segmentLength = math.abs(segmentLength) |
438 | |
439 | totalSegmentLength = totalSegmentLength + segmentLength |
440 | |
441 | if i < self.activeTurnSegmentIndex then |
442 | usedSegmentDistance = usedSegmentDistance + segmentLength |
443 | elseif i == self.activeTurnSegmentIndex then |
444 | usedSegmentDistance = usedSegmentDistance + (segmentLength-distanceToStop) |
445 | end |
446 | end |
447 | |
448 | local turnProgress = usedSegmentDistance / totalSegmentLength |
449 | self.vehicle:aiFieldWorkerTurnProgress(turnProgress, self.turnLeft) |
450 | if VehicleDebug.state == VehicleDebug.DEBUG_AI then |
451 | self.vehicle:addAIDebugText(string.format("turn progress: %.1f%%", turnProgress*100)) |
452 | end |
453 | |
454 | if not segment.slowDown then |
455 | distanceToStop = math.huge |
456 | end |
457 | |
458 | return tX, tZ, moveForwards, maxSpeed, distanceToStop |
459 | end |
592 | function AITurnStrategy:getIsBoxColliding(box) |
593 | box.x, box.y, box.z = localToWorld(self.vehicleDirectionNode, box.center[1], box.center[2], box.center[3]) |
594 | box.zx, box.zy, box.zz = localDirectionToWorld(self.vehicleDirectionNode, 0,0,1) |
595 | box.xx, box.xy, box.xz = localDirectionToWorld(self.vehicleDirectionNode, 1,0,0) |
596 | box.ry = math.atan2(box.zx, box.zz) |
597 | box.color = AITurnStrategy.COLLISION_BOX_COLOR_OK |
598 | |
599 | self.collisionHit = false |
600 | overlapBox(box.x,box.y,box.z, 0,box.ry,0, box.size[1],box.size[2],box.size[3], "collisionTestCallback", self, CollisionFlag.AI_BLOCKING, true, true, true) |
601 | if self.collisionHit then |
602 | box.color = AITurnStrategy.COLLISION_BOX_COLOR_HIT |
603 | return true |
604 | end |
605 | |
606 | local x1, _, z1 = localToWorld(self.vehicleDirectionNode, box.center[1]+box.size[1], 0, box.center[3]+box.size[3]) |
607 | local x2, _, z2 = localToWorld(self.vehicleDirectionNode, box.center[1]+box.size[1], 0, box.center[3]-box.size[3]) |
608 | |
609 | local t1 = getTerrainHeightAtWorldPos(g_currentMission.terrainRootNode, x1, 0, z1) |
610 | local t2 = getTerrainHeightAtWorldPos(g_currentMission.terrainRootNode, x2, 0, z2) |
611 | |
612 | -- water check has always one frame delay (async) |
613 | raycastClosest(x1, t1, z1, 0, -1, 0, "onWaterRaycastCallback", 200, self, CollisionFlag.WATER, false, false, true) |
614 | raycastClosest(x2, t2, z2, 0, -1, 0, "onWaterRaycastCallback2", 200, self, CollisionFlag.WATER, false, false, true) |
615 | |
616 | local waterY1 = self.lastWaterY1 |
617 | local waterY2 = self.lastWaterY2 |
618 | self.lastWaterY1 = -2000 |
619 | self.lastWaterY2 = -2000 |
620 | |
621 | -- check if at the box start or end position is water |
622 | if t1 < waterY1 or t2 < waterY2 then |
623 | if VehicleDebug.state == VehicleDebug.DEBUG_AI then |
624 | self.vehicle:addAIDebugText(string.format(" hit water: b%.1f f%.1f w%.1f", t1, t2, waterY1)) |
625 | end |
626 | |
627 | box.color = AITurnStrategy.COLLISION_BOX_COLOR_HIT |
628 | return true |
629 | end |
630 | |
631 | local testLength = 3 |
632 | |
633 | -- left and right side of the box |
634 | local angle1 = self:getCollisionBoxSlope(self.vehicleDirectionNode, box.center[1]+box.size[1], 0, box.center[3]+box.size[3], box.center[1]+box.size[1], 0, box.center[3]+box.size[3]-testLength) |
635 | local angle2 = self:getCollisionBoxSlope(self.vehicleDirectionNode, box.center[1]-box.size[1], 0, box.center[3]+box.size[3], box.center[1]-box.size[1], 0, box.center[3]+box.size[3]-testLength) |
636 | |
637 | -- center of vehicle |
638 | local angle3 = self:getCollisionBoxSlope(self.vehicleDirectionNode, 0, 0, box.center[3]+box.size[3], 0, 0, box.center[3]+box.size[3]-testLength) |
639 | |
640 | -- side angle of box |
641 | local angle4 = self:getCollisionBoxSlope(self.vehicleDirectionNode, box.center[1]+box.size[1]-testLength, 0, box.center[3]+box.size[3], box.center[1]+box.size[1], 0, box.center[3]+box.size[3]) |
642 | local angle5 = self:getCollisionBoxSlope(self.vehicleDirectionNode, box.center[1]-box.size[1]+testLength, 0, box.center[3]+box.size[3], box.center[1]-box.size[1], 0, box.center[3]+box.size[3]) |
643 | |
644 | local angleBetween = math.max(angle1, angle2, angle3, angle4, angle5) |
645 | |
646 | if angleBetween > AITurnStrategy.SLOPE_DETECTION_THRESHOLD then |
647 | box.color = AITurnStrategy.COLLISION_BOX_COLOR_HIT |
648 | return true |
649 | end |
650 | |
651 | -- check for density height heaps higher than 2m |
652 | x1, _, z1 = localToWorld(self.vehicleDirectionNode, box.center[1]+box.size[1], 0, box.center[3]+box.size[3]) |
653 | x2, _, z2 = localToWorld(self.vehicleDirectionNode, box.center[1]-box.size[1], 0, box.center[3]+box.size[3]) |
654 | local length = MathUtil.vector2Length(x1-x2, z1-z2) |
655 | local steps = math.floor(length/3) |
656 | for i=0,steps do |
657 | local alpha = math.min(1/(steps+1)*(i+math.random()), 1) |
658 | local x, z = MathUtil.lerp(x1, x2, alpha), MathUtil.lerp(z1, z2, alpha) |
659 | local _, densityHeight = DensityMapHeightUtil.getHeightAtWorldPos(x, 0, z) |
660 | |
661 | if densityHeight >= AITurnStrategy.DENSITY_HEIGHT_THRESHOLD then |
662 | box.color = AITurnStrategy.COLLISION_BOX_COLOR_HIT |
663 | return true |
664 | end |
665 | end |
666 | |
667 | return false |
668 | end |
731 | function AITurnStrategy:startTurn(driveStrategyStraight) |
732 | |
733 | local turnData = driveStrategyStraight.turnData |
734 | |
735 | self.isTurning = true |
736 | self.requestToEndTurn = false |
737 | |
738 | self:clearTurnSegments() |
739 | |
740 | self.turnSegmentsTotalLength = 0 |
741 | self.activeTurnSegmentIndex = 1 |
742 | |
743 | local allowLeft = self.usesExtraStraight == turnData.useExtraStraightLeft and driveStrategyStraight.gabAllowTurnLeft |
744 | local allowRight = self.usesExtraStraight == turnData.useExtraStraightRight and driveStrategyStraight.gabAllowTurnRight |
745 | if not allowLeft and not allowRight then |
746 | self:debugPrint("Stopping AI - not allowed in both directions (gabAllowTurnLeft: %s, gabAllowTurnRight: %s)", driveStrategyStraight.gabAllowTurnLeft, driveStrategyStraight.gabAllowTurnRight) |
747 | return false |
748 | end |
749 | |
750 | --# |
751 | AIVehicleUtil.updateInvertLeftRightMarkers(self.vehicle, self.vehicle) |
752 | for _,implement in pairs(self.vehicle:getAttachedAIImplements()) do |
753 | AIVehicleUtil.updateInvertLeftRightMarkers(self.vehicle, implement.object) |
754 | end |
755 | |
756 | -- determine turn direction |
757 | local leftAreaPercentage, rightAreaPercentage = AIVehicleUtil.getValidityOfTurnDirections(self.vehicle, turnData) |
758 | |
759 | if VehicleDebug.state == VehicleDebug.DEBUG_AI then |
760 | log(" --(I)--> self.turnLeft:", self.turnLeft, "leftAreaPercentage:", leftAreaPercentage, "rightAreaPercentage:", rightAreaPercentage) |
761 | end |
762 | |
763 | if driveStrategyStraight.corridorDistance ~= nil then |
764 | self.corridorPositionOffset = -driveStrategyStraight.corridorDistance |
765 | end |
766 | |
767 | local checkForLastValidPosition = function(vehicleNode, turnLeft, threshold) |
768 | if turnLeft and not allowLeft then |
769 | return false |
770 | end |
771 | if not turnLeft and not allowRight then |
772 | return false |
773 | end |
774 | |
775 | local position = driveStrategyStraight.lastValidTurnLeftPosition |
776 | if not turnLeft then |
777 | position = driveStrategyStraight.lastValidTurnRightPosition |
778 | end |
779 | |
780 | if position[1] ~= 0 and position[2] ~= 0 and position[3] ~= 0 then |
781 | local x, y, z = unpack(driveStrategyStraight.lastValidTurnCheckPosition) |
782 | local distance = MathUtil.vector3Length(position[1]-x, position[2]-y, position[3]-z) |
783 | if distance > threshold then |
784 | self.lastValidTurnPositionOffset = -distance |
785 | |
786 | return true |
787 | end |
788 | end |
789 | end |
790 | |
791 | if self.turnLeft == nil then |
792 | -- if both side are equal we prefer the opossite of the last used turn direction |
793 | if leftAreaPercentage == rightAreaPercentage then |
794 | if self.vehicle:getAIFieldWorkerLastTurnDirection() then |
795 | rightAreaPercentage = rightAreaPercentage + 0.01 |
796 | else |
797 | leftAreaPercentage = leftAreaPercentage + 0.01 |
798 | end |
799 | end |
800 | |
801 | local forcePreferLeft = self.collisionEndPosLeft == nil and self.collisionEndPosRight ~= nil |
802 | local forcePreferRight = self.collisionEndPosRight == nil and self.collisionEndPosLeft ~= nil and allowRight and rightAreaPercentage > AIVehicleUtil.VALID_AREA_THRESHOLD |
803 | local preferLeft = ((leftAreaPercentage > rightAreaPercentage or forcePreferLeft) and not forcePreferRight) |
804 | |
805 | if allowLeft and leftAreaPercentage > AIVehicleUtil.VALID_AREA_THRESHOLD and (preferLeft or not allowRight) then |
806 | self.turnLeft = true |
807 | |
808 | -- still check for last valid turn position since the distance to the field could be greater than 5m |
809 | checkForLastValidPosition(self.vehicleDirectionNode, true, 5) |
810 | elseif allowRight and rightAreaPercentage > AIVehicleUtil.VALID_AREA_THRESHOLD then |
811 | self.turnLeft = false |
812 | |
813 | -- still check for last valid turn position since the distance to the field could be greater than 5m |
814 | checkForLastValidPosition(self.vehicleDirectionNode, false, 5) |
815 | else |
816 | if not checkForLastValidPosition(self.vehicleDirectionNode, true, 5) then |
817 | if not checkForLastValidPosition(self.vehicleDirectionNode, false, 5) then |
818 | self:debugPrint("Stopping AIVehicle - no valid ground (I)") |
819 | return false |
820 | else |
821 | self.turnLeft = false |
822 | end |
823 | else |
824 | self.turnLeft = true |
825 | end |
826 | end |
827 | else |
828 | -- first, switch turn direction |
829 | self.turnLeft = not self.turnLeft |
830 | |
831 | if self.turnLeft then |
832 | if not allowLeft or leftAreaPercentage < AIVehicleUtil.VALID_AREA_THRESHOLD then |
833 | if not checkForLastValidPosition(self.vehicleDirectionNode, true, 5) then |
834 | self:debugPrint("Stopping AI - No ground left (%.3f)", leftAreaPercentage) |
835 | return false |
836 | end |
837 | else |
838 | -- still check for last valid turn position since the distance to the field could be greater than 5m |
839 | checkForLastValidPosition(self.vehicleDirectionNode, true, 5) |
840 | end |
841 | else |
842 | if not allowRight or rightAreaPercentage < AIVehicleUtil.VALID_AREA_THRESHOLD then |
843 | if not checkForLastValidPosition(self.vehicleDirectionNode, false, 5) then |
844 | self:debugPrint("Stopping AI - No ground right (%.3f)", rightAreaPercentage) |
845 | return false |
846 | end |
847 | else |
848 | -- still check for last valid turn position since the distance to the field could be greater than 5m |
849 | checkForLastValidPosition(self.vehicleDirectionNode, false, 5) |
850 | end |
851 | end |
852 | end |
853 | |
854 | if VehicleDebug.state == VehicleDebug.DEBUG_AI then |
855 | log(" --(II)--> self.turnLeft:", self.turnLeft, "leftAreaPercentage:", leftAreaPercentage, "rightAreaPercentage:", rightAreaPercentage) |
856 | end |
857 | |
858 | -- update turn data |
859 | driveStrategyStraight.turnLeft = not self.turnLeft |
860 | driveStrategyStraight:updateTurnData() |
861 | driveStrategyStraight.turnLeft = nil |
862 | |
863 | --# finally set new AI direction and target before turn -> if turn gets interrupted the direction afterwards will still be ok |
864 | self.vehicle.aiDriveDirection[1], self.vehicle.aiDriveDirection[2] = -self.vehicle.aiDriveDirection[1], -self.vehicle.aiDriveDirection[2] |
865 | |
866 | local sideOffset |
867 | if self.turnLeft then |
868 | sideOffset = turnData.sideOffsetLeft |
869 | else |
870 | sideOffset = turnData.sideOffsetRight |
871 | end |
872 | |
873 | -- move the ai target by the work width to the turn direction |
874 | local x, z = self.vehicle.aiDriveTarget[1], self.vehicle.aiDriveTarget[2] |
875 | local dirX, dirZ = self.vehicle.aiDriveDirection[1], self.vehicle.aiDriveDirection[2] |
876 | local sideDistance = 2*sideOffset |
877 | local sideDirX, sideDirY = -dirZ, dirX |
878 | x, z = x+sideDirX*sideDistance, z+sideDirY*sideDistance |
879 | |
880 | self.vehicle.aiDriveTarget[1], self.vehicle.aiDriveTarget[2] = x, z |
881 | |
882 | self.vehicle:aiFieldWorkerStartTurn(self.turnLeft, self) |
883 | |
884 | return true |
885 | end |
86 | function AITurnStrategy:update(dt) |
87 | |
88 | if VehicleDebug.state == VehicleDebug.DEBUG_AI then |
89 | |
90 | AITurnStrategy.drawTurnSegments(self.turnSegments) |
91 | |
92 | if self.maxTurningSizeBoxes ~= nil then |
93 | --if not self.isTurning then |
94 | if table.getn(self.maxTurningSizeBoxes) > 0 then |
95 | self.maxTurningSizeBoxes2 = {} |
96 | for _,box in pairs(self.maxTurningSizeBoxes) do |
97 | local box2 = {} |
98 | box2.points = {} |
99 | box2.color = {unpack(box.color)} |
100 | |
101 | local x,y,z = box.x,box.y,box.z |
102 | |
103 | local blx = box.xx*box.size[1] - box.zx*box.size[3] |
104 | local blz = box.xz*box.size[1] - box.zz*box.size[3] |
105 | |
106 | local brx = -box.xx*box.size[1] - box.zx*box.size[3] |
107 | local brz = -box.xz*box.size[1] - box.zz*box.size[3] |
108 | |
109 | local flx = box.xx*box.size[1] + box.zx*box.size[3] |
110 | local flz = box.xz*box.size[1] + box.zz*box.size[3] |
111 | |
112 | local frx = -box.xx*box.size[1] + box.zx*box.size[3] |
113 | local frz = -box.xz*box.size[1] + box.zz*box.size[3] |
114 | |
115 | |
116 | table.insert(box2.points, { x+blx, y-box.size[2], z+blz } ) -- lower: lb |
117 | table.insert(box2.points, { x+brx, y-box.size[2], z+brz } ) -- rb |
118 | table.insert(box2.points, { x+frx, y-box.size[2], z+frz } ) -- rf |
119 | table.insert(box2.points, { x+flx, y-box.size[2], z+flz } ) -- lf |
120 | |
121 | table.insert(box2.points, { x+blx, y+box.size[2], z+blz } ) -- upper: lb |
122 | table.insert(box2.points, { x+brx, y+box.size[2], z+brz } ) |
123 | table.insert(box2.points, { x+frx, y+box.size[2], z+frz } ) |
124 | table.insert(box2.points, { x+flx, y+box.size[2], z+flz } ) |
125 | |
126 | table.insert(box2.points, { x, y, z } ) |
127 | |
128 | table.insert(self.maxTurningSizeBoxes2, box2) |
129 | end |
130 | self.maxTurningSizeBoxes = {} |
131 | end |
132 | |
133 | if self.maxTurningSizeBoxes2 ~= nil then |
134 | for _,box2 in pairs(self.maxTurningSizeBoxes2) do |
135 | local p = box2.points |
136 | local c = box2.color |
137 | |
138 | -- bottom |
139 | drawDebugLine(p[1][1],p[1][2],p[1][3], c[1],c[2],c[3], p[2][1],p[2][2],p[2][3], c[1],c[2],c[3]) |
140 | drawDebugLine(p[2][1],p[2][2],p[2][3], c[1],c[2],c[3], p[3][1],p[3][2],p[3][3], c[1],c[2],c[3]) |
141 | drawDebugLine(p[3][1],p[3][2],p[3][3], c[1],c[2],c[3], p[4][1],p[4][2],p[4][3], c[1],c[2],c[3]) |
142 | drawDebugLine(p[4][1],p[4][2],p[4][3], c[1],c[2],c[3], p[1][1],p[1][2],p[1][3], c[1],c[2],c[3]) |
143 | -- top |
144 | drawDebugLine(p[5][1],p[5][2],p[5][3], c[1],c[2],c[3], p[6][1],p[6][2],p[6][3], c[1],c[2],c[3]) |
145 | drawDebugLine(p[6][1],p[6][2],p[6][3], c[1],c[2],c[3], p[7][1],p[7][2],p[7][3], c[1],c[2],c[3]) |
146 | drawDebugLine(p[7][1],p[7][2],p[7][3], c[1],c[2],c[3], p[8][1],p[8][2],p[8][3], c[1],c[2],c[3]) |
147 | drawDebugLine(p[8][1],p[8][2],p[8][3], c[1],c[2],c[3], p[5][1],p[5][2],p[5][3], c[1],c[2],c[3]) |
148 | -- left |
149 | drawDebugLine(p[1][1],p[1][2],p[1][3], c[1],c[2],c[3], p[5][1],p[5][2],p[5][3], c[1],c[2],c[3]) |
150 | drawDebugLine(p[4][1],p[4][2],p[4][3], c[1],c[2],c[3], p[8][1],p[8][2],p[8][3], c[1],c[2],c[3]) |
151 | -- right |
152 | drawDebugLine(p[2][1],p[2][2],p[2][3], c[1],c[2],c[3], p[6][1],p[6][2],p[6][3], c[1],c[2],c[3]) |
153 | drawDebugLine(p[3][1],p[3][2],p[3][3], c[1],c[2],c[3], p[7][1],p[7][2],p[7][3], c[1],c[2],c[3]) |
154 | -- center |
155 | p[9][2] = getTerrainHeightAtWorldPos(g_currentMission.terrainRootNode, p[9][1],p[9][2],p[9][3]) + 2 |
156 | drawDebugPoint(p[9][1],p[9][2],p[9][3], 1, 0, 0, 1) |
157 | |
158 | Utils.renderTextAtWorldPosition(p[9][1],p[9][2],p[9][3], self.strategyName, getCorrectTextSize(0.012), 0) |
159 | end |
160 | end |
161 | end |
162 | |
163 | end |
164 | |
165 | end |