Advertisement

How to make a Galaxy Generator in C++ using OpenGL and SDL.

Started by March 11, 2024 09:51 PM
18 comments, last by Acosix 9 months, 1 week ago

I don't know a lot about C++, I actually know more about C# and I've never made a game without a game engine, I only made games in Unity in this case.

But I want to make games with a lot of GameObjects (for those who know Unity), or objects, items, etc. in the screen, like for example Factorio, but I can't do this on Unity, not from what I know at least, I would have to use ECS and Dots, but it doesn't work on my computer at the moment, according to the forum it's something related to shader model. So the only option for me, is to make a game from scratch, without a game engine and then later learn Data Oriented Programming to optimize the game.

My goal is to make a "3D" Galaxy, with all the location being real and not just rendered (like the code that I will show), obviously I don't want 100 bilions of stars, I think that this is too much, but I want a good number, then I will make a "solar system" in some of the stars at runtime. In my reasearch I managed to found this code "https://github.com/beltoforion/Galaxy-Renderer/tree/master", that is Galaxy Renderer, it's based on this article "https://beltoforion.de/en/spiral_galaxy_renderer/?a=spiral_galaxy_renderer", you can change some variables and create different galaxies, but from what I understand from reading the code, there is a Star struct, but it doesn't even have any x, y and z position on the struct, it seems more like a spherical coordinate system, because it has an angle (theta0) and the tilt (tiltAngle), and the Vec3 which has x, y and z floats isn't used in a lot of places, so it doesn't keep stored the position of the stars, they kinda have a fixed movement and are being rendered in the screen. Unless i'm not understanding it right....

So since I want to make a game, I read a book "Game Programming in C++: Creating 3D Games (Game Design) 1st Edition", it uses C++, OpenGl and SDL, then I tried to implement the code from the Galaxy Renderer repository with code from this book, I manage to make it work, but it's not a 3D galaxy and I don't know how to make it one, I don't think that its even possible, because as I said I don't think that the autor of the code uses any sort of Vector3 to store the positions of any star. So I found another link that is a 3D Galaxy, the link is "https://github.com/frozein/VkGalaxy/tree/d5a33109c34213a4e112cf374795bde21eb92189", but he uses Vulkan, which I don't have any knowledge.

The code from the book that I'm using is here "https://github.com/gameprogcpp/code/tree/master", Chapter 11.

Main.cpp

#include "Game.h"

int main(int argc, char** argv)
{
	Game game;
	bool success = game.Initialize();
	if (success)
	{
		game.RunLoop();
	}
	game.Shutdown();
	return 0;
}

The "Math.h" is from the book, the link is here: https://github.com/gameprogcpp/code/blob/master/Chapter11/Math.h

Game.h

#pragma once
#include <unordered_map>
#include <string>
#include <vector>
#include "Math.h"
#include <SDL_types.h>

class Game
{
public:
	Game();

	bool Initialize();
	void RunLoop();
	void Shutdown();

	class Renderer* GetRenderer() { return mRenderer; }
 
	enum GameState
	{
		EGameplay,
		EPaused,
		EQuit
	};
 
	GameState GetState() const { return mGameState; }
	void SetState(GameState state) { mGameState = state; }
private:
	void UpdateGame();
	void GenerateOutput();
	void HandleKeyPress(int key);
	void ProcessInput();
 
	class Renderer* mRenderer;

	Uint32 mTicksCount;

	GameState mGameState;
};

Game.cpp

#include "Game.h"
#include "Renderer.h"
#include <SDL/SDL.h>
#include <algorithm>
#include <fstream>
#include <sstream>
#include <rapidjson/document.h>

Game::Game(): mRenderer(nullptr), mGameState(EGameplay){ }

bool Game::Initialize()
{
	if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO) != 0)
	{
		SDL_Log("Unable to initialize SDL: %s", SDL_GetError());
		return false;
	}

	mRenderer = new Renderer(this);

	//This Initialize function is different from the original, see the Renderer.h and Renderer.cpp to understand.
	if (!mRenderer->Initialize(1500, 1000, 35000.0))
	{
		SDL_Log("Failed to initialize renderer");
		delete mRenderer;
		mRenderer = nullptr;
		return false;
	}

	//Functions to initialize the galaxy and opengl to draw the galaxy
	mRenderer->InitGL();
	mRenderer->InitSimulation();

	mTicksCount = SDL_GetTicks();

	return true;
}

void Game::RunLoop()
{
	while (mGameState != EQuit)  
	{
		ProcessInput();
		UpdateGame();
		GenerateOutput();
		mRenderer->UpdateGalaxy();
	}
}

void Game::Shutdown()
{
	if (mRenderer)
	{
		mRenderer->Shutdown();
	}

	SDL_Quit();
}

void Game::UpdateGame()
{
	while (!SDL_TICKS_PASSED(SDL_GetTicks(), mTicksCount + 16));

	float deltaTime = (SDL_GetTicks() - mTicksCount) / 1000.0f;
	if (deltaTime > 0.05f)
	{
		deltaTime = 0.05f;
	}
	mTicksCount = SDL_GetTicks();
}

void Game::GenerateOutput()
{
	mRenderer->DrawGalaxy();
}

void Game::HandleKeyPress(int key)
{
	switch (key)
	{
	case SDLK_ESCAPE:
		SetState(Game::EQuit);
		break;
	case SDLK_KP_PLUS:
		mRenderer->ScaleAxis(0.9f);
		mRenderer->SetCameraOrientation({ 0,1,0 });
		break;
	case SDLK_KP_MINUS:
		mRenderer->ScaleAxis(1.1f);
		mRenderer->SetCameraOrientation({ 0,1,0 });
		break;
	default:
		break;
	}
}

void Game::ProcessInput()
{
	SDL_Event event;
	while (SDL_PollEvent(&event))
	{
		switch (event.type)
		{
		case SDL_QUIT:
			mGameState = EQuit;
			break;
		case SDL_KEYDOWN:
			if (!event.key.repeat)
			{
				if (mGameState == EGameplay)
				{
					HandleKeyPress(event.key.keysym.sym);
				}
			}
			break;
		}
	}
}

All these hpp files from the Galaxy Renderer can be found here, https://github.com/beltoforion/Galaxy-Renderer/tree/master.

Renderer.h

#pragma once
#include <string>
#include <vector>
#include <unordered_map>
#include <SDL.h>
#include "Math.h"
#include "Galaxy/Galaxy.hpp"
#include "Galaxy/VertexBufferBase.hpp"
#include "Galaxy/VertexBufferLines.hpp"
#include "Galaxy/VertexBufferStars.hpp"
#include "Galaxy/TextBuffer.hpp"

struct DirectionalLight
{
	// Direction of light
	Vector3 mDirection;
	// Diffuse color
	Vector3 mDiffuseColor;
	// Specular color
	Vector3 mSpecColor;
};

class Renderer
{
public:
	Renderer(class Game* game);
	~Renderer();

	bool Initialize(float screenWidth, float screenHeight, float axisLen);
	void Shutdown();

	void InitGL();
	void InitSimulation();

	void AdjustCamera();

	void DrawGalaxy();
	void UpdateGalaxy();

	void SetViewMatrix(const Matrix4& view) { mView = view; }

	Vector3 Unproject(const Vector3& screenPoint) const;

	void GetScreenDirection(Vector3& outStart, Vector3& outDir) const;
	float GetScreenWidth() const { return mScreenWidth; }
	float GetScreenHeight() const { return mScreenHeight; }

	void SetCameraOrientation(const glm::vec3& orientation);
	void ScaleAxis(float scale);
private:
	class Game* mGame;
	Matrix4 mView;
	Matrix4 mProjection;

	float mScreenWidth;
	float mScreenHeight;

	SDL_Window* mWindow;

	SDL_GLContext mContext;

	//GALAXY VARIABLES
	enum class DisplayItem : uint32_t
	{
		NONE = 0,
		AXIS = 1 << 1,
		STARS = 1 << 2,
		PAUSE = 1 << 3,
		HELP = 1 << 4,
		DENSITY_WAVES = 1 << 5,
		VELOCITY = 1 << 6,
		DUST = 1 << 7,
		H2 = 1 << 8,
		FILAMENTS = 1 << 9,
	};

	enum RenderUpdateHint : uint32_t
	{
		ruhNONE = 0,
		ruhDENSITY_WAVES = 1 << 1,
		ruhAXIS = 1 << 2,
		ruhSTARS = 1 << 3,
		ruhDUST = 1 << 4,
		ruhCREATE_VELOCITY_CURVE = 1 << 5,
		ruhCREATE_TEXT = 1 << 7
	};

	uint32_t _flags;

	Galaxy _galaxy;

	uint32_t _renderUpdateHint;

	VertexBufferLines _vertDensityWaves;
	VertexBufferLines _vertAxis;
	VertexBufferLines _vertVelocityCurve;
	VertexBufferStars _vertStars;

	std::vector<Galaxy::GalaxyParam> _predefinedGalaxies;

	void UpdateDensityWaves();
	void UpdateAxis();
	void UpdateStars();
	void UpdateVelocityCurve();
	void AddEllipsisVertices(std::vector<VertexColor>& vert, std::vector<int>& vertIdx, float a, float b, float angle, uint32_t pertNum, float pertAmp, ColorG col) const;

protected:
	glm::mat4 _matProjection;
	glm::mat4 _matView;
 
	void SetCamera(const glm::vec3& pos, const glm::vec3& lookAt, const glm::vec3& orient);

	glm::vec3 _camPos;  
	glm::vec3 _camLookAt;  
	glm::vec3 _camOrient;

	float _fov;
};

Texture.h and Shader.h are also from the book, their link are these, https://github.com/gameprogcpp/code/blob/master/Chapter11/Texture.h and https://github.com/gameprogcpp/code/blob/master/Chapter11/Shader.h respectivally.

Renderer.cpp

#include "Renderer.h"
#include "Texture.h"
#include <algorithm>
#include "Shader.h"
#include "Game.h"
#include <glew.h>

Renderer::Renderer(Game* game):mGame(game)
, _flags((int)DisplayItem::STARS | (int)DisplayItem::AXIS | (int)DisplayItem::HELP | (int)DisplayItem::DUST | (int)DisplayItem::H2 | (int)DisplayItem::FILAMENTS)
, _galaxy()
, _renderUpdateHint(ruhDENSITY_WAVES | ruhAXIS | ruhSTARS | ruhDUST | ruhCREATE_VELOCITY_CURVE | ruhCREATE_TEXT)
, _vertDensityWaves(2)
, _vertAxis()
, _vertVelocityCurve(1, GL_DYNAMIC_DRAW)
, _vertStars(GL_FUNC_ADD, GL_ONE)
, _fov(0) { }

Renderer::~Renderer() { }

bool Renderer::Initialize(float screenWidth, float screenHeight, float axisLen)
{
	_fov = axisLen;
	mScreenWidth = screenWidth;
	mScreenHeight = screenHeight;

	SDL_Init(SDL_INIT_VIDEO);

	SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
	SDL_GL_SetAttribute(SDL_GL_ACCELERATED_VISUAL, 1);
	SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8);
	SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8);
	SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8);
	SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 8);

	SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
	SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3);

	SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);

	mWindow = SDL_CreateWindow("Super Heroes & Villains", 100, 100,
 static_cast<int>(mScreenWidth), static_cast<int>(mScreenHeight), SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE);

	if (!mWindow)
	{
		SDL_Log("Failed to create window: %s", SDL_GetError());
		return false;
	}

	mContext = SDL_GL_CreateContext(mWindow);  

	glewExperimental = GL_TRUE;
	if (glewInit() != GLEW_OK)
	{
		SDL_Log("Failed to initialize GLEW.");
		return false;
	}

	glGetError();
	SDL_SetWindowFullscreen(mWindow, SDL_WINDOW_FULLSCREEN);
	return true;
}

void Renderer::Shutdown()
{
	SDL_GL_DeleteContext(mContext);
	SDL_DestroyWindow(mWindow);
}

void Renderer::AdjustCamera()
{
	double l = _fov / 2.0;
	double aspect = (double)static_cast<int>(mScreenWidth) / static_cast<int>(mScreenHeight);
	_matProjection = glm::ortho(-l * aspect, l * aspect, -l, l, -l, l);

	glm::dvec3 camPos(_camPos.x, _camPos.y, _camPos.z);
	glm::dvec3 camLookAt(_camLookAt.x, _camLookAt.y, _camLookAt.z);
	glm::dvec3 camOrient(_camOrient.x, _camOrient.y, _camOrient.z);
	_matView = glm::lookAt(camPos, camLookAt, camOrient);
}

void Renderer::DrawGalaxy()
{
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

	AdjustCamera();

	if (_flags & (int)DisplayItem::AXIS)
	{
		_vertAxis.Draw(_matView, _matProjection);
		CHECK_GL_ERROR
	}

	int features = 0;
	if (_flags & (int)DisplayItem::STARS)
		features |= 1 << 0;

	if (_flags & (int)DisplayItem::DUST)
		features |= 1 << 1;

	if (_flags & (int)DisplayItem::FILAMENTS)
		features |= 1 << 2;

	if (_flags & (int)DisplayItem::H2)
		features |= 1 << 3;

	if (features != 0)
	{
		_vertStars.UpdateShaderVariables(0, _galaxy.GetPertN(), _galaxy.GetPertAmp(), 	(int)_galaxy.GetDustRenderSize(), features);
		_vertStars.Draw(_matView, _matProjection);
	}

	if (_flags & (int)DisplayItem::DENSITY_WAVES)
	{
		_vertDensityWaves.Draw(_matView, _matProjection);
	}

	if (_flags & (int)DisplayItem::VELOCITY)
		_vertVelocityCurve.Draw(_matView, _matProjection);

	SDL_GL_SwapWindow(mWindow);
}

