Skip to content

Commit e8b467a

Browse files
committed
Cross-section rendering pipeline
Pipes all the right data to the GPU, still need to implement the actual cross-section logic. - Added a new rendering engine, CrossSectionRenderingEngine4D - Added method to Mesh4D to compute a special cross-section mesh that can be cross-sectioned in a vertex shader. - Added a method to Material4D to get a ShaderMaterial used for cross-section rendering
1 parent 6b2e3ae commit e8b467a

File tree

11 files changed

+379
-2
lines changed

11 files changed

+379
-2
lines changed

model/material_4d.cpp

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#include "material_4d.h"
22

3+
#include "../render/cross_section/cross_section_shader.glsl.gen.h"
34
#include "mesh_4d.h"
45

56
Color Material4D::get_albedo_color_of_edge(const int64_t p_edge_index, const Ref<Mesh4D> &p_for_mesh) {
@@ -112,6 +113,7 @@ Material4D::ColorSourceFlags Material4D::get_albedo_source_flags() const {
112113

113114
void Material4D::set_albedo_source_flags(const ColorSourceFlags p_albedo_source_flags) {
114115
_albedo_source_flags = p_albedo_source_flags;
116+
update_cross_section_material();
115117
}
116118

117119
Color Material4D::get_albedo_color() const {
@@ -120,6 +122,7 @@ Color Material4D::get_albedo_color() const {
120122

121123
void Material4D::set_albedo_color(const Color &p_albedo_color) {
122124
_albedo_color = p_albedo_color;
125+
update_cross_section_material();
123126
}
124127

125128
PackedColorArray Material4D::get_albedo_color_array() const {
@@ -128,10 +131,12 @@ PackedColorArray Material4D::get_albedo_color_array() const {
128131

129132
void Material4D::set_albedo_color_array(const PackedColorArray &p_albedo_color_array) {
130133
_albedo_color_array = p_albedo_color_array;
134+
update_cross_section_material();
131135
}
132136

133137
void Material4D::append_albedo_color(const Color &p_albedo_color) {
134138
_albedo_color_array.push_back(p_albedo_color);
139+
update_cross_section_material();
135140
}
136141

137142
void Material4D::resize_albedo_color_array(const int64_t p_size, const Color &p_fill_color) {
@@ -140,6 +145,30 @@ void Material4D::resize_albedo_color_array(const int64_t p_size, const Color &p_
140145
for (int64_t i = existing_size; i < p_size; i++) {
141146
_albedo_color_array.set(i, p_fill_color);
142147
}
148+
update_cross_section_material();
149+
}
150+
151+
Ref<ShaderMaterial> Material4D::get_cross_section_material() {
152+
if (_cross_section_material.is_null()) {
153+
_cross_section_material.instantiate();
154+
update_cross_section_material();
155+
}
156+
return _cross_section_material;
157+
}
158+
159+
void Material4D::update_cross_section_material() {
160+
if (_cross_section_material.is_null()) {
161+
// Want to skip updating if we never access the cross section material, so leave uninitialized until first get.
162+
return;
163+
}
164+
if (_cross_section_material->get_shader().is_null()) {
165+
// TODO this re-compiles the shader for every material, should cache the Shader object somewhere.
166+
Ref<Shader> cross_section_shader;
167+
cross_section_shader.instantiate();
168+
cross_section_shader->set_code(cross_section_shader_shader_glsl);
169+
_cross_section_material->set_shader(cross_section_shader);
170+
}
171+
_cross_section_material->set_shader_parameter("albedo", get_albedo_color());
143172
}
144173

145174
void Material4D::_bind_methods() {
@@ -158,6 +187,8 @@ void Material4D::_bind_methods() {
158187
ClassDB::bind_method(D_METHOD("append_albedo_color", "albedo_color"), &Material4D::append_albedo_color);
159188
ClassDB::bind_method(D_METHOD("resize_albedo_color_array", "size", "fill_color"), &Material4D::resize_albedo_color_array, DEFVAL(Color(1, 1, 1, 1)));
160189

190+
ClassDB::bind_method(D_METHOD("get_cross_section_material"), &Material4D::get_cross_section_material);
191+
161192
BIND_ENUM_CONSTANT(COLOR_SOURCE_FLAG_SINGLE_COLOR);
162193
BIND_ENUM_CONSTANT(COLOR_SOURCE_FLAG_PER_VERT);
163194
BIND_ENUM_CONSTANT(COLOR_SOURCE_FLAG_PER_EDGE);

model/material_4d.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,11 @@
44

55
#if GDEXTENSION
66
#include <godot_cpp/classes/resource.hpp>
7+
#include <godot_cpp/classes/shader_material.hpp>
78
#include <godot_cpp/core/gdvirtual.gen.inc>
89
#elif GODOT_MODULE
910
#include "core/io/resource.h"
11+
#include "scene/resources/material.h"
1012
#endif
1113

1214
class Mesh4D;
@@ -44,6 +46,10 @@ class Material4D : public Resource {
4446
PackedColorArray _albedo_color_array;
4547
Color _albedo_color = Color(1, 1, 1, 1);
4648
ColorSourceFlags _albedo_source_flags = COLOR_SOURCE_FLAG_SINGLE_COLOR;
49+
Ref<ShaderMaterial> _cross_section_material;
50+
51+
// Update _cross_section_material to match current settings on the material, skip if _cross_section_material is null.
52+
virtual void update_cross_section_material();
4753

4854
public:
4955
virtual Color get_albedo_color_of_edge(const int64_t p_edge_index, const Ref<Mesh4D> &p_for_mesh);
@@ -60,6 +66,8 @@ class Material4D : public Resource {
6066
void set_albedo_color_array(const PackedColorArray &p_albedo_color_array);
6167
void append_albedo_color(const Color &p_albedo_color);
6268
void resize_albedo_color_array(const int64_t p_size, const Color &p_fill_color = Color(1, 1, 1, 1));
69+
70+
Ref<ShaderMaterial> get_cross_section_material();
6371
};
6472

6573
VARIANT_ENUM_CAST(Material4D::ColorSourceFlags);

model/mesh_4d.cpp

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,12 @@
33
#include "wire/array_wire_mesh_4d.h"
44

55
#if GDEXTENSION
6+
#include <godot_cpp/classes/array_mesh.hpp>
7+
#include <godot_cpp/core/error_macros.hpp>
68
#include <godot_cpp/templates/hash_set.hpp>
9+
#elif GODOT_MODULE
10+
#include "core/error/error_macros.h"
11+
#include "scene/resources/mesh.h"
712
#endif
813

914
PackedInt32Array Mesh4D::deduplicate_edge_indices(const PackedInt32Array &p_items) {
@@ -58,6 +63,10 @@ bool Mesh4D::validate_mesh_data() {
5863
return ret;
5964
}
6065

66+
void Mesh4D::update_cross_section_mesh() {
67+
GDVIRTUAL_CALL(_update_cross_section_mesh);
68+
}
69+
6170
void Mesh4D::validate_material_for_mesh(const Ref<Material4D> &p_material) {
6271
GDVIRTUAL_CALL(_validate_material_for_mesh, p_material);
6372
const Material4D::ColorSourceFlags albedo_source_flags = p_material->get_albedo_source_flags();
@@ -93,6 +102,15 @@ Ref<WireMesh4D> Mesh4D::to_wire_mesh() {
93102
return to_array_wire_mesh();
94103
}
95104

105+
Ref<ArrayMesh> Mesh4D::get_cross_section_mesh() {
106+
if (_is_cross_section_mesh_dirty || _cross_section_mesh.is_null()) {
107+
_cross_section_mesh.instantiate();
108+
update_cross_section_mesh();
109+
_is_cross_section_mesh_dirty = false;
110+
}
111+
return _cross_section_mesh;
112+
}
113+
96114
Ref<Material4D> Mesh4D::get_material() const {
97115
return _material;
98116
}
@@ -126,9 +144,12 @@ void Mesh4D::_bind_methods() {
126144
ClassDB::bind_method(D_METHOD("is_mesh_data_valid"), &Mesh4D::is_mesh_data_valid);
127145
ClassDB::bind_method(D_METHOD("reset_mesh_data_validation"), &Mesh4D::reset_mesh_data_validation);
128146
ClassDB::bind_method(D_METHOD("validate_material_for_mesh", "material"), &Mesh4D::validate_material_for_mesh);
147+
ClassDB::bind_method(D_METHOD("mark_cross_section_mesh_dirty"), &Mesh4D::mark_cross_section_mesh_dirty);
148+
ClassDB::bind_method(D_METHOD("update_cross_section_mesh"), &Mesh4D::update_cross_section_mesh);
129149

130150
ClassDB::bind_method(D_METHOD("to_array_wire_mesh"), &Mesh4D::to_array_wire_mesh);
131151
ClassDB::bind_method(D_METHOD("to_wire_mesh"), &Mesh4D::to_wire_mesh);
152+
ClassDB::bind_method(D_METHOD("get_cross_section_mesh"), &Mesh4D::get_cross_section_mesh);
132153

133154
ClassDB::bind_method(D_METHOD("get_material"), &Mesh4D::get_material);
134155
ClassDB::bind_method(D_METHOD("set_material", "material"), &Mesh4D::set_material);
@@ -143,4 +164,5 @@ void Mesh4D::_bind_methods() {
143164
GDVIRTUAL_BIND(_get_vertices);
144165
GDVIRTUAL_BIND(_validate_material_for_mesh, "material");
145166
GDVIRTUAL_BIND(_validate_mesh_data);
167+
GDVIRTUAL_BIND(_update_cross_section_mesh);
146168
}

model/mesh_4d.h

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,12 @@
22

33
#include "material_4d.h"
44

5+
#if GDEXTENSION
6+
#include <godot_cpp/classes/array_mesh.hpp>
7+
#elif GODOT_MODULE
8+
#include "scene/resources/mesh.h"
9+
#endif
10+
511
class ArrayWireMesh4D;
612
class WireMesh4D;
713

@@ -10,10 +16,19 @@ class Mesh4D : public Resource {
1016

1117
Ref<Material4D> _material;
1218
bool _is_mesh_data_valid = false;
19+
bool _is_cross_section_mesh_dirty = true;
1320

1421
protected:
22+
Ref<ArrayMesh> _cross_section_mesh;
23+
1524
static void _bind_methods();
1625
virtual bool validate_mesh_data();
26+
// Call when the mesh is modified to indicate that
27+
// the 3D mesh used for cross-section rendering needs to be updated.
28+
void mark_cross_section_mesh_dirty() { _is_cross_section_mesh_dirty = true; };
29+
// Called when the cross-section mesh is requested and the cross-section mesh has been marked dirty.
30+
// Update the mesh referenced by _cross_section_mesh to match the current state of the mesh.
31+
virtual void update_cross_section_mesh();
1732

1833
public:
1934
static PackedInt32Array deduplicate_edge_indices(const PackedInt32Array &p_items);
@@ -25,6 +40,8 @@ class Mesh4D : public Resource {
2540

2641
Ref<ArrayWireMesh4D> to_array_wire_mesh();
2742
virtual Ref<WireMesh4D> to_wire_mesh();
43+
// Returns a reference to the mesh used for cross-section rendering.
44+
Ref<ArrayMesh> get_cross_section_mesh();
2845

2946
Ref<Material4D> get_material() const;
3047
void set_material(const Ref<Material4D> &p_material);
@@ -37,5 +54,6 @@ class Mesh4D : public Resource {
3754
GDVIRTUAL0R(PackedVector4Array, _get_edge_positions);
3855
GDVIRTUAL0R(PackedVector4Array, _get_vertices);
3956
GDVIRTUAL0R(bool, _validate_mesh_data);
57+
GDVIRTUAL0(_update_cross_section_mesh);
4058
GDVIRTUAL1(_validate_material_for_mesh, const Ref<Material4D> &);
4159
};

model/tetra/tetra_mesh_4d.cpp

Lines changed: 86 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,23 @@
11
#include "tetra_mesh_4d.h"
22

3+
#include "../material_4d.h"
34
#include "array_tetra_mesh_4d.h"
45

56
#if GDEXTENSION
6-
#include <godot_cpp/classes/mesh.hpp>
7+
#include <godot_cpp/classes/material.hpp>
78
#include <godot_cpp/classes/standard_material3d.hpp>
89
#include <godot_cpp/classes/surface_tool.hpp>
910
#elif GODOT_MODULE
10-
#include "scene/resources/mesh.h"
11+
#include "scene/resources/material.h"
1112
#include "scene/resources/surface_tool.h"
1213
#endif
1314

15+
namespace {
16+
Color vec4_to_color(Vector4 v) {
17+
return Color(v.x, v.y, v.z, v.w);
18+
}
19+
} //namespace
20+
1421
Ref<ArrayMesh> TetraMesh4D::export_uvw_map_mesh() {
1522
const PackedVector3Array uvw_map = get_cell_uvw_map();
1623
Ref<SurfaceTool> surface_tool;
@@ -47,6 +54,7 @@ Ref<ArrayMesh> TetraMesh4D::export_uvw_map_mesh() {
4754
void TetraMesh4D::tetra_mesh_clear_cache() {
4855
_edge_positions_cache.clear();
4956
_edge_indices_cache.clear();
57+
mark_cross_section_mesh_dirty();
5058
}
5159

5260
void TetraMesh4D::validate_material_for_mesh(const Ref<Material4D> &p_material) {
@@ -140,6 +148,82 @@ PackedVector4Array TetraMesh4D::get_edge_positions() {
140148
return _edge_positions_cache;
141149
}
142150

151+
void TetraMesh4D::update_cross_section_mesh() {
152+
ERR_FAIL_NULL(_cross_section_mesh);
153+
_cross_section_mesh->clear_surfaces();
154+
155+
Ref<SurfaceTool> surface_tool;
156+
surface_tool.instantiate();
157+
surface_tool->begin(Mesh::PRIMITIVE_TRIANGLES);
158+
surface_tool->set_smooth_group(-1);
159+
160+
PackedVector4Array vertices = get_vertices();
161+
PackedInt32Array cell_indices = get_cell_indices();
162+
PackedVector3Array cell_uvws = get_cell_uvw_map();
163+
PackedVector4Array cell_normals = get_cell_normals();
164+
Ref<Material4D> material = get_material();
165+
if (material.is_valid()) {
166+
surface_tool->set_material(material->get_cross_section_material());
167+
}
168+
surface_tool->set_custom_format(0, SurfaceTool::CUSTOM_RGBA_FLOAT);
169+
surface_tool->set_custom_format(1, SurfaceTool::CUSTOM_RGBA_FLOAT);
170+
surface_tool->set_custom_format(2, SurfaceTool::CUSTOM_RGBA_FLOAT);
171+
surface_tool->set_custom_format(3, SurfaceTool::CUSTOM_RGBA_FLOAT);
172+
for (int i = 0; i < cell_indices.size(); i += 4) {
173+
// Cramming a bunch of data where it fits. Each cell's cross section can be 0-2 triangles. We create two triangles for each cell
174+
// with all the info about the cell and figure everything out in the vertex shader after transforms have been applied.
175+
// As of 4.4.1, available slots are:
176+
// - Vertex position (3)
177+
// - Custom 0-3 (4 * 4)
178+
// - UV1 and UV2 (2 * 2)
179+
// - Normal (3)
180+
// - Tangent (3)
181+
// - Color (4)
182+
// Slots that don't work:
183+
// - Bone weights: get truncated, sorted, and normalized automatically
184+
// - Binormal: available in shader, but computed in SurfaceTool from normal/tangent
185+
//
186+
// Some alternative strategies:
187+
// - ImmediateMesh to compute cross-section on the CPU every frame, but that's likely slower.
188+
// - Some kind of compute shader or custom render pipeline, but that's not supported on the Compatibility renderer.
189+
190+
//// Shared attrs for both triangles:
191+
192+
// Cell vertex positions: Using custom because there are conveniently four of them and they each take a vector4.
193+
surface_tool->set_custom(0, vec4_to_color(vertices[cell_indices[i]]));
194+
surface_tool->set_custom(1, vec4_to_color(vertices[cell_indices[i + 1]]));
195+
surface_tool->set_custom(2, vec4_to_color(vertices[cell_indices[i + 2]]));
196+
surface_tool->set_custom(3, vec4_to_color(vertices[cell_indices[i + 3]]));
197+
198+
// UVW texture coords, need 4*3 float slots. Using UV, UV2, Normal, Color, and one vertex.y.
199+
Vector3 uvw1 = cell_uvws[cell_indices[i]];
200+
Vector3 uvw2 = cell_uvws[cell_indices[i + 1]];
201+
Vector3 uvw3 = cell_uvws[cell_indices[i + 2]];
202+
Vector3 uvw4 = cell_uvws[cell_indices[i + 3]];
203+
surface_tool->set_uv(Vector2(uvw1.x, uvw1.y));
204+
surface_tool->set_uv2(Vector2(uvw2.x, uvw2.y));
205+
surface_tool->set_normal(uvw3);
206+
surface_tool->set_color(Color(uvw4.x, uvw4.y, uvw4.z, uvw1.z));
207+
208+
// Not enough slots left for normals. Also interpolating the 4D normals gives weird results, needs more experimentation.
209+
// Currently flat normals are computed in the vertex shader.
210+
211+
//// Vertices:
212+
213+
// Not storing actual position data in the vertex positions, x is an index, y is UVW data, z is unused.
214+
surface_tool->add_vertex(Vector3(0.0, uvw2.z, 0.0));
215+
surface_tool->add_vertex(Vector3(1.0, uvw2.z, 0.0));
216+
surface_tool->add_vertex(Vector3(2.0, uvw2.z, 0.0));
217+
218+
surface_tool->add_vertex(Vector3(3.0, uvw2.z, 0.0));
219+
surface_tool->add_vertex(Vector3(4.0, uvw2.z, 0.0));
220+
surface_tool->add_vertex(Vector3(5.0, uvw2.z, 0.0));
221+
}
222+
surface_tool->commit(_cross_section_mesh);
223+
224+
// TODO Second surface for 4D "shadow" effect.
225+
}
226+
143227
void TetraMesh4D::_bind_methods() {
144228
ClassDB::bind_method(D_METHOD("export_uvw_map_mesh"), &TetraMesh4D::export_uvw_map_mesh);
145229
ClassDB::bind_method(D_METHOD("tetra_mesh_clear_cache"), &TetraMesh4D::tetra_mesh_clear_cache);

model/tetra/tetra_mesh_4d.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ class TetraMesh4D : public Mesh4D {
1818
PackedInt32Array _edge_indices_cache;
1919
PackedVector4Array _edge_positions_cache;
2020

21+
virtual void update_cross_section_mesh() override;
22+
2123
public:
2224
Ref<ArrayMesh> export_uvw_map_mesh();
2325
void tetra_mesh_clear_cache();

register_types.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@
7171
#include "physics/shapes/sphere_shape_4d.h"
7272

7373
// Render.
74+
#include "render/cross_section/cross_section_rendering_engine_4d.h"
7475
#include "render/rendering_engine_4d.h"
7576
#include "render/rendering_server_4d.h"
7677
#include "render/wireframe_canvas/wireframe_canvas_rendering_engine_4d.h"
@@ -206,6 +207,7 @@ void initialize_4d_module(ModuleInitializationLevel p_level) {
206207
GDREGISTER_CLASS(GhostPhysicsEngine4D);
207208
GDREGISTER_CLASS(WireframeRenderCanvas4D);
208209
GDREGISTER_CLASS(WireframeCanvasRenderingEngine4D);
210+
GDREGISTER_CLASS(CrossSectionRenderingEngine4D);
209211
#endif // GDEXTENSION
210212
PhysicsServer4D *physics_server = memnew(PhysicsServer4D);
211213
#ifdef TOOLS_ENABLED
@@ -217,6 +219,7 @@ void initialize_4d_module(ModuleInitializationLevel p_level) {
217219
// Render.
218220
RenderingServer4D *rendering_server = memnew(RenderingServer4D);
219221
rendering_server->register_rendering_engine("Wireframe Canvas", memnew(WireframeCanvasRenderingEngine4D));
222+
rendering_server->register_rendering_engine("Cross-section", memnew(CrossSectionRenderingEngine4D));
220223
add_godot_singleton("RenderingServer4D", rendering_server);
221224
#ifdef TOOLS_ENABLED
222225
} else if (p_level == MODULE_INITIALIZATION_LEVEL_EDITOR) {

render/SCsub

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,8 @@ Import("env_modules")
55

66
env_4d = env_modules.Clone()
77

8+
env_4d.GLSL_HEADER("cross_section/cross_section_shader.glsl")
9+
810
env_4d.add_source_files(env.modules_sources, "*.cpp")
911
env_4d.add_source_files(env.modules_sources, "wireframe_canvas/*.cpp")
12+
env_4d.add_source_files(env.modules_sources, "cross_section/*.cpp")

0 commit comments

Comments
 (0)