mirror of
https://github.com/copyrighttxt/watrbx-game-engine.git
synced 2026-09-04 20:57:49 +00:00
484 lines
15 KiB
C++
484 lines
15 KiB
C++
#include "stdafx.h"
|
|
#include "Sky.h"
|
|
|
|
#include "VisualEngine.h"
|
|
#include "TextureManager.h"
|
|
#include "ShaderManager.h"
|
|
#include "SceneManager.h"
|
|
#include "Util.h"
|
|
#include "EnvMap.h"
|
|
|
|
#include "GfxCore/Geometry.h"
|
|
#include "GfxCore/Device.h"
|
|
#include "GfxCore/States.h"
|
|
|
|
#include "v8datamodel/Sky.h"
|
|
|
|
#include "g3d/LightingParameters.h"
|
|
#include "GfxCore/pix.h"
|
|
|
|
#include "rbx/Profiler.h"
|
|
|
|
FASTFLAG(DebugRenderDownloadAssets)
|
|
FASTFLAGVARIABLE(RenderMoonBillboard, true)
|
|
|
|
namespace RBX
|
|
{
|
|
namespace Graphics
|
|
{
|
|
|
|
static const float kSunSize = 1000 / 2;
|
|
static const float kMoonSize = 500 / 2;
|
|
static const float kSkySize = 2700;
|
|
static const float kStarDistance = 2500;
|
|
static const int kStarTwinkleRate = 40;
|
|
static const float kStarTwinkleRatio = 0.2f;
|
|
|
|
static TextureRef::Status getCombinedStatus(const TextureRef (&textures)[6])
|
|
{
|
|
for (int i = 0; i < 6; ++i)
|
|
{
|
|
TextureRef::Status status = textures[i].getStatus();
|
|
|
|
if (status != TextureRef::Status_Loaded)
|
|
return status;
|
|
}
|
|
|
|
return TextureRef::Status_Loaded;
|
|
}
|
|
|
|
static Matrix4 getBillboardTransform(const RenderCamera& camera, const Vector3& position, float size)
|
|
{
|
|
if (FFlag::RenderMoonBillboard)
|
|
{
|
|
Matrix3 rotation = Math::getWellFormedRotForZVector(normalize(position - camera.getPosition()));
|
|
|
|
return Matrix4(rotation * Matrix3::fromDiagonal(Vector3(size)), position);
|
|
}
|
|
else
|
|
{
|
|
Matrix4 viewInverse = camera.getViewMatrix().inverse();
|
|
|
|
return Matrix4(viewInverse.upper3x3() * Matrix3::fromDiagonal(Vector3(size)), position);
|
|
}
|
|
}
|
|
|
|
struct SkyVertex
|
|
{
|
|
Vector3 position;
|
|
unsigned int color;
|
|
Vector2 texcoord;
|
|
};
|
|
|
|
static GeometryBatch* createQuad(Device* device, const shared_ptr<VertexLayout>& layout)
|
|
{
|
|
SkyVertex vertices[] =
|
|
{
|
|
{ Vector3(-1, -1, 0), 0xffffffff, Vector2(0, 1) },
|
|
{ Vector3(-1, +1, 0), 0xffffffff, Vector2(0, 0) },
|
|
{ Vector3(+1, -1, 0), 0xffffffff, Vector2(1, 1) },
|
|
{ Vector3(+1, +1, 0), 0xffffffff, Vector2(1, 0) },
|
|
};
|
|
|
|
shared_ptr<VertexBuffer> vb = device->createVertexBuffer(sizeof(SkyVertex), ARRAYSIZE(vertices), GeometryBuffer::Usage_Static);
|
|
|
|
vb->upload(0, vertices, sizeof(vertices));
|
|
|
|
shared_ptr<Geometry> geometry = device->createGeometry(layout, vb, shared_ptr<IndexBuffer>());
|
|
|
|
return new GeometryBatch(geometry, Geometry::Primitive_TriangleStrip, 4, 4);
|
|
}
|
|
|
|
void Sky::StarData::resize(Sky* sky, unsigned int count, bool dynamic)
|
|
{
|
|
if (count == 0)
|
|
reset();
|
|
else
|
|
{
|
|
stars.resize(count);
|
|
|
|
buffer = sky->visualEngine->getDevice()->createVertexBuffer(sizeof(SkyVertex), count, dynamic ? GeometryBuffer::Usage_Dynamic : GeometryBuffer::Usage_Static);
|
|
|
|
batch.reset(new GeometryBatch(sky->visualEngine->getDevice()->createGeometry(sky->layout, buffer, shared_ptr<IndexBuffer>()), Geometry::Primitive_Points, count, count));
|
|
}
|
|
}
|
|
|
|
void Sky::StarData::reset()
|
|
{
|
|
stars.clear();
|
|
batch.reset();
|
|
buffer.reset();
|
|
}
|
|
|
|
Sky::Sky(VisualEngine* visualEngine)
|
|
: visualEngine(visualEngine)
|
|
, starLight(Brightness_None)
|
|
, starTwinkleCounter(0)
|
|
, readyState(false)
|
|
{
|
|
std::vector<VertexLayout::Element> elements;
|
|
elements.push_back(VertexLayout::Element(0, offsetof(SkyVertex, position), VertexLayout::Format_Float3, VertexLayout::Semantic_Position));
|
|
elements.push_back(VertexLayout::Element(0, offsetof(SkyVertex, color), VertexLayout::Format_Color, VertexLayout::Semantic_Color));
|
|
elements.push_back(VertexLayout::Element(0, offsetof(SkyVertex, texcoord), VertexLayout::Format_Float2, VertexLayout::Semantic_Texture));
|
|
|
|
layout = visualEngine->getDevice()->createVertexLayout(elements);
|
|
|
|
quad.reset(createQuad(visualEngine->getDevice(), layout));
|
|
|
|
// preload default skybox so that we can show it even while fetching custom skies over HTTP
|
|
if (!FFlag::DebugRenderDownloadAssets)
|
|
loadSkyBoxDefault(skyBox);
|
|
|
|
// preload sun/mon
|
|
sun = visualEngine->getTextureManager()->load(ContentId("rbxasset://sky/sun.jpg"), TextureManager::Fallback_BlackTransparent);
|
|
moon = visualEngine->getTextureManager()->load(ContentId("rbxasset://sky/moon.jpg"), TextureManager::Fallback_BlackTransparent);
|
|
}
|
|
|
|
Sky::~Sky()
|
|
{
|
|
}
|
|
|
|
void Sky::update(const G3D::LightingParameters& lighting, int starCount, bool drawCelestialBodies)
|
|
{
|
|
skyColor = lighting.skyAmbient;
|
|
skyColor2 = lighting.skyAmbient2;
|
|
|
|
drawSunMoon = drawCelestialBodies;
|
|
|
|
sunPosition = (lighting.physicallyCorrect ? lighting.trueSunPosition : lighting.sunPosition);
|
|
sunColor = Color3(lighting.emissiveScale * .8f * G3D::clamp((sunPosition.y + 0.1f) * 10.0f, 0.f, 1.f));
|
|
|
|
moonPosition = (lighting.physicallyCorrect ? lighting.trueMoonPosition : lighting.moonPosition);
|
|
moonColor = Color3(G3D::min((1.0f-lighting.skyAmbient[1])+0.1f, 1.0f));
|
|
|
|
if (!drawCelestialBodies || starCount == 0)
|
|
{
|
|
starsNormal.reset();
|
|
starsTwinkle.reset();
|
|
}
|
|
else
|
|
{
|
|
Brightness oldStarLight = starLight;
|
|
|
|
if (lighting.moonPosition.y <= -0.3)
|
|
starLight = Brightness_None;
|
|
else if (lighting.moonPosition.y <= -0.1)
|
|
starLight = Brightness_Dim;
|
|
else if(lighting.moonPosition.y <= 0.2)
|
|
starLight = Brightness_Dimmer;
|
|
else
|
|
starLight = Brightness_Bright;
|
|
|
|
if (starsNormal.stars.size() + starsTwinkle.stars.size() != starCount)
|
|
{
|
|
createStarField(starCount);
|
|
|
|
updateStarsNormal();
|
|
updateStarsTwinkle();
|
|
}
|
|
else if (oldStarLight != starLight)
|
|
{
|
|
updateStarsNormal();
|
|
updateStarsTwinkle();
|
|
}
|
|
}
|
|
}
|
|
|
|
void Sky::prerender()
|
|
{
|
|
TextureRef::Status loadingStatus = getCombinedStatus(skyBoxLoading);
|
|
|
|
if (loadingStatus == TextureRef::Status_Loaded)
|
|
{
|
|
std::copy(skyBoxLoading, skyBoxLoading + 6, skyBox);
|
|
readyState = true;
|
|
|
|
if (EnvMap* envMap = visualEngine->getSceneManager()->getEnvMap())
|
|
{
|
|
envMap->markDirty(false);
|
|
}
|
|
}
|
|
|
|
if (loadingStatus != TextureRef::Status_Waiting)
|
|
std::fill(skyBoxLoading, skyBoxLoading + 6, TextureRef());
|
|
|
|
// Update star twinkle
|
|
starTwinkleCounter++;
|
|
}
|
|
|
|
void Sky::render(DeviceContext* context, const RenderCamera& camera, bool drawStars)
|
|
{
|
|
RBXPROFILER_SCOPE("Render", "Sky");
|
|
RBXPROFILER_SCOPE("GPU", "Sky");
|
|
PIX_SCOPE(context, "Sky");
|
|
|
|
// Update load-in-progress
|
|
if (starTwinkleCounter >= kStarTwinkleRate)
|
|
{
|
|
starTwinkleCounter = 0;
|
|
|
|
if (starLight != Brightness_None && !starsTwinkle.stars.empty())
|
|
updateStarsTwinkle();
|
|
}
|
|
|
|
shared_ptr<ShaderProgram> program = visualEngine->getShaderManager()->getProgramOrFFP("SkyVS", "SkyFS");
|
|
|
|
if (program)
|
|
{
|
|
int colorHandle = program->getConstantHandle("Color");
|
|
int color2Handle = program->getConstantHandle("Color2");
|
|
|
|
context->bindProgram(program.get());
|
|
|
|
context->setRasterizerState(RasterizerState::Cull_None);
|
|
context->setDepthState(DepthState(DepthState::Function_LessEqual, false));
|
|
|
|
// Render sky surface, if loaded
|
|
if (1)
|
|
{
|
|
context->setBlendState(BlendState::Mode_None);
|
|
|
|
for (int i = 0; i < 6; ++i)
|
|
{
|
|
PIX_SCOPE(context, "Skybox face %d", i);
|
|
float adjustAngle = (i == NORM_Y) ? G3D::pif() / 2 : (i == NORM_Y_NEG) ? -G3D::pif() / 2 : 0;
|
|
Matrix3 rotation = Matrix3::fromAxisAngle(Vector3(0, 1, 0), adjustAngle) * Matrix3::fromDiagonal(Vector3(-1, 1, 1)) * normalIdToMatrix3(static_cast<NormalId>(i));
|
|
Matrix4 transform(rotation * Matrix3::fromDiagonal(Vector3(kSkySize)), camera.getPosition() + rotation.column(2) * kSkySize);
|
|
|
|
context->bindTexture(0, skyBox[i].getTexture().get() ? skyBox[i].getTexture().get() : visualEngine->getTextureManager()->getFallbackTexture(TextureManager::Fallback_White).get(), SamplerState(SamplerState::Filter_Linear, SamplerState::Address_Clamp));
|
|
|
|
float colorData[] = {skyColor.r, skyColor.g, skyColor.b, 1};
|
|
float color2Data[] = {skyColor2.r, skyColor2.g, skyColor2.b, 1 };
|
|
context->setConstant(colorHandle, colorData, 1);
|
|
context->setConstant(color2Handle, color2Data, 1);
|
|
|
|
context->setWorldTransforms4x3(transform[0], 1);
|
|
context->draw(*quad);
|
|
}
|
|
}
|
|
|
|
// Render sun/moon
|
|
if (drawSunMoon)
|
|
{
|
|
PIX_SCOPE(context, "Sun/moon");
|
|
context->setBlendState(BlendState::Mode_Additive);
|
|
|
|
Matrix4 sunTransform = getBillboardTransform(camera, camera.getPosition() + sunPosition * kSkySize, kSunSize);
|
|
|
|
context->setConstant(colorHandle, &sunColor.r, 1);
|
|
context->setConstant(color2Handle,&sunColor.r, 1);
|
|
|
|
context->bindTexture(0, sun.getTexture().get(), SamplerState(SamplerState::Filter_Linear, SamplerState::Address_Clamp));
|
|
|
|
context->setWorldTransforms4x3(sunTransform[0], 1);
|
|
context->draw(*quad);
|
|
context->draw(*quad);
|
|
context->draw(*quad);
|
|
|
|
PIX_MARKER(context, "Moon:");
|
|
|
|
Matrix4 moonTransform = getBillboardTransform(camera, camera.getPosition() + moonPosition * kSkySize, kMoonSize);
|
|
|
|
context->setConstant(colorHandle, &moonColor.r, 1);
|
|
|
|
context->bindTexture(0, moon.getTexture().get(), SamplerState(SamplerState::Filter_Linear, SamplerState::Address_Clamp));
|
|
|
|
context->setWorldTransforms4x3(moonTransform[0], 1);
|
|
context->draw(*quad);
|
|
}
|
|
|
|
// Render stars
|
|
if (drawStars && starLight != Brightness_None && (starsNormal.batch || starsTwinkle.batch))
|
|
{
|
|
PIX_SCOPE(context, "Stars");
|
|
context->setBlendState(BlendState::Mode_AlphaBlend);
|
|
|
|
float colorData[] = {1, 1, 1, 1};
|
|
context->setConstant(colorHandle, colorData, 1);
|
|
context->setConstant(color2Handle, colorData, 1);
|
|
|
|
context->bindTexture(0, visualEngine->getTextureManager()->getFallbackTexture(TextureManager::Fallback_White).get(), SamplerState::Filter_Linear);
|
|
|
|
Matrix4 transform(Matrix3::identity(), camera.getPosition());
|
|
|
|
context->setWorldTransforms4x3(transform[0], 1);
|
|
|
|
if (starsNormal.batch)
|
|
context->draw(*starsNormal.batch);
|
|
|
|
if (starLight != Brightness_Dim && starsTwinkle.batch)
|
|
context->draw(*starsTwinkle.batch);
|
|
}
|
|
}
|
|
}
|
|
|
|
void Sky::setSkyBox(const ContentId& rt, const ContentId& lf, const ContentId& bk, const ContentId& ft, const ContentId& up, const ContentId& dn)
|
|
{
|
|
loadSkyBox(skyBoxLoading, rt, lf, bk, ft, up, dn);
|
|
}
|
|
|
|
void Sky::setSkyBoxDefault()
|
|
{
|
|
loadSkyBoxDefault(skyBoxLoading);
|
|
}
|
|
|
|
void Sky::loadSkyBoxDefault(TextureRef* textures)
|
|
{
|
|
RBX::Sky dummy;
|
|
|
|
loadSkyBox(textures, dummy.skyRt, dummy.skyLf, dummy.skyBk, dummy.skyFt, dummy.skyUp, dummy.skyDn);
|
|
}
|
|
|
|
void Sky::loadSkyBox(TextureRef* textures, const ContentId& rt, const ContentId& lf, const ContentId& bk, const ContentId& ft, const ContentId& up, const ContentId& dn)
|
|
{
|
|
TextureManager* tm = visualEngine->getTextureManager();
|
|
|
|
textures[NORM_X] = tm->load(rt, TextureManager::Fallback_None, "Sky.SkyboxRt");
|
|
textures[NORM_Y] = tm->load(up, TextureManager::Fallback_None, "Sky.SkyboxUp");
|
|
textures[NORM_Z] = tm->load(bk, TextureManager::Fallback_None, "Sky.SkyboxBk");
|
|
textures[NORM_X_NEG] = tm->load(lf, TextureManager::Fallback_None, "Sky.SkyboxLf");
|
|
textures[NORM_Y_NEG] = tm->load(dn, TextureManager::Fallback_None, "Sky.SkyboxDn");
|
|
textures[NORM_Z_NEG] = tm->load(ft, TextureManager::Fallback_None, "Sky.SkyboxFt");
|
|
}
|
|
|
|
void Sky::createStarField(int starCount)
|
|
{
|
|
int twinkle = static_cast<int>(starCount * kStarTwinkleRatio);
|
|
int normal = starCount - twinkle;
|
|
|
|
// create random positions and intensities for regular stars
|
|
starsNormal.resize(this, normal, false);
|
|
|
|
for (size_t i = 0; i < starsNormal.stars.size(); ++i)
|
|
{
|
|
Star& s = starsNormal.stars[i];
|
|
|
|
Vector3 r = Vector3::random();
|
|
|
|
s.position = r * kStarDistance;
|
|
s.intensity = G3D::uniformRandom(0.3f, 0.8f);
|
|
|
|
float tpos = fabs(r.y);
|
|
|
|
if (tpos < 0.15f)
|
|
s.intensity *= 0.6f;
|
|
else if (tpos < 0.3f)
|
|
s.intensity *= 0.75f;
|
|
else if (tpos < 0.6f)
|
|
s.intensity *= 0.9f;
|
|
|
|
s.intensity *= s.intensity;
|
|
}
|
|
|
|
// create random positions and intensities for twinkling stars
|
|
starsTwinkle.resize(this, twinkle, true);
|
|
|
|
for (size_t i = 0; i < starsTwinkle.stars.size(); ++i)
|
|
{
|
|
Star& s = starsTwinkle.stars[i];
|
|
|
|
Vector3 r = Vector3::random();
|
|
|
|
// only top hemisphere has twinkling stars
|
|
r.y = fabsf(r.y);
|
|
|
|
s.position = r * kStarDistance;
|
|
s.intensity = G3D::uniformRandom(0.2f, 0.7f);
|
|
|
|
float tpos = fabs(r.y);
|
|
|
|
if (tpos < 0.3f)
|
|
s.intensity *= 0.7f;
|
|
else if (tpos < 0.6f)
|
|
s.intensity *= 0.9f;
|
|
|
|
s.intensity *= s.intensity;
|
|
}
|
|
}
|
|
|
|
void Sky::updateStarsNormal()
|
|
{
|
|
bool colorOrderBGR = visualEngine->getDevice()->getCaps().colorOrderBGR;
|
|
|
|
const StarData& data = starsNormal;
|
|
|
|
if (data.stars.empty())
|
|
return;
|
|
|
|
SkyVertex* vertices = static_cast<SkyVertex*>(data.buffer->lock());
|
|
|
|
for (unsigned int i = 0; i < data.stars.size(); ++i)
|
|
{
|
|
vertices[i].position = data.stars[i].position;
|
|
|
|
float intensity = data.stars[i].intensity;
|
|
|
|
if (starLight == Brightness_Bright)
|
|
{
|
|
intensity *= 5 * 0.3f;
|
|
}
|
|
else if (starLight == Brightness_Dimmer)
|
|
{
|
|
intensity *= 2.5f * 0.15f;
|
|
}
|
|
else
|
|
{
|
|
intensity *= 0.6f;
|
|
}
|
|
|
|
float tnum = G3D::uniformRandom(0.0, 1.0);
|
|
|
|
if (tnum < 0.15)
|
|
vertices[i].color = packColor(Color4(1.0, 0.80f, 0.70f, intensity), colorOrderBGR); // yellow (15%)
|
|
else if (tnum < 0.55)
|
|
vertices[i].color = packColor(Color4(1.0, 1.0, 1.0, intensity), colorOrderBGR); // white (40%)
|
|
else if (tnum < 0.80)
|
|
vertices[i].color = packColor(Color4(0.70f, 0.80f, 1.0, intensity), colorOrderBGR); // blue (25%)
|
|
else
|
|
vertices[i].color = packColor(Color4(0.75f, 0.50f, 1.0, intensity), colorOrderBGR); // purple (20%)
|
|
|
|
vertices[i].texcoord = Vector2(0, 0);
|
|
}
|
|
|
|
data.buffer->unlock();
|
|
}
|
|
|
|
void Sky::updateStarsTwinkle()
|
|
{
|
|
bool colorOrderBGR = visualEngine->getDevice()->getCaps().colorOrderBGR;
|
|
|
|
const StarData& data = starsTwinkle;
|
|
|
|
if (data.stars.empty())
|
|
return;
|
|
|
|
SkyVertex* vertices = static_cast<SkyVertex*>(data.buffer->lock(GeometryBuffer::Lock_Discard));
|
|
|
|
for (unsigned int i = 0; i < data.stars.size(); ++i)
|
|
{
|
|
vertices[i].position = data.stars[i].position;
|
|
|
|
float randomBrightness = G3D::uniformRandom(0.3f, 1.7f);
|
|
float brightfactor = (starLight == Brightness_Dimmer) ? 0.5f : 1.f;
|
|
float intensity = data.stars[i].intensity * randomBrightness * brightfactor + 0.2f;
|
|
|
|
float tnum = G3D::uniformRandom(0.0, 1.0);
|
|
|
|
if (tnum < 0.15)
|
|
vertices[i].color = packColor(Color4(1.0, 0.80f, 0.70f, intensity), colorOrderBGR); // yellow (15%)
|
|
else if (tnum < 0.55)
|
|
vertices[i].color = packColor(Color4(1.0, 1.0, 1.0, intensity), colorOrderBGR); // white (40%)
|
|
else if (tnum < 0.80)
|
|
vertices[i].color = packColor(Color4(0.70f, 0.80f, 1.0, intensity), colorOrderBGR); // blue (25%)
|
|
else
|
|
vertices[i].color = packColor(Color4(0.75f, 0.50f, 1.0, intensity), colorOrderBGR); // purple (20%)
|
|
|
|
vertices[i].texcoord = Vector2(0, 0);
|
|
}
|
|
|
|
data.buffer->unlock();
|
|
}
|
|
|
|
}
|
|
}
|