experiment: unclamped lighting
This commit is contained in:
parent
58a346e563
commit
6d76a11e25
1
assets/opensb/lighting.config.patch
Normal file
1
assets/opensb/lighting.config.patch
Normal file
@ -0,0 +1 @@
|
|||||||
|
{}
|
@ -53,6 +53,12 @@ vec4 bicubicSample(sampler2D texture, vec2 texcoord, vec2 texscale) {
|
|||||||
mix(sample1, sample0, sx), sy);
|
mix(sample1, sample0, sx), sy);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
vec3 sampleLight(vec2 coord, vec2 scale) {
|
||||||
|
//soften super bright lights a little
|
||||||
|
vec3 rgb = bicubicSample(lightMap, coord, scale).rgb;
|
||||||
|
return mix(rgb, (rgb / (vec3(1.) + rgb)) * 1.666666, 0.666666);
|
||||||
|
}
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
vec4 texColor;
|
vec4 texColor;
|
||||||
if (fragmentTextureIndex > 2.9) {
|
if (fragmentTextureIndex > 2.9) {
|
||||||
@ -72,6 +78,6 @@ void main() {
|
|||||||
if (texColor.a == 0.99607843137)
|
if (texColor.a == 0.99607843137)
|
||||||
finalColor.a = fragmentColor.a;
|
finalColor.a = fragmentColor.a;
|
||||||
else if (lightMapEnabled && finalLightMapMultiplier > 0.0)
|
else if (lightMapEnabled && finalLightMapMultiplier > 0.0)
|
||||||
finalColor.rgb *= bicubicSample(lightMap, fragmentLightMapCoordinate, 1.0 / lightMapSize).rgb * finalLightMapMultiplier;
|
finalColor.rgb *= sampleLight(fragmentLightMapCoordinate, 1.0 / lightMapSize) * finalLightMapMultiplier;
|
||||||
gl_FragColor = finalColor;
|
gl_FragColor = finalColor;
|
||||||
}
|
}
|
@ -32,6 +32,13 @@ set(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/../cmake)
|
|||||||
set(CMAKE_CONFIGURATION_TYPES Debug RelWithAsserts RelWithDebInfo Release)
|
set(CMAKE_CONFIGURATION_TYPES Debug RelWithAsserts RelWithDebInfo Release)
|
||||||
set(CMAKE_EXPORT_COMPILE_COMMANDS ON CACHE INTERNAL "")
|
set(CMAKE_EXPORT_COMPILE_COMMANDS ON CACHE INTERNAL "")
|
||||||
set(CMAKE_EXE_LINKER_FLAGS_RELWITHASSERTS "" CACHE STRING "" FORCE)
|
set(CMAKE_EXE_LINKER_FLAGS_RELWITHASSERTS "" CACHE STRING "" FORCE)
|
||||||
|
|
||||||
|
#include(CheckIPOSupported)
|
||||||
|
#check_ipo_supported(RESULT lto_supported OUTPUT lto_output)
|
||||||
|
#if(lto_supported)
|
||||||
|
# set(CMAKE_INTERPROCEDURAL_OPTIMIZATION TRUE)
|
||||||
|
#endif()
|
||||||
|
|
||||||
# Update the docstring on CMAKE_BUILD_TYPE to show what options we actually
|
# Update the docstring on CMAKE_BUILD_TYPE to show what options we actually
|
||||||
# allow
|
# allow
|
||||||
# SET (CMAKE_BUILD_TYPE "${CMAKE_BUILD_TYPE}" CACHE STRING "Choose the type of build, options are: Debug RelWithAsserts RelWithDebInfo Release" FORCE)
|
# SET (CMAKE_BUILD_TYPE "${CMAKE_BUILD_TYPE}" CACHE STRING "Choose the type of build, options are: Debug RelWithAsserts RelWithDebInfo Release" FORCE)
|
||||||
|
@ -141,8 +141,7 @@ public:
|
|||||||
// The effect config will specify named parameters and textures which can be
|
// The effect config will specify named parameters and textures which can be
|
||||||
// set here.
|
// set here.
|
||||||
virtual void setEffectParameter(String const& parameterName, RenderEffectParameter const& parameter) = 0;
|
virtual void setEffectParameter(String const& parameterName, RenderEffectParameter const& parameter) = 0;
|
||||||
virtual void setEffectTexture(String const& textureName, Image const& image) = 0;
|
virtual void setEffectTexture(String const& textureName, ImageView const& image) = 0;
|
||||||
|
|
||||||
virtual bool switchEffectConfig(String const& name) = 0;
|
virtual bool switchEffectConfig(String const& name) = 0;
|
||||||
|
|
||||||
// Any further rendering will be scissored based on this rect, specified in
|
// Any further rendering will be scissored based on this rect, specified in
|
||||||
|
@ -118,7 +118,7 @@ Vec2U OpenGl20Renderer::screenSize() const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
OpenGl20Renderer::GlFrameBuffer::GlFrameBuffer(Json const& fbConfig) : config(fbConfig) {
|
OpenGl20Renderer::GlFrameBuffer::GlFrameBuffer(Json const& fbConfig) : config(fbConfig) {
|
||||||
texture = createGlTexture(Image(), TextureAddressing::Clamp, TextureFiltering::Nearest);
|
texture = createGlTexture(ImageView(), TextureAddressing::Clamp, TextureFiltering::Nearest);
|
||||||
glBindTexture(GL_TEXTURE_2D, texture->glTextureId());
|
glBindTexture(GL_TEXTURE_2D, texture->glTextureId());
|
||||||
|
|
||||||
Vec2U size = jsonToVec2U(config.getArray("size", { 256, 256 }));
|
Vec2U size = jsonToVec2U(config.getArray("size", { 256, 256 }));
|
||||||
@ -147,13 +147,15 @@ void OpenGl20Renderer::loadConfig(Json const& config) {
|
|||||||
|
|
||||||
for (auto& pair : config.getObject("frameBuffers", {}))
|
for (auto& pair : config.getObject("frameBuffers", {}))
|
||||||
m_frameBuffers[pair.first] = make_ref<GlFrameBuffer>(pair.second);
|
m_frameBuffers[pair.first] = make_ref<GlFrameBuffer>(pair.second);
|
||||||
|
|
||||||
|
setScreenSize(m_screenSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
void OpenGl20Renderer::loadEffectConfig(String const& name, Json const& effectConfig, StringMap<String> const& shaders) {
|
void OpenGl20Renderer::loadEffectConfig(String const& name, Json const& effectConfig, StringMap<String> const& shaders) {
|
||||||
if (m_effects.contains(name)) {
|
if (auto effect = m_effects.ptr(name)) {
|
||||||
Logger::warn("OpenGL effect {} already exists", name);
|
Logger::info("Reloading OpenGL effect {}", name);
|
||||||
switchEffectConfig(name);
|
glDeleteProgram(effect->program);
|
||||||
return;
|
m_effects.erase(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
GLint status = 0;
|
GLint status = 0;
|
||||||
@ -177,8 +179,18 @@ void OpenGl20Renderer::loadEffectConfig(String const& name, Json const& effectCo
|
|||||||
return shader;
|
return shader;
|
||||||
};
|
};
|
||||||
|
|
||||||
GLuint vertexShader = compileShader(GL_VERTEX_SHADER, "vertex");
|
GLuint vertexShader = 0, fragmentShader = 0;
|
||||||
GLuint fragmentShader = compileShader(GL_FRAGMENT_SHADER, "fragment");
|
try {
|
||||||
|
vertexShader = compileShader(GL_VERTEX_SHADER, "vertex");
|
||||||
|
fragmentShader = compileShader(GL_FRAGMENT_SHADER, "fragment");
|
||||||
|
}
|
||||||
|
catch (RendererException const& e) {
|
||||||
|
Logger::error("Shader compile error, using default: {}", e.what());
|
||||||
|
if (vertexShader) glDeleteShader(vertexShader);
|
||||||
|
if (fragmentShader) glDeleteShader(fragmentShader);
|
||||||
|
vertexShader = compileShader(GL_VERTEX_SHADER, DefaultVertexShader);
|
||||||
|
fragmentShader = compileShader(GL_FRAGMENT_SHADER, DefaultFragmentShader);
|
||||||
|
}
|
||||||
|
|
||||||
GLuint program = glCreateProgram();
|
GLuint program = glCreateProgram();
|
||||||
|
|
||||||
@ -308,7 +320,7 @@ void OpenGl20Renderer::setEffectParameter(String const& parameterName, RenderEff
|
|||||||
ptr->parameterValue = value;
|
ptr->parameterValue = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
void OpenGl20Renderer::setEffectTexture(String const& textureName, Image const& image) {
|
void OpenGl20Renderer::setEffectTexture(String const& textureName, ImageView const& image) {
|
||||||
auto ptr = m_currentEffect->textures.ptr(textureName);
|
auto ptr = m_currentEffect->textures.ptr(textureName);
|
||||||
if (!ptr)
|
if (!ptr)
|
||||||
return;
|
return;
|
||||||
@ -319,8 +331,8 @@ void OpenGl20Renderer::setEffectTexture(String const& textureName, Image const&
|
|||||||
ptr->textureValue = createGlTexture(image, ptr->textureAddressing, ptr->textureFiltering);
|
ptr->textureValue = createGlTexture(image, ptr->textureAddressing, ptr->textureFiltering);
|
||||||
} else {
|
} else {
|
||||||
glBindTexture(GL_TEXTURE_2D, ptr->textureValue->textureId);
|
glBindTexture(GL_TEXTURE_2D, ptr->textureValue->textureId);
|
||||||
ptr->textureValue->textureSize = image.size();
|
ptr->textureValue->textureSize = image.size;
|
||||||
uploadTextureImage(image.pixelFormat(), image.size(), image.data());
|
uploadTextureImage(image.format, image.size, image.data);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ptr->textureSizeUniform != -1) {
|
if (ptr->textureSizeUniform != -1) {
|
||||||
@ -789,7 +801,9 @@ bool OpenGl20Renderer::logGlErrorSummary(String prefix) {
|
|||||||
void OpenGl20Renderer::uploadTextureImage(PixelFormat pixelFormat, Vec2U size, uint8_t const* data) {
|
void OpenGl20Renderer::uploadTextureImage(PixelFormat pixelFormat, Vec2U size, uint8_t const* data) {
|
||||||
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
|
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
|
||||||
|
|
||||||
|
Maybe<GLenum> internalFormat;
|
||||||
GLenum format;
|
GLenum format;
|
||||||
|
GLenum type = GL_UNSIGNED_BYTE;
|
||||||
if (pixelFormat == PixelFormat::RGB24)
|
if (pixelFormat == PixelFormat::RGB24)
|
||||||
format = GL_RGB;
|
format = GL_RGB;
|
||||||
else if (pixelFormat == PixelFormat::RGBA32)
|
else if (pixelFormat == PixelFormat::RGBA32)
|
||||||
@ -798,10 +812,19 @@ void OpenGl20Renderer::uploadTextureImage(PixelFormat pixelFormat, Vec2U size, u
|
|||||||
format = GL_BGR;
|
format = GL_BGR;
|
||||||
else if (pixelFormat == PixelFormat::BGRA32)
|
else if (pixelFormat == PixelFormat::BGRA32)
|
||||||
format = GL_BGRA;
|
format = GL_BGRA;
|
||||||
else
|
else {
|
||||||
|
type = GL_FLOAT;
|
||||||
|
if (pixelFormat == PixelFormat::RGB_F) {
|
||||||
|
internalFormat = GL_RGB32F;
|
||||||
|
format = GL_RGB;
|
||||||
|
} else if (pixelFormat == PixelFormat::RGBA_F) {
|
||||||
|
internalFormat = GL_RGBA32F;
|
||||||
|
format = GL_RGBA;
|
||||||
|
} else
|
||||||
throw RendererException("Unsupported texture format in OpenGL20Renderer::uploadTextureImage");
|
throw RendererException("Unsupported texture format in OpenGL20Renderer::uploadTextureImage");
|
||||||
|
}
|
||||||
|
|
||||||
glTexImage2D(GL_TEXTURE_2D, 0, format, size[0], size[1], 0, format, GL_UNSIGNED_BYTE, data);
|
glTexImage2D(GL_TEXTURE_2D, 0, internalFormat.value(format), size[0], size[1], 0, format, type, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
void OpenGl20Renderer::flushImmediatePrimitives() {
|
void OpenGl20Renderer::flushImmediatePrimitives() {
|
||||||
@ -813,12 +836,12 @@ void OpenGl20Renderer::flushImmediatePrimitives() {
|
|||||||
renderGlBuffer(*m_immediateRenderBuffer, Mat3F::identity());
|
renderGlBuffer(*m_immediateRenderBuffer, Mat3F::identity());
|
||||||
}
|
}
|
||||||
|
|
||||||
auto OpenGl20Renderer::createGlTexture(Image const& image, TextureAddressing addressing, TextureFiltering filtering)
|
auto OpenGl20Renderer::createGlTexture(ImageView const& image, TextureAddressing addressing, TextureFiltering filtering)
|
||||||
-> RefPtr<GlLoneTexture> {
|
->RefPtr<GlLoneTexture> {
|
||||||
auto glLoneTexture = make_ref<GlLoneTexture>();
|
auto glLoneTexture = make_ref<GlLoneTexture>();
|
||||||
glLoneTexture->textureFiltering = filtering;
|
glLoneTexture->textureFiltering = filtering;
|
||||||
glLoneTexture->textureAddressing = addressing;
|
glLoneTexture->textureAddressing = addressing;
|
||||||
glLoneTexture->textureSize = image.size();
|
glLoneTexture->textureSize = image.size;
|
||||||
|
|
||||||
glGenTextures(1, &glLoneTexture->textureId);
|
glGenTextures(1, &glLoneTexture->textureId);
|
||||||
if (glLoneTexture->textureId == 0)
|
if (glLoneTexture->textureId == 0)
|
||||||
@ -844,7 +867,7 @@ auto OpenGl20Renderer::createGlTexture(Image const& image, TextureAddressing add
|
|||||||
|
|
||||||
|
|
||||||
if (!image.empty())
|
if (!image.empty())
|
||||||
uploadTextureImage(image.pixelFormat(), image.size(), image.data());
|
uploadTextureImage(image.format, image.size, image.data);
|
||||||
|
|
||||||
return glLoneTexture;
|
return glLoneTexture;
|
||||||
}
|
}
|
||||||
|
@ -25,7 +25,7 @@ public:
|
|||||||
void loadEffectConfig(String const& name, Json const& effectConfig, StringMap<String> const& shaders) override;
|
void loadEffectConfig(String const& name, Json const& effectConfig, StringMap<String> const& shaders) override;
|
||||||
|
|
||||||
void setEffectParameter(String const& parameterName, RenderEffectParameter const& parameter) override;
|
void setEffectParameter(String const& parameterName, RenderEffectParameter const& parameter) override;
|
||||||
void setEffectTexture(String const& textureName, Image const& image) override;
|
void setEffectTexture(String const& textureName, ImageView const& image) override;
|
||||||
|
|
||||||
void setScissorRect(Maybe<RectI> const& scissorRect) override;
|
void setScissorRect(Maybe<RectI> const& scissorRect) override;
|
||||||
|
|
||||||
@ -188,7 +188,8 @@ private:
|
|||||||
static bool logGlErrorSummary(String prefix);
|
static bool logGlErrorSummary(String prefix);
|
||||||
static void uploadTextureImage(PixelFormat pixelFormat, Vec2U size, uint8_t const* data);
|
static void uploadTextureImage(PixelFormat pixelFormat, Vec2U size, uint8_t const* data);
|
||||||
|
|
||||||
static RefPtr<GlLoneTexture> createGlTexture(Image const& texture, TextureAddressing addressing, TextureFiltering filtering);
|
|
||||||
|
static RefPtr<GlLoneTexture> createGlTexture(ImageView const& image, TextureAddressing addressing, TextureFiltering filtering);
|
||||||
|
|
||||||
shared_ptr<GlRenderBuffer> createGlRenderBuffer();
|
shared_ptr<GlRenderBuffer> createGlRenderBuffer();
|
||||||
|
|
||||||
|
@ -25,6 +25,7 @@ SET (star_base_HEADERS
|
|||||||
SET (star_base_SOURCES
|
SET (star_base_SOURCES
|
||||||
StarAnimatedPartSet.cpp
|
StarAnimatedPartSet.cpp
|
||||||
StarAssets.cpp
|
StarAssets.cpp
|
||||||
|
StarCellularLightArray.cpp
|
||||||
StarCellularLighting.cpp
|
StarCellularLighting.cpp
|
||||||
StarConfiguration.cpp
|
StarConfiguration.cpp
|
||||||
StarDirectoryAssetSource.cpp
|
StarDirectoryAssetSource.cpp
|
||||||
|
130
source/base/StarCellularLightArray.cpp
Normal file
130
source/base/StarCellularLightArray.cpp
Normal file
@ -0,0 +1,130 @@
|
|||||||
|
#include "StarCellularLightArray.hpp"
|
||||||
|
// just specializing these in a cpp file so I can iterate on them without recompiling like 40 files!!
|
||||||
|
|
||||||
|
namespace Star {
|
||||||
|
|
||||||
|
template <>
|
||||||
|
void CellularLightArray<ScalarLightTraits>::calculatePointLighting(size_t xmin, size_t ymin, size_t xmax, size_t ymax) {
|
||||||
|
float perBlockObstacleAttenuation = 1.0f / m_pointMaxObstacle;
|
||||||
|
float perBlockAirAttenuation = 1.0f / m_pointMaxAir;
|
||||||
|
|
||||||
|
for (PointLight light : m_pointLights) {
|
||||||
|
if (light.position[0] < 0 || light.position[0] > m_width - 1 || light.position[1] < 0 || light.position[1] > m_height - 1)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
float maxIntensity = ScalarLightTraits::maxIntensity(light.value);
|
||||||
|
Vec2F beamDirection = Vec2F(1, 0).rotate(light.beamAngle);
|
||||||
|
|
||||||
|
float maxRange = maxIntensity * m_pointMaxAir;
|
||||||
|
// The min / max considering the radius of the light
|
||||||
|
size_t lxmin = std::floor(std::max<float>(xmin, light.position[0] - maxRange));
|
||||||
|
size_t lymin = std::floor(std::max<float>(ymin, light.position[1] - maxRange));
|
||||||
|
size_t lxmax = std::ceil(std::min<float>(xmax, light.position[0] + maxRange));
|
||||||
|
size_t lymax = std::ceil(std::min<float>(ymax, light.position[1] + maxRange));
|
||||||
|
|
||||||
|
for (size_t x = lxmin; x < lxmax; ++x) {
|
||||||
|
for (size_t y = lymin; y < lymax; ++y) {
|
||||||
|
LightValue lvalue = getLight(x, y);
|
||||||
|
// + 0.5f to correct block position to center
|
||||||
|
Vec2F blockPos = Vec2F(x + 0.5f, y + 0.5f);
|
||||||
|
|
||||||
|
Vec2F relativeLightPosition = blockPos - light.position;
|
||||||
|
float distance = relativeLightPosition.magnitude();
|
||||||
|
if (distance == 0.0f) {
|
||||||
|
setLight(x, y, light.value + lvalue);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
float attenuation = distance * perBlockAirAttenuation;
|
||||||
|
if (attenuation >= 1.0f)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
Vec2F direction = relativeLightPosition / distance;
|
||||||
|
if (light.beam > 0.0f) {
|
||||||
|
attenuation += (1.0f - light.beamAmbience) * clamp(light.beam * (1.0f - direction * beamDirection), 0.0f, 1.0f);
|
||||||
|
if (attenuation >= 1.0f)
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
float remainingAttenuation = maxIntensity - attenuation;
|
||||||
|
if (remainingAttenuation <= 0.0f)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// Need to circularize manhattan attenuation here
|
||||||
|
float circularizedPerBlockObstacleAttenuation = perBlockObstacleAttenuation / max(fabs(direction[0]), fabs(direction[1]));
|
||||||
|
float blockAttenuation = lineAttenuation(blockPos, light.position, circularizedPerBlockObstacleAttenuation, remainingAttenuation);
|
||||||
|
|
||||||
|
// Apply single obstacle boost (determine single obstacle by one
|
||||||
|
// block unit of attenuation).
|
||||||
|
attenuation += blockAttenuation + min(blockAttenuation, circularizedPerBlockObstacleAttenuation) * m_pointObstacleBoost;
|
||||||
|
|
||||||
|
if (attenuation < 1.0f)
|
||||||
|
setLight(x, y, lvalue + ScalarLightTraits::subtract(light.value, attenuation));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
void CellularLightArray<ColoredLightTraits>::calculatePointLighting(size_t xmin, size_t ymin, size_t xmax, size_t ymax) {
|
||||||
|
float perBlockObstacleAttenuation = 1.0f / m_pointMaxObstacle;
|
||||||
|
float perBlockAirAttenuation = 1.0f / m_pointMaxAir;
|
||||||
|
|
||||||
|
for (PointLight light : m_pointLights) {
|
||||||
|
if (light.position[0] < 0 || light.position[0] > m_width - 1 || light.position[1] < 0 || light.position[1] > m_height - 1)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
float maxIntensity = ColoredLightTraits::maxIntensity(light.value);
|
||||||
|
Vec2F beamDirection = Vec2F(1, 0).rotate(light.beamAngle);
|
||||||
|
|
||||||
|
float maxRange = maxIntensity * m_pointMaxAir;
|
||||||
|
// The min / max considering the radius of the light
|
||||||
|
size_t lxmin = std::floor(std::max<float>(xmin, light.position[0] - maxRange));
|
||||||
|
size_t lymin = std::floor(std::max<float>(ymin, light.position[1] - maxRange));
|
||||||
|
size_t lxmax = std::ceil(std::min<float>(xmax, light.position[0] + maxRange));
|
||||||
|
size_t lymax = std::ceil(std::min<float>(ymax, light.position[1] + maxRange));
|
||||||
|
|
||||||
|
for (size_t x = lxmin; x < lxmax; ++x) {
|
||||||
|
for (size_t y = lymin; y < lymax; ++y) {
|
||||||
|
LightValue lvalue = getLight(x, y);
|
||||||
|
// + 0.5f to correct block position to center
|
||||||
|
Vec2F blockPos = Vec2F(x + 0.5f, y + 0.5f);
|
||||||
|
|
||||||
|
Vec2F relativeLightPosition = blockPos - light.position;
|
||||||
|
float distance = relativeLightPosition.magnitude();
|
||||||
|
if (distance == 0.0f) {
|
||||||
|
setLight(x, y, light.value + lvalue);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
float attenuation = distance * perBlockAirAttenuation;
|
||||||
|
if (attenuation >= 1.0f)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
Vec2F direction = relativeLightPosition / distance;
|
||||||
|
if (light.beam > 0.0f) {
|
||||||
|
attenuation += (1.0f - light.beamAmbience) * clamp(light.beam * (1.0f - direction * beamDirection), 0.0f, 1.0f);
|
||||||
|
if (attenuation >= 1.0f)
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
float remainingAttenuation = maxIntensity - attenuation;
|
||||||
|
if (remainingAttenuation <= 0.0f)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// Need to circularize manhattan attenuation here
|
||||||
|
float circularizedPerBlockObstacleAttenuation = perBlockObstacleAttenuation / max(fabs(direction[0]), fabs(direction[1]));
|
||||||
|
float blockAttenuation = lineAttenuation(blockPos, light.position, circularizedPerBlockObstacleAttenuation, remainingAttenuation);
|
||||||
|
|
||||||
|
// Apply single obstacle boost (determine single obstacle by one
|
||||||
|
// block unit of attenuation).
|
||||||
|
attenuation += blockAttenuation + min(blockAttenuation, circularizedPerBlockObstacleAttenuation) * m_pointObstacleBoost;
|
||||||
|
|
||||||
|
if (attenuation < 1.0f)
|
||||||
|
setLight(x, y, lvalue + ColoredLightTraits::subtract(light.value, attenuation));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -11,6 +11,7 @@ struct ScalarLightTraits {
|
|||||||
|
|
||||||
static float spread(float source, float dest, float drop);
|
static float spread(float source, float dest, float drop);
|
||||||
static float subtract(float value, float drop);
|
static float subtract(float value, float drop);
|
||||||
|
static float multiply(float v1, float v2);
|
||||||
|
|
||||||
static float maxIntensity(float value);
|
static float maxIntensity(float value);
|
||||||
static float minIntensity(float value);
|
static float minIntensity(float value);
|
||||||
@ -26,6 +27,7 @@ struct ColoredLightTraits {
|
|||||||
|
|
||||||
static Vec3F spread(Vec3F const& source, Vec3F const& dest, float drop);
|
static Vec3F spread(Vec3F const& source, Vec3F const& dest, float drop);
|
||||||
static Vec3F subtract(Vec3F value, float drop);
|
static Vec3F subtract(Vec3F value, float drop);
|
||||||
|
static Vec3F multiply(Vec3F value, float drop);
|
||||||
|
|
||||||
static float maxIntensity(Vec3F const& value);
|
static float maxIntensity(Vec3F const& value);
|
||||||
static float minIntensity(Vec3F const& value);
|
static float minIntensity(Vec3F const& value);
|
||||||
@ -139,6 +141,10 @@ inline float ScalarLightTraits::subtract(float c, float drop) {
|
|||||||
return std::max(c - drop, 0.0f);
|
return std::max(c - drop, 0.0f);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline float ScalarLightTraits::multiply(float v1, float v2) {
|
||||||
|
return v1 * v2;
|
||||||
|
}
|
||||||
|
|
||||||
inline float ScalarLightTraits::maxIntensity(float value) {
|
inline float ScalarLightTraits::maxIntensity(float value) {
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
@ -179,6 +185,10 @@ inline Vec3F ColoredLightTraits::subtract(Vec3F c, float drop) {
|
|||||||
return c;
|
return c;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline Vec3F ColoredLightTraits::multiply(Vec3F c, float drop) {
|
||||||
|
return c * drop;
|
||||||
|
}
|
||||||
|
|
||||||
inline float ColoredLightTraits::maxIntensity(Vec3F const& value) {
|
inline float ColoredLightTraits::maxIntensity(Vec3F const& value) {
|
||||||
return value.max();
|
return value.max();
|
||||||
}
|
}
|
||||||
@ -387,68 +397,6 @@ void CellularLightArray<LightTraits>::calculateLightSpread(size_t xMin, size_t y
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename LightTraits>
|
|
||||||
void CellularLightArray<LightTraits>::calculatePointLighting(size_t xmin, size_t ymin, size_t xmax, size_t ymax) {
|
|
||||||
float perBlockObstacleAttenuation = 1.0f / m_pointMaxObstacle;
|
|
||||||
float perBlockAirAttenuation = 1.0f / m_pointMaxAir;
|
|
||||||
|
|
||||||
for (PointLight light : m_pointLights) {
|
|
||||||
if (light.position[0] < 0 || light.position[0] > m_width - 1 || light.position[1] < 0 || light.position[1] > m_height - 1)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
float maxIntensity = LightTraits::maxIntensity(light.value);
|
|
||||||
Vec2F beamDirection = Vec2F(1, 0).rotate(light.beamAngle);
|
|
||||||
|
|
||||||
float maxRange = maxIntensity * m_pointMaxAir;
|
|
||||||
// The min / max considering the radius of the light
|
|
||||||
size_t lxmin = std::floor(std::max<float>(xmin, light.position[0] - maxRange));
|
|
||||||
size_t lymin = std::floor(std::max<float>(ymin, light.position[1] - maxRange));
|
|
||||||
size_t lxmax = std::ceil(std::min<float>(xmax, light.position[0] + maxRange));
|
|
||||||
size_t lymax = std::ceil(std::min<float>(ymax, light.position[1] + maxRange));
|
|
||||||
|
|
||||||
for (size_t x = lxmin; x < lxmax; ++x) {
|
|
||||||
for (size_t y = lymin; y < lymax; ++y) {
|
|
||||||
LightValue lvalue = getLight(x, y);
|
|
||||||
// + 0.5f to correct block position to center
|
|
||||||
Vec2F blockPos = Vec2F(x + 0.5f, y + 0.5f);
|
|
||||||
|
|
||||||
Vec2F relativeLightPosition = blockPos - light.position;
|
|
||||||
float distance = relativeLightPosition.magnitude();
|
|
||||||
if (distance == 0.0f) {
|
|
||||||
setLight(x, y, LightTraits::max(light.value, lvalue));
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
float attenuation = distance * perBlockAirAttenuation;
|
|
||||||
if (attenuation >= 1.0f)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
Vec2F direction = relativeLightPosition / distance;
|
|
||||||
if (light.beam > 0.0f) {
|
|
||||||
attenuation += (1.0f - light.beamAmbience) * clamp(light.beam * (1.0f - direction * beamDirection), 0.0f, 1.0f);
|
|
||||||
if (attenuation >= 1.0f)
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
float remainingAttenuation = maxIntensity - LightTraits::minIntensity(lvalue) - attenuation;
|
|
||||||
if (remainingAttenuation <= 0.0f)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
// Need to circularize manhattan attenuation here
|
|
||||||
float circularizedPerBlockObstacleAttenuation = perBlockObstacleAttenuation / max(fabs(direction[0]), fabs(direction[1]));
|
|
||||||
float blockAttenuation = lineAttenuation(blockPos, light.position, circularizedPerBlockObstacleAttenuation, remainingAttenuation);
|
|
||||||
|
|
||||||
// Apply single obstacle boost (determine single obstacle by one
|
|
||||||
// block unit of attenuation).
|
|
||||||
attenuation += blockAttenuation + min(blockAttenuation, circularizedPerBlockObstacleAttenuation) * m_pointObstacleBoost;
|
|
||||||
|
|
||||||
if (attenuation < 1.0f)
|
|
||||||
setLight(x, y, LightTraits::max(LightTraits::subtract(light.value, attenuation), lvalue));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename LightTraits>
|
template <typename LightTraits>
|
||||||
float CellularLightArray<LightTraits>::lineAttenuation(Vec2F const& start, Vec2F const& end,
|
float CellularLightArray<LightTraits>::lineAttenuation(Vec2F const& start, Vec2F const& end,
|
||||||
float perObstacleAttenuation, float maxAttenuation) {
|
float perObstacleAttenuation, float maxAttenuation) {
|
||||||
|
@ -2,6 +2,45 @@
|
|||||||
|
|
||||||
namespace Star {
|
namespace Star {
|
||||||
|
|
||||||
|
Lightmap::Lightmap() : m_width(0), m_height(0) {}
|
||||||
|
|
||||||
|
Lightmap::Lightmap(unsigned width, unsigned height) : m_width(width), m_height(height) {
|
||||||
|
m_data = std::make_unique<float[]>(len());
|
||||||
|
}
|
||||||
|
|
||||||
|
Lightmap::Lightmap(Lightmap const& lightMap) {
|
||||||
|
operator=(lightMap);
|
||||||
|
}
|
||||||
|
|
||||||
|
Lightmap::Lightmap(Lightmap&& lightMap) noexcept {
|
||||||
|
operator=(std::move(lightMap));
|
||||||
|
}
|
||||||
|
|
||||||
|
Lightmap& Lightmap::operator=(Lightmap const& lightMap) {
|
||||||
|
m_width = lightMap.m_width;
|
||||||
|
m_height = lightMap.m_height;
|
||||||
|
if (lightMap.m_data) {
|
||||||
|
m_data = std::make_unique<float[]>(len());
|
||||||
|
memcpy(m_data.get(), lightMap.m_data.get(), len());
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
Lightmap& Lightmap::operator=(Lightmap&& lightMap) noexcept {
|
||||||
|
m_width = take(lightMap.m_width);
|
||||||
|
m_height = take(lightMap.m_height);
|
||||||
|
m_data = take(lightMap.m_data);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
Lightmap::operator ImageView() {
|
||||||
|
ImageView view;
|
||||||
|
view.data = (uint8_t*)m_data.get();
|
||||||
|
view.size = size();
|
||||||
|
view.format = PixelFormat::RGB_F;
|
||||||
|
return view;
|
||||||
|
}
|
||||||
|
|
||||||
CellularLightingCalculator::CellularLightingCalculator(bool monochrome)
|
CellularLightingCalculator::CellularLightingCalculator(bool monochrome)
|
||||||
: m_monochrome(monochrome)
|
: m_monochrome(monochrome)
|
||||||
{
|
{
|
||||||
@ -104,6 +143,32 @@ void CellularLightingCalculator::calculate(Image& output) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CellularLightingCalculator::calculate(Lightmap& output) {
|
||||||
|
Vec2S arrayMin = Vec2S(m_queryRegion.min() - m_calculationRegion.min());
|
||||||
|
Vec2S arrayMax = Vec2S(m_queryRegion.max() - m_calculationRegion.min());
|
||||||
|
|
||||||
|
if (m_monochrome)
|
||||||
|
m_lightArray.right().calculate(arrayMin[0], arrayMin[1], arrayMax[0], arrayMax[1]);
|
||||||
|
else
|
||||||
|
m_lightArray.left().calculate(arrayMin[0], arrayMin[1], arrayMax[0], arrayMax[1]);
|
||||||
|
|
||||||
|
output = Lightmap(arrayMax[0] - arrayMin[0], arrayMax[1] - arrayMin[1]);
|
||||||
|
|
||||||
|
if (m_monochrome) {
|
||||||
|
for (size_t x = arrayMin[0]; x < arrayMax[0]; ++x) {
|
||||||
|
for (size_t y = arrayMin[1]; y < arrayMax[1]; ++y) {
|
||||||
|
output.set(x - arrayMin[0], y - arrayMin[1], m_lightArray.right().getLight(x, y));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (size_t x = arrayMin[0]; x < arrayMax[0]; ++x) {
|
||||||
|
for (size_t y = arrayMin[1]; y < arrayMax[1]; ++y) {
|
||||||
|
output.set(x - arrayMin[0], y - arrayMin[1], m_lightArray.left().getLight(x, y));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void CellularLightingCalculator::setupImage(Image& image, PixelFormat format) const {
|
void CellularLightingCalculator::setupImage(Image& image, PixelFormat format) const {
|
||||||
Vec2S arrayMin = Vec2S(m_queryRegion.min() - m_calculationRegion.min());
|
Vec2S arrayMin = Vec2S(m_queryRegion.min() - m_calculationRegion.min());
|
||||||
Vec2S arrayMax = Vec2S(m_queryRegion.max() - m_calculationRegion.min());
|
Vec2S arrayMax = Vec2S(m_queryRegion.max() - m_calculationRegion.min());
|
||||||
|
@ -11,6 +11,93 @@
|
|||||||
|
|
||||||
namespace Star {
|
namespace Star {
|
||||||
|
|
||||||
|
STAR_EXCEPTION(LightmapException, StarException);
|
||||||
|
|
||||||
|
class Lightmap {
|
||||||
|
public:
|
||||||
|
Lightmap();
|
||||||
|
Lightmap(unsigned width, unsigned height);
|
||||||
|
Lightmap(Lightmap const& lightMap);
|
||||||
|
Lightmap(Lightmap&& lightMap) noexcept;
|
||||||
|
|
||||||
|
Lightmap& operator=(Lightmap const& lightMap);
|
||||||
|
Lightmap& operator=(Lightmap&& lightMap) noexcept;
|
||||||
|
|
||||||
|
operator ImageView();
|
||||||
|
|
||||||
|
void set(unsigned x, unsigned y, float v);
|
||||||
|
void set(unsigned x, unsigned y, Vec3F const& v);
|
||||||
|
Vec3F get(unsigned x, unsigned y) const;
|
||||||
|
|
||||||
|
bool empty() const;
|
||||||
|
|
||||||
|
Vec2U size() const;
|
||||||
|
unsigned width() const;
|
||||||
|
unsigned height() const;
|
||||||
|
float* data();
|
||||||
|
|
||||||
|
private:
|
||||||
|
size_t len() const;
|
||||||
|
|
||||||
|
std::unique_ptr<float[]> m_data;
|
||||||
|
unsigned m_width;
|
||||||
|
unsigned m_height;
|
||||||
|
};
|
||||||
|
|
||||||
|
inline void Lightmap::set(unsigned x, unsigned y, float v) {
|
||||||
|
if (x >= m_width || y >= m_height) {
|
||||||
|
throw LightmapException(strf("[{}, {}] out of range in Lightmap::set", x, y));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
float* ptr = m_data.get() + (y * m_width * 3 + x * 3);
|
||||||
|
ptr[0] = ptr[1] = ptr[2] = v;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void Lightmap::set(unsigned x, unsigned y, Vec3F const& v) {
|
||||||
|
if (x >= m_width || y >= m_height) {
|
||||||
|
throw LightmapException(strf("[{}, {}] out of range in Lightmap::set", x, y));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
float* ptr = m_data.get() + (y * m_width * 3 + x * 3);
|
||||||
|
ptr[0] = v.x();
|
||||||
|
ptr[1] = v.y();
|
||||||
|
ptr[2] = v.z();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
inline Vec3F Lightmap::get(unsigned x, unsigned y) const {
|
||||||
|
if (x >= m_width || y >= m_height) {
|
||||||
|
throw LightmapException(strf("[{}, {}] out of range in Lightmap::get", x, y));
|
||||||
|
return Vec3F();
|
||||||
|
}
|
||||||
|
float* ptr = m_data.get() + (y * m_width * 3 + x * 3);
|
||||||
|
return Vec3F(ptr[0], ptr[1], ptr[2]);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool Lightmap::empty() const {
|
||||||
|
return m_width == 0 || m_height == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline Vec2U Lightmap::size() const {
|
||||||
|
return { m_width, m_height };
|
||||||
|
}
|
||||||
|
|
||||||
|
inline unsigned Lightmap::width() const {
|
||||||
|
return m_width;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline unsigned Lightmap::height() const {
|
||||||
|
return m_height;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline float* Lightmap::data() {
|
||||||
|
return m_data.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
inline size_t Lightmap::len() const {
|
||||||
|
return m_width * m_height * 3;
|
||||||
|
}
|
||||||
|
|
||||||
// Produce lighting values from an integral cellular grid. Allows for floating
|
// Produce lighting values from an integral cellular grid. Allows for floating
|
||||||
// positional point and cellular light sources, as well as pre-lighting cells
|
// positional point and cellular light sources, as well as pre-lighting cells
|
||||||
// individually.
|
// individually.
|
||||||
@ -43,6 +130,8 @@ public:
|
|||||||
// output image. The image will be reset to the size of the region given in
|
// output image. The image will be reset to the size of the region given in
|
||||||
// the call to 'begin', and formatted as RGB24.
|
// the call to 'begin', and formatted as RGB24.
|
||||||
void calculate(Image& output);
|
void calculate(Image& output);
|
||||||
|
// Same as above, but the color data in a float buffer instead.
|
||||||
|
void calculate(Lightmap& output);
|
||||||
|
|
||||||
void setupImage(Image& image, PixelFormat format = PixelFormat::RGB24) const;
|
void setupImage(Image& image, PixelFormat format = PixelFormat::RGB24) const;
|
||||||
private:
|
private:
|
||||||
|
@ -228,35 +228,8 @@ void ClientApplication::applicationInit(ApplicationControllerPtr appController)
|
|||||||
|
|
||||||
void ClientApplication::renderInit(RendererPtr renderer) {
|
void ClientApplication::renderInit(RendererPtr renderer) {
|
||||||
Application::renderInit(renderer);
|
Application::renderInit(renderer);
|
||||||
auto assets = m_root->assets();
|
renderReload();
|
||||||
|
m_root->registerReloadListener(m_reloadListener = make_shared<CallbackListener>([this]() { renderReload(); }));
|
||||||
auto loadEffectConfig = [&](String const& name) {
|
|
||||||
String path = strf("/rendering/effects/{}.config", name);
|
|
||||||
if (assets->assetExists(path)) {
|
|
||||||
StringMap<String> shaders;
|
|
||||||
auto config = assets->json(path);
|
|
||||||
auto shaderConfig = config.getObject("effectShaders");
|
|
||||||
for (auto& entry : shaderConfig) {
|
|
||||||
if (entry.second.isType(Json::Type::String)) {
|
|
||||||
String shader = entry.second.toString();
|
|
||||||
if (!shader.hasChar('\n')) {
|
|
||||||
auto shaderBytes = assets->bytes(AssetPath::relativeTo(path, shader));
|
|
||||||
shader = std::string(shaderBytes->ptr(), shaderBytes->size());
|
|
||||||
}
|
|
||||||
shaders[entry.first] = shader;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
renderer->loadEffectConfig(name, config, shaders);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
Logger::warn("No rendering config found for renderer with id '{}'", renderer->rendererId());
|
|
||||||
};
|
|
||||||
|
|
||||||
renderer->loadConfig(assets->json("/rendering/opengl20.config"));
|
|
||||||
|
|
||||||
loadEffectConfig("world");
|
|
||||||
loadEffectConfig("interface");
|
|
||||||
|
|
||||||
if (m_root->configuration()->get("limitTextureAtlasSize").optBool().value(false))
|
if (m_root->configuration()->get("limitTextureAtlasSize").optBool().value(false))
|
||||||
renderer->setSizeLimitEnabled(true);
|
renderer->setSizeLimitEnabled(true);
|
||||||
@ -427,12 +400,7 @@ void ClientApplication::render() {
|
|||||||
|
|
||||||
auto paintStart = Time::monotonicMicroseconds();
|
auto paintStart = Time::monotonicMicroseconds();
|
||||||
m_worldPainter->render(m_renderData, [&]() -> bool {
|
m_worldPainter->render(m_renderData, [&]() -> bool {
|
||||||
if (auto newMinPosition = worldClient->waitForLighting(&m_renderData.lightMap)) {
|
return worldClient->waitForLighting(&m_renderData);
|
||||||
m_renderData.lightMinPosition = *newMinPosition;
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
LogMap::set("client_render_world_painter", strf(u8"{:05d}\u00b5s", Time::monotonicMicroseconds() - paintStart));
|
LogMap::set("client_render_world_painter", strf(u8"{:05d}\u00b5s", Time::monotonicMicroseconds() - paintStart));
|
||||||
LogMap::set("client_render_world_total", strf(u8"{:05d}\u00b5s", Time::monotonicMicroseconds() - totalStart));
|
LogMap::set("client_render_world_total", strf(u8"{:05d}\u00b5s", Time::monotonicMicroseconds() - totalStart));
|
||||||
@ -458,6 +426,38 @@ void ClientApplication::getAudioData(int16_t* sampleData, size_t frameCount) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ClientApplication::renderReload() {
|
||||||
|
auto assets = m_root->assets();
|
||||||
|
auto renderer = Application::renderer();
|
||||||
|
|
||||||
|
auto loadEffectConfig = [&](String const& name) {
|
||||||
|
String path = strf("/rendering/effects/{}.config", name);
|
||||||
|
if (assets->assetExists(path)) {
|
||||||
|
StringMap<String> shaders;
|
||||||
|
auto config = assets->json(path);
|
||||||
|
auto shaderConfig = config.getObject("effectShaders");
|
||||||
|
for (auto& entry : shaderConfig) {
|
||||||
|
if (entry.second.isType(Json::Type::String)) {
|
||||||
|
String shader = entry.second.toString();
|
||||||
|
if (!shader.hasChar('\n')) {
|
||||||
|
auto shaderBytes = assets->bytes(AssetPath::relativeTo(path, shader));
|
||||||
|
shader = std::string(shaderBytes->ptr(), shaderBytes->size());
|
||||||
|
}
|
||||||
|
shaders[entry.first] = shader;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
renderer->loadEffectConfig(name, config, shaders);
|
||||||
|
} else
|
||||||
|
Logger::warn("No rendering config found for renderer with id '{}'", renderer->rendererId());
|
||||||
|
};
|
||||||
|
|
||||||
|
renderer->loadConfig(assets->json("/rendering/opengl20.config"));
|
||||||
|
|
||||||
|
loadEffectConfig("world");
|
||||||
|
loadEffectConfig("interface");
|
||||||
|
}
|
||||||
|
|
||||||
void ClientApplication::changeState(MainAppState newState) {
|
void ClientApplication::changeState(MainAppState newState) {
|
||||||
MainAppState oldState = m_state;
|
MainAppState oldState = m_state;
|
||||||
m_state = newState;
|
m_state = newState;
|
||||||
|
@ -53,6 +53,8 @@ private:
|
|||||||
String password;
|
String password;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
void renderReload();
|
||||||
|
|
||||||
void changeState(MainAppState newState);
|
void changeState(MainAppState newState);
|
||||||
void setError(String const& error);
|
void setError(String const& error);
|
||||||
void setError(String const& error, std::exception const& e);
|
void setError(String const& error, std::exception const& e);
|
||||||
@ -71,6 +73,8 @@ private:
|
|||||||
|
|
||||||
RootUPtr m_root;
|
RootUPtr m_root;
|
||||||
ThreadFunction<void> m_rootLoader;
|
ThreadFunction<void> m_rootLoader;
|
||||||
|
CallbackListenerPtr m_reloadListener;
|
||||||
|
|
||||||
MainAppState m_state = MainAppState::Startup;
|
MainAppState m_state = MainAppState::Startup;
|
||||||
|
|
||||||
// Valid after applicationInit is called
|
// Valid after applicationInit is called
|
||||||
|
@ -518,4 +518,10 @@ void Image::writePng(IODevicePtr device) const {
|
|||||||
png_destroy_write_struct(&png_ptr, &info_ptr);
|
png_destroy_write_struct(&png_ptr, &info_ptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ImageView::ImageView(Image const& image) {
|
||||||
|
size = image.size();
|
||||||
|
data = image.data();
|
||||||
|
format = image.pixelFormat();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -6,11 +6,13 @@
|
|||||||
|
|
||||||
namespace Star {
|
namespace Star {
|
||||||
|
|
||||||
enum class PixelFormat {
|
enum class PixelFormat : uint8_t {
|
||||||
RGB24,
|
RGB24,
|
||||||
RGBA32,
|
RGBA32,
|
||||||
BGR24,
|
BGR24,
|
||||||
BGRA32
|
BGRA32,
|
||||||
|
RGB_F,
|
||||||
|
RGBA_F
|
||||||
};
|
};
|
||||||
|
|
||||||
uint8_t bitsPerPixel(PixelFormat pf);
|
uint8_t bitsPerPixel(PixelFormat pf);
|
||||||
@ -148,8 +150,12 @@ inline uint8_t bitsPerPixel(PixelFormat pf) {
|
|||||||
return 32;
|
return 32;
|
||||||
case PixelFormat::BGR24:
|
case PixelFormat::BGR24:
|
||||||
return 24;
|
return 24;
|
||||||
default:
|
case PixelFormat::BGRA32:
|
||||||
return 32;
|
return 32;
|
||||||
|
case PixelFormat::RGB_F:
|
||||||
|
return 96;
|
||||||
|
default:
|
||||||
|
return 128;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -161,8 +167,12 @@ inline uint8_t bytesPerPixel(PixelFormat pf) {
|
|||||||
return 4;
|
return 4;
|
||||||
case PixelFormat::BGR24:
|
case PixelFormat::BGR24:
|
||||||
return 3;
|
return 3;
|
||||||
default:
|
case PixelFormat::BGRA32:
|
||||||
return 4;
|
return 4;
|
||||||
|
case PixelFormat::RGB_F:
|
||||||
|
return 12;
|
||||||
|
default:
|
||||||
|
return 16;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -307,4 +317,14 @@ void Image::forEachPixel(CallbackType&& callback) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct ImageView {
|
||||||
|
inline bool empty() const { return size.x() == 0 || size.y() == 0; }
|
||||||
|
ImageView() = default;
|
||||||
|
ImageView(Image const& image);
|
||||||
|
|
||||||
|
Vec2U size{0, 0};
|
||||||
|
uint8_t const* data = nullptr;
|
||||||
|
PixelFormat format = PixelFormat::RGB24;
|
||||||
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -214,13 +214,12 @@ Color jsonToColor(Json const& v) {
|
|||||||
if (v.type() != Json::Type::Array || (v.size() != 3 && v.size() != 4))
|
if (v.type() != Json::Type::Array || (v.size() != 3 && v.size() != 4))
|
||||||
throw JsonException("Json not an array of size 3 or 4 in jsonToColor");
|
throw JsonException("Json not an array of size 3 or 4 in jsonToColor");
|
||||||
Color c = Color::rgba(0, 0, 0, 255);
|
Color c = Color::rgba(0, 0, 0, 255);
|
||||||
|
c.setRedF((float)v.getInt(0) / 255.f);
|
||||||
c.setRed(v.getInt(0));
|
c.setGreenF((float)v.getInt(1) / 255.f);
|
||||||
c.setGreen(v.getInt(1));
|
c.setBlueF((float)v.getInt(2) / 255.f);
|
||||||
c.setBlue(v.getInt(2));
|
|
||||||
|
|
||||||
if (v.size() == 4)
|
if (v.size() == 4)
|
||||||
c.setAlpha(v.getInt(3));
|
c.setAlphaF((float)v.getInt(3) / 255.f);
|
||||||
|
|
||||||
return c;
|
return c;
|
||||||
} else if (v.type() == Json::Type::String) {
|
} else if (v.type() == Json::Type::String) {
|
||||||
@ -232,11 +231,11 @@ Color jsonToColor(Json const& v) {
|
|||||||
|
|
||||||
Json jsonFromColor(Color const& color) {
|
Json jsonFromColor(Color const& color) {
|
||||||
JsonArray result;
|
JsonArray result;
|
||||||
result.push_back(color.red());
|
result.push_back(color.redF() * 255.f);
|
||||||
result.push_back(color.green());
|
result.push_back(color.greenF() * 255.f);
|
||||||
result.push_back(color.blue());
|
result.push_back(color.blueF() * 255.f);
|
||||||
if (color.alpha() != 255) {
|
if (color.alphaF() < 255.f) {
|
||||||
result.push_back(color.alpha());
|
result.push_back(color.alphaF() * 255.f);
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
@ -321,7 +321,7 @@ void ItemDrop::render(RenderCallback* renderCallback) {
|
|||||||
void ItemDrop::renderLightSources(RenderCallback* renderCallback) {
|
void ItemDrop::renderLightSources(RenderCallback* renderCallback) {
|
||||||
LightSource light;
|
LightSource light;
|
||||||
light.pointLight = false;
|
light.pointLight = false;
|
||||||
light.color = Vec3B::filled(20);
|
light.color = Vec3F::filled(20.f / 255.f);
|
||||||
light.position = position();
|
light.position = position();
|
||||||
renderCallback->addLightSource(std::move(light));
|
renderCallback->addLightSource(std::move(light));
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,7 @@ namespace Star {
|
|||||||
|
|
||||||
struct LightSource {
|
struct LightSource {
|
||||||
Vec2F position;
|
Vec2F position;
|
||||||
Vec3B color;
|
Vec3F color;
|
||||||
|
|
||||||
bool pointLight;
|
bool pointLight;
|
||||||
// pointBeam of 0.0 means light has no beam component, as pointBeam goes up,
|
// pointBeam of 0.0 means light has no beam component, as pointBeam goes up,
|
||||||
|
@ -716,7 +716,7 @@ List<LightSource> NetworkedAnimator::lightSources(Vec2F const& translate) const
|
|||||||
|
|
||||||
lightSources.append(LightSource{
|
lightSources.append(LightSource{
|
||||||
position + translate,
|
position + translate,
|
||||||
color.toRgb(),
|
color.toRgbF(),
|
||||||
pair.second.pointLight,
|
pair.second.pointLight,
|
||||||
pair.second.pointBeam,
|
pair.second.pointBeam,
|
||||||
pointAngle,
|
pointAngle,
|
||||||
|
@ -1014,8 +1014,8 @@ bool Npc::isAdmin() const {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
Vec4B Npc::favoriteColor() const {
|
Color Npc::favoriteColor() const {
|
||||||
return Color::White.toRgba();
|
return Color::White;
|
||||||
}
|
}
|
||||||
|
|
||||||
float Npc::beamGunRadius() const {
|
float Npc::beamGunRadius() const {
|
||||||
|
@ -143,7 +143,7 @@ public:
|
|||||||
Direction facingDirection() const override;
|
Direction facingDirection() const override;
|
||||||
Direction walkingDirection() const override;
|
Direction walkingDirection() const override;
|
||||||
bool isAdmin() const override;
|
bool isAdmin() const override;
|
||||||
Vec4B favoriteColor() const override;
|
Color favoriteColor() const override;
|
||||||
float beamGunRadius() const override;
|
float beamGunRadius() const override;
|
||||||
void addParticles(List<Particle> const& particles) override;
|
void addParticles(List<Particle> const& particles) override;
|
||||||
void addSound(String const& sound, float volume = 1.0f, float pitch = 1.0f) override;
|
void addSound(String const& sound, float volume = 1.0f, float pitch = 1.0f) override;
|
||||||
|
@ -261,7 +261,7 @@ List<LightSource> Object::lightSources() const {
|
|||||||
|
|
||||||
LightSource lightSource;
|
LightSource lightSource;
|
||||||
lightSource.position = position() + centerOfTile(orientation->lightPosition);
|
lightSource.position = position() + centerOfTile(orientation->lightPosition);
|
||||||
lightSource.color = color.toRgb();
|
lightSource.color = color.toRgbF();
|
||||||
lightSource.pointLight = m_config->pointLight;
|
lightSource.pointLight = m_config->pointLight;
|
||||||
lightSource.pointBeam = m_config->pointBeam;
|
lightSource.pointBeam = m_config->pointBeam;
|
||||||
lightSource.beamAngle = orientation->beamAngle;
|
lightSource.beamAngle = orientation->beamAngle;
|
||||||
|
@ -103,11 +103,11 @@ List<Particle> const& ParticleManager::particles() const {
|
|||||||
return m_particles;
|
return m_particles;
|
||||||
}
|
}
|
||||||
|
|
||||||
List<pair<Vec2F, Vec3B>> ParticleManager::lightSources() const {
|
List<pair<Vec2F, Vec3F>> ParticleManager::lightSources() const {
|
||||||
List<pair<Vec2F, Vec3B>> lsources;
|
List<pair<Vec2F, Vec3F>> lsources;
|
||||||
for (auto const& particle : m_particles) {
|
for (auto const& particle : m_particles) {
|
||||||
if (particle.light != Color::Clear)
|
if (particle.light != Color::Clear)
|
||||||
lsources.append({particle.position, particle.light.toRgb()});
|
lsources.append({particle.position, particle.light.toRgbF()});
|
||||||
}
|
}
|
||||||
return lsources;
|
return lsources;
|
||||||
}
|
}
|
||||||
|
@ -24,7 +24,7 @@ public:
|
|||||||
void update(float dt, RectF const& cullRegion, float wind);
|
void update(float dt, RectF const& cullRegion, float wind);
|
||||||
|
|
||||||
List<Particle> const& particles() const;
|
List<Particle> const& particles() const;
|
||||||
List<pair<Vec2F, Vec3B>> lightSources() const;
|
List<pair<Vec2F, Vec3F>> lightSources() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
enum class TileType { Colliding, Water, Empty };
|
enum class TileType { Colliding, Water, Empty };
|
||||||
|
@ -1912,13 +1912,13 @@ bool Player::isAdmin() const {
|
|||||||
return m_isAdmin;
|
return m_isAdmin;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Player::setFavoriteColor(Vec4B color) {
|
void Player::setFavoriteColor(Color color) {
|
||||||
m_identity.color = color;
|
m_identity.color = color.toRgba();
|
||||||
updateIdentity();
|
updateIdentity();
|
||||||
}
|
}
|
||||||
|
|
||||||
Vec4B Player::favoriteColor() const {
|
Color Player::favoriteColor() const {
|
||||||
return m_identity.color;
|
return Color::rgba(m_identity.color);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Player::isTeleporting() const {
|
bool Player::isTeleporting() const {
|
||||||
|
@ -354,8 +354,8 @@ public:
|
|||||||
bool isDead() const;
|
bool isDead() const;
|
||||||
void kill();
|
void kill();
|
||||||
|
|
||||||
void setFavoriteColor(Vec4B color);
|
void setFavoriteColor(Color color);
|
||||||
Vec4B favoriteColor() const override;
|
Color favoriteColor() const override;
|
||||||
|
|
||||||
// Starts the teleport animation sequence, locking player movement and
|
// Starts the teleport animation sequence, locking player movement and
|
||||||
// preventing some update code
|
// preventing some update code
|
||||||
|
@ -379,7 +379,7 @@ void Projectile::renderLightSources(RenderCallback* renderCallback) {
|
|||||||
if (renderable.is<LightSource>())
|
if (renderable.is<LightSource>())
|
||||||
renderCallback->addLightSource(renderable.get<LightSource>());
|
renderCallback->addLightSource(renderable.get<LightSource>());
|
||||||
}
|
}
|
||||||
renderCallback->addLightSource({ position(), m_config->lightColor, m_config->pointLight, 0.0f, 0.0f, 0.0f });
|
renderCallback->addLightSource({position(), m_config->lightColor.toRgbF(), m_config->pointLight, 0.0f, 0.0f, 0.0f});
|
||||||
}
|
}
|
||||||
|
|
||||||
Maybe<Json> Projectile::receiveMessage(ConnectionId sendingConnection, String const& message, JsonArray const& args) {
|
Maybe<Json> Projectile::receiveMessage(ConnectionId sendingConnection, String const& message, JsonArray const& args) {
|
||||||
@ -824,7 +824,7 @@ void Projectile::processAction(Json const& action) {
|
|||||||
|
|
||||||
m_pendingRenderables.append(LightSource{
|
m_pendingRenderables.append(LightSource{
|
||||||
position(),
|
position(),
|
||||||
jsonToColor(parameters.get("color")).toRgb(),
|
jsonToColor(parameters.get("color")).toRgbF(),
|
||||||
parameters.getBool("pointLight", true),
|
parameters.getBool("pointLight", true),
|
||||||
0.0f,
|
0.0f,
|
||||||
0.0f,
|
0.0f,
|
||||||
|
@ -116,7 +116,7 @@ ProjectileConfigPtr ProjectileDatabase::readConfig(String const& path) {
|
|||||||
projectileConfig->fullbright = config.getBool("fullbright", false);
|
projectileConfig->fullbright = config.getBool("fullbright", false);
|
||||||
projectileConfig->renderLayer = parseRenderLayer(config.getString("renderLayer", "Projectile"));
|
projectileConfig->renderLayer = parseRenderLayer(config.getString("renderLayer", "Projectile"));
|
||||||
|
|
||||||
projectileConfig->lightColor = jsonToVec3B(config.get("lightColor", JsonArray{0, 0, 0}));
|
projectileConfig->lightColor = jsonToColor(config.get("lightColor", JsonArray{0, 0, 0}));
|
||||||
projectileConfig->lightPosition = jsonToVec2F(config.get("lightPosition", JsonArray{0, 0}));
|
projectileConfig->lightPosition = jsonToVec2F(config.get("lightPosition", JsonArray{0, 0}));
|
||||||
projectileConfig->pointLight = config.getBool("pointLight", false);
|
projectileConfig->pointLight = config.getBool("pointLight", false);
|
||||||
|
|
||||||
|
@ -66,7 +66,7 @@ struct ProjectileConfig {
|
|||||||
bool fullbright = false;
|
bool fullbright = false;
|
||||||
EntityRenderLayer renderLayer;
|
EntityRenderLayer renderLayer;
|
||||||
|
|
||||||
Vec3B lightColor;
|
Color lightColor;
|
||||||
Vec2F lightPosition;
|
Vec2F lightPosition;
|
||||||
bool pointLight = false;
|
bool pointLight = false;
|
||||||
|
|
||||||
|
@ -165,7 +165,7 @@ Maybe<float> ToolUser::toolRadius() const {
|
|||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
List<Drawable> ToolUser::renderObjectPreviews(Vec2F aimPosition, Direction walkingDirection, bool inToolRange, Vec4B favoriteColor) const {
|
List<Drawable> ToolUser::renderObjectPreviews(Vec2F aimPosition, Direction walkingDirection, bool inToolRange, Color favoriteColor) const {
|
||||||
if (m_suppress.get() || !m_user)
|
if (m_suppress.get() || !m_user)
|
||||||
return {};
|
return {};
|
||||||
|
|
||||||
@ -178,21 +178,16 @@ List<Drawable> ToolUser::renderObjectPreviews(Vec2F aimPosition, Direction walki
|
|||||||
|
|
||||||
Color opacityMask = Color::White;
|
Color opacityMask = Color::White;
|
||||||
opacityMask.setAlphaF(item->getAppropriateOpacity());
|
opacityMask.setAlphaF(item->getAppropriateOpacity());
|
||||||
Vec4B favoriteColorTrans;
|
Color favoriteColorTrans = favoriteColor;
|
||||||
if (inToolRange && objectDatabase->canPlaceObject(m_user->world(), aimPos, item->objectName())) {
|
if (!inToolRange || !objectDatabase->canPlaceObject(m_user->world(), aimPos, item->objectName()))
|
||||||
favoriteColorTrans = favoriteColor;
|
favoriteColorTrans.setHue(favoriteColor.hue() + 120);
|
||||||
} else {
|
|
||||||
Color color = Color::rgba(favoriteColor);
|
|
||||||
color.setHue(color.hue() + 120);
|
|
||||||
favoriteColorTrans = color.toRgba();
|
|
||||||
}
|
|
||||||
|
|
||||||
favoriteColorTrans[3] = m_objectPreviewOuterAlpha * 255;
|
favoriteColorTrans.setAlphaF(m_objectPreviewOuterAlpha);
|
||||||
Color nearWhite = Color::rgba(favoriteColorTrans);
|
Color nearWhite = favoriteColorTrans;
|
||||||
nearWhite.setValue(1 - (1 - nearWhite.value()) / 5);
|
nearWhite.setValue(1 - (1 - nearWhite.value()) / 5);
|
||||||
nearWhite.setSaturation(nearWhite.saturation() / 5);
|
nearWhite.setSaturation(nearWhite.saturation() / 5);
|
||||||
nearWhite.setAlphaF(m_objectPreviewInnerAlpha);
|
nearWhite.setAlphaF(m_objectPreviewInnerAlpha);
|
||||||
ImageOperation op = BorderImageOperation{m_beamGunGlowBorder, nearWhite.toRgba(), favoriteColorTrans, false, false};
|
ImageOperation op = BorderImageOperation{m_beamGunGlowBorder, nearWhite.toRgba(), favoriteColorTrans.toRgba(), false, false};
|
||||||
|
|
||||||
for (Drawable& drawable : drawables) {
|
for (Drawable& drawable : drawables) {
|
||||||
if (drawable.isImage())
|
if (drawable.isImage())
|
||||||
|
@ -43,7 +43,7 @@ public:
|
|||||||
// with the rest of everything else, there are TILE previews and OBJECT
|
// with the rest of everything else, there are TILE previews and OBJECT
|
||||||
// previews, but of course one has to go through the render method and the
|
// previews, but of course one has to go through the render method and the
|
||||||
// other has to be rendered separately.
|
// other has to be rendered separately.
|
||||||
List<Drawable> renderObjectPreviews(Vec2F aimPosition, Direction walkingDirection, bool inToolRange, Vec4B favoriteColor) const;
|
List<Drawable> renderObjectPreviews(Vec2F aimPosition, Direction walkingDirection, bool inToolRange, Color favoriteColor) const;
|
||||||
// Returns the facing override direciton if there is one
|
// Returns the facing override direciton if there is one
|
||||||
Maybe<Direction> setupHumanoidHandItems(Humanoid& humanoid, Vec2F position, Vec2F aimPosition) const;
|
Maybe<Direction> setupHumanoidHandItems(Humanoid& humanoid, Vec2F position, Vec2F aimPosition) const;
|
||||||
void setupHumanoidHandItemDrawables(Humanoid& humanoid) const;
|
void setupHumanoidHandItemDrawables(Humanoid& humanoid) const;
|
||||||
|
@ -1375,21 +1375,22 @@ void WorldClient::collectLiquid(List<Vec2I> const& tilePositions, LiquidId liqui
|
|||||||
m_outgoingPackets.append(make_shared<CollectLiquidPacket>(tilePositions, liquidId));
|
m_outgoingPackets.append(make_shared<CollectLiquidPacket>(tilePositions, liquidId));
|
||||||
}
|
}
|
||||||
|
|
||||||
Maybe<Vec2I> WorldClient::waitForLighting(Image* out) {
|
bool WorldClient::waitForLighting(WorldRenderData* renderData) {
|
||||||
MutexLocker prepLocker(m_lightMapPrepMutex);
|
MutexLocker prepLocker(m_lightMapPrepMutex);
|
||||||
MutexLocker lightMapLocker(m_lightMapMutex);
|
MutexLocker lightMapLocker(m_lightMapMutex);
|
||||||
if (out && !m_lightMap.empty()) {
|
if (renderData && !m_lightMap.empty()) {
|
||||||
for (auto& previewTile : m_previewTiles) {
|
for (auto& previewTile : m_previewTiles) {
|
||||||
if (previewTile.updateLight) {
|
if (previewTile.updateLight) {
|
||||||
Vec2I lightArrayPos = m_geometry.diff(previewTile.position, m_lightMinPosition);
|
Vec2I lightArrayPos = m_geometry.diff(previewTile.position, m_lightMinPosition);
|
||||||
if (lightArrayPos[0] >= 0 && lightArrayPos[0] < (int)m_lightMap.width() && lightArrayPos[1] >= 0 && lightArrayPos[1] < (int)m_lightMap.height())
|
if (lightArrayPos[0] >= 0 && lightArrayPos[0] < (int)m_lightMap.width() && lightArrayPos[1] >= 0 && lightArrayPos[1] < (int)m_lightMap.height())
|
||||||
m_lightMap.set(Vec2U(lightArrayPos), previewTile.light);
|
m_lightMap.set(lightArrayPos[0], lightArrayPos[1], Color::v3bToFloat(previewTile.light));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
*out = std::move(m_lightMap);
|
renderData->lightMap = std::move(m_lightMap);
|
||||||
return m_lightMinPosition;
|
renderData->lightMinPosition = m_lightMinPosition;
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
return {};
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
WorldClient::BroadcastCallback& WorldClient::broadcastCallback() {
|
WorldClient::BroadcastCallback& WorldClient::broadcastCallback() {
|
||||||
@ -1636,25 +1637,30 @@ void WorldClient::lightingTileGather() {
|
|||||||
|
|
||||||
void WorldClient::lightingCalc() {
|
void WorldClient::lightingCalc() {
|
||||||
MutexLocker prepLocker(m_lightMapPrepMutex);
|
MutexLocker prepLocker(m_lightMapPrepMutex);
|
||||||
|
|
||||||
RectI lightRange = m_pendingLightRange;
|
RectI lightRange = m_pendingLightRange;
|
||||||
List<LightSource> lights = std::move(m_pendingLights);
|
List<LightSource> lights = std::move(m_pendingLights);
|
||||||
List<std::pair<Vec2F, Vec3B>> particleLights = std::move(m_pendingParticleLights);
|
List<std::pair<Vec2F, Vec3F>> particleLights = std::move(m_pendingParticleLights);
|
||||||
m_lightingCalculator.setMonochrome(Root::singleton().configuration()->get("monochromeLighting").toBool());
|
m_lightingCalculator.setMonochrome(Root::singleton().configuration()->get("monochromeLighting").toBool());
|
||||||
m_lightingCalculator.begin(lightRange);
|
m_lightingCalculator.begin(lightRange);
|
||||||
lightingTileGather();
|
lightingTileGather();
|
||||||
|
|
||||||
prepLocker.unlock();
|
prepLocker.unlock();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
for (auto const& light : lights) {
|
for (auto const& light : lights) {
|
||||||
Vec2F position = m_geometry.nearestTo(Vec2F(m_lightingCalculator.calculationRegion().min()), light.position);
|
Vec2F position = m_geometry.nearestTo(Vec2F(m_lightingCalculator.calculationRegion().min()), light.position);
|
||||||
if (light.pointLight)
|
if (light.pointLight)
|
||||||
m_lightingCalculator.addPointLight(position, Color::v3bToFloat(light.color), light.pointBeam, light.beamAngle, light.beamAmbience);
|
m_lightingCalculator.addPointLight(position, light.color, light.pointBeam, light.beamAngle, light.beamAmbience);
|
||||||
else
|
else {
|
||||||
m_lightingCalculator.addSpreadLight(position, Color::v3bToFloat(light.color));
|
m_lightingCalculator.addSpreadLight(position, light.color);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto const& lightPair : particleLights) {
|
for (auto const& lightPair : particleLights) {
|
||||||
Vec2F position = m_geometry.nearestTo(Vec2F(m_lightingCalculator.calculationRegion().min()), lightPair.first);
|
Vec2F position = m_geometry.nearestTo(Vec2F(m_lightingCalculator.calculationRegion().min()), lightPair.first);
|
||||||
m_lightingCalculator.addSpreadLight(position, Color::v3bToFloat(lightPair.second));
|
m_lightingCalculator.addSpreadLight(position, lightPair.second);
|
||||||
}
|
}
|
||||||
|
|
||||||
m_lightingCalculator.calculate(m_pendingLightMap);
|
m_lightingCalculator.calculate(m_pendingLightMap);
|
||||||
|
@ -170,7 +170,7 @@ public:
|
|||||||
|
|
||||||
void collectLiquid(List<Vec2I> const& tilePositions, LiquidId liquidId);
|
void collectLiquid(List<Vec2I> const& tilePositions, LiquidId liquidId);
|
||||||
|
|
||||||
Maybe<Vec2I> waitForLighting(Image* out = nullptr);
|
bool waitForLighting(WorldRenderData* renderData = nullptr);
|
||||||
|
|
||||||
typedef std::function<bool(PlayerPtr, StringView)> BroadcastCallback;
|
typedef std::function<bool(PlayerPtr, StringView)> BroadcastCallback;
|
||||||
BroadcastCallback& broadcastCallback();
|
BroadcastCallback& broadcastCallback();
|
||||||
@ -278,10 +278,10 @@ private:
|
|||||||
Mutex m_lightMapPrepMutex;
|
Mutex m_lightMapPrepMutex;
|
||||||
Mutex m_lightMapMutex;
|
Mutex m_lightMapMutex;
|
||||||
|
|
||||||
Image m_pendingLightMap;
|
Lightmap m_pendingLightMap;
|
||||||
Image m_lightMap;
|
Lightmap m_lightMap;
|
||||||
List<LightSource> m_pendingLights;
|
List<LightSource> m_pendingLights;
|
||||||
List<std::pair<Vec2F, Vec3B>> m_pendingParticleLights;
|
List<std::pair<Vec2F, Vec3F>> m_pendingParticleLights;
|
||||||
RectI m_pendingLightRange;
|
RectI m_pendingLightRange;
|
||||||
Vec2I m_lightMinPosition;
|
Vec2I m_lightMinPosition;
|
||||||
List<PreviewTile> m_previewTiles;
|
List<PreviewTile> m_previewTiles;
|
||||||
|
@ -446,9 +446,9 @@ namespace WorldImpl {
|
|||||||
for (auto const& light : entity->lightSources()) {
|
for (auto const& light : entity->lightSources()) {
|
||||||
Vec2F position = worldGeometry.nearestTo(Vec2F(lighting.calculationRegion().min()), light.position);
|
Vec2F position = worldGeometry.nearestTo(Vec2F(lighting.calculationRegion().min()), light.position);
|
||||||
if (light.pointLight)
|
if (light.pointLight)
|
||||||
lighting.addPointLight(position, Color::v3bToFloat(light.color).sum() / 3.0f, light.pointBeam, light.beamAngle, light.beamAmbience);
|
lighting.addPointLight(position, light.color.sum() / 3.0f, light.pointBeam, light.beamAngle, light.beamAmbience);
|
||||||
else
|
else
|
||||||
lighting.addSpreadLight(position, Color::v3bToFloat(light.color).sum() / 3.0f);
|
lighting.addSpreadLight(position, light.color.sum() / 3.0f);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -9,6 +9,7 @@
|
|||||||
#include "StarWeatherTypes.hpp"
|
#include "StarWeatherTypes.hpp"
|
||||||
#include "StarEntity.hpp"
|
#include "StarEntity.hpp"
|
||||||
#include "StarThread.hpp"
|
#include "StarThread.hpp"
|
||||||
|
#include "StarCellularLighting.hpp"
|
||||||
|
|
||||||
namespace Star {
|
namespace Star {
|
||||||
|
|
||||||
@ -17,6 +18,7 @@ struct EntityDrawables {
|
|||||||
Map<EntityRenderLayer, List<Drawable>> layers;
|
Map<EntityRenderLayer, List<Drawable>> layers;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
struct WorldRenderData {
|
struct WorldRenderData {
|
||||||
void clear();
|
void clear();
|
||||||
|
|
||||||
@ -25,7 +27,7 @@ struct WorldRenderData {
|
|||||||
Vec2I tileMinPosition;
|
Vec2I tileMinPosition;
|
||||||
RenderTileArray tiles;
|
RenderTileArray tiles;
|
||||||
Vec2I lightMinPosition;
|
Vec2I lightMinPosition;
|
||||||
Image lightMap;
|
Lightmap lightMap;
|
||||||
|
|
||||||
List<EntityDrawables> entityDrawables;
|
List<EntityDrawables> entityDrawables;
|
||||||
List<Particle> const* particles;
|
List<Particle> const* particles;
|
||||||
|
@ -38,7 +38,7 @@ BeamItem::BeamItem(Json config) {
|
|||||||
m_innerBrightnessScale = config.get("innerBrightnessScale").toFloat();
|
m_innerBrightnessScale = config.get("innerBrightnessScale").toFloat();
|
||||||
m_firstStripeThickness = config.get("firstStripeThickness").toFloat();
|
m_firstStripeThickness = config.get("firstStripeThickness").toFloat();
|
||||||
m_secondStripeThickness = config.get("secondStripeThickness").toFloat();
|
m_secondStripeThickness = config.get("secondStripeThickness").toFloat();
|
||||||
m_color = {255, 255, 255, 255};
|
m_color = Color::White;
|
||||||
m_particleGenerateCooldown = .25;
|
m_particleGenerateCooldown = .25;
|
||||||
m_inRangeLastUpdate = false;
|
m_inRangeLastUpdate = false;
|
||||||
}
|
}
|
||||||
@ -160,12 +160,9 @@ List<Drawable> BeamItem::beamDrawables(bool canPlace) const {
|
|||||||
if ((endPoint - owner()->position()).magnitude() <= m_range && curveLen <= m_range) {
|
if ((endPoint - owner()->position()).magnitude() <= m_range && curveLen <= m_range) {
|
||||||
m_inRangeLastUpdate = true;
|
m_inRangeLastUpdate = true;
|
||||||
int numLines = projectOntoRange(m_minBeamLines, m_maxBeamLines);
|
int numLines = projectOntoRange(m_minBeamLines, m_maxBeamLines);
|
||||||
Vec4B mainColor = m_color;
|
Color mainColor = m_color;
|
||||||
if (!canPlace) {
|
if (!canPlace)
|
||||||
Color temp = Color::rgba(m_color);
|
mainColor.setHue(mainColor.hue() + 120);
|
||||||
temp.setHue(temp.hue() + 120);
|
|
||||||
mainColor = temp.toRgba();
|
|
||||||
}
|
|
||||||
m_lastUpdateColor = mainColor;
|
m_lastUpdateColor = mainColor;
|
||||||
|
|
||||||
String endImage = "";
|
String endImage = "";
|
||||||
@ -189,9 +186,9 @@ List<Drawable> BeamItem::beamDrawables(bool canPlace) const {
|
|||||||
for (auto line = 0; line < numLines; line++) {
|
for (auto line = 0; line < numLines; line++) {
|
||||||
float lineThickness = rangeRand(m_beamWidthDev, m_minBeamWidth, m_maxBeamWidth);
|
float lineThickness = rangeRand(m_beamWidthDev, m_minBeamWidth, m_maxBeamWidth);
|
||||||
float beamTransparency = rangeRand(m_beamTransDev, m_minBeamTrans, m_maxBeamTrans);
|
float beamTransparency = rangeRand(m_beamTransDev, m_minBeamTrans, m_maxBeamTrans);
|
||||||
mainColor[3] = mainColor[3] * beamTransparency;
|
mainColor.setAlphaF(mainColor.alphaF() * beamTransparency);
|
||||||
Vec2F previousLoc = m_beamCurve.origin(); // lines meet at origin and dest.
|
Vec2F previousLoc = m_beamCurve.origin(); // lines meet at origin and dest.
|
||||||
Color innerStripe = Color::rgba(mainColor);
|
Color innerStripe = mainColor;
|
||||||
innerStripe.setValue(1 - (1 - innerStripe.value()) / m_innerBrightnessScale);
|
innerStripe.setValue(1 - (1 - innerStripe.value()) / m_innerBrightnessScale);
|
||||||
innerStripe.setSaturation(innerStripe.saturation() / m_innerBrightnessScale);
|
innerStripe.setSaturation(innerStripe.saturation() / m_innerBrightnessScale);
|
||||||
Vec4B firstStripe = innerStripe.toRgba();
|
Vec4B firstStripe = innerStripe.toRgba();
|
||||||
@ -206,7 +203,7 @@ List<Drawable> BeamItem::beamDrawables(bool canPlace) const {
|
|||||||
m_beamCurve.pointAt(pos) + Vec2F(rangeRand(m_beamJitterDev, -m_maxBeamJitter, m_maxBeamJitter),
|
m_beamCurve.pointAt(pos) + Vec2F(rangeRand(m_beamJitterDev, -m_maxBeamJitter, m_maxBeamJitter),
|
||||||
rangeRand(m_beamJitterDev, -m_maxBeamJitter, m_maxBeamJitter));
|
rangeRand(m_beamJitterDev, -m_maxBeamJitter, m_maxBeamJitter));
|
||||||
res.push_back(
|
res.push_back(
|
||||||
Drawable::makeLine(Line2F(previousLoc, currentLoc), lineThickness, Color::rgba(mainColor), Vec2F()));
|
Drawable::makeLine(Line2F(previousLoc, currentLoc), lineThickness, mainColor, Vec2F()));
|
||||||
res.push_back(Drawable::makeLine(Line2F(previousLoc, currentLoc),
|
res.push_back(Drawable::makeLine(Line2F(previousLoc, currentLoc),
|
||||||
lineThickness * m_firstStripeThickness,
|
lineThickness * m_firstStripeThickness,
|
||||||
Color::rgba(firstStripe),
|
Color::rgba(firstStripe),
|
||||||
@ -218,7 +215,7 @@ List<Drawable> BeamItem::beamDrawables(bool canPlace) const {
|
|||||||
previousLoc = std::move(currentLoc);
|
previousLoc = std::move(currentLoc);
|
||||||
}
|
}
|
||||||
res.push_back(Drawable::makeLine(
|
res.push_back(Drawable::makeLine(
|
||||||
Line2F(previousLoc, m_beamCurve.dest()), lineThickness, Color::rgba(mainColor), Vec2F()));
|
Line2F(previousLoc, m_beamCurve.dest()), lineThickness, mainColor, Vec2F()));
|
||||||
res.push_back(Drawable::makeLine(Line2F(previousLoc, m_beamCurve.dest()),
|
res.push_back(Drawable::makeLine(Line2F(previousLoc, m_beamCurve.dest()),
|
||||||
lineThickness * m_firstStripeThickness,
|
lineThickness * m_firstStripeThickness,
|
||||||
Color::rgba(firstStripe),
|
Color::rgba(firstStripe),
|
||||||
@ -243,7 +240,7 @@ List<Drawable> BeamItem::beamDrawables(bool canPlace) const {
|
|||||||
beamParticle.position = m_beamCurve.pointAt(curveLoc);
|
beamParticle.position = m_beamCurve.pointAt(curveLoc);
|
||||||
beamParticle.size = 1.0f;
|
beamParticle.size = 1.0f;
|
||||||
|
|
||||||
Color randomColor = Color::rgba(m_lastUpdateColor);
|
Color randomColor = m_lastUpdateColor;
|
||||||
randomColor.setValue(1 - (1 - randomColor.value()) / Random::randf(1, 4));
|
randomColor.setValue(1 - (1 - randomColor.value()) / Random::randf(1, 4));
|
||||||
randomColor.setSaturation(randomColor.saturation() / Random::randf(1, 4));
|
randomColor.setSaturation(randomColor.saturation() / Random::randf(1, 4));
|
||||||
|
|
||||||
|
@ -63,10 +63,10 @@ protected:
|
|||||||
float m_innerBrightnessScale;
|
float m_innerBrightnessScale;
|
||||||
float m_firstStripeThickness;
|
float m_firstStripeThickness;
|
||||||
float m_secondStripeThickness;
|
float m_secondStripeThickness;
|
||||||
Vec4B m_color;
|
Color m_color;
|
||||||
|
|
||||||
mutable bool m_inRangeLastUpdate;
|
mutable bool m_inRangeLastUpdate;
|
||||||
mutable Vec4B m_lastUpdateColor;
|
mutable Color m_lastUpdateColor;
|
||||||
mutable float m_particleGenerateCooldown;
|
mutable float m_particleGenerateCooldown;
|
||||||
|
|
||||||
CSplineF m_beamCurve;
|
CSplineF m_beamCurve;
|
||||||
|
@ -26,7 +26,7 @@ public:
|
|||||||
virtual Vec2F aimPosition() const = 0;
|
virtual Vec2F aimPosition() const = 0;
|
||||||
|
|
||||||
virtual bool isAdmin() const = 0;
|
virtual bool isAdmin() const = 0;
|
||||||
virtual Vec4B favoriteColor() const = 0;
|
virtual Color favoriteColor() const = 0;
|
||||||
virtual String species() const = 0;
|
virtual String species() const = 0;
|
||||||
|
|
||||||
virtual void requestEmote(String const& emote) = 0;
|
virtual void requestEmote(String const& emote) = 0;
|
||||||
|
@ -56,7 +56,7 @@ List<LightSource> InspectionTool::lightSources() const {
|
|||||||
LightSource lightSource;
|
LightSource lightSource;
|
||||||
lightSource.pointLight = true;
|
lightSource.pointLight = true;
|
||||||
lightSource.position = owner()->position() + owner()->handPosition(hand(), m_lightPosition - m_handPosition);
|
lightSource.position = owner()->position() + owner()->handPosition(hand(), m_lightPosition - m_handPosition);
|
||||||
lightSource.color = m_lightColor.toRgb();
|
lightSource.color = m_lightColor.toRgbF();
|
||||||
lightSource.pointBeam = m_beamWidth;
|
lightSource.pointBeam = m_beamWidth;
|
||||||
lightSource.beamAngle = angle;
|
lightSource.beamAngle = angle;
|
||||||
lightSource.beamAmbience = m_ambientFactor;
|
lightSource.beamAmbience = m_ambientFactor;
|
||||||
|
@ -122,7 +122,7 @@ void MaterialItem::update(float dt, FireMode fireMode, bool shifting, HashSet<Mo
|
|||||||
void MaterialItem::render(RenderCallback* renderCallback, EntityRenderLayer) {
|
void MaterialItem::render(RenderCallback* renderCallback, EntityRenderLayer) {
|
||||||
if (m_collisionOverride != TileCollisionOverride::None) {
|
if (m_collisionOverride != TileCollisionOverride::None) {
|
||||||
float pulseLevel = 1.f - 0.3f * 0.5f * ((float)sin(2 * Constants::pi * 4.0 * Time::monotonicTime()) + 1.f);
|
float pulseLevel = 1.f - 0.3f * 0.5f * ((float)sin(2 * Constants::pi * 4.0 * Time::monotonicTime()) + 1.f);
|
||||||
Color color = Color::rgba(owner()->favoriteColor()).mix(Color::White);
|
Color color = owner()->favoriteColor().mix(Color::White);
|
||||||
color.setAlphaF(color.alphaF() * pulseLevel * 0.95f);
|
color.setAlphaF(color.alphaF() * pulseLevel * 0.95f);
|
||||||
auto addIndicator = [&](String const& path) {
|
auto addIndicator = [&](String const& path) {
|
||||||
Vec2F basePosition = Vec2F(0.5f, 0.5f);
|
Vec2F basePosition = Vec2F(0.5f, 0.5f);
|
||||||
@ -336,7 +336,7 @@ TileCollisionOverride& MaterialItem::collisionOverride() {
|
|||||||
List<PreviewTile> MaterialItem::previewTiles(bool shifting) const {
|
List<PreviewTile> MaterialItem::previewTiles(bool shifting) const {
|
||||||
List<PreviewTile> result;
|
List<PreviewTile> result;
|
||||||
if (initialized()) {
|
if (initialized()) {
|
||||||
Color lightColor = Color::rgba(owner()->favoriteColor());
|
Color lightColor = owner()->favoriteColor();
|
||||||
Vec3B light = lightColor.toRgb();
|
Vec3B light = lightColor.toRgb();
|
||||||
|
|
||||||
auto material = materialId();
|
auto material = materialId();
|
||||||
|
@ -241,7 +241,7 @@ List<LightSource> Flashlight::lightSources() const {
|
|||||||
LightSource lightSource;
|
LightSource lightSource;
|
||||||
lightSource.pointLight = true;
|
lightSource.pointLight = true;
|
||||||
lightSource.position = owner()->position() + owner()->handPosition(hand(), (m_lightPosition - m_handPosition) / TilePixels);
|
lightSource.position = owner()->position() + owner()->handPosition(hand(), (m_lightPosition - m_handPosition) / TilePixels);
|
||||||
lightSource.color = m_lightColor.toRgb();
|
lightSource.color = m_lightColor.toRgbF();
|
||||||
lightSource.pointBeam = m_beamWidth;
|
lightSource.pointBeam = m_beamWidth;
|
||||||
lightSource.beamAngle = angle;
|
lightSource.beamAngle = angle;
|
||||||
lightSource.beamAmbience = m_ambientFactor;
|
lightSource.beamAmbience = m_ambientFactor;
|
||||||
@ -367,7 +367,7 @@ List<PreviewTile> BeamMiningTool::previewTiles(bool shifting) const {
|
|||||||
|
|
||||||
if (ownerp && worldp) {
|
if (ownerp && worldp) {
|
||||||
if (ownerp->isAdmin() || ownerp->inToolRange()) {
|
if (ownerp->isAdmin() || ownerp->inToolRange()) {
|
||||||
Color lightColor = Color::rgba(ownerp->favoriteColor());
|
Color lightColor = ownerp->favoriteColor();
|
||||||
if (!ready())
|
if (!ready())
|
||||||
lightColor *= Color::rgbaf(0.75f, 0.75f, 0.75f, 1.0f);
|
lightColor *= Color::rgbaf(0.75f, 0.75f, 0.75f, 1.0f);
|
||||||
Vec3B light = lightColor.toRgb();
|
Vec3B light = lightColor.toRgb();
|
||||||
@ -604,13 +604,13 @@ PaintingBeamTool::PaintingBeamTool(Json const& config, String const& directory,
|
|||||||
m_blockVolume = assets->json("/sfx.config:miningBlockVolume").toFloat();
|
m_blockVolume = assets->json("/sfx.config:miningBlockVolume").toFloat();
|
||||||
m_endType = EndType::Object;
|
m_endType = EndType::Object;
|
||||||
|
|
||||||
for (auto color : instanceValue("colorNumbers").toArray())
|
for (auto& color : instanceValue("colorNumbers").toArray())
|
||||||
m_colors.append(jsonToColor(color));
|
m_colors.append(jsonToColor(color));
|
||||||
|
|
||||||
m_colorKeys = jsonToStringList(instanceValue("colorKeys"));
|
m_colorKeys = jsonToStringList(instanceValue("colorKeys"));
|
||||||
|
|
||||||
m_colorIndex = instanceValue("colorIndex", 0).toInt();
|
m_colorIndex = instanceValue("colorIndex", 0).toInt();
|
||||||
m_color = m_colors[m_colorIndex].toRgba();
|
m_color = m_colors[m_colorIndex];
|
||||||
}
|
}
|
||||||
|
|
||||||
ItemPtr PaintingBeamTool::clone() const {
|
ItemPtr PaintingBeamTool::clone() const {
|
||||||
@ -666,7 +666,7 @@ List<PreviewTile> PaintingBeamTool::previewTiles(bool shifting) const {
|
|||||||
void PaintingBeamTool::init(ToolUserEntity* owner, ToolHand hand) {
|
void PaintingBeamTool::init(ToolUserEntity* owner, ToolHand hand) {
|
||||||
FireableItem::init(owner, hand);
|
FireableItem::init(owner, hand);
|
||||||
BeamItem::init(owner, hand);
|
BeamItem::init(owner, hand);
|
||||||
m_color = m_colors[m_colorIndex].toRgba();
|
m_color = m_colors[m_colorIndex];
|
||||||
}
|
}
|
||||||
|
|
||||||
List<Drawable> PaintingBeamTool::nonRotatedDrawables() const {
|
List<Drawable> PaintingBeamTool::nonRotatedDrawables() const {
|
||||||
@ -681,7 +681,7 @@ void PaintingBeamTool::fire(FireMode mode, bool shifting, bool edgeTriggered) {
|
|||||||
|
|
||||||
if (mode == FireMode::Alt && edgeTriggered) {
|
if (mode == FireMode::Alt && edgeTriggered) {
|
||||||
m_colorIndex = (m_colorIndex + 1) % m_colors.size();
|
m_colorIndex = (m_colorIndex + 1) % m_colors.size();
|
||||||
m_color = m_colors[m_colorIndex].toRgba();
|
m_color = m_colors[m_colorIndex];
|
||||||
setInstanceValue("colorIndex", m_colorIndex);
|
setInstanceValue("colorIndex", m_colorIndex);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -76,7 +76,7 @@ LuaAnimationComponent<Base>::LuaAnimationComponent() {
|
|||||||
animationCallbacks.registerCallback("addLightSource", [this](LuaTable const& lightSourceTable) {
|
animationCallbacks.registerCallback("addLightSource", [this](LuaTable const& lightSourceTable) {
|
||||||
m_lightSources.append({
|
m_lightSources.append({
|
||||||
lightSourceTable.get<Vec2F>("position"),
|
lightSourceTable.get<Vec2F>("position"),
|
||||||
lightSourceTable.get<Color>("color").toRgb(),
|
lightSourceTable.get<Color>("color").toRgbF(),
|
||||||
lightSourceTable.get<Maybe<bool>>("pointLight").value(),
|
lightSourceTable.get<Maybe<bool>>("pointLight").value(),
|
||||||
lightSourceTable.get<Maybe<float>>("pointBeam").value(),
|
lightSourceTable.get<Maybe<float>>("pointBeam").value(),
|
||||||
lightSourceTable.get<Maybe<float>>("beamAngle").value(),
|
lightSourceTable.get<Maybe<float>>("beamAngle").value(),
|
||||||
|
@ -44,14 +44,14 @@ void TilePainter::adjustLighting(WorldRenderData& renderData) const {
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
auto lightIndex = Vec2U(pos - renderData.lightMinPosition);
|
auto lightIndex = Vec2U(pos - renderData.lightMinPosition);
|
||||||
auto lightValue = renderData.lightMap.get(lightIndex).vec3();
|
auto lightValue = renderData.lightMap.get(lightIndex.x(), lightIndex.y());
|
||||||
|
|
||||||
auto const& liquid = m_liquids[tile.liquidId];
|
auto const& liquid = m_liquids[tile.liquidId];
|
||||||
Vec3F tileLight = Vec3F(lightValue);
|
Vec3F tileLight = Vec3F(lightValue);
|
||||||
float darknessLevel = (1 - tileLight.sum() / (3.0f * 255.0f)) * drawLevel;
|
float darknessLevel = (1.f - tileLight.sum() / 3.0f) * drawLevel;
|
||||||
lightValue = Vec3B(tileLight.piecewiseMultiply(Vec3F::filled(1 - darknessLevel) + liquid.bottomLightMix * darknessLevel));
|
lightValue = tileLight.piecewiseMultiply(Vec3F::filled(1.f - darknessLevel) + liquid.bottomLightMix * darknessLevel);
|
||||||
|
|
||||||
renderData.lightMap.set(lightIndex, lightValue);
|
renderData.lightMap.set(lightIndex.x(), lightIndex.y(), lightValue);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user