Open source Star Ruler 2 source code!

This commit is contained in:
Lucas de Vries
2018-07-17 14:15:37 +02:00
commit cc307720ff
4342 changed files with 2365070 additions and 0 deletions
+169
View File
@@ -0,0 +1,169 @@
#include "resource/library.h"
#include "main/console.h"
#include "main/logging.h"
#include "files.h"
#include <set>
#include "render/gl_shader.h"
namespace resource {
threads::Mutex reloadTextureMutex;
std::set<std::string> reloadTextures;
threads::Mutex reloadShaderMutex;
std::set<std::string> reloadShaders;
threads::Mutex reloadSkinMutex;
std::set<std::string> reloadSkins;
void Library::clearWatchedResources() {
clearWatches();
{
threads::Lock lock(reloadTextureMutex);
reloadTextures.clear();
}
{
threads::Lock lock(reloadShaderMutex);
reloadShaders.clear();
}
{
threads::Lock lock(reloadSkinMutex);
reloadSkins.clear();
}
}
void Library::watchTexture(const std::string& filename) {
//info("Watching texture '%s'", filename.c_str());
watchFile(filename, [filename]() -> bool {
threads::Lock lock(reloadTextureMutex);
if(reloadTextures.find(filename) == reloadTextures.end()) {
reloadTextures.insert(filename);
info("Reloading texture '%s'", filename.c_str());
}
return true;
});
}
void Library::watchSkin(const std::string& filename) {
//info("Watching skin '%s'", filename.c_str());
watchFile(filename, [filename]() -> bool {
threads::Lock lock(reloadSkinMutex);
if(reloadSkins.find(filename) == reloadSkins.end()) {
reloadSkins.insert(filename);
info("Reloading skin '%s'", filename.c_str());
}
return true;
});
}
void Library::watchShader(const std::string& shadername, const std::string& filename) {
std::string absFilename = getAbsolutePath(filename);
//info("Watching shader '%s' (%s)", shadername.c_str(), filename.c_str());
watchFile(filename, [absFilename,shadername]() -> bool {
threads::Lock lock(reloadShaderMutex);
if(reloadShaders.find(absFilename) == reloadShaders.end()) {
reloadShaders.insert(shadername);
}
return true;
});
}
void Library::reloadWatchedResources() {
if(!reloadTextures.empty()) {
threads::Lock lock(reloadTextureMutex);
foreach(name, reloadTextures) {
auto tex = texture_files.find(*name);
if(tex == texture_files.end())
continue;
if(tex->second == 0)
continue;
Image* img = loadImage(name->c_str());
if(img) {
tex->second->load(*img, tex->second->hasMipMaps);
delete img;
}
}
reloadTextures.clear();
//Mark all spritesheets as dirty, or they won't properly handle changes in resolution
foreach(sheet,spritesheets)
sheet->second->dirty = true;
}
if(!reloadShaders.empty()) {
threads::Lock lock(reloadShaderMutex);
foreach(name, reloadShaders) {
auto program = programs.find(*name);
if(program != programs.end()) {
if(program->second->compile() != 0)
error("-In Shader Program '%s'", program->first.c_str());
foreach(shader, shaders) {
if(shader->second->program == program->second) {
info("Reloading shader (%s)", shader->first.c_str());
if(shader->second->compile() != 0)
error("-In Shader '%s'", shader->first.c_str());
}
}
}
}
reloadShaders.clear();
}
if(!reloadSkins.empty()) {
threads::Lock lock(reloadShaderMutex);
foreach(name, reloadSkins) {
auto skin = skin_files.find(*name);
if(skin != skin_files.end())
loadSkin(*name, skin->second);
}
reloadSkins.clear();
}
}
void Library::bindHotloading() {
console.addCommand("reload", [this](argList& args) {
if(args.empty()) {
console.printLn("Specify exact name of shader to reload");
return;
}
auto shader = shaders.find(args[0]);
if(shader != shaders.end()) {
int result = shader->second->compile();
if(result == 0)
console.printLn("Successfully recompiled shader");
else
error("Recompilation of '%s' failed", shader->first.c_str());
}
else {
std::string path = getAbsolutePath(args[0]);
auto tex = texture_files.find(path);
if(tex != texture_files.end()) {
if(tex->second) {
Image* img = loadImage(path.c_str());
if(img) {
tex->second->load(*img, tex->second->hasMipMaps);
console.printLn("Texture reloaded");
delete img;
}
}
else {
console.printLn("Texture type cannot be reloaded");
}
}
else {
console.printLn("No Shader or Texture match found");
}
}
}, true );
}
};
+314
View File
@@ -0,0 +1,314 @@
#include "render/render_state.h"
#include "main/references.h"
#include "resource/library.h"
#include "scene/particle_system.h"
#include "ISoundSource.h"
#include "files.h"
#include "str_util.h"
#include "num_util.h"
#include <iostream>
#include <fstream>
#include <string>
namespace resource {
void Library::clear() {
foreach(mat, materials)
delete mat->second;
materials.clear();
material_names.clear();
foreach(mat, matGroups)
delete mat->second;
matGroups.clear();
foreach(sheet, spritesheets)
delete sheet->second;
spritesheets.clear();
spritesheet_names.clear();
foreach(tex, textures)
delete *tex;
textures.clear();
texture_files.clear();
clearTextures();
foreach(mesh, meshes)
if(mesh->second != errors.mesh)
delete mesh->second;
meshes.clear();
foreach(shader, shaders)
delete shader->second;
shaders.clear();
settingsShaders.clear();
foreach(program, programs)
delete program->second;
programs.clear();
foreach(font, font_list)
delete *font;
font_list.clear();
fonts.clear();
foreach(skin, skins)
delete skin->second;
skin_files.clear();
skins.clear();
foreach(sound, sounds)
delete sound->second;
sounds.clear();
clearWatchedResources();
delete errors.texture;
errors.texture = 0;
errors.material.textures[0] = 0;
delete errors.mesh;
errors.mesh = 0;
mat_indices.clear();
}
void Library::prepErrorResources() {
if(!devices.render) {
errors.material.constant = true;
errors.mesh = new ErrorMesh();
return;
}
errors.particleSystem = scene::createDummyParticleSystem();
errors.material.constant = true;
errors.material.lighting = false;
errors.skin.materialName = "~invalid_skin";
errors.mesh = devices.render->createMesh(Mesh());
render::RenderState* img2d = new render::RenderState();
img2d->baseMat = render::MAT_Alpha;
img2d->lighting = false;
img2d->depthTest = render::DT_NoDepthTest;
img2d->depthWrite = false;
img2d->constant = true;
materials["Image2D"] = img2d;
material_names.push_back("Image2D");
mat_indices[img2d] = material_names.size();
}
void Library::generateErrorResources() {
if(!devices.render)
return;
{ //Checkboard error image
Image img(64,64,FMT_RGB);
for(unsigned r = 0; r < 64; ++r)
for(unsigned x = 0; x < 64; ++x)
img.rgb[x+(r*64)] = (x/2 % 2) ^ (r/2 % 2) ? ColorRGB(0,0,0) : ColorRGB(255,0,255);
errors.texture = devices.render->createTexture(img);
errors.material.textures[0] = errors.texture;
}
{ //Tetrahedron error mesh
Mesh mesh;
mesh.vertices.push_back( Vertex(vec3f(0,1,0)) );
mesh.vertices.push_back( Vertex(vec3f(1,0,0)) );
mesh.vertices.push_back( Vertex(vec3f(-1,0,1)) );
mesh.vertices.push_back( Vertex(vec3f(-1,0,-1)) );
mesh.faces.push_back(Mesh::Face(0,1,2));
mesh.faces.push_back(Mesh::Face(0,2,3));
mesh.faces.push_back(Mesh::Face(0,3,1));
mesh.faces.push_back(Mesh::Face(1,2,3));
errors.mesh->resetToMesh(mesh);
}
}
void Library::load(ResourceType type, const std::string& filename) {
switch(type) {
case RT_Sound:
loadSounds(filename);
break;
case RT_Material:
case RT_SpriteSheet:
loadMaterials(filename);
break;
case RT_Mesh:
loadModels(filename);
break;
case RT_Font:
loadFonts(filename);
break;
case RT_Shader:
loadShaders(filename);
break;
case RT_Skin:
loadSkins(filename);
break;
case RT_ParticleSystem:
{
auto* pSys = scene::loadParticleSystem(filename);
if(pSys)
particleSystems[getBasename(filename,false)] = pSys;
}
break;
}
}
void Library::loadDirectory(ResourceType type, const std::string& filename) {
std::vector<std::string> files;
std::string dirname(filename);
listDirectory(dirname, files);
foreach(it, files) {
std::string& file = *it;
if(file.size() < 4)
continue;
if(!file.compare(file.size() - 4, 4, ".txt"))
load(type, path_join(dirname, file));
}
}
Library::ResourceAccessor Library::operator[](const char* name) const {
ResourceAccessor access;
access.source = this;
access.name = name;
return access;
}
#define access_ref(type, func) \
Library::ResourceAccessor::operator const type &() { return source->func(name); }\
Library::ResourceAccessor::operator const type *() { return &source->func(name); }
#define access_ptr(type, func) \
Library::ResourceAccessor::operator const type &() { return *source->func(name); }\
Library::ResourceAccessor::operator const type *() { return source->func(name); }
access_ref(render::RenderMesh, getMesh);
access_ref(render::RenderState, getMaterial);
access_ref(render::Font, getFont);
access_ref(render::SpriteSheet, getSpriteSheet);
access_ref(gui::skin::Skin, getSkin);
access_ptr(Sound, getSound);
access_ptr(render::Shader, getShader);
const render::RenderState& Library::getErrorMaterial() const {
return errors.material;
}
const render::Texture* Library::getErrorTexture() const {
return errors.texture;
}
const render::SpriteSheet& Library::getErrorSpriteSheet() const {
return errors.spriteSheet;
}
const render::RenderState& Library::getMaterial(const std::string& name) const {
auto mat = materials.find(name);
if(mat == materials.end())
return errors.material;
else
return *mat->second;
}
const render::MaterialGroup& Library::getMaterialGroup(const std::string& name) const {
auto mat = matGroups.find(name);
if(mat == matGroups.end())
return errors.group;
else
return *mat->second;
}
const render::SpriteSheet& Library::getSpriteSheet(const std::string& name) const {
auto sheet = spritesheets.find(name);
if(sheet == spritesheets.end())
return errors.spriteSheet;
else
return *sheet->second;
}
const render::RenderMesh& Library::getMesh(const std::string& name) const {
auto mesh = meshes.find(name);
if(mesh == meshes.end())
return *errors.mesh;
else
return *mesh->second;
}
const gui::skin::Skin& Library::getSkin(const std::string& name) const {
auto skin = skins.find(name);
if(skin == skins.end())
return errors.skin;
else
return *skin->second;
}
const scene::ParticleSystemDesc* Library::getParticleSystem(const std::string& name) const {
auto sys = particleSystems.find(name);
if(sys == particleSystems.end())
return errors.particleSystem;
else
return sys->second;
}
//TODO: Decide how to handle the error font (it should still render some text)
const render::Font& Library::getFont(const std::string& name) const {
auto font = fonts.find(name);
if(font == fonts.end())
return *fonts.begin()->second;
else
return *font->second;
}
const render::Shader* Library::getShader(const std::string& name) const {
auto shader = shaders.find(name);
if(shader == shaders.end())
return 0;
else
return shader->second;
}
const Sound* Library::getSound(const std::string& name) const {
auto sound = sounds.find(name);
if(sound == sounds.end())
return 0;
else
return sound->second;
}
render::Sprite Library::getSprite(const std::string& desc) {
render::Sprite sprt;
std::string tmp = desc;
auto pos = desc.find("*");
if(pos != std::string::npos && pos < tmp.size() - 1) {
sprt.color = toColor(trim(tmp.substr(pos+1)));
tmp = trim(tmp.substr(0, pos));
}
pos = tmp.find("::");
if(pos == std::string::npos || pos >= tmp.size() - 2) {
auto sheet = spritesheets.find(tmp);
if(sheet != spritesheets.end()) {
sprt.sheet = sheet->second;
sprt.index = 0;
}
else {
auto img = materials.find(tmp);
if(img != materials.end()) {
sprt.mat = img->second;
}
}
}
else {
sprt.sheet = &getSpriteSheet(tmp.substr(0, pos));
sprt.index = toNumber<unsigned>(tmp.substr(pos+2));
}
return sprt;
}
};
+247
View File
@@ -0,0 +1,247 @@
#pragma once
#include "compat/misc.h"
#include "render/render_state.h"
#include "render/render_mesh.h"
#include "mesh.h"
#include "render/driver.h"
#include "render/texture.h"
#include "render/spritesheet.h"
#include "render/shader.h"
#include "gui/skin.h"
#include "str_util.h"
#include <climits>
#include <map>
#include <unordered_map>
namespace audio {
class ISoundSource;
class ISound;
};
namespace scene {
struct ParticleSystemDesc;
};
const Mesh emptyMesh;
namespace resource {
enum ResourceType {
RT_Sound,
RT_Material,
RT_Mesh,
RT_Shader,
RT_Font,
RT_Skin,
RT_SpriteSheet,
RT_ParticleSystem
};
class Sound {
public:
audio::ISoundSource* source;
std::string streamSource;
float volume;
bool loaded;
Sound() : source(0), volume(1.f), loaded(false) {}
audio::ISound* play2D(bool loop, bool pause, bool priority) const;
audio::ISound* play3D(const vec3d& pos, bool loop, bool pause, bool priority) const;
};
struct ShaderGlobal {
std::string name;
render::Shader::VarType type;
unsigned arraySize;
int size;
void* ptr;
ShaderGlobal();
template<class T>
void setValue(unsigned index, T& v) const {
if(index < arraySize)
((T*)ptr)[index] = v;
}
};
render::Texture* queueImage(Image* img, int priority = 0, bool mipmap = true, bool cachePixels = false);
void queueTextureUpdate(render::Texture* tex, Image* img, int priority = 0, bool mipmap = true, bool cachePixels = false);
class Library {
void loadMaterials(const std::string& filename);
void loadFonts(const std::string& filename);
void loadModels(const std::string& filename);
void loadShaders(const std::string& filename);
void loadSkins(const std::string& filename);
void loadSkin(const std::string& filename, gui::skin::Skin* skin);
void loadSounds(const std::string& filename);
static void initSkinNames();
void clearTextures();
class ErrorShader : public render::Shader {
int compile() { return 0; }
void bind(float*) const {}
void updateDynamicVars() const {}
void saveDynamicVars(float*) const { }
void loadDynamicVars(float*) const { }
public:
ErrorShader() { constant = true; dynamicFloats = 0; }
};
class ErrorMesh : public render::RenderMesh {
AABBoxf box;
void resetToMesh(const Mesh& mesh) {}
const RenderMesh* selectLOD(double distance) const { return this; }
void setLOD(double distance, const RenderMesh* mesh) {}
const AABBoxf& getBoundingBox() const { return box; }
unsigned getMeshBytes() const { return 0; }
const Mesh& getMesh() const { return emptyMesh; }
void render() const {};
};
struct {
render::Texture* texture;
render::RenderState material;
render::MaterialGroup group;
render::RenderMesh* mesh;
scene::ParticleSystemDesc* particleSystem;
render::SpriteSheet spriteSheet;
ErrorShader shader;
gui::skin::Skin skin;
} errors;
public:
struct ResourceAccessor;
umap<std::string, Sound*> sounds;
umap<std::string, render::RenderState*> materials;
umap<std::string, render::MaterialGroup*> matGroups;
umap<std::string, render::RenderMesh*> meshes;
umap<std::string, render::Shader*> shaders;
umap<std::string, render::ShaderProgram*> programs;
std::vector<render::Shader*> settingsShaders;
umap<std::string, render::Font*> fonts;
std::vector<render::Font*> font_list;
umap<std::string, gui::skin::Skin*> skins;
umap<std::string, scene::ParticleSystemDesc*> particleSystems;
umap<std::string, render::SpriteSheet*> spritesheets;
std::vector<render::Texture*> textures;
umap<std::string, render::Texture*> texture_files;
umap<std::string, gui::skin::Skin*> skin_files;
std::vector<std::string> material_names;
std::vector<std::string> spritesheet_names;
umap<const render::RenderState*, unsigned> mat_indices;
umap<const render::SpriteSheet*, unsigned> sheet_indices;
ResourceAccessor operator[](const char* name) const;
//Returns the specified material, or an error material if the material is not loaded
const render::RenderState& getMaterial(const std::string& name) const;
//Returns the specified material group, or an error material group if the material group is not loaded
const render::MaterialGroup& getMaterialGroup(const std::string& name) const;
//Returns the specified spritesheet, or an error spritesheet if it is not loaded
const render::SpriteSheet& getSpriteSheet(const std::string& name) const;
//Returns the specified mesh, or an error mesh if the mesh is not loaded
const render::RenderMesh& getMesh(const std::string& name) const;
//Returns the specified skin, or an error skin if the skin is not loaded
const gui::skin::Skin& getSkin(const std::string& name) const;
//Returns the specified font, or an error font if the font is not loaded
const render::Font& getFont(const std::string& name) const;
//Returns the specified particle system, or an error particle system if the system is not loaded
const scene::ParticleSystemDesc* getParticleSystem(const std::string& name) const;
//Returns the specified texture, or an error shader (possibly 0) if the shader is not loaded
const render::Shader* getShader(const std::string& name) const;
//Returns the specified sound, or an error sound (possibly 0) if the sound is missing
const Sound* getSound(const std::string& name) const;
//Returns a reference to a particular sprite from a description
render::Sprite getSprite(const std::string& desc);
//Returns the descriptor string for a particular sprite
std::string getSpriteDesc(const render::Sprite& sprt) const;
//Creates fallback resources for when a resource is missing
void prepErrorResources();
void generateErrorResources();
const render::RenderState& getErrorMaterial() const;
const render::SpriteSheet& getErrorSpriteSheet() const;
const render::Texture* getErrorTexture() const;
//Clears all resources held by the library
void clear();
void load(ResourceType type, const std::string& filename);
void loadDirectory(ResourceType type, const std::string& filename);
void bindHotloading();
void watchTexture(const std::string& filename);
void watchShader(const std::string& shadername, const std::string& filename);
void watchSkin(const std::string& skinname);
//Bind materials to skins
void bindSkinMaterials();
//Bind fonts to skins
void bindSkinFonts();
//Compile all loaded shaders
void compileShaders();
//Clear shader global variables
void clearShaderGlobals();
//Iterate over all shader globals
void iterateShaderGlobals(std::function<void(std::string&,ShaderGlobal*)> func);
//Compiles any watched resources that need reloaded
void reloadWatchedResources();
//Clears list of watched resources (called by clear)
void clearWatchedResources();
//Load queued sounds
bool hasQueuedSounds();
bool processSounds(int maxPriority = INT_MIN, int amount = INT_MAX);
//Load queued images in the background
bool hasQueuedImages();
bool processImages(int maxPriority = INT_MIN, int amount = INT_MAX);
//Load queued textures - must be called from the thread that handles the driver
bool hasQueuedTextures();
bool processTextures(int maxPriority = INT_MIN, bool singleFrame = false);
//Load queued meshes - must be called from the thread that handles the driver
bool hasQueuedMeshes();
bool processMeshes(int maxPriority = INT_MIN, int amount = INT_MAX);
struct ResourceAccessor {
const Library* source;
const char* name;
operator const Sound*();
operator const render::RenderState*();
operator const render::RenderMesh*();
operator const render::Shader*();
operator const render::Font*();
operator const render::SpriteSheet*();
operator const gui::skin::Skin*();
operator const Sound&();
operator const render::RenderState&();
operator const render::RenderMesh&();
operator const render::Shader&();
operator const render::Font&();
operator const render::SpriteSheet&();
operator const gui::skin::Skin&();
};
};
};
+115
View File
@@ -0,0 +1,115 @@
#include "main/references.h"
#include "resource/library.h"
#include "main/logging.h"
#include "str_util.h"
#include "num_util.h"
#include "main/tick.h"
#include "render/font.h"
#include <iostream>
#include <fstream>
#include <string>
namespace resource {
void Library::loadFonts(const std::string& filename) {
std::string font_name, font_file, font_locale;
std::vector<std::pair<int,int>> pages;
std::vector<std::string> replaces;
int size = 12;
render::Font* bold = 0;
render::Font* italic = 0;
auto makeFont = [&]() {
if(font_name.empty() || font_file.empty())
return;
if(font_locale.empty() || font_locale == game_locale) {
render::Font* font = 0;
auto p_ext = strrchr(font_file.c_str(), '.');
if(!p_ext || strcmp_nocase(p_ext, ".fnt") == 0)
font = render::loadFontFNT(devices.render, font_file.c_str());
else if(strcmp_nocase(p_ext, ".ttf") == 0)
font = render::loadFontFT2(*devices.render, font_file.c_str(), pages, size);
else if(strcmp_nocase(p_ext, ".otf") == 0)
font = render::loadFontFT2(*devices.render, font_file.c_str(), pages, size);
else
error("Font file '%s' in unrecognized format.", font_file.c_str());
if(font) {
font->bold = bold;
font->italic = italic;
fonts[font_name] = font;
font_list.push_back(font);
foreach(it, replaces)
fonts[*it] = font;
}
}
font_name.clear();
font_file.clear();
font_locale.clear();
replaces.clear();
};
DataHandler datahandler;
datahandler("Font", [&](std::string& value) {
makeFont();
font_name = value;
bold = 0;
italic = 0;
pages.clear();
size = 12;
});
datahandler("File", [&](std::string& value) {
font_file = devices.mods.resolve(value);
});
datahandler("Locale", [&](std::string& value) {
font_locale = value;
});
datahandler("Replace", [&](std::string& value) {
replaces.push_back(value);
});
datahandler("Size", [&](std::string& value) {
size = toNumber<int>(value);
});
datahandler("Bold", [&](std::string& value) {
auto it = fonts.find(value);
if(it != fonts.end()) {
bold = it->second;
}
else {
error("Font '%s' does not exist.", value.c_str());
}
});
datahandler("Italic", [&](std::string& value) {
auto it = fonts.find(value);
if(it != fonts.end()) {
italic = it->second;
}
else {
error("Font '%s' does not exist.", value.c_str());
}
});
datahandler.defaultHandler([&](std::string& key, std::string& value) {
if(key.compare(0, 4, "Page") == 0) {
std::vector<std::string> numbers;
split(value, numbers, '-');
if(numbers.size() == 2)
pages.push_back(std::pair<int,int>(toNumber<int>(numbers[0], 0, std::hex),
toNumber<int>(numbers[1], 255, std::hex)));
}
});
datahandler.read(filename);
makeFont();
}
};
+631
View File
@@ -0,0 +1,631 @@
#include "main/references.h"
#include "main/logging.h"
#include "main/initialization.h"
#include "render/render_state.h"
#include "resource/library.h"
#include "compat/misc.h"
#include "str_util.h"
#include "num_util.h"
#include "threads.h"
#include "files.h"
#include <iostream>
#include <fstream>
#include <string>
#include <unordered_map>
#include <functional>
#include <tuple>
#include <queue>
#include <stdint.h>
#include "main/profiler.h"
extern bool cancelAssets;
namespace resource {
umap<std::string, render::SpriteMode> INIT_VAR(spriteModes) {
spriteModes["Horizontal"] = render::SM_Horizontal;
spriteModes["Vertical"] = render::SM_Vertical;
} INIT_VAR_END;
umap<std::string, render::FaceCulling> INIT_VAR(cullingModes) {
cullingModes["None"] = render::FC_None;
cullingModes["Front"] = render::FC_Front;
cullingModes["Back"] = render::FC_Back;
cullingModes["Both"] = render::FC_Both;
} INIT_VAR_END;
umap<std::string, render::DepthTest> INIT_VAR(depthTests) {
depthTests["Never"] = render::DT_Never;
depthTests["Less"] = render::DT_Less;
depthTests["LessEqual"] = render::DT_LessEqual;
depthTests["Equal"] = render::DT_Equal;
depthTests["GreaterEqual"] = render::DT_GreaterEqual;
depthTests["Greater"] = render::DT_Greater;
depthTests["Always"] = render::DT_Always;
depthTests["NoDepthTest"] = render::DT_NoDepthTest;
} INIT_VAR_END;
umap<std::string, render::TextureWrap> INIT_VAR(textureWraps) {
textureWraps["Repeat"] = render::TW_Repeat;
textureWraps["Clamp"] = render::TW_Clamp;
textureWraps["ClampEdge"] = render::TW_ClampEdge;
textureWraps["Mirror"] = render::TW_Mirror;
} INIT_VAR_END;
umap<std::string, render::TextureFilter> INIT_VAR(textureFilters) {
textureFilters["Linear"] = render::TF_Linear;
textureFilters["Nearest"] = render::TF_Nearest;
} INIT_VAR_END;
umap<std::string, render::DrawMode> INIT_VAR(drawModes) {
drawModes["Line"] = render::DM_Line;
drawModes["Fill"] = render::DM_Fill;
} INIT_VAR_END;
umap<std::string, render::BaseMaterial> INIT_VAR(baseMats) {
baseMats["Solid"] = render::MAT_Solid;
baseMats["Add"] = render::MAT_Add;
baseMats["Alpha"] = render::MAT_Alpha;
baseMats["Font"] = render::MAT_Font;
baseMats["Overlay"] = render::MAT_Overlay;
} INIT_VAR_END;
const unsigned DRIVER_MIPMAP_SIZE = 512 * 512;
struct QueuedTexture {
int priority;
std::vector<Image*> images;
render::Texture* tex;
std::string filename;
bool mipmap;
bool cachePixels;
mutable unsigned row, lod;
QueuedTexture(render::Texture* dest, int Priority, Image* source, bool Mipmap, bool CachePixels)
: priority(Priority), tex(dest), mipmap(Mipmap), cachePixels(CachePixels), row(0), lod(0)
{
images.push_back(source);
}
QueuedTexture(render::Texture* dest, int Priority, const std::string& source, bool Mipmap, bool CachePixels)
: priority(Priority), tex(dest), filename(source), mipmap(Mipmap), cachePixels(CachePixels), row(0), lod(0)
{
}
//Loads the image, returning the number of bytes loaded
// If true was returned, the image is done loading or there was a failure
// The number of bytes loaded may exceed maxBytes - it is only a soft limit
bool load(unsigned maxBytes, unsigned& loadedBytes);
bool operator<(const QueuedTexture& other) const {
return priority < other.priority;
}
void clearImages() {
for(auto i = images.begin(); i != images.end(); ++i)
delete *i;
images.clear();
}
};
threads::Mutex queuedImagesLock;
std::priority_queue<QueuedTexture> queuedImages;
threads::Mutex queuedTexturesLock;
std::priority_queue<QueuedTexture> queuedTextures;
threads::Mutex texIdentLock;
std::unordered_map<std::string,render::Texture*> texNames;
unsigned maxTexSize = 0;
void Library::clearTextures() {
texNames.clear();
maxTexSize = 0;
}
bool Library::hasQueuedImages() {
return !queuedImages.empty();
}
unsigned getMaxTextureSize() {
if(maxTexSize == 0) {
auto* texQuality = devices.settings.engine.getSetting("iTextureQuality");
if(texQuality) {
switch(texQuality->getInteger()) {
case 0: maxTexSize = 256; break;
case 1: maxTexSize = 512; break;
case 2: maxTexSize = 1024; break;
case 3: maxTexSize = 2048; break;
case 4: maxTexSize = 4096; break;
default:
case 5: maxTexSize = 8192; break;
}
}
else {
maxTexSize = 65536;
}
}
return maxTexSize * maxTexSize;
}
bool Library::processImages(int maxPriority, int amount) {
bool processedAny = false;
int processed = 0;
while(!queuedImages.empty()) {
queuedImagesLock.lock();
if(queuedImages.empty()) {
queuedImagesLock.release();
break;
}
auto elem = queuedImages.top();
if(elem.priority < maxPriority) {
queuedImagesLock.release();
break;
}
if(elem.tex)
textures.push_back(elem.tex);
queuedImages.pop();
queuedImagesLock.release();
if(cancelAssets)
continue;
processedAny = true;
if(!elem.filename.empty()) {
Image* img = loadImage(elem.filename.c_str());
if(!img) {
error("Error: Could not load image '%s'", elem.filename.c_str());
continue;
}
unsigned texSize = getMaxTextureSize();
while(img->width * img->height > texSize) {
auto* prev = img;
img = img->makeMipmap();
delete prev;
}
elem.images.push_back(img);
//Loading images this way causes weirdness
/*if(elem.mipmap && img->width * img->height > DRIVER_MIPMAP_SIZE) {
while(img->width > 2 && img->height > 2) {
img = img->makeMipmap();
if(img)
elem.images.push_back(img);
else
break;
}
}*/
}
++processed;
queuedTexturesLock.lock();
queuedTextures.push(elem);
queuedTexturesLock.release();
if(processed >= amount)
break;
}
return processedAny;
}
bool Library::hasQueuedTextures() {
return !queuedTextures.empty();
}
bool QueuedTexture::load(unsigned maxBytes, unsigned& loadedBytes) {
if(images.empty())
return true;
if(!tex) {
clearImages();
return true;
}
if(loadedBytes >= maxBytes)
return false;
Image* img = images.front();
unsigned rowBytes = img->width * ColorDepths[img->format];
unsigned loadRows = (maxBytes - loadedBytes) / rowBytes;
if(loadRows == 0)
loadRows = 1;
if(loadRows > img->height - row)
loadRows = img->height - row;
loadedBytes += rowBytes * loadRows;
if(lod == 0/* && loadRows == img->height && img->width * img->height <= DRIVER_MIPMAP_SIZE*/) {
tex->load(*img, mipmap, cachePixels);
clearImages();
return true;
}
else {
if(row == 0)
tex->loadStart(*img, mipmap, cachePixels, lod);
tex->loadPartial(*img, recti(0, row, img->width, row+loadRows), cachePixels, lod);
row += loadRows;
if(row == img->height) {
delete img;
images.erase(images.begin());
tex->loadFinish(mipmap && images.empty(), lod);
if(!mipmap || images.empty())
return true;
//Reset for the next LOD
lod += 1;
row = 0;
return load(maxBytes, loadedBytes);
}
}
return false;
}
bool Library::processTextures(int maxPriority, bool singleFrame) {
bool processedAny = false;
const unsigned frameByteLimit = 1024 * 1024 * 4;
int64_t byteLimit = singleFrame ? frameByteLimit : 0xffffffff;
while(!queuedTextures.empty()) {
queuedTexturesLock.lock();
if(queuedTextures.empty()) {
queuedTexturesLock.release();
break;
}
auto tex = queuedTextures.top();
if(tex.priority < maxPriority) {
queuedTexturesLock.release();
break;
}
queuedTextures.pop();
//Track filenames even if we can't load the resource at the moment, in case the file is created while we're running
if(tex.tex && !tex.filename.empty())
texture_files[tex.filename] = tex.tex;
queuedTexturesLock.release();
if(cancelAssets)
continue;
processedAny = true;
unsigned loaded = 0;
if(!tex.load(byteLimit, loaded)) {
threads::Lock lock(queuedTexturesLock);
queuedTextures.push(tex);
}
byteLimit -= (int64_t)loaded;
if(byteLimit <= 0)
break;
}
return processedAny;
}
render::Texture* queueImage(const std::string& abs_file, int priority = -20, bool mipmap = true, bool cachePixels = false, bool cubemap = false) {
bool queue = false;
render::Texture* tex;
{
texIdentLock.lock();
render::Texture*& pTex = texNames[abs_file];
if(pTex == 0) {
if(cubemap)
pTex = render::RenderDriver::createCubemap();
else
pTex = render::RenderDriver::createTexture();
queue = true;
}
tex = pTex;
texIdentLock.release();
}
if(queue) {
queuedImagesLock.lock();
resource::QueuedTexture queued = resource::QueuedTexture(tex, priority, abs_file, mipmap, cachePixels);
queuedImages.push(queued);
queuedImagesLock.release();
}
return tex;
}
render::Texture* queueImage(Image* img, int priority, bool mipmap, bool cachePixels) {
render::Texture* tex = render::RenderDriver::createTexture();
queuedTexturesLock.lock();
resource::QueuedTexture queued = resource::QueuedTexture(tex, priority, img, mipmap, cachePixels);
queuedTextures.push(queued);
queuedTexturesLock.release();
return tex;
}
void queueTextureUpdate(render::Texture* tex, Image* img, int priority, bool mipmap, bool cachePixels) {
queuedTexturesLock.lock();
resource::QueuedTexture queued = resource::QueuedTexture(tex, priority, img, mipmap, cachePixels);
queuedTextures.push(queued);
queuedTexturesLock.release();
}
void Library::loadMaterials(const std::string& filename) {
DataHandler datahandler;
render::RenderState* state = nullptr;
render::SpriteSheet* sheet = nullptr;
render::MaterialGroup* group = nullptr;
std::string matName;
int priority = -61;
bool texdefs = false;
//Initialization handling
datahandler("SpriteSheet", [&](std::string& value) {
matName = value;
priority = -61;
group = nullptr;
sheet = new render::SpriteSheet();
state = &sheet->material;
spritesheets[matName] = sheet;
sheet_indices[sheet] = (unsigned)spritesheet_names.size();
spritesheet_names.push_back(matName);
texdefs = false;
});
datahandler("Material", [&](std::string& value) {
matName = value;
sheet = 0;
group = nullptr;
priority = -61;
if(materials.find(matName) != materials.end()) {
error("Duplicate material: %s", matName.c_str());
state = materials[matName];
*state = render::RenderState();
}
else {
state = new render::RenderState();
state->constant = true;
}
materials[matName] = state;
mat_indices[state] = (unsigned)material_names.size();
material_names.push_back(matName);
texdefs = false;
});
datahandler("MaterialGroup", [&](std::string& value) {
matName = value;
sheet = nullptr;
state = nullptr;
group = new render::MaterialGroup();
group->prefix = value + "_";
matGroups[value] = group;
});
datahandler("Template", [&](std::string& value) {
if(!group)
return;
auto it = materials.find(value);
if(it != materials.end())
group->base = *it->second;
else
error("Could not find template material '%s' for '%s'", value.c_str(), matName.c_str());
});
datahandler("Prefix", [&](std::string& value) {
if(!group)
return;
group->prefix = value;
});
datahandler("Folder", [&](std::string& value) {
if(!group)
return;
auto& g = *group;
auto& mats = materials;
auto& inds = mat_indices;
auto& names = material_names;
int texPriority = priority;
devices.mods.listFiles(value, "*.png", [&](const std::string& filename) {
std::string id = g.prefix + getBasename(filename, false);
makeIdentifier(id);
auto* mat = new render::RenderState();
*mat = g.base;
mat->constant = true;
mats[id] = mat;
inds[mat] = (unsigned)names.size();
names.push_back(id);
g.names.push_back(id);
g.materials.push_back(mat);
if(load_resources) {
mat->textures[0] = queueImage(filename, texPriority, mat->mipmap, mat->cachePixels);
if(watch_resources)
devices.library.watchTexture(filename);
}
}, true);
});
datahandler("Inherit", [&](std::string& value) {
if(!state)
return;
auto it = materials.find(value);
if(it != materials.end())
*state = *it->second;
else
error("Could not find material '%s' to inherit for '%s'", value.c_str(), matName.c_str());
});
//Renderstate members
HANDLE_BOOL(datahandler, "DepthWrite", state, depthWrite);
HANDLE_ENUM(datahandler, "DepthTest", state, depthTest, depthTests);
HANDLE_ENUM(datahandler, "Culling", state, culling, cullingModes);
HANDLE_BOOL(datahandler, "Lighting", state, lighting);
HANDLE_BOOL(datahandler, "NormalizeNormals", state, normalizeNormals);
HANDLE_ENUM_W(datahandler, "Shader", state, shader, shaders, load_resources);
HANDLE_NUM(datahandler, "Shininess", state, shininess);
HANDLE_ENUM(datahandler, "WrapVertical", state, wrapVertical, textureWraps);
HANDLE_ENUM(datahandler, "WrapHorizontal", state, wrapHorizontal, textureWraps);
HANDLE_ENUM(datahandler, "FilterMin", state, filterMin, textureFilters);
HANDLE_ENUM(datahandler, "FilterMag", state, filterMag, textureFilters);
HANDLE_ENUM(datahandler, "DrawMode", state, drawMode, drawModes);
HANDLE_ENUM(datahandler, "Blend", state, baseMat, baseMats);
datahandler("Mipmap", [&](std::string& value) {
if(!state)
return;
if(texdefs) {
warn("Warning: Mipmap statement should be before texture"
" declarations.\n %s", datahandler.position().c_str());
}
state->mipmap = toBool(value);
});
datahandler("CachePixels", [&](std::string& value) {
if(!state)
return;
state->cachePixels = toBool(value);
});
datahandler("LoadPriority", [&](std::string& value) {
if(value == "Critical" || value == "Menu")
priority = 10;
else if(value == "Game")
priority = -10;
else if(value == "High")
priority = -31;
else if(value == "Low")
priority = -91;
else
priority = -111 + toNumber<int>(value);
});
datahandler("Alpha", [&](std::string& value) {
if(!state)
return;
if(toBool(value))
state->baseMat = render::MAT_Alpha;
else
state->baseMat = render::MAT_Solid;
});
datahandler("Diffuse", [&](std::string& value) {
int r,g,b,a;
if(int args = sscanf(value.c_str(),"#%2x%2x%2x%2x", &r,&g,&b,&a)) {
if(args == 3)
state->diffuse = Colorf(Color(r,g,b));
else if(args == 4)
state->diffuse = Colorf(Color(r,g,b,a));
}
else {
sscanf(value.c_str(), "%f,%f,%f,%f", &state->diffuse.r, &state->diffuse.g, &state->diffuse.b, &state->diffuse.a);
}
});
datahandler("Specular", [&](std::string& value) {
int r,g,b,a;
if(int args = sscanf(value.c_str(),"#%2x%2x%2x%2x", &r,&g,&b,&a)) {
if(args == 3)
state->specular = Colorf(Color(r,g,b));
else if(args == 4)
state->specular = Colorf(Color(r,g,b,a));
}
else {
sscanf(value.c_str(), "%f,%f,%f,%f", &state->specular.r, &state->specular.g, &state->specular.b, &state->specular.a);
}
});
//Spritesheet members
HANDLE_ENUM(datahandler, "Mode", sheet, mode, spriteModes);
datahandler("Size", [&](std::string& value) {
if(!sheet)
return;
std::vector<std::string> args;
split(value, args, ',');
if(args.size() != 2)
return;
sheet->width = toNumber<int>(args[0]);
sheet->height = toNumber<int>(args[1]);
});
datahandler("Spacing", [&](std::string& value) {
if(!sheet)
return;
sheet->spacing = toNumber<int>(value);
});
datahandler.defaultHandler([&](std::string& key, std::string& value) {
if(key.size() >= 7 && (key.compare(0, 7, "Texture") == 0 || key.compare(0, 7, "Cubemap") == 0)) {
//Figure out the texture number to set
int texNum = 0;
texdefs = true;
if (key.size() > 7) {
std::string num = key.substr(7, key.size() - 7);
texNum = min_(toNumber<int>(num)-1, RENDER_MAX_TEXTURES);
}
value = getAbsolutePath( devices.mods.resolve(value) );
if(load_resources) {
state->textures[texNum] = queueImage(value, priority, state->mipmap, state->cachePixels, key[0] == 'C');
if(watch_resources)
watchTexture(value);
}
}
});
datahandler.read(filename);
}
std::string Library::getSpriteDesc(const render::Sprite& sprt) const {
std::string ret;
if(sprt.mat) {
auto it = mat_indices.find(sprt.mat);
if(it != mat_indices.end())
ret = material_names[it->second];
}
else {
auto it = sheet_indices.find(sprt.sheet);
if(it == sheet_indices.end())
return ret;
ret += spritesheet_names[it->second];
ret += "::";
ret += toString(sprt.index);
}
if(sprt.color.color != 0xffffffff) {
ret += "*";
ret += toString(sprt.color);
}
return ret;
}
};
+246
View File
@@ -0,0 +1,246 @@
#include "main/references.h"
#include "main/initialization.h"
#include "main/logging.h"
#include "render/render_mesh.h"
#include "resource/library.h"
#include "str_util.h"
#include "num_util.h"
#include "util/mesh_generation.h"
#include "threads.h"
#include "files.h"
#include <iostream>
#include <fstream>
#include <string>
#include <tuple>
#include "render/x_loader.h"
#include "render/obj_loader.h"
#include "render/bmf_loader.h"
#include "render/ogex_loader.h"
bool useModelCache = false;
extern bool cancelAssets;
namespace resource {
threads::Signal unqueuedMeshes;
threads::Mutex queuedMeshLock;
std::vector<std::tuple<Mesh*,render::RenderMesh*>> queuedMeshes;
bool Library::hasQueuedMeshes() {
return !unqueuedMeshes.check(0) || !queuedMeshes.empty();
}
bool Library::processMeshes(int maxPriority, int amount) {
if(queuedMeshes.empty())
return false;
//TODO: Probably this has a race condition (threads end while this loop is running)
queuedMeshLock.lock();
while(!queuedMeshes.empty()) {
auto& pair = queuedMeshes.back();
std::get<1>(pair)->resetToMesh( *std::get<0>(pair) );
delete std::get<0>(pair);
queuedMeshes.pop_back();
}
queuedMeshLock.release();
return true;
}
struct MeshLoadData {
bool isSphere;
bool makeTangents;
std::string fileName;
unsigned width, height;
render::RenderMesh* rMesh;
const render::RenderMesh* lodMesh;
double lodDist;
MeshLoadData() : isSphere(false), makeTangents(false), width(0), height(0), lodMesh(0), lodDist(1) { }
};
threads::atomic_int activeMeshThreads;
threads::threadreturn threadcall LoadMesh(void* loadData) {
MeshLoadData& data = *(MeshLoadData*)loadData;
Mesh* mesh = 0;
while(++activeMeshThreads > 4) {
--activeMeshThreads;
threads::sleep(5);
}
if(cancelAssets) {
--activeMeshThreads;
unqueuedMeshes.signalDown();
delete &data;
return 0;
}
//Create raw mesh
if(data.isSphere) {
mesh = generateSphereMesh(data.height, data.width);
}
else {
mesh = new Mesh();
//TODO: Make work if multiple meshes happen to have the same name, especially on Windows
std::string cacheModel = devices.mods.getProfile("model_cache") + "/" + getBasename(data.fileName);
std::string sourceModel = devices.mods.resolve(data.fileName);
if(useModelCache && fileExists(cacheModel) && (getModifiedTime(cacheModel) - getModifiedTime(sourceModel)) >= 0) {
render::loadBinaryMesh(cacheModel.c_str(), *mesh);
if(mesh->faces.empty())
goto failedCache;
}
else {
failedCache:
if(fileExists(sourceModel)) {
if(match(sourceModel.c_str(), ".x"))
render::loadMeshX(sourceModel.c_str(), *mesh);
else if(match(sourceModel.c_str(), ".ogex"))
render::loadMeshOGEX(sourceModel.c_str(), *mesh);
else
render::loadMeshOBJ(sourceModel.c_str(), *mesh);
if(!mesh->faces.empty() && useModelCache)
render::saveBinaryMesh(cacheModel.c_str(), *mesh);
}
else {
error("Could not find model file '%s'", sourceModel.c_str());
}
}
}
if(!mesh || mesh->faces.empty())
error("Could not load mesh '%s'", data.fileName.c_str());
//Queue mesh for the main thread to generate the GL mesh
if(mesh) {
//Calculate tangents and binormals
if(data.makeTangents) {
mesh->tangents.resize(mesh->vertices.size());
std::vector<vec3f> binormals(mesh->vertices.size());
for(auto i = mesh->faces.begin(), end = mesh->faces.end(); i != end; ++i) {
auto& face = *i;
auto& a = mesh->vertices[face.a];
auto& b = mesh->vertices[face.b];
auto& c = mesh->vertices[face.c];
vec3f d1 = b.position - a.position;
vec3f d2 = c.position - a.position;
vec2f s = vec2f(b.u - a.u, c.u - a.u);
vec2f t = vec2f(b.v - a.v, c.v - a.v);
float r = (s.x * t.y - s.y * t.x);
if(r < 0.0001f && r > -0.0001f)
continue;
r = 1.f / r;
vec3f bnDir = ((d2 * s.x) - (d1 * s.y)) * r;
vec3f td = ((d1 * t.y) - (d2 * t.x)) * r;
vec4f tanDir = vec4f(td.x, td.y, td.z, 0.f);
mesh->tangents[face.a] += tanDir; if(mesh->tangents[face.a].zero()) mesh->tangents[face.a] = tanDir;
mesh->tangents[face.b] += tanDir; if(mesh->tangents[face.b].zero()) mesh->tangents[face.b] = tanDir;
mesh->tangents[face.c] += tanDir; if(mesh->tangents[face.c].zero()) mesh->tangents[face.c] = tanDir;
binormals[face.a] += bnDir; if(binormals[face.a].zero()) binormals[face.a] = bnDir;
binormals[face.b] += bnDir; if(binormals[face.b].zero()) binormals[face.b] = bnDir;
binormals[face.c] += bnDir; if(binormals[face.c].zero()) binormals[face.c] = bnDir;
}
for(unsigned i = 0; i < mesh->vertices.size(); ++i) {
auto& vertex = mesh->vertices[i];
auto& tangent = mesh->tangents[i];
vec3f t = vec3f(tangent.x, tangent.y, tangent.z);
bool handedness = (vertex.normal.cross(t).dot(binormals[i]) > 0.f);
//Restrict to tangent plane
t = (t - vertex.normal * vertex.normal.dot(t)).normalized();
tangent.x = t.x;
tangent.y = t.y;
tangent.z = t.z;
tangent.w = handedness ? 1.f : -1.f;
}
}
if(!mesh->colors.empty() && mesh->colors.size() < mesh->vertices.size())
mesh->colors.resize(mesh->vertices.size());
//TODO: Handle an invalid mesh being loaded (the referenced mesh must be valid, but we have no data to load in)
if(data.lodMesh)
data.rMesh->setLOD(data.lodDist, data.lodMesh);
queuedMeshLock.lock();
queuedMeshes.push_back(std::tuple<Mesh*,render::RenderMesh*>(mesh,data.rMesh));
queuedMeshLock.release();
}
--activeMeshThreads;
unqueuedMeshes.signalDown();
delete &data;
return 0;
}
void Library::loadModels(const std::string& filename) {
MeshLoadData* meshData = 0;
DataHandler datahandler;
auto makeModel = [&](bool final) {
if(load_resources && meshData) {
unqueuedMeshes.signalUp();
threads::createThread(LoadMesh,meshData);
}
if(!final)
meshData = new MeshLoadData();
};
datahandler("Model", [&](std::string& value) {
makeModel(false);
if(load_resources)
meshData->rMesh = devices.render->createMesh(Mesh());
else
meshData->rMesh = errors.mesh;
meshes[value] = meshData->rMesh;
});
datahandler("Mesh", [&](std::string& value) {
meshData->fileName = value;
});
datahandler("Tangents", [&](std::string& value) {
meshData->makeTangents = toBool(value, true);
});
datahandler("Sphere", [&](std::string& value) {
if(sscanf(value.c_str(), "%d x %d", &meshData->width, &meshData->height) == 2 && meshData->width > 0 && meshData->height > 0 && meshData->width*meshData->height < 256*256)
meshData->isSphere = true;
});
datahandler("LOD", [&](std::string& value) {
char name[256];
if(sscanf(value.c_str(), " %255s > %lf", name, &meshData->lodDist) == 2)
meshData->lodMesh = &getMesh(name);
});
datahandler.read(filename);
makeModel(true);
}
};
+679
View File
@@ -0,0 +1,679 @@
#include "main/references.h"
#include "main/initialization.h"
#include "main/logging.h"
#include "render/render_state.h"
#include "render/shader.h"
#include "resource/library.h"
#include "str_util.h"
#include "num_util.h"
#include <iostream>
#include <fstream>
#include <string>
#include "util/random.h"
#include "files.h"
#include <set>
//External shader uniform handlers
extern void shader_frameTime(float*,unsigned short,void*);
extern void shader_gameTime(float*,unsigned short,void*);
extern void shader_frameTime_cycle(float*,unsigned short,void*);
extern void shader_gameTime_cycle(float*,unsigned short,void*);
extern void shader_frameTime_cycle_abs(float*,unsigned short,void*);
extern void shader_gameTime_cycle_abs(float*,unsigned short,void*);
extern void shader_pixelRatio(float*,unsigned short,void*);
extern void shader_sprite_pos(float*,unsigned short,void*);
extern void shader_skin_margin_src(float*,unsigned short,void*);
extern void shader_skin_margin_dest(float*,unsigned short,void*);
extern void shader_skin_src_pos(float*,unsigned short,void*);
extern void shader_skin_src_size(float*,unsigned short,void*);
extern void shader_skin_dst_size(float*,unsigned short,void*);
extern void shader_skin_mode(float*,unsigned short,void*);
extern void shader_skin_gradientCount(float*,unsigned short,void*);
extern void shader_skin_gradientMode(float*,unsigned short,void*);
extern void shader_skin_gradientRects(float*,unsigned short,void*);
extern void shader_skin_gradientCols(float*,unsigned short,void*);
extern void shader_unique(float* values,unsigned short,void*);
extern void shader_node_color(float*,unsigned short,void*);
extern void shader_node_distance(float*,unsigned short,void*);
extern void shader_node_scale(float*,unsigned short,void*);
extern void shader_node_selected(float*,unsigned short,void*);
extern void shader_emp_flag(int*,unsigned short,void*);
extern void shader_obj_velocity(float*,unsigned short,void*);
extern void shader_obj_position(float*,unsigned short,void*);
extern void shader_obj_rotation(float*,unsigned short,void*);
extern void shader_node_position(float*,unsigned short,void*);
extern void shader_node_rotation(float*,unsigned short,void*);
extern void shader_obj_id(int*,unsigned short,void*);
extern void shader_obj_acceleration(float*,unsigned short,void*);
extern void shader_plane_minrad(float*,unsigned short,void*);
extern void shader_plane_maxrad(float*,unsigned short,void*);
extern void shader_tex_size(float*,unsigned short,void*);
extern void shader_light_radius(float*,unsigned short,void*);
extern void shader_light_position(float*,unsigned short,void*);
extern void shader_light_screen(float*,unsigned short,void*);
extern void shader_light_active(float*,unsigned short,void*);
extern unsigned registerVar(const std::string& name);
extern void shader_statevars(float*,unsigned short,void*);
extern void shader_quadrant_damage(float*,unsigned short,void*);
void shader_invView(float* mat,unsigned short,void*) {
devices.render->getInverseView(mat);
}
void shader_random(float* values,unsigned short,void*) {
values[0] = randomf();
}
namespace resource {
ShaderGlobal::ShaderGlobal() : type(render::Shader::VT_invalid), size(0), ptr(0), arraySize(1) {
}
std::unordered_map<std::string, ShaderGlobal*> shaderGlobals;
void shader_global(float* out, unsigned short n, void* args) {
ShaderGlobal* glob = (ShaderGlobal*)args;
memcpy(out, glob->ptr, glob->size);
}
void loadToInts(int* ints, std::vector<std::string>::iterator from, std::vector<std::string>::iterator to) {
for(; from != to; ++from, ++ints)
*ints = atoi(from->c_str());
}
void loadToInt2s(int* ints, std::vector<std::string>::iterator from, std::vector<std::string>::iterator to) {
for(; from != to; ++from, ints += 2)
sscanf(from->c_str(),"<%d,%d>",ints,ints+1);
}
void loadToInt3s(int* ints, std::vector<std::string>::iterator from, std::vector<std::string>::iterator to) {
for(; from != to; ++from, ints += 3)
sscanf(from->c_str(),"<%d,%d,%d>",ints,ints+1,ints+2);
}
void loadToInt4s(int* ints, std::vector<std::string>::iterator from, std::vector<std::string>::iterator to) {
for(; from != to; ++from, ints += 4)
sscanf(from->c_str(),"<%d,%d,%d,%d>",ints,ints+1,ints+2,ints+3);
}
void loadToFloats(float* floats, std::vector<std::string>::iterator from, std::vector<std::string>::iterator to) {
for(; from != to; ++from, ++floats)
*floats = (float)atof(from->c_str());
}
void loadToFloat2s(float* floats, std::vector<std::string>::iterator from, std::vector<std::string>::iterator to) {
for(; from != to; ++from, floats += 2)
sscanf(from->c_str(),"<%f,%f>",floats,floats+1);
}
void loadToFloat3s(float* floats, std::vector<std::string>::iterator from, std::vector<std::string>::iterator to) {
for(; from != to; ++from, floats += 3)
sscanf(from->c_str(),"<%f,%f,%f>",floats,floats+1,floats+2);
}
void loadToFloat4s(float* floats, std::vector<std::string>::iterator from, std::vector<std::string>::iterator to) {
for(; from != to; ++from, floats += 4)
sscanf(from->c_str(),"<%f,%f,%f,%f>",floats,floats+1,floats+2,floats+3);
}
void Library::compileShaders() {
foreach(program,programs) {
int result = program->second->compile();
if(result != 0)
error("-In Shader Program '%s'", program->first.c_str());
}
foreach(shader,shaders) {
int result = shader->second->compile();
if(result != 0)
error("-In Shader '%s'", shader->first.c_str());
}
}
void Library::clearShaderGlobals() {
foreach(it, shaderGlobals) {
free(it->second->ptr);
delete it->second;
}
shaderGlobals.clear();
}
void Library::iterateShaderGlobals(std::function<void(std::string&,resource::ShaderGlobal*)> func) {
foreach(it, shaderGlobals)
func(it->second->name, it->second);
}
void Library::loadShaders(const std::string& filename) {
render::Shader* shader = 0;
std::vector<render::Shader::Variable> vars;
std::string vertex_file;
std::string fragment_file;
DataHandler datahandler;
bool activeBlock = true, anyBlockActive = false;
int shaderLevel = 3;
auto* sl = devices.settings.engine.getSetting("iShaderLevel");
if(sl)
shaderLevel = sl->getInteger();
auto BuildShader = [&]() {
if(!shader)
return;
auto progName = vertex_file + "|" + fragment_file;
auto p = programs.find(progName);
if(p != programs.end()) {
shader->program = p->second;
return;
}
render::ShaderProgram* program = 0;
if(load_resources)
program = devices.render->createShaderProgram(vertex_file.c_str(), fragment_file.c_str());
shader->program = program;
programs[progName] = program;
if(watch_resources) {
watchShader(progName, vertex_file);
watchShader(progName, fragment_file);
}
fragment_file.clear();
vertex_file.clear();
};
datahandler.controlHandler([&](std::string& line) -> bool {
if(!line.empty() && line[0] == '#') {
if(line.compare(0, 6, "#endif") == 0) {
activeBlock = true;
anyBlockActive = false;
return false;
}
else if(line.compare(0, 5, "#else") == 0) {
activeBlock = !anyBlockActive;
anyBlockActive = true;
return false;
}
else {
std::string control, condition;
if(splitKeyValue(line, control, condition, " ")) {
if(control == "#if" || control == "#elif") {
if(anyBlockActive) {
activeBlock = false;
}
else {
bool enterBlock = false;
if(condition == "fallback") {
auto* fallback = devices.settings.engine.getSetting("bShaderFallback");
if(fallback)
enterBlock = *fallback;
}
else if(condition == "low") {
enterBlock = shaderLevel == 1;
}
else if(condition == "medium") {
enterBlock = shaderLevel == 2;
}
else if(condition == "high") {
enterBlock = shaderLevel == 3;
}
else if(condition == "extreme") {
enterBlock = shaderLevel == 4;
}
else {
activeBlock = toBool(condition);
}
activeBlock = enterBlock;
anyBlockActive = activeBlock;
}
return false;
}
}
}
error("Unrecognized directive %s", line.c_str());
return false;
}
return activeBlock;
});
datahandler("Shader", [&](std::string& value) {
fragment_file.clear();
vertex_file.clear();
if(shaders.find(value) != shaders.end()) {
shader = 0;
error("Duplicate shader entry '%s'", value.c_str());
return;
}
shader = devices.render->createShader();
shaders[value] = shader;
});
datahandler("Vertex", [&](std::string& value) {
vertex_file = devices.mods.resolve(value);
if(!fragment_file.empty())
BuildShader();
});
datahandler("Fragment", [&](std::string& value) {
fragment_file = devices.mods.resolve(value);
if(!vertex_file.empty())
BuildShader();
});
datahandler("Settings Reload", [&](std::string& value) {
if(shader && toBool(value))
settingsShaders.push_back(shader);
});
datahandler("Variable", [&](std::string& value) {
if(!shader)
return;
//Split into left and right parts
std::vector<std::string> parts;
split(value, parts, '=');
if(parts.size() != 2)
return;
//Find variable type and name
std::vector<std::string> decl;
split(parts[0], decl, ' ');
if(decl.size() != 2)
return;
//Find arguments to variable
std::vector<std::string> args;
split(parts[1], args, ' ');
if(args.size() == 0)
return;
//Find the correct variable type
render::Shader::VarType type = render::Shader::VT_invalid;
std::string typeName, arraySizeText;
unsigned arraySize;
if(split(decl[0], typeName, '[', arraySizeText, ']')) {
arraySize = atoi(arraySizeText.c_str());
if(arraySize > 100 || arraySize == 0)
return;
}
else {
typeName = decl[0];
arraySize = 1;
}
if(typeName == "tex" || typeName == "int")
type = render::Shader::VT_int;
else if(typeName == "ivec2")
type = render::Shader::VT_int2;
else if(typeName == "ivec3")
type = render::Shader::VT_int3;
else if(typeName == "ivec4")
type = render::Shader::VT_int4;
else if(typeName == "float")
type = render::Shader::VT_float;
else if(typeName == "vec2")
type = render::Shader::VT_float2;
else if(typeName == "vec3")
type = render::Shader::VT_float3;
else if(typeName == "vec4")
type = render::Shader::VT_float4;
else if(typeName == "mat3")
type = render::Shader::VT_mat3;
if(type == render::Shader::VT_invalid)
return;
render::Shader::Variable var(type, arraySize);
if(args[0].find_first_not_of("0123456789.-+eE") != args[0].npos) {
auto& call = args[0];
if(call == "global") {
if(args.size() >= 2) {
ShaderGlobal* glob;
auto it = shaderGlobals.find(args[1]);
if(it == shaderGlobals.end()) {
glob = new ShaderGlobal();
glob->name = args[1];
glob->type = type;
glob->arraySize = arraySize;
shaderGlobals[glob->name] = glob;
switch(type) {
case render::Shader::VT_int:
glob->size = sizeof(int);
break;
case render::Shader::VT_int2:
glob->size = sizeof(int) * 2;
break;
case render::Shader::VT_int3:
glob->size = sizeof(int) * 3;
break;
case render::Shader::VT_int4:
glob->size = sizeof(int) * 4;
break;
case render::Shader::VT_float:
glob->size = sizeof(float);
break;
case render::Shader::VT_float2:
glob->size = sizeof(float) * 2;
break;
case render::Shader::VT_float3:
glob->size = sizeof(float) * 3;
break;
case render::Shader::VT_float4:
glob->size = sizeof(float) * 4;
break;
case render::Shader::VT_mat3:
glob->size = sizeof(float) * 9;
break;
}
glob->size *= arraySize;
glob->ptr = malloc(glob->size);
memset(glob->ptr, 0, glob->size);
}
else {
glob = it->second;
}
var._floatcall = shader_global;
var._args = (void*)glob;
var.constant = false;
}
}
else switch(var.type) {
case render::Shader::VT_int:
if(call == "emp_flag") {
var._intcall = shader_emp_flag;
var.constant = false;
}
else if(call == "obj_id") {
var._intcall = shader_obj_id;
var.constant = false;
}
break;
case render::Shader::VT_float:
if(call == "time") {
var._floatcall = shader_frameTime;
}
else if(call == "game_time") {
var._floatcall = shader_gameTime;
}
else if(call == "unique") {
var._floatcall = shader_unique;
var.constant = false;
}
else if(call == "random") {
var._floatcall = shader_random;
}
else if(call == "time_cycle") {
var._floatcall = shader_frameTime_cycle;
float* pArgs = new float[var.count];
var._args = pArgs;
for(auto i = 0; i < var.count; ++i)
pArgs[i] = 1000;
loadToFloats(pArgs,args.begin()+1,
(int)args.size() <= var.count+1 ? args.end() : args.begin()+(1+var.count));
}
else if(call == "game_time_cycle") {
var._floatcall = shader_gameTime_cycle;
float* pArgs = new float[var.count];
var._args = pArgs;
for(auto i = 0; i < var.count; ++i)
pArgs[i] = 1000;
loadToFloats(pArgs,args.begin()+1,
(int)args.size() <= var.count+1 ? args.end() : args.begin()+(1+var.count));
}
else if(call == "time_cycle_abs") {
var._floatcall = shader_frameTime_cycle_abs;
float* pArgs = new float[var.count];
var._args = pArgs;
for(auto i = 0; i < var.count; ++i)
pArgs[i] = 1000;
loadToFloats(pArgs,args.begin()+1,
(int)args.size() <= var.count+1 ? args.end() : args.begin()+(1+var.count));
}
else if(call == "game_time_cycle_abs") {
var._floatcall = shader_gameTime_cycle_abs;
float* pArgs = new float[var.count];
var._args = pArgs;
for(auto i = 0; i < var.count; ++i)
pArgs[i] = 1000;
loadToFloats(pArgs,args.begin()+1,
(int)args.size() <= var.count+1 ? args.end() : args.begin()+(1+var.count));
}
else if(call == "node_distance") {
var._floatcall = shader_node_distance;
var.constant = false;
}
else if(call == "node_selected") {
var._floatcall = shader_node_selected;
var.constant = false;
}
else if(call == "node_scale") {
var._floatcall = shader_node_scale;
var.constant = false;
}
else if(call == "obj_velocity") {
var._floatcall = shader_obj_velocity;
var.constant = false;
}
else if(call == "obj_acceleration") {
var._floatcall = shader_obj_acceleration;
var.constant = false;
}
else if(call == "plane_minrad") {
var._floatcall = shader_plane_minrad;
var.constant = false;
}
else if(call == "plane_maxrad") {
var._floatcall = shader_plane_maxrad;
var.constant = false;
}
else if(call == "state_vars") {
var._floatcall = shader_statevars;
var.constant = false;
int* pArgs = new int[var.count];
memset(pArgs,0,sizeof(int) * var.count);
var._args = pArgs;
unsigned index = 0;
auto i = args.begin() + 1;
auto end = args.end();
for(; i != end && index < var.count; ++i, ++index)
pArgs[index] = registerVar(*i);
}
else if(call == "pixel_ratio") {
var._floatcall = shader_pixelRatio;
}
else if(call == "skin_grd_count") {
var._floatcall = shader_skin_gradientCount;
var.constant = false;
}
else if(call == "skin_grd_mode") {
var._floatcall = shader_skin_gradientMode;
var.constant = false;
}
else if(call == "light_radius") {
var._floatcall = shader_light_radius;
var.constant = true;
int* pArgs = new int[var.count];
memset(pArgs,0,sizeof(int) * var.count);
var._args = pArgs;
loadToInts(pArgs, args.begin()+1,
(int)args.size() <= var.count+1 ? args.end() : args.begin()+(1+var.count));
}
else if(call == "light_active") {
var._floatcall = shader_light_active;
var.constant = true;
int* pArgs = new int[var.count];
memset(pArgs,0,sizeof(int) * var.count);
var._args = pArgs;
loadToInts(pArgs, args.begin()+1,
(int)args.size() <= var.count+1 ? args.end() : args.begin()+(1+var.count));
}
break;
case render::Shader::VT_float2:
if(call == "skin_src_pos") {
var._floatcall = shader_skin_src_pos;
var.constant = false;
}
else if(call == "skin_src_size") {
var._floatcall = shader_skin_src_size;
var.constant = false;
}
else if(call == "skin_dst_size") {
var._floatcall = shader_skin_dst_size;
var.constant = false;
}
else if(call == "skin_dim_modes") {
var._floatcall = shader_skin_mode;
var.constant = false;
}
else if(call == "tex_size") {
var._floatcall = shader_tex_size;
int* pArgs = new int[var.count];
memset(pArgs,0,sizeof(int) * var.count);
var._args = pArgs;
loadToInts(pArgs, args.begin()+1,
(int)args.size() <= var.count+1 ? args.end() : args.begin()+(1+var.count));
}
else if(call == "light_screen_position") {
var._floatcall = shader_light_screen;
var.constant = true;
int* pArgs = new int[var.count];
memset(pArgs,0,sizeof(int) * var.count);
var._args = pArgs;
loadToInts(pArgs, args.begin()+1,
(int)args.size() <= var.count+1 ? args.end() : args.begin()+(1+var.count));
}
break;
case render::Shader::VT_float3:
if(call == "light_position") {
var._floatcall = shader_light_position;
var.constant = true;
int* pArgs = new int[var.count];
memset(pArgs,0,sizeof(int) * var.count);
var._args = pArgs;
loadToInts(pArgs, args.begin()+1,
(int)args.size() <= var.count+1 ? args.end() : args.begin()+(1+var.count));
}
else if(call == "obj_position") {
var._floatcall = shader_obj_position;
var.constant = false;
}
else if(call == "node_position") {
var._floatcall = shader_node_position;
var.constant = false;
}
break;
case render::Shader::VT_float4:
if(call == "node_color") {
var._floatcall = shader_node_color;
var.constant = false;
}
else if(call == "skin_grd_colors") {
var._floatcall = shader_skin_gradientCols;
var.constant = false;
}
else if(call == "skin_grd_rects") {
var._floatcall = shader_skin_gradientRects;
var.constant = false;
}
else if(call == "skin_margin_src") {
var._floatcall = shader_skin_margin_src;
var.constant = false;
}
else if(call == "skin_margin_dest") {
var._floatcall = shader_skin_margin_dest;
var.constant = false;
}
else if(call == "sprite_pos") {
var._floatcall = shader_sprite_pos;
var.constant = false;
}
else if(call == "obj_quadrant_damage") {
var._floatcall = shader_quadrant_damage;
var.constant = false;
}
else if(call == "obj_rotation") {
var._floatcall = shader_obj_rotation;
var.constant = false;
}
else if(call == "node_rotation") {
var._floatcall = shader_node_rotation;
var.constant = false;
}
break;
case render::Shader::VT_mat3:
if(call == "inverse_view") {
var._floatcall = shader_invView;
}
break;
}
}
if(var._intcall == 0) {
auto from = args.begin(), to = args.size() <= var.count ? args.end() : args.begin()+var.count;
switch(var.type) {
case render::Shader::VT_int:
loadToInts(var._ints, from, to); break;
case render::Shader::VT_int2:
loadToInt2s(var._ints, from, to); break;
case render::Shader::VT_int3:
loadToInt3s(var._ints, from, to); break;
case render::Shader::VT_int4:
loadToInt4s(var._ints, from, to); break;
case render::Shader::VT_float:
loadToFloats(var._floats, from, to); break;
case render::Shader::VT_float2:
loadToFloat2s(var._floats, from, to); break;
case render::Shader::VT_float3:
loadToFloat3s(var._floats, from, to); break;
case render::Shader::VT_float4:
loadToFloat4s(var._floats, from, to); break;
}
}
shader->addVariable(decl[1], var);
});
datahandler.read(filename);
}
};
+339
View File
@@ -0,0 +1,339 @@
#include "render/render_state.h"
#include "main/references.h"
#include "main/initialization.h"
#include "resource/library.h"
#include "gui/skin.h"
#include "str_util.h"
#include "num_util.h"
#include "compat/misc.h"
#include "main/logging.h"
#include <iostream>
#include <fstream>
#include <string>
#include <unordered_map>
#include <functional>
namespace resource {
umap<std::string, DimensionMode> INIT_VAR(dimension_modes) {
dimension_modes["Uniform"] = DM_Uniform;
dimension_modes["Scaled"] = DM_Scaled;
dimension_modes["Tiled"] = DM_Tiled;
} INIT_VAR_END;
umap<std::string, GradientMode> INIT_VAR(gradient_modes) {
gradient_modes["Normal"] = GM_Normal;
gradient_modes["Overlay"] = GM_Overlay;
} INIT_VAR_END;
umap<std::string, AspectMarginMode> INIT_VAR(aspect_modes) {
aspect_modes["Horizontal"] = AMM_Horizontal;
aspect_modes["Vertical"] = AMM_Vertical;
aspect_modes["None"] = AMM_None;
} INIT_VAR_END;
void parseElementIdentifier(const std::string& str, int& style, unsigned& flags, bool addIfMissing) {
std::vector<std::string> args;
split(str, args, ',');
flags = 0;
style = gui::skin::getStyleIndex(trim(args[0]), addIfMissing);
for(unsigned i = 1; i < args.size(); ++i)
flags |= gui::skin::getElementFlag(trim(args[i]), addIfMissing);
}
unsigned parseElementFlags(const std::string& str, bool addIfMissing) {
std::vector<std::string> args;
split(str, args, ',');
unsigned flags = 0;
for(unsigned i = 0; i < args.size(); ++i)
flags |= gui::skin::getElementFlag(trim(args[i]), addIfMissing);
return flags;
}
#define HANDLE_RELCOORD(handler, key, obj, member, pos) handler(key, [&](std::string& value) {\
if(!obj || obj->member.empty())\
return;\
\
auto& pos = obj->member.back().area.pos;\
\
if(value[value.size() - 1] == '%') {\
value = value.substr(0, value.size() - 1);\
double val = toNumber<double>(value);\
\
if(val < 0)\
pos.set(RPT_Right, 0, -val / 100.0);\
else\
pos.set(RPT_Left, 0, val / 100.0);\
}\
else {\
int val = toNumber<int>(value);\
\
if(val < 0)\
pos.set(RPT_Right, -val, 0.0);\
else\
pos.set(RPT_Left, val, 0.0);\
}\
});
#define HANDLE_GCOLOR(handler, key, obj, member, pos) handler(key, [&](std::string& value) {\
if(!obj || obj->member.empty())\
return;\
obj->member.back().pos = toColor(value);\
});
void Library::loadSkins(const std::string& filename) {
DataHandler datahandler;
gui::skin::Skin* skin = 0;
//Handling for the skin styles file
datahandler("Skin", [&](std::string& value) {
if(isIdentifier(value)) {
skin = new gui::skin::Skin();
skins[value] = skin;
}
else {
skin = 0;
error("Skin '%s' is not a valid identifier", value.c_str());
}
});
datahandler("Material", [&](std::string& value) {
if(!skin)
return;
skin->materialName = value;
});
datahandler("File", [&](std::string& value) {
if(!skin)
return;
std::string filename = devices.mods.resolve(value);
loadSkin(filename, skin);
skin_files[filename] = skin;
if(watch_resources)
watchSkin(filename);
});
datahandler.read(filename);
}
void Library::loadSkin(const std::string& filename, gui::skin::Skin* skin) {
DataHandler skinhandler;
gui::skin::Element* ele = 0;
gui::skin::Style* style = 0;
//Handling for global skin stuff
skinhandler("Color", [&](std::string& value) {
std::vector<std::string> args;
split(value, args, '=');
if(args.size() != 2)
return;
unsigned index = gui::skin::getColorIndex(trim(args[0]), true);
skin->setColor(index, toColor(trim(args[1])));
});
skinhandler("Font", [&](std::string& value) {
std::vector<std::string> args;
split(value, args, '=');
if(args.size() != 2)
return;
unsigned index = gui::skin::getFontIndex(trim(args[0]), true);
skin->setFont(index, &getFont(trim(args[1])));
});
//Handling for styles and elements
skinhandler("Style", [&](std::string& value) {
if(!isIdentifier(value)) {
style = 0;
error("Style '%s' is not a valid identifier", value.c_str());
return;
}
unsigned index = gui::skin::getStyleIndex(value, true);
if(!skin->hasStyle(index)) {
style = new gui::skin::Style();
skin->setStyle(index, style);
}
else {
style = (gui::skin::Style*)&skin->getStyle(index);
}
ele = 0;
});
skinhandler("Element", [&](std::string& value) {
unsigned flags = parseElementFlags(value, true);
if(style) {
ele = style->getExactElement(flags);
if(!ele) {
ele = new gui::skin::Element();
ele->flags = flags;
ele->material = skin->material;
style->addElement(ele);
}
else {
ele->clear();
}
}
else {
ele = new gui::skin::Element();
ele->flags = flags;
}
});
skinhandler("Shape", [&](std::string& value) {
if(!style)
return;
if(value == "Regular")
style->irregular = false;
else
style->irregular = true;
});
skinhandler("Inherit", [&](std::string& value) {
if(!style)
return;
if(ele) {
unsigned oldFlags = ele->flags;
int style;
unsigned flags;
parseElementIdentifier(value, style, flags, false);
if(style == -1)
return;
*ele = skin->getElement(style, flags);
ele->flags = oldFlags;
}
else {
int styleID;
unsigned flags;
parseElementIdentifier(value, styleID, flags, false);
if(styleID == -1)
return;
const gui::skin::Style& old = skin->getStyle(styleID);
style->irregular = old.irregular;
foreach(it, old.elements) {
ele = new gui::skin::Element();
*ele = **it;
style->addElement(ele);
}
ele = 0;
}
});
skinhandler("Rect", [&](std::string& value) {
if(!ele)
return;
sscanf(value.c_str(), " [ %i , %i ] [ %i , %i ]", &ele->area.topLeft.x,
&ele->area.topLeft.y, &ele->area.botRight.x, &ele->area.botRight.y);
});
HANDLE_ENUM(skinhandler, "Horizontal", ele, horizMode, dimension_modes);
HANDLE_ENUM(skinhandler, "Vertical", ele, vertMode, dimension_modes);
HANDLE_ENUM(skinhandler, "AspectMargin", ele, aspectMargin, aspect_modes);
HANDLE_ENUM(skinhandler, "GradientMode", ele, gradMode, gradient_modes);
HANDLE_BOOL(skinhandler, "Filled", ele, filled);
skinhandler("Margin", [&](std::string& value) {
std::vector<std::string> numbers;
split(value, numbers, ',', true);
if(numbers.size() == 4) {
ele->margin = recti(
toNumber<int>(numbers[0]),
toNumber<int>(numbers[1]),
toNumber<int>(numbers[2]),
toNumber<int>(numbers[3]));
}
else if(numbers.size() == 2) {
int x = toNumber<int>(numbers[0]);
int y = toNumber<int>(numbers[1]);
ele->margin = recti(x, y, x, y);
}
else if(numbers.size() == 1) {
int num = toNumber<int>(numbers[0]);
ele->margin = recti(num, num, num, num);
}
else {
error("Margin specifier '%s' invalid.", value.c_str());
}
});
//Handling for gradients
skinhandler("Add Gradient", [&](std::string& value) {
ele->gradients.push_back(gui::skin::Gradient());
});
HANDLE_RELCOORD(skinhandler, "GX1", ele, gradients, left);
HANDLE_RELCOORD(skinhandler, "GY1", ele, gradients, top);
HANDLE_RELCOORD(skinhandler, "GX2", ele, gradients, right);
HANDLE_RELCOORD(skinhandler, "GY2", ele, gradients, bottom);
HANDLE_GCOLOR(skinhandler, "TopLeft", ele, gradients, colors[0]);
HANDLE_GCOLOR(skinhandler, "TopRight", ele, gradients, colors[1]);
HANDLE_GCOLOR(skinhandler, "BotLeft", ele, gradients, colors[2]);
HANDLE_GCOLOR(skinhandler, "BotRight", ele, gradients, colors[3]);
//Handling for layers
skinhandler("Layer", [&](std::string& value) {
if(!ele)
return;
int style;
unsigned flags;
parseElementIdentifier(value, style, flags, false);
if(style == -1)
return;
ele->layers.push_back(gui::skin::Layer());
ele->layers.back().ele = &skin->getElement(style, flags);
});
HANDLE_RELCOORD(skinhandler, "OX1", ele, layers, left);
HANDLE_RELCOORD(skinhandler, "OY1", ele, layers, top);
HANDLE_RELCOORD(skinhandler, "OX2", ele, layers, right);
HANDLE_RELCOORD(skinhandler, "OY2", ele, layers, bottom);
skinhandler("Color Override", [&](std::string& value) {
if(!ele)
return;
if(ele->layers.size() == 0)
return;
ele->layers.back().hasOverride = true;
ele->layers.back().override = toColor(value);
});
//Read styles file
skinhandler.read(filename);
}
void Library::bindSkinMaterials() {
foreach(it, skins) {
const render::RenderState& mat = getMaterial(it->second->materialName);
it->second->material = &mat;
foreach(style, it->second->styles) {
if(*style) {
foreach(ele, (*style)->elements) {
(*ele)->material = &mat;
}
}
}
}
}
};
+217
View File
@@ -0,0 +1,217 @@
#include "main/references.h"
#include "main/logging.h"
#include "main/initialization.h"
#include "resource/library.h"
#include "ISound.h"
#include "ISoundSource.h"
#include "ISoundDevice.h"
#include "SLoadError.h"
#include "files.h"
#include <iostream>
#include <fstream>
#include <string>
#include <unordered_map>
#include <queue>
namespace resource {
struct QueuedSound {
int priority;
Sound* snd;
std::string filename;
float volume;
bool operator<(const QueuedSound& other) const {
return priority < other.priority;
}
};
std::priority_queue<QueuedSound> queuedSounds;
audio::ISound* Sound::play2D(bool loop, bool pause, bool priority) const {
if(!loaded)
return nullptr;
if(source) {
auto* ptr = devices.sound->play2D(source, loop, true, priority);
if(ptr) {
ptr->grab();
if(!pause)
ptr->resume();
}
return ptr;
}
else if(!streamSource.empty()) {
try {
auto* source = devices.sound->loadStreamingSound(streamSource);
if(source) {
source->setDefaultVolume(volume);
audio::ISound* sound = devices.sound->play2D(source, loop, true, priority);
if(sound) {
sound->grab();
if(!pause)
sound->resume();
}
source->drop();
return sound;
}
else {
return nullptr;
}
}
catch(audio::SLoadError) {
return nullptr;
}
}
return nullptr;
}
audio::ISound* Sound::play3D(const vec3d& pos, bool loop, bool pause, bool priority) const {
if(!loaded)
return nullptr;
if(source) {
auto* ptr = devices.sound->play3D(source, snd_vec(pos), loop, true, priority);
if(ptr) {
ptr->grab();
if(!pause)
ptr->resume();
}
return ptr;
}
else if(!streamSource.empty()) {
try {
auto* source = devices.sound->loadStreamingSound(streamSource);
if(source) {
source->setDefaultVolume(volume);
audio::ISound* sound = devices.sound->play3D(source, snd_vec(pos), loop, true, priority);
if(sound) {
sound->grab();
if(!pause)
sound->resume();
}
source->drop();
return sound;
}
else {
return nullptr;
}
}
catch(audio::SLoadError) {
return nullptr;
}
}
return nullptr;
}
bool Library::hasQueuedSounds() {
return !queuedSounds.empty();
}
bool Library::processSounds(int maxPriority, int amount) {
bool processedAny = false;
int i = 0;
while(!queuedSounds.empty()) {
auto elem = queuedSounds.top();
if(elem.priority < maxPriority)
break;
queuedSounds.pop();
processedAny = true;
audio::ISoundSource* source = 0;
try {
source = devices.sound->loadSound(elem.filename.c_str());
if(source) {
source->setDefaultVolume(elem.volume);
elem.snd->source = source;
elem.snd->loaded = true;
}
}
catch(const audio::SLoadError& err) {
error("Could not load sound '%s': %s", elem.filename.c_str(), err.what());
source = 0;
}
++i;
if(i >= amount)
break;
}
return processedAny;
}
void Library::loadSounds(const std::string& filename) {
std::string sound_name, sound_file;
float volume = 1.f;
int priority = -61;
bool streamed = false;
DataHandler datahandler;
auto makeSound = [&]() {
if(sound_name.empty() || sound_file.empty())
return;
Sound* snd = new Sound();
if(load_resources && use_sound) {
if(streamed) {
snd->streamSource = devices.mods.resolve(sound_file);
snd->volume = volume;
if(fileExists(snd->streamSource))
snd->loaded = true;
else
error("Could not locate streaming sound source: '%s'", sound_file.c_str());
}
else {
resource::QueuedSound queued = {priority, snd, devices.mods.resolve(sound_file), volume};
queuedSounds.push(queued);
}
}
sounds[sound_name] = snd;
sound_name.clear();
sound_file.clear();
volume = 1.f;
priority = -61;
streamed = false;
};
datahandler("Sound", [&](std::string& value) {
makeSound();
sound_name = value;
});
datahandler("Stream", [&](std::string& value) {
makeSound();
sound_name = value;
streamed = true;
});
datahandler("File", [&](std::string& value) {
sound_file = value;
});
datahandler("Volume", [&](std::string& value) {
volume = toNumber<float>(value);
});
datahandler("LoadPriority", [&](std::string& value) {
if(streamed)
error("LoadPriority is not valid for streaming sounds");
if(value == "Critical" || value == "Menu")
priority = 10;
else if(value == "Game")
priority = -10;
else
priority = -111 + toNumber<int>(value);
});
datahandler.read(filename);
makeSound();
}
}
+92
View File
@@ -0,0 +1,92 @@
#include "resource/locale.h"
#include "threads.h"
#include "str_util.h"
#include "util/format.h"
#include "main/logging.h"
#include <stdarg.h>
namespace resource {
void Locale::clear() {
foreach(it, localizations)
delete it->second;
localizations.clear();
hashLocalizations.clear();
}
void Locale::load(const std::string& filename) {
DataReader datafile(filename);
while(datafile++) {
if(!datafile.value.empty() && datafile.value[0] == '\"')
datafile.value = datafile.value.substr(1, datafile.value.size() - 2);
if(isIdentifier(datafile.key)) {
std::string* str = new std::string(unescape(datafile.value));
localizations[datafile.key] = str;
std::string withHash = "#";
withHash += datafile.key;
hashLocalizations[withHash] = str;
}
else {
error("Locale key '%s' is not a valid identifier", datafile.key.c_str());
}
}
}
std::string Locale::localize(const std::string& text, bool requireHash, bool doUnescape, bool doFormat) {
bool hasHash = !text.empty() && text[0] == '#';
if(requireHash && !hasHash)
return doUnescape ? unescape(text) : text;
auto pos = doFormat ? text.find(':') : std::string::npos;
if(pos == std::string::npos) {
if(hasHash) {
auto it = hashLocalizations.find(text);
if(it == hashLocalizations.end())
return doUnescape ? unescape(text) : text;
return *it->second;
}
else {
auto it = localizations.find(text);
if(it == localizations.end())
return doUnescape ? unescape(text) : text;
return *it->second;
}
}
else {
std::string result;
std::vector<std::string> arguments;
split(text, arguments, ':', false, true);
if(hasHash) {
auto it = hashLocalizations.find(arguments[0]);
if(it == hashLocalizations.end())
return doUnescape ? unescape(arguments[0]) : arguments[0];
result = *it->second;
}
else {
auto it = localizations.find(arguments[0]);
if(it == localizations.end())
return doUnescape ? unescape(arguments[0]) : arguments[0];
result = *it->second;
}
FormatArg args[16];
unsigned argCnt = arguments.size();
for(unsigned i = 1; i < argCnt; ++i) {
args[i-1].type = FormatArg::Arg_string;
args[i-1].s = &arguments[i];
}
std::string output;
format(output, result.c_str(), argCnt-1, args);
return output;
}
}
Locale::~Locale() {
clear();
}
};
+20
View File
@@ -0,0 +1,20 @@
#pragma once
#include "compat/misc.h"
#include <string>
#include <unordered_map>
namespace resource {
class Locale {
public:
umap<std::string, std::string*> localizations;
umap<std::string, std::string*> hashLocalizations;
void clear();
void load(const std::string& filename);
std::string localize(const std::string& text, bool requireHash = false, bool doUnescape = true, bool doFormat = true);
~Locale();
};
};