Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'source/blender/draw/intern/draw_cache_impl_hair.c')
-rw-r--r--source/blender/draw/intern/draw_cache_impl_hair.c404
1 files changed, 404 insertions, 0 deletions
diff --git a/source/blender/draw/intern/draw_cache_impl_hair.c b/source/blender/draw/intern/draw_cache_impl_hair.c
new file mode 100644
index 00000000000..b25d6986b81
--- /dev/null
+++ b/source/blender/draw/intern/draw_cache_impl_hair.c
@@ -0,0 +1,404 @@
+/*
+ * ***** 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.
+ *
+ * The Original Code is Copyright (C) 2017 by Blender Foundation.
+ * All rights reserved.
+ *
+ * Contributor(s): Blender Foundation, Mike Erwin, Dalai Felinto
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file draw_cache_impl_strands.c
+ * \ingroup draw
+ *
+ * \brief Strands API for render engines
+ */
+
+#include "MEM_guardedalloc.h"
+
+#include "BLI_utildefines.h"
+#include "BLI_math_vector.h"
+#include "BLI_ghash.h"
+
+#include "DNA_hair_types.h"
+#include "DNA_scene_types.h"
+
+#include "BKE_hair.h"
+#include "BKE_mesh_sample.h"
+
+#include "DEG_depsgraph.h"
+
+#include "GPU_batch.h"
+#include "GPU_extensions.h"
+#include "GPU_texture.h"
+
+#include "draw_common.h"
+#include "draw_cache_impl.h" /* own include */
+#include "DRW_render.h"
+
+// timing
+//#define DEBUG_TIME
+#ifdef DEBUG_TIME
+# include "PIL_time_utildefines.h"
+#else
+# define TIMEIT_START(var)
+# define TIMEIT_VALUE(var)
+# define TIMEIT_VALUE_PRINT(var)
+# define TIMEIT_END(var)
+# define TIMEIT_BENCH(expr, id) (expr)
+# define TIMEIT_BLOCK_INIT(var)
+# define TIMEIT_BLOCK_START(var)
+# define TIMEIT_BLOCK_END(var)
+# define TIMEIT_BLOCK_STATS(var)
+#endif
+
+/* ---------------------------------------------------------------------- */
+/* Hair Gwn_Batch Cache */
+
+typedef struct HairBatchCache {
+ Gwn_VertBuf *fiber_verts;
+ Gwn_IndexBuf *fiber_edges;
+ Gwn_Batch *fibers;
+ DRWHairFiberTextureBuffer texbuffer;
+
+ Gwn_VertBuf *follicle_verts;
+ Gwn_IndexBuf *follicle_edges;
+ Gwn_Batch *follicles;
+
+ Gwn_VertBuf *guide_curve_verts;
+ Gwn_IndexBuf *guide_curve_edges;
+ Gwn_Batch *guide_curves;
+
+ /* settings to determine if cache is invalid */
+ bool is_dirty;
+} HairBatchCache;
+
+/* Gwn_Batch cache management. */
+
+static void hair_batch_cache_clear(HairSystem *hsys);
+
+static bool hair_batch_cache_valid(HairSystem *hsys)
+{
+ HairBatchCache *cache = hsys->draw_batch_cache;
+
+ if (cache == NULL) {
+ return false;
+ }
+
+ if (cache->is_dirty) {
+ return false;
+ }
+
+ return true;
+}
+
+static void hair_batch_cache_init(HairSystem *hsys)
+{
+ HairBatchCache *cache = hsys->draw_batch_cache;
+
+ if (!cache) {
+ cache = hsys->draw_batch_cache = MEM_callocN(sizeof(*cache), __func__);
+ }
+ else {
+ memset(cache, 0, sizeof(*cache));
+ }
+
+ cache->is_dirty = false;
+}
+
+static HairBatchCache *hair_batch_cache_get(HairSystem *hsys)
+{
+ if (!hair_batch_cache_valid(hsys)) {
+ hair_batch_cache_clear(hsys);
+ hair_batch_cache_init(hsys);
+ }
+ return hsys->draw_batch_cache;
+}
+
+void DRW_hair_batch_cache_dirty(HairSystem *hsys, int mode)
+{
+ HairBatchCache *cache = hsys->draw_batch_cache;
+ if (cache == NULL) {
+ return;
+ }
+ switch (mode) {
+ case BKE_HAIR_BATCH_DIRTY_ALL:
+ cache->is_dirty = true;
+ break;
+ default:
+ BLI_assert(0);
+ }
+}
+
+static void hair_batch_cache_clear(HairSystem *hsys)
+{
+ HairBatchCache *cache = hsys->draw_batch_cache;
+
+ if (hsys->draw_texture_cache) {
+ GPU_texture_free(hsys->draw_texture_cache);
+ hsys->draw_texture_cache = NULL;
+ }
+
+ if (cache) {
+ GWN_BATCH_DISCARD_SAFE(cache->fibers);
+ GWN_VERTBUF_DISCARD_SAFE(cache->fiber_verts);
+ GWN_INDEXBUF_DISCARD_SAFE(cache->fiber_edges);
+
+ GWN_BATCH_DISCARD_SAFE(cache->follicles);
+ GWN_VERTBUF_DISCARD_SAFE(cache->follicle_verts);
+ GWN_INDEXBUF_DISCARD_SAFE(cache->follicle_edges);
+
+ GWN_BATCH_DISCARD_SAFE(cache->guide_curves);
+ GWN_VERTBUF_DISCARD_SAFE(cache->guide_curve_verts);
+ GWN_INDEXBUF_DISCARD_SAFE(cache->guide_curve_edges);
+
+ {
+ DRWHairFiberTextureBuffer *buffer = &cache->texbuffer;
+ if (buffer->data) {
+ MEM_freeN(buffer->data);
+ buffer->data = NULL;
+ }
+ buffer->fiber_start = 0;
+ buffer->strand_map_start = 0;
+ buffer->strand_vertex_start = 0;
+ buffer->width = 0;
+ buffer->height = 0;
+ }
+ }
+}
+
+void DRW_hair_batch_cache_free(HairSystem *hsys)
+{
+ hair_batch_cache_clear(hsys);
+ MEM_SAFE_FREE(hsys->draw_batch_cache);
+}
+
+static void hair_batch_cache_ensure_fibers(HairSystem *hsys, int subdiv, HairBatchCache *cache)
+{
+ TIMEIT_START(hair_batch_cache_ensure_fibers);
+
+ GWN_VERTBUF_DISCARD_SAFE(cache->fiber_verts);
+ GWN_INDEXBUF_DISCARD_SAFE(cache->fiber_edges);
+
+ const int totfibers = hsys->pattern ? hsys->pattern->num_follicles : 0;
+ int *fiber_lengths = BKE_hair_get_fiber_lengths(hsys, subdiv);
+ int totpoint = 0;
+ for (int i = 0; i < totfibers; ++i) {
+ totpoint += fiber_lengths[i];
+ }
+ const int totseg = totpoint - totfibers;
+
+ static Gwn_VertFormat format = { 0 };
+ static unsigned curve_param_id, fiber_index_id;
+
+ /* initialize vertex format */
+ if (format.attrib_ct == 0) {
+ fiber_index_id = GWN_vertformat_attr_add(&format, "fiber_index", GWN_COMP_I32, 1, GWN_FETCH_INT);
+ curve_param_id = GWN_vertformat_attr_add(&format, "curve_param", GWN_COMP_F32, 1, GWN_FETCH_FLOAT);
+ }
+
+ cache->fiber_verts = GWN_vertbuf_create_with_format(&format);
+
+ Gwn_IndexBufBuilder elb;
+ {
+ TIMEIT_START(data_alloc);
+ Gwn_PrimType prim_type;
+ unsigned prim_ct, vert_ct;
+ prim_type = GWN_PRIM_TRIS;
+ prim_ct = 2 * totseg;
+ vert_ct = 2 * totpoint;
+
+ GWN_vertbuf_data_alloc(cache->fiber_verts, vert_ct);
+ GWN_indexbuf_init(&elb, prim_type, prim_ct, vert_ct);
+ TIMEIT_END(data_alloc);
+ }
+
+ TIMEIT_START(data_fill);
+ TIMEIT_BLOCK_INIT(GWN_vertbuf_attr_set);
+ TIMEIT_BLOCK_INIT(GWN_indexbuf_add_tri_verts);
+ int vi = 0;
+ for (int i = 0; i < totfibers; ++i) {
+ const int fiblen = fiber_lengths[i];
+ const float da = fiblen > 1 ? 1.0f / (fiblen-1) : 0.0f;
+
+ float a = 0.0f;
+ for (int k = 0; k < fiblen; ++k) {
+ TIMEIT_BLOCK_START(GWN_vertbuf_attr_set);
+ GWN_vertbuf_attr_set(cache->fiber_verts, fiber_index_id, vi, &i);
+ GWN_vertbuf_attr_set(cache->fiber_verts, curve_param_id, vi, &a);
+ GWN_vertbuf_attr_set(cache->fiber_verts, fiber_index_id, vi+1, &i);
+ GWN_vertbuf_attr_set(cache->fiber_verts, curve_param_id, vi+1, &a);
+ TIMEIT_BLOCK_END(GWN_vertbuf_attr_set);
+
+ if (k > 0) {
+ TIMEIT_BLOCK_START(GWN_indexbuf_add_tri_verts);
+ GWN_indexbuf_add_tri_verts(&elb, vi-2, vi-1, vi+1);
+ GWN_indexbuf_add_tri_verts(&elb, vi+1, vi, vi-2);
+ TIMEIT_BLOCK_END(GWN_indexbuf_add_tri_verts);
+ }
+
+ vi += 2;
+ a += da;
+ }
+ }
+ TIMEIT_BLOCK_STATS(GWN_vertbuf_attr_set);
+ TIMEIT_BLOCK_STATS(GWN_indexbuf_add_tri_verts);
+#ifdef DEBUG_TIME
+ printf("Total GWN time: %f\n", _timeit_var_GWN_vertbuf_attr_set + _timeit_var_GWN_indexbuf_add_tri_verts);
+#endif
+ fflush(stdout);
+ TIMEIT_END(data_fill);
+
+ if (fiber_lengths)
+ {
+ MEM_freeN(fiber_lengths);
+ }
+
+ TIMEIT_BENCH(cache->fiber_edges = GWN_indexbuf_build(&elb), indexbuf_build);
+
+ TIMEIT_END(hair_batch_cache_ensure_fibers);
+}
+
+static void hair_batch_cache_ensure_fiber_texbuffer(HairSystem *hsys, struct DerivedMesh *scalp, int subdiv, HairBatchCache *cache)
+{
+ DRWHairFiberTextureBuffer *buffer = &cache->texbuffer;
+ static const int elemsize = 8;
+ const int width = GPU_max_texture_size();
+ const int align = width * elemsize;
+
+ // Offsets in bytes
+ int b_size, b_strand_map_start, b_strand_vertex_start, b_fiber_start;
+ BKE_hair_get_texture_buffer_size(hsys, subdiv, &b_size,
+ &b_strand_map_start, &b_strand_vertex_start, &b_fiber_start);
+ // Pad for alignment
+ b_size += align - b_size % align;
+
+ // Convert to element size as texture offsets
+ const int size = b_size / elemsize;
+ const int height = size / width;
+
+ buffer->data = MEM_mallocN(b_size, "hair fiber texture buffer");
+ BKE_hair_get_texture_buffer(hsys, scalp, subdiv, buffer->data);
+
+ buffer->width = width;
+ buffer->height = height;
+ buffer->strand_map_start = b_strand_map_start / elemsize;
+ buffer->strand_vertex_start = b_strand_vertex_start / elemsize;
+ buffer->fiber_start = b_fiber_start / elemsize;
+}
+
+Gwn_Batch *DRW_hair_batch_cache_get_fibers(
+ HairSystem *hsys,
+ struct DerivedMesh *scalp,
+ int subdiv,
+ const DRWHairFiberTextureBuffer **r_buffer)
+{
+ HairBatchCache *cache = hair_batch_cache_get(hsys);
+
+ TIMEIT_START(DRW_hair_batch_cache_get_fibers);
+
+ if (cache->fibers == NULL) {
+ TIMEIT_BENCH(hair_batch_cache_ensure_fibers(hsys, subdiv, cache),
+ hair_batch_cache_ensure_fibers);
+
+ TIMEIT_BENCH(cache->fibers = GWN_batch_create(GWN_PRIM_TRIS, cache->fiber_verts, cache->fiber_edges),
+ GWN_batch_create);
+
+ TIMEIT_BENCH(hair_batch_cache_ensure_fiber_texbuffer(hsys, scalp, subdiv, cache),
+ hair_batch_cache_ensure_fiber_texbuffer);
+ }
+
+ if (r_buffer) {
+ *r_buffer = &cache->texbuffer;
+ }
+
+ TIMEIT_END(DRW_hair_batch_cache_get_fibers);
+
+ return cache->fibers;
+}
+
+static void hair_batch_cache_ensure_follicles(
+ HairSystem *hsys,
+ struct DerivedMesh *scalp,
+ eHairDrawFollicleMode mode,
+ HairBatchCache *cache)
+{
+ GWN_VERTBUF_DISCARD_SAFE(cache->follicle_verts);
+ GWN_INDEXBUF_DISCARD_SAFE(cache->follicle_edges);
+
+ const HairPattern *pattern = hsys->pattern;
+
+ static Gwn_VertFormat format = { 0 };
+ static unsigned pos_id;
+
+ /* initialize vertex format */
+ if (format.attrib_ct == 0) {
+ pos_id = GWN_vertformat_attr_add(&format, "pos", GWN_COMP_F32, 3, GWN_FETCH_FLOAT);
+ }
+
+ cache->follicle_verts = GWN_vertbuf_create_with_format(&format);
+
+ GWN_vertbuf_data_alloc(cache->follicle_verts, pattern->num_follicles);
+
+ HairFollicle *follicle = pattern->follicles;
+ for (int i = 0; i < pattern->num_follicles; ++i, ++follicle) {
+ float co[3], nor[3], tang[3];
+ BKE_mesh_sample_eval(scalp, &follicle->mesh_sample, co, nor, tang);
+
+ GWN_vertbuf_attr_set(cache->follicle_verts, pos_id, (unsigned int)i, co);
+ }
+}
+
+Gwn_Batch *DRW_hair_batch_cache_get_follicle_points(
+ HairSystem *hsys,
+ struct DerivedMesh *scalp)
+{
+ HairBatchCache *cache = hair_batch_cache_get(hsys);
+
+ if (cache->follicles == NULL) {
+ hair_batch_cache_ensure_follicles(hsys, scalp, HAIR_DRAW_FOLLICLE_POINTS, cache);
+
+ cache->follicles = GWN_batch_create(GWN_PRIM_POINTS, cache->follicle_verts, NULL);
+ }
+
+ return cache->follicles;
+
+}
+
+Gwn_Batch *DRW_hair_batch_cache_get_follicle_axes(
+ HairSystem *hsys,
+ struct DerivedMesh *scalp)
+{
+ return NULL;
+}
+
+Gwn_Batch *DRW_hair_batch_cache_get_guide_curve_points(
+ HairSystem *hsys,
+ struct DerivedMesh *scalp,
+ int subdiv)
+{
+ return NULL;
+}
+
+Gwn_Batch *DRW_hair_batch_cache_get_guide_curve_edges(
+ HairSystem *hsys,
+ struct DerivedMesh *scalp,
+ int subdiv)
+{
+ return NULL;
+}