1 // Copyright © 2008-2021 Pioneer Developers. See AUTHORS.txt for details
2 // Licensed under the terms of the GPL v3. See licenses/GPL-3.txt
3
4 #include "GasGiant.h"
5
6 #include "FileSystem.h"
7 #include "Game.h"
8 #include "GameConfig.h"
9 #include "Pi.h"
10 #include "galaxy/AtmosphereParameters.h"
11 #include "graphics/Frustum.h"
12 #include "graphics/Graphics.h"
13 #include "graphics/Material.h"
14 #include "graphics/RenderState.h"
15 #include "graphics/Renderer.h"
16 #include "graphics/Texture.h"
17 #include "graphics/VertexArray.h"
18 #include "graphics/opengl/GenGasGiantColourMaterial.h"
19 #include "perlin.h"
20 #include "utils.h"
21 #include "vcacheopt/vcacheopt.h"
22
23 RefCountedPtr<GasPatchContext> GasGiant::s_patchContext;
24
25 namespace {
26 static Uint32 s_texture_size_small = 16;
27 static Uint32 s_texture_size_cpu[5];
28 static Uint32 s_texture_size_gpu[5];
29 static Uint32 s_noiseOctaves[5];
30 static float s_initialCPUDelayTime = 60.0f; // (perhaps) 60 seconds seems like a reasonable default
31 static float s_initialGPUDelayTime = 5.0f; // (perhaps) 5 seconds seems like a reasonable default
32 static std::vector<GasGiant *> s_allGasGiants;
33
34 static const std::string GGJupiter("GGJupiter");
35 static const std::string GGNeptune("GGNeptune");
36 static const std::string GGNeptune2("GGNeptune2");
37 static const std::string GGSaturn("GGSaturn");
38 static const std::string GGSaturn2("GGSaturn2");
39 static const std::string GGUranus("GGUranus");
40
SplitData(const std::string & spec,Uint32 & cpuOut,Uint32 & gpuOut,Uint32 & octavesOut)41 bool SplitData(const std::string &spec, Uint32 &cpuOut, Uint32 &gpuOut, Uint32 &octavesOut)
42 {
43 static const std::string delim(",");
44
45 enum dataEntries {
46 eCPU = 0,
47 eGPU,
48 eOCTAVES
49 };
50
51 size_t i = 0, start = 0, end = 0;
52 while (end != std::string::npos) {
53 // get to the first non-delim char
54 start = spec.find_first_not_of(delim, end);
55
56 // read the end, no more to do
57 if (start == std::string::npos)
58 break;
59
60 // find the end - next delim or end of string
61 end = spec.find_first_of(delim, start);
62
63 // extract the fragment and remember it
64 switch (i) {
65 case eCPU:
66 cpuOut = ceil_pow2(Clamp(atoi(spec.substr(start, (end == std::string::npos) ? std::string::npos : end - start).c_str()), 64, 4096));
67 break;
68 case eGPU:
69 gpuOut = ceil_pow2(Clamp(atoi(spec.substr(start, (end == std::string::npos) ? std::string::npos : end - start).c_str()), 64, 4096));
70 break;
71 case eOCTAVES:
72 octavesOut = Clamp(atoi(spec.substr(start, (end == std::string::npos) ? std::string::npos : end - start).c_str()), 1, 16);
73 break;
74 default:
75 assert(false);
76 break;
77 }
78 i++;
79 }
80
81 return i == 4;
82 }
83 } // namespace
84
85 class GasPatchContext : public RefCounted {
86 public:
87 #pragma pack(push, 4)
88 struct VBOVertex {
89 vector3f pos;
90 vector3f norm;
91 };
92 #pragma pack(pop)
93
94 int edgeLen;
95
IDX_VBO_COUNT_ALL_IDX() const96 inline int IDX_VBO_COUNT_ALL_IDX() const { return ((edgeLen - 1) * (edgeLen - 1)) * 2 * 3; }
97
NUMVERTICES() const98 inline int NUMVERTICES() const { return edgeLen * edgeLen; }
99
100 double frac;
101
102 std::unique_ptr<Uint32[]> indices;
103 RefCountedPtr<Graphics::IndexBuffer> indexBuffer;
104
GasPatchContext(const int _edgeLen)105 GasPatchContext(const int _edgeLen) :
106 edgeLen(_edgeLen)
107 {
108 Init();
109 }
110
~GasPatchContext()111 ~GasPatchContext()
112 {
113 Cleanup();
114 }
115
Refresh()116 void Refresh()
117 {
118 Cleanup();
119 Init();
120 }
121
Cleanup()122 void Cleanup()
123 {
124 indices.reset();
125 }
126
GetIndices(std::vector<Uint32> & pl)127 int GetIndices(std::vector<Uint32> &pl)
128 {
129 // calculate how many tri's there are
130 const int tri_count = IDX_VBO_COUNT_ALL_IDX() / 3;
131
132 // pre-allocate enough space
133 pl.reserve(IDX_VBO_COUNT_ALL_IDX());
134
135 // add all of the middle indices
136 for (int i = 0; i < IDX_VBO_COUNT_ALL_IDX(); ++i) {
137 pl.push_back(indices[i]);
138 }
139
140 return tri_count;
141 }
142
Init()143 void Init()
144 {
145 PROFILE_SCOPED()
146 frac = 1.0 / double(edgeLen - 1);
147
148 // also want vtx indices for tris not touching edge of patch
149 indices.reset(new Uint32[IDX_VBO_COUNT_ALL_IDX()]);
150 Uint32 *idx = indices.get();
151 for (int x = 0; x < edgeLen - 1; x++) {
152 for (int y = 0; y < edgeLen - 1; y++) {
153 idx[0] = x + edgeLen * y;
154 idx[1] = x + 1 + edgeLen * y;
155 idx[2] = x + edgeLen * (y + 1);
156 idx += 3;
157
158 idx[0] = x + 1 + edgeLen * y;
159 idx[1] = x + 1 + edgeLen * (y + 1);
160 idx[2] = x + edgeLen * (y + 1);
161 idx += 3;
162 }
163 }
164
165 // these will hold the optimised indices
166 std::vector<Uint32> pl_short;
167
168 // populate the N indices lists from the arrays built during InitTerrainIndices()
169 // iterate over each index list and optimize it
170 Uint32 tri_count = GetIndices(pl_short);
171 VertexCacheOptimizerUInt vco;
172 #ifndef NDEBUG
173 VertexCacheOptimizerUInt::Result res = vco.Optimize(&pl_short[0], tri_count);
174 assert(0 == res);
175 #else
176 vco.Optimize(&pl_short[0], tri_count);
177 #endif
178
179 //create buffer & copy
180 indexBuffer.Reset(Pi::renderer->CreateIndexBuffer(pl_short.size(), Graphics::BUFFER_USAGE_STATIC));
181 Uint32 *idxPtr = indexBuffer->Map(Graphics::BUFFER_MAP_WRITE);
182 for (Uint32 j = 0; j < pl_short.size(); j++) {
183 idxPtr[j] = pl_short[j];
184 }
185 indexBuffer->Unmap();
186
187 if (indices) {
188 indices.reset();
189 }
190 }
191 };
192
193 class GasPatch {
194 public:
195 RefCountedPtr<GasPatchContext> ctx;
196 vector3d v[4];
197 std::unique_ptr<Graphics::VertexBuffer> m_vertexBuffer;
198 GasGiant *gasSphere;
199 vector3d clipCentroid;
200 double clipRadius;
201
GasPatch(const RefCountedPtr<GasPatchContext> & _ctx,GasGiant * gs,vector3d v0,vector3d v1,vector3d v2,vector3d v3)202 GasPatch(const RefCountedPtr<GasPatchContext> &_ctx, GasGiant *gs, vector3d v0, vector3d v1, vector3d v2, vector3d v3) :
203 ctx(_ctx),
204 gasSphere(gs),
205 clipCentroid(((v0 + v1 + v2 + v3) * 0.25).Normalized()),
206 clipRadius(0.0)
207 {
208 PROFILE_SCOPED()
209 v[0] = v0;
210 v[1] = v1;
211 v[2] = v2;
212 v[3] = v3;
213 for (int i = 0; i < 4; i++) {
214 clipRadius = std::max(clipRadius, (v[i] - clipCentroid).Length());
215 }
216
217 UpdateVBOs();
218 }
219
~GasPatch()220 ~GasPatch() {}
221
222 /* in patch surface coords, [0,1] */
GetSpherePoint(const double x,const double y) const223 vector3d GetSpherePoint(const double x, const double y) const
224 {
225 return (v[0] + x * (1.0 - y) * (v[1] - v[0]) + x * y * (v[2] - v[0]) + (1.0 - x) * y * (v[3] - v[0])).Normalized();
226 }
227
UpdateVBOs()228 void UpdateVBOs()
229 {
230 PROFILE_SCOPED()
231 //create buffer and upload data
232 Graphics::VertexBufferDesc vbd;
233 vbd.attrib[0].semantic = Graphics::ATTRIB_POSITION;
234 vbd.attrib[0].format = Graphics::ATTRIB_FORMAT_FLOAT3;
235 vbd.attrib[1].semantic = Graphics::ATTRIB_NORMAL;
236 vbd.attrib[1].format = Graphics::ATTRIB_FORMAT_FLOAT3;
237 vbd.numVertices = ctx->NUMVERTICES();
238 vbd.usage = Graphics::BUFFER_USAGE_STATIC;
239 m_vertexBuffer.reset(Pi::renderer->CreateVertexBuffer(vbd));
240
241 GasPatchContext::VBOVertex *vtxPtr = m_vertexBuffer->Map<GasPatchContext::VBOVertex>(Graphics::BUFFER_MAP_WRITE);
242 assert(m_vertexBuffer->GetDesc().stride == sizeof(GasPatchContext::VBOVertex));
243
244 const Sint32 edgeLen = ctx->edgeLen;
245 const double frac = ctx->frac;
246 for (Sint32 y = 0; y < edgeLen; y++) {
247 for (Sint32 x = 0; x < edgeLen; x++) {
248 const vector3d p = GetSpherePoint(x * frac, y * frac);
249 const vector3d pSubCentroid = p - clipCentroid;
250 clipRadius = std::max(clipRadius, p.Length());
251 vtxPtr->pos = vector3f(pSubCentroid);
252 vtxPtr->norm = vector3f(p);
253
254 ++vtxPtr; // next vertex
255 }
256 }
257 m_vertexBuffer->Unmap();
258 }
259
Render(Graphics::Renderer * renderer,const vector3d & campos,const matrix4x4d & modelView,const Graphics::Frustum & frustum)260 void Render(Graphics::Renderer *renderer, const vector3d &campos, const matrix4x4d &modelView, const Graphics::Frustum &frustum)
261 {
262 if (!frustum.TestPoint(clipCentroid, clipRadius))
263 return;
264
265 RefCountedPtr<Graphics::Material> mat = gasSphere->GetSurfaceMaterial();
266 Graphics::RenderState *rs = gasSphere->GetSurfRenderState();
267
268 const vector3d relpos = clipCentroid - campos;
269 renderer->SetTransform(matrix4x4f(modelView * matrix4x4d::Translation(relpos)));
270
271 Pi::statSceneTris += 2 * (ctx->edgeLen - 1) * (ctx->edgeLen - 1);
272 ++Pi::statNumPatches;
273
274 renderer->DrawBufferIndexed(m_vertexBuffer.get(), ctx->indexBuffer.Get(), rs, mat.Get());
275 renderer->GetStats().AddToStatCount(Graphics::Stats::STAT_PATCHES, 1);
276 }
277 };
278
279 Graphics::RenderTarget *GasGiant::s_renderTarget;
280 Graphics::RenderState *GasGiant::s_quadRenderState;
281
282 // static
UpdateAllGasGiants()283 void GasGiant::UpdateAllGasGiants()
284 {
285 PROFILE_SCOPED()
286 for (std::vector<GasGiant *>::iterator i = s_allGasGiants.begin(); i != s_allGasGiants.end(); ++i) {
287 (*i)->Update();
288 }
289 }
290
291 // static
OnChangeDetailLevel()292 void GasGiant::OnChangeDetailLevel()
293 {
294 s_patchContext.Reset(new GasPatchContext(127));
295
296 // reinit the geosphere terrain data
297 for (std::vector<GasGiant *>::iterator i = s_allGasGiants.begin(); i != s_allGasGiants.end(); ++i) {
298 // clearout anything we don't need
299 (*i)->Reset();
300
301 // reinit the terrain with the new settings
302 (*i)->m_terrain.Reset(Terrain::InstanceTerrain((*i)->GetSystemBody()));
303 }
304 }
305
GasGiant(const SystemBody * body)306 GasGiant::GasGiant(const SystemBody *body) :
307 BaseSphere(body),
308 m_hasTempCampos(false),
309 m_tempCampos(0.0),
310 m_hasGpuJobRequest(false),
311 m_timeDelay(s_initialCPUDelayTime)
312 {
313 s_allGasGiants.push_back(this);
314
315 for (int i = 0; i < NUM_PATCHES; i++) {
316 m_hasJobRequest[i] = false;
317 }
318
319 Random rng(GetSystemBody()->GetSeed() + 4609837);
320
321 const bool bEnableGPUJobs = (Pi::config->Int("EnableGPUJobs") == 1);
322 if (bEnableGPUJobs)
323 m_timeDelay = s_initialGPUDelayTime + (rng.Double() * (s_initialGPUDelayTime * 0.5));
324
325 //SetUpMaterials is not called until first Render since light count is zero :)
326
327 //BuildFirstPatches and GenerateTexture are only called when we first attempt to render
328 }
329
~GasGiant()330 GasGiant::~GasGiant()
331 {
332 // update thread should not be able to access us now, so we can safely continue to delete
333 assert(std::count(s_allGasGiants.begin(), s_allGasGiants.end(), this) == 1);
334 s_allGasGiants.erase(std::find(s_allGasGiants.begin(), s_allGasGiants.end(), this));
335 }
336
Reset()337 void GasGiant::Reset()
338 {
339 {
340 for (int i = 0; i < NUM_PATCHES; i++) {
341 if (m_hasJobRequest[i] && m_job[i].HasJob())
342 m_job[i].GetJob()->OnCancel();
343 m_hasJobRequest[i] = false;
344 }
345 }
346
347 for (int p = 0; p < NUM_PATCHES; p++) {
348 // delete patches
349 if (m_patches[p]) {
350 m_patches[p].reset();
351 }
352 }
353
354 m_surfaceTextureSmall.Reset();
355 m_surfaceTexture.Reset();
356 m_surfaceMaterial.Reset();
357 }
358
359 //static
OnAddTextureFaceResult(const SystemPath & path,GasGiantJobs::STextureFaceResult * res)360 bool GasGiant::OnAddTextureFaceResult(const SystemPath &path, GasGiantJobs::STextureFaceResult *res)
361 {
362 // Find the correct GeoSphere via it's system path, and give it the split result
363 for (std::vector<GasGiant *>::iterator i = s_allGasGiants.begin(), iEnd = s_allGasGiants.end(); i != iEnd; ++i) {
364 if (path == (*i)->GetSystemBody()->GetPath()) {
365 (*i)->AddTextureFaceResult(res);
366 return true;
367 }
368 }
369 // GasGiant not found to return the data to, cancel (which deletes it) instead
370 if (res) {
371 res->OnCancel();
372 delete res;
373 }
374 return false;
375 }
376
377 //static
OnAddGPUGenResult(const SystemPath & path,GasGiantJobs::SGPUGenResult * res)378 bool GasGiant::OnAddGPUGenResult(const SystemPath &path, GasGiantJobs::SGPUGenResult *res)
379 {
380 // Find the correct GeoSphere via it's system path, and give it the split result
381 for (std::vector<GasGiant *>::iterator i = s_allGasGiants.begin(), iEnd = s_allGasGiants.end(); i != iEnd; ++i) {
382 if (path == (*i)->GetSystemBody()->GetPath()) {
383 (*i)->AddGPUGenResult(res);
384 return true;
385 }
386 }
387 // GasGiant not found to return the data to, cancel (which deletes it) instead
388 if (res) {
389 res->OnCancel();
390 delete res;
391 }
392 return false;
393 }
394
395 #define DUMP_TO_TEXTURE 0
396
397 #if DUMP_TO_TEXTURE
398 #include "FileSystem.h"
399 #include "PngWriter.h"
400 #include "graphics/opengl/TextureGL.h"
textureDump(const char * destFile,const int width,const int height,const Color * buf)401 void textureDump(const char *destFile, const int width, const int height, const Color *buf)
402 {
403 const std::string dir = "generated_textures";
404 FileSystem::userFiles.MakeDirectory(dir);
405 const std::string fname = FileSystem::JoinPathBelow(dir, destFile);
406
407 // pad rows to 4 bytes, which is the default row alignment for OpenGL
408 //const int stride = (3*width + 3) & ~3;
409 const int stride = width * 4;
410
411 write_png(FileSystem::userFiles, fname, &buf[0].r, width, height, stride, 4);
412
413 printf("texture %s saved\n", fname.c_str());
414 }
415 #endif
416
AddTextureFaceResult(GasGiantJobs::STextureFaceResult * res)417 bool GasGiant::AddTextureFaceResult(GasGiantJobs::STextureFaceResult *res)
418 {
419 bool result = false;
420 assert(res);
421 assert(res->face() >= 0 && res->face() < NUM_PATCHES);
422 m_jobColorBuffers[res->face()].reset(res->data().colors);
423 m_hasJobRequest[res->face()] = false;
424 const Sint32 uvDims = res->data().uvDims;
425 assert(uvDims > 0 && uvDims <= 4096);
426
427 // tidyup
428 delete res;
429
430 bool bCreateTexture = true;
431 for (int i = 0; i < NUM_PATCHES; i++) {
432 bCreateTexture = bCreateTexture & (!m_hasJobRequest[i]);
433 }
434
435 if (bCreateTexture) {
436 // create texture
437 const vector2f texSize(1.0f, 1.0f);
438 const vector3f dataSize(uvDims, uvDims, 0.0f);
439 const Graphics::TextureDescriptor texDesc(
440 Graphics::TEXTURE_RGBA_8888,
441 dataSize, texSize, Graphics::LINEAR_CLAMP,
442 true, false, false, 0, Graphics::TEXTURE_CUBE_MAP);
443 m_surfaceTexture.Reset(Pi::renderer->CreateTexture(texDesc));
444
445 // update with buffer from above
446 Graphics::TextureCubeData tcd;
447 tcd.posX = m_jobColorBuffers[0].get();
448 tcd.negX = m_jobColorBuffers[1].get();
449 tcd.posY = m_jobColorBuffers[2].get();
450 tcd.negY = m_jobColorBuffers[3].get();
451 tcd.posZ = m_jobColorBuffers[4].get();
452 tcd.negZ = m_jobColorBuffers[5].get();
453 m_surfaceTexture->Update(tcd, dataSize, Graphics::TEXTURE_RGBA_8888);
454
455 #if DUMP_TO_TEXTURE
456 for (int iFace = 0; iFace < NUM_PATCHES; iFace++) {
457 char filename[1024];
458 snprintf(filename, 1024, "%s%d.png", GetSystemBody()->GetName().c_str(), iFace);
459 textureDump(filename, uvDims, uvDims, m_jobColorBuffers[iFace].get());
460 }
461 #endif
462
463 // cleanup the temporary color buffer storage
464 for (int i = 0; i < NUM_PATCHES; i++) {
465 m_jobColorBuffers[i].reset();
466 }
467
468 // change the planet texture for the new higher resolution texture
469 if (m_surfaceMaterial.Get()) {
470 m_surfaceMaterial->texture0 = m_surfaceTexture.Get();
471 m_surfaceTextureSmall.Reset();
472 }
473 }
474
475 return result;
476 }
477
AddGPUGenResult(GasGiantJobs::SGPUGenResult * res)478 bool GasGiant::AddGPUGenResult(GasGiantJobs::SGPUGenResult *res)
479 {
480 bool result = false;
481 assert(res);
482 m_hasGpuJobRequest = false;
483 assert(!m_gpuJob.HasJob());
484 #ifndef NDEBUG
485 const Sint32 uvDims = res->data().uvDims;
486 assert(uvDims > 0 && uvDims <= 4096);
487 #endif
488
489 #if DUMP_TO_TEXTURE
490 for (int iFace = 0; iFace < NUM_PATCHES; iFace++) {
491 std::unique_ptr<Color, FreeDeleter> buffer(static_cast<Color *>(malloc(uvDims * uvDims * 4)));
492 Graphics::Texture *pTex = res->data().texture.Get();
493 Graphics::TextureGL *pGLTex = static_cast<Graphics::TextureGL *>(pTex);
494 pGLTex->Bind();
495 glGetTexImage(GL_TEXTURE_CUBE_MAP_POSITIVE_X + iFace, 0, GL_RGBA, GL_UNSIGNED_BYTE, buffer.get());
496 pGLTex->Unbind();
497
498 char filename[1024];
499 snprintf(filename, 1024, "%s%d.png", GetSystemBody()->GetName().c_str(), iFace);
500 textureDump(filename, uvDims, uvDims, buffer.get());
501 }
502 #endif
503
504 // tidyup
505 delete res;
506
507 if (m_builtTexture.Valid()) {
508 m_surfaceTexture = m_builtTexture;
509 m_builtTexture.Reset();
510
511 // these won't be automatically generated otherwise since we used it as a render target
512 m_surfaceTexture->BuildMipmaps();
513
514 // change the planet texture for the new higher resolution texture
515 if (m_surfaceMaterial.Get()) {
516 m_surfaceMaterial->texture0 = m_surfaceTexture.Get();
517 m_surfaceTextureSmall.Reset();
518 }
519 }
520
521 return result;
522 }
523
524 // in patch surface coords, [0,1]
GetSpherePointFromCorners(const double x,const double y,const vector3d * corners)525 inline vector3d GetSpherePointFromCorners(const double x, const double y, const vector3d *corners)
526 {
527 return (corners[0] + x * (1.0 - y) * (corners[1] - corners[0]) + x * y * (corners[2] - corners[0]) + (1.0 - x) * y * (corners[3] - corners[0])).Normalized();
528 }
529
GenerateTexture()530 void GasGiant::GenerateTexture()
531 {
532 using namespace GasGiantJobs;
533 for (int i = 0; i < NUM_PATCHES; i++) {
534 if (m_hasGpuJobRequest || m_hasJobRequest[i])
535 return;
536 }
537
538 const bool bEnableGPUJobs = (Pi::config->Int("EnableGPUJobs") == 1);
539
540 // scope the small texture generation
541 {
542 const vector2f texSize(1.0f, 1.0f);
543 const vector3f dataSize(s_texture_size_small, s_texture_size_small, 0.0f);
544 const Graphics::TextureDescriptor texDesc(
545 Graphics::TEXTURE_RGBA_8888,
546 dataSize, texSize, Graphics::LINEAR_CLAMP,
547 false, false, false, 0, Graphics::TEXTURE_CUBE_MAP);
548 m_surfaceTextureSmall.Reset(Pi::renderer->CreateTexture(texDesc));
549
550 const Terrain *pTerrain = GetTerrain();
551 const double fracStep = 1.0 / double(s_texture_size_small - 1);
552
553 Graphics::TextureCubeData tcd;
554 std::unique_ptr<Color[]> bufs[NUM_PATCHES];
555 for (int i = 0; i < NUM_PATCHES; i++) {
556 Color *colors = new Color[(s_texture_size_small * s_texture_size_small)];
557 for (Uint32 v = 0; v < s_texture_size_small; v++) {
558 for (Uint32 u = 0; u < s_texture_size_small; u++) {
559 // where in this row & colum are we now.
560 const double ustep = double(u) * fracStep;
561 const double vstep = double(v) * fracStep;
562
563 // get point on the surface of the sphere
564 const vector3d p = GetSpherePointFromCorners(ustep, vstep, &GetPatchFaces(i, 0));
565 // get colour using `p`
566 const vector3d colour = pTerrain->GetColor(p, 0.0, p);
567
568 // convert to ubyte and store
569 Color *col = colors + (u + (v * s_texture_size_small));
570 col[0].r = Uint8(colour.x * 255.0);
571 col[0].g = Uint8(colour.y * 255.0);
572 col[0].b = Uint8(colour.z * 255.0);
573 col[0].a = 255;
574 }
575 }
576 bufs[i].reset(colors);
577 }
578
579 // update with buffer from above
580 tcd.posX = bufs[0].get();
581 tcd.negX = bufs[1].get();
582 tcd.posY = bufs[2].get();
583 tcd.negY = bufs[3].get();
584 tcd.posZ = bufs[4].get();
585 tcd.negZ = bufs[5].get();
586 m_surfaceTextureSmall->Update(tcd, dataSize, Graphics::TEXTURE_RGBA_8888);
587 }
588
589 // create small texture
590 if (!bEnableGPUJobs) {
591 for (int i = 0; i < NUM_PATCHES; i++) {
592 assert(!m_hasJobRequest[i]);
593 assert(!m_job[i].HasJob());
594 m_hasJobRequest[i] = true;
595 GasGiantJobs::STextureFaceRequest *ssrd = new GasGiantJobs::STextureFaceRequest(&GetPatchFaces(i, 0), GetSystemBody()->GetPath(), i, s_texture_size_cpu[Pi::detail.planets], GetTerrain());
596 m_job[i] = Pi::GetAsyncJobQueue()->Queue(new GasGiantJobs::SingleTextureFaceJob(ssrd));
597 }
598 } else {
599 // use m_surfaceTexture texture?
600 // create texture
601 const vector2f texSize(1.0f, 1.0f);
602 const vector3f dataSize(s_texture_size_gpu[Pi::detail.planets], s_texture_size_gpu[Pi::detail.planets], 0.0f);
603 const Graphics::TextureDescriptor texDesc(
604 Graphics::TEXTURE_RGBA_8888,
605 dataSize, texSize, Graphics::LINEAR_CLAMP,
606 true, false, false, 0, Graphics::TEXTURE_CUBE_MAP);
607 m_builtTexture.Reset(Pi::renderer->CreateTexture(texDesc));
608
609 const std::string ColorFracName = GetTerrain()->GetColorFractalName();
610 Output("Color Fractal name: %s\n", ColorFracName.c_str());
611
612 Uint32 GasGiantType = Graphics::OGL::GEN_JUPITER_TEXTURE;
613 if (ColorFracName == GGSaturn) {
614 GasGiantType = Graphics::OGL::GEN_SATURN_TEXTURE;
615 } else if (ColorFracName == GGSaturn2) {
616 GasGiantType = Graphics::OGL::GEN_SATURN2_TEXTURE;
617 } else if (ColorFracName == GGNeptune) {
618 GasGiantType = Graphics::OGL::GEN_NEPTUNE_TEXTURE;
619 } else if (ColorFracName == GGNeptune2) {
620 GasGiantType = Graphics::OGL::GEN_NEPTUNE2_TEXTURE;
621 } else if (ColorFracName == GGUranus) {
622 GasGiantType = Graphics::OGL::GEN_URANUS_TEXTURE;
623 }
624 const Uint32 octaves = (Pi::config->Int("AMD_MESA_HACKS") == 0) ? s_noiseOctaves[Pi::detail.planets] : std::min(5U, s_noiseOctaves[Pi::detail.planets]);
625 GasGiantType = (octaves << 16) | GasGiantType;
626
627 assert(!m_hasGpuJobRequest);
628 assert(!m_gpuJob.HasJob());
629
630 Random rng(GetSystemBody()->GetSeed() + 4609837);
631 const std::string parentname = GetSystemBody()->GetParent()->GetName();
632 const float hueShift = (parentname == "Sol") ? 0.0f : float(((rng.Double() * 2.0) - 1.0) * 0.9);
633
634 GasGiantJobs::GenFaceQuad *pQuad = new GasGiantJobs::GenFaceQuad(Pi::renderer, vector2f(s_texture_size_gpu[Pi::detail.planets], s_texture_size_gpu[Pi::detail.planets]), s_quadRenderState, GasGiantType);
635
636 GasGiantJobs::SGPUGenRequest *pGPUReq = new GasGiantJobs::SGPUGenRequest(GetSystemBody()->GetPath(), s_texture_size_gpu[Pi::detail.planets], GetTerrain(), GetSystemBody()->GetRadius(), hueShift, pQuad, m_builtTexture.Get());
637 m_gpuJob = Pi::GetSyncJobQueue()->Queue(new GasGiantJobs::SingleGPUGenJob(pGPUReq));
638 m_hasGpuJobRequest = true;
639 }
640 }
641
Update()642 void GasGiant::Update()
643 {
644 PROFILE_SCOPED()
645 // assuming that we haven't already generated the texture from the render call.
646 if (m_timeDelay > 0.0f) {
647 m_timeDelay -= Pi::game->GetTimeStep();
648 if (m_timeDelay <= 0.0001f && !m_surfaceTexture.Valid()) {
649 // Use the fact that we have a patch as a latch to prevent repeat generation requests.
650 if (m_patches[0].get())
651 return;
652
653 BuildFirstPatches();
654 return;
655 }
656 }
657 }
658
Render(Graphics::Renderer * renderer,const matrix4x4d & modelView,vector3d campos,const float radius,const std::vector<Camera::Shadow> & shadows)659 void GasGiant::Render(Graphics::Renderer *renderer, const matrix4x4d &modelView, vector3d campos, const float radius, const std::vector<Camera::Shadow> &shadows)
660 {
661 PROFILE_SCOPED()
662 if (!m_surfaceTexture.Valid()) {
663 // Use the fact that we have a patch as a latch to prevent repeat generation requests.
664 if (!m_patches[0].get()) {
665 BuildFirstPatches();
666 }
667 }
668
669 // store this for later usage in the update method.
670 m_tempCampos = campos;
671 m_hasTempCampos = true;
672
673 matrix4x4d trans = modelView;
674 trans.Translate(-campos.x, -campos.y, -campos.z);
675 renderer->SetTransform(matrix4x4f(trans)); //need to set this for the following line to work
676 matrix4x4d modv = matrix4x4d(renderer->GetTransform());
677 matrix4x4d proj = matrix4x4d(renderer->GetProjection());
678 Graphics::Frustum frustum(modv, proj);
679
680 // no frustum test of entire gasSphere, since Space::Render does this
681 // for each body using its GetBoundingRadius() value
682
683 //First draw - create materials (they do not change afterwards)
684 if (!m_surfaceMaterial)
685 SetUpMaterials();
686
687 {
688 //Update material parameters
689 //XXX no need to calculate AP every frame
690 m_materialParameters.atmosphere = GetSystemBody()->CalcAtmosphereParams();
691 m_materialParameters.atmosphere.center = trans * vector3d(0.0, 0.0, 0.0);
692 m_materialParameters.atmosphere.planetRadius = radius;
693
694 m_materialParameters.shadows = shadows;
695
696 m_surfaceMaterial->specialParameter0 = &m_materialParameters;
697
698 if (m_materialParameters.atmosphere.atmosDensity > 0.0) {
699 m_atmosphereMaterial->specialParameter0 = &m_materialParameters;
700
701 // make atmosphere sphere slightly bigger than required so
702 // that the edges of the pixel shader atmosphere jizz doesn't
703 // show ugly polygonal angles
704 DrawAtmosphereSurface(renderer, trans, campos, m_materialParameters.atmosphere.atmosRadius * 1.01, m_atmosRenderState, m_atmosphereMaterial);
705 }
706 }
707
708 Color ambient;
709
710 // save old global ambient
711 const Color oldAmbient = renderer->GetAmbientColor();
712
713 {
714 // give planet some ambient lighting if the viewer is close to it
715 double camdist = campos.Length();
716 camdist = 0.1 / (camdist * camdist);
717 // why the fuck is this returning 0.1 when we are sat on the planet??
718 // JJ: Because campos is relative to a unit-radius planet - 1.0 at the surface
719 // XXX oh well, it is the value we want anyway...
720 ambient.r = ambient.g = ambient.b = camdist * 255;
721 ambient.a = 255;
722 }
723
724 renderer->SetAmbientColor(ambient);
725
726 renderer->SetTransform(matrix4x4f(modelView));
727
728 for (int i = 0; i < NUM_PATCHES; i++) {
729 m_patches[i]->Render(renderer, campos, modelView, frustum);
730 }
731
732 m_surfaceMaterial->Unapply();
733
734 renderer->SetAmbientColor(oldAmbient);
735
736 renderer->GetStats().AddToStatCount(Graphics::Stats::STAT_GASGIANTS, 1);
737 }
738
SetUpMaterials()739 void GasGiant::SetUpMaterials()
740 {
741 //solid
742 Graphics::RenderStateDesc rsd;
743 m_surfRenderState = Pi::renderer->CreateRenderState(rsd);
744
745 //blended
746 rsd.blendMode = Graphics::BLEND_ALPHA_ONE;
747 rsd.cullMode = Graphics::CULL_NONE;
748 rsd.depthWrite = false;
749 m_atmosRenderState = Pi::renderer->CreateRenderState(rsd);
750
751 // Request material for this planet, with atmosphere.
752 // Separate materials for surface and sky.
753 Graphics::MaterialDescriptor surfDesc;
754 surfDesc.effect = Graphics::EFFECT_GASSPHERE_TERRAIN;
755
756 //planetoid with atmosphere
757 const AtmosphereParameters ap(GetSystemBody()->CalcAtmosphereParams());
758 surfDesc.lighting = true;
759 assert(ap.atmosDensity > 0.0);
760 {
761 surfDesc.quality |= Graphics::HAS_ATMOSPHERE;
762 }
763
764 surfDesc.quality |= Graphics::HAS_ECLIPSES;
765 surfDesc.textures = 1;
766
767 assert(m_surfaceTextureSmall.Valid() || m_surfaceTexture.Valid());
768 m_surfaceMaterial.Reset(Pi::renderer->CreateMaterial(surfDesc));
769 m_surfaceMaterial->texture0 = m_surfaceTexture.Valid() ? m_surfaceTexture.Get() : m_surfaceTextureSmall.Get();
770
771 {
772 Graphics::MaterialDescriptor skyDesc;
773 skyDesc.effect = Graphics::EFFECT_GEOSPHERE_SKY;
774 skyDesc.lighting = true;
775 skyDesc.quality |= Graphics::HAS_ECLIPSES;
776 m_atmosphereMaterial.Reset(Pi::renderer->CreateMaterial(skyDesc));
777 }
778 }
779
BuildFirstPatches()780 void GasGiant::BuildFirstPatches()
781 {
782 PROFILE_SCOPED()
783 if (s_patchContext.Get() == nullptr) {
784 s_patchContext.Reset(new GasPatchContext(127));
785 }
786
787 using namespace GasGiantJobs;
788 m_patches[0].reset(new GasPatch(s_patchContext, this, p1, p2, p3, p4));
789 m_patches[1].reset(new GasPatch(s_patchContext, this, p4, p3, p7, p8));
790 m_patches[2].reset(new GasPatch(s_patchContext, this, p1, p4, p8, p5));
791 m_patches[3].reset(new GasPatch(s_patchContext, this, p2, p1, p5, p6));
792 m_patches[4].reset(new GasPatch(s_patchContext, this, p3, p2, p6, p7));
793 m_patches[5].reset(new GasPatch(s_patchContext, this, p8, p7, p6, p5));
794
795 GenerateTexture();
796 }
797
Init()798 void GasGiant::Init()
799 {
800 IniConfig cfg;
801 cfg.Read(FileSystem::gameDataFiles, "configs/GasGiants.ini");
802 // NB: limit the ranges of all values loaded from the file
803 // NB: round to the nearest power of 2 for all texture sizes
804 s_texture_size_small = ceil_pow2(Clamp(cfg.Int("texture_size_small", 16), 16, 64));
805
806 SplitData(cfg.String("texture_size_0"), s_texture_size_cpu[0], s_texture_size_gpu[0], s_noiseOctaves[0]);
807 SplitData(cfg.String("texture_size_1"), s_texture_size_cpu[1], s_texture_size_gpu[1], s_noiseOctaves[1]);
808 SplitData(cfg.String("texture_size_2"), s_texture_size_cpu[2], s_texture_size_gpu[2], s_noiseOctaves[2]);
809 SplitData(cfg.String("texture_size_3"), s_texture_size_cpu[3], s_texture_size_gpu[3], s_noiseOctaves[3]);
810 SplitData(cfg.String("texture_size_4"), s_texture_size_cpu[4], s_texture_size_gpu[4], s_noiseOctaves[4]);
811
812 s_initialCPUDelayTime = Clamp(cfg.Float("cpu_delay_time", 60.0f), 0.0f, 120.0f);
813 s_initialGPUDelayTime = Clamp(cfg.Float("gpu_delay_time", 5.0f), 0.0f, 120.0f);
814
815 if (s_patchContext.Get() == nullptr) {
816 s_patchContext.Reset(new GasPatchContext(127));
817 }
818 CreateRenderTarget(s_texture_size_gpu[Pi::detail.planets], s_texture_size_gpu[Pi::detail.planets]);
819 }
820
Uninit()821 void GasGiant::Uninit()
822 {
823 s_patchContext.Reset();
824 }
825
826 //static
CreateRenderTarget(const Uint16 width,const Uint16 height)827 void GasGiant::CreateRenderTarget(const Uint16 width, const Uint16 height)
828 {
829 /* @fluffyfreak here's a rendertarget implementation you can use for oculusing and other things. It's pretty simple:
830 - fill out a RenderTargetDesc struct and call Renderer::CreateRenderTarget
831 - pass target to Renderer::SetRenderTarget to start rendering to texture
832 - set up viewport, clear etc, then draw as usual
833 - SetRenderTarget(0) to resume render to screen
834 - you can access the attached texture with GetColorTexture to use it with a material
835 You can reuse the same target with multiple textures.
836 In that case, leave the color format to NONE so the initial texture is not created, then use SetColorTexture to attach your own.
837 */
838 Graphics::RenderStateDesc rsd;
839 rsd.depthTest = false;
840 rsd.depthWrite = false;
841 rsd.blendMode = Graphics::BLEND_ALPHA;
842 s_quadRenderState = Pi::renderer->CreateRenderState(rsd);
843
844 // Complete the RT description so we can request a buffer.
845 // NB: we don't want it to create use a texture because we share it with the textured quad created above.
846 Graphics::RenderTargetDesc rtDesc(
847 width,
848 height,
849 Graphics::TEXTURE_NONE, // don't create a texture
850 Graphics::TEXTURE_NONE, // don't create a depth buffer
851 false);
852 s_renderTarget = Pi::renderer->CreateRenderTarget(rtDesc);
853 }
854
855 //static
SetRenderTargetCubemap(const Uint32 face,Graphics::Texture * pTexture,const bool unBind)856 void GasGiant::SetRenderTargetCubemap(const Uint32 face, Graphics::Texture *pTexture, const bool unBind /*= true*/)
857 {
858 s_renderTarget->SetCubeFaceTexture(face, pTexture);
859 }
860
861 //static
BeginRenderTarget()862 void GasGiant::BeginRenderTarget()
863 {
864 Pi::renderer->SetRenderTarget(s_renderTarget);
865 }
866
867 //static
EndRenderTarget()868 void GasGiant::EndRenderTarget()
869 {
870 Pi::renderer->SetRenderTarget(nullptr);
871 }
872