Merge branch 'lighting'
This commit is contained in:
commit
9b10964a3e
1
.gitignore
vendored
1
.gitignore
vendored
@ -6,6 +6,7 @@
|
||||
/mac/
|
||||
/dist/
|
||||
/installer/
|
||||
/vcpkg/
|
||||
/client_distribution/
|
||||
/server_distribution/
|
||||
enc_temp_folder/
|
||||
|
1
assets/opensb/lighting.config.patch
Normal file
1
assets/opensb/lighting.config.patch
Normal file
@ -0,0 +1 @@
|
||||
{}
|
21
assets/opensb/objects/opensb/object.patch.lua
Normal file
21
assets/opensb/objects/opensb/object.patch.lua
Normal file
@ -0,0 +1,21 @@
|
||||
-- unused for now
|
||||
|
||||
local function modLight(light)
|
||||
for i = 1, #light do
|
||||
light[i] = light[i] * 0.4
|
||||
end
|
||||
end
|
||||
|
||||
function patch(object, path)
|
||||
if object.lightColor then
|
||||
modLight(object.lightColor)
|
||||
object.pointLight = true
|
||||
return object;
|
||||
elseif object.lightColors then
|
||||
for i, v in pairs(object.lightColors) do
|
||||
modLight(v)
|
||||
end
|
||||
object.pointLight = true
|
||||
return object;
|
||||
end
|
||||
end
|
@ -53,6 +53,15 @@ vec4 bicubicSample(sampler2D texture, vec2 texcoord, vec2 texscale) {
|
||||
mix(sample1, sample0, sx), sy);
|
||||
}
|
||||
|
||||
vec3 sampleLight(vec2 coord, vec2 scale) {
|
||||
//soften super bright lights a little
|
||||
const float threshold = 1.0;
|
||||
vec3 rgb = bicubicSample(lightMap, coord, scale).rgb;
|
||||
vec3 lower = min(rgb, threshold);
|
||||
vec3 upper = max(rgb, threshold) - threshold;
|
||||
return lower + (upper / (vec3(1.) + upper));
|
||||
}
|
||||
|
||||
void main() {
|
||||
vec4 texColor;
|
||||
if (fragmentTextureIndex > 2.9) {
|
||||
@ -72,6 +81,6 @@ void main() {
|
||||
if (texColor.a == 0.99607843137)
|
||||
finalColor.a = fragmentColor.a;
|
||||
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;
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
-- revert cursor frames if a mod replaced cursors.png with a SD version again
|
||||
-- Revert cursor frames if a mod replaced cursors.png with a SD version again
|
||||
if assets.image("/cursors/cursors.png"):size()[1] == 64 then
|
||||
local path = "/cursors/opensb/revert.cursor.patch"
|
||||
assets.add(path, '{"scale":null}')
|
||||
@ -8,4 +8,11 @@ if assets.image("/cursors/cursors.png"):size()[1] == 64 then
|
||||
path = "/cursors/opensb/revert.frames.patch"
|
||||
assets.add(path, '{"frameGrid":{"size":[16,16]}}')
|
||||
assets.patch("/cursors/cursors.frames", path)
|
||||
end
|
||||
end
|
||||
|
||||
-- Add object patches
|
||||
--local objects = assets.byExtension("object")
|
||||
--local path = "/objects/opensb/object.patch.lua"
|
||||
--for i = 1, #objects do
|
||||
-- assets.patch(objects[i], path)
|
||||
--end
|
BIN
assets/opensb/tiles/shadows.png
Normal file
BIN
assets/opensb/tiles/shadows.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 412 B |
@ -32,6 +32,13 @@ set(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/../cmake)
|
||||
set(CMAKE_CONFIGURATION_TYPES Debug RelWithAsserts RelWithDebInfo Release)
|
||||
set(CMAKE_EXPORT_COMPILE_COMMANDS ON CACHE INTERNAL "")
|
||||
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
|
||||
# allow
|
||||
# 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
|
||||
// set here.
|
||||
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;
|
||||
|
||||
// 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) {
|
||||
texture = createGlTexture(Image(), TextureAddressing::Clamp, TextureFiltering::Nearest);
|
||||
texture = createGlTexture(ImageView(), TextureAddressing::Clamp, TextureFiltering::Nearest);
|
||||
glBindTexture(GL_TEXTURE_2D, texture->glTextureId());
|
||||
|
||||
Vec2U size = jsonToVec2U(config.getArray("size", { 256, 256 }));
|
||||
@ -147,13 +147,15 @@ void OpenGl20Renderer::loadConfig(Json const& config) {
|
||||
|
||||
for (auto& pair : config.getObject("frameBuffers", {}))
|
||||
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) {
|
||||
if (m_effects.contains(name)) {
|
||||
Logger::warn("OpenGL effect {} already exists", name);
|
||||
switchEffectConfig(name);
|
||||
return;
|
||||
if (auto effect = m_effects.ptr(name)) {
|
||||
Logger::info("Reloading OpenGL effect {}", name);
|
||||
glDeleteProgram(effect->program);
|
||||
m_effects.erase(name);
|
||||
}
|
||||
|
||||
GLint status = 0;
|
||||
@ -177,8 +179,18 @@ void OpenGl20Renderer::loadEffectConfig(String const& name, Json const& effectCo
|
||||
return shader;
|
||||
};
|
||||
|
||||
GLuint vertexShader = compileShader(GL_VERTEX_SHADER, "vertex");
|
||||
GLuint fragmentShader = compileShader(GL_FRAGMENT_SHADER, "fragment");
|
||||
GLuint vertexShader = 0, fragmentShader = 0;
|
||||
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();
|
||||
|
||||
@ -308,7 +320,7 @@ void OpenGl20Renderer::setEffectParameter(String const& parameterName, RenderEff
|
||||
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);
|
||||
if (!ptr)
|
||||
return;
|
||||
@ -319,8 +331,8 @@ void OpenGl20Renderer::setEffectTexture(String const& textureName, Image const&
|
||||
ptr->textureValue = createGlTexture(image, ptr->textureAddressing, ptr->textureFiltering);
|
||||
} else {
|
||||
glBindTexture(GL_TEXTURE_2D, ptr->textureValue->textureId);
|
||||
ptr->textureValue->textureSize = image.size();
|
||||
uploadTextureImage(image.pixelFormat(), image.size(), image.data());
|
||||
ptr->textureValue->textureSize = image.size;
|
||||
uploadTextureImage(image.format, image.size, image.data);
|
||||
}
|
||||
|
||||
if (ptr->textureSizeUniform != -1) {
|
||||
@ -789,7 +801,9 @@ bool OpenGl20Renderer::logGlErrorSummary(String prefix) {
|
||||
void OpenGl20Renderer::uploadTextureImage(PixelFormat pixelFormat, Vec2U size, uint8_t const* data) {
|
||||
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
|
||||
|
||||
Maybe<GLenum> internalFormat;
|
||||
GLenum format;
|
||||
GLenum type = GL_UNSIGNED_BYTE;
|
||||
if (pixelFormat == PixelFormat::RGB24)
|
||||
format = GL_RGB;
|
||||
else if (pixelFormat == PixelFormat::RGBA32)
|
||||
@ -798,10 +812,19 @@ void OpenGl20Renderer::uploadTextureImage(PixelFormat pixelFormat, Vec2U size, u
|
||||
format = GL_BGR;
|
||||
else if (pixelFormat == PixelFormat::BGRA32)
|
||||
format = GL_BGRA;
|
||||
else
|
||||
throw RendererException("Unsupported texture format in OpenGL20Renderer::uploadTextureImage");
|
||||
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");
|
||||
}
|
||||
|
||||
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() {
|
||||
@ -813,12 +836,12 @@ void OpenGl20Renderer::flushImmediatePrimitives() {
|
||||
renderGlBuffer(*m_immediateRenderBuffer, Mat3F::identity());
|
||||
}
|
||||
|
||||
auto OpenGl20Renderer::createGlTexture(Image const& image, TextureAddressing addressing, TextureFiltering filtering)
|
||||
-> RefPtr<GlLoneTexture> {
|
||||
auto OpenGl20Renderer::createGlTexture(ImageView const& image, TextureAddressing addressing, TextureFiltering filtering)
|
||||
->RefPtr<GlLoneTexture> {
|
||||
auto glLoneTexture = make_ref<GlLoneTexture>();
|
||||
glLoneTexture->textureFiltering = filtering;
|
||||
glLoneTexture->textureAddressing = addressing;
|
||||
glLoneTexture->textureSize = image.size();
|
||||
glLoneTexture->textureSize = image.size;
|
||||
|
||||
glGenTextures(1, &glLoneTexture->textureId);
|
||||
if (glLoneTexture->textureId == 0)
|
||||
@ -844,7 +867,7 @@ auto OpenGl20Renderer::createGlTexture(Image const& image, TextureAddressing add
|
||||
|
||||
|
||||
if (!image.empty())
|
||||
uploadTextureImage(image.pixelFormat(), image.size(), image.data());
|
||||
uploadTextureImage(image.format, image.size, image.data);
|
||||
|
||||
return glLoneTexture;
|
||||
}
|
||||
|
@ -25,7 +25,7 @@ public:
|
||||
void loadEffectConfig(String const& name, Json const& effectConfig, StringMap<String> const& shaders) 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;
|
||||
|
||||
@ -188,7 +188,8 @@ private:
|
||||
static bool logGlErrorSummary(String prefix);
|
||||
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();
|
||||
|
||||
|
@ -25,6 +25,7 @@ SET (star_base_HEADERS
|
||||
SET (star_base_SOURCES
|
||||
StarAnimatedPartSet.cpp
|
||||
StarAssets.cpp
|
||||
StarCellularLightArray.cpp
|
||||
StarCellularLighting.cpp
|
||||
StarConfiguration.cpp
|
||||
StarDirectoryAssetSource.cpp
|
||||
|
@ -110,8 +110,15 @@ Assets::Assets(Settings settings, StringList assetSources) {
|
||||
m_assetSources = std::move(assetSources);
|
||||
|
||||
auto luaEngine = LuaEngine::create();
|
||||
m_luaEngine = luaEngine;
|
||||
auto pushGlobalContext = [&luaEngine](String const& name, LuaCallbacks & callbacks) {
|
||||
auto table = luaEngine->createTable();
|
||||
for (auto const& p : callbacks.callbacks())
|
||||
table.set(p.first, luaEngine->createWrappedFunction(p.second));
|
||||
luaEngine->setGlobal(name, table);
|
||||
};
|
||||
pushGlobalContext("sb", LuaBindings::makeUtilityCallbacks());
|
||||
auto decorateLuaContext = [this](LuaContext& context, MemoryAssetSourcePtr newFiles) {
|
||||
context.setCallbacks("sb", LuaBindings::makeUtilityCallbacks());
|
||||
LuaCallbacks callbacks;
|
||||
callbacks.registerCallbackWithSignature<StringSet, String>("byExtension", bind(&Assets::scanExtension, this, _1));
|
||||
callbacks.registerCallbackWithSignature<Json, String>("json", bind(&Assets::json, this, _1));
|
||||
@ -133,38 +140,40 @@ Assets::Assets(Settings settings, StringList assetSources) {
|
||||
return b ? scan(a.value(), *b) : scan(a.value());
|
||||
});
|
||||
|
||||
callbacks.registerCallback("add", [this, &newFiles](LuaEngine& engine, String const& path, LuaValue const& data) {
|
||||
ByteArray bytes;
|
||||
if (auto str = engine.luaMaybeTo<String>(data))
|
||||
bytes = ByteArray(str->utf8Ptr(), str->utf8Size());
|
||||
else {
|
||||
auto json = engine.luaTo<Json>(data).repr();
|
||||
bytes = ByteArray(json.utf8Ptr(), json.utf8Size());
|
||||
}
|
||||
newFiles->set(path, bytes);
|
||||
});
|
||||
if (newFiles) {
|
||||
callbacks.registerCallback("add", [this, &newFiles](LuaEngine& engine, String const& path, LuaValue const& data) {
|
||||
ByteArray bytes;
|
||||
if (auto str = engine.luaMaybeTo<String>(data))
|
||||
bytes = ByteArray(str->utf8Ptr(), str->utf8Size());
|
||||
else {
|
||||
auto json = engine.luaTo<Json>(data).repr();
|
||||
bytes = ByteArray(json.utf8Ptr(), json.utf8Size());
|
||||
}
|
||||
newFiles->set(path, bytes);
|
||||
});
|
||||
|
||||
callbacks.registerCallback("patch", [this, &newFiles](String const& path, String const& patchPath) -> bool {
|
||||
if (auto file = m_files.ptr(path)) {
|
||||
if (newFiles->contains(patchPath)) {
|
||||
file->patchSources.append(make_pair(patchPath, newFiles));
|
||||
return true;
|
||||
} else {
|
||||
if (auto asset = m_files.ptr(patchPath)) {
|
||||
file->patchSources.append(make_pair(patchPath, asset->source));
|
||||
callbacks.registerCallback("patch", [this, &newFiles](String const& path, String const& patchPath) -> bool {
|
||||
if (auto file = m_files.ptr(path)) {
|
||||
if (newFiles->contains(patchPath)) {
|
||||
file->patchSources.append(make_pair(patchPath, newFiles));
|
||||
return true;
|
||||
} else {
|
||||
if (auto asset = m_files.ptr(patchPath)) {
|
||||
file->patchSources.append(make_pair(patchPath, asset->source));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
});
|
||||
return false;
|
||||
});
|
||||
|
||||
callbacks.registerCallback("erase", [this](String const& path) -> bool {
|
||||
bool erased = m_files.erase(path);
|
||||
if (erased)
|
||||
m_filesByExtension[AssetPath::extension(path).toLower()].erase(path);
|
||||
return erased;
|
||||
});
|
||||
callbacks.registerCallback("erase", [this](String const& path) -> bool {
|
||||
bool erased = m_files.erase(path);
|
||||
if (erased)
|
||||
m_filesByExtension[AssetPath::extension(path).toLower()].erase(path);
|
||||
return erased;
|
||||
});
|
||||
}
|
||||
|
||||
context.setCallbacks("assets", callbacks);
|
||||
};
|
||||
@ -883,21 +892,35 @@ Json Assets::readJson(String const& path) const {
|
||||
try {
|
||||
Json result = inputUtf8Json(streamData.begin(), streamData.end(), false);
|
||||
for (auto const& pair : m_files.get(path).patchSources) {
|
||||
auto patchStream = pair.second->read(pair.first);
|
||||
auto patchJson = inputUtf8Json(patchStream.begin(), patchStream.end(), false);
|
||||
if (patchJson.isType(Json::Type::Array)) {
|
||||
auto patchData = patchJson.toArray();
|
||||
try {
|
||||
result = checkPatchArray(pair.first, pair.second, result, patchData, {});
|
||||
} catch (JsonPatchTestFail const& e) {
|
||||
Logger::debug("Patch test failure from file {} in source: '{}' at '{}'. Caused by: {}", pair.first, pair.second->metadata().value("name", ""), m_assetSourcePaths.getLeft(pair.second), e.what());
|
||||
} catch (JsonPatchException const& e) {
|
||||
Logger::error("Could not apply patch from file {} in source: '{}' at '{}'. Caused by: {}", pair.first, pair.second->metadata().value("name", ""), m_assetSourcePaths.getLeft(pair.second), e.what());
|
||||
auto& patchPath = pair.first;
|
||||
auto& patchSource = pair.second;
|
||||
auto patchStream = patchSource->read(patchPath);
|
||||
if (pair.first.endsWith(".lua")) {
|
||||
MutexLocker luaLocker(m_luaMutex);
|
||||
// Kae: i don't like that lock. perhaps have a LuaEngine and patch context cache per worker thread later on?
|
||||
LuaContextPtr& context = m_patchContexts[patchPath];
|
||||
if (!context) {
|
||||
context = make_shared<LuaContext>(as<LuaEngine>(m_luaEngine.get())->createContext());
|
||||
context->load(patchStream, patchPath);
|
||||
}
|
||||
auto newResult = context->invokePath<Json>("patch", result, path);
|
||||
if (newResult)
|
||||
result = std::move(newResult);
|
||||
} else {
|
||||
auto patchJson = inputUtf8Json(patchStream.begin(), patchStream.end(), false);
|
||||
if (patchJson.isType(Json::Type::Array)) {
|
||||
auto patchData = patchJson.toArray();
|
||||
try {
|
||||
result = checkPatchArray(patchPath, patchSource, result, patchData, {});
|
||||
} catch (JsonPatchTestFail const& e) {
|
||||
Logger::debug("Patch test failure from file {} in source: '{}' at '{}'. Caused by: {}", patchPath, patchSource->metadata().value("name", ""), m_assetSourcePaths.getLeft(patchSource), e.what());
|
||||
} catch (JsonPatchException const& e) {
|
||||
Logger::error("Could not apply patch from file {} in source: '{}' at '{}'. Caused by: {}", patchPath, patchSource->metadata().value("name", ""), m_assetSourcePaths.getLeft(patchSource), e.what());
|
||||
}
|
||||
} else if (patchJson.isType(Json::Type::Object)) {//Kae: Do a good ol' json merge instead if the .patch file is a Json object
|
||||
result = jsonMergeNulling(result, patchJson.toObject());
|
||||
}
|
||||
}
|
||||
} else if (patchJson.isType(Json::Type::Object)) { //Kae: Do a good ol' json merge instead if the .patch file is a Json object
|
||||
auto patchData = patchJson.toObject();
|
||||
result = jsonMergeNulling(result, patchData);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
} catch (std::exception const& e) {
|
||||
|
@ -7,6 +7,7 @@
|
||||
#include "StarThread.hpp"
|
||||
#include "StarAssetSource.hpp"
|
||||
#include "StarAssetPath.hpp"
|
||||
#include "StarRefPtr.hpp"
|
||||
|
||||
namespace Star {
|
||||
|
||||
@ -16,6 +17,8 @@ STAR_CLASS(Image);
|
||||
STAR_STRUCT(FramesSpecification);
|
||||
STAR_CLASS(Assets);
|
||||
|
||||
STAR_CLASS(LuaContext);
|
||||
|
||||
STAR_EXCEPTION(AssetException, StarException);
|
||||
|
||||
// The contents of an assets .frames file, which can be associated with one or
|
||||
@ -313,6 +316,11 @@ private:
|
||||
mutable StringMap<String> m_bestFramesFiles;
|
||||
mutable StringMap<FramesSpecificationConstPtr> m_framesSpecifications;
|
||||
|
||||
// Lua
|
||||
RefPtr<RefCounter> m_luaEngine; // dumb but to avoid including Lua.hpp in here...
|
||||
mutable StringMap<LuaContextPtr> m_patchContexts;
|
||||
mutable Mutex m_luaMutex;
|
||||
|
||||
// Paths of all used asset sources, in load order.
|
||||
StringList m_assetSources;
|
||||
|
||||
|
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 subtract(float value, float drop);
|
||||
static float multiply(float v1, float v2);
|
||||
|
||||
static float maxIntensity(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 subtract(Vec3F value, float drop);
|
||||
static Vec3F multiply(Vec3F value, float drop);
|
||||
|
||||
static float maxIntensity(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);
|
||||
}
|
||||
|
||||
inline float ScalarLightTraits::multiply(float v1, float v2) {
|
||||
return v1 * v2;
|
||||
}
|
||||
|
||||
inline float ScalarLightTraits::maxIntensity(float value) {
|
||||
return value;
|
||||
}
|
||||
@ -179,6 +185,10 @@ inline Vec3F ColoredLightTraits::subtract(Vec3F c, float drop) {
|
||||
return c;
|
||||
}
|
||||
|
||||
inline Vec3F ColoredLightTraits::multiply(Vec3F c, float drop) {
|
||||
return c * drop;
|
||||
}
|
||||
|
||||
inline float ColoredLightTraits::maxIntensity(Vec3F const& value) {
|
||||
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>
|
||||
float CellularLightArray<LightTraits>::lineAttenuation(Vec2F const& start, Vec2F const& end,
|
||||
float perObstacleAttenuation, float maxAttenuation) {
|
||||
|
@ -2,6 +2,45 @@
|
||||
|
||||
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)
|
||||
: 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 {
|
||||
Vec2S arrayMin = Vec2S(m_queryRegion.min() - m_calculationRegion.min());
|
||||
Vec2S arrayMax = Vec2S(m_queryRegion.max() - m_calculationRegion.min());
|
||||
|
@ -11,6 +11,105 @@
|
||||
|
||||
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);
|
||||
void add(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 void Lightmap::add(unsigned x, unsigned y, Vec3F const& v) {
|
||||
if (x >= m_width || y >= m_height) {
|
||||
throw LightmapException(strf("[{}, {}] out of range in Lightmap::add", 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
|
||||
// positional point and cellular light sources, as well as pre-lighting cells
|
||||
// individually.
|
||||
@ -43,6 +142,8 @@ public:
|
||||
// output image. The image will be reset to the size of the region given in
|
||||
// the call to 'begin', and formatted as RGB24.
|
||||
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;
|
||||
private:
|
||||
|
@ -228,35 +228,8 @@ void ClientApplication::applicationInit(ApplicationControllerPtr appController)
|
||||
|
||||
void ClientApplication::renderInit(RendererPtr renderer) {
|
||||
Application::renderInit(renderer);
|
||||
auto assets = m_root->assets();
|
||||
|
||||
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");
|
||||
renderReload();
|
||||
m_root->registerReloadListener(m_reloadListener = make_shared<CallbackListener>([this]() { renderReload(); }));
|
||||
|
||||
if (m_root->configuration()->get("limitTextureAtlasSize").optBool().value(false))
|
||||
renderer->setSizeLimitEnabled(true);
|
||||
@ -427,12 +400,7 @@ void ClientApplication::render() {
|
||||
|
||||
auto paintStart = Time::monotonicMicroseconds();
|
||||
m_worldPainter->render(m_renderData, [&]() -> bool {
|
||||
if (auto newMinPosition = worldClient->waitForLighting(&m_renderData.lightMap)) {
|
||||
m_renderData.lightMinPosition = *newMinPosition;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
return worldClient->waitForLighting(&m_renderData);
|
||||
});
|
||||
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));
|
||||
@ -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) {
|
||||
MainAppState oldState = m_state;
|
||||
m_state = newState;
|
||||
|
@ -53,6 +53,8 @@ private:
|
||||
String password;
|
||||
};
|
||||
|
||||
void renderReload();
|
||||
|
||||
void changeState(MainAppState newState);
|
||||
void setError(String const& error);
|
||||
void setError(String const& error, std::exception const& e);
|
||||
@ -71,6 +73,8 @@ private:
|
||||
|
||||
RootUPtr m_root;
|
||||
ThreadFunction<void> m_rootLoader;
|
||||
CallbackListenerPtr m_reloadListener;
|
||||
|
||||
MainAppState m_state = MainAppState::Startup;
|
||||
|
||||
// Valid after applicationInit is called
|
||||
|
@ -518,4 +518,10 @@ void Image::writePng(IODevicePtr device) const {
|
||||
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 {
|
||||
|
||||
enum class PixelFormat {
|
||||
enum class PixelFormat : uint8_t {
|
||||
RGB24,
|
||||
RGBA32,
|
||||
BGR24,
|
||||
BGRA32
|
||||
BGRA32,
|
||||
RGB_F,
|
||||
RGBA_F
|
||||
};
|
||||
|
||||
uint8_t bitsPerPixel(PixelFormat pf);
|
||||
@ -148,8 +150,12 @@ inline uint8_t bitsPerPixel(PixelFormat pf) {
|
||||
return 32;
|
||||
case PixelFormat::BGR24:
|
||||
return 24;
|
||||
default:
|
||||
case PixelFormat::BGRA32:
|
||||
return 32;
|
||||
case PixelFormat::RGB_F:
|
||||
return 96;
|
||||
default:
|
||||
return 128;
|
||||
}
|
||||
}
|
||||
|
||||
@ -161,8 +167,12 @@ inline uint8_t bytesPerPixel(PixelFormat pf) {
|
||||
return 4;
|
||||
case PixelFormat::BGR24:
|
||||
return 3;
|
||||
default:
|
||||
case PixelFormat::BGRA32:
|
||||
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,7 +214,6 @@ Color jsonToColor(Json const& v) {
|
||||
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");
|
||||
Color c = Color::rgba(0, 0, 0, 255);
|
||||
|
||||
c.setRed(v.getInt(0));
|
||||
c.setGreen(v.getInt(1));
|
||||
c.setBlue(v.getInt(2));
|
||||
@ -235,9 +234,8 @@ Json jsonFromColor(Color const& color) {
|
||||
result.push_back(color.red());
|
||||
result.push_back(color.green());
|
||||
result.push_back(color.blue());
|
||||
if (color.alpha() != 255) {
|
||||
if (color.alpha() < 255)
|
||||
result.push_back(color.alpha());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -321,7 +321,7 @@ void ItemDrop::render(RenderCallback* renderCallback) {
|
||||
void ItemDrop::renderLightSources(RenderCallback* renderCallback) {
|
||||
LightSource light;
|
||||
light.pointLight = false;
|
||||
light.color = Vec3B::filled(20);
|
||||
light.color = Vec3F::filled(20.f / 255.f);
|
||||
light.position = position();
|
||||
renderCallback->addLightSource(std::move(light));
|
||||
}
|
||||
|
@ -7,7 +7,7 @@ namespace Star {
|
||||
|
||||
struct LightSource {
|
||||
Vec2F position;
|
||||
Vec3B color;
|
||||
Vec3F color;
|
||||
|
||||
bool pointLight;
|
||||
// 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{
|
||||
position + translate,
|
||||
color.toRgb(),
|
||||
color.toRgbF(),
|
||||
pair.second.pointLight,
|
||||
pair.second.pointBeam,
|
||||
pointAngle,
|
||||
|
@ -1014,8 +1014,8 @@ bool Npc::isAdmin() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
Vec4B Npc::favoriteColor() const {
|
||||
return Color::White.toRgba();
|
||||
Color Npc::favoriteColor() const {
|
||||
return Color::White;
|
||||
}
|
||||
|
||||
float Npc::beamGunRadius() const {
|
||||
|
@ -143,7 +143,7 @@ public:
|
||||
Direction facingDirection() const override;
|
||||
Direction walkingDirection() const override;
|
||||
bool isAdmin() const override;
|
||||
Vec4B favoriteColor() const override;
|
||||
Color favoriteColor() const override;
|
||||
float beamGunRadius() const override;
|
||||
void addParticles(List<Particle> const& particles) 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.position = position() + centerOfTile(orientation->lightPosition);
|
||||
lightSource.color = color.toRgb();
|
||||
lightSource.color = color.toRgbF();
|
||||
lightSource.pointLight = m_config->pointLight;
|
||||
lightSource.pointBeam = m_config->pointBeam;
|
||||
lightSource.beamAngle = orientation->beamAngle;
|
||||
|
@ -103,11 +103,11 @@ List<Particle> const& ParticleManager::particles() const {
|
||||
return m_particles;
|
||||
}
|
||||
|
||||
List<pair<Vec2F, Vec3B>> ParticleManager::lightSources() const {
|
||||
List<pair<Vec2F, Vec3B>> lsources;
|
||||
List<pair<Vec2F, Vec3F>> ParticleManager::lightSources() const {
|
||||
List<pair<Vec2F, Vec3F>> lsources;
|
||||
for (auto const& particle : m_particles) {
|
||||
if (particle.light != Color::Clear)
|
||||
lsources.append({particle.position, particle.light.toRgb()});
|
||||
lsources.append({particle.position, particle.light.toRgbF()});
|
||||
}
|
||||
return lsources;
|
||||
}
|
||||
|
@ -24,7 +24,7 @@ public:
|
||||
void update(float dt, RectF const& cullRegion, float wind);
|
||||
|
||||
List<Particle> const& particles() const;
|
||||
List<pair<Vec2F, Vec3B>> lightSources() const;
|
||||
List<pair<Vec2F, Vec3F>> lightSources() const;
|
||||
|
||||
private:
|
||||
enum class TileType { Colliding, Water, Empty };
|
||||
|
@ -1912,13 +1912,13 @@ bool Player::isAdmin() const {
|
||||
return m_isAdmin;
|
||||
}
|
||||
|
||||
void Player::setFavoriteColor(Vec4B color) {
|
||||
m_identity.color = color;
|
||||
void Player::setFavoriteColor(Color color) {
|
||||
m_identity.color = color.toRgba();
|
||||
updateIdentity();
|
||||
}
|
||||
|
||||
Vec4B Player::favoriteColor() const {
|
||||
return m_identity.color;
|
||||
Color Player::favoriteColor() const {
|
||||
return Color::rgba(m_identity.color);
|
||||
}
|
||||
|
||||
bool Player::isTeleporting() const {
|
||||
|
@ -354,8 +354,8 @@ public:
|
||||
bool isDead() const;
|
||||
void kill();
|
||||
|
||||
void setFavoriteColor(Vec4B color);
|
||||
Vec4B favoriteColor() const override;
|
||||
void setFavoriteColor(Color color);
|
||||
Color favoriteColor() const override;
|
||||
|
||||
// Starts the teleport animation sequence, locking player movement and
|
||||
// preventing some update code
|
||||
|
@ -379,7 +379,7 @@ void Projectile::renderLightSources(RenderCallback* renderCallback) {
|
||||
if (renderable.is<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) {
|
||||
@ -824,7 +824,7 @@ void Projectile::processAction(Json const& action) {
|
||||
|
||||
m_pendingRenderables.append(LightSource{
|
||||
position(),
|
||||
jsonToColor(parameters.get("color")).toRgb(),
|
||||
jsonToColor(parameters.get("color")).toRgbF(),
|
||||
parameters.getBool("pointLight", true),
|
||||
0.0f,
|
||||
0.0f,
|
||||
|
@ -116,7 +116,7 @@ ProjectileConfigPtr ProjectileDatabase::readConfig(String const& path) {
|
||||
projectileConfig->fullbright = config.getBool("fullbright", false);
|
||||
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->pointLight = config.getBool("pointLight", false);
|
||||
|
||||
|
@ -66,7 +66,7 @@ struct ProjectileConfig {
|
||||
bool fullbright = false;
|
||||
EntityRenderLayer renderLayer;
|
||||
|
||||
Vec3B lightColor;
|
||||
Color lightColor;
|
||||
Vec2F lightPosition;
|
||||
bool pointLight = false;
|
||||
|
||||
|
@ -165,7 +165,7 @@ Maybe<float> ToolUser::toolRadius() const {
|
||||
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)
|
||||
return {};
|
||||
|
||||
@ -178,21 +178,16 @@ List<Drawable> ToolUser::renderObjectPreviews(Vec2F aimPosition, Direction walki
|
||||
|
||||
Color opacityMask = Color::White;
|
||||
opacityMask.setAlphaF(item->getAppropriateOpacity());
|
||||
Vec4B favoriteColorTrans;
|
||||
if (inToolRange && objectDatabase->canPlaceObject(m_user->world(), aimPos, item->objectName())) {
|
||||
favoriteColorTrans = favoriteColor;
|
||||
} else {
|
||||
Color color = Color::rgba(favoriteColor);
|
||||
color.setHue(color.hue() + 120);
|
||||
favoriteColorTrans = color.toRgba();
|
||||
}
|
||||
Color favoriteColorTrans = favoriteColor;
|
||||
if (!inToolRange || !objectDatabase->canPlaceObject(m_user->world(), aimPos, item->objectName()))
|
||||
favoriteColorTrans.setHue(favoriteColor.hue() + 120);
|
||||
|
||||
favoriteColorTrans[3] = m_objectPreviewOuterAlpha * 255;
|
||||
Color nearWhite = Color::rgba(favoriteColorTrans);
|
||||
favoriteColorTrans.setAlphaF(m_objectPreviewOuterAlpha);
|
||||
Color nearWhite = favoriteColorTrans;
|
||||
nearWhite.setValue(1 - (1 - nearWhite.value()) / 5);
|
||||
nearWhite.setSaturation(nearWhite.saturation() / 5);
|
||||
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) {
|
||||
if (drawable.isImage())
|
||||
|
@ -43,7 +43,7 @@ public:
|
||||
// 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
|
||||
// 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
|
||||
Maybe<Direction> setupHumanoidHandItems(Humanoid& humanoid, Vec2F position, Vec2F aimPosition) const;
|
||||
void setupHumanoidHandItemDrawables(Humanoid& humanoid) const;
|
||||
|
@ -1375,21 +1375,23 @@ void WorldClient::collectLiquid(List<Vec2I> const& tilePositions, LiquidId liqui
|
||||
m_outgoingPackets.append(make_shared<CollectLiquidPacket>(tilePositions, liquidId));
|
||||
}
|
||||
|
||||
Maybe<Vec2I> WorldClient::waitForLighting(Image* out) {
|
||||
bool WorldClient::waitForLighting(WorldRenderData* renderData) {
|
||||
MutexLocker prepLocker(m_lightMapPrepMutex);
|
||||
MutexLocker lightMapLocker(m_lightMapMutex);
|
||||
if (out && !m_lightMap.empty()) {
|
||||
if (renderData && !m_lightMap.empty()) {
|
||||
for (auto& previewTile : m_previewTiles) {
|
||||
if (previewTile.updateLight) {
|
||||
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())
|
||||
m_lightMap.set(Vec2U(lightArrayPos), previewTile.light);
|
||||
if (lightArrayPos[0] >= 0 && lightArrayPos[0] < (int)m_lightMap.width()
|
||||
&& lightArrayPos[1] >= 0 && lightArrayPos[1] < (int)m_lightMap.height())
|
||||
m_lightMap.set(lightArrayPos[0], lightArrayPos[1], Color::v3bToFloat(previewTile.light));
|
||||
}
|
||||
}
|
||||
*out = std::move(m_lightMap);
|
||||
return m_lightMinPosition;
|
||||
renderData->lightMap = std::move(m_lightMap);
|
||||
renderData->lightMinPosition = m_lightMinPosition;
|
||||
return true;
|
||||
}
|
||||
return {};
|
||||
return false;
|
||||
}
|
||||
|
||||
WorldClient::BroadcastCallback& WorldClient::broadcastCallback() {
|
||||
@ -1636,25 +1638,30 @@ void WorldClient::lightingTileGather() {
|
||||
|
||||
void WorldClient::lightingCalc() {
|
||||
MutexLocker prepLocker(m_lightMapPrepMutex);
|
||||
|
||||
RectI lightRange = m_pendingLightRange;
|
||||
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.begin(lightRange);
|
||||
lightingTileGather();
|
||||
|
||||
prepLocker.unlock();
|
||||
|
||||
|
||||
|
||||
for (auto const& light : lights) {
|
||||
Vec2F position = m_geometry.nearestTo(Vec2F(m_lightingCalculator.calculationRegion().min()), light.position);
|
||||
if (light.pointLight)
|
||||
m_lightingCalculator.addPointLight(position, Color::v3bToFloat(light.color), light.pointBeam, light.beamAngle, light.beamAmbience);
|
||||
else
|
||||
m_lightingCalculator.addSpreadLight(position, Color::v3bToFloat(light.color));
|
||||
m_lightingCalculator.addPointLight(position, light.color, light.pointBeam, light.beamAngle, light.beamAmbience);
|
||||
else {
|
||||
m_lightingCalculator.addSpreadLight(position, light.color);
|
||||
}
|
||||
}
|
||||
|
||||
for (auto const& lightPair : particleLights) {
|
||||
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);
|
||||
|
@ -170,7 +170,7 @@ public:
|
||||
|
||||
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;
|
||||
BroadcastCallback& broadcastCallback();
|
||||
@ -278,10 +278,10 @@ private:
|
||||
Mutex m_lightMapPrepMutex;
|
||||
Mutex m_lightMapMutex;
|
||||
|
||||
Image m_pendingLightMap;
|
||||
Image m_lightMap;
|
||||
Lightmap m_pendingLightMap;
|
||||
Lightmap m_lightMap;
|
||||
List<LightSource> m_pendingLights;
|
||||
List<std::pair<Vec2F, Vec3B>> m_pendingParticleLights;
|
||||
List<std::pair<Vec2F, Vec3F>> m_pendingParticleLights;
|
||||
RectI m_pendingLightRange;
|
||||
Vec2I m_lightMinPosition;
|
||||
List<PreviewTile> m_previewTiles;
|
||||
|
@ -446,9 +446,9 @@ namespace WorldImpl {
|
||||
for (auto const& light : entity->lightSources()) {
|
||||
Vec2F position = worldGeometry.nearestTo(Vec2F(lighting.calculationRegion().min()), light.position);
|
||||
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
|
||||
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 "StarEntity.hpp"
|
||||
#include "StarThread.hpp"
|
||||
#include "StarCellularLighting.hpp"
|
||||
|
||||
namespace Star {
|
||||
|
||||
@ -17,6 +18,7 @@ struct EntityDrawables {
|
||||
Map<EntityRenderLayer, List<Drawable>> layers;
|
||||
};
|
||||
|
||||
|
||||
struct WorldRenderData {
|
||||
void clear();
|
||||
|
||||
@ -25,7 +27,7 @@ struct WorldRenderData {
|
||||
Vec2I tileMinPosition;
|
||||
RenderTileArray tiles;
|
||||
Vec2I lightMinPosition;
|
||||
Image lightMap;
|
||||
Lightmap lightMap;
|
||||
|
||||
List<EntityDrawables> entityDrawables;
|
||||
List<Particle> const* particles;
|
||||
|
@ -38,7 +38,7 @@ BeamItem::BeamItem(Json config) {
|
||||
m_innerBrightnessScale = config.get("innerBrightnessScale").toFloat();
|
||||
m_firstStripeThickness = config.get("firstStripeThickness").toFloat();
|
||||
m_secondStripeThickness = config.get("secondStripeThickness").toFloat();
|
||||
m_color = {255, 255, 255, 255};
|
||||
m_color = Color::White;
|
||||
m_particleGenerateCooldown = .25;
|
||||
m_inRangeLastUpdate = false;
|
||||
}
|
||||
@ -160,12 +160,9 @@ List<Drawable> BeamItem::beamDrawables(bool canPlace) const {
|
||||
if ((endPoint - owner()->position()).magnitude() <= m_range && curveLen <= m_range) {
|
||||
m_inRangeLastUpdate = true;
|
||||
int numLines = projectOntoRange(m_minBeamLines, m_maxBeamLines);
|
||||
Vec4B mainColor = m_color;
|
||||
if (!canPlace) {
|
||||
Color temp = Color::rgba(m_color);
|
||||
temp.setHue(temp.hue() + 120);
|
||||
mainColor = temp.toRgba();
|
||||
}
|
||||
Color mainColor = m_color;
|
||||
if (!canPlace)
|
||||
mainColor.setHue(mainColor.hue() + 120);
|
||||
m_lastUpdateColor = mainColor;
|
||||
|
||||
String endImage = "";
|
||||
@ -189,9 +186,9 @@ List<Drawable> BeamItem::beamDrawables(bool canPlace) const {
|
||||
for (auto line = 0; line < numLines; line++) {
|
||||
float lineThickness = rangeRand(m_beamWidthDev, m_minBeamWidth, m_maxBeamWidth);
|
||||
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.
|
||||
Color innerStripe = Color::rgba(mainColor);
|
||||
Color innerStripe = mainColor;
|
||||
innerStripe.setValue(1 - (1 - innerStripe.value()) / m_innerBrightnessScale);
|
||||
innerStripe.setSaturation(innerStripe.saturation() / m_innerBrightnessScale);
|
||||
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),
|
||||
rangeRand(m_beamJitterDev, -m_maxBeamJitter, m_maxBeamJitter));
|
||||
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),
|
||||
lineThickness * m_firstStripeThickness,
|
||||
Color::rgba(firstStripe),
|
||||
@ -218,7 +215,7 @@ List<Drawable> BeamItem::beamDrawables(bool canPlace) const {
|
||||
previousLoc = std::move(currentLoc);
|
||||
}
|
||||
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()),
|
||||
lineThickness * m_firstStripeThickness,
|
||||
Color::rgba(firstStripe),
|
||||
@ -243,7 +240,7 @@ List<Drawable> BeamItem::beamDrawables(bool canPlace) const {
|
||||
beamParticle.position = m_beamCurve.pointAt(curveLoc);
|
||||
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.setSaturation(randomColor.saturation() / Random::randf(1, 4));
|
||||
|
||||
|
@ -63,10 +63,10 @@ protected:
|
||||
float m_innerBrightnessScale;
|
||||
float m_firstStripeThickness;
|
||||
float m_secondStripeThickness;
|
||||
Vec4B m_color;
|
||||
Color m_color;
|
||||
|
||||
mutable bool m_inRangeLastUpdate;
|
||||
mutable Vec4B m_lastUpdateColor;
|
||||
mutable Color m_lastUpdateColor;
|
||||
mutable float m_particleGenerateCooldown;
|
||||
|
||||
CSplineF m_beamCurve;
|
||||
|
@ -26,7 +26,7 @@ public:
|
||||
virtual Vec2F aimPosition() const = 0;
|
||||
|
||||
virtual bool isAdmin() const = 0;
|
||||
virtual Vec4B favoriteColor() const = 0;
|
||||
virtual Color favoriteColor() const = 0;
|
||||
virtual String species() const = 0;
|
||||
|
||||
virtual void requestEmote(String const& emote) = 0;
|
||||
|
@ -56,7 +56,7 @@ List<LightSource> InspectionTool::lightSources() const {
|
||||
LightSource lightSource;
|
||||
lightSource.pointLight = true;
|
||||
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.beamAngle = angle;
|
||||
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) {
|
||||
if (m_collisionOverride != TileCollisionOverride::None) {
|
||||
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);
|
||||
auto addIndicator = [&](String const& path) {
|
||||
Vec2F basePosition = Vec2F(0.5f, 0.5f);
|
||||
@ -336,7 +336,7 @@ TileCollisionOverride& MaterialItem::collisionOverride() {
|
||||
List<PreviewTile> MaterialItem::previewTiles(bool shifting) const {
|
||||
List<PreviewTile> result;
|
||||
if (initialized()) {
|
||||
Color lightColor = Color::rgba(owner()->favoriteColor());
|
||||
Color lightColor = owner()->favoriteColor();
|
||||
Vec3B light = lightColor.toRgb();
|
||||
|
||||
auto material = materialId();
|
||||
|
@ -241,7 +241,7 @@ List<LightSource> Flashlight::lightSources() const {
|
||||
LightSource lightSource;
|
||||
lightSource.pointLight = true;
|
||||
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.beamAngle = angle;
|
||||
lightSource.beamAmbience = m_ambientFactor;
|
||||
@ -367,7 +367,7 @@ List<PreviewTile> BeamMiningTool::previewTiles(bool shifting) const {
|
||||
|
||||
if (ownerp && worldp) {
|
||||
if (ownerp->isAdmin() || ownerp->inToolRange()) {
|
||||
Color lightColor = Color::rgba(ownerp->favoriteColor());
|
||||
Color lightColor = ownerp->favoriteColor();
|
||||
if (!ready())
|
||||
lightColor *= Color::rgbaf(0.75f, 0.75f, 0.75f, 1.0f);
|
||||
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_endType = EndType::Object;
|
||||
|
||||
for (auto color : instanceValue("colorNumbers").toArray())
|
||||
for (auto& color : instanceValue("colorNumbers").toArray())
|
||||
m_colors.append(jsonToColor(color));
|
||||
|
||||
m_colorKeys = jsonToStringList(instanceValue("colorKeys"));
|
||||
|
||||
m_colorIndex = instanceValue("colorIndex", 0).toInt();
|
||||
m_color = m_colors[m_colorIndex].toRgba();
|
||||
m_color = m_colors[m_colorIndex];
|
||||
}
|
||||
|
||||
ItemPtr PaintingBeamTool::clone() const {
|
||||
@ -666,7 +666,7 @@ List<PreviewTile> PaintingBeamTool::previewTiles(bool shifting) const {
|
||||
void PaintingBeamTool::init(ToolUserEntity* owner, ToolHand hand) {
|
||||
FireableItem::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 {
|
||||
@ -681,7 +681,7 @@ void PaintingBeamTool::fire(FireMode mode, bool shifting, bool edgeTriggered) {
|
||||
|
||||
if (mode == FireMode::Alt && edgeTriggered) {
|
||||
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);
|
||||
return;
|
||||
}
|
||||
|
@ -76,7 +76,7 @@ LuaAnimationComponent<Base>::LuaAnimationComponent() {
|
||||
animationCallbacks.registerCallback("addLightSource", [this](LuaTable const& lightSourceTable) {
|
||||
m_lightSources.append({
|
||||
lightSourceTable.get<Vec2F>("position"),
|
||||
lightSourceTable.get<Color>("color").toRgb(),
|
||||
lightSourceTable.get<Color>("color").toRgbF(),
|
||||
lightSourceTable.get<Maybe<bool>>("pointLight").value(),
|
||||
lightSourceTable.get<Maybe<float>>("pointBeam").value(),
|
||||
lightSourceTable.get<Maybe<float>>("beamAngle").value(),
|
||||
|
@ -38,20 +38,19 @@ TilePainter::TilePainter(RendererPtr renderer) : TileDrawer() {
|
||||
void TilePainter::adjustLighting(WorldRenderData& renderData) const {
|
||||
RectI lightRange = RectI::withSize(renderData.lightMinPosition, Vec2I(renderData.lightMap.size()));
|
||||
forEachRenderTile(renderData, lightRange, [&](Vec2I const& pos, RenderTile const& tile) {
|
||||
// Only adjust lighting for full tiles
|
||||
// Only adjust lighting for tiles with liquid above the draw threshold
|
||||
float drawLevel = liquidDrawLevel(byteToFloat(tile.liquidLevel));
|
||||
if (drawLevel == 0.0f)
|
||||
return;
|
||||
|
||||
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];
|
||||
Vec3F tileLight = Vec3F(lightValue);
|
||||
float darknessLevel = (1 - tileLight.sum() / (3.0f * 255.0f)) * drawLevel;
|
||||
lightValue = Vec3B(tileLight.piecewiseMultiply(Vec3F::filled(1 - darknessLevel) + liquid.bottomLightMix * darknessLevel));
|
||||
float darknessLevel = (1.f - (lightValue.sum() / 3.0f)) * drawLevel;
|
||||
lightValue = lightValue.piecewiseMultiply(Vec3F::filled(1.f - darknessLevel) + liquid.bottomLightMix * darknessLevel);
|
||||
|
||||
renderData.lightMap.set(lightIndex, lightValue);
|
||||
renderData.lightMap.set(lightIndex.x(), lightIndex.y(), lightValue);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -83,6 +83,7 @@ void WorldPainter::render(WorldRenderData& renderData, function<bool()> lightWai
|
||||
LogMap::set("client_render_world_async_light_wait", strf(u8"{:05d}\u00b5s", Time::monotonicMicroseconds() - start));
|
||||
}
|
||||
|
||||
m_renderer->setEffectParameter("lightMapEnabled", !renderData.isFullbright);
|
||||
if (renderData.isFullbright) {
|
||||
m_renderer->setEffectTexture("lightMap", Image::filled(Vec2U(1, 1), { 255, 255, 255, 255 }, PixelFormat::RGB24));
|
||||
m_renderer->setEffectParameter("lightMapMultiplier", 1.0f);
|
||||
|
Loading…
Reference in New Issue
Block a user