void Renderer::UpdateGalaxy()
{
	if (!(_flags & (int)DisplayItem::PAUSE))
 	//_time += GalaxyWnd::TimeStepSize;

	if ((_renderUpdateHint & ruhDENSITY_WAVES) != 0)
 		UpdateDensityWaves();

	if ((_renderUpdateHint & ruhAXIS) != 0)
 		UpdateAxis();

	if ((_renderUpdateHint & ruhSTARS) != 0)
 		UpdateStars();

	if ((_renderUpdateHint & ruhCREATE_VELOCITY_CURVE) != 0)
 		UpdateVelocityCurve();

	_camOrient = { 0, 1, 0 };
	_camPos = { 0, 0, 5000 };
	_camLookAt = { 0, 0, 0 };
}

void Renderer::InitGL()
{
	glViewport(0, 0, GetScreenWidth(), GetScreenHeight());

	_vertDensityWaves.Initialize();
	_vertAxis.Initialize();
	_vertVelocityCurve.Initialize();
	_vertStars.Initialize();

	glDisable(GL_DEPTH_TEST);
	glClearColor(0.0f, .0f, 0.08f, 0.0f);
	SetCameraOrientation({ 0, 1, 0 });
}

void Renderer::InitSimulation()
{
	_galaxy.Reset({13000, 4000, 0.0004f, 0.85f, 0.95f, 100000, true, 2, 40, 70, 4000});
}

void Renderer::UpdateDensityWaves()
{
	std::vector<VertexColor> vert;
	std::vector<int> idx;

	int num = 100;
	float dr = _galaxy.GetFarFieldRad() / num;
	for (int i = 0; i <= num; ++i)
	{
		float r = dr * (i + 1);
	 	AddEllipsisVertices(vert, idx, r, r * _galaxy.GetExcentricity(r), Helper::RAD_TO_DEG * _galaxy.GetAngularOffset(r), _galaxy.GetPertN(), _galaxy.GetPertAmp(), { 1, 1, 1, 0.2f });
	}
 
	int pertNum = 0;
	float pertAmp = 0;
	auto r = _galaxy.GetCoreRad();
	AddEllipsisVertices(vert, idx, r, r, 0, pertNum, pertAmp, { 1, 1, 0, 0.5 });

	r = _galaxy.GetRad();
	AddEllipsisVertices(vert, idx, r, r, 0, pertNum, pertAmp, { 0, 1, 0, 0.5 });

	r = _galaxy.GetFarFieldRad();
	AddEllipsisVertices(vert, idx, r, r, 0, pertNum, pertAmp, { 1, 0, 0, 0.5 });

	_vertDensityWaves.CreateBuffer(vert, idx, GL_LINE_STRIP);

	_renderUpdateHint &= ~ruhDENSITY_WAVES;
}

void Renderer::UpdateAxis()
{
	std::vector<VertexColor> vert;
	std::vector<int> idx;

	GLfloat s = (GLfloat)std::pow(10, (int)(std::log10(_fov / 2)));
	GLfloat l = _fov / 100, p = 0;

	float r = 0.3, g = 0.3, b = 0.3, a = 0.8;
	for (int i = 0; p < _fov; ++i)
	{
		p += s;
		idx.push_back((int)vert.size());
		vert.push_back({ p, -l, 0, r, g, b, a });

 		idx.push_back((int)vert.size());
 		vert.push_back({ p,  l, 0, r, g, b, a });

 		idx.push_back((int)vert.size());
 		vert.push_back({ -p, -l, 0, r, g, b, a });

 		idx.push_back((int)vert.size());
 		vert.push_back({ -p,  0, 0, r, g, b, a });

 		idx.push_back((int)vert.size());
 		vert.push_back({ -l, p, 0, r, g, b, a });

 		idx.push_back((int)vert.size());
 		vert.push_back({ 0, p, 0, r, g, b, a });

 		idx.push_back((int)vert.size());
 		vert.push_back({ -l, -p, 0, r, g, b, a });

 		idx.push_back((int)vert.size());
 		vert.push_back({ 0, -p, 0, r, g, b, a });
	}

	idx.push_back((int)vert.size());
	vert.push_back({ -_fov, 0, 0, r, g, b, a });

	idx.push_back((int)vert.size());
	vert.push_back({ _fov, 0, 0, r, g, b, a });

	idx.push_back((int)vert.size());
	vert.push_back({ 0, -_fov, 0, r, g, b, a });

	idx.push_back((int)vert.size());
	vert.push_back({ 0, _fov, 0, r, g, b, a });

	_vertAxis.CreateBuffer(vert, idx, GL_LINES);

	s = (GLfloat)std::pow(10, (int)(std::log10(_fov / 2)));
	l = _fov / 100, p = 0;

	_renderUpdateHint &= ~ruhAXIS;
}

void Renderer::UpdateStars()
{
	std::vector<VertexStar> vert;
	std::vector<int> idx;

	const auto& stars = _galaxy.GetStars();

	float a = 1;
	ColorG color = { 1, 1, 1, a };

	for (int i = 1; i < stars.size(); ++i)
	{
 		const ColorG& col = Helper::ColorFromTemperature(stars[i].temp);
 		color = { col.r, col.g, col.b, a };

 		idx.push_back((int)vert.size());
 		vert.push_back({ stars[i], color });
	}

	_vertStars.CreateBuffer(vert, idx, GL_POINTS);
	_renderUpdateHint &= ~ruhSTARS;
}

void Renderer::UpdateVelocityCurve()
{
	const auto& stars = _galaxy.GetStars();

	std::vector<VertexColor> vert;
	vert.reserve(1000);

	std::vector<int> idx;
	idx.reserve(1000);

	float r = 0, v = 0;
	float cr = 0.5, cg = 1, cb = 1, ca = 1;
	for (int r = 0; r < _galaxy.GetFarFieldRad(); r += 100)
	{
 		idx.push_back((int)vert.size());

 		if (_galaxy.HasDarkMatter())
  		vert.push_back({ (float)r, Helper::VelocityWithDarkMatter((float)r) * 10.f, 0,  cr, cg, cb, ca });
 else
  		vert.push_back({ (float)r, Helper::VelocityWithoutDarkMatter((float)r) * 10.f, 0,  cr, cg, cb, ca });
	}

	_vertVelocityCurve.CreateBuffer(vert, idx, GL_POINTS);
	_renderUpdateHint &= ~ruhCREATE_VELOCITY_CURVE;
}

void Renderer::AddEllipsisVertices(std::vector<VertexColor>& vert, std::vector<int>& vertIdx, float a, float b, float angle, uint32_t pertNum, float pertAmp, ColorG col) const
{
	const int steps = 100;
	const float x = 0;
	const float y = 0;

	float beta = -angle * Helper::DEG_TO_RAD;
	float sinbeta = std::sin(beta);
	float cosbeta = std::cos(beta);

	int firstPointIdx = static_cast<int>(vert.size());
	for (int i = 0; i < 360; i += 360 / steps)
	{
 		float alpha = i * Helper::DEG_TO_RAD;
 		float sinalpha = std::sin(alpha);
 		float cosalpha = std::cos(alpha);

 		GLfloat fx = (GLfloat)(x + (a * cosalpha * cosbeta - b * sinalpha * sinbeta));
 		GLfloat fy = (GLfloat)(y + (a * cosalpha * sinbeta + b * sinalpha * cosbeta));

 		if (pertNum > 0)
 		{
  			fx += (GLfloat)((a / pertAmp) * std::sin(alpha * 2 * pertNum));
  			fy += (GLfloat)((a / pertAmp) * std::cos(alpha * 2 * pertNum));
 		}

 		vertIdx.push_back((int)vert.size());

 		VertexColor vc = { fx, fy, 0, col.r, col.g, col.b, col.a };
 		vert.push_back(vc);
	}

	vertIdx.push_back(firstPointIdx);
	vertIdx.push_back(0xFFFF);
}

void Renderer::SetCameraOrientation(const glm::vec3& orient)
{
	_camOrient = orient;
	AdjustCamera();
}

void Renderer::ScaleAxis(float scale)
{
	_fov *= scale;
	AdjustCamera();
}

void Renderer::SetCamera(const glm::vec3& pos, const glm::vec3& lookAt, const glm::vec3& orient)
{
	_camOrient = orient;
	_camPos = pos;
	_camLookAt = lookAt;
	AdjustCamera();
}

That code produces the default Galaxy from the Galaxy Renderer.

I think your next step would be to figure out what it is that you actually want.

Anything galaxy scale is going to rapidly fail due to math issues. Even planetary scale is difficult in games for many reasons.

