1 // Ryzom - MMORPG Framework <http://dev.ryzom.com/projects/ryzom/>
2 // Copyright (C) 2010 Winch Gate Property Limited
4 // This program is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU Affero General Public License as
6 // published by the Free Software Foundation, either version 3 of the
7 // License, or (at your option) any later version.
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU Affero General Public License for more details.
14 // You should have received a copy of the GNU Affero General Public License
15 // along with this program. If not, see <http://www.gnu.org/licenses/>.
20 #include "patat_grid.h"
22 #include "nel/misc/aabbox.h"
23 #include "nel/misc/file.h"
24 #include "nel/misc/i_xml.h"
25 #include "nel/misc/path.h"
26 #include "nel/ligo/ligo_config.h"
29 using namespace NLMISC
;
30 using namespace NLLIGO
;
32 extern NLLIGO::CLigoConfig LigoConfig
;
37 CPatatGrid::CPatatGrid()
44 CPatatGrid::~CPatatGrid()
52 void CPatatGrid::init()
54 // allocate 1 free entry (for no zone entry)
56 _EntryTable
.resize(1);
63 nlinfo("Initialized CPatatGrid, move grid is 1024x1024 uint16 (%.2f Mb)", (float)(sizeof(TEntryIndex
)*1024*1024)/(1024.0f
*1024.0f
));
67 void CPatatGrid::readPrimitive(IPrimitive
*primitive
, std::vector
<uint32
> &inFile
)
69 if (dynamic_cast<CPrimZone
*>(primitive
) != NULL
)
74 if (primitive
->getPropertyByName("name", primName
) &&
75 primitive
->getPropertyByName("class", className
) &&
76 _PrimZoneFilters
.find(className
) != _PrimZoneFilters
.end())
78 _ZoneMap
.insert(TZoneMap::value_type(primName
, (sint32
)_PrimZones
.size()));
79 inFile
.push_back((uint32
)_PrimZones
.size());
80 _PrimZones
.push_back(static_cast<CPrimZone
&>(*primitive
));
81 // _PrimZones.back().Name = primName;
87 for (i
=0; i
<primitive
->getNumChildren(); ++i
)
91 if (!primitive
->getChild(child
, i
))
94 readPrimitive(child
, inFile
);
100 * Use patat prim file
102 void CPatatGrid::usePrim(const string
&primFile
, std::vector
<uint32
> &inFile
)
105 CIFile f(CPath::lookup(primFile));
111 nlinfo("Loaded prim file '%s'", primFile.c_str());
114 uint firstPrimZone = _PrimZones.size();
117 for (i=0; i<region.VZones.size(); ++i)
119 if (_ZoneMap.find(region.VZones[i].Name) != _ZoneMap.end())
121 nlwarning("Prim zone %s (in file %s) already added, discarded", region.VZones[i].Name.c_str(), primFile.c_str());
125 _ZoneMap.insert(TZoneMap::value_type(region.VZones[i].Name, _PrimZones.size()));
126 inFile.push_back(_PrimZones.size());
127 _PrimZones.push_back(region.VZones[i]);
132 // lookup for primitive file
133 CIFile
f(CPath::lookup(primFile
));
140 nlinfo("Loaded prim file '%s'", primFile
.c_str());
143 if (!prims
.read(xml
.getRootNode(), primFile
.c_str(), LigoConfig
))
145 nlwarning("Can't use primitive file '%s', xml parse error", primFile
.c_str());
150 uint firstPrimZone
= (uint
)_PrimZones
.size();
154 CPrimNode
*primRootNode
= prims
.RootNode
;
156 // read recursive node
157 readPrimitive(primRootNode
, inFile
);
159 const uint32 maxGridEntryIndex
= (1 << (sizeof(TEntryIndex
)*8)) - 1;
161 _FlagTable
.resize(_PrimZones
.size(), false);
163 // for each patat, sample points (check they belong to the patat) and find the matching entry
164 for (i
=firstPrimZone
; i
<_PrimZones
.size(); ++i
)
166 CPrimZone
&zone
= _PrimZones
[i
];
168 if (zone
.VPoints
.empty())
171 // build bounding box
172 double xmin
= 1.0e10f
, xmax
= -xmin
, ymin
= xmin
, ymax
= xmax
;
176 for (j
=0; j
<zone
.VPoints
.size(); ++j
)
178 if (zone
.VPoints
[j
].x
< xmin
) xmin
= zone
.VPoints
[j
].x
;
179 if (zone
.VPoints
[j
].y
< ymin
) ymin
= zone
.VPoints
[j
].y
;
180 if (zone
.VPoints
[j
].x
> xmax
) xmax
= zone
.VPoints
[j
].x
;
181 if (zone
.VPoints
[j
].y
> ymax
) ymax
= zone
.VPoints
[j
].y
;
184 xmin
= _SelectGrid
.round(xmin
);
185 ymin
= _SelectGrid
.round(ymin
);
186 xmax
= _SelectGrid
.round(xmax
);
187 ymax
= _SelectGrid
.round(ymax
);
189 nlinfo("Sampling %d/%d (%d samples)", i
-firstPrimZone
+1, _PrimZones
.size()-firstPrimZone
, (sint
)((xmax
-xmin
)/PatatGridResolution
* (ymax
-ymin
)/PatatGridResolution
));
192 for (y
=ymin
; y
<=ymax
; y
+=PatatGridResolution
)
194 for (x
=xmin
; x
<=xmax
; x
+=PatatGridResolution
)
196 CVector
v((float)x
, (float)y
, 0.0f
);
198 // check sampled point belongs to the patat
199 if (!zone
.contains(v
))
202 // get the entry index for this point
203 TEntryIndex index
= (TEntryIndex
)getEntryIndex(v
);
204 CEntry
&entry
= _EntryTable
[index
];
206 // compute the new hash code for this point
207 // basically, hash is previous hash code times zone number
208 //uint32 hash = (index == 0) ? i : entry.HashCode * i;
210 // other hash function, assuming index0 entry has 0 hashcode.
211 uint32 hash
= (entry
.HashCode
<<4) + (entry
.HashCode
>>28) + i
;
213 pair
<TEntryMap::iterator
, TEntryMap::iterator
> range
;
214 TEntryMap::iterator it
;
216 // get all entries with this hash code
217 range
= _EntryMap
.equal_range(hash
);
219 // check if selected entries match the new point
220 for (it
=range
.first
; it
!=range
.second
; ++it
)
222 CEntry
&selected
= _EntryTable
[(*it
).second
];
224 // selected must have one zone more than entry
225 if (selected
.Zones
.size() != entry
.Zones
.size()+1)
229 for (k
=0; k
<entry
.Zones
.size(); ++k
)
230 if (entry
.Zones
[k
] != selected
.Zones
[k
])
233 // if first zones of selected are different from entry, give up
234 if (k
!= entry
.Zones
.size())
237 // if all zones in selected and extended entry matches, found good entry
238 if (selected
.Zones
.back() == i
)
242 // if found an entry matching for the point, set up the grid to point on the selected entry
243 if (it
!= _EntryMap
.end())
245 CEntry
&selected
= _EntryTable
[(*it
).second
];
246 setEntryIndex(v
, selected
.EntryIndex
);
249 ++selected
.NumSamples
;
251 // if no more samples point to this entry, free it and leave it _FreeEntries stack
252 if (entry
.EntryIndex
!= 0 && entry
.NumSamples
== 0)
254 entry
.HashCode
= 0xffffffff;
256 _FreeEntries
.push_back(entry
.EntryIndex
);
261 // else create a new entry
264 // use a freed entry if possible
265 if (!_FreeEntries
.empty())
267 newIndex
= _FreeEntries
.front();
268 _FreeEntries
.pop_front();
272 newIndex
= (uint32
)_EntryTable
.size();
273 _EntryTable
.resize(newIndex
+1);
276 // reloads entry, as it might have be reallocated
277 CEntry
&entry
= _EntryTable
[index
];
279 // checks the index fits in table
280 if (newIndex
>= maxGridEntryIndex
)
282 nlerror("Error in PatatGrid build, type 'TEntryIndex' too short (%d bytes), use wider type !", sizeof(TEntryIndex
));
285 CEntry
&newEntry
= _EntryTable
[newIndex
];
288 newEntry
.EntryIndex
= (TEntryIndex
)newIndex
;
289 newEntry
.HashCode
= hash
;
290 newEntry
.NumSamples
= 1;
291 newEntry
.Zones
= entry
.Zones
;
292 newEntry
.Zones
.push_back(i
);
295 _EntryMap
.insert(TEntryMap::value_type(hash
, (TEntryIndex
)newIndex
));
297 // points point to new entry
298 setEntryIndex(v
, newEntry
.EntryIndex
);
308 bool CPatatGrid::diff(TEntryIndex previous
, TEntryIndex next
, vector
<uint32
> &in
, vector
<uint32
> &out
)
310 if (previous
>= _EntryTable
.size())
312 nlwarning("Entry index %d out of bounds, assume empty zone", previous
);
315 if (next
>= _EntryTable
.size())
317 nlwarning("Entry index %d out of bounds, assume empty zone", next
);
324 if (previous
== next
)
327 CEntry
&p
= _EntryTable
[previous
];
328 CEntry
&n
= _EntryTable
[next
];
332 // flag all of previous
333 for (i
=0; i
<p
.Zones
.size(); ++i
)
334 _FlagTable
[p
.Zones
[i
]] = true;
337 for (i
=0; i
<n
.Zones
.size(); ++i
)
339 // if flag clear, a new zone
340 if (!_FlagTable
[n
.Zones
[i
]])
341 in
.push_back(n
.Zones
[i
]);
342 _FlagTable
[n
.Zones
[i
]] = false;
346 for (i
=0; i
<p
.Zones
.size(); ++i
)
348 // if flag set (didn't change), a left zone
349 if (_FlagTable
[p
.Zones
[i
]])
350 out
.push_back(p
.Zones
[i
]);
351 _FlagTable
[p
.Zones
[i
]] = false;
354 return (!in
.empty() || !out
.empty());