diff options
Diffstat (limited to 'source/blender')
35 files changed, 1170 insertions, 138 deletions
diff --git a/source/blender/blenkernel/BKE_volume_render.h b/source/blender/blenkernel/BKE_volume_render.h new file mode 100644 index 00000000000..72360f316a0 --- /dev/null +++ b/source/blender/blenkernel/BKE_volume_render.h @@ -0,0 +1,72 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * 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 General Public License for more details. + * + * You should have received a copy of the GNU 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. + * + * Contributor(s): Brecht Van Lommel. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#ifndef __BKE_VOLUME_RENDER_H__ +#define __BKE_VOLUME_RENDER_H__ + +/** \file BKE_volume_render.h + * \ingroup bke + * \brief Volume datablock rendering and viewport drawing utilities. + */ + +#include "BLI_sys_types.h" + +#include "DNA_volume_types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct Volume; +struct VolumeGrid; + +/* Dense Voxels */ + +bool BKE_volume_grid_dense_bounds(const struct Volume *volume, + struct VolumeGrid *volume_grid, + int64_t min[3], + int64_t max[3]); +void BKE_volume_grid_dense_transform_matrix(const struct VolumeGrid *volume_grid, + const int64_t min[3], + const int64_t max[3], + float matrix[4][4]); +void BKE_volume_grid_dense_voxels(const struct Volume *volume, + struct VolumeGrid *volume_grid, + const int64_t min[3], + const int64_t max[3], + float *voxels); + +/* Wireframe */ + +typedef void (*BKE_volume_wireframe_cb)( + void *userdata, float (*verts)[3], int (*edges)[2], int totvert, int totedge); + +void BKE_volume_grid_wireframe(const struct Volume *volume, + struct VolumeGrid *volume_grid, + BKE_volume_wireframe_cb cb, + void *cb_userdata); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/source/blender/blenkernel/CMakeLists.txt b/source/blender/blenkernel/CMakeLists.txt index d3dfa422ebd..7fd5470cecc 100644 --- a/source/blender/blenkernel/CMakeLists.txt +++ b/source/blender/blenkernel/CMakeLists.txt @@ -242,6 +242,7 @@ set(SRC intern/undo_system.c intern/unit.c intern/volume.cc + intern/volume_render.cc intern/workspace.c intern/world.c intern/writeavi.c @@ -375,6 +376,7 @@ set(SRC BKE_undo_system.h BKE_unit.h BKE_volume.h + BKE_volume_render.h BKE_workspace.h BKE_world.h BKE_writeavi.h diff --git a/source/blender/blenkernel/intern/volume_render.cc b/source/blender/blenkernel/intern/volume_render.cc new file mode 100644 index 00000000000..d8fbbce3bae --- /dev/null +++ b/source/blender/blenkernel/intern/volume_render.cc @@ -0,0 +1,358 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * 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 General Public License for more details. + * + * You should have received a copy of the GNU 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. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/blenkernel/intern/volume_render.cc + * \ingroup bke + */ + +#include "MEM_guardedalloc.h" + +#include "BLI_math_matrix.h" +#include "BLI_math_vector.h" + +#include "DNA_volume_types.h" + +#include "BKE_volume.h" +#include "BKE_volume_render.h" + +#ifdef WITH_OPENVDB +# include <openvdb/openvdb.h> +# include <openvdb/tools/Dense.h> +#endif + +/* Dense Voxels */ + +bool BKE_volume_grid_dense_bounds(const Volume *volume, + VolumeGrid *volume_grid, + int64_t min[3], + int64_t max[3]) +{ +#ifdef WITH_OPENVDB + openvdb::GridBase::ConstPtr grid = BKE_volume_grid_openvdb_for_read(volume, volume_grid); + + openvdb::CoordBBox bbox = grid->evalActiveVoxelBoundingBox(); + if (!bbox.empty()) { + /* OpenVDB bbox is inclusive, so add 1 to convert. */ + min[0] = bbox.min().x(); + min[1] = bbox.min().y(); + min[2] = bbox.min().z(); + max[0] = bbox.max().x() + 1; + max[1] = bbox.max().y() + 1; + max[2] = bbox.max().z() + 1; + return true; + } +#else + UNUSED_VARS(volume, volume_grid); +#endif + + min[0] = 0; + min[1] = 0; + min[2] = 0; + max[0] = 0; + max[1] = 0; + max[2] = 0; + return false; +} + +/* Transform matrix from unit cube to object space, for 3D texture sampling. */ +void BKE_volume_grid_dense_transform_matrix(const VolumeGrid *volume_grid, + const int64_t min[3], + const int64_t max[3], + float mat[4][4]) +{ +#ifdef WITH_OPENVDB + float index_to_world[4][4]; + BKE_volume_grid_transform_matrix(volume_grid, index_to_world); + + float texture_to_index[4][4]; + float loc[3] = {(float)min[0], (float)min[1], (float)min[2]}; + float size[3] = {(float)(max[0] - min[0]), (float)(max[1] - min[1]), (float)(max[2] - min[2])}; + size_to_mat4(texture_to_index, size); + copy_v3_v3(texture_to_index[3], loc); + + mul_m4_m4m4(mat, index_to_world, texture_to_index); +#else + UNUSED_VARS(volume_grid, min, max); + unit_m4(mat); +#endif +} + +void BKE_volume_grid_dense_voxels(const Volume *volume, + VolumeGrid *volume_grid, + const int64_t min[3], + const int64_t max[3], + float *voxels) +{ +#ifdef WITH_OPENVDB + openvdb::GridBase::ConstPtr grid = BKE_volume_grid_openvdb_for_read(volume, volume_grid); + + /* Convert to OpenVDB inclusive bbox with -1. */ + openvdb::CoordBBox bbox(min[0], min[1], min[2], max[0] - 1, max[1] - 1, max[2] - 1); + + switch (BKE_volume_grid_type(volume_grid)) { + case VOLUME_GRID_BOOLEAN: { + openvdb::tools::Dense<float, openvdb::tools::LayoutXYZ> dense(bbox, voxels); + openvdb::tools::copyToDense(*openvdb::gridConstPtrCast<openvdb::BoolGrid>(grid), dense); + break; + } + case VOLUME_GRID_FLOAT: { + openvdb::tools::Dense<float, openvdb::tools::LayoutXYZ> dense(bbox, voxels); + openvdb::tools::copyToDense(*openvdb::gridConstPtrCast<openvdb::FloatGrid>(grid), dense); + break; + } + case VOLUME_GRID_DOUBLE: { + openvdb::tools::Dense<float, openvdb::tools::LayoutXYZ> dense(bbox, voxels); + openvdb::tools::copyToDense(*openvdb::gridConstPtrCast<openvdb::DoubleGrid>(grid), dense); + break; + } + case VOLUME_GRID_INT: { + openvdb::tools::Dense<float, openvdb::tools::LayoutXYZ> dense(bbox, voxels); + openvdb::tools::copyToDense(*openvdb::gridConstPtrCast<openvdb::Int32Grid>(grid), dense); + break; + } + case VOLUME_GRID_INT64: { + openvdb::tools::Dense<float, openvdb::tools::LayoutXYZ> dense(bbox, voxels); + openvdb::tools::copyToDense(*openvdb::gridConstPtrCast<openvdb::Int64Grid>(grid), dense); + break; + } + case VOLUME_GRID_MASK: { + openvdb::tools::Dense<float, openvdb::tools::LayoutXYZ> dense(bbox, voxels); + openvdb::tools::copyToDense(*openvdb::gridConstPtrCast<openvdb::MaskGrid>(grid), dense); + break; + } + case VOLUME_GRID_VECTOR_FLOAT: { + openvdb::tools::Dense<openvdb::Vec3f, openvdb::tools::LayoutXYZ> dense( + bbox, (openvdb::Vec3f *)voxels); + openvdb::tools::copyToDense(*openvdb::gridConstPtrCast<openvdb::Vec3fGrid>(grid), dense); + break; + } + case VOLUME_GRID_VECTOR_DOUBLE: { + openvdb::tools::Dense<openvdb::Vec3f, openvdb::tools::LayoutXYZ> dense( + bbox, (openvdb::Vec3f *)voxels); + openvdb::tools::copyToDense(*openvdb::gridConstPtrCast<openvdb::Vec3dGrid>(grid), dense); + break; + } + case VOLUME_GRID_VECTOR_INT: { + openvdb::tools::Dense<openvdb::Vec3f, openvdb::tools::LayoutXYZ> dense( + bbox, (openvdb::Vec3f *)voxels); + openvdb::tools::copyToDense(*openvdb::gridConstPtrCast<openvdb::Vec3IGrid>(grid), dense); + break; + } + case VOLUME_GRID_STRING: + case VOLUME_GRID_POINTS: + case VOLUME_GRID_UNKNOWN: { + /* Zero channels to copy. */ + break; + } + } +#else + UNUSED_VARS(volume, volume_grid, min, max, voxels); +#endif +} + +/* Wireframe */ + +#ifdef WITH_OPENVDB +struct VolumeWireframe { + std::vector<openvdb::Vec3f> verts; + std::vector<openvdb::Vec2I> edges; + + template<typename GridType> + void add_grid(openvdb::GridBase::ConstPtr gridbase, const bool points, const bool coarse) + { + using TreeType = typename GridType::TreeType; + using Depth2Type = typename TreeType::RootNodeType::ChildNodeType::ChildNodeType; + using NodeCIter = typename TreeType::NodeCIter; + using GridConstPtr = typename GridType::ConstPtr; + + GridConstPtr grid = openvdb::gridConstPtrCast<GridType>(gridbase); + const openvdb::math::Transform &transform = grid->transform(); + const int depth = (coarse) ? 2 : 3; + + NodeCIter iter = grid->tree().cbeginNode(); + iter.setMaxDepth(depth); + + for (; iter; ++iter) { + if (iter.getDepth() == depth) { + openvdb::CoordBBox coordbbox; + + if (depth == 2) { + /* Internal node at depth 2. */ + const Depth2Type *node = nullptr; + iter.getNode(node); + if (node) { + node->evalActiveBoundingBox(coordbbox, false); + } + else { + continue; + } + } + else { + /* Leaf node. */ + if (!iter.getBoundingBox(coordbbox)) { + continue; + } + } + + /* +1 to convert from exclusive to include bounds. */ + coordbbox.max() = coordbbox.max().offsetBy(1); + openvdb::BBoxd bbox = transform.indexToWorld(coordbbox); + + if (points) { + add_point(bbox); + } + else { + add_box(bbox); + } + } + } + } + + void add_point(const openvdb::BBoxd &bbox) + { + verts.push_back(bbox.getCenter()); + } + + void add_box(const openvdb::BBoxd &bbox) + { + /* TODO: deduplicate edges, hide flat edges? */ + openvdb::Vec3f min = bbox.min(); + openvdb::Vec3f max = bbox.max(); + + const int vert_offset = verts.size(); + const int edge_offset = edges.size(); + + /* Create vertices. */ + verts.resize(vert_offset + 8); + verts[vert_offset + 0] = openvdb::Vec3f(min[0], min[1], min[2]); + verts[vert_offset + 1] = openvdb::Vec3f(max[0], min[1], min[2]); + verts[vert_offset + 2] = openvdb::Vec3f(max[0], max[1], min[2]); + verts[vert_offset + 3] = openvdb::Vec3f(min[0], max[1], min[2]); + verts[vert_offset + 4] = openvdb::Vec3f(min[0], min[1], max[2]); + verts[vert_offset + 5] = openvdb::Vec3f(max[0], min[1], max[2]); + verts[vert_offset + 6] = openvdb::Vec3f(max[0], max[1], max[2]); + verts[vert_offset + 7] = openvdb::Vec3f(min[0], max[1], max[2]); + + /* Create edges. */ + const int box_edges[12][2] = {{0, 1}, + {1, 2}, + {2, 3}, + {3, 0}, + {4, 5}, + {5, 6}, + {6, 7}, + {7, 4}, + {0, 4}, + {1, 5}, + {2, 6}, + {3, 7}}; + + edges.resize(edge_offset + 12); + for (int i = 0; i < 12; i++) { + edges[edge_offset + i] = openvdb::Vec2I(vert_offset + box_edges[i][0], + vert_offset + box_edges[i][1]); + } + } +}; +#endif + +void BKE_volume_grid_wireframe(const Volume *volume, + VolumeGrid *volume_grid, + BKE_volume_wireframe_cb cb, + void *cb_userdata) +{ +#ifdef WITH_OPENVDB + VolumeWireframe wireframe; + + if (volume->display.wireframe_type == VOLUME_WIREFRAME_NONE) { + /* Nothing. */ + } + else if (volume->display.wireframe_type == VOLUME_WIREFRAME_BOUNDS) { + /* Bounding box. */ + float min[3], max[3]; + BKE_volume_grid_bounds(volume_grid, min, max); + + openvdb::BBoxd bbox(min, max); + wireframe.add_box(bbox); + } + else { + /* Tree nodes. */ + openvdb::GridBase::ConstPtr grid = BKE_volume_grid_openvdb_for_read(volume, volume_grid); + const bool points = (volume->display.wireframe_type == VOLUME_WIREFRAME_POINTS); + const bool coarse = (volume->display.wireframe_detail == VOLUME_WIREFRAME_COARSE); + + switch (BKE_volume_grid_type(volume_grid)) { + case VOLUME_GRID_BOOLEAN: { + wireframe.add_grid<openvdb::BoolGrid>(grid, points, coarse); + break; + } + case VOLUME_GRID_FLOAT: { + wireframe.add_grid<openvdb::FloatGrid>(grid, points, coarse); + break; + } + case VOLUME_GRID_DOUBLE: { + wireframe.add_grid<openvdb::DoubleGrid>(grid, points, coarse); + break; + } + case VOLUME_GRID_INT: { + wireframe.add_grid<openvdb::Int32Grid>(grid, points, coarse); + break; + } + case VOLUME_GRID_INT64: { + wireframe.add_grid<openvdb::Int64Grid>(grid, points, coarse); + break; + } + case VOLUME_GRID_MASK: { + wireframe.add_grid<openvdb::MaskGrid>(grid, points, coarse); + break; + } + case VOLUME_GRID_VECTOR_FLOAT: { + wireframe.add_grid<openvdb::Vec3fGrid>(grid, points, coarse); + break; + } + case VOLUME_GRID_VECTOR_DOUBLE: { + wireframe.add_grid<openvdb::Vec3dGrid>(grid, points, coarse); + break; + } + case VOLUME_GRID_VECTOR_INT: { + wireframe.add_grid<openvdb::Vec3IGrid>(grid, points, coarse); + break; + } + case VOLUME_GRID_STRING: { + wireframe.add_grid<openvdb::StringGrid>(grid, points, coarse); + break; + } + case VOLUME_GRID_POINTS: + case VOLUME_GRID_UNKNOWN: { + break; + } + } + } + + cb(cb_userdata, + (float(*)[3])wireframe.verts.data(), + (int(*)[2])wireframe.edges.data(), + wireframe.verts.size(), + wireframe.edges.size()); +#else + UNUSED_VARS(volume, volume_grid); + cb(cb_userdata, NULL, NULL, 0, 0); +#endif +} diff --git a/source/blender/draw/CMakeLists.txt b/source/blender/draw/CMakeLists.txt index 81f2214b402..46263f694cb 100644 --- a/source/blender/draw/CMakeLists.txt +++ b/source/blender/draw/CMakeLists.txt @@ -53,10 +53,13 @@ set(SRC intern/draw_cache_impl_curve.c intern/draw_cache_impl_displist.c intern/draw_cache_impl_gpencil.c + intern/draw_cache_impl_hair.c intern/draw_cache_impl_lattice.c intern/draw_cache_impl_mesh.c intern/draw_cache_impl_metaball.c intern/draw_cache_impl_particles.c + intern/draw_cache_impl_pointcloud.c + intern/draw_cache_impl_volume.c intern/draw_color_management.c intern/draw_common.c intern/draw_debug.c @@ -139,6 +142,7 @@ set(SRC engines/overlay/overlay_outline.c engines/overlay/overlay_paint.c engines/overlay/overlay_particle.c + engines/overlay/overlay_pointcloud.c engines/overlay/overlay_sculpt.c engines/overlay/overlay_shader.c engines/overlay/overlay_wireframe.c @@ -377,6 +381,8 @@ data_to_c_simple(engines/overlay/shaders/paint_weight_vert.glsl SRC) data_to_c_simple(engines/overlay/shaders/paint_wire_vert.glsl SRC) data_to_c_simple(engines/overlay/shaders/particle_vert.glsl SRC) data_to_c_simple(engines/overlay/shaders/particle_frag.glsl SRC) +data_to_c_simple(engines/overlay/shaders/pointcloud_vert.glsl SRC) +data_to_c_simple(engines/overlay/shaders/pointcloud_frag.glsl SRC) data_to_c_simple(engines/overlay/shaders/sculpt_mask_vert.glsl SRC) data_to_c_simple(engines/overlay/shaders/volume_velocity_vert.glsl SRC) data_to_c_simple(engines/overlay/shaders/wireframe_vert.glsl SRC) diff --git a/source/blender/draw/engines/eevee/eevee_engine.c b/source/blender/draw/engines/eevee/eevee_engine.c index a886fa26589..70933e21719 100644 --- a/source/blender/draw/engines/eevee/eevee_engine.c +++ b/source/blender/draw/engines/eevee/eevee_engine.c @@ -127,6 +127,12 @@ void EEVEE_cache_populate(void *vedata, Object *ob) if (ELEM(ob->type, OB_MESH, OB_CURVE, OB_SURF, OB_FONT, OB_MBALL)) { EEVEE_materials_cache_populate(vedata, sldata, ob, &cast_shadow); } + else if (ob->type == OB_HAIR) { + EEVEE_object_hair_cache_populate(vedata, sldata, ob, &cast_shadow); + } + else if (ob->type == OB_VOLUME) { + EEVEE_volumes_cache_object_add(sldata, vedata, draw_ctx->scene, ob); + } else if (!USE_SCENE_LIGHT(draw_ctx->v3d)) { /* do not add any scene light sources to the cache */ } diff --git a/source/blender/draw/engines/eevee/eevee_materials.c b/source/blender/draw/engines/eevee/eevee_materials.c index 230a0725493..bb64fe32bc3 100644 --- a/source/blender/draw/engines/eevee/eevee_materials.c +++ b/source/blender/draw/engines/eevee/eevee_materials.c @@ -32,9 +32,10 @@ #include "BKE_paint.h" #include "BKE_particle.h" -#include "DNA_world_types.h" +#include "DNA_hair_types.h" #include "DNA_modifier_types.h" #include "DNA_view3d_types.h" +#include "DNA_world_types.h" #include "GPU_material.h" @@ -1021,7 +1022,7 @@ static struct DRWShadingGroup *EEVEE_default_shading_group_get(EEVEE_ViewLayerDa EEVEE_PassList *psl = vedata->psl; - BLI_assert(!is_hair || (ob && psys && md)); + BLI_assert(!is_hair || (ob && ((psys && md) || ob->type == OB_HAIR))); SET_FLAG_FROM_TEST(options, is_hair, VAR_MAT_HAIR); SET_FLAG_FROM_TEST(options, holdout, VAR_MAT_HOLDOUT); @@ -1715,7 +1716,12 @@ BLI_INLINE Material *eevee_object_material_get(Object *ob, int slot) { Material *ma = BKE_object_material_get(ob, slot + 1); if (ma == NULL) { - ma = BKE_material_default_empty(); + if (ob->type == OB_VOLUME) { + ma = BKE_material_default_volume(); + } + else { + ma = BKE_material_default_empty(); + } } return ma; } @@ -2064,6 +2070,14 @@ void EEVEE_particle_hair_cache_populate(EEVEE_Data *vedata, } } +void EEVEE_object_hair_cache_populate(EEVEE_Data *vedata, + EEVEE_ViewLayerData *sldata, + Object *ob, + bool *cast_shadow) +{ + eevee_hair_cache_populate(vedata, sldata, ob, NULL, NULL, HAIR_MATERIAL_NR, cast_shadow); +} + void EEVEE_materials_cache_finish(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata) { EEVEE_StorageList *stl = ((EEVEE_Data *)vedata)->stl; diff --git a/source/blender/draw/engines/eevee/eevee_private.h b/source/blender/draw/engines/eevee/eevee_private.h index 52fb8b3cced..e2470b4fa76 100644 --- a/source/blender/draw/engines/eevee/eevee_private.h +++ b/source/blender/draw/engines/eevee/eevee_private.h @@ -902,6 +902,10 @@ void EEVEE_particle_hair_cache_populate(EEVEE_Data *vedata, EEVEE_ViewLayerData *sldata, Object *ob, bool *cast_shadow); +void EEVEE_object_hair_cache_populate(EEVEE_Data *vedata, + EEVEE_ViewLayerData *sldata, + Object *ob, + bool *cast_shadow); void EEVEE_materials_cache_finish(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata); struct GPUMaterial *EEVEE_material_world_lightprobe_get(struct Scene *scene, struct World *wo); struct GPUMaterial *EEVEE_material_world_background_get(struct Scene *scene, struct World *wo); diff --git a/source/blender/draw/engines/eevee/eevee_render.c b/source/blender/draw/engines/eevee/eevee_render.c index eedc64f32e6..c8357b56b18 100644 --- a/source/blender/draw/engines/eevee/eevee_render.c +++ b/source/blender/draw/engines/eevee/eevee_render.c @@ -177,7 +177,7 @@ bool EEVEE_render_init(EEVEE_Data *ved, RenderEngine *engine, struct Depsgraph * void EEVEE_render_cache(void *vedata, struct Object *ob, struct RenderEngine *engine, - struct Depsgraph *UNUSED(depsgraph)) + struct Depsgraph *depsgraph) { EEVEE_ViewLayerData *sldata = EEVEE_view_layer_data_ensure(); EEVEE_LightProbesInfo *pinfo = sldata->probes; @@ -211,6 +211,13 @@ void EEVEE_render_cache(void *vedata, if (ELEM(ob->type, OB_MESH, OB_CURVE, OB_SURF, OB_FONT, OB_MBALL)) { EEVEE_materials_cache_populate(vedata, sldata, ob, &cast_shadow); } + else if (ob->type == OB_HAIR) { + EEVEE_object_hair_cache_populate(vedata, sldata, ob, &cast_shadow); + } + else if (ob->type == OB_VOLUME) { + Scene *scene = DEG_get_evaluated_scene(depsgraph); + EEVEE_volumes_cache_object_add(sldata, vedata, scene, ob); + } else if (ob->type == OB_LIGHTPROBE) { EEVEE_lightprobes_cache_add(sldata, vedata, ob); } diff --git a/source/blender/draw/engines/eevee/eevee_volumes.c b/source/blender/draw/engines/eevee/eevee_volumes.c index 41f8dddf0fb..0e82ac237c0 100644 --- a/source/blender/draw/engines/eevee/eevee_volumes.c +++ b/source/blender/draw/engines/eevee/eevee_volumes.c @@ -29,11 +29,15 @@ #include "DNA_object_force_types.h" #include "DNA_fluid_types.h" +#include "DNA_volume_types.h" #include "DNA_world_types.h" -#include "BKE_modifier.h" -#include "BKE_mesh.h" #include "BKE_fluid.h" +#include "BKE_global.h" +#include "BKE_mesh.h" +#include "BKE_modifier.h" +#include "BKE_volume.h" +#include "BKE_volume_render.h" #include "ED_screen.h" @@ -59,6 +63,7 @@ static struct { GPUTexture *depth_src; GPUTexture *dummy_density; + GPUTexture *dummy_color; GPUTexture *dummy_flame; GPUTexture *dummy_scatter; @@ -368,8 +373,7 @@ void EEVEE_volumes_cache_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata) /* Fix principle volumetric not working with world materials. */ ListBase gpu_grids = GPU_material_volume_grids(mat); - for (GPUMaterialVolumeGrid *gpu_grid = gpu_grids.first; gpu_grid; - gpu_grid = gpu_grid->next) { + LISTBASE_FOREACH (GPUMaterialVolumeGrid *, gpu_grid, &gpu_grids) { DRW_shgroup_uniform_texture(grp, gpu_grid->sampler_name, e_data.dummy_density); } @@ -390,64 +394,92 @@ void EEVEE_volumes_cache_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata) } } -typedef struct EEVEE_InstanceVolumeMatrix { - DrawData dd; - float volume_mat[4][4]; -} EEVEE_InstanceVolumeMatrix; - -void EEVEE_volumes_cache_object_add(EEVEE_ViewLayerData *sldata, - EEVEE_Data *vedata, - Scene *scene, - Object *ob) +static bool eevee_volume_object_grids_init(Object *ob, ListBase *gpu_grids, DRWShadingGroup *grp) { - static const float white[3] = {1.0f, 1.0f, 1.0f}; - - float *texcoloc = NULL; - float *texcosize = NULL; - struct ModifierData *md = NULL; - Material *ma = BKE_object_material_get(ob, 1); - - if (ma == NULL) { - return; + Volume *volume = ob->data; + BKE_volume_load(volume, G.main); + + /* Test if we need to use multiple transforms. */ + DRWVolumeGrid *first_drw_grid = NULL; + bool multiple_transforms = true; + + LISTBASE_FOREACH (GPUMaterialVolumeGrid *, gpu_grid, gpu_grids) { + VolumeGrid *volume_grid = BKE_volume_grid_find(volume, gpu_grid->name); + DRWVolumeGrid *drw_grid = (volume_grid) ? + DRW_volume_batch_cache_get_grid(volume, volume_grid) : + NULL; + + if (drw_grid) { + if (first_drw_grid == NULL) { + first_drw_grid = drw_grid; + } + else if (drw_grid && + !equals_m4m4(drw_grid->object_to_texture, first_drw_grid->object_to_texture)) { + multiple_transforms = true; + break; + } + } } - float size[3]; - mat4_to_size(size, ob->obmat); - /* Check if any of the axes have 0 length. (see T69070) */ - const float epsilon = 1e-8f; - if ((size[0] < epsilon) || (size[1] < epsilon) || (size[2] < epsilon)) { - return; + /* Bail out of no grids to render. */ + if (first_drw_grid == NULL) { + return false; } - struct GPUMaterial *mat = EEVEE_material_mesh_volume_get(scene, ma); - eGPUMaterialStatus status = GPU_material_status(mat); - - if (status == GPU_MAT_QUEUED) { - vedata->stl->g_data->queued_shaders_count++; + /* Set transform matrix for the volume as a whole. This one is also used for + * clipping so must map the entire bounding box to 0..1. */ + float bounds_to_object[4][4]; + + if (multiple_transforms) { + /* For multiple grids with different transform, we first transform from object space + * to bounds, then for each individual grid from bounds to texture. */ + BoundBox *bb = BKE_volume_boundbox_get(ob); + float bb_size[3]; + sub_v3_v3v3(bb_size, bb->vec[6], bb->vec[0]); + size_to_mat4(bounds_to_object, bb_size); + copy_v3_v3(bounds_to_object[3], bb->vec[0]); + + invert_m4_m4(first_drw_grid->object_to_bounds, bounds_to_object); + DRW_shgroup_uniform_mat4(grp, "volumeObjectToTexture", first_drw_grid->object_to_bounds); } - /* If shader failed to compile or is currently compiling. */ - if (status != GPU_MAT_SUCCESS) { - return; + else { + /* All grid transforms are equal, we can transform to texture space immediately. */ + DRW_shgroup_uniform_mat4(grp, "volumeObjectToTexture", first_drw_grid->object_to_texture); } - DRWShadingGroup *grp = DRW_shgroup_material_create(mat, vedata->psl->volumetric_objects_ps); - - BKE_mesh_texspace_get_reference((struct Mesh *)ob->data, NULL, &texcoloc, &texcosize); + /* Don't use orco transform here, only matrix. */ + DRW_shgroup_uniform_vec3_copy(grp, "volumeOrcoLoc", (float[3]){0.5f, 0.5f, 0.5f}); + DRW_shgroup_uniform_vec3_copy(grp, "volumeOrcoSize", (float[3]){0.5f, 0.5f, 0.5f}); + + /* Bind volume grid textures. */ + LISTBASE_FOREACH (GPUMaterialVolumeGrid *, gpu_grid, gpu_grids) { + VolumeGrid *volume_grid = BKE_volume_grid_find(volume, gpu_grid->name); + DRWVolumeGrid *drw_grid = (volume_grid) ? + DRW_volume_batch_cache_get_grid(volume, volume_grid) : + NULL; + + DRW_shgroup_uniform_texture( + grp, gpu_grid->sampler_name, (drw_grid) ? drw_grid->texture : e_data.dummy_density); + + if (drw_grid && multiple_transforms) { + /* Specify per-volume tranform matrix that is applied after the + * transform from object to bounds. */ + mul_m4_m4m4(drw_grid->bounds_to_texture, drw_grid->object_to_texture, bounds_to_object); + DRW_shgroup_uniform_mat4(grp, gpu_grid->transform_name, drw_grid->bounds_to_texture); + } + } - /* TODO(fclem) remove those "unnecessary" UBOs */ - DRW_shgroup_uniform_block(grp, "planar_block", sldata->planar_ubo); - DRW_shgroup_uniform_block(grp, "probe_block", sldata->probe_ubo); - DRW_shgroup_uniform_block(grp, "shadow_block", sldata->shadow_ubo); - DRW_shgroup_uniform_block(grp, "light_block", sldata->light_ubo); - DRW_shgroup_uniform_block(grp, "grid_block", sldata->grid_ubo); - DRW_shgroup_uniform_block( - grp, "renderpass_block", EEVEE_material_default_render_pass_ubo_get(sldata)); + return true; +} - DRW_shgroup_uniform_block(grp, "common_block", sldata->common_ubo); - DRW_shgroup_uniform_vec3(grp, "volumeOrcoLoc", texcoloc, 1); - DRW_shgroup_uniform_vec3(grp, "volumeOrcoSize", texcosize, 1); +static bool eevee_volume_object_mesh_init(Scene *scene, + Object *ob, + ListBase *gpu_grids, + DRWShadingGroup *grp) +{ + static const float white[3] = {1.0f, 1.0f, 1.0f}; + ModifierData *md = NULL; - ListBase gpu_grids = GPU_material_volume_grids(mat); /* Smoke Simulation */ if (((ob->base_flag & BASE_FROM_DUPLI) == 0) && (md = modifiers_findByType(ob, eModifierType_Fluid)) && @@ -458,7 +490,7 @@ void EEVEE_volumes_cache_object_add(EEVEE_ViewLayerData *sldata, /* Don't try to show liquid domains here. */ if (!mds->fluid || !(mds->type == FLUID_DOMAIN_TYPE_GAS)) { - return; + return false; } /* Don't show smoke before simulation starts, this could be made an option in the future. */ @@ -479,7 +511,7 @@ void EEVEE_volumes_cache_object_add(EEVEE_ViewLayerData *sldata, BLI_addtail(&e_data.smoke_domains, BLI_genericNodeN(mmd)); } - for (GPUMaterialVolumeGrid *gpu_grid = gpu_grids.first; gpu_grid; gpu_grid = gpu_grid->next) { + LISTBASE_FOREACH (GPUMaterialVolumeGrid *, gpu_grid, gpu_grids) { if (STREQ(gpu_grid->name, "density")) { DRW_shgroup_uniform_texture_ref(grp, gpu_grid->sampler_name, @@ -510,10 +542,85 @@ void EEVEE_volumes_cache_object_add(EEVEE_ViewLayerData *sldata, DRW_shgroup_uniform_vec2(grp, "volumeTemperature", &mds->flame_ignition, 1); } else { - for (GPUMaterialVolumeGrid *gpu_grid = gpu_grids.first; gpu_grid; gpu_grid = gpu_grid->next) { + LISTBASE_FOREACH (GPUMaterialVolumeGrid *, gpu_grid, gpu_grids) { DRW_shgroup_uniform_texture(grp, gpu_grid->sampler_name, e_data.dummy_density); } - DRW_shgroup_uniform_vec3(grp, "volumeColor", white, 1); + } + + /* Transform for mesh volumes. */ + static const float unit_mat[4][4] = {{1.0f, 0.0f, 0.0f, 0.0f}, + {0.0f, 1.0f, 0.0f, 0.0f}, + {0.0f, 0.0f, 1.0f, 0.0f}, + {0.0f, 0.0f, 0.0f, 1.0f}}; + float *texco_loc, *texco_size; + BKE_mesh_texspace_get_reference((struct Mesh *)ob->data, NULL, &texco_loc, &texco_size); + + DRW_shgroup_uniform_mat4(grp, "volumeObjectToTexture", unit_mat); + DRW_shgroup_uniform_vec3(grp, "volumeOrcoLoc", texco_loc, 1); + DRW_shgroup_uniform_vec3(grp, "volumeOrcoSize", texco_size, 1); + + return true; +} + +void EEVEE_volumes_cache_object_add(EEVEE_ViewLayerData *sldata, + EEVEE_Data *vedata, + Scene *scene, + Object *ob) +{ + Material *ma = BKE_object_material_get(ob, 1); + + if (ma == NULL) { + if (ob->type == OB_VOLUME) { + ma = BKE_material_default_volume(); + } + else { + return; + } + } + + float size[3]; + mat4_to_size(size, ob->obmat); + /* Check if any of the axes have 0 length. (see T69070) */ + const float epsilon = 1e-8f; + if ((size[0] < epsilon) || (size[1] < epsilon) || (size[2] < epsilon)) { + return; + } + + struct GPUMaterial *mat = EEVEE_material_mesh_volume_get(scene, ma); + eGPUMaterialStatus status = GPU_material_status(mat); + + if (status == GPU_MAT_QUEUED) { + vedata->stl->g_data->queued_shaders_count++; + } + /* If shader failed to compile or is currently compiling. */ + if (status != GPU_MAT_SUCCESS) { + return; + } + + DRWShadingGroup *grp = DRW_shgroup_material_create(mat, vedata->psl->volumetric_objects_ps); + + /* TODO(fclem) remove those "unnecessary" UBOs */ + DRW_shgroup_uniform_block(grp, "planar_block", sldata->planar_ubo); + DRW_shgroup_uniform_block(grp, "probe_block", sldata->probe_ubo); + DRW_shgroup_uniform_block(grp, "shadow_block", sldata->shadow_ubo); + DRW_shgroup_uniform_block(grp, "light_block", sldata->light_ubo); + DRW_shgroup_uniform_block(grp, "grid_block", sldata->grid_ubo); + DRW_shgroup_uniform_block( + grp, "renderpass_block", EEVEE_material_default_render_pass_ubo_get(sldata)); + + DRW_shgroup_uniform_block(grp, "common_block", sldata->common_ubo); + + ListBase gpu_grids = GPU_material_volume_grids(mat); + + if (ob->type == OB_VOLUME) { + if (!eevee_volume_object_grids_init(ob, &gpu_grids, grp)) { + return; + } + } + else { + if (!eevee_volume_object_mesh_init(scene, ob, &gpu_grids, grp)) { + return; + } } /* TODO Reduce to number of slices intersecting. */ @@ -757,6 +864,7 @@ void EEVEE_volumes_free(void) DRW_TEXTURE_FREE_SAFE(e_data.dummy_density); DRW_TEXTURE_FREE_SAFE(e_data.dummy_flame); + DRW_TEXTURE_FREE_SAFE(e_data.dummy_color); DRW_SHADER_FREE_SAFE(e_data.volumetric_clear_sh); DRW_SHADER_FREE_SAFE(e_data.scatter_sh); diff --git a/source/blender/draw/engines/eevee/shaders/volumetric_frag.glsl b/source/blender/draw/engines/eevee/shaders/volumetric_frag.glsl index 2b1ae60654e..f5e92cd7495 100644 --- a/source/blender/draw/engines/eevee/shaders/volumetric_frag.glsl +++ b/source/blender/draw/engines/eevee/shaders/volumetric_frag.glsl @@ -7,6 +7,7 @@ #ifdef MESH_SHADER uniform vec3 volumeOrcoLoc; uniform vec3 volumeOrcoSize; +uniform mat4 volumeObjectToTexture; #endif flat in int slice; @@ -35,8 +36,10 @@ void main() worldPosition = point_view_to_world(viewPosition); #ifdef MESH_SHADER volumeObjectLocalCoord = point_world_to_object(worldPosition); + /* TODO: redundant transform */ volumeObjectLocalCoord = (volumeObjectLocalCoord - volumeOrcoLoc + volumeOrcoSize) / (volumeOrcoSize * 2.0); + volumeObjectLocalCoord = (volumeObjectToTexture * vec4(volumeObjectLocalCoord, 1.0)).xyz; if (any(lessThan(volumeObjectLocalCoord, vec3(0.0))) || any(greaterThan(volumeObjectLocalCoord, vec3(1.0)))) diff --git a/source/blender/draw/engines/overlay/overlay_engine.c b/source/blender/draw/engines/overlay/overlay_engine.c index 740ca42800e..9b900a53d8d 100644 --- a/source/blender/draw/engines/overlay/overlay_engine.c +++ b/source/blender/draw/engines/overlay/overlay_engine.c @@ -161,6 +161,7 @@ static void OVERLAY_cache_init(void *vedata) OVERLAY_motion_path_cache_init(vedata); OVERLAY_outline_cache_init(vedata); OVERLAY_particle_cache_init(vedata); + OVERLAY_pointcloud_cache_init(vedata); OVERLAY_wireframe_cache_init(vedata); } @@ -205,6 +206,11 @@ static bool overlay_object_is_edit_mode(const OVERLAY_PrivateData *pd, const Obj return pd->ctx_mode == CTX_MODE_EDIT_METABALL; case OB_FONT: return pd->ctx_mode == CTX_MODE_EDIT_TEXT; + case OB_HAIR: + case OB_POINTCLOUD: + case OB_VOLUME: + /* No edit mode yet. */ + return false; } } return false; @@ -223,8 +229,16 @@ static void OVERLAY_cache_populate(void *vedata, Object *ob) const bool in_paint_mode = (ob == draw_ctx->obact) && (draw_ctx->object_mode & OB_MODE_ALL_PAINT); const bool in_sculpt_mode = (ob == draw_ctx->obact) && (ob->sculpt != NULL); - const bool has_surface = ELEM( - ob->type, OB_MESH, OB_CURVE, OB_SURF, OB_MBALL, OB_FONT, OB_GPENCIL); + const bool has_surface = ELEM(ob->type, + OB_MESH, + OB_CURVE, + OB_SURF, + OB_MBALL, + OB_FONT, + OB_GPENCIL, + OB_HAIR, + OB_POINTCLOUD, + OB_VOLUME); const bool draw_surface = (ob->dt >= OB_WIRE) && (renderable || (ob->dt == OB_WIRE)); const bool draw_facing = draw_surface && (pd->overlay.flag & V3D_OVERLAY_FACE_ORIENTATION); const bool draw_bones = (pd->overlay.flag & V3D_OVERLAY_HIDE_BONES) == 0; @@ -361,6 +375,12 @@ static void OVERLAY_cache_populate(void *vedata, Object *ob) OVERLAY_particle_cache_populate(vedata, ob); } + /* TODO: these should not be overlays, just here for testing since it's + * easier to implement than integrating it into eevee/workbench. */ + if (ob->type == OB_POINTCLOUD) { + OVERLAY_pointcloud_cache_populate(vedata, ob); + } + /* Relationship, object center, bounbox ... */ if (!pd->hide_overlays) { OVERLAY_extra_cache_populate(vedata, ob); @@ -434,6 +454,7 @@ static void OVERLAY_draw_scene(void *vedata) OVERLAY_armature_draw(vedata); OVERLAY_particle_draw(vedata); OVERLAY_metaball_draw(vedata); + OVERLAY_pointcloud_draw(vedata); OVERLAY_gpencil_draw(vedata); OVERLAY_extra_draw(vedata); diff --git a/source/blender/draw/engines/overlay/overlay_extra.c b/source/blender/draw/engines/overlay/overlay_extra.c index 53550fb115e..4c525acd5be 100644 --- a/source/blender/draw/engines/overlay/overlay_extra.c +++ b/source/blender/draw/engines/overlay/overlay_extra.c @@ -475,13 +475,25 @@ static void OVERLAY_texture_space(OVERLAY_ExtraCallBuffers *cb, Object *ob, cons texcosize = mb->size; break; } + case ID_HA: + case ID_PT: + case ID_VO: { + /* No user defined texture space support. */ + break; + } default: BLI_assert(0); } float mat[4][4]; - size_to_mat4(mat, texcosize); - copy_v3_v3(mat[3], texcoloc); + + if (texcoloc != NULL && texcosize != NULL) { + size_to_mat4(mat, texcosize); + copy_v3_v3(mat[3], texcoloc); + } + else { + unit_m4(mat); + } mul_m4_m4m4(mat, ob->obmat, mat); diff --git a/source/blender/draw/engines/overlay/overlay_private.h b/source/blender/draw/engines/overlay/overlay_private.h index 167a8e940df..de35d4f760e 100644 --- a/source/blender/draw/engines/overlay/overlay_private.h +++ b/source/blender/draw/engines/overlay/overlay_private.h @@ -95,6 +95,7 @@ typedef struct OVERLAY_PassList { DRWPass *paint_color_ps; DRWPass *paint_overlay_ps; DRWPass *particle_ps; + DRWPass *pointcloud_ps; DRWPass *sculpt_mask_ps; DRWPass *wireframe_ps; DRWPass *wireframe_xray_ps; @@ -251,6 +252,7 @@ typedef struct OVERLAY_PrivateData { DRWShadingGroup *paint_face_grp; DRWShadingGroup *particle_dots_grp; DRWShadingGroup *particle_shapes_grp; + DRWShadingGroup *pointcloud_dots_grp; DRWShadingGroup *sculpt_mask_grp; DRWShadingGroup *wires_grp[2][2]; /* With and without coloring. */ DRWShadingGroup *wires_all_grp[2][2]; /* With and without coloring. */ @@ -532,6 +534,10 @@ void OVERLAY_particle_cache_init(OVERLAY_Data *vedata); void OVERLAY_particle_cache_populate(OVERLAY_Data *vedata, Object *ob); void OVERLAY_particle_draw(OVERLAY_Data *vedata); +void OVERLAY_pointcloud_cache_init(OVERLAY_Data *vedata); +void OVERLAY_pointcloud_cache_populate(OVERLAY_Data *vedata, Object *ob); +void OVERLAY_pointcloud_draw(OVERLAY_Data *vedata); + void OVERLAY_sculpt_cache_init(OVERLAY_Data *vedata); void OVERLAY_sculpt_cache_populate(OVERLAY_Data *vedata, Object *ob); void OVERLAY_sculpt_draw(OVERLAY_Data *vedata); @@ -597,6 +603,7 @@ GPUShader *OVERLAY_shader_paint_weight(void); GPUShader *OVERLAY_shader_paint_wire(void); GPUShader *OVERLAY_shader_particle_dot(void); GPUShader *OVERLAY_shader_particle_shape(void); +GPUShader *OVERLAY_shader_pointcloud_dot(void); GPUShader *OVERLAY_shader_sculpt_mask(void); GPUShader *OVERLAY_shader_volume_velocity(bool use_needle); GPUShader *OVERLAY_shader_wireframe(void); diff --git a/source/blender/draw/engines/overlay/overlay_shader.c b/source/blender/draw/engines/overlay/overlay_shader.c index d5509c7f56b..0b2f98294ec 100644 --- a/source/blender/draw/engines/overlay/overlay_shader.c +++ b/source/blender/draw/engines/overlay/overlay_shader.c @@ -102,6 +102,8 @@ extern char datatoc_paint_weight_vert_glsl[]; extern char datatoc_paint_wire_vert_glsl[]; extern char datatoc_particle_vert_glsl[]; extern char datatoc_particle_frag_glsl[]; +extern char datatoc_pointcloud_vert_glsl[]; +extern char datatoc_pointcloud_frag_glsl[]; extern char datatoc_sculpt_mask_vert_glsl[]; extern char datatoc_volume_velocity_vert_glsl[]; extern char datatoc_wireframe_vert_glsl[]; @@ -184,6 +186,7 @@ typedef struct OVERLAY_Shaders { GPUShader *paint_wire; GPUShader *particle_dot; GPUShader *particle_shape; + GPUShader *pointcloud_dot; GPUShader *sculpt_mask; GPUShader *uniform_color; GPUShader *volume_velocity_needle_sh; @@ -1272,6 +1275,25 @@ GPUShader *OVERLAY_shader_particle_shape(void) return sh_data->particle_shape; } +GPUShader *OVERLAY_shader_pointcloud_dot(void) +{ + const DRWContextState *draw_ctx = DRW_context_state_get(); + const GPUShaderConfigData *sh_cfg = &GPU_shader_cfg_data[draw_ctx->sh_cfg]; + OVERLAY_Shaders *sh_data = &e_data.sh_data[draw_ctx->sh_cfg]; + if (!sh_data->pointcloud_dot) { + sh_data->pointcloud_dot = GPU_shader_create_from_arrays({ + .vert = (const char *[]){sh_cfg->lib, + datatoc_common_globals_lib_glsl, + datatoc_common_view_lib_glsl, + datatoc_pointcloud_vert_glsl, + NULL}, + .frag = (const char *[]){datatoc_pointcloud_frag_glsl, NULL}, + .defs = (const char *[]){sh_cfg->def, "#define USE_DOTS\n", NULL}, + }); + } + return sh_data->pointcloud_dot; +} + GPUShader *OVERLAY_shader_sculpt_mask(void) { const DRWContextState *draw_ctx = DRW_context_state_get(); diff --git a/source/blender/draw/engines/overlay/overlay_wireframe.c b/source/blender/draw/engines/overlay/overlay_wireframe.c index 6b0e5e0b72e..cdf8005b827 100644 --- a/source/blender/draw/engines/overlay/overlay_wireframe.c +++ b/source/blender/draw/engines/overlay/overlay_wireframe.c @@ -22,6 +22,7 @@ #include "DNA_mesh_types.h" #include "DNA_view3d_types.h" +#include "DNA_volume_types.h" #include "BKE_curve.h" #include "BKE_displist.h" @@ -163,6 +164,20 @@ void OVERLAY_wireframe_cache_populate(OVERLAY_Data *vedata, } } + if (use_wire && ob->type == OB_VOLUME) { + /* Volume object as points exception. */ + Volume *volume = ob->data; + if (volume->display.wireframe_type == VOLUME_WIREFRAME_POINTS) { + float *color; + OVERLAY_ExtraCallBuffers *cb = OVERLAY_extra_call_buffer_get(vedata, ob); + DRW_object_wire_theme_get(ob, draw_ctx->view_layer, &color); + + struct GPUBatch *geom = DRW_cache_object_face_wireframe_get(ob); + OVERLAY_extra_loose_points(cb, geom, ob->obmat, color); + return; + } + } + const bool is_edit_mode = DRW_object_is_in_edit_mode(ob); bool has_edit_mesh_cage = false; if (is_mesh && is_edit_mode) { diff --git a/source/blender/draw/engines/workbench/shaders/workbench_volume_frag.glsl b/source/blender/draw/engines/workbench/shaders/workbench_volume_frag.glsl index e957f8bbe9c..0c2b7850f94 100644 --- a/source/blender/draw/engines/workbench/shaders/workbench_volume_frag.glsl +++ b/source/blender/draw/engines/workbench/shaders/workbench_volume_frag.glsl @@ -11,6 +11,7 @@ uniform sampler3D shadowTexture; uniform sampler3D flameTexture; uniform sampler1D flameColorTexture; uniform sampler1D transferTexture; +uniform mat4 volumeObjectToTexture; uniform int samplesLen = 256; uniform float noiseOfs = 0.0; @@ -110,20 +111,24 @@ void volume_properties(vec3 ls_pos, out vec3 scattering, out float extinction) scattering = tval.rgb * 1500.0; extinction = max(1e-4, tval.a * 50.0); #else +# ifdef VOLUME_SMOKE float flame = sample_volume_texture(flameTexture, co).r; vec4 emission = texture(flameColorTexture, flame); +# endif + vec3 density = sample_volume_texture(densityTexture, co).rgb; float shadows = sample_volume_texture(shadowTexture, co).r; - vec4 density = sample_volume_texture(densityTexture, co); /* rgb: color, a: density */ - scattering = density.rgb * densityScale; + scattering = density * densityScale; extinction = max(1e-4, dot(scattering, vec3(0.33333))); scattering *= activeColor; /* Scale shadows in log space and clamp them to avoid completely black shadows. */ scattering *= exp(clamp(log(shadows) * densityScale * 0.1, -2.5, 0.0)) * M_PI; +# ifdef VOLUME_SMOKE /* 800 is arbitrary and here to mimic old viewport. TODO make it a parameter */ scattering += pow(emission.rgb, vec3(2.2)) * emission.a * 800.0; +# endif #endif } @@ -201,9 +206,16 @@ void main() vec3 ls_ray_ori = point_view_to_object(vs_ray_ori); vec3 ls_ray_end = point_view_to_object(vs_ray_end); +# ifdef VOLUME_SMOKE ls_ray_dir = (OrcoTexCoFactors[0].xyz + ls_ray_dir * OrcoTexCoFactors[1].xyz) * 2.0 - 1.0; ls_ray_ori = (OrcoTexCoFactors[0].xyz + ls_ray_ori * OrcoTexCoFactors[1].xyz) * 2.0 - 1.0; ls_ray_end = (OrcoTexCoFactors[0].xyz + ls_ray_end * OrcoTexCoFactors[1].xyz) * 2.0 - 1.0; +# else + ls_ray_dir = (volumeObjectToTexture * vec4(ls_ray_dir, 1.0)).xyz * 2.0f - 1.0; + ls_ray_ori = (volumeObjectToTexture * vec4(ls_ray_ori, 1.0)).xyz * 2.0f - 1.0; + ls_ray_end = (volumeObjectToTexture * vec4(ls_ray_end, 1.0)).xyz * 2.0f - 1.0; +# endif + ls_ray_dir -= ls_ray_ori; /* TODO: Align rays to volume center so that it mimics old behaviour of slicing the volume. */ diff --git a/source/blender/draw/engines/workbench/shaders/workbench_volume_vert.glsl b/source/blender/draw/engines/workbench/shaders/workbench_volume_vert.glsl index 1a32a202290..7327a92e04f 100644 --- a/source/blender/draw/engines/workbench/shaders/workbench_volume_vert.glsl +++ b/source/blender/draw/engines/workbench/shaders/workbench_volume_vert.glsl @@ -5,6 +5,8 @@ uniform float slicePosition; uniform int sliceAxis; /* -1 is no slice, 0 is X, 1 is Y, 2 is Z. */ +uniform mat4 volumeTextureToObject; + in vec3 pos; RESOURCE_ID_VARYING @@ -31,7 +33,12 @@ void main() #else vec3 final_pos = pos; #endif + +#ifdef VOLUME_SMOKE final_pos = ((final_pos * 0.5 + 0.5) - OrcoTexCoFactors[0].xyz) / OrcoTexCoFactors[1].xyz; +#else + final_pos = (volumeTextureToObject * vec4(final_pos * 0.5 + 0.5, 1.0)).xyz; +#endif gl_Position = point_object_to_ndc(final_pos); PASS_RESOURCE_ID diff --git a/source/blender/draw/engines/workbench/workbench_engine.c b/source/blender/draw/engines/workbench/workbench_engine.c index 742489c7311..e9525af5682 100644 --- a/source/blender/draw/engines/workbench/workbench_engine.c +++ b/source/blender/draw/engines/workbench/workbench_engine.c @@ -33,8 +33,9 @@ #include "BKE_paint.h" #include "BKE_particle.h" -#include "DNA_image_types.h" #include "DNA_fluid_types.h" +#include "DNA_hair_types.h" +#include "DNA_image_types.h" #include "DNA_mesh_types.h" #include "DNA_modifier_types.h" #include "DNA_node_types.h" @@ -192,12 +193,12 @@ static void workbench_cache_common_populate(WORKBENCH_PrivateData *wpd, static void workbench_cache_hair_populate(WORKBENCH_PrivateData *wpd, Object *ob, + ParticleSystem *psys, ModifierData *md, eV3DShadingColorType color_type, - bool use_texpaint_mode) + bool use_texpaint_mode, + const int matnr) { - ParticleSystem *psys = ((ParticleSystemModifierData *)md)->psys; - ParticleSettings *part = psys->part; const DRWContextState *draw_ctx = DRW_context_state_get(); const Scene *scene = draw_ctx->scene; @@ -206,8 +207,8 @@ static void workbench_cache_hair_populate(WORKBENCH_PrivateData *wpd, int interp = (imapaint && imapaint->interp == IMAGEPAINT_INTERP_LINEAR) ? SHD_INTERP_LINEAR : SHD_INTERP_CLOSEST; DRWShadingGroup *grp = (use_texpaint_mode) ? - workbench_image_hair_setup(wpd, ob, part->omat, ima, NULL, interp) : - workbench_material_hair_setup(wpd, ob, part->omat, color_type); + workbench_image_hair_setup(wpd, ob, matnr, ima, NULL, interp) : + workbench_material_hair_setup(wpd, ob, matnr, color_type); DRW_shgroup_hair_create_sub(ob, psys, md, grp); } @@ -244,14 +245,20 @@ static eV3DShadingColorType workbench_color_type_get(WORKBENCH_PrivateData *wpd, color_type = V3D_SHADING_OBJECT_COLOR; } - *r_sculpt_pbvh = is_sculpt_pbvh; - *r_texpaint_mode = false; + if (r_sculpt_pbvh) { + *r_sculpt_pbvh = is_sculpt_pbvh; + } + if (r_texpaint_mode) { + *r_texpaint_mode = false; + } if (!is_sculpt_pbvh && !is_render) { /* Force texture or vertex mode if object is in paint mode. */ if (is_texpaint_mode && me && me->mloopuv) { color_type = V3D_SHADING_TEXTURE_COLOR; - *r_texpaint_mode = true; + if (r_texpaint_mode) { + *r_texpaint_mode = true; + } } else if (is_vertpaint_mode && me && me->mloopcol) { color_type = V3D_SHADING_VERTEX_COLOR; @@ -286,8 +293,8 @@ void workbench_cache_populate(void *ved, Object *ob) } if (ob->type == OB_MESH && ob->modifiers.first != NULL) { - bool use_sculpt_pbvh, use_texpaint_mode; - int color_type = workbench_color_type_get(wpd, ob, &use_sculpt_pbvh, &use_texpaint_mode, NULL); + bool use_texpaint_mode; + int color_type = workbench_color_type_get(wpd, ob, NULL, &use_texpaint_mode, NULL); LISTBASE_FOREACH (ModifierData *, md, &ob->modifiers) { if (md->type != eModifierType_ParticleSystem) { @@ -301,7 +308,8 @@ void workbench_cache_populate(void *ved, Object *ob) const int draw_as = (part->draw_as == PART_DRAW_REND) ? part->ren_as : part->draw_as; if (draw_as == PART_DRAW_PATH) { - workbench_cache_hair_populate(wpd, ob, md, color_type, use_texpaint_mode); + workbench_cache_hair_populate( + wpd, ob, psys, md, color_type, use_texpaint_mode, part->omat); } } } @@ -311,7 +319,7 @@ void workbench_cache_populate(void *ved, Object *ob) if (md && modifier_isEnabled(wpd->scene, md, eModifierMode_Realtime)) { FluidModifierData *fmd = (FluidModifierData *)md; if (fmd->domain && fmd->domain->type == FLUID_DOMAIN_TYPE_GAS) { - workbench_volume_cache_populate(vedata, wpd->scene, ob, md); + workbench_volume_cache_populate(vedata, wpd->scene, ob, md, V3D_SHADING_SINGLE_COLOR); return; /* Do not draw solid in this case. */ } } @@ -325,7 +333,7 @@ void workbench_cache_populate(void *ved, Object *ob) return; } - if (ELEM(ob->type, OB_MESH, OB_CURVE, OB_SURF, OB_FONT, OB_MBALL)) { + if (ELEM(ob->type, OB_MESH, OB_CURVE, OB_SURF, OB_FONT, OB_MBALL, OB_POINTCLOUD)) { bool use_sculpt_pbvh, use_texpaint_mode, draw_shadow, has_transp_mat = false; eV3DShadingColorType color_type = workbench_color_type_get( wpd, ob, &use_sculpt_pbvh, &use_texpaint_mode, &draw_shadow); @@ -344,6 +352,16 @@ void workbench_cache_populate(void *ved, Object *ob) workbench_shadow_cache_populate(vedata, ob, has_transp_mat); } } + else if (ob->type == OB_HAIR) { + int color_type = workbench_color_type_get(wpd, ob, NULL, NULL, NULL); + workbench_cache_hair_populate(wpd, ob, NULL, NULL, color_type, false, HAIR_MATERIAL_NR); + } + else if (ob->type == OB_VOLUME) { + if (wpd->shading.type != OB_WIRE) { + int color_type = workbench_color_type_get(wpd, ob, NULL, NULL, NULL); + workbench_volume_cache_populate(vedata, wpd->scene, ob, NULL, color_type); + } + } } void workbench_cache_finish(void *ved) diff --git a/source/blender/draw/engines/workbench/workbench_private.h b/source/blender/draw/engines/workbench/workbench_private.h index a68b66e0f85..49becf93f48 100644 --- a/source/blender/draw/engines/workbench/workbench_private.h +++ b/source/blender/draw/engines/workbench/workbench_private.h @@ -101,6 +101,7 @@ typedef struct WORKBENCH_TextureList { struct GPUTexture *smaa_area_tx; struct GPUTexture *dummy_image_tx; struct GPUTexture *dummy_volume_tx; + struct GPUTexture *dummy_shadow_tx; struct GPUTexture *dummy_coba_tx; } WORKBENCH_TextureList; @@ -405,7 +406,7 @@ GPUShader *workbench_shader_outline_get(void); GPUShader *workbench_shader_antialiasing_accumulation_get(void); GPUShader *workbench_shader_antialiasing_get(int stage); -GPUShader *workbench_shader_volume_get(bool slice, bool coba, bool cubic); +GPUShader *workbench_shader_volume_get(bool slice, bool coba, bool cubic, bool smoke); void workbench_shader_depth_of_field_get(GPUShader **prepare_sh, GPUShader **downsample_sh, @@ -479,7 +480,8 @@ void workbench_volume_cache_init(WORKBENCH_Data *vedata); void workbench_volume_cache_populate(WORKBENCH_Data *vedata, struct Scene *UNUSED(scene), struct Object *ob, - struct ModifierData *md); + struct ModifierData *md, + eV3DShadingColorType color_type); void workbench_volume_draw_pass(WORKBENCH_Data *vedata); void workbench_volume_draw_finish(WORKBENCH_Data *vedata); diff --git a/source/blender/draw/engines/workbench/workbench_shader.c b/source/blender/draw/engines/workbench/workbench_shader.c index 14a980fe628..2e796056029 100644 --- a/source/blender/draw/engines/workbench/workbench_shader.c +++ b/source/blender/draw/engines/workbench/workbench_shader.c @@ -106,7 +106,7 @@ static struct { struct GPUShader *aa_accum_sh; struct GPUShader *smaa_sh[3]; - struct GPUShader *volume_sh[2][2][2]; + struct GPUShader *volume_sh[2][2][2][2]; struct DRWShaderLibrary *lib; } e_data = {{{{NULL}}}}; @@ -448,9 +448,9 @@ GPUShader *workbench_shader_antialiasing_get(int stage) return e_data.smaa_sh[stage]; } -GPUShader *workbench_shader_volume_get(bool slice, bool coba, bool cubic) +GPUShader *workbench_shader_volume_get(bool slice, bool coba, bool cubic, bool smoke) { - GPUShader **shader = &e_data.volume_sh[slice][coba][cubic]; + GPUShader **shader = &e_data.volume_sh[slice][coba][cubic][smoke]; if (*shader == NULL) { DynStr *ds = BLI_dynstr_new(); @@ -464,6 +464,9 @@ GPUShader *workbench_shader_volume_get(bool slice, bool coba, bool cubic) if (cubic) { BLI_dynstr_append(ds, "#define USE_TRICUBIC\n"); } + if (smoke) { + BLI_dynstr_append(ds, "#define VOLUME_SMOKE\n"); + } char *defines = BLI_dynstr_get_cstring(ds); BLI_dynstr_free(ds); @@ -513,7 +516,7 @@ void workbench_shader_free(void) DRW_SHADER_FREE_SAFE(sh_array[j]); } for (int j = 0; j < sizeof(e_data.volume_sh) / sizeof(void *); j++) { - struct GPUShader **sh_array = &e_data.volume_sh[0][0][0]; + struct GPUShader **sh_array = &e_data.volume_sh[0][0][0][0]; DRW_SHADER_FREE_SAFE(sh_array[j]); } diff --git a/source/blender/draw/engines/workbench/workbench_volume.c b/source/blender/draw/engines/workbench/workbench_volume.c index a3072b834bd..1eccc99d9e9 100644 --- a/source/blender/draw/engines/workbench/workbench_volume.c +++ b/source/blender/draw/engines/workbench/workbench_volume.c @@ -22,16 +22,20 @@ #include "workbench_private.h" -#include "BKE_object.h" -#include "BKE_fluid.h" +#include "DNA_fluid_types.h" +#include "DNA_modifier_types.h" +#include "DNA_object_force_types.h" +#include "DNA_volume_types.h" #include "BLI_rand.h" #include "BLI_dynstr.h" #include "BLI_string_utils.h" -#include "DNA_modifier_types.h" -#include "DNA_object_force_types.h" -#include "DNA_fluid_types.h" +#include "BKE_fluid.h" +#include "BKE_global.h" +#include "BKE_object.h" +#include "BKE_volume.h" +#include "BKE_volume_render.h" #include "GPU_draw.h" @@ -40,9 +44,11 @@ void workbench_volume_engine_init(WORKBENCH_Data *vedata) WORKBENCH_TextureList *txl = vedata->txl; if (txl->dummy_volume_tx == NULL) { - float pixel[4] = {0.0f, 0.0f, 0.0f, 0.0f}; - txl->dummy_volume_tx = GPU_texture_create_3d(1, 1, 1, GPU_RGBA8, pixel, NULL); - txl->dummy_coba_tx = GPU_texture_create_1d(1, GPU_RGBA8, pixel, NULL); + float zero[4] = {0.0f, 0.0f, 0.0f, 0.0f}; + float one[4] = {1.0f, 1.0f, 1.0f, 1.0f}; + txl->dummy_volume_tx = GPU_texture_create_3d(1, 1, 1, GPU_RGBA8, zero, NULL); + txl->dummy_shadow_tx = GPU_texture_create_3d(1, 1, 1, GPU_RGBA8, one, NULL); + txl->dummy_coba_tx = GPU_texture_create_1d(1, GPU_RGBA8, zero, NULL); } } @@ -54,10 +60,9 @@ void workbench_volume_cache_init(WORKBENCH_Data *vedata) vedata->stl->wpd->volumes_do = false; } -void workbench_volume_cache_populate(WORKBENCH_Data *vedata, - Scene *UNUSED(scene), - Object *ob, - ModifierData *md) +static void workbench_volume_modifier_cache_populate(WORKBENCH_Data *vedata, + Object *ob, + ModifierData *md) { FluidModifierData *mmd = (FluidModifierData *)md; FluidDomainSettings *mds = mmd->domain; @@ -90,8 +95,7 @@ void workbench_volume_cache_populate(WORKBENCH_Data *vedata, const bool use_slice = (mds->slice_method == FLUID_DOMAIN_SLICE_AXIS_ALIGNED && mds->axis_slice_method == AXIS_SLICE_SINGLE); const bool cubic_interp = (mds->interp_method == VOLUME_INTERP_CUBIC); - - GPUShader *sh = workbench_shader_volume_get(use_slice, mds->use_coba, cubic_interp); + GPUShader *sh = workbench_shader_volume_get(use_slice, mds->use_coba, cubic_interp, true); if (use_slice) { float invviewmat[4][4]; @@ -162,6 +166,107 @@ void workbench_volume_cache_populate(WORKBENCH_Data *vedata, BLI_addtail(&wpd->smoke_domains, BLI_genericNodeN(mmd)); } +static void workbench_volume_material_color(WORKBENCH_PrivateData *wpd, + Object *ob, + eV3DShadingColorType color_type, + float color[3]) +{ + Material *ma = BKE_object_material_get(ob, VOLUME_MATERIAL_NR); + WORKBENCH_UBO_Material ubo_data; + workbench_material_ubo_data(wpd, ob, ma, &ubo_data, color_type); + copy_v3_v3(color, ubo_data.base_color); +} + +static void workbench_volume_object_cache_populate(WORKBENCH_Data *vedata, + Object *ob, + eV3DShadingColorType color_type) +{ + /* Create 3D textures. */ + Volume *volume = ob->data; + BKE_volume_load(volume, G.main); + VolumeGrid *volume_grid = BKE_volume_grid_active_get(volume); + if (volume_grid == NULL) { + return; + } + DRWVolumeGrid *grid = DRW_volume_batch_cache_get_grid(volume, volume_grid); + if (grid == NULL) { + return; + } + + WORKBENCH_PrivateData *wpd = vedata->stl->wpd; + WORKBENCH_TextureList *txl = vedata->txl; + DefaultTextureList *dtxl = DRW_viewport_texture_list_get(); + + wpd->volumes_do = true; + + /* Create shader. */ + GPUShader *sh = workbench_shader_volume_get(false, false, false, false); + + /* Compute color. */ + float color[3]; + workbench_volume_material_color(wpd, ob, color_type, color); + + /* Combined texture to object, and object to world transform. */ + float texture_to_world[4][4]; + mul_m4_m4m4(texture_to_world, ob->obmat, grid->texture_to_object); + + /* Compute world space dimensions for step size. */ + float world_size[3]; + mat4_to_size(world_size, texture_to_world); + abs_v3(world_size); + + /* Compute step parameters. */ + double noise_ofs; + BLI_halton_1d(3, 0.0, wpd->taa_sample, &noise_ofs); + float step_length, max_slice; + int resolution[3]; + GPU_texture_get_mipmap_size(grid->texture, 0, resolution); + float slice_ct[3] = {resolution[0], resolution[1], resolution[2]}; + mul_v3_fl(slice_ct, max_ff(0.001f, 5.0f)); + max_slice = max_fff(slice_ct[0], slice_ct[1], slice_ct[2]); + invert_v3(slice_ct); + mul_v3_v3(slice_ct, world_size); + step_length = len_v3(slice_ct); + + /* Compute density scale. */ + const float density_scale = volume->display.density; + + /* Set uniforms. */ + DRWShadingGroup *grp = DRW_shgroup_create(sh, vedata->psl->volume_ps); + DRW_shgroup_uniform_block(grp, "world_block", wpd->world_ubo); + DRW_shgroup_uniform_int_copy(grp, "samplesLen", max_slice); + DRW_shgroup_uniform_float_copy(grp, "stepLength", step_length); + DRW_shgroup_uniform_float_copy(grp, "noiseOfs", noise_ofs); + DRW_shgroup_state_enable(grp, DRW_STATE_CULL_FRONT); + + DRW_shgroup_uniform_texture(grp, "densityTexture", grid->texture); + /* TODO: implement shadow texture, see manta_smoke_calc_transparency. */ + DRW_shgroup_uniform_texture(grp, "shadowTexture", txl->dummy_shadow_tx); + DRW_shgroup_uniform_vec3_copy(grp, "activeColor", color); + + DRW_shgroup_uniform_texture_ref(grp, "depthBuffer", &dtxl->depth); + DRW_shgroup_uniform_float_copy(grp, "densityScale", density_scale); + + DRW_shgroup_uniform_mat4(grp, "volumeObjectToTexture", grid->object_to_texture); + DRW_shgroup_uniform_mat4(grp, "volumeTextureToObject", grid->texture_to_object); + + DRW_shgroup_call(grp, DRW_cache_cube_get(), ob); +} + +void workbench_volume_cache_populate(WORKBENCH_Data *vedata, + Scene *UNUSED(scene), + Object *ob, + ModifierData *md, + eV3DShadingColorType color_type) +{ + if (md == NULL) { + workbench_volume_object_cache_populate(vedata, ob, color_type); + } + else { + workbench_volume_modifier_cache_populate(vedata, ob, md); + } +} + void workbench_volume_draw_pass(WORKBENCH_Data *vedata) { WORKBENCH_PassList *psl = vedata->psl; @@ -189,4 +294,4 @@ void workbench_volume_draw_finish(WORKBENCH_Data *vedata) GPU_free_smoke(mmd); } BLI_freelistN(&wpd->smoke_domains); -}
\ No newline at end of file +} diff --git a/source/blender/draw/intern/draw_cache.c b/source/blender/draw/intern/draw_cache.c index d0cea5b8c5c..1f76b5ac431 100644 --- a/source/blender/draw/intern/draw_cache.c +++ b/source/blender/draw/intern/draw_cache.c @@ -19,13 +19,16 @@ */ #include "DNA_scene_types.h" +#include "DNA_hair_types.h" #include "DNA_mesh_types.h" #include "DNA_meta_types.h" #include "DNA_curve_types.h" -#include "DNA_object_types.h" -#include "DNA_particle_types.h" #include "DNA_modifier_types.h" #include "DNA_lattice_types.h" +#include "DNA_object_types.h" +#include "DNA_particle_types.h" +#include "DNA_pointcloud_types.h" +#include "DNA_volume_types.h" #include "UI_resources.h" @@ -799,6 +802,12 @@ GPUBatch *DRW_cache_object_edge_detection_get(Object *ob, bool *r_is_manifold) return DRW_cache_text_edge_detection_get(ob, r_is_manifold); case OB_MBALL: return DRW_cache_mball_edge_detection_get(ob, r_is_manifold); + case OB_HAIR: + return NULL; + case OB_POINTCLOUD: + return NULL; + case OB_VOLUME: + return NULL; default: return NULL; } @@ -817,6 +826,12 @@ GPUBatch *DRW_cache_object_face_wireframe_get(Object *ob) return DRW_cache_text_face_wireframe_get(ob); case OB_MBALL: return DRW_cache_mball_face_wireframe_get(ob); + case OB_HAIR: + return NULL; + case OB_POINTCLOUD: + return NULL; + case OB_VOLUME: + return DRW_cache_volume_face_wireframe_get(ob); case OB_GPENCIL: { return DRW_cache_gpencil_face_wireframe_get(ob); } @@ -837,7 +852,13 @@ GPUBatch *DRW_cache_object_loose_edges_get(struct Object *ob) case OB_FONT: return DRW_cache_text_loose_edges_get(ob); case OB_MBALL: - /* Cannot have any loose edge */ + return NULL; + case OB_HAIR: + return NULL; + case OB_POINTCLOUD: + return NULL; + case OB_VOLUME: + return NULL; default: return NULL; } @@ -856,6 +877,12 @@ GPUBatch *DRW_cache_object_surface_get(Object *ob) return DRW_cache_text_surface_get(ob); case OB_MBALL: return DRW_cache_mball_surface_get(ob); + case OB_HAIR: + return NULL; + case OB_POINTCLOUD: + return NULL; + case OB_VOLUME: + return NULL; default: return NULL; } @@ -875,6 +902,12 @@ int DRW_cache_object_material_count_get(struct Object *ob) return DRW_curve_material_count_get(ob->data); case OB_MBALL: return DRW_metaball_material_count_get(ob->data); + case OB_HAIR: + return DRW_hair_material_count_get(ob->data); + case OB_POINTCLOUD: + return DRW_pointcloud_material_count_get(ob->data); + case OB_VOLUME: + return DRW_volume_material_count_get(ob->data); default: BLI_assert(0); return 0; @@ -896,6 +929,12 @@ GPUBatch **DRW_cache_object_surface_material_get(struct Object *ob, return DRW_cache_text_surface_shaded_get(ob, gpumat_array, gpumat_array_len); case OB_MBALL: return DRW_cache_mball_surface_shaded_get(ob, gpumat_array, gpumat_array_len); + case OB_HAIR: + return NULL; + case OB_POINTCLOUD: + return NULL; + case OB_VOLUME: + return NULL; default: return NULL; } @@ -3214,6 +3253,27 @@ GPUBatch *DRW_cache_lattice_vert_overlay_get(Object *ob) /** \} */ /* -------------------------------------------------------------------- */ +/** \name PointCloud + * \{ */ + +GPUBatch *DRW_cache_pointcloud_get_dots(Object *object) +{ + return DRW_pointcloud_batch_cache_get_dots(object); +} + +/* -------------------------------------------------------------------- */ +/** \name Volume + * \{ */ + +GPUBatch *DRW_cache_volume_face_wireframe_get(Object *ob) +{ + BLI_assert(ob->type == OB_VOLUME); + return DRW_volume_batch_cache_get_wireframes_face(ob->data); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ /** \name Particles * \{ */ @@ -3444,6 +3504,15 @@ void drw_batch_cache_validate(Object *ob) case OB_LATTICE: DRW_lattice_batch_cache_validate((Lattice *)ob->data); break; + case OB_HAIR: + DRW_hair_batch_cache_validate((Hair *)ob->data); + break; + case OB_POINTCLOUD: + DRW_pointcloud_batch_cache_validate((PointCloud *)ob->data); + break; + case OB_VOLUME: + DRW_volume_batch_cache_validate((Volume *)ob->data); + break; default: break; } diff --git a/source/blender/draw/intern/draw_cache.h b/source/blender/draw/intern/draw_cache.h index 8ac0d7ada21..77c7b6b9307 100644 --- a/source/blender/draw/intern/draw_cache.h +++ b/source/blender/draw/intern/draw_cache.h @@ -29,6 +29,8 @@ struct ModifierData; struct Object; struct PTCacheEdit; struct ParticleSystem; +struct Volume; +struct VolumeGrid; struct bGPDstroke; void DRW_shape_cache_free(void); @@ -200,6 +202,39 @@ struct GPUBatch **DRW_cache_mball_surface_shaded_get(struct Object *ob, struct GPUBatch *DRW_cache_mball_face_wireframe_get(struct Object *ob); struct GPUBatch *DRW_cache_mball_edge_detection_get(struct Object *ob, bool *r_is_manifold); +/* Hair */ +struct GPUBatch *DRW_cache_hair_surface_get(struct Object *ob); +struct GPUBatch **DRW_cache_hair_surface_shaded_get(struct Object *ob, + struct GPUMaterial **gpumat_array, + uint gpumat_array_len); +struct GPUBatch *DRW_cache_hair_face_wireframe_get(struct Object *ob); +struct GPUBatch *DRW_cache_hair_edge_detection_get(struct Object *ob, bool *r_is_manifold); + +/* PointCloud */ +struct GPUBatch *DRW_cache_pointcloud_get_dots(struct Object *obj); + +/* Volume */ +typedef struct DRWVolumeGrid { + struct DRWVolumeGrid *next, *prev; + + /* Grid name. */ + char *name; + + /* 3D texture. */ + struct GPUTexture *texture; + + /* Transform between 0..1 texture space and object space. */ + float texture_to_object[4][4]; + float object_to_texture[4][4]; + + /* Transfrom from bounds to texture space. */ + float object_to_bounds[4][4]; + float bounds_to_texture[4][4]; +} DRWVolumeGrid; + +DRWVolumeGrid *DRW_volume_batch_cache_get_grid(struct Volume *volume, struct VolumeGrid *grid); +struct GPUBatch *DRW_cache_volume_face_wireframe_get(struct Object *ob); + /* GPencil */ struct GPUBatch *DRW_cache_gpencil_strokes_get(struct Object *ob, int cfra); struct GPUBatch *DRW_cache_gpencil_fills_get(struct Object *ob, int cfra); diff --git a/source/blender/draw/intern/draw_cache_impl.h b/source/blender/draw/intern/draw_cache_impl.h index 755f794d201..b3450bf4715 100644 --- a/source/blender/draw/intern/draw_cache_impl.h +++ b/source/blender/draw/intern/draw_cache_impl.h @@ -32,11 +32,14 @@ struct ModifierData; struct PTCacheEdit; struct ParticleSystem; +struct bGPdata; struct Curve; +struct Hair; struct Lattice; struct Mesh; struct MetaBall; -struct bGPdata; +struct PointCloud; +struct Volume; /* Expose via BKE callbacks */ void DRW_mball_batch_cache_dirty_tag(struct MetaBall *mb, int mode); @@ -61,11 +64,26 @@ void DRW_particle_batch_cache_free(struct ParticleSystem *psys); void DRW_gpencil_batch_cache_dirty_tag(struct bGPdata *gpd); void DRW_gpencil_batch_cache_free(struct bGPdata *gpd); +void DRW_hair_batch_cache_dirty_tag(struct Hair *hair, int mode); +void DRW_hair_batch_cache_validate(struct Hair *hair); +void DRW_hair_batch_cache_free(struct Hair *hair); + +void DRW_pointcloud_batch_cache_dirty_tag(struct PointCloud *pointcloud, int mode); +void DRW_pointcloud_batch_cache_validate(struct PointCloud *pointcloud); +void DRW_pointcloud_batch_cache_free(struct PointCloud *pointcloud); + +void DRW_volume_batch_cache_dirty_tag(struct Volume *volume, int mode); +void DRW_volume_batch_cache_validate(struct Volume *volume); +void DRW_volume_batch_cache_free(struct Volume *volume); + /* Garbage collection */ void DRW_batch_cache_free_old(struct Object *ob, int ctime); void DRW_mesh_batch_cache_free_old(struct Mesh *me, int ctime); +/* Generic */ +void DRW_vertbuf_create_wiredata(struct GPUVertBuf *vbo, const int vert_len); + /* Curve */ void DRW_curve_batch_cache_create_requested(struct Object *ob); @@ -118,6 +136,19 @@ struct GPUBatch *DRW_lattice_batch_cache_get_all_edges(struct Lattice *lt, struct GPUBatch *DRW_lattice_batch_cache_get_all_verts(struct Lattice *lt); struct GPUBatch *DRW_lattice_batch_cache_get_edit_verts(struct Lattice *lt); +/* Hair */ +int DRW_hair_material_count_get(struct Hair *hair); + +/* PointCloud */ +int DRW_pointcloud_material_count_get(struct PointCloud *pointcloud); + +struct GPUBatch *DRW_pointcloud_batch_cache_get_dots(struct Object *ob); + +/* Volume */ +int DRW_volume_material_count_get(struct Volume *volume); + +struct GPUBatch *DRW_volume_batch_cache_get_wireframes_face(struct Volume *volume); + /* Mesh */ void DRW_mesh_batch_cache_create_requested(struct Object *ob, struct Mesh *me, diff --git a/source/blender/draw/intern/draw_cache_impl_displist.c b/source/blender/draw/intern/draw_cache_impl_displist.c index 50979d72189..bc4eb37c3b4 100644 --- a/source/blender/draw/intern/draw_cache_impl_displist.c +++ b/source/blender/draw/intern/draw_cache_impl_displist.c @@ -214,7 +214,7 @@ void DRW_displist_vertbuf_create_pos_and_nor(ListBase *lb, GPUVertBuf *vbo) } } -void DRW_displist_vertbuf_create_wiredata(ListBase *lb, GPUVertBuf *vbo) +void DRW_vertbuf_create_wiredata(GPUVertBuf *vbo, const int vert_len) { static GPUVertFormat format = {0}; static struct { @@ -232,23 +232,27 @@ void DRW_displist_vertbuf_create_wiredata(ListBase *lb, GPUVertBuf *vbo) } } - int vbo_len_used = curve_render_surface_vert_len_get(lb); - GPU_vertbuf_init_with_format(vbo, &format); - GPU_vertbuf_data_alloc(vbo, vbo_len_used); + GPU_vertbuf_data_alloc(vbo, vert_len); if (vbo->format.stride == 1) { - memset(vbo->data, 0xFF, (size_t)vbo_len_used); + memset(vbo->data, 0xFF, (size_t)vert_len); } else { GPUVertBufRaw wd_step; GPU_vertbuf_attr_get_raw_data(vbo, attr_id.wd, &wd_step); - for (int i = 0; i < vbo_len_used; i++) { + for (int i = 0; i < vert_len; i++) { *((float *)GPU_vertbuf_raw_step(&wd_step)) = 1.0f; } } } +void DRW_displist_vertbuf_create_wiredata(ListBase *lb, GPUVertBuf *vbo) +{ + const int vert_len = curve_render_surface_vert_len_get(lb); + DRW_vertbuf_create_wiredata(vbo, vert_len); +} + void DRW_displist_indexbuf_create_triangles_in_order(ListBase *lb, GPUIndexBuf *ibo) { const int tri_len = curve_render_surface_tri_len_get(lb); diff --git a/source/blender/draw/intern/draw_cache_impl_particles.c b/source/blender/draw/intern/draw_cache_impl_particles.c index 795e7be63b1..c4433cea0df 100644 --- a/source/blender/draw/intern/draw_cache_impl_particles.c +++ b/source/blender/draw/intern/draw_cache_impl_particles.c @@ -179,7 +179,7 @@ static void particle_batch_cache_clear_point(ParticlePointCache *point_cache) GPU_VERTBUF_DISCARD_SAFE(point_cache->pos); } -static void particle_batch_cache_clear_hair(ParticleHairCache *hair_cache) +void particle_batch_cache_clear_hair(ParticleHairCache *hair_cache) { /* TODO more granular update tagging. */ GPU_VERTBUF_DISCARD_SAFE(hair_cache->proc_point_buf); diff --git a/source/blender/draw/intern/draw_common.c b/source/blender/draw/intern/draw_common.c index b1ad1455d8c..87cd1cd8546 100644 --- a/source/blender/draw/intern/draw_common.c +++ b/source/blender/draw/intern/draw_common.c @@ -437,7 +437,15 @@ bool DRW_object_is_flat(Object *ob, int *r_axis) { float dim[3]; - if (!ELEM(ob->type, OB_MESH, OB_CURVE, OB_SURF, OB_FONT, OB_MBALL)) { + if (!ELEM(ob->type, + OB_MESH, + OB_CURVE, + OB_SURF, + OB_FONT, + OB_MBALL, + OB_HAIR, + OB_POINTCLOUD, + OB_VOLUME)) { /* Non-meshes object cannot be considered as flat. */ return false; } diff --git a/source/blender/draw/intern/draw_hair.c b/source/blender/draw/intern/draw_hair.c index c88071dc6d6..c89f5f5bba6 100644 --- a/source/blender/draw/intern/draw_hair.c +++ b/source/blender/draw/intern/draw_hair.c @@ -142,9 +142,16 @@ static DRWShadingGroup *drw_shgroup_create_hair_procedural_ex(Object *object, int thickness_res = (scene->r.hair_type == SCE_HAIR_SHAPE_STRAND) ? 1 : 2; ParticleHairCache *hair_cache; - ParticleSettings *part = psys->part; - bool need_ft_update = particles_ensure_procedural_data( - object, psys, md, &hair_cache, subdiv, thickness_res); + bool need_ft_update; + if (psys) { + /* Old particle hair. */ + need_ft_update = particles_ensure_procedural_data( + object, psys, md, &hair_cache, subdiv, thickness_res); + } + else { + /* New hair object. */ + need_ft_update = hair_ensure_procedural_data(object, &hair_cache, subdiv, thickness_res); + } DRWShadingGroup *shgrp; if (shgrp_parent) { @@ -185,34 +192,58 @@ static DRWShadingGroup *drw_shgroup_create_hair_procedural_ex(Object *object, DRW_shgroup_uniform_texture(shgrp, "ac", g_dummy_texture); } - if ((dupli_parent != NULL) && (dupli_object != NULL)) { - if (dupli_object->type & OB_DUPLICOLLECTION) { - copy_m4_m4(dupli_mat, dupli_parent->obmat); + if (psys) { + if ((dupli_parent != NULL) && (dupli_object != NULL)) { + if (dupli_object->type & OB_DUPLICOLLECTION) { + copy_m4_m4(dupli_mat, dupli_parent->obmat); + } + else { + copy_m4_m4(dupli_mat, dupli_object->ob->obmat); + invert_m4(dupli_mat); + mul_m4_m4m4(dupli_mat, object->obmat, dupli_mat); + } } else { - copy_m4_m4(dupli_mat, dupli_object->ob->obmat); - invert_m4(dupli_mat); - mul_m4_m4m4(dupli_mat, object->obmat, dupli_mat); + unit_m4(dupli_mat); } } else { - unit_m4(dupli_mat); + /* New hair object. */ + copy_m4_m4(dupli_mat, object->obmat); + } + + /* Get hair shape parameters. */ + float hair_rad_shape, hair_rad_root, hair_rad_tip; + bool hair_close_tip; + if (psys) { + /* Old particle hair. */ + ParticleSettings *part = psys->part; + hair_rad_shape = part->shape; + hair_rad_root = part->rad_root * part->rad_scale * 0.5f; + hair_rad_tip = part->rad_tip * part->rad_scale * 0.5f; + hair_close_tip = (part->shape_flag & PART_SHAPE_CLOSE_TIP) != 0; + } + else { + /* TODO: implement for new hair object. */ + hair_rad_shape = 1.0f; + hair_rad_root = 0.005f; + hair_rad_tip = 0.0f; + hair_close_tip = true; } DRW_shgroup_uniform_texture(shgrp, "hairPointBuffer", hair_cache->final[subdiv].proc_tex); DRW_shgroup_uniform_int(shgrp, "hairStrandsRes", &hair_cache->final[subdiv].strands_res, 1); DRW_shgroup_uniform_int_copy(shgrp, "hairThicknessRes", thickness_res); - DRW_shgroup_uniform_float(shgrp, "hairRadShape", &part->shape, 1); + DRW_shgroup_uniform_float_copy(shgrp, "hairRadShape", hair_rad_shape); DRW_shgroup_uniform_vec4_copy(shgrp, "hairDupliMatrix[0]", dupli_mat[0]); DRW_shgroup_uniform_vec4_copy(shgrp, "hairDupliMatrix[1]", dupli_mat[1]); DRW_shgroup_uniform_vec4_copy(shgrp, "hairDupliMatrix[2]", dupli_mat[2]); DRW_shgroup_uniform_vec4_copy(shgrp, "hairDupliMatrix[3]", dupli_mat[3]); - DRW_shgroup_uniform_float_copy(shgrp, "hairRadRoot", part->rad_root * part->rad_scale * 0.5f); - DRW_shgroup_uniform_float_copy(shgrp, "hairRadTip", part->rad_tip * part->rad_scale * 0.5f); - DRW_shgroup_uniform_bool_copy( - shgrp, "hairCloseTip", (part->shape_flag & PART_SHAPE_CLOSE_TIP) != 0); - /* TODO(fclem): Until we have a better way to cull the hair and render with orco, bypass culling - * test. */ + DRW_shgroup_uniform_float_copy(shgrp, "hairRadRoot", hair_rad_root); + DRW_shgroup_uniform_float_copy(shgrp, "hairRadTip", hair_rad_tip); + DRW_shgroup_uniform_bool_copy(shgrp, "hairCloseTip", hair_close_tip); + /* TODO(fclem): Until we have a better way to cull the hair and render with orco, bypass + * culling test. */ GPUBatch *geom = hair_cache->final[subdiv].proc_hairs[thickness_res - 1]; DRW_shgroup_call_no_cull(shgrp, geom, object); diff --git a/source/blender/draw/intern/draw_hair_private.h b/source/blender/draw/intern/draw_hair_private.h index c03b4822b1b..4d9eaf88a7d 100644 --- a/source/blender/draw/intern/draw_hair_private.h +++ b/source/blender/draw/intern/draw_hair_private.h @@ -79,6 +79,8 @@ typedef struct ParticleHairCache { int point_len; } ParticleHairCache; +void particle_batch_cache_clear_hair(struct ParticleHairCache *hair_cache); + bool particles_ensure_procedural_data(struct Object *object, struct ParticleSystem *psys, struct ModifierData *md, @@ -86,4 +88,9 @@ bool particles_ensure_procedural_data(struct Object *object, int subdiv, int thickness_res); +bool hair_ensure_procedural_data(struct Object *object, + struct ParticleHairCache **r_hair_cache, + int subdiv, + int thickness_res); + #endif /* __DRAW_HAIR_PRIVATE_H__ */ diff --git a/source/blender/draw/intern/draw_manager.c b/source/blender/draw/intern/draw_manager.c index 618922d8544..df3a1e3da79 100644 --- a/source/blender/draw/intern/draw_manager.c +++ b/source/blender/draw/intern/draw_manager.c @@ -38,6 +38,7 @@ #include "BKE_editmesh.h" #include "BKE_global.h" #include "BKE_gpencil.h" +#include "BKE_hair.h" #include "BKE_lattice.h" #include "BKE_main.h" #include "BKE_mball.h" @@ -48,6 +49,8 @@ #include "BKE_paint.h" #include "BKE_pbvh.h" #include "BKE_pointcache.h" +#include "BKE_pointcloud.h" +#include "BKE_volume.h" #include "draw_manager.h" #include "DNA_camera_types.h" @@ -2653,6 +2656,15 @@ void DRW_engines_register(void) BKE_gpencil_batch_cache_dirty_tag_cb = DRW_gpencil_batch_cache_dirty_tag; BKE_gpencil_batch_cache_free_cb = DRW_gpencil_batch_cache_free; + + BKE_hair_batch_cache_dirty_tag_cb = DRW_hair_batch_cache_dirty_tag; + BKE_hair_batch_cache_free_cb = DRW_hair_batch_cache_free; + + BKE_pointcloud_batch_cache_dirty_tag_cb = DRW_pointcloud_batch_cache_dirty_tag; + BKE_pointcloud_batch_cache_free_cb = DRW_pointcloud_batch_cache_free; + + BKE_volume_batch_cache_dirty_tag_cb = DRW_volume_batch_cache_dirty_tag; + BKE_volume_batch_cache_free_cb = DRW_volume_batch_cache_free; } } diff --git a/source/blender/gpu/GPU_material.h b/source/blender/gpu/GPU_material.h index da00306bf9f..337c0b03308 100644 --- a/source/blender/gpu/GPU_material.h +++ b/source/blender/gpu/GPU_material.h @@ -233,7 +233,8 @@ typedef struct GPUMaterialTexture { typedef struct GPUMaterialVolumeGrid { struct GPUMaterialVolumeGrid *next, *prev; char *name; - char sampler_name[32]; /* Name of sampler in GLSL. */ + char sampler_name[32]; /* Name of sampler in GLSL. */ + char transform_name[32]; /* Name of 4x4 matrix in GLSL. */ int users; } GPUMaterialVolumeGrid; diff --git a/source/blender/gpu/intern/gpu_codegen.c b/source/blender/gpu/intern/gpu_codegen.c index bcaa95c2f59..066b8d633d2 100644 --- a/source/blender/gpu/intern/gpu_codegen.c +++ b/source/blender/gpu/intern/gpu_codegen.c @@ -335,6 +335,7 @@ static int codegen_process_uniforms_functions(GPUMaterial *material, /* Volume Grids */ for (GPUMaterialVolumeGrid *grid = graph->volume_grids.first; grid; grid = grid->next) { BLI_dynstr_appendf(ds, "uniform sampler3D %s;\n", grid->sampler_name); + BLI_dynstr_appendf(ds, "uniform mat4 %s = mat4(0.0);\n", grid->transform_name); } /* Print other uniforms */ @@ -432,6 +433,9 @@ static void codegen_call_functions(DynStr *ds, GPUNodeGraph *graph, GPUOutput *f else if (input->source == GPU_SOURCE_VOLUME_GRID) { BLI_dynstr_append(ds, input->volume_grid->sampler_name); } + else if (input->source == GPU_SOURCE_VOLUME_GRID_TRANSFORM) { + BLI_dynstr_append(ds, input->volume_grid->transform_name); + } else if (input->source == GPU_SOURCE_OUTPUT) { codegen_convert_datatype( ds, input->link->output->type, input->type, "tmp", input->link->output->id); diff --git a/source/blender/gpu/intern/gpu_node_graph.c b/source/blender/gpu/intern/gpu_node_graph.c index 0d5cc46c0b9..0eb62bf5b2f 100644 --- a/source/blender/gpu/intern/gpu_node_graph.c +++ b/source/blender/gpu/intern/gpu_node_graph.c @@ -123,6 +123,10 @@ static void gpu_node_input_link(GPUNode *node, GPUNodeLink *link, const eGPUType input->source = GPU_SOURCE_VOLUME_GRID; input->volume_grid = link->volume_grid; break; + case GPU_NODE_LINK_VOLUME_GRID_TRANSFORM: + input->source = GPU_SOURCE_VOLUME_GRID_TRANSFORM; + input->volume_grid = link->volume_grid; + break; case GPU_NODE_LINK_ATTR: input->source = GPU_SOURCE_ATTR; input->attr = link->attr; @@ -342,6 +346,7 @@ static GPUMaterialVolumeGrid *gpu_node_graph_add_volume_grid(GPUNodeGraph *graph grid = MEM_callocN(sizeof(*grid), __func__); grid->name = BLI_strdup(name); BLI_snprintf(grid->sampler_name, sizeof(grid->sampler_name), "vsamp%d", num_grids); + BLI_snprintf(grid->transform_name, sizeof(grid->transform_name), "vtfm%d", num_grids); BLI_addtail(&graph->volume_grids, grid); } @@ -432,16 +437,20 @@ GPUNodeLink *GPU_volume_grid(GPUMaterial *mat, const char *name) link->link_type = GPU_NODE_LINK_VOLUME_GRID; link->volume_grid = gpu_node_graph_add_volume_grid(graph, name); + GPUNodeLink *transform_link = gpu_node_link_create(); + transform_link->link_type = GPU_NODE_LINK_VOLUME_GRID_TRANSFORM; + transform_link->volume_grid = link->volume_grid; + /* Two special cases, where we adjust the output values of smoke grids to * bring the into standard range without having to modify the grid values. */ if (strcmp(name, "color") == 0) { - GPU_link(mat, "node_attribute_volume_color", link, &link); + GPU_link(mat, "node_attribute_volume_color", link, transform_link, &link); } else if (strcmp(name, "temperature") == 0) { - GPU_link(mat, "node_attribute_volume_temperature", link, &link); + GPU_link(mat, "node_attribute_volume_temperature", link, transform_link, &link); } else { - GPU_link(mat, "node_attribute_volume", link, &link); + GPU_link(mat, "node_attribute_volume", link, transform_link, &link); } return link; @@ -590,7 +599,7 @@ static void gpu_inputs_free(ListBase *inputs) else if (ELEM(input->source, GPU_SOURCE_TEX, GPU_SOURCE_TEX_TILED_MAPPING)) { input->texture->users--; } - else if (ELEM(input->source, GPU_SOURCE_VOLUME_GRID)) { + else if (ELEM(input->source, GPU_SOURCE_VOLUME_GRID, GPU_SOURCE_VOLUME_GRID_TRANSFORM)) { input->volume_grid->users--; } diff --git a/source/blender/gpu/intern/gpu_node_graph.h b/source/blender/gpu/intern/gpu_node_graph.h index ceaeea2bfa8..8506c6a87e2 100644 --- a/source/blender/gpu/intern/gpu_node_graph.h +++ b/source/blender/gpu/intern/gpu_node_graph.h @@ -62,6 +62,7 @@ typedef enum { GPU_NODE_LINK_IMAGE_TILED, GPU_NODE_LINK_IMAGE_TILED_MAPPING, GPU_NODE_LINK_VOLUME_GRID, + GPU_NODE_LINK_VOLUME_GRID_TRANSFORM, GPU_NODE_LINK_OUTPUT, GPU_NODE_LINK_UNIFORM, } GPUNodeLinkType; @@ -131,7 +132,7 @@ typedef struct GPUInput { struct GPUMaterialTexture *texture; /* GPU_SOURCE_ATTR */ struct GPUMaterialAttribute *attr; - /* GPU_SOURCE_VOLUME_GRID */ + /* GPU_SOURCE_VOLUME_GRID | GPU_SOURCE_VOLUME_GRID_TRANSFORM */ struct GPUMaterialVolumeGrid *volume_grid; }; } GPUInput; diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_volume_info.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_volume_info.glsl index a80cd3cb329..e6d7b9d3721 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_volume_info.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_volume_info.glsl @@ -4,18 +4,24 @@ uniform vec3 volumeColor = vec3(1.0); uniform vec2 volumeTemperature = vec2(0.0); /* Generic volume attribute. */ -void node_attribute_volume(sampler3D tex, out vec3 outvec) +void node_attribute_volume(sampler3D tex, mat4 transform, out vec3 outvec) { #if defined(MESH_SHADER) && defined(VOLUMETRICS) vec3 cos = volumeObjectLocalCoord; #else vec3 cos = vec3(0.0); #endif + + /* Optional per-grid transform. */ + if (transform[3][3] != 0.0) { + cos = (transform * vec4(cos, 1.0)).xyz; + } + outvec = texture(tex, cos).rgb; } /* Special color attribute for smoke. */ -void node_attribute_volume_color(sampler3D tex, out vec3 outvec) +void node_attribute_volume_color(sampler3D tex, mat4 transform, out vec3 outvec) { #if defined(MESH_SHADER) && defined(VOLUMETRICS) vec3 cos = volumeObjectLocalCoord; @@ -23,6 +29,11 @@ void node_attribute_volume_color(sampler3D tex, out vec3 outvec) vec3 cos = vec3(0.0); #endif + /* Optional per-grid transform. */ + if (transform[3][3] != 0.0) { + cos = (transform * vec4(cos, 1.0)).xyz; + } + /* Density is premultiplied for interpolation, divide it out here. */ vec4 value = texture(tex, cos).rgba; if (value.a > 1e-8) { @@ -33,7 +44,7 @@ void node_attribute_volume_color(sampler3D tex, out vec3 outvec) } /* Special temperature attribute for smoke. */ -void node_attribute_volume_temperature(sampler3D tex, out float outf) +void node_attribute_volume_temperature(sampler3D tex, mat4 transform, out float outf) { #if defined(MESH_SHADER) && defined(VOLUMETRICS) vec3 cos = volumeObjectLocalCoord; @@ -41,6 +52,11 @@ void node_attribute_volume_temperature(sampler3D tex, out float outf) vec3 cos = vec3(0.0); #endif + /* Optional per-grid transform. */ + if (transform[3][3] != 0.0) { + cos = (transform * vec4(cos, 1.0)).xyz; + } + float value = texture(tex, cos).r; if (volumeTemperature.x < volumeTemperature.y) { outf = (value > 0.01) ? |