Floating point math only has a few digits of useful numbers. A float, or single-precision number, has about 6 decimal digits of precision. That means if you're able to represent a centimeter scale you're limited to about 4 kilometers. A double, or double-precision number, gives about 15 decimal digits of precision which can work for representing things on the surface of the planet, but you'll have to do a lot of work to turn it into something you can render.

Anything on interstellar scale is going to be basically meaningless with either single-precision or double-precision numbers. You'll need to use entirely different math systems to represent them in a meaningful way, and you'll need to convert them into something meaningful to render.

Beyond just that math issue, you'll have an issue of content. The vast majority of space is empty. Even on the planet, 2/3 of it is water, and humanity is packed into tight little clusters of cities. Video games often limit their worlds to just a few square kilometers of virtual space and that's more than enough for interesting content. For comparison Zelda BotW is about 8 x 10 kilometers, measured by data miners who unpacked the maps. GTA V is about 81 square kilometers. Most maps on Ark 1 are 40, 64, or 81 square kilometers. Most game set in space aren't about the vast open distances of actual space, they're generally set in tiny areas for dogfighting, space stations, or dodging asteroids / space mines that in practice aren't that big.

If you're trying to get a bunch of dots that spin and move, that's a different objective than an actual interstellar simulation, different again from a space traveling game, which itself would either need you to create fictional rules around faster-than-light travel or wait more than 4 years at light speed just to travel from our solar system to Proxima Centauri, our nearest star. As for watching stars move, that's something computers notice incredibly small changes each night, and generations notice constellations barely drifting in the sky, where you're better off just drawing dots on a large texture and calling it the sky.

What is it you actually want in your game? What's your objective for the galaxy of stars?

Advertisement

I'm aware of the floating point problem, I guess what I meant by “real” is not the real scale of the galaxy, I meant more that just 2D rendered in the screen, like that other link where you can orbit the galaxy and see the Stars. This step of representing the galaxy on a “real” scale is a thing that I will try to do later, and I'm also aware of ways to represent this stuff, I already researched about and find it interesting, there are games that have galaxy size maps, if I'm not mistaken, there are ways to do it, yes I know that it's hard. Regarding the content issue, I'm also aware of those games out there with vast spaces but empty and soulless, I guess that I can think of some ways around this that would work in this game, but that's way beyond what I'm trying to do right now. And is even something that I could cut out, depending of what I want to do after, maybe limiting stuff to planets.

I guess that I didn't expressed myself right. I don't want a game about interstellar simulation, yes there will be space traveling in this game, and the distance will not be problem, there will be multiple ways to travel those imense distances, I think the problem will be the incentive to travel to other places. Yes, stars/constellations and galaxies barely move in the sky, but I dont want them to move, time would pass more or less like ours. Well I don't want to simulate the sky or anything like that, It would be cool though, and possible I guess.

My objective is that the galaxy would be the local where the players will play, I just want to simulate a galaxy in a procedural way, so that it doesn't seems weird and unrealistic, that site codes provides that, but is rendered on the screen in a 2D way, and I want it to be rendered in a 3D way. Then maybe somehow use the information to make the locations of the stars, then make the solar systems and after I could use some “tricks”, like the ones they used on KSP (Kerbal Space Program) and another games to render and load different things, planets, stars, etc.

So you want a game with multiple planets, requiring to generate most of your stuff procedurally.

Since universe and galaxy scale has to be a matter of tricks, and probably solar system scale as well, i would not get started by developing those tricks.
I would rather start at a single procedural planet and care about the tricks later.
Planet level likely is the scale where the real work is, since that's the scale a player can actually observe in detail and interact with. From earth all other planets already are just single pixels on a static environment map for example. Only moon and sun are simple spheres.

So a good question would be how to make procedural planets, which is something many people work on. I see height map based approaches using 6 maps to deform a cube into a sphere and doing height displacement. That's simple but can't do caves, arches or overhangs, and steep cliffs become lower quality.
I also see voxel based techniques, sometimes replacing cubes with 6 sided cylinders, or prisms from icosahedron basis, etc. This resolves the heightmap limitations but is much higher complexity, making procedural generation much harder as well.

That's likely your first big question you should think about. I see related projects on YouTube and also here on this forum.

@JoeJ I want to develop things from “top to bottom”, from the galaxy to the planets, because I have seen more tutorials and stuff about planets, so in my mind that part is more easy, making a galaxy in the another hand I didn't see many or almost none tutorials and stuff, so that is the hard part. And the galaxy will also be important in the procedural story of the game.

biell2015 said:
making a galaxy in the another hand I didn't see many or almost none tutorials and stuff, so that is the hard part.

