mirror of
https://github.com/luanti-org/luanti.git
synced 2025-06-27 16:36:03 +00:00
Move client-specific files to 'src/client' (#7902)
Update Android.mk Remove 'src/client' from include_directories
This commit is contained in:
parent
ddd9317b73
commit
5f1cd555cd
85 changed files with 187 additions and 116 deletions
873
src/shader.cpp
873
src/shader.cpp
|
@ -1,873 +0,0 @@
|
|||
/*
|
||||
Minetest
|
||||
Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
|
||||
Copyright (C) 2013 Kahrl <kahrl@gmx.net>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation; either version 2.1 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License along
|
||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include <fstream>
|
||||
#include <iterator>
|
||||
#include "shader.h"
|
||||
#include "irrlichttypes_extrabloated.h"
|
||||
#include "debug.h"
|
||||
#include "filesys.h"
|
||||
#include "util/container.h"
|
||||
#include "util/thread.h"
|
||||
#include "settings.h"
|
||||
#include <ICameraSceneNode.h>
|
||||
#include <IGPUProgrammingServices.h>
|
||||
#include <IMaterialRenderer.h>
|
||||
#include <IMaterialRendererServices.h>
|
||||
#include <IShaderConstantSetCallBack.h>
|
||||
#include "client/renderingengine.h"
|
||||
#include "EShaderTypes.h"
|
||||
#include "log.h"
|
||||
#include "gamedef.h"
|
||||
#include "client/tile.h"
|
||||
|
||||
/*
|
||||
A cache from shader name to shader path
|
||||
*/
|
||||
MutexedMap<std::string, std::string> g_shadername_to_path_cache;
|
||||
|
||||
/*
|
||||
Gets the path to a shader by first checking if the file
|
||||
name_of_shader/filename
|
||||
exists in shader_path and if not, using the data path.
|
||||
|
||||
If not found, returns "".
|
||||
|
||||
Utilizes a thread-safe cache.
|
||||
*/
|
||||
std::string getShaderPath(const std::string &name_of_shader,
|
||||
const std::string &filename)
|
||||
{
|
||||
std::string combined = name_of_shader + DIR_DELIM + filename;
|
||||
std::string fullpath;
|
||||
/*
|
||||
Check from cache
|
||||
*/
|
||||
bool incache = g_shadername_to_path_cache.get(combined, &fullpath);
|
||||
if(incache)
|
||||
return fullpath;
|
||||
|
||||
/*
|
||||
Check from shader_path
|
||||
*/
|
||||
std::string shader_path = g_settings->get("shader_path");
|
||||
if (!shader_path.empty()) {
|
||||
std::string testpath = shader_path + DIR_DELIM + combined;
|
||||
if(fs::PathExists(testpath))
|
||||
fullpath = testpath;
|
||||
}
|
||||
|
||||
/*
|
||||
Check from default data directory
|
||||
*/
|
||||
if (fullpath.empty()) {
|
||||
std::string rel_path = std::string("client") + DIR_DELIM
|
||||
+ "shaders" + DIR_DELIM
|
||||
+ name_of_shader + DIR_DELIM
|
||||
+ filename;
|
||||
std::string testpath = porting::path_share + DIR_DELIM + rel_path;
|
||||
if(fs::PathExists(testpath))
|
||||
fullpath = testpath;
|
||||
}
|
||||
|
||||
// Add to cache (also an empty result is cached)
|
||||
g_shadername_to_path_cache.set(combined, fullpath);
|
||||
|
||||
// Finally return it
|
||||
return fullpath;
|
||||
}
|
||||
|
||||
/*
|
||||
SourceShaderCache: A cache used for storing source shaders.
|
||||
*/
|
||||
|
||||
class SourceShaderCache
|
||||
{
|
||||
public:
|
||||
void insert(const std::string &name_of_shader, const std::string &filename,
|
||||
const std::string &program, bool prefer_local)
|
||||
{
|
||||
std::string combined = name_of_shader + DIR_DELIM + filename;
|
||||
// Try to use local shader instead if asked to
|
||||
if(prefer_local){
|
||||
std::string path = getShaderPath(name_of_shader, filename);
|
||||
if(!path.empty()){
|
||||
std::string p = readFile(path);
|
||||
if (!p.empty()) {
|
||||
m_programs[combined] = p;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
m_programs[combined] = program;
|
||||
}
|
||||
|
||||
std::string get(const std::string &name_of_shader,
|
||||
const std::string &filename)
|
||||
{
|
||||
std::string combined = name_of_shader + DIR_DELIM + filename;
|
||||
StringMap::iterator n = m_programs.find(combined);
|
||||
if (n != m_programs.end())
|
||||
return n->second;
|
||||
return "";
|
||||
}
|
||||
|
||||
// Primarily fetches from cache, secondarily tries to read from filesystem
|
||||
std::string getOrLoad(const std::string &name_of_shader,
|
||||
const std::string &filename)
|
||||
{
|
||||
std::string combined = name_of_shader + DIR_DELIM + filename;
|
||||
StringMap::iterator n = m_programs.find(combined);
|
||||
if (n != m_programs.end())
|
||||
return n->second;
|
||||
std::string path = getShaderPath(name_of_shader, filename);
|
||||
if (path.empty()) {
|
||||
infostream << "SourceShaderCache::getOrLoad(): No path found for \""
|
||||
<< combined << "\"" << std::endl;
|
||||
return "";
|
||||
}
|
||||
infostream << "SourceShaderCache::getOrLoad(): Loading path \""
|
||||
<< path << "\"" << std::endl;
|
||||
std::string p = readFile(path);
|
||||
if (!p.empty()) {
|
||||
m_programs[combined] = p;
|
||||
return p;
|
||||
}
|
||||
return "";
|
||||
}
|
||||
private:
|
||||
StringMap m_programs;
|
||||
|
||||
std::string readFile(const std::string &path)
|
||||
{
|
||||
std::ifstream is(path.c_str(), std::ios::binary);
|
||||
if(!is.is_open())
|
||||
return "";
|
||||
std::ostringstream tmp_os;
|
||||
tmp_os << is.rdbuf();
|
||||
return tmp_os.str();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
ShaderCallback: Sets constants that can be used in shaders
|
||||
*/
|
||||
|
||||
class ShaderCallback : public video::IShaderConstantSetCallBack
|
||||
{
|
||||
std::vector<IShaderConstantSetter*> m_setters;
|
||||
|
||||
public:
|
||||
ShaderCallback(const std::vector<IShaderConstantSetterFactory *> &factories)
|
||||
{
|
||||
for (IShaderConstantSetterFactory *factory : factories)
|
||||
m_setters.push_back(factory->create());
|
||||
}
|
||||
|
||||
~ShaderCallback()
|
||||
{
|
||||
for (IShaderConstantSetter *setter : m_setters)
|
||||
delete setter;
|
||||
}
|
||||
|
||||
virtual void OnSetConstants(video::IMaterialRendererServices *services, s32 userData)
|
||||
{
|
||||
video::IVideoDriver *driver = services->getVideoDriver();
|
||||
sanity_check(driver != NULL);
|
||||
|
||||
bool is_highlevel = userData;
|
||||
|
||||
for (IShaderConstantSetter *setter : m_setters)
|
||||
setter->onSetConstants(services, is_highlevel);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
MainShaderConstantSetter: Set basic constants required for almost everything
|
||||
*/
|
||||
|
||||
class MainShaderConstantSetter : public IShaderConstantSetter
|
||||
{
|
||||
CachedVertexShaderSetting<float, 16> m_world_view_proj;
|
||||
CachedVertexShaderSetting<float, 16> m_world;
|
||||
|
||||
public:
|
||||
MainShaderConstantSetter() :
|
||||
m_world_view_proj("mWorldViewProj"),
|
||||
m_world("mWorld")
|
||||
{}
|
||||
~MainShaderConstantSetter() = default;
|
||||
|
||||
virtual void onSetConstants(video::IMaterialRendererServices *services,
|
||||
bool is_highlevel)
|
||||
{
|
||||
video::IVideoDriver *driver = services->getVideoDriver();
|
||||
sanity_check(driver);
|
||||
|
||||
// Set clip matrix
|
||||
core::matrix4 worldViewProj;
|
||||
worldViewProj = driver->getTransform(video::ETS_PROJECTION);
|
||||
worldViewProj *= driver->getTransform(video::ETS_VIEW);
|
||||
worldViewProj *= driver->getTransform(video::ETS_WORLD);
|
||||
if (is_highlevel)
|
||||
m_world_view_proj.set(*reinterpret_cast<float(*)[16]>(worldViewProj.pointer()), services);
|
||||
else
|
||||
services->setVertexShaderConstant(worldViewProj.pointer(), 0, 4);
|
||||
|
||||
// Set world matrix
|
||||
core::matrix4 world = driver->getTransform(video::ETS_WORLD);
|
||||
if (is_highlevel)
|
||||
m_world.set(*reinterpret_cast<float(*)[16]>(world.pointer()), services);
|
||||
else
|
||||
services->setVertexShaderConstant(world.pointer(), 4, 4);
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
class MainShaderConstantSetterFactory : public IShaderConstantSetterFactory
|
||||
{
|
||||
public:
|
||||
virtual IShaderConstantSetter* create()
|
||||
{ return new MainShaderConstantSetter(); }
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
ShaderSource
|
||||
*/
|
||||
|
||||
class ShaderSource : public IWritableShaderSource
|
||||
{
|
||||
public:
|
||||
ShaderSource();
|
||||
~ShaderSource();
|
||||
|
||||
/*
|
||||
- If shader material specified by name is found from cache,
|
||||
return the cached id.
|
||||
- Otherwise generate the shader material, add to cache and return id.
|
||||
|
||||
The id 0 points to a null shader. Its material is EMT_SOLID.
|
||||
*/
|
||||
u32 getShaderIdDirect(const std::string &name,
|
||||
const u8 material_type, const u8 drawtype);
|
||||
|
||||
/*
|
||||
If shader specified by the name pointed by the id doesn't
|
||||
exist, create it, then return id.
|
||||
|
||||
Can be called from any thread. If called from some other thread
|
||||
and not found in cache, the call is queued to the main thread
|
||||
for processing.
|
||||
*/
|
||||
|
||||
u32 getShader(const std::string &name,
|
||||
const u8 material_type, const u8 drawtype);
|
||||
|
||||
ShaderInfo getShaderInfo(u32 id);
|
||||
|
||||
// Processes queued shader requests from other threads.
|
||||
// Shall be called from the main thread.
|
||||
void processQueue();
|
||||
|
||||
// Insert a shader program into the cache without touching the
|
||||
// filesystem. Shall be called from the main thread.
|
||||
void insertSourceShader(const std::string &name_of_shader,
|
||||
const std::string &filename, const std::string &program);
|
||||
|
||||
// Rebuild shaders from the current set of source shaders
|
||||
// Shall be called from the main thread.
|
||||
void rebuildShaders();
|
||||
|
||||
void addShaderConstantSetterFactory(IShaderConstantSetterFactory *setter)
|
||||
{
|
||||
m_setter_factories.push_back(setter);
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
// The id of the thread that is allowed to use irrlicht directly
|
||||
std::thread::id m_main_thread;
|
||||
|
||||
// Cache of source shaders
|
||||
// This should be only accessed from the main thread
|
||||
SourceShaderCache m_sourcecache;
|
||||
|
||||
// A shader id is index in this array.
|
||||
// The first position contains a dummy shader.
|
||||
std::vector<ShaderInfo> m_shaderinfo_cache;
|
||||
// The former container is behind this mutex
|
||||
std::mutex m_shaderinfo_cache_mutex;
|
||||
|
||||
// Queued shader fetches (to be processed by the main thread)
|
||||
RequestQueue<std::string, u32, u8, u8> m_get_shader_queue;
|
||||
|
||||
// Global constant setter factories
|
||||
std::vector<IShaderConstantSetterFactory *> m_setter_factories;
|
||||
|
||||
// Shader callbacks
|
||||
std::vector<ShaderCallback *> m_callbacks;
|
||||
};
|
||||
|
||||
IWritableShaderSource *createShaderSource()
|
||||
{
|
||||
return new ShaderSource();
|
||||
}
|
||||
|
||||
/*
|
||||
Generate shader given the shader name.
|
||||
*/
|
||||
ShaderInfo generate_shader(const std::string &name,
|
||||
u8 material_type, u8 drawtype, std::vector<ShaderCallback *> &callbacks,
|
||||
const std::vector<IShaderConstantSetterFactory *> &setter_factories,
|
||||
SourceShaderCache *sourcecache);
|
||||
|
||||
/*
|
||||
Load shader programs
|
||||
*/
|
||||
void load_shaders(const std::string &name, SourceShaderCache *sourcecache,
|
||||
video::E_DRIVER_TYPE drivertype, bool enable_shaders,
|
||||
std::string &vertex_program, std::string &pixel_program,
|
||||
std::string &geometry_program, bool &is_highlevel);
|
||||
|
||||
ShaderSource::ShaderSource()
|
||||
{
|
||||
m_main_thread = std::this_thread::get_id();
|
||||
|
||||
// Add a dummy ShaderInfo as the first index, named ""
|
||||
m_shaderinfo_cache.emplace_back();
|
||||
|
||||
// Add main global constant setter
|
||||
addShaderConstantSetterFactory(new MainShaderConstantSetterFactory());
|
||||
}
|
||||
|
||||
ShaderSource::~ShaderSource()
|
||||
{
|
||||
for (ShaderCallback *callback : m_callbacks) {
|
||||
delete callback;
|
||||
}
|
||||
for (IShaderConstantSetterFactory *setter_factorie : m_setter_factories) {
|
||||
delete setter_factorie;
|
||||
}
|
||||
}
|
||||
|
||||
u32 ShaderSource::getShader(const std::string &name,
|
||||
const u8 material_type, const u8 drawtype)
|
||||
{
|
||||
/*
|
||||
Get shader
|
||||
*/
|
||||
|
||||
if (std::this_thread::get_id() == m_main_thread) {
|
||||
return getShaderIdDirect(name, material_type, drawtype);
|
||||
}
|
||||
|
||||
/*errorstream<<"getShader(): Queued: name=\""<<name<<"\""<<std::endl;*/
|
||||
|
||||
// We're gonna ask the result to be put into here
|
||||
|
||||
static ResultQueue<std::string, u32, u8, u8> result_queue;
|
||||
|
||||
// Throw a request in
|
||||
m_get_shader_queue.add(name, 0, 0, &result_queue);
|
||||
|
||||
/* infostream<<"Waiting for shader from main thread, name=\""
|
||||
<<name<<"\""<<std::endl;*/
|
||||
|
||||
while(true) {
|
||||
GetResult<std::string, u32, u8, u8>
|
||||
result = result_queue.pop_frontNoEx();
|
||||
|
||||
if (result.key == name) {
|
||||
return result.item;
|
||||
}
|
||||
|
||||
errorstream << "Got shader with invalid name: " << result.key << std::endl;
|
||||
}
|
||||
|
||||
infostream << "getShader(): Failed" << std::endl;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
This method generates all the shaders
|
||||
*/
|
||||
u32 ShaderSource::getShaderIdDirect(const std::string &name,
|
||||
const u8 material_type, const u8 drawtype)
|
||||
{
|
||||
//infostream<<"getShaderIdDirect(): name=\""<<name<<"\""<<std::endl;
|
||||
|
||||
// Empty name means shader 0
|
||||
if (name.empty()) {
|
||||
infostream<<"getShaderIdDirect(): name is empty"<<std::endl;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Check if already have such instance
|
||||
for(u32 i=0; i<m_shaderinfo_cache.size(); i++){
|
||||
ShaderInfo *info = &m_shaderinfo_cache[i];
|
||||
if(info->name == name && info->material_type == material_type &&
|
||||
info->drawtype == drawtype)
|
||||
return i;
|
||||
}
|
||||
|
||||
/*
|
||||
Calling only allowed from main thread
|
||||
*/
|
||||
if (std::this_thread::get_id() != m_main_thread) {
|
||||
errorstream<<"ShaderSource::getShaderIdDirect() "
|
||||
"called not from main thread"<<std::endl;
|
||||
return 0;
|
||||
}
|
||||
|
||||
ShaderInfo info = generate_shader(name, material_type, drawtype,
|
||||
m_callbacks, m_setter_factories, &m_sourcecache);
|
||||
|
||||
/*
|
||||
Add shader to caches (add dummy shaders too)
|
||||
*/
|
||||
|
||||
MutexAutoLock lock(m_shaderinfo_cache_mutex);
|
||||
|
||||
u32 id = m_shaderinfo_cache.size();
|
||||
m_shaderinfo_cache.push_back(info);
|
||||
|
||||
infostream<<"getShaderIdDirect(): "
|
||||
<<"Returning id="<<id<<" for name \""<<name<<"\""<<std::endl;
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
|
||||
ShaderInfo ShaderSource::getShaderInfo(u32 id)
|
||||
{
|
||||
MutexAutoLock lock(m_shaderinfo_cache_mutex);
|
||||
|
||||
if(id >= m_shaderinfo_cache.size())
|
||||
return ShaderInfo();
|
||||
|
||||
return m_shaderinfo_cache[id];
|
||||
}
|
||||
|
||||
void ShaderSource::processQueue()
|
||||
{
|
||||
|
||||
|
||||
}
|
||||
|
||||
void ShaderSource::insertSourceShader(const std::string &name_of_shader,
|
||||
const std::string &filename, const std::string &program)
|
||||
{
|
||||
/*infostream<<"ShaderSource::insertSourceShader(): "
|
||||
"name_of_shader=\""<<name_of_shader<<"\", "
|
||||
"filename=\""<<filename<<"\""<<std::endl;*/
|
||||
|
||||
sanity_check(std::this_thread::get_id() == m_main_thread);
|
||||
|
||||
m_sourcecache.insert(name_of_shader, filename, program, true);
|
||||
}
|
||||
|
||||
void ShaderSource::rebuildShaders()
|
||||
{
|
||||
MutexAutoLock lock(m_shaderinfo_cache_mutex);
|
||||
|
||||
/*// Oh well... just clear everything, they'll load sometime.
|
||||
m_shaderinfo_cache.clear();
|
||||
m_name_to_id.clear();*/
|
||||
|
||||
/*
|
||||
FIXME: Old shader materials can't be deleted in Irrlicht,
|
||||
or can they?
|
||||
(This would be nice to do in the destructor too)
|
||||
*/
|
||||
|
||||
// Recreate shaders
|
||||
for (ShaderInfo &i : m_shaderinfo_cache) {
|
||||
ShaderInfo *info = &i;
|
||||
if (!info->name.empty()) {
|
||||
*info = generate_shader(info->name, info->material_type,
|
||||
info->drawtype, m_callbacks,
|
||||
m_setter_factories, &m_sourcecache);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
ShaderInfo generate_shader(const std::string &name, u8 material_type, u8 drawtype,
|
||||
std::vector<ShaderCallback *> &callbacks,
|
||||
const std::vector<IShaderConstantSetterFactory *> &setter_factories,
|
||||
SourceShaderCache *sourcecache)
|
||||
{
|
||||
ShaderInfo shaderinfo;
|
||||
shaderinfo.name = name;
|
||||
shaderinfo.material_type = material_type;
|
||||
shaderinfo.drawtype = drawtype;
|
||||
shaderinfo.material = video::EMT_SOLID;
|
||||
switch (material_type) {
|
||||
case TILE_MATERIAL_OPAQUE:
|
||||
case TILE_MATERIAL_LIQUID_OPAQUE:
|
||||
shaderinfo.base_material = video::EMT_SOLID;
|
||||
break;
|
||||
case TILE_MATERIAL_ALPHA:
|
||||
case TILE_MATERIAL_LIQUID_TRANSPARENT:
|
||||
shaderinfo.base_material = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
|
||||
break;
|
||||
case TILE_MATERIAL_BASIC:
|
||||
case TILE_MATERIAL_WAVING_LEAVES:
|
||||
case TILE_MATERIAL_WAVING_PLANTS:
|
||||
shaderinfo.base_material = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
|
||||
break;
|
||||
}
|
||||
|
||||
bool enable_shaders = g_settings->getBool("enable_shaders");
|
||||
if (!enable_shaders)
|
||||
return shaderinfo;
|
||||
|
||||
video::IVideoDriver *driver = RenderingEngine::get_video_driver();
|
||||
|
||||
video::IGPUProgrammingServices *gpu = driver->getGPUProgrammingServices();
|
||||
if(!gpu){
|
||||
errorstream<<"generate_shader(): "
|
||||
"failed to generate \""<<name<<"\", "
|
||||
"GPU programming not supported."
|
||||
<<std::endl;
|
||||
return shaderinfo;
|
||||
}
|
||||
|
||||
// Choose shader language depending on driver type and settings
|
||||
// Then load shaders
|
||||
std::string vertex_program;
|
||||
std::string pixel_program;
|
||||
std::string geometry_program;
|
||||
bool is_highlevel;
|
||||
load_shaders(name, sourcecache, driver->getDriverType(),
|
||||
enable_shaders, vertex_program, pixel_program,
|
||||
geometry_program, is_highlevel);
|
||||
// Check hardware/driver support
|
||||
if (!vertex_program.empty() &&
|
||||
!driver->queryFeature(video::EVDF_VERTEX_SHADER_1_1) &&
|
||||
!driver->queryFeature(video::EVDF_ARB_VERTEX_PROGRAM_1)){
|
||||
infostream<<"generate_shader(): vertex shaders disabled "
|
||||
"because of missing driver/hardware support."
|
||||
<<std::endl;
|
||||
vertex_program = "";
|
||||
}
|
||||
if (!pixel_program.empty() &&
|
||||
!driver->queryFeature(video::EVDF_PIXEL_SHADER_1_1) &&
|
||||
!driver->queryFeature(video::EVDF_ARB_FRAGMENT_PROGRAM_1)){
|
||||
infostream<<"generate_shader(): pixel shaders disabled "
|
||||
"because of missing driver/hardware support."
|
||||
<<std::endl;
|
||||
pixel_program = "";
|
||||
}
|
||||
if (!geometry_program.empty() &&
|
||||
!driver->queryFeature(video::EVDF_GEOMETRY_SHADER)){
|
||||
infostream<<"generate_shader(): geometry shaders disabled "
|
||||
"because of missing driver/hardware support."
|
||||
<<std::endl;
|
||||
geometry_program = "";
|
||||
}
|
||||
|
||||
// If no shaders are used, don't make a separate material type
|
||||
if (vertex_program.empty() && pixel_program.empty() && geometry_program.empty())
|
||||
return shaderinfo;
|
||||
|
||||
// Create shaders header
|
||||
std::string shaders_header = "#version 120\n";
|
||||
|
||||
static const char* drawTypes[] = {
|
||||
"NDT_NORMAL",
|
||||
"NDT_AIRLIKE",
|
||||
"NDT_LIQUID",
|
||||
"NDT_FLOWINGLIQUID",
|
||||
"NDT_GLASSLIKE",
|
||||
"NDT_ALLFACES",
|
||||
"NDT_ALLFACES_OPTIONAL",
|
||||
"NDT_TORCHLIKE",
|
||||
"NDT_SIGNLIKE",
|
||||
"NDT_PLANTLIKE",
|
||||
"NDT_FENCELIKE",
|
||||
"NDT_RAILLIKE",
|
||||
"NDT_NODEBOX",
|
||||
"NDT_GLASSLIKE_FRAMED",
|
||||
"NDT_FIRELIKE",
|
||||
"NDT_GLASSLIKE_FRAMED_OPTIONAL",
|
||||
"NDT_PLANTLIKE_ROOTED",
|
||||
};
|
||||
|
||||
for (int i = 0; i < 14; i++){
|
||||
shaders_header += "#define ";
|
||||
shaders_header += drawTypes[i];
|
||||
shaders_header += " ";
|
||||
shaders_header += itos(i);
|
||||
shaders_header += "\n";
|
||||
}
|
||||
|
||||
static const char* materialTypes[] = {
|
||||
"TILE_MATERIAL_BASIC",
|
||||
"TILE_MATERIAL_ALPHA",
|
||||
"TILE_MATERIAL_LIQUID_TRANSPARENT",
|
||||
"TILE_MATERIAL_LIQUID_OPAQUE",
|
||||
"TILE_MATERIAL_WAVING_LEAVES",
|
||||
"TILE_MATERIAL_WAVING_PLANTS",
|
||||
"TILE_MATERIAL_OPAQUE"
|
||||
};
|
||||
|
||||
for (int i = 0; i < 7; i++){
|
||||
shaders_header += "#define ";
|
||||
shaders_header += materialTypes[i];
|
||||
shaders_header += " ";
|
||||
shaders_header += itos(i);
|
||||
shaders_header += "\n";
|
||||
}
|
||||
|
||||
shaders_header += "#define MATERIAL_TYPE ";
|
||||
shaders_header += itos(material_type);
|
||||
shaders_header += "\n";
|
||||
shaders_header += "#define DRAW_TYPE ";
|
||||
shaders_header += itos(drawtype);
|
||||
shaders_header += "\n";
|
||||
|
||||
if (g_settings->getBool("generate_normalmaps")) {
|
||||
shaders_header += "#define GENERATE_NORMALMAPS 1\n";
|
||||
} else {
|
||||
shaders_header += "#define GENERATE_NORMALMAPS 0\n";
|
||||
}
|
||||
shaders_header += "#define NORMALMAPS_STRENGTH ";
|
||||
shaders_header += ftos(g_settings->getFloat("normalmaps_strength"));
|
||||
shaders_header += "\n";
|
||||
float sample_step;
|
||||
int smooth = (int)g_settings->getFloat("normalmaps_smooth");
|
||||
switch (smooth){
|
||||
case 0:
|
||||
sample_step = 0.0078125; // 1.0 / 128.0
|
||||
break;
|
||||
case 1:
|
||||
sample_step = 0.00390625; // 1.0 / 256.0
|
||||
break;
|
||||
case 2:
|
||||
sample_step = 0.001953125; // 1.0 / 512.0
|
||||
break;
|
||||
default:
|
||||
sample_step = 0.0078125;
|
||||
break;
|
||||
}
|
||||
shaders_header += "#define SAMPLE_STEP ";
|
||||
shaders_header += ftos(sample_step);
|
||||
shaders_header += "\n";
|
||||
|
||||
if (g_settings->getBool("enable_bumpmapping"))
|
||||
shaders_header += "#define ENABLE_BUMPMAPPING\n";
|
||||
|
||||
if (g_settings->getBool("enable_parallax_occlusion")){
|
||||
int mode = g_settings->getFloat("parallax_occlusion_mode");
|
||||
float scale = g_settings->getFloat("parallax_occlusion_scale");
|
||||
float bias = g_settings->getFloat("parallax_occlusion_bias");
|
||||
int iterations = g_settings->getFloat("parallax_occlusion_iterations");
|
||||
shaders_header += "#define ENABLE_PARALLAX_OCCLUSION\n";
|
||||
shaders_header += "#define PARALLAX_OCCLUSION_MODE ";
|
||||
shaders_header += itos(mode);
|
||||
shaders_header += "\n";
|
||||
shaders_header += "#define PARALLAX_OCCLUSION_SCALE ";
|
||||
shaders_header += ftos(scale);
|
||||
shaders_header += "\n";
|
||||
shaders_header += "#define PARALLAX_OCCLUSION_BIAS ";
|
||||
shaders_header += ftos(bias);
|
||||
shaders_header += "\n";
|
||||
shaders_header += "#define PARALLAX_OCCLUSION_ITERATIONS ";
|
||||
shaders_header += itos(iterations);
|
||||
shaders_header += "\n";
|
||||
}
|
||||
|
||||
shaders_header += "#define USE_NORMALMAPS ";
|
||||
if (g_settings->getBool("enable_bumpmapping") || g_settings->getBool("enable_parallax_occlusion"))
|
||||
shaders_header += "1\n";
|
||||
else
|
||||
shaders_header += "0\n";
|
||||
|
||||
if (g_settings->getBool("enable_waving_water")){
|
||||
shaders_header += "#define ENABLE_WAVING_WATER 1\n";
|
||||
shaders_header += "#define WATER_WAVE_HEIGHT ";
|
||||
shaders_header += ftos(g_settings->getFloat("water_wave_height"));
|
||||
shaders_header += "\n";
|
||||
shaders_header += "#define WATER_WAVE_LENGTH ";
|
||||
shaders_header += ftos(g_settings->getFloat("water_wave_length"));
|
||||
shaders_header += "\n";
|
||||
shaders_header += "#define WATER_WAVE_SPEED ";
|
||||
shaders_header += ftos(g_settings->getFloat("water_wave_speed"));
|
||||
shaders_header += "\n";
|
||||
} else{
|
||||
shaders_header += "#define ENABLE_WAVING_WATER 0\n";
|
||||
}
|
||||
|
||||
shaders_header += "#define ENABLE_WAVING_LEAVES ";
|
||||
if (g_settings->getBool("enable_waving_leaves"))
|
||||
shaders_header += "1\n";
|
||||
else
|
||||
shaders_header += "0\n";
|
||||
|
||||
shaders_header += "#define ENABLE_WAVING_PLANTS ";
|
||||
if (g_settings->getBool("enable_waving_plants"))
|
||||
shaders_header += "1\n";
|
||||
else
|
||||
shaders_header += "0\n";
|
||||
|
||||
if (g_settings->getBool("tone_mapping"))
|
||||
shaders_header += "#define ENABLE_TONE_MAPPING\n";
|
||||
|
||||
shaders_header += "#define FOG_START ";
|
||||
shaders_header += ftos(rangelim(g_settings->getFloat("fog_start"), 0.0f, 0.99f));
|
||||
shaders_header += "\n";
|
||||
|
||||
// Call addHighLevelShaderMaterial() or addShaderMaterial()
|
||||
const c8* vertex_program_ptr = 0;
|
||||
const c8* pixel_program_ptr = 0;
|
||||
const c8* geometry_program_ptr = 0;
|
||||
if (!vertex_program.empty()) {
|
||||
vertex_program = shaders_header + vertex_program;
|
||||
vertex_program_ptr = vertex_program.c_str();
|
||||
}
|
||||
if (!pixel_program.empty()) {
|
||||
pixel_program = shaders_header + pixel_program;
|
||||
pixel_program_ptr = pixel_program.c_str();
|
||||
}
|
||||
if (!geometry_program.empty()) {
|
||||
geometry_program = shaders_header + geometry_program;
|
||||
geometry_program_ptr = geometry_program.c_str();
|
||||
}
|
||||
ShaderCallback *cb = new ShaderCallback(setter_factories);
|
||||
s32 shadermat = -1;
|
||||
if(is_highlevel){
|
||||
infostream<<"Compiling high level shaders for "<<name<<std::endl;
|
||||
shadermat = gpu->addHighLevelShaderMaterial(
|
||||
vertex_program_ptr, // Vertex shader program
|
||||
"vertexMain", // Vertex shader entry point
|
||||
video::EVST_VS_1_1, // Vertex shader version
|
||||
pixel_program_ptr, // Pixel shader program
|
||||
"pixelMain", // Pixel shader entry point
|
||||
video::EPST_PS_1_2, // Pixel shader version
|
||||
geometry_program_ptr, // Geometry shader program
|
||||
"geometryMain", // Geometry shader entry point
|
||||
video::EGST_GS_4_0, // Geometry shader version
|
||||
scene::EPT_TRIANGLES, // Geometry shader input
|
||||
scene::EPT_TRIANGLE_STRIP, // Geometry shader output
|
||||
0, // Support maximum number of vertices
|
||||
cb, // Set-constant callback
|
||||
shaderinfo.base_material, // Base material
|
||||
1 // Userdata passed to callback
|
||||
);
|
||||
if(shadermat == -1){
|
||||
errorstream<<"generate_shader(): "
|
||||
"failed to generate \""<<name<<"\", "
|
||||
"addHighLevelShaderMaterial failed."
|
||||
<<std::endl;
|
||||
dumpShaderProgram(warningstream, "Vertex", vertex_program);
|
||||
dumpShaderProgram(warningstream, "Pixel", pixel_program);
|
||||
dumpShaderProgram(warningstream, "Geometry", geometry_program);
|
||||
delete cb;
|
||||
return shaderinfo;
|
||||
}
|
||||
}
|
||||
else{
|
||||
infostream<<"Compiling assembly shaders for "<<name<<std::endl;
|
||||
shadermat = gpu->addShaderMaterial(
|
||||
vertex_program_ptr, // Vertex shader program
|
||||
pixel_program_ptr, // Pixel shader program
|
||||
cb, // Set-constant callback
|
||||
shaderinfo.base_material, // Base material
|
||||
0 // Userdata passed to callback
|
||||
);
|
||||
|
||||
if(shadermat == -1){
|
||||
errorstream<<"generate_shader(): "
|
||||
"failed to generate \""<<name<<"\", "
|
||||
"addShaderMaterial failed."
|
||||
<<std::endl;
|
||||
dumpShaderProgram(warningstream, "Vertex", vertex_program);
|
||||
dumpShaderProgram(warningstream,"Pixel", pixel_program);
|
||||
delete cb;
|
||||
return shaderinfo;
|
||||
}
|
||||
}
|
||||
callbacks.push_back(cb);
|
||||
|
||||
// HACK, TODO: investigate this better
|
||||
// Grab the material renderer once more so minetest doesn't crash on exit
|
||||
driver->getMaterialRenderer(shadermat)->grab();
|
||||
|
||||
// Apply the newly created material type
|
||||
shaderinfo.material = (video::E_MATERIAL_TYPE) shadermat;
|
||||
return shaderinfo;
|
||||
}
|
||||
|
||||
void load_shaders(const std::string &name, SourceShaderCache *sourcecache,
|
||||
video::E_DRIVER_TYPE drivertype, bool enable_shaders,
|
||||
std::string &vertex_program, std::string &pixel_program,
|
||||
std::string &geometry_program, bool &is_highlevel)
|
||||
{
|
||||
vertex_program = "";
|
||||
pixel_program = "";
|
||||
geometry_program = "";
|
||||
is_highlevel = false;
|
||||
|
||||
if(enable_shaders){
|
||||
// Look for high level shaders
|
||||
if(drivertype == video::EDT_DIRECT3D9){
|
||||
// Direct3D 9: HLSL
|
||||
// (All shaders in one file)
|
||||
vertex_program = sourcecache->getOrLoad(name, "d3d9.hlsl");
|
||||
pixel_program = vertex_program;
|
||||
geometry_program = vertex_program;
|
||||
}
|
||||
else if(drivertype == video::EDT_OPENGL){
|
||||
// OpenGL: GLSL
|
||||
vertex_program = sourcecache->getOrLoad(name, "opengl_vertex.glsl");
|
||||
pixel_program = sourcecache->getOrLoad(name, "opengl_fragment.glsl");
|
||||
geometry_program = sourcecache->getOrLoad(name, "opengl_geometry.glsl");
|
||||
}
|
||||
if (!vertex_program.empty() || !pixel_program.empty() || !geometry_program.empty()){
|
||||
is_highlevel = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void dumpShaderProgram(std::ostream &output_stream,
|
||||
const std::string &program_type, const std::string &program)
|
||||
{
|
||||
output_stream << program_type << " shader program:" << std::endl <<
|
||||
"----------------------------------" << std::endl;
|
||||
size_t pos = 0;
|
||||
size_t prev = 0;
|
||||
s16 line = 1;
|
||||
while ((pos = program.find('\n', prev)) != std::string::npos) {
|
||||
output_stream << line++ << ": "<< program.substr(prev, pos - prev) <<
|
||||
std::endl;
|
||||
prev = pos + 1;
|
||||
}
|
||||
output_stream << line << ": " << program.substr(prev) << std::endl <<
|
||||
"End of " << program_type << " shader program." << std::endl <<
|
||||
" " << std::endl;
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue