!XT (Code) Update copyright headers in Code/Sandbox.
[CRYENGINE.git] / Code / Sandbox / EditorQt / Objects / RoadObject.cpp
blob95db357dd005865b884cded1d90116460f32fe57
1 // Copyright 2001-2018 Crytek GmbH / Crytek Group. All rights reserved.
3 #include "StdAfx.h"
4 #include "RoadObject.h"
5 #include "Viewport.h"
6 #include "Terrain/Heightmap.h"
7 #include "Material/Material.h"
8 #include "../Vegetation/VegetationMap.h"
9 #include "../Vegetation/VegetationObject.h"
10 #include "Objects/ObjectLoader.h"
11 #include "Objects/InspectorWidgetCreator.h"
12 #include <Cry3DEngine/I3DEngine.h>
13 #include <CryCore/Containers/CryArray.h>
14 #include "Util/MFCUtil.h"
15 #include "Serialization/Decorators/EditorActionButton.h"
16 #include <Preferences/ViewportPreferences.h>
18 #include "CryEditDoc.h"
20 //////////////////////////////////////////////////////////////////////////
21 // class CRoadSector
23 void CRoadSector::Release()
25 if (m_pRoadSector)
26 GetIEditorImpl()->Get3DEngine()->DeleteRenderNode(m_pRoadSector);
27 m_pRoadSector = 0;
30 //////////////////////////////////////////////////////////////////////////
31 // CRoadObject implementation.
32 //////////////////////////////////////////////////////////////////////////
33 IMPLEMENT_DYNCREATE(CRoadObject, CSplineObject)
35 #define RAY_DISTANCE 100000.0f
37 //////////////////////////////////////////////////////////////////////////
38 CRoadObject::CRoadObject()
40 m_bNeedUpdateSectors = true;
41 mv_width = 4.0f;
42 mv_borderWidth = 6.0f;
43 mv_eraseVegWidth = 3.f;
44 mv_eraseVegWidthVar = 0.f;
45 mv_step = 4.0f;
46 mv_tileLength = 4.0f;
47 mv_sortPrio = 0;
48 m_ignoreTerrainHoles = false;
49 m_physicalize = false;
51 m_bIgnoreParamUpdate = false;
53 SetColor(CMFCUtils::Vec2Rgb(Vec3(0, 0.8f, 1)));
54 mv_ratioViewDist = 100;
57 //////////////////////////////////////////////////////////////////////////
58 void CRoadObject::Done()
60 m_sectors.clear();
61 __super::Done();
64 //////////////////////////////////////////////////////////////////////////
65 void CRoadObject::InitBaseVariables()
67 if (m_pVarObject == nullptr)
69 m_pVarObject = stl::make_unique<CVarObject>();
72 m_pVarObject->AddVariable(mv_width, "Width", functor(*this, &CRoadObject::OnParamChange));
73 m_pVarObject->AddVariable(mv_borderWidth, "BorderWidth", functor(*this, &CRoadObject::OnParamChange));
74 m_pVarObject->AddVariable(mv_eraseVegWidth, "EraseVegWidth");
75 m_pVarObject->AddVariable(mv_eraseVegWidthVar, "EraseVegWidthVar");
76 m_pVarObject->AddVariable(mv_step, "StepSize", functor(*this, &CRoadObject::OnParamChange));
77 m_pVarObject->AddVariable(mv_ratioViewDist, "ViewDistRatio", functor(*this, &CRoadObject::OnParamChange));
78 mv_ratioViewDist.SetLimits(0, 255);
79 m_pVarObject->AddVariable(mv_tileLength, "TileLength", functor(*this, &CRoadObject::OnParamChange));
80 mv_step.SetLimits(0.25f, 10.f);
81 mv_tileLength.SetLimits(0.001f, 1000.f);
84 //////////////////////////////////////////////////////////////////////////
85 void CRoadObject::InitVariables()
87 InitBaseVariables();
89 m_pVarObject->AddVariable(mv_sortPrio, "SortPriority", functor(*this, &CRoadObject::OnParamChange));
90 mv_sortPrio.SetLimits(0, 255);
92 m_pVarObject->AddVariable(m_ignoreTerrainHoles, "IgnoreTerrainHoles", functor(*this, &CRoadObject::OnParamChange));
93 m_pVarObject->AddVariable(m_physicalize, "Physicalize", functor(*this, &CRoadObject::OnParamChange));
96 //////////////////////////////////////////////////////////////////////////
97 void CRoadObject::InvalidateTM(int nWhyFlags)
99 __super::InvalidateTM(nWhyFlags);
100 SetRoadSectors();
103 //////////////////////////////////////////////////////////////////////////
104 void CRoadObject::CreateInspectorWidgets(CInspectorWidgetCreator& creator)
106 CSplineObject::CreateInspectorWidgets(creator);
108 creator.AddPropertyTree<CRoadObject>("Road", [](CRoadObject* pObject, Serialization::IArchive& ar, bool bMultiEdit)
110 pObject->m_pVarObject->SerializeVariable(&pObject->mv_width, ar);
111 pObject->m_pVarObject->SerializeVariable(&pObject->mv_borderWidth, ar);
112 pObject->m_pVarObject->SerializeVariable(&pObject->mv_eraseVegWidth, ar);
113 pObject->m_pVarObject->SerializeVariable(&pObject->mv_eraseVegWidthVar, ar);
114 pObject->m_pVarObject->SerializeVariable(&pObject->mv_step, ar);
115 pObject->m_pVarObject->SerializeVariable(&pObject->mv_ratioViewDist, ar);
116 pObject->m_pVarObject->SerializeVariable(&pObject->mv_tileLength, ar);
118 if (ar.openBlock("operators", "<operators"))
120 ar(Serialization::ActionButton(std::bind(&CRoadObject::AlignHeightMap, pObject)), "align_heightmap", "^Align Height Map");
121 ar(Serialization::ActionButton(std::bind(&CRoadObject::EraseVegetation, pObject)), "erase_vegetation", "^Erase Vegetation");
122 ar.closeBlock();
127 //////////////////////////////////////////////////////////////////////////
128 float CRoadObject::GetLocalWidth(int index, float t)
130 float kof = t;
131 int i = index;
133 if (index >= m_points.size() - 1)
135 kof = 1.0f + t;
136 i = m_points.size() - 2;
137 if (i < 0)
138 return mv_width;
141 float an1 = m_points[i].isDefaultWidth ? mv_width : m_points[i].width;
142 float an2 = m_points[i + 1].isDefaultWidth ? mv_width : m_points[i + 1].width;
144 if (an1 == an2)
145 return an1;
147 float af = kof * 2 - 1.0f;
148 float ed = 1.0f;
149 if (af < 0.0f)
150 ed = -1.0f;
151 //af = ed-af;
152 af = af;
153 //af = ed-af;
154 af = (af + 1.0f) / 2;
155 return ((1.0f - af) * an1 + af * an2);
158 //////////////////////////////////////////////////////////////////////////
159 void CRoadObject::OnUpdate()
161 SetRoadSectors();
164 //////////////////////////////////////////////////////////////////////////
165 void CRoadObject::SetRoadSectors()
167 const Matrix34& wtm = GetWorldTM();
169 int sizeOld = m_sectors.size();
170 int sizeNew = 0;
172 int points_size = m_points.size();
174 float fSegLen = 0.0f;
176 for (int i = 0; i < points_size - 1; ++i)
178 int kn = GetRoadSectorCount(i);
180 for (int k = 0; k <= kn; ++k)
182 if (i != points_size - 2 && k == kn)
183 break;
184 float t = float(k) / kn;
185 Vec3 p = GetBezierPos(i, t);
186 Vec3 n = GetLocalBezierNormal(i, t);
188 float fWidth = GetLocalWidth(i, t);
190 Vec3 r = p - 0.5f * fWidth * n;
191 Vec3 l = p + 0.5f * fWidth * n;
193 if (sizeNew >= sizeOld)
195 CRoadSector sector;
196 m_sectors.push_back(sector);
199 m_sectors[sizeNew].points.clear();
200 m_sectors[sizeNew].points.push_back(l);
201 m_sectors[sizeNew].points.push_back(r);
202 m_sectors[sizeNew].points.push_back(l);
203 m_sectors[sizeNew].points.push_back(r);
205 m_sectors[sizeNew].t0 = (fSegLen + GetBezierSegmentLength(i, t)) / mv_tileLength;
207 sizeNew++;
209 fSegLen += GetBezierSegmentLength(i);
212 if (sizeNew < sizeOld)
213 m_sectors.resize(sizeNew);
215 for (size_t i = 0; i < m_sectors.size(); ++i)
217 m_sectors[i].points[0] = wtm.TransformPoint(m_sectors[i].points[0]);
218 m_sectors[i].points[1] = wtm.TransformPoint(m_sectors[i].points[1]);
220 if (i > 0)
222 // adjust mesh
223 m_sectors[i - 1].points[2] = m_sectors[i].points[0];
224 m_sectors[i - 1].points[3] = m_sectors[i].points[1];
225 m_sectors[i - 1].t1 = m_sectors[i].t0;
229 if (m_sectors.size() > 0)
230 m_sectors.pop_back();
232 // mark end of the road for road alpha fading
233 if (m_sectors.size() > 0)
234 m_sectors[m_sectors.size() - 1].t1 *= -1.f;
236 // call only in the end of this function
237 UpdateSectors();
240 //////////////////////////////////////////////////////////////////////////
241 int CRoadObject::GetRoadSectorCount(int index)
243 int kn = int((GetBezierSegmentLength(index) + 0.5f) / GetStepSize());
244 if (kn == 0)
245 return 1;
246 return kn;
249 //////////////////////////////////////////////////////////////////////////
250 void CRoadObject::UpdateSectors()
252 if (!m_bNeedUpdateSectors)
253 return;
255 m_mergeIndex = -1;
257 if (!m_sectors.size())
258 return;
260 for (size_t i = 0; i < m_sectors.size(); ++i)
262 if (m_sectors[i].m_pRoadSector)
263 GetIEditorImpl()->Get3DEngine()->DeleteRenderNode(m_sectors[i].m_pRoadSector);
264 m_sectors[i].m_pRoadSector = 0;
267 int MAX_TRAPEZOIDS_IN_CHUNK = 16;
268 int nChunksNum = m_sectors.size() / MAX_TRAPEZOIDS_IN_CHUNK + 1;
270 CRoadSector& sectorFirstGlobal = m_sectors[0];
271 CRoadSector& sectorLastGlobal = m_sectors[m_sectors.size() - 1];
273 for (int nChunkId = 0; nChunkId < nChunksNum; ++nChunkId)
275 int nStartSecId = nChunkId * MAX_TRAPEZOIDS_IN_CHUNK;
276 int nSectorsNum = min(MAX_TRAPEZOIDS_IN_CHUNK, (int)m_sectors.size() - nStartSecId);
278 if (!nSectorsNum)
279 break;
281 CRoadSector& sectorFirst = m_sectors[nStartSecId];
283 // make render node
284 if (!sectorFirst.m_pRoadSector)
285 sectorFirst.m_pRoadSector = GetIEditorImpl()->Get3DEngine()->CreateRenderNode(eERType_Road);
286 if (!sectorFirst.m_pRoadSector)
287 return;
288 int renderFlags = 0;
289 if (CheckFlags(OBJFLAG_INVISIBLE) || IsHiddenBySpec())
290 renderFlags = ERF_HIDDEN;
291 sectorFirst.m_pRoadSector->SetRndFlags(renderFlags);
292 sectorFirst.m_pRoadSector->SetViewDistRatio(mv_ratioViewDist);
293 sectorFirst.m_pRoadSector->SetMinSpec(GetMinSpec());
294 sectorFirst.m_pRoadSector->SetMaterialLayers(GetMaterialLayersMask());
295 sectorFirst.m_pRoadSector->SetEditorObjectId(GetId().hipart >> 32);
297 // make list of verts
298 PodArray<Vec3> lstPoints;
299 for (int i = 0; i < nSectorsNum; ++i)
301 lstPoints.Add(m_sectors[nStartSecId + i].points[0]);
302 lstPoints.Add(m_sectors[nStartSecId + i].points[1]);
305 CRoadSector& sectorLast = m_sectors[nStartSecId + nSectorsNum - 1];
307 // Extend final boundary to cover holes in roads caused by f16 meshes.
308 // Overlapping the roads slightly seems to be the nicest way to fix the issue
309 Vec3 sectorLastOffset2 = sectorLast.points[2] - sectorLast.points[0];
310 Vec3 sectorLastOffset3 = sectorLast.points[3] - sectorLast.points[1];
312 sectorLastOffset2.Normalize();
313 sectorLastOffset3.Normalize();
315 const float sectorLastOffset = 0.075f;
316 sectorLastOffset2 *= sectorLastOffset;
317 sectorLastOffset3 *= sectorLastOffset;
319 lstPoints.Add(sectorLast.points[2] + sectorLastOffset2);
320 lstPoints.Add(sectorLast.points[3] + sectorLastOffset3);
322 //assert(lstPoints.Count()>=4);
323 assert(!(lstPoints.Count() & 1));
325 IRoadRenderNode* pRoadRN = static_cast<IRoadRenderNode*>(sectorFirst.m_pRoadSector);
326 pRoadRN->SetVertices(lstPoints.GetElements(), lstPoints.Count(), fabs(sectorFirst.t0), fabs(sectorLast.t1), fabs(sectorFirstGlobal.t0), fabs(sectorLastGlobal.t1));
327 pRoadRN->SetSortPriority(mv_sortPrio);
328 pRoadRN->SetIgnoreTerrainHoles(m_ignoreTerrainHoles);
329 pRoadRN->SetPhysicalize(m_physicalize);
331 if (GetMaterial())
332 ((CMaterial*)GetMaterial())->AssignToEntity(sectorFirst.m_pRoadSector);
333 else
334 sectorFirst.m_pRoadSector->SetMaterial(NULL);
338 void CRoadObject::OnEvent(ObjectEvent event)
340 if (event == EVENT_CONFIG_SPEC_CHANGE)
342 m_bNeedUpdateSectors = true;
343 UpdateSectors();
345 __super::OnEvent(event);
348 //////////////////////////////////////////////////////////////////////////
349 void CRoadObject::SetHidden(bool bHidden)
351 __super::SetHidden(bHidden);
352 UpdateSectors();
355 //////////////////////////////////////////////////////////////////////////
356 void CRoadObject::UpdateVisibility(bool visible)
358 if (visible == CheckFlags(OBJFLAG_INVISIBLE))
360 __super::UpdateVisibility(visible);
361 UpdateSectors();
365 //////////////////////////////////////////////////////////////////////////
367 void CRoadObject::RegisterOnEngine()
369 for (CRoadSectorVector::size_type i = 0; i < m_sectors.size(); ++i)
371 if (m_sectors[i].m_pRoadSector)
373 GetIEditorImpl()->Get3DEngine()->RegisterEntity(m_sectors[i].m_pRoadSector);
378 //////////////////////////////////////////////////////////////////////////
380 void CRoadObject::UnRegisterFromEngine()
382 for (CRoadSectorVector::size_type i = 0; i < m_sectors.size(); ++i)
384 if (m_sectors[i].m_pRoadSector)
386 GetIEditorImpl()->Get3DEngine()->UnRegisterEntityAsJob(m_sectors[i].m_pRoadSector);
391 //////////////////////////////////////////////////////////////////////////
392 void CRoadObject::SetMaterial(IEditorMaterial* pMaterial)
394 CMaterial* pPrevMaterial = (CMaterial*)GetMaterial();
395 __super::SetMaterial(pMaterial);
397 if (pPrevMaterial != pMaterial)
398 UpdateSectors();
401 //////////////////////////////////////////////////////////////////////////
402 void CRoadObject::DrawSectorLines(DisplayContext& dc)
404 const Matrix34& wtm = GetWorldTM();
405 float fPointSize = 0.5f;
407 dc.SetColor(RGB(127, 127, 255));
408 for (size_t i = 0; i < m_sectors.size(); ++i)
410 dc.DrawLine(m_sectors[i].points[0], m_sectors[i].points[1]);
411 for (size_t k = 0; k < m_sectors[i].points.size(); k += 2)
413 if (k + 3 < m_sectors[i].points.size())
415 dc.DrawLine(m_sectors[i].points[k + 1], m_sectors[i].points[k + 3]);
416 dc.DrawLine(m_sectors[i].points[k + 3], m_sectors[i].points[k + 2]);
417 dc.DrawLine(m_sectors[i].points[k + 2], m_sectors[i].points[k]);
423 //////////////////////////////////////////////////////////////////////////
424 void CRoadObject::DrawRoadObject(DisplayContext& dc, COLORREF col)
426 if (IsSelected())
428 DrawSectorLines(dc);
432 //////////////////////////////////////////////////////////////////////////
433 void CRoadObject::Display(CObjectRenderHelper& objRenderHelper)
435 DisplayContext& dc = objRenderHelper.GetDisplayContextRef();
437 if (!gViewportDebugPreferences.showRoadObjectHelper)
438 return;
440 COLORREF col = 0;
442 if (m_points.size() > 1)
444 if ((IsSelected() || IsHighlighted()))
446 col = dc.GetSelectedColor();
447 dc.SetColor(col);
449 else
451 if (IsFrozen())
452 dc.SetFreezeColor();
453 else
454 dc.SetColor(GetColor());
455 col = GetColor();
458 DrawRoadObject(dc, col);
460 __super::Display(objRenderHelper);
463 //////////////////////////////////////////////////////////////////////////
464 void CRoadObject::Serialize(CObjectArchive& ar)
466 m_bIgnoreParamUpdate = true;
467 m_bNeedUpdateSectors = false;
468 __super::Serialize(ar);
469 m_bNeedUpdateSectors = true;
470 m_bIgnoreParamUpdate = false;
471 if (ar.bLoading)
472 UpdateSectors();
475 //////////////////////////////////////////////////////////////////////////
476 XmlNodeRef CRoadObject::Export(const string& levelPath, XmlNodeRef& xmlNode)
478 //XmlNodeRef objNode = __super::Export( levelPath,xmlNode );
479 //return objNode;
480 return 0;
483 //////////////////////////////////////////////////////////////////////////
484 void CRoadObject::OnParamChange(IVariable* var)
486 if (!m_bIgnoreParamUpdate)
487 SetRoadSectors();
490 //////////////////////////////////////////////////////////////////////////
491 void CRoadObject::SetSelected(bool bSelect)
493 __super::SetSelected(bSelect);
494 SetRoadSectors();
497 //////////////////////////////////////////////////////////////////////////
498 void CRoadObject::AlignHeightMap()
500 if (!GetIEditorImpl()->GetIUndoManager()->IsUndoRecording())
501 GetIEditorImpl()->GetIUndoManager()->Begin();
503 CHeightmap* heightmap = GetIEditorImpl()->GetHeightmap();
504 float unitSize = heightmap->GetUnitSize();
505 const Matrix34& wtm = GetWorldTM();
507 int minx = 0, miny = 0, maxx = 0, maxy = 0;
508 bool bIsInitMinMax = false;
510 const int nPoints = GetPointCount();
512 for (int i = 0; i < nPoints - 1; ++i)
514 float fminx = 0, fminy = 0, fmaxx = 0, fmaxy = 0;
515 bool bIsInitminmax = false;
516 int kn = int (0.5f + (GetBezierSegmentLength(i) + 1) * 4);
518 Vec3 tmp;
520 for (int k = 0; k <= kn; ++k)
522 float t = float(k) / kn;
523 Vec3 p = GetBezierPos(i, t);
524 Vec3 n = GetLocalBezierNormal(i, t);
526 float fWidth = GetLocalWidth(i, t);
527 if (fWidth < 2)
528 fWidth = 2;
530 Vec3 p1 = p - (0.5f * fWidth + mv_borderWidth + 0.5f * unitSize) * n;
531 Vec3 p2 = p + (0.5f * fWidth + mv_borderWidth + 0.5f * unitSize) * n;
533 p1 = wtm.TransformPoint(p1);
534 p2 = wtm.TransformPoint(p2);
536 tmp = p1;
538 if (!bIsInitminmax)
540 fminx = p1.x;
541 fminy = p1.y;
542 fmaxx = p1.x;
543 fmaxy = p1.y;
544 bIsInitminmax = true;
546 fminx = min(fminx, p1.x);
547 fminx = min(fminx, p2.x);
548 fminy = min(fminy, p1.y);
549 fminy = min(fminy, p2.y);
550 fmaxx = max(fmaxx, p1.x);
551 fmaxx = max(fmaxx, p2.x);
552 fmaxy = max(fmaxy, p1.y);
553 fmaxy = max(fmaxy, p2.y);
556 heightmap->RecordUndo(int(fminy / unitSize) - 1, int(fminx / unitSize) - 1, int(fmaxy / unitSize) + unitSize - int(fminy / unitSize) + 1, int(fmaxx / unitSize) + unitSize - int(fminx / unitSize) + 1);
558 for (int ty = int(fminx / unitSize); ty <= int(fmaxx / unitSize) + unitSize; ++ty)
559 for (int tx = int(fminy / unitSize); tx <= int(fmaxy / unitSize) + unitSize; ++tx)
561 int x = ty * unitSize;
562 int y = tx * unitSize;
564 Vec3 p3 = Vec3(x, y, 0.0f);
566 int findk = -1;
567 float fWidth = GetLocalWidth(i, 0);
568 float fWidth1 = GetLocalWidth(i, 1);
569 if (fWidth < fWidth1)
570 fWidth = fWidth1;
571 float mind = 0.5f * fWidth + mv_borderWidth + 0.5f * unitSize;
573 for (int k = 0; k < kn; ++k)
575 float t = float(k) / kn;
576 Vec3 p1 = wtm.TransformPoint(GetBezierPos(i, t));
577 Vec3 p2 = wtm.TransformPoint(GetBezierPos(i, t + 1.0f / kn));
578 p1.z = 0.0f;
579 p2.z = 0.0f;
581 //Vec3 d = p2 - p1;
582 //float u = d.Dot(p3-p1) / (d).GetLengthSquared();
583 //if (-0.1f <= u && u <=1.1f)
585 float d = PointToLineDistance(p1, p2, p3);
586 if (d < mind)
588 findk = k;
589 mind = d;
594 if (findk != -1)
596 float t = float(findk) / kn;
598 float st = 1.0f / kn;
599 int sign = 1;
600 for (int tt = 0; tt < 24; ++tt)
602 Vec3 p0 = wtm.TransformPoint(GetBezierPos(i, t));
604 p0.z = 0.0f;
606 Vec3 ploc = GetBezierPos(i, t);
607 Vec3 nloc = GetLocalBezierNormal(i, t);
609 Vec3 p1 = wtm.TransformPoint(ploc - nloc);
610 p1.z = 0.0f;
612 if (((p0 - p1).Cross(p3 - p1).z) < 0.0f)
613 sign = 1;
614 else
615 sign = -1;
617 st /= 1.618f;
618 t = t + sign * st;
621 if (t < 0.0f || t > 1.0f)
622 continue;
624 Vec3 p1 = wtm.TransformPoint(GetBezierPos(i, t));
625 Vec3 p2 = wtm.TransformPoint(GetBezierPos(i, t + 1.0f / kn));
627 Vec3 p1_0 = p1;
628 p1_0.z = 0.0f;
629 p2.z = 0.0f;
630 Vec3 p = Vec3(x, y, 0.0f);
631 Vec3 e = (p2 - p1_0).Cross(p1_0 - p);
633 Vec3 p1loc = GetBezierPos(i, t);
634 Vec3 nloc = GetLocalBezierNormal(i, t);
636 Vec3 n = wtm.TransformPoint(p1loc - nloc) - p1;
637 n.Normalize();
639 Vec3 nproj = n;
640 nproj.z = 0.0f;
642 float kof = nproj.GetLength();
644 float length = (p1_0 - p).GetLength() / kof;
646 Vec3 pos;
648 if (e.z < 0.0f)
649 pos = p1 - length * n;
650 else
651 pos = p1 + length * n;
653 float fWidth = GetLocalWidth(i, t);
654 if (length <= fWidth / 2 + mv_borderWidth + 0.5 * unitSize)
656 //int tx = pos_directed_rounding(y / unitSize);
657 //int ty = pos_directed_rounding(x / unitSize);
659 float z;
660 if (length <= fWidth / 2 + 0.5 * unitSize)
661 z = pos.z;
662 else
664 //continue;
665 float kof = (length - (fWidth / 2 + 0.5 * unitSize)) / mv_borderWidth;
666 kof = 1.0f - (cos(kof * 3.141593f) + 1.0f) / 2;
667 float z1 = heightmap->GetXY(tx, ty);
668 z = kof * z1 + (1.0f - kof) * pos.z;
671 heightmap->SetXY(tx, ty, clamp_tpl(z, 0.0f, heightmap->GetMaxHeight()));
673 if (!bIsInitMinMax)
675 minx = tx;
676 miny = ty;
677 maxx = tx;
678 maxy = ty;
679 bIsInitMinMax = true;
681 if (minx > tx) minx = tx;
682 if (miny > ty) miny = ty;
683 if (maxx < tx) maxx = tx;
684 if (maxy < ty) maxy = ty;
688 //break;
691 int w = maxx - minx;
692 if (w < maxy - miny) w = maxy - miny;
693 heightmap->UpdateEngineTerrain(minx, miny, w, w, CHeightmap::ETerrainUpdateType::Elevation);
695 if (GetIEditorImpl()->GetIUndoManager()->IsUndoRecording())
696 GetIEditorImpl()->GetIUndoManager()->Accept("Heightmap Aligning");
698 SetRoadSectors();
701 //////////////////////////////////////////////////////////////////////////
702 void CRoadObject::EraseVegetation()
704 if (CVegetationMap* vegetationMap = GetIEditorImpl()->GetVegetationMap())
706 if (!GetIEditorImpl()->GetIUndoManager()->IsUndoRecording())
707 GetIEditorImpl()->GetIUndoManager()->Begin();
709 bool bModified = false;
711 vegetationMap->StoreBaseUndo(CVegetationMap::eStoreUndo_Begin);
713 const Matrix34& wtm = GetWorldTM();
714 const int nPoints = GetPointCount();
715 const float fEraseWidth = mv_eraseVegWidth + mv_eraseVegWidthVar * 0.5f;
716 const float fSpareWidth = 5.0f;
718 for (int i = 0; i < nPoints - 1; ++i)
720 const int nSegment = GetBezierSegmentLength(i);
721 const int nSector = GetRoadSectorCount(i);
723 for (int k = 0; k < nSector; ++k)
725 const float t1 = float(k) / nSector;
726 const float t2 = float(k + 1) / nSector;
728 const Vec3 p1 = GetBezierPos(i, t1);
729 const Vec3 n1 = GetLocalBezierNormal(i, t1);
730 const Vec3 p2 = GetBezierPos(i, t2);
731 const Vec3 n2 = GetLocalBezierNormal(i, t2);
733 const float fRoadWidth1 = GetLocalWidth(i, t1);
734 const float fRoadWidth2 = GetLocalWidth(i, t2);
736 const Vec3 r1(wtm.TransformPoint(p1 - (0.5f * fRoadWidth1 + fEraseWidth + fSpareWidth) * n1));
737 const Vec3 l1(wtm.TransformPoint(p1 + (0.5f * fRoadWidth1 + fEraseWidth + fSpareWidth) * n1));
738 const Vec3 r2(wtm.TransformPoint(p2 - (0.5f * fRoadWidth2 + fEraseWidth + fSpareWidth) * n2));
739 const Vec3 l2(wtm.TransformPoint(p2 + (0.5f * fRoadWidth2 + fEraseWidth + fSpareWidth) * n2));
741 const float fminx = min(r1.x, min(l1.x, min(r2.x, l2.x)));
742 const float fminy = min(r1.y, min(l1.y, min(r2.y, l2.y)));
743 const float fmaxx = max(r1.x, max(l1.x, max(r2.x, l2.x)));
744 const float fmaxy = max(r1.y, max(l1.y, max(r2.y, l2.y)));
746 std::vector<CVegetationInstance*> instances;
747 vegetationMap->GetObjectInstances(fminx, fminy, fmaxx, fmaxy, instances);
749 const int segmentStart = k * nSegment / nSector;
750 const int segmentEnd = (k + 1) * nSegment / nSector;
752 for (std::vector<CVegetationInstance*>::iterator it = instances.begin(); it != instances.end(); ++it)
754 float fFinalEraseWidth = fEraseWidth;
756 if (mv_eraseVegWidthVar != 0.f)
758 fFinalEraseWidth -= float(rand() % int(mv_eraseVegWidthVar * 10.f)) * 0.1f;
760 if (CVegetationObject* obj = (*it)->object)
762 fFinalEraseWidth += obj->GetObjectSize() * 0.5f;
764 for (int l = segmentStart; l < segmentEnd; ++l)
766 const float ts = float(l) / nSegment;
767 const float fWidth = 0.5f * GetLocalWidth(i, ts) + fFinalEraseWidth;
769 const Vec2 pos1(wtm.TransformPoint(GetBezierPos(i, ts)));
770 const Vec2 pos2((*it)->pos);
772 const float fDistance = pos1.GetDistance(pos2);
774 if (fDistance <= fWidth)
776 vegetationMap->StoreBaseUndo(CVegetationMap::eStoreUndo_Once);
777 vegetationMap->DeleteObjInstance(*it);
779 if (!bModified)
781 GetIEditorImpl()->SetModifiedFlag();
782 bModified = true;
784 break;
790 vegetationMap->StoreBaseUndo(CVegetationMap::eStoreUndo_End);
792 if (GetIEditorImpl()->GetIUndoManager()->IsUndoRecording())
794 if (bModified)
796 GetIEditorImpl()->GetIUndoManager()->Accept("Erase Vegetation On Road");
798 else
800 GetIEditorImpl()->GetIUndoManager()->Cancel();
806 //////////////////////////////////////////////////////////////////////////
807 void CRoadObject::SetMinSpec(uint32 nSpec, bool bSetChildren)
809 __super::SetMinSpec(nSpec, bSetChildren);
810 UpdateSectors();
813 //////////////////////////////////////////////////////////////////////////
814 void CRoadObject::SetMaterialLayersMask(uint32 nLayersMask)
816 __super::SetMaterialLayersMask(nLayersMask);
817 UpdateSectors();
820 //////////////////////////////////////////////////////////////////////////
821 void CRoadObject::SetLayerId(uint16 nLayerId)
823 for (size_t i = 0; i < m_sectors.size(); ++i)
825 if (m_sectors[i].m_pRoadSector)
826 m_sectors[i].m_pRoadSector->SetLayerId(nLayerId);
830 void CRoadObject::UpdateHighlightPassState(bool bSelected, bool bHighlighted)
832 for (size_t i = 0; i < m_sectors.size(); ++i)
834 if (m_sectors[i].m_pRoadSector)
835 m_sectors[i].m_pRoadSector->SetEditorObjectInfo(bSelected, bHighlighted);
840 //////////////////////////////////////////////////////////////////////////
841 void CRoadObject::SetPhysics(bool isPhysics)
843 for (size_t i = 0; i < m_sectors.size(); ++i)
845 IRenderNode* pRenderNode = m_sectors[i].m_pRoadSector;
846 if (pRenderNode)
848 if (isPhysics)
849 pRenderNode->SetRndFlags(pRenderNode->GetRndFlags() & ~ERF_NO_PHYSICS);
850 else
851 pRenderNode->SetRndFlags(pRenderNode->GetRndFlags() | ERF_NO_PHYSICS);
856 //////////////////////////////////////////////////////////////////////////
857 // Class Description of RoadObject.
859 //////////////////////////////////////////////////////////////////////////
860 class CRoadObjectClassDesc : public CObjectClassDesc
862 public:
863 ObjectType GetObjectType() { return OBJTYPE_ROAD; };
864 const char* ClassName() { return "Road"; };
865 const char* Category() { return "Misc"; };
866 CRuntimeClass* GetRuntimeClass() { return RUNTIME_CLASS(CRoadObject); };
869 REGISTER_CLASS_DESC(CRoadObjectClassDesc);