Personally i have no doubt that making a single planet is multiple orders of magnitudes harder than making a galaxy. Galaxy is just a distribution of points, eventually forming a spiral or an ellipse, which is easy. People rarely work on this not because it's hard, but because they don't need it for their games.

And i would agree to a to down philosophy, as i usually think the same way.
But here it does not apply because there is a gap of nothing between the scales: Universe → galaxies → solar systems → planets. The gap is so huge, having sorted out the larger scale does not help at all to create the smaller scales.

Thus, trying to minimize risk of wasted work, i would start with the hardest problem. Because if i fail at that and have to give up, no time is wasted for stuff which works but is now useless.

However, i assume that planet surface matters for your game. That you can land on a planet, leave the ship and walk on it, eventually shooting stuff or building bases. If you don't need this at all, and the game is mainly about space trade and dogfights, i fully agree with you. (And it's all just said for the sake of discussion anyway.)

Edit: Oh, and since you have have started learning C++, maybe starting with the hard stuff is not really a good idea. I do not say you should stop working on galaxies, i only mean maybe there is no point yet to go into all related details.

Advertisement

Seems likely that you're going to have to partition the space, in order to make doubles do the job. Speaking of partitioning the space, it also helps simplify the gravitation – only neighbours are used to calculate gravitation. It's a hack, but it does the job.

@JoeJ

JoeJ said:

Personally i have no doubt that making a single planet is multiple orders of magnitudes harder than making a galaxy. Galaxy is just a distribution of points, eventually forming a spiral or an ellipse, which is easy. People rarely work on this not because it's hard, but because they don't need it for their games.

I personally find making a planet more easy, the plante itself, viewed from space, it's basically making a sphere, making a mesh, raising the number of vertices, putting noise on the mesh and changing the mesh with the noise based on height to make montains and bottom of oceans, Sebastian Lague for example has tutorials on this, also a lot of other people. Now speaking about the surface, it just a terrain, which also have a lot of tutorials, it just procedural mesh generation, a plane, there is also noise, and changing the mesh with the noise based on height, and then after everything cames down to loading chunks of terrain in the right moments, you would have also to fix the problem in the edges. There is also a lot of tutorials about that, I can remember all this stuff just from memory. I don't see a lot of games with those sort of stuff, entire planets surfaces being rendered, but there is a lot of tutorials about that, in the another hand, I don't see any tutorial about galaxies, but there is also not a lot of games with galaxy, which is a good point.

JoeJ said:
And i would agree to a to down philosophy, as i usually think the same way. But here it does not apply because there is a gap of nothing between the scales: Universe → galaxies → solar systems → planets. The gap is so huge, having sorted out the larger scale does not help at all to create the smaller scales.

I will not use a Universe, so there isn't that on my scale. Obviosly it helps, by creating the galaxy, I would have all stars position, everything that I would need to do is to make a solar system around them, which is just an elipse and then make planets. Not that hard, assuming that I already found stuff about how to do it, as I said.

JoeJ said:
Thus, trying to minimize risk of wasted work, i would start with the hardest problem. Because if i fail at that and have to give up, no time is wasted for stuff which works but is now useless.

Well, yeah, whether it goes right or wrong, I could give up that idea of galaxy and just make the planets, is much easier and after find a way around planet to planet travelling.

JoeJ said:
However, i assume that planet surface matters for your game. That you can land on a planet, leave the ship and walk on it, eventually shooting stuff or building bases. If you don't need this at all, and the game is mainly about space trade and dogfights, i fully agree with you. (And it's all just said for the sake of discussion anyway.)

It will matter a lot.

JoeJ said:
Edit: Oh, and since you have have started learning C++, maybe starting with the hard stuff is not really a good idea. I do not say you should stop working on galaxies, i only mean maybe there is no point yet to go into all related details.

Well, maybe if no one help me, I could figure it out, if given enough time. Once in Unity I made an algorithm to make a hole (square) in the face of a object, i figure it all by myself, It took me weeks, but I did it, I was going to make the hole go all through the object, but I stopped and I went to do something else, maybe I can do this too. Game dev is hard, basically if no one helps you and it doesn't have tutorials about it, you have to figure it all by yourself.

biell2015 said:
I don't see a lot of games with those sort of stuff, entire planets surfaces being rendered, but there is a lot of tutorials about that

