1 #include "pch.h"
2 #include "../ogre/common/Def_Str.h"
3 #include "../ogre/common/RenderConst.h"
4 #include "../ogre/common/GuiCom.h"
5 #include "../ogre/common/CScene.h"
6 #include "settings.h"
7 #include "CApp.h"
8 #include "CGui.h"
9 #include "../road/Road.h"
10 #include "../vdrift/pathmanager.h"
11
12 #include <btBulletCollisionCommon.h>
13 #include <btBulletDynamicsCommon.h>
14 #include <BulletCollision/CollisionDispatch/btCollisionWorld.h>
15
16 #include <OgreTimer.h>
17 #include <OgreTerrain.h>
18 #include <OgreRenderWindow.h>
19 #include <OgreManualObject.h>
20 #include <OgreHardwarePixelBuffer.h>
21 #include <OgreRectangle2D.h>
22 #include <OgreCamera.h>
23 #include <OgreTextureManager.h>
24 #include <OgreRenderTexture.h>
25 #include <OgreViewport.h>
26 #include <OgreMaterialManager.h>
27 #include <OgreSceneNode.h>
28 using namespace Ogre;
29
30
31 // Setup render 2 texture (road maps)
32 // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Rnd2TexSetup()33 void App::Rnd2TexSetup()
34 {
35 /// RT: 0 road minimap, 1 road for grass, 2 terrain minimap, 3 track preview full
36 const uint32 visMask[RTs] =
37 { RV_Road, RV_Road+RV_Objects, RV_Terrain+RV_Objects, RV_MaskAll-RV_Hud };
38 const int dim[RTs] = //1025: sc->td.iVertsX
39 { 1024, 1025, 1024, 1024 };
40
41 asp = float(mWindow->getWidth())/float(mWindow->getHeight());
42 Real sz = pSet->size_minimap;
43 xm1 = 1-sz/asp, ym1 = -1+sz, xm2 = 1.0, ym2 = -1.0;
44 AxisAlignedBox aab; aab.setInfinite();
45
46 for (int i=0; i < RTs+RTsAdd; ++i)
47 {
48 SRndTrg& r = rt[i]; bool full = i==3;
49 String si = toStr(i), sMtr = /*i==3 ? "road_mini_add" :*/ "road_mini_"+si;
50 if (i < RTs)
51 {
52 String sTex = "RttTex"+si, sCam = "RttCam"+si;
53
54 TextureManager::getSingleton().remove(sTex);
55 mSceneMgr->destroyCamera(sCam); // dont destroy old - const tex sizes opt..
56
57 /// rnd to tex - same dim as Hmap // after track load
58 Real fDim = scn->sc->td.fTerWorldSize; // world dim ..vdr
59 TexturePtr texture = TextureManager::getSingleton().createManual(
60 sTex, rgDef, TEX_TYPE_2D,
61 dim[i], dim[i], 0, PF_R8G8B8A8, TU_RENDERTARGET);
62
63 r.cam = mSceneMgr->createCamera(sCam); // up
64 r.cam->setPosition(Vector3(0,1000,0/*-300..*/)); r.cam->setOrientation(Quaternion(0.5,-0.5,0.5,0.5));
65 r.cam->setNearClipDistance(0.5); r.cam->setFarClipDistance(50000);
66 r.cam->setAspectRatio(1.0); if (!full) r.cam->setProjectionType(PT_ORTHOGRAPHIC);
67 r.cam->setOrthoWindow(fDim,fDim); //rt[i].rndCam->setPolygonMode(PM_WIREFRAME);
68
69 r.tex = texture->getBuffer()->getRenderTarget();
70 r.tex->setAutoUpdated(false); r.tex->addListener(this);
71 Viewport* rvp = r.tex->addViewport(r.cam);
72 rvp->setClearEveryFrame(true); rvp->setBackgroundColour(ColourValue(0,0,0,0));
73 rvp->setOverlaysEnabled(false); rvp->setSkiesEnabled(full);
74 rvp->setVisibilityMask(visMask[i]);
75 rvp->setShadowsEnabled(false);
76
77 if (i != 3) rvp->setMaterialScheme("reflection");
78 }
79 /// minimap . . . . . . . . . . . . . . . . . . . . . . . . . . .
80 if (r.ndMini) mSceneMgr->destroySceneNode(r.ndMini);
81 ResourcePtr mt = MaterialManager::getSingleton().getByName(sMtr);
82 if (!mt.isNull()) mt->reload();
83
84 r.mini = new Rectangle2D(true); // screen rect preview
85 if (i == RTs) r.mini->setCorners(-1/asp, 1, 1/asp, -1); // fullscr,square
86 else r.mini->setCorners(xm1, ym1, xm2, ym2); //+i*sz*all
87
88 r.mini->setBoundingBox(aab);
89 r.ndMini = mSceneMgr->getRootSceneNode()->createChildSceneNode("Minimap"+si);
90 r.ndMini->attachObject(r.mini); r.mini->setCastShadows(false);
91 r.mini->setMaterial(i == RTs+1 ? "BrushPrvMtr" : sMtr);
92 r.mini->setRenderQueueGroup(RQG_Hud2);
93 r.mini->setVisibilityFlags(i == RTs ? RV_MaskPrvCam : RV_Hud);
94 }
95
96 // pos dot on minimap . . . . . . . .
97 if (!ndPos)
98 { mpos = Create2D("hud/CarPos", 0.2f, true); // dot size
99 mpos->setVisibilityFlags(RV_Hud);
100 mpos->setRenderQueueGroup(RQG_Hud3 /*RENDER_QUEUE_OVERLAY+1*/);
101 ndPos = mSceneMgr->getRootSceneNode()->createChildSceneNode(
102 Vector3(xm1+(xm2-xm1)/2, ym1+(ym2-ym1)/2, 0));
103 float fHudSize = 0.04f;
104 ndPos->scale(fHudSize, fHudSize, 1);
105 ndPos->attachObject(mpos);
106 }
107 if (ndPos) ndPos->setVisible(pSet->trackmap);
108 UpdMiniVis();
109 }
110
UpdMiniVis()111 void App::UpdMiniVis()
112 {
113 for (int i=0; i < RTs+RTsAdd; ++i)
114 if (rt[i].ndMini)
115 rt[i].ndMini->setVisible(pSet->trackmap && (i == pSet->num_mini));
116 }
117
118
119 /// Image from road
120 /// . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
SaveGrassDens()121 void App::SaveGrassDens()
122 {
123 Ogre::Timer ti;
124
125 for (int i=0; i < RTs-1; ++i) //-1 preview camera manual
126 {
127 if (!rt[i].tex) return;
128 rt[i].tex->update(); // all have to exist
129 }
130
131 int w = rt[1].tex->getWidth(), h = rt[1].tex->getHeight();
132 using Ogre::uint;
133 uint *rd = new uint[w*h]; // road render
134 uint *gd = new uint[w*h]; // grass dens
135 PixelBox pb_rd(w,h,1, PF_BYTE_RGBA, rd);
136 rt[1].tex->copyContentsToMemory(pb_rd, RenderTarget::FB_FRONT);
137
138 const int f = std::max(0, scn->sc->grDensSmooth);
139 float sum = 0.f;
140 register int v,i,j,x,y, a,b,d,m;
141
142 // gauss kernel for smoothing
143 int *mask = new int[(f*2+1)*(f*2+1)]; m = 0;
144 if (f==0)
145 { mask[0] = 256.f; sum = 256.f; }
146 else
147 for (j = -f; j <= f; ++j)
148 for (i = -f; i <= f; ++i, ++m)
149 {
150 v = std::max(0.f, (1.f - sqrtf((float)(i*i+j*j)) / float(f)) * 256.f);
151 mask[m] = v; sum += v;
152 }
153 sum = 2.f / sum; //par normally would be 1.f - but road needs to stay black and be smooth outside
154 // change smooth to distance from road with fade ?..
155
156 /// road - rotate, smooth -----------
157 for (y = f; y < h-f; ++y) { a = y*w +f;
158 for (x = f; x < w-f; ++x, ++a)
159 { b = x*w + (h-y); // rot 90 ccw b=a
160
161 // sum kernel
162 v = 0; m = 0;
163 for (j = -f; j <= f; ++j) { d = b -f + j*w;
164 for (i = -f; i <= f; ++i, ++d, ++m)
165 v += ((rd[d] >> 16) & 0xFF) * mask[m] / 256; }
166
167 v = std::max(0, (int)(255.f * (1.f - v * sum) )); // avg, inv, clamp
168
169 gd[a] = 0xFF000000 + v; // write
170 } }
171
172 v = 0xFF0000FF; // frame f [] todo: get from rd[b] not clear..
173 for (y = 0; y <= f; ++y) for (x=0; x < w; ++x) gd[y*w+x] = v; // - up
174 for (y=h-f-1; y < h; ++y) for (x=0; x < w; ++x) gd[y*w+x] = v; // - down
175 for (x = 0; x <= f; ++x) for (y=0; y < h; ++y) gd[y*w+x] = v; // | left
176 for (x=w-f-1; x < w; ++x) for (y=0; y < h; ++y) gd[y*w+x] = v; // | right
177
178 LogO(String("::: Time road dens: ") + fToStr(ti.getMilliseconds(),0,3) + " ms"); ti.reset();
179
180 if (!IsVdrTrack()) // vdr trk no grass, only previews
181 {
182 Image im; // for trees, before grass angle and height
183 im.loadDynamicImage((uchar*)gd, w,h,1, PF_BYTE_RGBA);
184 im.save(gcom->TrkDir()+"objects/roadDensity.png");
185 }
186 delete[] rd; delete[] gd; delete[] mask;
187
188 // road, terrain ----------------
189 int u = pSet->allow_save ? pSet->gui.track_user : 1;
190 rt[0].tex->writeContentsToFile(gcom->pathTrk[u] + pSet->gui.track + "/preview/road.png");
191 rt[2].tex->writeContentsToFile(gcom->pathTrk[u] + pSet->gui.track + "/preview/terrain.jpg");
192
193 LogO(String("::: Time save prv : ") + fToStr(ti.getMilliseconds(),0,3) + " ms");
194 }
195
196
197 /// pre and post rnd to tex
198 //-----------------------------------------------------------------------------------------------------------
preRenderTargetUpdate(const RenderTargetEvent & evt)199 void App::preRenderTargetUpdate(const RenderTargetEvent &evt)
200 {
201 const String& s = evt.source->getName();
202 int num = atoi(s.substr(s.length()-1, s.length()-1).c_str());
203
204 if (num == 3) // full
205 {
206 rt[3].cam->setPosition(mCamera->getPosition());
207 rt[3].cam->setDirection(mCamera->getDirection());
208 }
209 else if (scn->road)
210 scn->road->SetForRnd(num == 0 ? "render_clr" : "render_grass");
211 }
212
postRenderTargetUpdate(const RenderTargetEvent & evt)213 void App::postRenderTargetUpdate(const RenderTargetEvent &evt)
214 {
215 const String& s = evt.source->getName();
216 int num = atoi(s.substr(s.length()-1, s.length()-1).c_str());
217
218 if (num == 3) // full
219 {
220 }
221 else if (scn->road)
222 { scn->road->UnsetForRnd();
223 scn->road->UpdLodVis(pSet->road_dist);
224 }
225
226 // restore shadows splits todo...
227 //mCamera->setFarClipDistance(pSet->view_distance*1.1f);
228 //mCamera->setNearClipDistance(0.1f);
229 //UpdPSSMMaterials();
230 }
231
232
233 /// save water depth map
234 //-----------------------------------------------------------------------------------------------------------
SaveWaterDepth()235 void App::SaveWaterDepth()
236 {
237 if (scn->sc->fluids.empty())
238 {
239 gui->Delete(gcom->TrkDir()+"objects/waterDepth.png"); // no tex if no fluids
240 return;
241 }
242 Ogre::Timer ti;
243
244 // 2048 for bigger terrains ?
245 int w = 1024, h = w; float fh = h-1, fw = w-1;
246 using Ogre::uint;
247 uint *wd = new uint[w*h]; // water depth
248 register int x,y,a,i,ia,id;
249 register float fa,fd;
250
251 /// write to img -----------
252 // get ter height to fluid height difference for below
253 for (y = 0; y < h; ++y) { a = y*w;
254 for (x = 0; x < w; ++x, ++a)
255 {
256 // pos 0..1
257 float fx = float(y)/fh, fz = float(x)/fw;
258 // pos on ter -terSize..terSize
259 float w = scn->sc->td.fTerWorldSize;
260 float wx = (fx-0.5f) * w, wz = -(fz-0.5f) * w;
261
262 fa = 0.f; // fluid y pos
263 for (i=0; i < scn->sc->fluids.size(); ++i)
264 {
265 const FluidBox& fb = scn->sc->fluids[i];
266 const float sizex = fb.size.x*0.5f, sizez = fb.size.z*0.5f;
267 // check rect 2d - no rot ! todo: make 2nd type circle..
268 if (wx > fb.pos.x - sizex && wx < fb.pos.x + sizex &&
269 wz > fb.pos.z - sizez && wz < fb.pos.z + sizez)
270 {
271 float f = fb.pos.y - scn->terrain->getHeightAtTerrainPosition(fx,fz);
272 if (f > fa) fa = f;
273 }
274 } //par
275 fd = fa * 0.4f * 255.f; // depth far full at 2.5 m
276 fa = fa * 8.f * 255.f; // alpha near full at 1/8 m
277
278 ia = std::max(0, std::min(255, (int)fa )); // clamp
279 id = std::max(0, std::min(255, (int)fd ));
280
281 wd[a] = 0xFF000000 + /*0x01 */ ia + 0x0100 * id; // write
282 } }
283
284 Image im; // save img
285 im.loadDynamicImage((uchar*)wd, w,h,1, PF_BYTE_RGBA);
286 im.save(gcom->TrkDir()+"objects/waterDepth.png");
287 delete[] wd;
288
289 try {
290 TexturePtr tex = TextureManager::getSingleton().getByName("waterDepth.png");
291 if (!tex.isNull())
292 tex->reload();
293 else // 1st fluid after start, refresh matdef ?..
294 TextureManager::getSingleton().load("waterDepth.png", rgDef);
295 } catch(...) { }
296
297 LogO(String("::: Time WaterDepth: ") + fToStr(ti.getMilliseconds(),0,3) + " ms");
298 }
299
300
301
302 /// . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
303 /// align terrain to road selected segments
304 //-----------------------------------------------------------------------------------------------------------
305 struct RayResult : public btCollisionWorld::RayResultCallback
306 {
RayResultRayResult307 RayResult(const btVector3& rayFromWorld, const btVector3& rayToWorld)
308 : m_rayFromWorld(rayFromWorld), m_rayToWorld(rayToWorld)
309 { }
310
311 btVector3 m_rayFromWorld;//used to calculate hitPointWorld from hitFraction
312 btVector3 m_rayToWorld;
313
314 btVector3 m_hitNormalWorld;
315 btVector3 m_hitPointWorld;
316
addSingleResultRayResult317 virtual btScalar addSingleResult(btCollisionWorld::LocalRayResult& rayResult, bool normalInWorldSpace)
318 {
319 const btCollisionObject* obj = rayResult.m_collisionObject;
320 if (obj->getUserPointer() != (void*)111) // allow only road
321 return 1.0;
322
323 //caller already does the filter on the m_closestHitFraction
324 btAssert(rayResult.m_hitFraction <= m_closestHitFraction);
325
326 m_closestHitFraction = rayResult.m_hitFraction;
327 m_collisionObject = obj;
328
329 if (normalInWorldSpace)
330 m_hitNormalWorld = rayResult.m_hitNormalLocal;
331 else ///need to transform normal into worldspace
332 m_hitNormalWorld = m_collisionObject->getWorldTransform().getBasis()*rayResult.m_hitNormalLocal;
333
334 m_hitPointWorld.setInterpolate3(m_rayFromWorld,m_rayToWorld,rayResult.m_hitFraction);
335 return rayResult.m_hitFraction;
336 }
337 };
338
AlignTerToRoad()339 void App::AlignTerToRoad()
340 {
341 SplineRoad* road = scn->road;
342 if (road->vSel.empty()) return;
343 Ogre::Timer ti;
344
345 /// create bullet road for selected segments
346 road->ed_Wmul = pSet->al_w_mul;
347 road->ed_Wadd = pSet->al_w_add;
348 road->RebuildRoadInt(true);
349
350 // terrain
351 float *fHmap = scn->terrain->getHeightData();
352 const int w = scn->sc->td.iVertsX, h = w;
353 const float fh = h-1, fw = w-1;
354
355 float *rd = new float[w*h]; // road depth
356 bool *rh = new bool[w*h]; // road hit
357
358 const float ws = scn->sc->td.fTerWorldSize;
359 const float Len = 400; // max ray length
360 register int x,y,a;
361 register float v,k, fx,fz, wx,wz;
362
363 /// ray casts -----------
364 for (y = 0; y < h; ++y) { a = y*w;
365 for (x = 0; x < w; ++x, ++a)
366 {
367 // pos 0..1
368 fx = float(x)/fh; fz = float(y)/fw;
369 // pos on ter -terSize..terSize
370 wx = (fx-0.5f) * ws; wz = (fz-0.5f) * ws;
371
372 btVector3 from(wx,wz,Len), to(wx,wz,-Len); // x -z y
373 RayResult rayRes(from, to);
374 world->rayTest(from, to, rayRes);
375
376 // terrain height if not hit
377 rh[a] = rayRes.hasHit();
378 rd[a] = rayRes.hasHit() ? rayRes.m_hitPointWorld.getZ() : fHmap[a];
379 } }
380
381 // smooth edges, road-terrain border
382 const float fv = pSet->al_smooth;
383 if (fv > 0.5f)
384 {
385 const int f = std::ceil(fv);
386 const unsigned int fs = (f*2+1)*(f*2+1);
387 float ff = 0.f;
388 register int i,j,m,d,b;
389
390 // gauss kernel for smoothing
391 float *mask = new float[fs]; m = 0;
392 for (j = -f; j <= f; ++j)
393 for (i = -f; i <= f; ++i, ++m)
394 {
395 v = std::max(0.f, 1.f - sqrtf((float)(i*i+j*j)) / fv );
396 mask[m] = v; ff += v;
397 }
398 ff = 1.f / ff; // smooth, outside (>1.f)
399
400 // sum kernel
401 for (y = f; y < h-f; ++y) { a = y*w +f;
402 for (x = f; x < w-f; ++x, ++a)
403 {
404 v = 0; m = 0; b = 0;
405 for (j = -f; j <= f; ++j) { d = a -f + j*w;
406 for (i = -f; i <= f; ++i, ++d, ++m)
407 { k = mask[m]; //maskB ?
408 v += rd[d] * k;
409 if (rh[d] && k > 0.1f) ++b;
410 } }
411 if (b > 0 && b < fs*0.8f) //par?
412 rd[a] = v * ff;
413 } }
414 delete[] mask;
415 }
416
417 // set new hmap
418 for (y = 0; y < h; ++y) { a = y*w;
419 for (x = 0; x < w; ++x, ++a)
420 {
421 fHmap[a] = rd[a];
422 } }
423
424 delete[] rd; delete[] rh;
425
426
427
428 // clear bullet world
429 for (int i=0; i < road->vbtTriMesh.size(); ++i)
430 delete road->vbtTriMesh[i];
431 road->vbtTriMesh.clear();
432
433 for (int i = world->getNumCollisionObjects() - 1; i >= 0; i--)
434 {
435 btCollisionObject* obj = world->getCollisionObjectArray()[i];
436 if (obj->getUserPointer() == (void*)111) // only road
437 {
438 delete obj->getCollisionShape(); //?
439
440 btRigidBody* body = btRigidBody::upcast(obj);
441 if (body && body->getMotionState())
442 delete body->getMotionState();
443
444 world->removeCollisionObject(obj);
445 delete obj;
446 }
447 }
448
449
450 // update terrain
451 scn->terrain->dirty(); //rect..
452 scn->UpdBlendmap();
453 bTerUpd = true;
454
455
456 // put sel segs on terrain
457 for (std::set<int>::const_iterator it = scn->road->vSel.begin(); it != scn->road->vSel.end(); ++it)
458 scn->road->mP[*it].onTer = true;
459
460 // restore orig road width
461 scn->road->Rebuild(true);
462
463 // todo: ?restore road sel after load F5..
464
465 LogO(String("::: Time Ter Align: ") + fToStr(ti.getMilliseconds(),0,3) + " ms");
466 }
467