OpenGothic
Open source reimplementation of Gothic I and II
Loading...
Searching...
No Matches
waymatrix.cpp
Go to the documentation of this file.
1#include "waymatrix.h"
2
3#include <Tempest/Log>
4#include <algorithm>
5#include <limits>
6
7#include "utils/dbgpainter.h"
9#include "world.h"
10
11using namespace Tempest;
12
13WayMatrix::WayMatrix(World &world, const zenkit::WayNet& dat)
14 :world(world) {
15
16 wayPoints.resize(dat.points.size());
17 for(size_t i=0; i<wayPoints.size(); ++i){
18 wayPoints[i] = WayPoint(*dat.points[i]);
19 }
20
21 edges.resize(dat.edges.size());
22 for(size_t i=0; i<edges.size(); ++i){
23 WayEdge e = {};
24 e.a = size_t(std::distance(dat.points.begin(), std::find(dat.points.begin(), dat.points.end(), dat.edges[i].first )));
25 e.b = size_t(std::distance(dat.points.begin(), std::find(dat.points.begin(), dat.points.end(), dat.edges[i].second)));
26 edges[i] = e;
27 }
28
29 stk[0].reserve(256);
30 stk[1].reserve(256);
31 }
32
34 indexPoints.clear();
35 adjustWaypoints(wayPoints);
36 adjustWaypoints(freePoints);
37 adjustWaypoints(startPoints);
38 std::sort(indexPoints.begin(),indexPoints.end(),[](const WayPoint* a,const WayPoint* b){
39 return a->name<b->name;
40 });
41
42 for(auto& i:freePoints)
43 fpInd.push_back(&i);
44
45 std::sort(fpInd.begin(),fpInd.end(),[](const WayPoint* a,const WayPoint* b){
46 return a->pos.x < b->pos.x;
47 });
48
49 for(auto& i:edges) {
50 if(i.a<wayPoints.size() && i.b<wayPoints.size()) {
51 auto& a = wayPoints[i.a];
52 auto& b = wayPoints[i.b];
53
54 a.connect(b);
55 b.connect(a);
56 }
57 }
58
59 calculateLadderPoints();
60 }
61
62const WayPoint *WayMatrix::findWayPoint(const Vec3& at, const std::function<bool(const WayPoint&)>& filter) const {
63 const WayPoint* ret =nullptr;
64 float dist=std::numeric_limits<float>::max();
65 for(auto& w:wayPoints) {
66 if(!filter(w))
67 continue;
68 auto dp0 = at-w.position();
69 float l0 = dp0.quadLength();
70
71 if(l0<dist){
72 ret = &w;
73 dist = l0;
74 }
75 }
76 return ret;
77 }
78
79const WayPoint *WayMatrix::findFreePoint(const Vec3& at, std::string_view name, const std::function<bool(const WayPoint&)>& filter) const {
80 auto& index = findFpIndex(name);
81 return findFreePoint(at.x,at.y,at.z,index,filter);
82 }
83
84const WayPoint *WayMatrix::findNextPoint(const Vec3& at) const {
85 const WayPoint* ret = nullptr;
86 float dist = distanceThreshold;
87
88 dist*=dist;
89 for(auto pw:indexPoints){
90 auto& w = *pw;
91 auto dp = w.position()-at;
92 float l = dp.quadLength();
93
94 if(l<dist && !w.isLocked()) {
95 ret = &w;
96 dist = l;
97 }
98 }
99 return ret;
100 }
101
102void WayMatrix::addFreePoint(const Vec3& pos, const Vec3& dir, std::string_view name) {
103 freePoints.emplace_back(pos,dir,name,true);
104 }
105
106void WayMatrix::addStartPoint(const Vec3& pos, const Vec3& dir, std::string_view name) {
107 startPoints.emplace_back(pos,dir,name,true);
108 }
109
111 if(startPoints.size()>0)
112 return startPoints[0];
113
114 for(auto& i:freePoints)
115 if(i.name=="START_GOTHIC2")
116 return i;
117
118 for(auto& i:freePoints)
119 if(i.name=="START")
120 return i;
121
122 static WayPoint p(Vec3(),"START");
123 return p;
124 }
125
127 for(auto& i:indexPoints)
128 if(i->name=="TOT")
129 return *i;
130 static WayPoint p(Vec3(-1000,-1000,-1000),"TOT");
131 return p;
132 }
133
134const WayPoint* WayMatrix::findWayPoint(std::string_view name) const {
135 auto it = std::lower_bound(indexPoints.begin(),indexPoints.end(),name,[](const WayPoint* a, std::string_view b){
136 return a->name<b;
137 });
138 if(it!=indexPoints.end() && !(*it)->isFreePoint() && name==(*it)->name)
139 return *it;
140 return nullptr;
141 }
142
143const WayPoint* WayMatrix::findPoint(std::string_view name, bool inexact) const {
144 if(name.empty())
145 return nullptr;
146 for(size_t i=0; i<startPoints.size(); ++i) {
147 auto& sp = startPoints[startPoints.size()-1-i];
148 if(name==sp.name)
149 return &sp;
150 }
151 auto it = std::lower_bound(indexPoints.begin(),indexPoints.end(),name,[](const WayPoint* a, std::string_view b){
152 return a->name<b;
153 });
154 if(it!=indexPoints.end() && name==(*it)->name)
155 return *it;
156 if(!inexact)
157 return nullptr;
158 for(auto i:indexPoints)
159 if(i->checkName(name))
160 return i;
161 return nullptr;
162 }
163
165 static bool ddraw=false;
166 if(!ddraw)
167 return;
168 static bool fp = true;
169 static bool sp = false;
170 auto *ppoints = &wayPoints;
171 if (fp)
172 ppoints = &freePoints;
173 if (sp)
174 ppoints = &startPoints;
175
176 auto &points = *ppoints;
177 int id = 0;
178 for(auto& i:points) {
179 float x = i.pos.x, y = i.pos.y, z = i.pos.z;
180 p.mvp.project(x,y,z);
181 if(z<0.f || z>1.f)
182 continue;
183
184 x = (0.5f*x+0.5f)*float(p.w);
185 y = (0.5f*y+0.5f)*float(p.h);
186
187 p.setBrush(Tempest::Color(1,0,0,1));
188 p.painter.drawRect(int(x),int(y),4,4);
189
190 p.drawText(int(x),int(y),(i.name+" #"+std::to_string(id)).c_str());
191 ++id;
192 }
193 }
194
195void WayMatrix::adjustWaypoints(std::vector<WayPoint> &wp) {
196 for(auto& w:wp) {
197 auto ray = world.physic()->landRay(w.position());
198 if(ray.hasCol) {
199 //NOTE: probably doesn't match original game and need to be removed
200 w.pos.y = ray.v.y;
201 }
202 indexPoints.push_back(&w);
203 }
204 }
205
206void WayMatrix::calculateLadderPoints() {
207 static const float dist = 100.f;
208 for(uint32_t i=0;;++i) {
209 auto inter = world.mobsiById(i);
210 if(inter==nullptr)
211 break;
212 if(!inter->isLadder())
213 continue;
214 auto box = inter->bBox();
215 for(auto& e:edges) {
216 if(e.a>=wayPoints.size() || e.b>=wayPoints.size() || e.a==e.b)
217 continue;
218 auto& a = wayPoints[e.a], b = wayPoints[e.b];
219 Vec3 posA = a.position(), posB = b.position();
220 Vec3 dTopA = box[1] - posA;
221 Vec3 dBotA = box[0] - posA;
222 Vec3 dBA = posB - posA;
223 if(dBA.x<0)
224 std::swap(dTopA.x,dBotA.x);
225 if(dBA.y<0)
226 std::swap(dTopA.y,dBotA.y);
227 if(dBA.z<0)
228 std::swap(dTopA.z,dBotA.z);
229 float max = std::min({dTopA.x/dBA.x,dTopA.y/dBA.y,dTopA.z/dBA.z});
230 float min = std::max({dBotA.x/dBA.x,dBotA.y/dBA.y,dBotA.z/dBA.z});
231 if(max<min || max<0.f || min>1.f)
232 continue;
233 float dy = 0.5f * std::abs(box[0].y+box[1].y-posA.y-posB.y);
234 if(dy<dist) {
235 wayPoints[e.a].ladder = inter;
236 wayPoints[e.b].ladder = inter;
237 break;
238 }
239 }
240 }
241 }
242
243const WayMatrix::FpIndex &WayMatrix::findFpIndex(std::string_view name) const {
244 auto it = std::lower_bound(fpIndex.begin(),fpIndex.end(),name,[](FpIndex& l, std::string_view r){
245 return l.key<r;
246 });
247 if(it!=fpIndex.end() && it->key==name){
248 return *it;
249 }
250
251 FpIndex id;
252 id.key = name;
253 for(auto& w:freePoints){
254 if(!w.checkName(name))
255 continue;
256 id.index.push_back(&w);
257 }
258 // TODO: good index, not sort by 'x' :)
259 std::sort(id.index.begin(),id.index.end(),[](const WayPoint* a,const WayPoint* b){
260 return a->pos.x < b->pos.x;
261 });
262
263 it = fpIndex.insert(it,std::move(id));
264 return *it;
265 }
266
267const WayPoint *WayMatrix::findFreePoint(float x, float y, float z, const FpIndex& ind,
268 const std::function<bool(const WayPoint&)>& filter) const {
269 float R = distanceThreshold;
270 auto b = std::lower_bound(ind.index.begin(),ind.index.end(), x-R ,[](const WayPoint *a, float b){
271 return a->pos.x < b;
272 });
273 auto e = std::upper_bound(ind.index.begin(),ind.index.end(), x+R ,[](float a,const WayPoint *b){
274 return a < b->pos.x;
275 });
276
277 const WayPoint* ret=nullptr;
278 auto count = std::distance(b,e);(void) count;
279 float dist = R*R;
280 for(auto i=b;i!=e;++i){
281 auto& w = **i;
282 if(!w.isFreePoint())
283 continue;
284 float dx = w.pos.x-x;
285 float dy = w.pos.y-y;
286 float dz = w.pos.z-z;
287 float l = dx*dx+dy*dy+dz*dz;
288 if(l>dist)
289 continue;
290 if(!filter(w))
291 continue;
292 ret = &w;
293 dist = l;
294 }
295 return ret;
296 }
297
298WayPath WayMatrix::wayTo(const WayPoint** begin, size_t beginSz, const Tempest::Vec3 exactBegin, const WayPoint& end) const {
299 if(beginSz==0)
300 return WayPath();
301
302 intptr_t endId = std::distance<const WayPoint*>(&wayPoints[0],&end);
303 if(endId<0 || size_t(endId)>=wayPoints.size()){
304 if(!end.isConnected()) {
305 // free-point
306 WayPath ret;
307 ret.add(end);
308 return ret;
309 }
310 return WayPath();
311 }
312
313 pathGen++;
314 if(pathGen==1){
315 // new cycle
316 for(auto& i:wayPoints)
317 i.pathGen=0;
318 }
319
320 std::vector<const WayPoint*> *front=&stk[0], *back=&stk[1];
321 stk[0].clear();
322 stk[1].clear();
323
324 end.pathLen = 0;
325 end.pathGen = pathGen;
326 front->push_back(&end);
327
328 while(front->size()>0) {
329 bool done = true;
330 for(size_t i=0; i<beginSz; ++i)
331 if(begin[i]->pathGen!=pathGen) {
332 done = false;
333 break;
334 }
335 if(done)
336 break;
337
338 for(auto& wp:*front) {
339 int32_t l0 = wp->pathLen;
340
341 for(auto i:wp->connections()){
342 auto& w = *i.point;
343 int32_t l1 = l0+i.len;
344 if(w.pathGen!=pathGen || w.pathLen>l1){
345 w.pathLen = l1;
346 w.pathGen = pathGen;
347 back->push_back(&w);
348 }
349 }
350 }
351 std::swap(front,back);
352 back->clear();
353 }
354
355 const WayPoint* first = begin[0];
356 for(size_t i=0; i<beginSz; ++i) {
357 int32_t iLen = begin[i]->pathLen + int((exactBegin - begin[i]->position()).length());
358 int32_t fLen = first ->pathLen + int((exactBegin - first ->position()).length());
359 if(iLen<fLen)
360 first = begin[i];
361 }
362
363 WayPath ret;
364 ret.add(*first);
365 const WayPoint* current = first;
366 while(current!=&end) {
367 int32_t l0 = current->pathLen, l1=l0;
368
369 const WayPoint* next=nullptr;
370 for(auto i:current->connections()){
371 if(i.point->pathGen==pathGen && i.point->pathLen+i.len<=l0 && i.point->pathLen<l1){
372 next = i.point;
373 l1 = i.point->pathLen;
374 }
375 }
376 if(next==nullptr)
377 return WayPath();
378 ret.add(*next);
379 current=next;
380 }
381
382 ret.reverse();
383 return ret;
384 }
void setBrush(const Tempest::Brush &brush)
void drawText(int x, int y, std::string_view txt)
const int w
Definition dbgpainter.h:20
Tempest::Painter & painter
Definition dbgpainter.h:18
const int h
Definition dbgpainter.h:21
const Tempest::Matrix4x4 mvp
Definition dbgpainter.h:19
RayLandResult landRay(const Tempest::Vec3 &from, float maxDy=0) const
auto bBox() const -> const Tempest::Vec3 *
const WayPoint * findNextPoint(const Tempest::Vec3 &at) const
Definition waymatrix.cpp:84
const WayPoint & startPoint() const
void buildIndex()
Definition waymatrix.cpp:33
void addFreePoint(const Tempest::Vec3 &pos, const Tempest::Vec3 &dir, std::string_view name)
const WayPoint * findFreePoint(const Tempest::Vec3 &at, std::string_view name, const std::function< bool(const WayPoint &)> &filter) const
const WayPoint * findPoint(std::string_view name, bool inexact) const
void addStartPoint(const Tempest::Vec3 &pos, const Tempest::Vec3 &dir, std::string_view name)
const WayPoint * findWayPoint(const Tempest::Vec3 &at, const std::function< bool(const WayPoint &)> &filter) const
WayMatrix(World &owner, const zenkit::WayNet &dat)
Definition waymatrix.cpp:13
const WayPoint & deadPoint() const
WayPath wayTo(const WayPoint **begin, size_t beginSz, const Tempest::Vec3 exactBegin, const WayPoint &end) const
void marchPoints(DbgPainter &p) const
void add(const WayPoint &p)
Definition waypath.h:15
void reverse()
Definition waypath.cpp:29
bool isConnected() const
Definition waypoint.cpp:38
const std::vector< Conn > & connections() const
Definition waypoint.h:50
void connect(WayPoint &w)
Definition waypoint.cpp:67
uint16_t pathGen
Definition waypoint.h:45
int32_t pathLen
Definition waypoint.h:44
Definition world.h:31
DynamicWorld * physic() const
Definition world.h:81
Interactive * mobsiById(uint32_t id)
Definition world.cpp:218