That's because JoeJ is right, planet surfaces are hard, at least if you are going for a decent level of realism and planet size. Most of the existing games with that sort of thing are lacking in one or both of those attributes. The reason why there are lots of tutorials about planet generation are because it's cool, and because doing something basic for a simple demo is not hard. For something like that you can ignore many practical issues of scale (float precision, LOD). However, doing something more sophisticated than you described is much much much more difficult. To give some perspective, I've been working on procedural planet terrain generation for already 1.5 years (see blog). It took me 2-3 weeks to make a simple noise-based planet generator like those tutorials, but anything beyond that takes orders of magnitude more work. I have results that are close to “state of the art" for real-time procedural generation but still expect to do at least 1 more year of work before AAA-level environment quality is possible with no artistic intervention. Just building the terrain material system and its dependencies has already taken about 9 months of 15-20 hours per week, and the work is ongoing.

I do also plan on doing galaxy generation at some point, but it's a much lower priority. I expect it to be very simple compared to planets, i.e. maybe a few weeks of work. Most of the effort will go into generating the 3D noise functions for spawning stars. Galactic space is so big and travel is so impractical that no players would ever be able to tell that the galaxy is not generated correctly.

@Aressera

Aressera said:
That's because JoeJ is right, planet surfaces are hard, at least if you are going for a decent level of realism and planet size. Most of the existing games with that sort of thing are lacking in one or both of those attributes.

He isn't. Planet size and realism it just comes down to the number of vertices and another stuff. KSP, Space Engineers, Star Citizens and No Man's Sky planets are very big, but some of those games don't have realistic planets I guess and I would need realism.

Aressera said:
The reason why there are lots of tutorials about planet generation are because it's cool, and because doing something basic for a simple demo is not hard. For something like that you can ignore many practical issues of scale (float precision, LOD). However, doing something more sophisticated than you described is much much much more difficult.

Yes, that is the reason, that is why it isn't hard, there is a lot of tutorials about it, even on LOD, and float precision. I didn't described anything sophisticated 😐, I'm describing LOD, float precision, Occlusion Culling and another tricks that I don't remember the name right now, those aren't hard, there is even videos about them.

Aressera said:
To give some perspective, I've been working on procedural planet terrain generation for already 1.5 years (see blog). It took me 2-3 weeks to make a simple noise-based planet generator like those tutorials, but anything beyond that takes orders of magnitude more work. I have results that are close to “state of the art" for real-time procedural generation but still expect to do at least 1 more year of work before AAA-level environment quality is possible with no artistic intervention. Just building the terrain material system and its dependencies has already taken about 9 months of 15-20 hours per week, and the work is ongoing.

Well yes, if everyone tries to do this from scratch it will take them a long time, that is why I said that game dev is hard. If for example the other people that make videos about this, share their code, this could be more easier, heck if companies or people that work or worked in companies shared their code or teach this stuff, assuming I guess that they could legally, that would be more easier. But they don't, so everyone starts to do this from scratch, it's like a cicle, people see that cool stuff, some can do it, some can't, the ones that can make videos or simple do it in a bad manner for others to learn, and then the ones that can't, not undertanding how to do it or simple not knowing, have to discover how to do it themselves. That is why making a galaxy is hard. I will have to almost do it from scratch.

Aressera said:
I do also plan on doing galaxy generation at some point, but it's a much lower priority. I expect it to be very simple compared to planets, i.e. maybe a few weeks of work. Most of the effort will go into generating the 3D noise functions for spawning stars. Galactic space is so big and travel is so impractical that no players would ever be able to tell that the galaxy is not generated correctly.

Yeah, maybe your implementation would be easy, maybe depending on a engine I could do this in days, on Unity for example I could just make some function to spawn spheres on a elipse from some origin, simple and quick. But like I said, I want the positions of them, it's important to me, it will relate to a lot of the game, it isn't simple.

And to be fair, the players will be able to tell whether or not the galaxy was generated correctly. Also, travel is not impractical, from the four games that I mentioned before. KSP, Space Engineers, Star citizens and No Man's Sky. 2 of them, Star Citizens and No Man's Sky, use some sort of fast than light travel, so you can trave between stars, planets and solar systems. But it's cool to travel without it too, Star Citizens from what I can remember you can go without fast than light travel, and it would take a lot of time, like here https://www.youtube.com/watch?v=UTtG1_jLcno,​ a guy took 7 hours to travel 26,000 km at 1060 m/s, which is the coolest thing ever.

The part of the Gas Giant, maybe they used the same trick that the KSP devs did, using a camera to renderize the ship and stuff around it and another camera to renderize big objects, like planets, making this effect that they are really bigger than they really are. And then as the player get closer to the planet, you could move the camera and render it more closely, not really hard stuff I guess, at least not in Unity, without a game engine it could be hard. To represent the distance, I honestly don't know how they did it, I don't remember the tricks to do it, but I know that there is ways to represent the vastness of space.

This topic is closed to new replies.

Advertisement