Provide a default material for VoxelMesherTransvoxel with variable-lod
parent
5a05854fde
commit
d9b74359f8
|
@ -0,0 +1,57 @@
|
|||
#include "transvoxel_shader_minimal.h"
|
||||
|
||||
namespace zylann::voxel::transvoxel_shader_minimal {
|
||||
|
||||
// clang-format off
|
||||
const char *GDSHADER_SOURCE =
|
||||
"shader_type spatial;\n"
|
||||
"\n"
|
||||
"// From Voxel Tools API\n"
|
||||
"uniform int u_transition_mask;\n"
|
||||
"\n"
|
||||
"float get_transvoxel_secondary_factor(int idata) {\n"
|
||||
" int cell_border_mask = (idata >> 0) & 63; // Which sides the cell is touching\n"
|
||||
" int vertex_border_mask = (idata >> 8) & 63; // Which sides the vertex is touching\n"
|
||||
" // If the vertex is near a side where there is a low-resolution neighbor,\n"
|
||||
" // move it to secondary position\n"
|
||||
" int m = u_transition_mask & cell_border_mask;\n"
|
||||
" float t = float(m != 0);\n"
|
||||
" // If the vertex lies on one or more sides, and at least one side has no low-resolution neighbor,\n"
|
||||
" // don't move the vertex.\n"
|
||||
" t *= float((vertex_border_mask & ~u_transition_mask) == 0);\n"
|
||||
" \n"
|
||||
" // Debugging\n"
|
||||
" //t *= 0.5 + 0.5 * sin(TIME * 4.0);\n"
|
||||
" //t *= 2.0;\n"
|
||||
"\n"
|
||||
" return t;\n"
|
||||
"}\n"
|
||||
"\n"
|
||||
"vec3 get_transvoxel_position(vec3 vertex_pos, vec4 fdata) {\n"
|
||||
" int idata = floatBitsToInt(fdata.a);\n"
|
||||
"\n"
|
||||
" // Move vertices to smooth transitions\n"
|
||||
" float secondary_factor = get_transvoxel_secondary_factor(idata);\n"
|
||||
" vec3 secondary_position = fdata.xyz;\n"
|
||||
" vec3 pos = mix(vertex_pos, secondary_position, secondary_factor);\n"
|
||||
"\n"
|
||||
" // If the mesh combines transitions and the vertex belongs to a transition,\n"
|
||||
" // when that transition isn't active we change the position of the vertices so\n"
|
||||
" // all triangles will be degenerate and won't be visible.\n"
|
||||
" // This is an alternative to rendering them separately,\n"
|
||||
" // which has less draw calls and less mesh resources to create in Godot.\n"
|
||||
" // Ideally I would tweak the index buffer like LOD does but Godot does not\n"
|
||||
" // expose anything to use it that way.\n"
|
||||
" int itransition = (idata >> 16) & 0xff; // Is the vertex on a transition mesh?\n"
|
||||
" float transition_cull = float(itransition == 0 || (itransition & u_transition_mask) != 0);\n"
|
||||
" pos *= transition_cull;\n"
|
||||
"\n"
|
||||
" return pos;\n"
|
||||
"}\n"
|
||||
"\n"
|
||||
"void vertex() {\n"
|
||||
" VERTEX = get_transvoxel_position(VERTEX, CUSTOM0);\n"
|
||||
"}\n"
|
||||
"\n";
|
||||
|
||||
} // namespace zylann::voxel::transvoxel_shader_minimal
|
|
@ -0,0 +1,10 @@
|
|||
#ifndef VOXEL_TRANSVOXEL_SHADER_MINIMAL_H
|
||||
#define VOXEL_TRANSVOXEL_SHADER_MINIMAL_H
|
||||
|
||||
namespace zylann::voxel::transvoxel_shader_minimal {
|
||||
|
||||
extern const char *GDSHADER_SOURCE;
|
||||
|
||||
} // namespace zylann::voxel::transvoxel_shader_minimal
|
||||
|
||||
#endif // VOXEL_TRANSVOXEL_SHADER_MINIMAL_H
|
|
@ -5,10 +5,27 @@
|
|||
#include "../../thirdparty/meshoptimizer/meshoptimizer.h"
|
||||
#include "../../util/godot/funcs.h"
|
||||
#include "../../util/profiling.h"
|
||||
#include "transvoxel_shader_minimal.h"
|
||||
#include "transvoxel_tables.cpp"
|
||||
|
||||
namespace zylann::voxel {
|
||||
|
||||
namespace {
|
||||
Ref<ShaderMaterial> g_minimal_shader_material;
|
||||
}
|
||||
|
||||
void VoxelMesherTransvoxel::load_static_resources() {
|
||||
Ref<Shader> shader;
|
||||
shader.instantiate();
|
||||
shader->set_code(transvoxel_shader_minimal::GDSHADER_SOURCE);
|
||||
g_minimal_shader_material.instantiate();
|
||||
g_minimal_shader_material->set_shader(shader);
|
||||
}
|
||||
|
||||
void VoxelMesherTransvoxel::free_static_resources() {
|
||||
g_minimal_shader_material.unref();
|
||||
}
|
||||
|
||||
VoxelMesherTransvoxel::VoxelMesherTransvoxel() {
|
||||
set_padding(transvoxel::MIN_PADDING, transvoxel::MAX_PADDING);
|
||||
}
|
||||
|
@ -340,6 +357,10 @@ bool VoxelMesherTransvoxel::get_transitions_enabled() const {
|
|||
return _transitions_enabled;
|
||||
}
|
||||
|
||||
Ref<ShaderMaterial> VoxelMesherTransvoxel::get_default_lod_material() const {
|
||||
return g_minimal_shader_material;
|
||||
}
|
||||
|
||||
void VoxelMesherTransvoxel::_bind_methods() {
|
||||
ClassDB::bind_method(D_METHOD("build_transition_mesh", "voxel_buffer", "direction"),
|
||||
&VoxelMesherTransvoxel::build_transition_mesh);
|
||||
|
|
|
@ -50,6 +50,11 @@ public:
|
|||
void set_transitions_enabled(bool enable);
|
||||
bool get_transitions_enabled() const;
|
||||
|
||||
Ref<ShaderMaterial> get_default_lod_material() const override;
|
||||
|
||||
static void load_static_resources();
|
||||
static void free_static_resources();
|
||||
|
||||
// Not sure if that's necessary, currently transitions are either combined or not generated
|
||||
// enum TransitionMode {
|
||||
// // No transition meshes will be generated
|
||||
|
|
|
@ -103,15 +103,25 @@ public:
|
|||
virtual Ref<Material> get_material_by_index(unsigned int i) const;
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
// If the mesher has problems, messages may be returned by this method so they can be shown to the user.
|
||||
virtual void get_configuration_warnings(TypedArray<String> &out_warnings) const {}
|
||||
#endif
|
||||
|
||||
// Returns `true` if the mesher generates a separate mesh for collisions.
|
||||
// Returns `true` if the mesher generates specific data for mesh collisions, which will be found in
|
||||
// `CollisionSurface`.
|
||||
// If `false`, the rendering mesh may be used as collider.
|
||||
virtual bool is_generating_collision_surface() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Gets a special default material to be used to render meshes produced with this mesher, when variable level of
|
||||
// detail is used. If null, standard materials or default Godot shaders can be used. This is mostly to provide a
|
||||
// default shader that looks ok. Users are still expected to tweak them if need be.
|
||||
// Such material is not meant to be modified.
|
||||
virtual Ref<ShaderMaterial> get_default_lod_material() const {
|
||||
return Ref<ShaderMaterial>();
|
||||
}
|
||||
|
||||
protected:
|
||||
static void _bind_methods();
|
||||
|
||||
|
|
|
@ -136,6 +136,8 @@ void initialize_voxel_module(ModuleInitializationLevel p_level) {
|
|||
VoxelMetadataFactory::get_singleton().add_constructor_by_type<gd::VoxelMetadataVariant>(
|
||||
gd::METADATA_TYPE_VARIANT);
|
||||
|
||||
VoxelMesherTransvoxel::load_static_resources();
|
||||
|
||||
// TODO Can I prevent users from instancing it? is "register_virtual_class" correct for a class that's not
|
||||
// abstract?
|
||||
ClassDB::register_class<gd::VoxelServer>();
|
||||
|
@ -266,6 +268,7 @@ void uninitialize_voxel_module(ModuleInitializationLevel p_level) {
|
|||
// users can write custom generators, which run inside threads, and these threads are hosted in the server...
|
||||
// See https://github.com/Zylann/godot_voxel/issues/189
|
||||
|
||||
VoxelMesherTransvoxel::free_static_resources();
|
||||
VoxelStringNames::destroy_singleton();
|
||||
VoxelGraphNodeDB::destroy_singleton();
|
||||
gd::VoxelServer::destroy_singleton();
|
||||
|
|
|
@ -177,6 +177,24 @@ Ref<Material> VoxelLodTerrain::get_material() const {
|
|||
void VoxelLodTerrain::set_material(Ref<Material> p_material) {
|
||||
// TODO Update existing block surfaces
|
||||
_material = p_material;
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
// Create a fork of the default shader if a new empty ShaderMaterial is assigned
|
||||
if (Engine::get_singleton()->is_editor_hint()) {
|
||||
Ref<ShaderMaterial> sm = p_material;
|
||||
if (sm.is_valid() && sm->get_shader().is_null() && _mesher.is_valid()) {
|
||||
Ref<ShaderMaterial> default_sm = _mesher->get_default_lod_material();
|
||||
if (default_sm.is_valid()) {
|
||||
Ref<Shader> default_shader = default_sm->get_shader();
|
||||
ZN_ASSERT_RETURN(default_shader.is_valid());
|
||||
Ref<Shader> shader_copy = default_shader->duplicate();
|
||||
sm->set_shader(shader_copy);
|
||||
}
|
||||
}
|
||||
|
||||
update_configuration_warnings();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
unsigned int VoxelLodTerrain::get_data_block_size() const {
|
||||
|
@ -1503,6 +1521,14 @@ void VoxelLodTerrain::apply_mesh_update(VoxelServer::BlockMeshOutput &ob) {
|
|||
block->set_world(get_world_3d());
|
||||
|
||||
Ref<ShaderMaterial> shader_material = _material;
|
||||
|
||||
if (_material.is_null()) {
|
||||
Ref<ShaderMaterial> default_material = _mesher->get_default_lod_material();
|
||||
if (default_material.is_valid()) {
|
||||
shader_material = default_material;
|
||||
}
|
||||
}
|
||||
|
||||
if (shader_material.is_valid() && block->get_shader_material().is_null()) {
|
||||
ZN_PROFILE_SCOPE_NAMED("Add ShaderMaterial");
|
||||
|
||||
|
@ -1859,8 +1885,22 @@ TypedArray<String> VoxelLodTerrain::get_configuration_warnings() const {
|
|||
return warnings;
|
||||
}
|
||||
Ref<VoxelMesher> mesher = get_mesher();
|
||||
if (mesher.is_valid() && !mesher->supports_lod()) {
|
||||
warnings.append(TTR("The assigned mesher does not support level of detail (LOD), results may be unexpected."));
|
||||
if (mesher.is_valid()) {
|
||||
if (!mesher->supports_lod()) {
|
||||
warnings.append(
|
||||
TTR("The assigned mesher ({0}) does not support level of detail (LOD), results may be unexpected.")
|
||||
.format(varray(mesher->get_class())));
|
||||
}
|
||||
if (_material.is_valid() && mesher->get_default_lod_material().is_valid()) {
|
||||
Ref<ShaderMaterial> sm = _material;
|
||||
if (sm.is_null()) {
|
||||
warnings.append(
|
||||
TTR("The current mesher ({0}) requires custom shader code to render properly. The current "
|
||||
"material might not be appropriate. Hint: you can assign a newly created {1} to fork the "
|
||||
"default shader.")
|
||||
.format(varray(mesher->get_class(), ShaderMaterial::get_class_static())));
|
||||
}
|
||||
}
|
||||
}
|
||||
return warnings;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue