Rework Bake from Multires

The main idea is to switch Bake from Multires from legacy DerivedMesh
to Subdiv. On the development side of things this change removes a lot
of code, also making it easier easier to rework CustomData and related
topics, without being pulled down by the DerivedMesh.

On the user level switch to Subdiv means:

- Much more closer handling of the multi-resolution data: the derived
  mesh code was close, but not exactly the same when it comes to the
  final look of mesh.

  Other than less obvious cases (like old DerivedMesh approach doing
  recursive subdivision instead of pushing subdivided vertices on the
  limit surface) there are more obvious ones like difference in edge
  creases, and non-supported vertex creases by the DerivedMesh.

- UV interpolation is done correctly now when baking to non-base level
  (baking to multi-resolution level >= 1).

  Previously in this case the old derived mesh interpolation was used
  to interpolate face-varying data, which gives different results from
  the OpenSubdiv interpolation.

- Ngon faces are properly supported now.

A possible remaining issue is the fact that getting normal from CCG
always uses smooth interpolation. Based on the code it always has been
the case, so while it is something to look into it might be considered
a separate topic to dig into.
This commit is contained in:
Sergey Sharybin
2025-08-20 18:12:04 +02:00
parent a56922099e
commit e0154de320
45 changed files with 1567 additions and 8402 deletions

View File

@@ -2148,7 +2148,7 @@ class CYCLES_RENDER_PT_bake_output(CyclesButtonsPanel, Panel):
if rd.use_bake_multires:
layout.prop(rd, "use_bake_clear", text="Clear Image")
if rd.bake_type == 'DISPLACEMENT':
if rd.bake_type in {'DISPLACEMENT', 'VECTOR_DISPLACEMENT'}:
layout.prop(rd, "use_bake_lores_mesh")
else:
layout.prop(cbk, "target")

View File

@@ -48,10 +48,6 @@ struct CCGKey {
int has_mask;
};
/* initialize 'key' at the specified level */
void CCG_key(CCGKey *key, const CCGSubSurf *ss, int level);
void CCG_key_top_level(CCGKey *key, const CCGSubSurf *ss);
inline blender::float3 &CCG_elem_co(const CCGKey & /*key*/, CCGElem *elem)
{
return *reinterpret_cast<blender::float3 *>(elem);
@@ -104,3 +100,16 @@ inline blender::float3 &CCG_elem_offset_co(const CCGKey &key, CCGElem *elem, int
{
return CCG_elem_co(key, CCG_elem_offset(key, elem, offset));
}
inline int CCG_grid_size(const int level)
{
BLI_assert(level > 0);
return (1 << (level - 1)) + 1;
}
inline int CCG_grid_factor(int low_level, int high_level)
{
BLI_assert(low_level > 0 && high_level > 0);
BLI_assert(low_level <= high_level);
return 1 << (high_level - low_level);
}

View File

@@ -1,214 +0,0 @@
/* SPDX-FileCopyrightText: 2001-2002 NaN Holding BV. All rights reserved.
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
/** \file
* \ingroup bke
*
* Basic design of the DerivedMesh system:
*
* #DerivedMesh is a common set of interfaces for mesh systems.
*
* There are three main mesh data structures in Blender:
* #Mesh, #CDDerivedMesh and #BMesh.
*
* These, and a few others, all implement #DerivedMesh interfaces,
* which contains unified drawing interfaces, a few utility interfaces,
* and a bunch of read-only interfaces intended mostly for conversion from
* one format to another.
*
* All Mesh structures in blender make use of #CustomData, which is used to store
* per-element attributes and interpolate them (e.g. UVs, vertex-colors, vertex-groups, etc).
*
* Mesh is the "serialized" structure, used for storing object-mode mesh data
* and also for saving stuff to disk. Its interfaces are also what #DerivedMesh
* uses to communicate with.
*
* #CDDM is a little mesh library, that uses Mesh data structures in the backend.
* It's mostly used for modifiers, and has the advantages of not taking much
* resources.
*
* #BMesh is a full-on BREP, used for edit-mode, some modifiers, etc.
* It's much more capable (if memory-intensive) then CDDM.
*
* #DerivedMesh is somewhat hackish. Many places assumes that a #DerivedMesh is
* a CDDM (most of the time by simply copying it and converting it to one).
* CDDM is the original structure for modifiers, but has since been superseded
* by #BMesh, at least for the foreseeable future.
*/
/*
* NOTE: This structure is read-only, for all practical purposes.
* At some point in the future, we may want to consider
* creating a replacement structure that implements a proper
* abstract mesh kernel interface. Or, we can leave this
* as it is and stick with using BMesh and CDDM.
*/
#include "BLI_math_vector_types.hh"
#include "DNA_customdata_types.h"
struct CCGElem;
struct CCGKey;
struct CustomData_MeshMasks;
struct Mesh;
enum DerivedMeshType {
DM_TYPE_CDDM,
DM_TYPE_CCGDM,
};
struct DerivedMesh {
/** Private DerivedMesh data, only for internal DerivedMesh use */
CustomData vertData, edgeData, faceData, loopData, polyData;
int numVertData, numEdgeData, numTessFaceData, numLoopData, numPolyData;
DerivedMeshType type;
/* Always owned by this object. */
int *face_offsets;
/* Misc. Queries */
/* Also called in Editmode */
int (*getNumVerts)(DerivedMesh *dm);
int (*getNumEdges)(DerivedMesh *dm);
int (*getNumLoops)(DerivedMesh *dm);
int (*getNumPolys)(DerivedMesh *dm);
/**
* Return a pointer to the entire array of verts/edges/face from the
* derived mesh. if such an array does not exist yet, it will be created,
* and freed on the next ->release(). consider using getVert/Edge/Face if
* you are only interested in a few verts/edges/faces.
*/
/**
* \warning The real return type is `float(*)[3]`.
*/
float *(*getVertArray)(DerivedMesh *dm);
blender::int2 *(*getEdgeArray)(DerivedMesh *dm);
int *(*getCornerVertArray)(DerivedMesh *dm);
int *(*getCornerEdgeArray)(DerivedMesh *dm);
int *(*getPolyArray)(DerivedMesh *dm);
/**
* Copy all verts/edges/faces from the derived mesh into
* `*{vert/edge/face}_r` (must point to a buffer large enough).
*/
void (*copyVertArray)(DerivedMesh *dm, float (*r_positions)[3]);
void (*copyEdgeArray)(DerivedMesh *dm, blender::int2 *r_edge);
void (*copyCornerVertArray)(DerivedMesh *dm, int *r_corner_verts);
void (*copyCornerEdgeArray)(DerivedMesh *dm, int *r_corner_edges);
void (*copyPolyArray)(DerivedMesh *dm, int *r_face_offsets);
/**
* Return a pointer to the entire array of vert/edge/face custom data
* from the derived mesh (this gives a pointer to the actual data, not a copy).
*/
void *(*getVertDataArray)(DerivedMesh *dm, eCustomDataType type);
void *(*getEdgeDataArray)(DerivedMesh *dm, eCustomDataType type);
void *(*getLoopDataArray)(DerivedMesh *dm, eCustomDataType type);
void *(*getPolyDataArray)(DerivedMesh *dm, eCustomDataType type);
/** Optional grid access for subsurf */
int (*getNumGrids)(DerivedMesh *dm);
int (*getGridSize)(DerivedMesh *dm);
CCGElem **(*getGridData)(DerivedMesh *dm);
int *(*getGridOffset)(DerivedMesh *dm);
void (*getGridKey)(DerivedMesh *dm, CCGKey *key);
/* Direct Access Operations
* - Can be undefined
* - Must be defined for modifiers that only deform however. */
/**
* Release reference to the DerivedMesh. This function decides internally
* if the DerivedMesh will be freed, or cached for later use.
*/
void (*release)(DerivedMesh *dm);
};
/**
* Utility function to initialize a #DerivedMesh's function pointers to
* the default implementation (for those functions which have a default).
*/
void DM_init_funcs(DerivedMesh *dm);
/**
* Utility function to initialize a #DerivedMesh for the desired number
* of vertices, edges and faces (doesn't allocate memory for them, just
* sets up the custom data layers)>
*/
void DM_init(DerivedMesh *dm,
DerivedMeshType type,
int numVerts,
int numEdges,
int numTessFaces,
int numLoops,
int numPolys);
/**
* Utility function to initialize a DerivedMesh for the desired number
* of vertices, edges and faces, with a layer setup copied from source
*/
void DM_from_template(DerivedMesh *dm,
DerivedMesh *source,
DerivedMeshType type,
int numVerts,
int numEdges,
int numTessFaces,
int numLoops,
int numPolys);
void DM_release(DerivedMesh *dm);
/**
* set the #CD_FLAG_NOCOPY flag in custom data layers where the mask is
* zero for the layer type, so only layer types specified by the mask
* will be copied
*/
void DM_set_only_copy(DerivedMesh *dm, const CustomData_MeshMasks *mask);
/**
* Creates a CDDerivedMesh from the given Mesh, this will reference the
* original data in Mesh, but it is safe to apply vertex coordinates or
* calculate normals as those functions will automatically create new
* data to not overwrite the original.
*/
DerivedMesh *CDDM_from_mesh(Mesh *mesh);
/* -------------------------------------------------------------------- */
/** \name Custom Data Layer Access Functions
*
* \return pointer to first data layer which matches type (a flat array)
* if they return NULL, data doesn't exist.
* \note these return pointers - any change modifies the internals of the mesh.
* \{ */
void *DM_get_vert_data_layer(DerivedMesh *dm, eCustomDataType type);
void *DM_get_edge_data_layer(DerivedMesh *dm, eCustomDataType type);
void *DM_get_poly_data_layer(DerivedMesh *dm, eCustomDataType type);
void *DM_get_loop_data_layer(DerivedMesh *dm, eCustomDataType type);
/** \} */
/**
* Custom data copy functions
* copy count elements from source_index in source to dest_index in dest
* these copy all layers for which the CD_FLAG_NOCOPY flag is not set.
*/
void DM_copy_vert_data(
const DerivedMesh *source, DerivedMesh *dest, int source_index, int dest_index, int count);
/**
* Interpolates vertex data from the vertices indexed by `src_indices` in the
* source mesh using the given weights and stores the result in the vertex
* indexed by `dest_index` in the `dest` mesh.
*/
void DM_interp_vert_data(const DerivedMesh *source,
DerivedMesh *dest,
int *src_indices,
float *weights,
int count,
int dest_index);

View File

@@ -8,12 +8,11 @@
* \ingroup bke
*/
#include "BKE_subsurf.hh"
#include "BLI_array.hh"
#include "BLI_math_matrix_types.hh"
#include "BLI_utildefines.h"
struct Depsgraph;
struct DerivedMesh;
struct MDisps;
struct Mesh;
struct ModifierData;
@@ -26,6 +25,14 @@ struct Settings;
struct ToMeshSettings;
} // namespace blender::bke::subdiv
enum MultiresModifiedFlags {
/* indicates the grids have been sculpted on, so MDisps
* have to be updated */
MULTIRES_COORDS_MODIFIED = 1,
/* indicates elements have been hidden or unhidden */
MULTIRES_HIDDEN_MODIFIED = 2,
};
/**
* Delete mesh mdisps and grid paint masks.
*/
@@ -52,9 +59,6 @@ enum class MultiresFlags : uint8_t {
};
ENUM_OPERATORS(MultiresFlags, MultiresFlags::IgnoreSimplify);
DerivedMesh *multires_make_derived_from_derived(
DerivedMesh *dm, MultiresModifierData *mmd, Scene *scene, Object *ob, MultiresFlags flags);
MultiresModifierData *find_multires_modifier_before(Scene *scene, ModifierData *lastmd);
/**
* used for applying scale on mdisps layer and syncing subdivide levels when joining objects.
@@ -126,10 +130,6 @@ void multiresModifier_ensure_external_read(Mesh *mesh, const MultiresModifierDat
/* Adapted from `sculptmode.c` */
void old_mdisps_bilinear(float out[3], float (*disps)[3], int st, float u, float v);
/**
* Find per-corner coordinate with given per-face UV coord.
*/
int mdisp_rot_face_to_crn(int face_size, int face_side, float u, float v, float *x, float *y);
/* Reshaping, define in multires_reshape.cc */
/**

View File

@@ -288,8 +288,8 @@ BLI_INLINE void grid_uv_to_ptex_face_uv(float grid_u,
*/
BLI_INLINE int grid_size_from_level(int level);
/* Simplified version of mdisp_rot_face_to_crn, only handles quad and
* works in normalized coordinates.
/* Find per-corner coordinate with given per-face UV coord.
* Only handles quad and works in normalized coordinates.
*
* NOTE: Output coordinates are in ptex coordinates. */
BLI_INLINE int rotate_quad_to_corner(float quad_u,

View File

@@ -176,12 +176,6 @@ struct SubdivCCG : blender::NonCopyable {
bool hidden = false;
} dirty;
/* Cached values, are not supposed to be accessed directly. */
struct {
/* Indexed by face, indicates index of the first grid which corresponds to the face. */
blender::Array<int> start_face_grid_index;
} cache_;
~SubdivCCG();
};
@@ -199,7 +193,7 @@ struct SubdivCCG : blender::NonCopyable {
std::unique_ptr<SubdivCCG> BKE_subdiv_to_ccg(blender::bke::subdiv::Subdiv &subdiv,
const SubdivToCCGSettings &settings,
const Mesh &coarse_mesh,
SubdivCCGMaskEvaluator *mask_evaluator);
SubdivCCGMaskEvaluator *mask_evaluator = nullptr);
/* Helper function, creates Mesh structure which is properly setup to use
* grids.
@@ -278,8 +272,6 @@ inline int BKE_subdiv_ccg_grid_to_face_index(const SubdivCCG &subdiv_ccg, const
return subdiv_ccg.grid_to_face_map[grid_index];
}
blender::float3 BKE_subdiv_ccg_eval_limit_point(const SubdivCCG &subdiv_ccg,
const SubdivCCGCoord &coord);
void BKE_subdiv_ccg_eval_limit_positions(const SubdivCCG &subdiv_ccg,
const CCGKey &key,
int grid_index,
@@ -309,15 +301,6 @@ bool BKE_subdiv_ccg_coord_is_mesh_boundary(blender::OffsetIndices<int> faces,
const SubdivCCG &subdiv_ccg,
SubdivCCGCoord coord);
/* Get array which is indexed by face index and contains index of a first grid of the face.
*
* The "ensure" version allocates the mapping if it's not known yet and stores it in the subdiv_ccg
* descriptor. This function is NOT safe for threading.
*
* The "get" version simply returns cached array. */
const int *BKE_subdiv_ccg_start_face_grid_index_ensure(SubdivCCG &subdiv_ccg);
const int *BKE_subdiv_ccg_start_face_grid_index_get(const SubdivCCG &subdiv_ccg);
blender::BitGroupVector<> &BKE_subdiv_ccg_grid_hidden_ensure(SubdivCCG &subdiv_ccg);
void BKE_subdiv_ccg_grid_hidden_free(SubdivCCG &subdiv_ccg);

View File

@@ -1,121 +0,0 @@
/* SPDX-FileCopyrightText: 2001-2002 NaN Holding BV. All rights reserved.
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
/** \file
* \ingroup bke
*/
/* struct DerivedMesh is used directly */
#include "BKE_mesh_legacy_derived_mesh.hh"
/* Thread sync primitives used directly. */
#include "BLI_ordered_edge.hh"
#include "BLI_threads.h"
#include "BLI_utildefines.h"
#include "BLI_vector_set.hh"
struct CCGEdge;
struct CCGElem;
struct CCGFace;
struct CCGSubSurf;
struct CCGVert;
struct DerivedMesh;
struct Mesh;
struct MultiresModifierData;
struct Object;
struct Scene;
namespace blender::bke::pbvh {
class Tree;
}
struct SubsurfModifierData;
/**************************** External *****************************/
enum SubsurfFlags {
SUBSURF_USE_RENDER_PARAMS = 1,
SUBSURF_IS_FINAL_CALC = 2,
SUBSURF_FOR_EDIT_MODE = 4,
SUBSURF_IN_EDIT_MODE = 8,
SUBSURF_ALLOC_PAINT_MASK = 16,
SUBSURF_USE_GPU_BACKEND = 32,
SUBSURF_IGNORE_SIMPLIFY = 64,
};
ENUM_OPERATORS(SubsurfFlags, SUBSURF_IGNORE_SIMPLIFY);
DerivedMesh *subsurf_make_derived_from_derived(DerivedMesh *dm,
SubsurfModifierData *smd,
const Scene *scene,
float (*vertCos)[3],
SubsurfFlags flags);
void subsurf_calculate_limit_positions(Mesh *mesh, float (*r_positions)[3]);
/**
* Get grid-size from 'level', level must be greater than zero.
*/
int BKE_ccg_gridsize(int level);
/**
* X/Y grid coordinates at 'low_level' can be multiplied by the result
* of this function to convert to grid coordinates at 'high_level'.
*/
int BKE_ccg_factor(int low_level, int high_level);
enum MultiresModifiedFlags {
/* indicates the grids have been sculpted on, so MDisps
* have to be updated */
MULTIRES_COORDS_MODIFIED = 1,
/* indicates elements have been hidden or unhidden */
MULTIRES_HIDDEN_MODIFIED = 2,
};
/**************************** Internal *****************************/
struct CCGDerivedMesh {
DerivedMesh dm;
CCGSubSurf *ss;
int freeSS;
int drawInteriorEdges, useSubsurfUv;
struct {
int startVert;
CCGVert *vert;
} *vertMap;
struct {
int startVert;
int startEdge;
CCGEdge *edge;
} *edgeMap;
struct {
int startVert;
int startEdge;
int startFace;
CCGFace *face;
} *faceMap;
CCGElem **gridData;
int *gridOffset;
CCGFace **gridFaces;
unsigned int **gridHidden;
/* Elements in arrays above. */
unsigned int numGrid;
struct {
MultiresModifierData *mmd;
int local_mmd;
int lvl, totlvl;
float (*orco)[3];
Object *ob;
} multires;
blender::VectorSet<blender::OrderedEdge> *ehash;
ThreadMutex loops_cache_lock;
ThreadRWMutex origindex_cache_rwlock;
};

View File

@@ -34,9 +34,6 @@ set(INC_SYS
set(SRC
${CMAKE_SOURCE_DIR}/release/datafiles/userdef/userdef_default.c
intern/CCGSubSurf.cc
intern/CCGSubSurf_legacy.cc
intern/CCGSubSurf_util.cc
intern/action.cc
intern/action_bones.cc
intern/action_mirror.cc
@@ -205,7 +202,6 @@ set(SRC
intern/mesh_flip_faces.cc
intern/mesh_iterators.cc
intern/mesh_legacy_convert.cc
intern/mesh_legacy_derived_mesh.cc
intern/mesh_mapping.cc
intern/mesh_merge_customdata.cc
intern/mesh_mirror.cc
@@ -295,7 +291,6 @@ set(SRC
intern/subdiv_modifier.cc
intern/subdiv_stats.cc
intern/subdiv_topology.cc
intern/subsurf_ccg.cc
intern/text.cc
intern/text_suggestions.cc
intern/texture.cc
@@ -461,7 +456,6 @@ set(SRC
BKE_mesh_fair.hh
BKE_mesh_iterators.hh
BKE_mesh_legacy_convert.hh
BKE_mesh_legacy_derived_mesh.hh
BKE_mesh_mapping.hh
BKE_mesh_mirror.hh
BKE_mesh_remap.hh
@@ -523,7 +517,6 @@ set(SRC
BKE_subdiv_mesh.hh
BKE_subdiv_modifier.hh
BKE_subdiv_topology.hh
BKE_subsurf.hh
BKE_text.h
BKE_text_suggestions.h
BKE_texture.h
@@ -553,9 +546,6 @@ set(SRC
particle_private.h
tracking_private.h
intern/CCGSubSurf.h
intern/CCGSubSurf_inline.h
intern/CCGSubSurf_intern.h
intern/attribute_access_intern.hh
intern/attribute_storage_access.hh
intern/data_transfer_intern.hh

View File

@@ -1,1574 +0,0 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup bke
*/
#include <cstdlib>
#include <cstring>
#include "MEM_guardedalloc.h"
#include "BLI_utildefines.h" /* for BLI_assert */
#include "BKE_ccg.hh"
#include "BKE_subsurf.hh"
#include "CCGSubSurf.h"
#include "CCGSubSurf_inline.h"
#include "CCGSubSurf_intern.h"
/***/
int BKE_ccg_gridsize(int level)
{
return ccg_gridsize(level);
}
int BKE_ccg_factor(int low_level, int high_level)
{
BLI_assert(low_level > 0 && high_level > 0);
BLI_assert(low_level <= high_level);
return 1 << (high_level - low_level);
}
/***/
static CCGVert *_vert_new(CCGVertHDL vHDL, CCGSubSurf *ss)
{
int num_vert_data = ss->subdivLevels + 1;
CCGVert *v = static_cast<CCGVert *>(CCGSUBSURF_alloc(
ss, sizeof(CCGVert) + ss->meshIFC.vertDataSize * num_vert_data + ss->meshIFC.vertUserSize));
uint8_t *user_data;
v->vHDL = vHDL;
v->edges = nullptr;
v->faces = nullptr;
v->numEdges = v->numFaces = 0;
v->flags = 0;
user_data = static_cast<uint8_t *>(ccgSubSurf_getVertUserData(ss, v));
memset(user_data, 0, ss->meshIFC.vertUserSize);
if (ss->useAgeCounts) {
*((int *)&user_data[ss->vertUserAgeOffset]) = ss->currentAge;
}
return v;
}
static void _vert_remEdge(CCGVert *v, CCGEdge *e)
{
for (int i = 0; i < v->numEdges; i++) {
if (v->edges[i] == e) {
v->edges[i] = v->edges[--v->numEdges];
break;
}
}
}
static void _vert_remFace(CCGVert *v, CCGFace *f)
{
for (int i = 0; i < v->numFaces; i++) {
if (v->faces[i] == f) {
v->faces[i] = v->faces[--v->numFaces];
break;
}
}
}
static void _vert_addEdge(CCGVert *v, CCGEdge *e, CCGSubSurf *ss)
{
v->edges = static_cast<CCGEdge **>(CCGSUBSURF_realloc(
ss, v->edges, (v->numEdges + 1) * sizeof(*v->edges), v->numEdges * sizeof(*v->edges)));
v->edges[v->numEdges++] = e;
}
static void _vert_addFace(CCGVert *v, CCGFace *f, CCGSubSurf *ss)
{
v->faces = static_cast<CCGFace **>(CCGSUBSURF_realloc(
ss, v->faces, (v->numFaces + 1) * sizeof(*v->faces), v->numFaces * sizeof(*v->faces)));
v->faces[v->numFaces++] = f;
}
static CCGEdge *_vert_findEdgeTo(const CCGVert *v, const CCGVert *vQ)
{
for (int i = 0; i < v->numEdges; i++) {
CCGEdge *e = v->edges[v->numEdges - 1 - i]; /* XXX, note reverse. */
if ((e->v0 == v && e->v1 == vQ) || (e->v1 == v && e->v0 == vQ)) {
return e;
}
}
return nullptr;
}
static void _vert_free(CCGVert *v, CCGSubSurf *ss)
{
if (v->edges) {
CCGSUBSURF_free(ss, v->edges);
}
if (v->faces) {
CCGSUBSURF_free(ss, v->faces);
}
CCGSUBSURF_free(ss, v);
}
/***/
static CCGEdge *_edge_new(CCGEdgeHDL eHDL, CCGVert *v0, CCGVert *v1, float crease, CCGSubSurf *ss)
{
int num_edge_data = ccg_edgebase(ss->subdivLevels + 1);
CCGEdge *e = static_cast<CCGEdge *>(CCGSUBSURF_alloc(
ss, sizeof(CCGEdge) + ss->meshIFC.vertDataSize * num_edge_data + ss->meshIFC.edgeUserSize));
uint8_t *user_data;
e->eHDL = eHDL;
e->v0 = v0;
e->v1 = v1;
e->crease = crease;
e->faces = nullptr;
e->numFaces = 0;
e->flags = 0;
_vert_addEdge(v0, e, ss);
_vert_addEdge(v1, e, ss);
user_data = static_cast<uint8_t *>(ccgSubSurf_getEdgeUserData(ss, e));
memset(user_data, 0, ss->meshIFC.edgeUserSize);
if (ss->useAgeCounts) {
*((int *)&user_data[ss->edgeUserAgeOffset]) = ss->currentAge;
}
return e;
}
static void _edge_remFace(CCGEdge *e, CCGFace *f)
{
for (int i = 0; i < e->numFaces; i++) {
if (e->faces[i] == f) {
e->faces[i] = e->faces[--e->numFaces];
break;
}
}
}
static void _edge_addFace(CCGEdge *e, CCGFace *f, CCGSubSurf *ss)
{
e->faces = static_cast<CCGFace **>(CCGSUBSURF_realloc(
ss, e->faces, (e->numFaces + 1) * sizeof(*e->faces), e->numFaces * sizeof(*e->faces)));
e->faces[e->numFaces++] = f;
}
static void *_edge_getCoVert(CCGEdge *e, CCGVert *v, int lvl, int x, int dataSize)
{
int levelBase = ccg_edgebase(lvl);
if (v == e->v0) {
return &EDGE_getLevelData(e)[dataSize * (levelBase + x)];
}
return &EDGE_getLevelData(e)[dataSize * (levelBase + (1 << lvl) - x)];
}
static void _edge_free(CCGEdge *e, CCGSubSurf *ss)
{
if (e->faces) {
CCGSUBSURF_free(ss, e->faces);
}
CCGSUBSURF_free(ss, e);
}
static void _edge_unlinkMarkAndFree(CCGEdge *e, CCGSubSurf *ss)
{
_vert_remEdge(e->v0, e);
_vert_remEdge(e->v1, e);
e->v0->flags |= Vert_eEffected;
e->v1->flags |= Vert_eEffected;
_edge_free(e, ss);
}
static CCGFace *_face_new(
CCGFaceHDL fHDL, CCGVert **verts, CCGEdge **edges, int numVerts, CCGSubSurf *ss)
{
int maxGridSize = ccg_gridsize(ss->subdivLevels);
int num_face_data = (numVerts * maxGridSize + numVerts * maxGridSize * maxGridSize + 1);
CCGFace *f = static_cast<CCGFace *>(CCGSUBSURF_alloc(
ss,
sizeof(CCGFace) + sizeof(CCGVert *) * numVerts + sizeof(CCGEdge *) * numVerts +
ss->meshIFC.vertDataSize * num_face_data + ss->meshIFC.faceUserSize));
uint8_t *user_data;
f->numVerts = numVerts;
BLI_assert(numVerts > 2);
f->fHDL = fHDL;
f->flags = 0;
for (int i = 0; i < numVerts; i++) {
FACE_getVerts(f)[i] = verts[i];
FACE_getEdges(f)[i] = edges[i];
_vert_addFace(verts[i], f, ss);
_edge_addFace(edges[i], f, ss);
}
user_data = static_cast<uint8_t *>(ccgSubSurf_getFaceUserData(ss, f));
memset(user_data, 0, ss->meshIFC.faceUserSize);
if (ss->useAgeCounts) {
*((int *)&user_data[ss->faceUserAgeOffset]) = ss->currentAge;
}
return f;
}
static void _face_free(CCGFace *f, CCGSubSurf *ss)
{
CCGSUBSURF_free(ss, f);
}
static void _face_unlinkMarkAndFree(CCGFace *f, CCGSubSurf *ss)
{
int j;
for (j = 0; j < f->numVerts; j++) {
_vert_remFace(FACE_getVerts(f)[j], f);
_edge_remFace(FACE_getEdges(f)[j], f);
FACE_getVerts(f)[j]->flags |= Vert_eEffected;
}
_face_free(f, ss);
}
/***/
CCGSubSurf *ccgSubSurf_new(CCGMeshIFC *ifc,
int subdivLevels,
CCGAllocatorIFC *allocatorIFC,
CCGAllocatorHDL allocator)
{
if (!allocatorIFC) {
allocatorIFC = ccg_getStandardAllocatorIFC();
allocator = nullptr;
}
if (subdivLevels < 1) {
return nullptr;
}
CCGSubSurf *ss = static_cast<CCGSubSurf *>(allocatorIFC->alloc(allocator, sizeof(*ss)));
ss->allocatorIFC = *allocatorIFC;
ss->allocator = allocator;
ss->vMap = ccg_ehash_new(0, &ss->allocatorIFC, ss->allocator);
ss->eMap = ccg_ehash_new(0, &ss->allocatorIFC, ss->allocator);
ss->fMap = ccg_ehash_new(0, &ss->allocatorIFC, ss->allocator);
ss->meshIFC = *ifc;
ss->subdivLevels = subdivLevels;
ss->numGrids = 0;
ss->allowEdgeCreation = 0;
ss->defaultCreaseValue = 0;
ss->defaultEdgeUserData = nullptr;
ss->useAgeCounts = 0;
ss->vertUserAgeOffset = ss->edgeUserAgeOffset = ss->faceUserAgeOffset = 0;
ss->calcVertNormals = 0;
ss->normalDataOffset = 0;
ss->allocMask = 0;
ss->q = CCGSUBSURF_alloc(ss, ss->meshIFC.vertDataSize);
ss->r = CCGSUBSURF_alloc(ss, ss->meshIFC.vertDataSize);
ss->currentAge = 0;
ss->syncState = eSyncState_None;
ss->oldVMap = ss->oldEMap = ss->oldFMap = nullptr;
ss->lenTempArrays = 0;
ss->tempVerts = nullptr;
ss->tempEdges = nullptr;
return ss;
}
void ccgSubSurf_free(CCGSubSurf *ss)
{
CCGAllocatorIFC allocatorIFC = ss->allocatorIFC;
CCGAllocatorHDL allocator = ss->allocator;
if (ss->syncState) {
ccg_ehash_free(ss->oldFMap, (EHEntryFreeFP)_face_free, ss);
ccg_ehash_free(ss->oldEMap, (EHEntryFreeFP)_edge_free, ss);
ccg_ehash_free(ss->oldVMap, (EHEntryFreeFP)_vert_free, ss);
MEM_freeN(ss->tempVerts);
MEM_freeN(ss->tempEdges);
}
CCGSUBSURF_free(ss, ss->r);
CCGSUBSURF_free(ss, ss->q);
if (ss->defaultEdgeUserData) {
CCGSUBSURF_free(ss, ss->defaultEdgeUserData);
}
ccg_ehash_free(ss->fMap, (EHEntryFreeFP)_face_free, ss);
ccg_ehash_free(ss->eMap, (EHEntryFreeFP)_edge_free, ss);
ccg_ehash_free(ss->vMap, (EHEntryFreeFP)_vert_free, ss);
CCGSUBSURF_free(ss, ss);
if (allocatorIFC.release) {
allocatorIFC.release(allocator);
}
}
CCGError ccgSubSurf_setAllowEdgeCreation(CCGSubSurf *ss,
int allowEdgeCreation,
float defaultCreaseValue,
void *defaultUserData)
{
if (ss->defaultEdgeUserData) {
CCGSUBSURF_free(ss, ss->defaultEdgeUserData);
}
ss->allowEdgeCreation = !!allowEdgeCreation;
ss->defaultCreaseValue = defaultCreaseValue;
ss->defaultEdgeUserData = CCGSUBSURF_alloc(ss, ss->meshIFC.edgeUserSize);
if (defaultUserData) {
memcpy(ss->defaultEdgeUserData, defaultUserData, ss->meshIFC.edgeUserSize);
}
else {
memset(ss->defaultEdgeUserData, 0, ss->meshIFC.edgeUserSize);
}
return eCCGError_None;
}
void ccgSubSurf_getAllowEdgeCreation(CCGSubSurf *ss,
int *allowEdgeCreation_r,
float *defaultCreaseValue_r,
void *defaultUserData_r)
{
if (allowEdgeCreation_r) {
*allowEdgeCreation_r = ss->allowEdgeCreation;
}
if (ss->allowEdgeCreation) {
if (defaultCreaseValue_r) {
*defaultCreaseValue_r = ss->defaultCreaseValue;
}
if (defaultUserData_r) {
memcpy(defaultUserData_r, ss->defaultEdgeUserData, ss->meshIFC.edgeUserSize);
}
}
}
CCGError ccgSubSurf_setSubdivisionLevels(CCGSubSurf *ss, int subdivisionLevels)
{
if (subdivisionLevels <= 0) {
return eCCGError_InvalidValue;
}
if (subdivisionLevels != ss->subdivLevels) {
ss->numGrids = 0;
ss->subdivLevels = subdivisionLevels;
ccg_ehash_free(ss->vMap, (EHEntryFreeFP)_vert_free, ss);
ccg_ehash_free(ss->eMap, (EHEntryFreeFP)_edge_free, ss);
ccg_ehash_free(ss->fMap, (EHEntryFreeFP)_face_free, ss);
ss->vMap = ccg_ehash_new(0, &ss->allocatorIFC, ss->allocator);
ss->eMap = ccg_ehash_new(0, &ss->allocatorIFC, ss->allocator);
ss->fMap = ccg_ehash_new(0, &ss->allocatorIFC, ss->allocator);
}
return eCCGError_None;
}
void ccgSubSurf_getUseAgeCounts(CCGSubSurf *ss,
int *useAgeCounts_r,
int *vertUserOffset_r,
int *edgeUserOffset_r,
int *faceUserOffset_r)
{
*useAgeCounts_r = ss->useAgeCounts;
if (vertUserOffset_r) {
*vertUserOffset_r = ss->vertUserAgeOffset;
}
if (edgeUserOffset_r) {
*edgeUserOffset_r = ss->edgeUserAgeOffset;
}
if (faceUserOffset_r) {
*faceUserOffset_r = ss->faceUserAgeOffset;
}
}
CCGError ccgSubSurf_setUseAgeCounts(
CCGSubSurf *ss, int useAgeCounts, int vertUserOffset, int edgeUserOffset, int faceUserOffset)
{
if (useAgeCounts) {
if ((vertUserOffset + 4 > ss->meshIFC.vertUserSize) ||
(edgeUserOffset + 4 > ss->meshIFC.edgeUserSize) ||
(faceUserOffset + 4 > ss->meshIFC.faceUserSize))
{
return eCCGError_InvalidValue;
}
ss->useAgeCounts = 1;
ss->vertUserAgeOffset = vertUserOffset;
ss->edgeUserAgeOffset = edgeUserOffset;
ss->faceUserAgeOffset = faceUserOffset;
}
else {
ss->useAgeCounts = 0;
ss->vertUserAgeOffset = ss->edgeUserAgeOffset = ss->faceUserAgeOffset = 0;
}
return eCCGError_None;
}
CCGError ccgSubSurf_setCalcVertexNormals(CCGSubSurf *ss, int useVertNormals, int normalDataOffset)
{
if (useVertNormals) {
if (normalDataOffset < 0 || normalDataOffset + 12 > ss->meshIFC.vertDataSize) {
return eCCGError_InvalidValue;
}
ss->calcVertNormals = 1;
ss->normalDataOffset = normalDataOffset;
}
else {
ss->calcVertNormals = 0;
ss->normalDataOffset = 0;
}
return eCCGError_None;
}
void ccgSubSurf_setAllocMask(CCGSubSurf *ss, int allocMask, int maskOffset)
{
ss->allocMask = allocMask;
ss->maskDataOffset = maskOffset;
}
void ccgSubSurf_setNumLayers(CCGSubSurf *ss, int numLayers)
{
ss->meshIFC.numLayers = numLayers;
}
/***/
CCGError ccgSubSurf_initFullSync(CCGSubSurf *ss)
{
if (ss->syncState != eSyncState_None) {
return eCCGError_InvalidSyncState;
}
ss->currentAge++;
ss->oldVMap = ss->vMap;
ss->oldEMap = ss->eMap;
ss->oldFMap = ss->fMap;
ss->vMap = ccg_ehash_new(0, &ss->allocatorIFC, ss->allocator);
ss->eMap = ccg_ehash_new(0, &ss->allocatorIFC, ss->allocator);
ss->fMap = ccg_ehash_new(0, &ss->allocatorIFC, ss->allocator);
ss->numGrids = 0;
ss->lenTempArrays = 12;
ss->tempVerts = MEM_malloc_arrayN<CCGVert *>(size_t(ss->lenTempArrays), "CCGSubsurf tempVerts");
ss->tempEdges = MEM_malloc_arrayN<CCGEdge *>(size_t(ss->lenTempArrays), "CCGSubsurf tempEdges");
ss->syncState = eSyncState_Vert;
return eCCGError_None;
}
CCGError ccgSubSurf_initPartialSync(CCGSubSurf *ss)
{
if (ss->syncState != eSyncState_None) {
return eCCGError_InvalidSyncState;
}
ss->currentAge++;
ss->syncState = eSyncState_Partial;
return eCCGError_None;
}
CCGError ccgSubSurf_syncVertDel(CCGSubSurf *ss, CCGVertHDL vHDL)
{
if (ss->syncState != eSyncState_Partial) {
return eCCGError_InvalidSyncState;
}
void **prevp;
CCGVert *v = static_cast<CCGVert *>(ccg_ehash_lookupWithPrev(ss->vMap, vHDL, &prevp));
if (!v || v->numFaces || v->numEdges) {
return eCCGError_InvalidValue;
}
*prevp = v->next;
_vert_free(v, ss);
return eCCGError_None;
}
CCGError ccgSubSurf_syncEdgeDel(CCGSubSurf *ss, CCGEdgeHDL eHDL)
{
if (ss->syncState != eSyncState_Partial) {
return eCCGError_InvalidSyncState;
}
void **prevp;
CCGEdge *e = static_cast<CCGEdge *>(ccg_ehash_lookupWithPrev(ss->eMap, eHDL, &prevp));
if (!e || e->numFaces) {
return eCCGError_InvalidValue;
}
*prevp = e->next;
_edge_unlinkMarkAndFree(e, ss);
return eCCGError_None;
}
CCGError ccgSubSurf_syncFaceDel(CCGSubSurf *ss, CCGFaceHDL fHDL)
{
if (ss->syncState != eSyncState_Partial) {
return eCCGError_InvalidSyncState;
}
void **prevp;
CCGFace *f = static_cast<CCGFace *>(ccg_ehash_lookupWithPrev(ss->fMap, fHDL, &prevp));
if (!f) {
return eCCGError_InvalidValue;
}
*prevp = f->next;
_face_unlinkMarkAndFree(f, ss);
return eCCGError_None;
}
CCGError ccgSubSurf_syncVert(
CCGSubSurf *ss, CCGVertHDL vHDL, const void *vertData, int seam, CCGVert **v_r)
{
void **prevp;
CCGVert *v = nullptr;
short seamflag = (seam) ? Vert_eSeam : 0;
if (ss->syncState == eSyncState_Partial) {
v = static_cast<CCGVert *>(ccg_ehash_lookupWithPrev(ss->vMap, vHDL, &prevp));
if (!v) {
v = _vert_new(vHDL, ss);
VertDataCopy(static_cast<float *>(ccg_vert_getCo(v, 0, ss->meshIFC.vertDataSize)),
static_cast<const float *>(vertData),
ss);
ccg_ehash_insert(ss->vMap, (EHEntry *)v);
v->flags = Vert_eEffected | seamflag;
}
else if (!VertDataEqual(
static_cast<const float *>(vertData),
static_cast<const float *>(ccg_vert_getCo(v, 0, ss->meshIFC.vertDataSize)),
ss) ||
((v->flags & Vert_eSeam) != seamflag))
{
int i, j;
VertDataCopy(static_cast<float *>(ccg_vert_getCo(v, 0, ss->meshIFC.vertDataSize)),
static_cast<const float *>(vertData),
ss);
v->flags = Vert_eEffected | seamflag;
for (i = 0; i < v->numEdges; i++) {
CCGEdge *e = v->edges[i];
e->v0->flags |= Vert_eEffected;
e->v1->flags |= Vert_eEffected;
}
for (i = 0; i < v->numFaces; i++) {
CCGFace *f = v->faces[i];
for (j = 0; j < f->numVerts; j++) {
FACE_getVerts(f)[j]->flags |= Vert_eEffected;
}
}
}
}
else {
if (ss->syncState != eSyncState_Vert) {
return eCCGError_InvalidSyncState;
}
v = static_cast<CCGVert *>(ccg_ehash_lookupWithPrev(ss->oldVMap, vHDL, &prevp));
if (!v) {
v = _vert_new(vHDL, ss);
VertDataCopy(static_cast<float *>(ccg_vert_getCo(v, 0, ss->meshIFC.vertDataSize)),
static_cast<const float *>(vertData),
ss);
ccg_ehash_insert(ss->vMap, (EHEntry *)v);
v->flags = Vert_eEffected | seamflag;
}
else if (!VertDataEqual(
static_cast<const float *>(vertData),
static_cast<const float *>(ccg_vert_getCo(v, 0, ss->meshIFC.vertDataSize)),
ss) ||
((v->flags & Vert_eSeam) != seamflag))
{
*prevp = v->next;
ccg_ehash_insert(ss->vMap, (EHEntry *)v);
VertDataCopy(static_cast<float *>(ccg_vert_getCo(v, 0, ss->meshIFC.vertDataSize)),
static_cast<const float *>(vertData),
ss);
v->flags = Vert_eEffected | Vert_eChanged | seamflag;
}
else {
*prevp = v->next;
ccg_ehash_insert(ss->vMap, (EHEntry *)v);
v->flags = 0;
}
}
if (v_r) {
*v_r = v;
}
return eCCGError_None;
}
CCGError ccgSubSurf_syncEdge(CCGSubSurf *ss,
CCGEdgeHDL eHDL,
CCGVertHDL e_vHDL0,
CCGVertHDL e_vHDL1,
float crease,
CCGEdge **e_r)
{
void **prevp;
CCGEdge *e = nullptr, *eNew;
if (ss->syncState == eSyncState_Partial) {
e = static_cast<CCGEdge *>(ccg_ehash_lookupWithPrev(ss->eMap, eHDL, &prevp));
if (!e || e->v0->vHDL != e_vHDL0 || e->v1->vHDL != e_vHDL1 || crease != e->crease) {
CCGVert *v0 = static_cast<CCGVert *>(ccg_ehash_lookup(ss->vMap, e_vHDL0));
CCGVert *v1 = static_cast<CCGVert *>(ccg_ehash_lookup(ss->vMap, e_vHDL1));
eNew = _edge_new(eHDL, v0, v1, crease, ss);
if (e) {
*prevp = eNew;
eNew->next = e->next;
_edge_unlinkMarkAndFree(e, ss);
}
else {
ccg_ehash_insert(ss->eMap, (EHEntry *)eNew);
}
eNew->v0->flags |= Vert_eEffected;
eNew->v1->flags |= Vert_eEffected;
}
}
else {
if (ss->syncState == eSyncState_Vert) {
ss->syncState = eSyncState_Edge;
}
else if (ss->syncState != eSyncState_Edge) {
return eCCGError_InvalidSyncState;
}
e = static_cast<CCGEdge *>(ccg_ehash_lookupWithPrev(ss->oldEMap, eHDL, &prevp));
if (!e || e->v0->vHDL != e_vHDL0 || e->v1->vHDL != e_vHDL1 || e->crease != crease) {
CCGVert *v0 = static_cast<CCGVert *>(ccg_ehash_lookup(ss->vMap, e_vHDL0));
CCGVert *v1 = static_cast<CCGVert *>(ccg_ehash_lookup(ss->vMap, e_vHDL1));
e = _edge_new(eHDL, v0, v1, crease, ss);
ccg_ehash_insert(ss->eMap, (EHEntry *)e);
e->v0->flags |= Vert_eEffected;
e->v1->flags |= Vert_eEffected;
}
else {
*prevp = e->next;
ccg_ehash_insert(ss->eMap, (EHEntry *)e);
e->flags = 0;
if ((e->v0->flags | e->v1->flags) & Vert_eChanged) {
e->v0->flags |= Vert_eEffected;
e->v1->flags |= Vert_eEffected;
}
}
}
if (e_r) {
*e_r = e;
}
return eCCGError_None;
}
CCGError ccgSubSurf_syncFace(
CCGSubSurf *ss, CCGFaceHDL fHDL, int numVerts, CCGVertHDL *vHDLs, CCGFace **f_r)
{
void **prevp;
CCGFace *f = nullptr, *fNew;
int j, k, topologyChanged = 0;
if (UNLIKELY(numVerts > ss->lenTempArrays)) {
ss->lenTempArrays = (numVerts < ss->lenTempArrays * 2) ? ss->lenTempArrays * 2 : numVerts;
ss->tempVerts = static_cast<CCGVert **>(
MEM_reallocN(ss->tempVerts, sizeof(*ss->tempVerts) * ss->lenTempArrays));
ss->tempEdges = static_cast<CCGEdge **>(
MEM_reallocN(ss->tempEdges, sizeof(*ss->tempEdges) * ss->lenTempArrays));
}
if (ss->syncState == eSyncState_Partial) {
f = static_cast<CCGFace *>(ccg_ehash_lookupWithPrev(ss->fMap, fHDL, &prevp));
for (k = 0; k < numVerts; k++) {
ss->tempVerts[k] = static_cast<CCGVert *>(ccg_ehash_lookup(ss->vMap, vHDLs[k]));
}
for (k = 0; k < numVerts; k++) {
ss->tempEdges[k] = _vert_findEdgeTo(ss->tempVerts[k], ss->tempVerts[(k + 1) % numVerts]);
}
if (f) {
if (f->numVerts != numVerts ||
memcmp(FACE_getVerts(f), ss->tempVerts, sizeof(*ss->tempVerts) * numVerts) != 0 ||
memcmp(FACE_getEdges(f), ss->tempEdges, sizeof(*ss->tempEdges) * numVerts) != 0)
{
topologyChanged = 1;
}
}
if (!f || topologyChanged) {
fNew = _face_new(fHDL, ss->tempVerts, ss->tempEdges, numVerts, ss);
if (f) {
ss->numGrids += numVerts - f->numVerts;
*prevp = fNew;
fNew->next = f->next;
_face_unlinkMarkAndFree(f, ss);
}
else {
ss->numGrids += numVerts;
ccg_ehash_insert(ss->fMap, (EHEntry *)fNew);
}
for (k = 0; k < numVerts; k++) {
FACE_getVerts(fNew)[k]->flags |= Vert_eEffected;
}
}
}
else {
if (ELEM(ss->syncState, eSyncState_Vert, eSyncState_Edge)) {
ss->syncState = eSyncState_Face;
}
else if (ss->syncState != eSyncState_Face) {
return eCCGError_InvalidSyncState;
}
f = static_cast<CCGFace *>(ccg_ehash_lookupWithPrev(ss->oldFMap, fHDL, &prevp));
for (k = 0; k < numVerts; k++) {
ss->tempVerts[k] = static_cast<CCGVert *>(ccg_ehash_lookup(ss->vMap, vHDLs[k]));
if (!ss->tempVerts[k]) {
return eCCGError_InvalidValue;
}
}
for (k = 0; k < numVerts; k++) {
ss->tempEdges[k] = _vert_findEdgeTo(ss->tempVerts[k], ss->tempVerts[(k + 1) % numVerts]);
if (!ss->tempEdges[k]) {
if (ss->allowEdgeCreation) {
CCGEdge *e = ss->tempEdges[k] = _edge_new((CCGEdgeHDL)-1,
ss->tempVerts[k],
ss->tempVerts[(k + 1) % numVerts],
ss->defaultCreaseValue,
ss);
ccg_ehash_insert(ss->eMap, (EHEntry *)e);
e->v0->flags |= Vert_eEffected;
e->v1->flags |= Vert_eEffected;
if (ss->meshIFC.edgeUserSize) {
memcpy(ccgSubSurf_getEdgeUserData(ss, e),
ss->defaultEdgeUserData,
ss->meshIFC.edgeUserSize);
}
}
else {
return eCCGError_InvalidValue;
}
}
}
if (f) {
if (f->numVerts != numVerts ||
memcmp(FACE_getVerts(f), ss->tempVerts, sizeof(*ss->tempVerts) * numVerts) != 0 ||
memcmp(FACE_getEdges(f), ss->tempEdges, sizeof(*ss->tempEdges) * numVerts) != 0)
{
topologyChanged = 1;
}
}
if (!f || topologyChanged) {
f = _face_new(fHDL, ss->tempVerts, ss->tempEdges, numVerts, ss);
ccg_ehash_insert(ss->fMap, (EHEntry *)f);
ss->numGrids += numVerts;
for (k = 0; k < numVerts; k++) {
FACE_getVerts(f)[k]->flags |= Vert_eEffected;
}
}
else {
*prevp = f->next;
ccg_ehash_insert(ss->fMap, (EHEntry *)f);
f->flags = 0;
ss->numGrids += f->numVerts;
for (j = 0; j < f->numVerts; j++) {
if (FACE_getVerts(f)[j]->flags & Vert_eChanged) {
for (k = 0; k < f->numVerts; k++) {
FACE_getVerts(f)[k]->flags |= Vert_eEffected;
}
break;
}
}
}
}
if (f_r) {
*f_r = f;
}
return eCCGError_None;
}
static void ccgSubSurf__sync(CCGSubSurf *ss)
{
ccgSubSurf__sync_legacy(ss);
}
CCGError ccgSubSurf_processSync(CCGSubSurf *ss)
{
if (ss->syncState == eSyncState_Partial) {
ss->syncState = eSyncState_None;
ccgSubSurf__sync(ss);
}
else if (ss->syncState) {
ccg_ehash_free(ss->oldFMap, (EHEntryFreeFP)_face_unlinkMarkAndFree, ss);
ccg_ehash_free(ss->oldEMap, (EHEntryFreeFP)_edge_unlinkMarkAndFree, ss);
ccg_ehash_free(ss->oldVMap, (EHEntryFreeFP)_vert_free, ss);
MEM_freeN(ss->tempEdges);
MEM_freeN(ss->tempVerts);
ss->lenTempArrays = 0;
ss->oldFMap = ss->oldEMap = ss->oldVMap = nullptr;
ss->tempVerts = nullptr;
ss->tempEdges = nullptr;
ss->syncState = eSyncState_None;
ccgSubSurf__sync(ss);
}
else {
return eCCGError_InvalidSyncState;
}
return eCCGError_None;
}
void ccgSubSurf__allFaces(CCGSubSurf *ss, CCGFace ***faces, int *numFaces, int *freeFaces)
{
CCGFace **array;
int i, num;
if (*faces == nullptr) {
array = MEM_malloc_arrayN<CCGFace *>(size_t(ss->fMap->numEntries), "CCGSubsurf allFaces");
num = 0;
for (i = 0; i < ss->fMap->curSize; i++) {
CCGFace *f = (CCGFace *)ss->fMap->buckets[i];
for (; f; f = f->next) {
array[num++] = f;
}
}
*faces = array;
*numFaces = num;
*freeFaces = 1;
}
else {
*freeFaces = 0;
}
}
void ccgSubSurf__effectedFaceNeighbors(CCGSubSurf *ss,
CCGFace **faces,
int numFaces,
CCGVert ***verts,
int *numVerts,
CCGEdge ***edges,
int *numEdges)
{
CCGVert **arrayV;
CCGEdge **arrayE;
int numV, numE, i, j;
arrayV = MEM_malloc_arrayN<CCGVert *>(size_t(ss->vMap->numEntries), "CCGSubsurf arrayV");
arrayE = MEM_malloc_arrayN<CCGEdge *>(size_t(ss->eMap->numEntries), "CCGSubsurf arrayV");
numV = numE = 0;
for (i = 0; i < numFaces; i++) {
CCGFace *f = faces[i];
f->flags |= Face_eEffected;
}
for (i = 0; i < ss->vMap->curSize; i++) {
CCGVert *v = (CCGVert *)ss->vMap->buckets[i];
for (; v; v = v->next) {
for (j = 0; j < v->numFaces; j++) {
if (!(v->faces[j]->flags & Face_eEffected)) {
break;
}
}
if (j == v->numFaces) {
arrayV[numV++] = v;
v->flags |= Vert_eEffected;
}
}
}
for (i = 0; i < ss->eMap->curSize; i++) {
CCGEdge *e = (CCGEdge *)ss->eMap->buckets[i];
for (; e; e = e->next) {
for (j = 0; j < e->numFaces; j++) {
if (!(e->faces[j]->flags & Face_eEffected)) {
break;
}
}
if (j == e->numFaces) {
e->flags |= Edge_eEffected;
arrayE[numE++] = e;
}
}
}
*verts = arrayV;
*numVerts = numV;
*edges = arrayE;
*numEdges = numE;
}
CCGError ccgSubSurf_updateFromFaces(CCGSubSurf *ss, int lvl, CCGFace **effectedF, int numEffectedF)
{
int i, S, x, gridSize, cornerIdx, subdivLevels;
int vertDataSize = ss->meshIFC.vertDataSize, freeF;
subdivLevels = ss->subdivLevels;
lvl = (lvl) ? lvl : subdivLevels;
gridSize = ccg_gridsize(lvl);
cornerIdx = gridSize - 1;
ccgSubSurf__allFaces(ss, &effectedF, &numEffectedF, &freeF);
for (i = 0; i < numEffectedF; i++) {
CCGFace *f = effectedF[i];
for (S = 0; S < f->numVerts; S++) {
CCGEdge *e = FACE_getEdges(f)[S];
CCGEdge *prevE = FACE_getEdges(f)[(S + f->numVerts - 1) % f->numVerts];
VertDataCopy((float *)FACE_getCenterData(f), FACE_getIFCo(f, lvl, S, 0, 0), ss);
VertDataCopy(
VERT_getCo(FACE_getVerts(f)[S], lvl), FACE_getIFCo(f, lvl, S, cornerIdx, cornerIdx), ss);
for (x = 0; x < gridSize; x++) {
VertDataCopy(FACE_getIECo(f, lvl, S, x), FACE_getIFCo(f, lvl, S, x, 0), ss);
}
for (x = 0; x < gridSize; x++) {
int eI = gridSize - 1 - x;
VertDataCopy(
static_cast<float *>(_edge_getCoVert(e, FACE_getVerts(f)[S], lvl, eI, vertDataSize)),
FACE_getIFCo(f, lvl, S, cornerIdx, x),
ss);
VertDataCopy(static_cast<float *>(
_edge_getCoVert(prevE, FACE_getVerts(f)[S], lvl, eI, vertDataSize)),
FACE_getIFCo(f, lvl, S, x, cornerIdx),
ss);
}
}
}
if (freeF) {
MEM_freeN(effectedF);
}
return eCCGError_None;
}
CCGError ccgSubSurf_updateToFaces(CCGSubSurf *ss, int lvl, CCGFace **effectedF, int numEffectedF)
{
int i, S, x, gridSize, cornerIdx, subdivLevels;
int vertDataSize = ss->meshIFC.vertDataSize, freeF;
subdivLevels = ss->subdivLevels;
lvl = (lvl) ? lvl : subdivLevels;
gridSize = ccg_gridsize(lvl);
cornerIdx = gridSize - 1;
ccgSubSurf__allFaces(ss, &effectedF, &numEffectedF, &freeF);
for (i = 0; i < numEffectedF; i++) {
CCGFace *f = effectedF[i];
for (S = 0; S < f->numVerts; S++) {
int prevS = (S + f->numVerts - 1) % f->numVerts;
CCGEdge *e = FACE_getEdges(f)[S];
CCGEdge *prevE = FACE_getEdges(f)[prevS];
for (x = 0; x < gridSize; x++) {
int eI = gridSize - 1 - x;
VertDataCopy(FACE_getIFCo(f, lvl, S, cornerIdx, x),
static_cast<const float *>(
_edge_getCoVert(e, FACE_getVerts(f)[S], lvl, eI, vertDataSize)),
ss);
VertDataCopy(FACE_getIFCo(f, lvl, S, x, cornerIdx),
static_cast<const float *>(
_edge_getCoVert(prevE, FACE_getVerts(f)[S], lvl, eI, vertDataSize)),
ss);
}
for (x = 1; x < gridSize - 1; x++) {
VertDataCopy(FACE_getIFCo(f, lvl, S, 0, x), FACE_getIECo(f, lvl, prevS, x), ss);
VertDataCopy(FACE_getIFCo(f, lvl, S, x, 0), FACE_getIECo(f, lvl, S, x), ss);
}
VertDataCopy(FACE_getIFCo(f, lvl, S, 0, 0), (float *)FACE_getCenterData(f), ss);
VertDataCopy(
FACE_getIFCo(f, lvl, S, cornerIdx, cornerIdx), VERT_getCo(FACE_getVerts(f)[S], lvl), ss);
}
}
if (freeF) {
MEM_freeN(effectedF);
}
return eCCGError_None;
}
CCGError ccgSubSurf_stitchFaces(CCGSubSurf *ss, int lvl, CCGFace **effectedF, int numEffectedF)
{
CCGVert **effectedV;
CCGEdge **effectedE;
int numEffectedV, numEffectedE, freeF;
int i, S, x, gridSize, cornerIdx, subdivLevels, edgeSize;
int vertDataSize = ss->meshIFC.vertDataSize;
subdivLevels = ss->subdivLevels;
lvl = (lvl) ? lvl : subdivLevels;
gridSize = ccg_gridsize(lvl);
edgeSize = ccg_edgesize(lvl);
cornerIdx = gridSize - 1;
ccgSubSurf__allFaces(ss, &effectedF, &numEffectedF, &freeF);
ccgSubSurf__effectedFaceNeighbors(
ss, effectedF, numEffectedF, &effectedV, &numEffectedV, &effectedE, &numEffectedE);
/* zero */
for (i = 0; i < numEffectedV; i++) {
CCGVert *v = effectedV[i];
if (v->numFaces) {
VertDataZero(VERT_getCo(v, lvl), ss);
}
}
for (i = 0; i < numEffectedE; i++) {
CCGEdge *e = effectedE[i];
if (e->numFaces) {
for (x = 0; x < edgeSize; x++) {
VertDataZero(EDGE_getCo(e, lvl, x), ss);
}
}
}
/* add */
for (i = 0; i < numEffectedF; i++) {
CCGFace *f = effectedF[i];
VertDataZero((float *)FACE_getCenterData(f), ss);
for (S = 0; S < f->numVerts; S++) {
for (x = 0; x < gridSize; x++) {
VertDataZero(FACE_getIECo(f, lvl, S, x), ss);
}
}
for (S = 0; S < f->numVerts; S++) {
int prevS = (S + f->numVerts - 1) % f->numVerts;
CCGEdge *e = FACE_getEdges(f)[S];
CCGEdge *prevE = FACE_getEdges(f)[prevS];
VertDataAdd((float *)FACE_getCenterData(f), FACE_getIFCo(f, lvl, S, 0, 0), ss);
if (FACE_getVerts(f)[S]->flags & Vert_eEffected) {
VertDataAdd(VERT_getCo(FACE_getVerts(f)[S], lvl),
FACE_getIFCo(f, lvl, S, cornerIdx, cornerIdx),
ss);
}
for (x = 1; x < gridSize - 1; x++) {
VertDataAdd(FACE_getIECo(f, lvl, S, x), FACE_getIFCo(f, lvl, S, x, 0), ss);
VertDataAdd(FACE_getIECo(f, lvl, prevS, x), FACE_getIFCo(f, lvl, S, 0, x), ss);
}
for (x = 0; x < gridSize - 1; x++) {
int eI = gridSize - 1 - x;
if (FACE_getEdges(f)[S]->flags & Edge_eEffected) {
VertDataAdd(
static_cast<float *>(_edge_getCoVert(e, FACE_getVerts(f)[S], lvl, eI, vertDataSize)),
FACE_getIFCo(f, lvl, S, cornerIdx, x),
ss);
}
if (FACE_getEdges(f)[prevS]->flags & Edge_eEffected) {
if (x != 0) {
VertDataAdd(static_cast<float *>(
_edge_getCoVert(prevE, FACE_getVerts(f)[S], lvl, eI, vertDataSize)),
FACE_getIFCo(f, lvl, S, x, cornerIdx),
ss);
}
}
}
}
}
/* average */
for (i = 0; i < numEffectedV; i++) {
CCGVert *v = effectedV[i];
if (v->numFaces) {
VertDataMulN(VERT_getCo(v, lvl), 1.0f / v->numFaces, ss);
}
}
for (i = 0; i < numEffectedE; i++) {
CCGEdge *e = effectedE[i];
VertDataCopy(EDGE_getCo(e, lvl, 0), VERT_getCo(e->v0, lvl), ss);
VertDataCopy(EDGE_getCo(e, lvl, edgeSize - 1), VERT_getCo(e->v1, lvl), ss);
if (e->numFaces) {
for (x = 1; x < edgeSize - 1; x++) {
VertDataMulN(EDGE_getCo(e, lvl, x), 1.0f / e->numFaces, ss);
}
}
}
/* copy */
for (i = 0; i < numEffectedF; i++) {
CCGFace *f = effectedF[i];
VertDataMulN((float *)FACE_getCenterData(f), 1.0f / f->numVerts, ss);
for (S = 0; S < f->numVerts; S++) {
for (x = 1; x < gridSize - 1; x++) {
VertDataMulN(FACE_getIECo(f, lvl, S, x), 0.5f, ss);
}
}
for (S = 0; S < f->numVerts; S++) {
int prevS = (S + f->numVerts - 1) % f->numVerts;
CCGEdge *e = FACE_getEdges(f)[S];
CCGEdge *prevE = FACE_getEdges(f)[prevS];
VertDataCopy(FACE_getIFCo(f, lvl, S, 0, 0), (float *)FACE_getCenterData(f), ss);
VertDataCopy(
FACE_getIFCo(f, lvl, S, cornerIdx, cornerIdx), VERT_getCo(FACE_getVerts(f)[S], lvl), ss);
for (x = 1; x < gridSize - 1; x++) {
VertDataCopy(FACE_getIFCo(f, lvl, S, x, 0), FACE_getIECo(f, lvl, S, x), ss);
VertDataCopy(FACE_getIFCo(f, lvl, S, 0, x), FACE_getIECo(f, lvl, prevS, x), ss);
}
for (x = 0; x < gridSize - 1; x++) {
int eI = gridSize - 1 - x;
VertDataCopy(FACE_getIFCo(f, lvl, S, cornerIdx, x),
static_cast<const float *>(
_edge_getCoVert(e, FACE_getVerts(f)[S], lvl, eI, vertDataSize)),
ss);
VertDataCopy(FACE_getIFCo(f, lvl, S, x, cornerIdx),
static_cast<const float *>(
_edge_getCoVert(prevE, FACE_getVerts(f)[S], lvl, eI, vertDataSize)),
ss);
}
VertDataCopy(FACE_getIECo(f, lvl, S, 0), (float *)FACE_getCenterData(f), ss);
VertDataCopy(
FACE_getIECo(f, lvl, S, gridSize - 1), FACE_getIFCo(f, lvl, S, gridSize - 1, 0), ss);
}
}
for (i = 0; i < numEffectedV; i++) {
effectedV[i]->flags = 0;
}
for (i = 0; i < numEffectedE; i++) {
effectedE[i]->flags = 0;
}
for (i = 0; i < numEffectedF; i++) {
effectedF[i]->flags = 0;
}
MEM_freeN(effectedE);
MEM_freeN(effectedV);
if (freeF) {
MEM_freeN(effectedF);
}
return eCCGError_None;
}
/*** External API accessor functions ***/
int ccgSubSurf_getNumVerts(const CCGSubSurf *ss)
{
return ss->vMap->numEntries;
}
int ccgSubSurf_getNumEdges(const CCGSubSurf *ss)
{
return ss->eMap->numEntries;
}
int ccgSubSurf_getNumFaces(const CCGSubSurf *ss)
{
return ss->fMap->numEntries;
}
CCGVert *ccgSubSurf_getVert(CCGSubSurf *ss, CCGVertHDL v)
{
return (CCGVert *)ccg_ehash_lookup(ss->vMap, v);
}
CCGEdge *ccgSubSurf_getEdge(CCGSubSurf *ss, CCGEdgeHDL e)
{
return (CCGEdge *)ccg_ehash_lookup(ss->eMap, e);
}
CCGFace *ccgSubSurf_getFace(CCGSubSurf *ss, CCGFaceHDL f)
{
return (CCGFace *)ccg_ehash_lookup(ss->fMap, f);
}
int ccgSubSurf_getSubdivisionLevels(const CCGSubSurf *ss)
{
return ss->subdivLevels;
}
int ccgSubSurf_getEdgeSize(const CCGSubSurf *ss)
{
return ccgSubSurf_getEdgeLevelSize(ss, ss->subdivLevels);
}
int ccgSubSurf_getEdgeLevelSize(const CCGSubSurf *ss, int level)
{
if (level < 1 || level > ss->subdivLevels) {
return -1;
}
return ccg_edgesize(level);
}
int ccgSubSurf_getGridSize(const CCGSubSurf *ss)
{
return ccgSubSurf_getGridLevelSize(ss, ss->subdivLevels);
}
int ccgSubSurf_getGridLevelSize(const CCGSubSurf *ss, int level)
{
if (level < 1 || level > ss->subdivLevels) {
return -1;
}
return ccg_gridsize(level);
}
int ccgSubSurf_getSimpleSubdiv(const CCGSubSurf *ss)
{
return ss->meshIFC.simpleSubdiv;
}
/* Vert accessors */
CCGVertHDL ccgSubSurf_getVertVertHandle(CCGVert *v)
{
return v->vHDL;
}
int ccgSubSurf_getVertAge(CCGSubSurf *ss, CCGVert *v)
{
if (ss->useAgeCounts) {
uint8_t *user_data = static_cast<uint8_t *>(ccgSubSurf_getVertUserData(ss, v));
return ss->currentAge - *((int *)&user_data[ss->vertUserAgeOffset]);
}
return 0;
}
void *ccgSubSurf_getVertUserData(CCGSubSurf *ss, CCGVert *v)
{
return VERT_getLevelData(v) + ss->meshIFC.vertDataSize * (ss->subdivLevels + 1);
}
int ccgSubSurf_getVertNumFaces(CCGVert *v)
{
return v->numFaces;
}
CCGFace *ccgSubSurf_getVertFace(CCGVert *v, int index)
{
if (index < 0 || index >= v->numFaces) {
return nullptr;
}
return v->faces[index];
}
int ccgSubSurf_getVertNumEdges(CCGVert *v)
{
return v->numEdges;
}
CCGEdge *ccgSubSurf_getVertEdge(CCGVert *v, int index)
{
if (index < 0 || index >= v->numEdges) {
return nullptr;
}
return v->edges[index];
}
void *ccgSubSurf_getVertData(CCGSubSurf *ss, CCGVert *v)
{
return ccgSubSurf_getVertLevelData(ss, v, ss->subdivLevels);
}
void *ccgSubSurf_getVertLevelData(CCGSubSurf *ss, CCGVert *v, int level)
{
if (level < 0 || level > ss->subdivLevels) {
return nullptr;
}
return ccg_vert_getCo(v, level, ss->meshIFC.vertDataSize);
}
/* Edge accessors */
CCGEdgeHDL ccgSubSurf_getEdgeEdgeHandle(CCGEdge *e)
{
return e->eHDL;
}
int ccgSubSurf_getEdgeAge(CCGSubSurf *ss, CCGEdge *e)
{
if (ss->useAgeCounts) {
uint8_t *user_data = static_cast<uint8_t *>(ccgSubSurf_getEdgeUserData(ss, e));
return ss->currentAge - *((int *)&user_data[ss->edgeUserAgeOffset]);
}
return 0;
}
void *ccgSubSurf_getEdgeUserData(CCGSubSurf *ss, CCGEdge *e)
{
return (EDGE_getLevelData(e) + ss->meshIFC.vertDataSize * ccg_edgebase(ss->subdivLevels + 1));
}
int ccgSubSurf_getEdgeNumFaces(CCGEdge *e)
{
return e->numFaces;
}
CCGFace *ccgSubSurf_getEdgeFace(CCGEdge *e, int index)
{
if (index < 0 || index >= e->numFaces) {
return nullptr;
}
return e->faces[index];
}
CCGVert *ccgSubSurf_getEdgeVert0(CCGEdge *e)
{
return e->v0;
}
CCGVert *ccgSubSurf_getEdgeVert1(CCGEdge *e)
{
return e->v1;
}
void *ccgSubSurf_getEdgeDataArray(CCGSubSurf *ss, CCGEdge *e)
{
return ccgSubSurf_getEdgeData(ss, e, 0);
}
void *ccgSubSurf_getEdgeData(CCGSubSurf *ss, CCGEdge *e, int x)
{
return ccgSubSurf_getEdgeLevelData(ss, e, x, ss->subdivLevels);
}
void *ccgSubSurf_getEdgeLevelData(CCGSubSurf *ss, CCGEdge *e, int x, int level)
{
if (level < 0 || level > ss->subdivLevels) {
return nullptr;
}
return ccg_edge_getCo(e, level, x, ss->meshIFC.vertDataSize);
}
float ccgSubSurf_getEdgeCrease(CCGEdge *e)
{
return e->crease;
}
/* Face accessors */
CCGFaceHDL ccgSubSurf_getFaceFaceHandle(CCGFace *f)
{
return f->fHDL;
}
int ccgSubSurf_getFaceAge(CCGSubSurf *ss, CCGFace *f)
{
if (ss->useAgeCounts) {
uint8_t *user_data = static_cast<uint8_t *>(ccgSubSurf_getFaceUserData(ss, f));
return ss->currentAge - *((int *)&user_data[ss->faceUserAgeOffset]);
}
return 0;
}
void *ccgSubSurf_getFaceUserData(CCGSubSurf *ss, CCGFace *f)
{
int maxGridSize = ccg_gridsize(ss->subdivLevels);
return FACE_getCenterData(f) +
ss->meshIFC.vertDataSize *
(1 + f->numVerts * maxGridSize + f->numVerts * maxGridSize * maxGridSize);
}
int ccgSubSurf_getFaceNumVerts(CCGFace *f)
{
return f->numVerts;
}
CCGVert *ccgSubSurf_getFaceVert(CCGFace *f, int index)
{
if (index < 0 || index >= f->numVerts) {
return nullptr;
}
return FACE_getVerts(f)[index];
}
CCGEdge *ccgSubSurf_getFaceEdge(CCGFace *f, int index)
{
if (index < 0 || index >= f->numVerts) {
return nullptr;
}
return FACE_getEdges(f)[index];
}
int ccgSubSurf_getFaceEdgeIndex(CCGFace *f, CCGEdge *e)
{
for (int i = 0; i < f->numVerts; i++) {
if (FACE_getEdges(f)[i] == e) {
return i;
}
}
return -1;
}
void *ccgSubSurf_getFaceCenterData(CCGFace *f)
{
return FACE_getCenterData(f);
}
void *ccgSubSurf_getFaceGridEdgeDataArray(CCGSubSurf *ss, CCGFace *f, int gridIndex)
{
return ccgSubSurf_getFaceGridEdgeData(ss, f, gridIndex, 0);
}
void *ccgSubSurf_getFaceGridEdgeData(CCGSubSurf *ss, CCGFace *f, int gridIndex, int x)
{
return ccg_face_getIECo(
f, ss->subdivLevels, gridIndex, x, ss->subdivLevels, ss->meshIFC.vertDataSize);
}
void *ccgSubSurf_getFaceGridDataArray(CCGSubSurf *ss, CCGFace *f, int gridIndex)
{
return ccgSubSurf_getFaceGridData(ss, f, gridIndex, 0, 0);
}
void *ccgSubSurf_getFaceGridData(CCGSubSurf *ss, CCGFace *f, int gridIndex, int x, int y)
{
return ccg_face_getIFCo(
f, ss->subdivLevels, gridIndex, x, y, ss->subdivLevels, ss->meshIFC.vertDataSize);
}
/*** External API iterator functions ***/
void ccgSubSurf_initVertIterator(CCGSubSurf *ss, CCGVertIterator *viter)
{
ccg_ehashIterator_init(ss->vMap, viter);
}
void ccgSubSurf_initEdgeIterator(CCGSubSurf *ss, CCGEdgeIterator *eiter)
{
ccg_ehashIterator_init(ss->eMap, eiter);
}
void ccgSubSurf_initFaceIterator(CCGSubSurf *ss, CCGFaceIterator *fiter)
{
ccg_ehashIterator_init(ss->fMap, fiter);
}
CCGVert *ccgVertIterator_getCurrent(CCGVertIterator *vi)
{
return (CCGVert *)ccg_ehashIterator_getCurrent((EHashIterator *)vi);
}
int ccgVertIterator_isStopped(CCGVertIterator *vi)
{
return ccg_ehashIterator_isStopped((EHashIterator *)vi);
}
void ccgVertIterator_next(CCGVertIterator *vi)
{
ccg_ehashIterator_next((EHashIterator *)vi);
}
CCGEdge *ccgEdgeIterator_getCurrent(CCGEdgeIterator *ei)
{
return (CCGEdge *)ccg_ehashIterator_getCurrent((EHashIterator *)ei);
}
int ccgEdgeIterator_isStopped(CCGEdgeIterator *ei)
{
return ccg_ehashIterator_isStopped((EHashIterator *)ei);
}
void ccgEdgeIterator_next(CCGEdgeIterator *ei)
{
ccg_ehashIterator_next((EHashIterator *)ei);
}
CCGFace *ccgFaceIterator_getCurrent(CCGFaceIterator *fi)
{
return (CCGFace *)ccg_ehashIterator_getCurrent((EHashIterator *)fi);
}
int ccgFaceIterator_isStopped(CCGFaceIterator *fi)
{
return ccg_ehashIterator_isStopped((EHashIterator *)fi);
}
void ccgFaceIterator_next(CCGFaceIterator *fi)
{
ccg_ehashIterator_next((EHashIterator *)fi);
}
/*** External API final vert/edge/face interface. ***/
int ccgSubSurf_getNumFinalVerts(const CCGSubSurf *ss)
{
int edgeSize = ccg_edgesize(ss->subdivLevels);
int gridSize = ccg_gridsize(ss->subdivLevels);
int numFinalVerts = (ss->vMap->numEntries + ss->eMap->numEntries * (edgeSize - 2) +
ss->fMap->numEntries +
ss->numGrids * ((gridSize - 2) + ((gridSize - 2) * (gridSize - 2))));
return numFinalVerts;
}
int ccgSubSurf_getNumFinalEdges(const CCGSubSurf *ss)
{
int edgeSize = ccg_edgesize(ss->subdivLevels);
int gridSize = ccg_gridsize(ss->subdivLevels);
int numFinalEdges = (ss->eMap->numEntries * (edgeSize - 1) +
ss->numGrids * ((gridSize - 1) + 2 * ((gridSize - 2) * (gridSize - 1))));
return numFinalEdges;
}
int ccgSubSurf_getNumFinalFaces(const CCGSubSurf *ss)
{
int gridSize = ccg_gridsize(ss->subdivLevels);
int numFinalFaces = ss->numGrids * ((gridSize - 1) * (gridSize - 1));
return numFinalFaces;
}
/***/
void CCG_key(CCGKey *key, const CCGSubSurf *ss, int level)
{
key->level = level;
key->elem_size = ss->meshIFC.vertDataSize;
key->has_normals = ss->calcVertNormals;
/* if normals are present, always the last three floats of an
* element */
if (key->has_normals) {
key->normal_offset = key->elem_size - sizeof(float[3]);
}
else {
key->normal_offset = -1;
}
key->grid_size = ccgSubSurf_getGridLevelSize(ss, level);
key->grid_area = key->grid_size * key->grid_size;
key->grid_bytes = key->elem_size * key->grid_area;
key->has_mask = ss->allocMask;
if (key->has_mask) {
key->mask_offset = ss->maskDataOffset;
}
else {
key->mask_offset = -1;
}
}
void CCG_key_top_level(CCGKey *key, const CCGSubSurf *ss)
{
CCG_key(key, ss, ccgSubSurf_getSubdivisionLevels(ss));
}

View File

@@ -1,219 +0,0 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
/** \file
* \ingroup bke
*/
using CCGMeshHDL = void *;
using CCGVertHDL = void *;
using CCGEdgeHDL = void *;
using CCGFaceHDL = void *;
struct CCGEdge;
struct CCGFace;
struct CCGSubSurf;
struct CCGVert;
struct CCGMeshIFC {
int vertUserSize, edgeUserSize, faceUserSize;
int numLayers;
int vertDataSize;
int simpleSubdiv;
};
/***/
using CCGAllocatorHDL = void *;
struct CCGAllocatorIFC {
void *(*alloc)(CCGAllocatorHDL a, int numBytes);
void *(*realloc)(CCGAllocatorHDL a, void *ptr, int newSize, int oldSize);
void (*free)(CCGAllocatorHDL a, void *ptr);
void (*release)(CCGAllocatorHDL a);
};
/* private, so we can allocate on the stack */
struct EHashIterator {
struct EHash *eh;
int curBucket;
struct EHEntry *curEntry;
};
/***/
enum CCGError {
eCCGError_None = 0,
eCCGError_InvalidSyncState,
eCCGError_InvalidValue,
};
/***/
/* TODO(sergey): This actually depends on subsurf level as well. */
#define CCG_TASK_LIMIT 16
/***/
CCGSubSurf *ccgSubSurf_new(CCGMeshIFC *ifc,
int subdivLevels,
CCGAllocatorIFC *allocatorIFC,
CCGAllocatorHDL allocator);
void ccgSubSurf_free(CCGSubSurf *ss);
CCGError ccgSubSurf_initFullSync(CCGSubSurf *ss);
CCGError ccgSubSurf_initPartialSync(CCGSubSurf *ss);
#ifdef WITH_OPENSUBDIV
CCGError ccgSubSurf_initOpenSubdivSync(CCGSubSurf *ss);
#endif
CCGError ccgSubSurf_syncVert(
CCGSubSurf *ss, CCGVertHDL vHDL, const void *vertData, int seam, CCGVert **v_r);
CCGError ccgSubSurf_syncEdge(CCGSubSurf *ss,
CCGEdgeHDL eHDL,
CCGVertHDL e_vHDL0,
CCGVertHDL e_vHDL1,
float crease,
CCGEdge **e_r);
CCGError ccgSubSurf_syncFace(
CCGSubSurf *ss, CCGFaceHDL fHDL, int numVerts, CCGVertHDL *vHDLs, CCGFace **f_r);
CCGError ccgSubSurf_syncVertDel(CCGSubSurf *ss, CCGVertHDL vHDL);
CCGError ccgSubSurf_syncEdgeDel(CCGSubSurf *ss, CCGEdgeHDL eHDL);
CCGError ccgSubSurf_syncFaceDel(CCGSubSurf *ss, CCGFaceHDL fHDL);
CCGError ccgSubSurf_processSync(CCGSubSurf *ss);
/**
* Copy face grid coordinates to other places.
*/
CCGError ccgSubSurf_updateFromFaces(CCGSubSurf *ss,
int lvl,
CCGFace **effectedF,
int numEffectedF);
/**
* Copy other places to face grid coordinates.
*/
CCGError ccgSubSurf_updateToFaces(CCGSubSurf *ss, int lvl, CCGFace **effectedF, int numEffectedF);
/**
* Update normals for specified faces.
*/
CCGError ccgSubSurf_updateNormals(CCGSubSurf *ss, CCGFace **effectedF, int numEffectedF);
/**
* Compute subdivision levels from a given starting point, used by multi-res subdivide/propagate,
* by filling in coordinates at a certain level, and then subdividing that up to the highest level.
*/
CCGError ccgSubSurf_updateLevels(CCGSubSurf *ss, int lvl, CCGFace **effectedF, int numEffectedF);
/**
* Stitch together face grids, averaging coordinates at edges and vertices, for multi-res
* displacements.
*/
CCGError ccgSubSurf_stitchFaces(CCGSubSurf *ss, int lvl, CCGFace **effectedF, int numEffectedF);
CCGError ccgSubSurf_setSubdivisionLevels(CCGSubSurf *ss, int subdivisionLevels);
CCGError ccgSubSurf_setAllowEdgeCreation(CCGSubSurf *ss,
int allowEdgeCreation,
float defaultCreaseValue,
void *defaultUserData);
void ccgSubSurf_getAllowEdgeCreation(CCGSubSurf *ss,
int *allowEdgeCreation_r,
float *defaultCreaseValue_r,
void *defaultUserData_r);
void ccgSubSurf_getUseAgeCounts(CCGSubSurf *ss,
int *useAgeCounts_r,
int *vertUserOffset_r,
int *edgeUserOffset_r,
int *faceUserOffset_r);
CCGError ccgSubSurf_setUseAgeCounts(
CCGSubSurf *ss, int useAgeCounts, int vertUserOffset, int edgeUserOffset, int faceUserOffset);
CCGError ccgSubSurf_setCalcVertexNormals(CCGSubSurf *ss, int useVertNormals, int normalDataOffset);
void ccgSubSurf_setAllocMask(CCGSubSurf *ss, int allocMask, int maskOffset);
void ccgSubSurf_setNumLayers(CCGSubSurf *ss, int numLayers);
/***/
int ccgSubSurf_getNumVerts(const CCGSubSurf *ss);
int ccgSubSurf_getNumEdges(const CCGSubSurf *ss);
int ccgSubSurf_getNumFaces(const CCGSubSurf *ss);
int ccgSubSurf_getSubdivisionLevels(const CCGSubSurf *ss);
int ccgSubSurf_getEdgeSize(const CCGSubSurf *ss);
int ccgSubSurf_getEdgeLevelSize(const CCGSubSurf *ss, int level);
int ccgSubSurf_getGridSize(const CCGSubSurf *ss);
int ccgSubSurf_getGridLevelSize(const CCGSubSurf *ss, int level);
int ccgSubSurf_getSimpleSubdiv(const CCGSubSurf *ss);
CCGVert *ccgSubSurf_getVert(CCGSubSurf *ss, CCGVertHDL v);
CCGVertHDL ccgSubSurf_getVertVertHandle(CCGVert *v);
int ccgSubSurf_getVertNumFaces(CCGVert *v);
CCGFace *ccgSubSurf_getVertFace(CCGVert *v, int index);
int ccgSubSurf_getVertNumEdges(CCGVert *v);
CCGEdge *ccgSubSurf_getVertEdge(CCGVert *v, int index);
int ccgSubSurf_getVertAge(CCGSubSurf *ss, CCGVert *v);
void *ccgSubSurf_getVertUserData(CCGSubSurf *ss, CCGVert *v);
void *ccgSubSurf_getVertData(CCGSubSurf *ss, CCGVert *v);
void *ccgSubSurf_getVertLevelData(CCGSubSurf *ss, CCGVert *v, int level);
CCGEdge *ccgSubSurf_getEdge(CCGSubSurf *ss, CCGEdgeHDL e);
CCGEdgeHDL ccgSubSurf_getEdgeEdgeHandle(CCGEdge *e);
int ccgSubSurf_getEdgeNumFaces(CCGEdge *e);
CCGFace *ccgSubSurf_getEdgeFace(CCGEdge *e, int index);
CCGVert *ccgSubSurf_getEdgeVert0(CCGEdge *e);
CCGVert *ccgSubSurf_getEdgeVert1(CCGEdge *e);
float ccgSubSurf_getEdgeCrease(CCGEdge *e);
int ccgSubSurf_getEdgeAge(CCGSubSurf *ss, CCGEdge *e);
void *ccgSubSurf_getEdgeUserData(CCGSubSurf *ss, CCGEdge *e);
void *ccgSubSurf_getEdgeDataArray(CCGSubSurf *ss, CCGEdge *e);
void *ccgSubSurf_getEdgeData(CCGSubSurf *ss, CCGEdge *e, int x);
void *ccgSubSurf_getEdgeLevelData(CCGSubSurf *ss, CCGEdge *e, int x, int level);
CCGFace *ccgSubSurf_getFace(CCGSubSurf *ss, CCGFaceHDL f);
CCGFaceHDL ccgSubSurf_getFaceFaceHandle(CCGFace *f);
int ccgSubSurf_getFaceNumVerts(CCGFace *f);
CCGVert *ccgSubSurf_getFaceVert(CCGFace *f, int index);
CCGEdge *ccgSubSurf_getFaceEdge(CCGFace *f, int index);
int ccgSubSurf_getFaceEdgeIndex(CCGFace *f, CCGEdge *e);
int ccgSubSurf_getFaceAge(CCGSubSurf *ss, CCGFace *f);
void *ccgSubSurf_getFaceUserData(CCGSubSurf *ss, CCGFace *f);
void *ccgSubSurf_getFaceCenterData(CCGFace *f);
void *ccgSubSurf_getFaceGridEdgeDataArray(CCGSubSurf *ss, CCGFace *f, int gridIndex);
void *ccgSubSurf_getFaceGridEdgeData(CCGSubSurf *ss, CCGFace *f, int gridIndex, int x);
void *ccgSubSurf_getFaceGridDataArray(CCGSubSurf *ss, CCGFace *f, int gridIndex);
void *ccgSubSurf_getFaceGridData(CCGSubSurf *ss, CCGFace *f, int gridIndex, int x, int y);
int ccgSubSurf_getNumFinalVerts(const CCGSubSurf *ss);
int ccgSubSurf_getNumFinalEdges(const CCGSubSurf *ss);
int ccgSubSurf_getNumFinalFaces(const CCGSubSurf *ss);
/***/
using CCGEdgeIterator = EHashIterator;
using CCGFaceIterator = EHashIterator;
using CCGVertIterator = EHashIterator;
void ccgSubSurf_initVertIterator(CCGSubSurf *ss, CCGVertIterator *viter);
void ccgSubSurf_initEdgeIterator(CCGSubSurf *ss, CCGEdgeIterator *eiter);
void ccgSubSurf_initFaceIterator(CCGSubSurf *ss, CCGFaceIterator *fiter);
CCGVert *ccgVertIterator_getCurrent(CCGVertIterator *vi);
int ccgVertIterator_isStopped(CCGVertIterator *vi);
void ccgVertIterator_next(CCGVertIterator *vi);
CCGEdge *ccgEdgeIterator_getCurrent(CCGEdgeIterator *ei);
int ccgEdgeIterator_isStopped(CCGEdgeIterator *ei);
void ccgEdgeIterator_next(CCGEdgeIterator *ei);
CCGFace *ccgFaceIterator_getCurrent(CCGFaceIterator *fi);
int ccgFaceIterator_isStopped(CCGFaceIterator *fi);
void ccgFaceIterator_next(CCGFaceIterator *fi);

View File

@@ -1,264 +0,0 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup bke
*/
#pragma once
#include "BLI_assert.h"
#include "BLI_compiler_compat.h"
#include "CCGSubSurf_intern.h"
#include <cmath>
#include <cstdint>
#include <cstring>
BLI_INLINE int ccg_gridsize(int level)
{
BLI_assert(level > 0);
BLI_assert(level <= CCGSUBSURF_LEVEL_MAX + 1);
return (1 << (level - 1)) + 1;
}
BLI_INLINE int ccg_edgesize(int level)
{
BLI_assert(level > 0);
BLI_assert(level <= CCGSUBSURF_LEVEL_MAX + 1);
return 1 + (1 << level);
}
BLI_INLINE int ccg_spacing(int high_level, int low_level)
{
BLI_assert(high_level > 0 && low_level > 0);
BLI_assert(high_level >= low_level);
BLI_assert((high_level - low_level) <= CCGSUBSURF_LEVEL_MAX);
return 1 << (high_level - low_level);
}
BLI_INLINE int ccg_edgebase(int level)
{
BLI_assert(level > 0);
BLI_assert(level <= CCGSUBSURF_LEVEL_MAX + 1);
return level + (1 << level) - 1;
}
/* **** */
BLI_INLINE uint8_t *VERT_getLevelData(CCGVert *v)
{
return (uint8_t *)(&(v)[1]);
}
BLI_INLINE uint8_t *EDGE_getLevelData(CCGEdge *e)
{
return (uint8_t *)(&(e)[1]);
}
BLI_INLINE CCGVert **FACE_getVerts(CCGFace *f)
{
return (CCGVert **)(&f[1]);
}
BLI_INLINE CCGEdge **FACE_getEdges(CCGFace *f)
{
return (CCGEdge **)(&(FACE_getVerts(f)[f->numVerts]));
}
BLI_INLINE uint8_t *FACE_getCenterData(CCGFace *f)
{
return (uint8_t *)(&(FACE_getEdges(f)[(f)->numVerts]));
}
/* **** */
BLI_INLINE void *ccg_vert_getCo(CCGVert *v, int lvl, int dataSize)
{
return &VERT_getLevelData(v)[lvl * dataSize];
}
BLI_INLINE float *ccg_vert_getNo(CCGVert *v, int lvl, int dataSize, int normalDataOffset)
{
return (float *)&VERT_getLevelData(v)[lvl * dataSize + normalDataOffset];
}
BLI_INLINE void *ccg_edge_getCo(CCGEdge *e, int lvl, int x, int dataSize)
{
int levelBase = ccg_edgebase(lvl);
return &EDGE_getLevelData(e)[dataSize * (levelBase + x)];
}
BLI_INLINE float *ccg_edge_getNo(CCGEdge *e, int lvl, int x, int dataSize, int normalDataOffset)
{
int levelBase = ccg_edgebase(lvl);
return (float *)&EDGE_getLevelData(e)[dataSize * (levelBase + x) + normalDataOffset];
}
BLI_INLINE void *ccg_face_getIECo(CCGFace *f, int lvl, int S, int x, int levels, int dataSize)
{
int maxGridSize = ccg_gridsize(levels);
int spacing = ccg_spacing(levels, lvl);
uint8_t *gridBase = FACE_getCenterData(f) +
dataSize * (1 + S * (maxGridSize + maxGridSize * maxGridSize));
return &gridBase[dataSize * x * spacing];
}
BLI_INLINE void *ccg_face_getIENo(
CCGFace *f, int lvl, int S, int x, int levels, int dataSize, int normalDataOffset)
{
int maxGridSize = ccg_gridsize(levels);
int spacing = ccg_spacing(levels, lvl);
uint8_t *gridBase = FACE_getCenterData(f) +
dataSize * (1 + S * (maxGridSize + maxGridSize * maxGridSize));
return &gridBase[dataSize * x * spacing + normalDataOffset];
}
BLI_INLINE void *ccg_face_getIFCo(
CCGFace *f, int lvl, int S, int x, int y, int levels, int dataSize)
{
int maxGridSize = ccg_gridsize(levels);
int spacing = ccg_spacing(levels, lvl);
uint8_t *gridBase = FACE_getCenterData(f) +
dataSize * (1 + S * (maxGridSize + maxGridSize * maxGridSize));
return &gridBase[dataSize * (maxGridSize + (y * maxGridSize + x) * spacing)];
}
BLI_INLINE float *ccg_face_getIFNo(
CCGFace *f, int lvl, int S, int x, int y, int levels, int dataSize, int normalDataOffset)
{
int maxGridSize = ccg_gridsize(levels);
int spacing = ccg_spacing(levels, lvl);
uint8_t *gridBase = FACE_getCenterData(f) +
dataSize * (1 + S * (maxGridSize + maxGridSize * maxGridSize));
return (float *)&gridBase[dataSize * (maxGridSize + (y * maxGridSize + x) * spacing) +
normalDataOffset];
}
BLI_INLINE int ccg_face_getVertIndex(CCGFace *f, CCGVert *v)
{
for (int i = 0; i < f->numVerts; i++) {
if (FACE_getVerts(f)[i] == v) {
return i;
}
}
return -1;
}
BLI_INLINE int ccg_face_getEdgeIndex(CCGFace *f, CCGEdge *e)
{
for (int i = 0; i < f->numVerts; i++) {
if (FACE_getEdges(f)[i] == e) {
return i;
}
}
return -1;
}
BLI_INLINE void *ccg_face_getIFCoEdge(
CCGFace *f, CCGEdge *e, int f_ed_idx, int lvl, int eX, int eY, int levels, int dataSize)
{
int maxGridSize = ccg_gridsize(levels);
int spacing = ccg_spacing(levels, lvl);
int x, y, cx, cy;
BLI_assert(f_ed_idx == ccg_face_getEdgeIndex(f, e));
eX = eX * spacing;
eY = eY * spacing;
if (e->v0 != FACE_getVerts(f)[f_ed_idx]) {
eX = (maxGridSize * 2 - 1) - 1 - eX;
}
y = maxGridSize - 1 - eX;
x = maxGridSize - 1 - eY;
if (x < 0) {
f_ed_idx = (f_ed_idx + f->numVerts - 1) % f->numVerts;
cx = y;
cy = -x;
}
else if (y < 0) {
f_ed_idx = (f_ed_idx + 1) % f->numVerts;
cx = -y;
cy = x;
}
else {
cx = x;
cy = y;
}
return ccg_face_getIFCo(f, levels, f_ed_idx, cx, cy, levels, dataSize);
}
BLI_INLINE void Normalize(float no[3])
{
const float length = sqrtf(no[0] * no[0] + no[1] * no[1] + no[2] * no[2]);
if (length > EPSILON) {
const float length_inv = 1.0f / length;
no[0] *= length_inv;
no[1] *= length_inv;
no[2] *= length_inv;
}
else {
NormZero(no);
}
}
/* Data layers mathematics. */
BLI_INLINE bool VertDataEqual(const float a[], const float b[], const CCGSubSurf *ss)
{
for (int i = 0; i < ss->meshIFC.numLayers; i++) {
if (a[i] != b[i]) {
return false;
}
}
return true;
}
BLI_INLINE void VertDataZero(float v[], const CCGSubSurf *ss)
{
memset(v, 0, sizeof(float) * ss->meshIFC.numLayers);
}
BLI_INLINE void VertDataCopy(float dst[], const float src[], const CCGSubSurf *ss)
{
for (int i = 0; i < ss->meshIFC.numLayers; i++) {
dst[i] = src[i];
}
}
BLI_INLINE void VertDataAdd(float a[], const float b[], const CCGSubSurf *ss)
{
for (int i = 0; i < ss->meshIFC.numLayers; i++) {
a[i] += b[i];
}
}
BLI_INLINE void VertDataSub(float a[], const float b[], const CCGSubSurf *ss)
{
for (int i = 0; i < ss->meshIFC.numLayers; i++) {
a[i] -= b[i];
}
}
BLI_INLINE void VertDataMulN(float v[], float f, const CCGSubSurf *ss)
{
for (int i = 0; i < ss->meshIFC.numLayers; i++) {
v[i] *= f;
}
}
BLI_INLINE void VertDataAvg4(float v[],
const float a[],
const float b[],
const float c[],
const float d[],
const CCGSubSurf *ss)
{
for (int i = 0; i < ss->meshIFC.numLayers; i++) {
v[i] = (a[i] + b[i] + c[i] + d[i]) * 0.25f;
}
}

View File

@@ -1,258 +0,0 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup bke
*/
#pragma once
#include "CCGSubSurf.h"
/**
* Definitions which defines internal behavior of CCGSubSurf.
*/
/* Define this to see dump of the grids after the subsurf applied. */
#undef DUMP_RESULT_GRIDS
/* used for normalize_v3 in BLI_math_vector
* float.h's FLT_EPSILON causes trouble with subsurf normals - campbell */
#define EPSILON (1.0e-35f)
/* With this limit a single triangle becomes over 3 million faces */
#define CCGSUBSURF_LEVEL_MAX 11
/**
* Common type definitions.
*/
/**
* Hash implementation.
*/
struct EHEntry {
struct EHEntry *next;
void *key;
};
struct EHash {
EHEntry **buckets;
int numEntries, curSize, curSizeIdx;
CCGAllocatorIFC allocatorIFC;
CCGAllocatorHDL allocator;
};
using EHEntryFreeFP = void (*)(EHEntry *, void *);
#define EHASH_alloc(eh, nb) (EHEntry **)((eh)->allocatorIFC.alloc((eh)->allocator, nb))
#define EHASH_free(eh, ptr) ((eh)->allocatorIFC.free((eh)->allocator, ptr))
#define EHASH_hash(eh, item) (((uintptr_t)(item)) % ((unsigned int)(eh)->curSize))
/* Generic hash functions. */
EHash *ccg_ehash_new(int estimatedNumEntries,
CCGAllocatorIFC *allocatorIFC,
CCGAllocatorHDL allocator);
void ccg_ehash_free(EHash *eh, EHEntryFreeFP freeEntry, void *user_data);
void ccg_ehash_insert(EHash *eh, EHEntry *entry);
void *ccg_ehash_lookupWithPrev(EHash *eh, void *key, void ***prevp_r);
void *ccg_ehash_lookup(EHash *eh, void *key);
/* Hash elements iteration. */
void ccg_ehashIterator_init(EHash *eh, EHashIterator *ehi);
void *ccg_ehashIterator_getCurrent(EHashIterator *ehi);
void ccg_ehashIterator_next(EHashIterator *ehi);
int ccg_ehashIterator_isStopped(EHashIterator *ehi);
/**
* Standard allocator implementation.
*/
CCGAllocatorIFC *ccg_getStandardAllocatorIFC(void);
/**
* Catmull-Clark Gridding Subdivision Surface.
*/
/* ** Data structures, constants. enums ** */
enum {
Vert_eEffected = (1 << 0),
Vert_eChanged = (1 << 1),
Vert_eSeam = (1 << 2),
} /*VertFlags*/;
enum {
Edge_eEffected = (1 << 0),
} /*CCGEdgeFlags*/;
enum {
Face_eEffected = (1 << 0),
} /*FaceFlags*/;
struct CCGVert {
CCGVert *next; /* EHData.next */
CCGVertHDL vHDL; /* EHData.key */
short numEdges, numFaces, flags;
int osd_index; /* Index of the vertex in the map, used by OSD. */
CCGEdge **edges;
CCGFace **faces;
// uint8_t *levelData;
// uint8_t *user_data;
};
struct CCGEdge {
CCGEdge *next; /* EHData.next */
CCGEdgeHDL eHDL; /* EHData.key */
short numFaces, flags;
float crease;
CCGVert *v0, *v1;
CCGFace **faces;
// uint8_t *levelData;
// uint8_t *user_data;
};
struct CCGFace {
CCGFace *next; /* EHData.next */
CCGFaceHDL fHDL; /* EHData.key */
short numVerts, flags;
int osd_index;
// CCGVert **verts;
// CCGEdge **edges;
// uint8_t *centerData;
// uint8_t **gridData;
// uint8_t *user_data;
};
enum SyncState {
eSyncState_None = 0,
eSyncState_Vert,
eSyncState_Edge,
eSyncState_Face,
eSyncState_Partial,
};
struct CCGSubSurf {
EHash *vMap; /* map of CCGVertHDL -> Vert */
EHash *eMap; /* map of CCGEdgeHDL -> Edge */
EHash *fMap; /* map of CCGFaceHDL -> Face */
CCGMeshIFC meshIFC;
CCGAllocatorIFC allocatorIFC;
CCGAllocatorHDL allocator;
int subdivLevels;
int numGrids;
int allowEdgeCreation;
float defaultCreaseValue;
void *defaultEdgeUserData;
void *q, *r;
/* Data for calc vert normals. */
int calcVertNormals;
int normalDataOffset;
/* Data for paint masks. */
int allocMask;
int maskDataOffset;
/* Data for age'ing (to debug sync). */
int currentAge;
int useAgeCounts;
int vertUserAgeOffset;
int edgeUserAgeOffset;
int faceUserAgeOffset;
/* Data used during syncing. */
SyncState syncState;
EHash *oldVMap, *oldEMap, *oldFMap;
int lenTempArrays;
CCGVert **tempVerts;
CCGEdge **tempEdges;
};
/* ** Utility macros ** */
#define CCGSUBSURF_alloc(ss, nb) ((ss)->allocatorIFC.alloc((ss)->allocator, nb))
#define CCGSUBSURF_realloc(ss, ptr, nb, ob) \
((ss)->allocatorIFC.realloc((ss)->allocator, ptr, nb, ob))
#define CCGSUBSURF_free(ss, ptr) ((ss)->allocatorIFC.free((ss)->allocator, ptr))
#define VERT_getCo(v, lvl) (float *)ccg_vert_getCo(v, lvl, vertDataSize)
#define VERT_getNo(v, lvl) ccg_vert_getNo(v, lvl, vertDataSize, normalDataOffset)
#define EDGE_getCo(e, lvl, x) (float *)ccg_edge_getCo(e, lvl, x, vertDataSize)
#define EDGE_getNo(e, lvl, x) ccg_edge_getNo(e, lvl, x, vertDataSize, normalDataOffset)
#define FACE_getIFNo(f, lvl, S, x, y) \
ccg_face_getIFNo(f, lvl, S, x, y, subdivLevels, vertDataSize, normalDataOffset)
#if 0
# define FACE_calcIFNo(f, lvl, S, x, y, no) \
_face_calcIFNo(f, lvl, S, x, y, no, subdivLevels, vertDataSize)
#endif
#define FACE_getIENo(f, lvl, S, x) \
ccg_face_getIENo(f, lvl, S, x, subdivLevels, vertDataSize, normalDataOffset)
#define FACE_getIECo(f, lvl, S, x) \
(float *)ccg_face_getIECo(f, lvl, S, x, subdivLevels, vertDataSize)
#define FACE_getIFCo(f, lvl, S, x, y) \
(float *)ccg_face_getIFCo(f, lvl, S, x, y, subdivLevels, vertDataSize)
#define NormZero(av) \
{ \
float *_a = (float *)av; \
_a[0] = _a[1] = _a[2] = 0.0f; \
} \
(void)0
#define NormCopy(av, bv) \
{ \
float *_a = (float *)av; \
const float *_b = (const float *)bv; \
_a[0] = _b[0]; \
_a[1] = _b[1]; \
_a[2] = _b[2]; \
} \
(void)0
#define NormAdd(av, bv) \
{ \
float *_a = (float *)av; \
const float *_b = (const float *)bv; \
_a[0] += _b[0]; \
_a[1] += _b[1]; \
_a[2] += _b[2]; \
} \
(void)0
/* ** General purpose functions ** */
/* `CCGSubSurf.cc` */
void ccgSubSurf__allFaces(CCGSubSurf *ss, CCGFace ***faces, int *numFaces, int *freeFaces);
void ccgSubSurf__effectedFaceNeighbors(CCGSubSurf *ss,
CCGFace **faces,
int numFaces,
CCGVert ***verts,
int *numVerts,
CCGEdge ***edges,
int *numEdges);
/* `CCGSubSurf_legacy.cc` */
void ccgSubSurf__sync_legacy(CCGSubSurf *ss);
/* `CCGSubSurf_util.cc` */
#ifdef DUMP_RESULT_GRIDS
void ccgSubSurf__dumpCoords(CCGSubSurf *ss);
#endif

View File

@@ -1,1371 +0,0 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup bke
*/
#include <algorithm>
#include "MEM_guardedalloc.h"
#include "BLI_task.h"
#include "BLI_utildefines.h" /* for BLI_assert */
#include "CCGSubSurf.h"
#include "CCGSubSurf_inline.h"
#include "CCGSubSurf_intern.h"
#define FACE_calcIFNo(f, lvl, S, x, y, no) \
_face_calcIFNo(f, lvl, S, x, y, no, subdivLevels, vertDataSize)
/* TODO(sergey): Deduplicate the following functions/ */
static void *_edge_getCoVert(CCGEdge *e, CCGVert *v, int lvl, int x, int dataSize)
{
int levelBase = ccg_edgebase(lvl);
if (v == e->v0) {
return &EDGE_getLevelData(e)[dataSize * (levelBase + x)];
}
return &EDGE_getLevelData(e)[dataSize * (levelBase + (1 << lvl) - x)];
}
/* *************************************************** */
static int _edge_isBoundary(const CCGEdge *e)
{
return e->numFaces < 2;
}
static bool _vert_isBoundary(const CCGVert *v)
{
for (int i = 0; i < v->numEdges; i++) {
if (_edge_isBoundary(v->edges[i])) {
return true;
}
}
return false;
}
static CCGVert *_edge_getOtherVert(CCGEdge *e, CCGVert *vQ)
{
if (vQ == e->v0) {
return e->v1;
}
return e->v0;
}
static float *_face_getIFNoEdge(CCGFace *f,
CCGEdge *e,
int f_ed_idx,
int lvl,
int eX,
int eY,
int levels,
int dataSize,
int normalDataOffset)
{
return (float *)((uint8_t *)ccg_face_getIFCoEdge(f, e, f_ed_idx, lvl, eX, eY, levels, dataSize) +
normalDataOffset);
}
static void _face_calcIFNo(
CCGFace *f, int lvl, int S, int x, int y, float no[3], int levels, int dataSize)
{
const float *a = static_cast<float *>(
ccg_face_getIFCo(f, lvl, S, x + 0, y + 0, levels, dataSize));
const float *b = static_cast<float *>(
ccg_face_getIFCo(f, lvl, S, x + 1, y + 0, levels, dataSize));
const float *c = static_cast<float *>(
ccg_face_getIFCo(f, lvl, S, x + 1, y + 1, levels, dataSize));
const float *d = static_cast<float *>(
ccg_face_getIFCo(f, lvl, S, x + 0, y + 1, levels, dataSize));
const float a_cX = c[0] - a[0], a_cY = c[1] - a[1], a_cZ = c[2] - a[2];
const float b_dX = d[0] - b[0], b_dY = d[1] - b[1], b_dZ = d[2] - b[2];
no[0] = b_dY * a_cZ - b_dZ * a_cY;
no[1] = b_dZ * a_cX - b_dX * a_cZ;
no[2] = b_dX * a_cY - b_dY * a_cX;
Normalize(no);
}
static int VERT_seam(const CCGVert *v)
{
return ((v->flags & Vert_eSeam) != 0);
}
static float EDGE_getSharpness(CCGEdge *e, int lvl)
{
if (!lvl) {
return e->crease;
}
if (!e->crease) {
return 0.0f;
}
if (e->crease - lvl < 0.0f) {
return 0.0f;
}
return e->crease - lvl;
}
struct CCGSubSurfCalcSubdivData {
CCGSubSurf *ss;
CCGVert **effectedV;
CCGEdge **effectedE;
CCGFace **effectedF;
int numEffectedV;
int numEffectedE;
int numEffectedF;
int curLvl;
};
static void ccgSubSurf__calcVertNormals_faces_accumulate_cb(
void *__restrict userdata, const int ptrIdx, const TaskParallelTLS *__restrict /*tls*/)
{
CCGSubSurfCalcSubdivData *data = static_cast<CCGSubSurfCalcSubdivData *>(userdata);
CCGSubSurf *ss = data->ss;
CCGFace *f = data->effectedF[ptrIdx];
const int subdivLevels = ss->subdivLevels;
const int lvl = ss->subdivLevels;
const int gridSize = ccg_gridsize(lvl);
const int normalDataOffset = ss->normalDataOffset;
const int vertDataSize = ss->meshIFC.vertDataSize;
int S, x, y;
float no[3];
for (S = 0; S < f->numVerts; S++) {
for (y = 0; y < gridSize - 1; y++) {
for (x = 0; x < gridSize - 1; x++) {
NormZero(FACE_getIFNo(f, lvl, S, x, y));
}
}
if (FACE_getEdges(f)[(S - 1 + f->numVerts) % f->numVerts]->flags & Edge_eEffected) {
for (x = 0; x < gridSize - 1; x++) {
NormZero(FACE_getIFNo(f, lvl, S, x, gridSize - 1));
}
}
if (FACE_getEdges(f)[S]->flags & Edge_eEffected) {
for (y = 0; y < gridSize - 1; y++) {
NormZero(FACE_getIFNo(f, lvl, S, gridSize - 1, y));
}
}
if (FACE_getVerts(f)[S]->flags & Vert_eEffected) {
NormZero(FACE_getIFNo(f, lvl, S, gridSize - 1, gridSize - 1));
}
}
for (S = 0; S < f->numVerts; S++) {
int yLimit = !(FACE_getEdges(f)[(S - 1 + f->numVerts) % f->numVerts]->flags & Edge_eEffected);
int xLimit = !(FACE_getEdges(f)[S]->flags & Edge_eEffected);
int yLimitNext = xLimit;
int xLimitPrev = yLimit;
for (y = 0; y < gridSize - 1; y++) {
for (x = 0; x < gridSize - 1; x++) {
int xPlusOk = (!xLimit || x < gridSize - 2);
int yPlusOk = (!yLimit || y < gridSize - 2);
FACE_calcIFNo(f, lvl, S, x, y, no);
NormAdd(FACE_getIFNo(f, lvl, S, x + 0, y + 0), no);
if (xPlusOk) {
NormAdd(FACE_getIFNo(f, lvl, S, x + 1, y + 0), no);
}
if (yPlusOk) {
NormAdd(FACE_getIFNo(f, lvl, S, x + 0, y + 1), no);
}
if (xPlusOk && yPlusOk) {
if (x < gridSize - 2 || y < gridSize - 2 || FACE_getVerts(f)[S]->flags & Vert_eEffected)
{
NormAdd(FACE_getIFNo(f, lvl, S, x + 1, y + 1), no);
}
}
if (x == 0 && y == 0) {
int K;
if (!yLimitNext || 1 < gridSize - 1) {
NormAdd(FACE_getIFNo(f, lvl, (S + 1) % f->numVerts, 0, 1), no);
}
if (!xLimitPrev || 1 < gridSize - 1) {
NormAdd(FACE_getIFNo(f, lvl, (S - 1 + f->numVerts) % f->numVerts, 1, 0), no);
}
for (K = 0; K < f->numVerts; K++) {
if (K != S) {
NormAdd(FACE_getIFNo(f, lvl, K, 0, 0), no);
}
}
}
else if (y == 0) {
NormAdd(FACE_getIFNo(f, lvl, (S + 1) % f->numVerts, 0, x), no);
if (!yLimitNext || x < gridSize - 2) {
NormAdd(FACE_getIFNo(f, lvl, (S + 1) % f->numVerts, 0, x + 1), no);
}
}
else if (x == 0) {
NormAdd(FACE_getIFNo(f, lvl, (S - 1 + f->numVerts) % f->numVerts, y, 0), no);
if (!xLimitPrev || y < gridSize - 2) {
NormAdd(FACE_getIFNo(f, lvl, (S - 1 + f->numVerts) % f->numVerts, y + 1, 0), no);
}
}
}
}
}
}
static void ccgSubSurf__calcVertNormals_faces_finalize_cb(
void *__restrict userdata, const int ptrIdx, const TaskParallelTLS *__restrict /*tls*/)
{
CCGSubSurfCalcSubdivData *data = static_cast<CCGSubSurfCalcSubdivData *>(userdata);
CCGSubSurf *ss = data->ss;
CCGFace *f = data->effectedF[ptrIdx];
const int subdivLevels = ss->subdivLevels;
const int lvl = ss->subdivLevels;
const int gridSize = ccg_gridsize(lvl);
const int normalDataOffset = ss->normalDataOffset;
const int vertDataSize = ss->meshIFC.vertDataSize;
int S, x, y;
for (S = 0; S < f->numVerts; S++) {
NormCopy(FACE_getIFNo(f, lvl, (S + 1) % f->numVerts, 0, gridSize - 1),
FACE_getIFNo(f, lvl, S, gridSize - 1, 0));
}
for (S = 0; S < f->numVerts; S++) {
for (y = 0; y < gridSize; y++) {
for (x = 0; x < gridSize; x++) {
float *no = FACE_getIFNo(f, lvl, S, x, y);
Normalize(no);
}
}
VertDataCopy(
(float *)(FACE_getCenterData(f) + normalDataOffset), FACE_getIFNo(f, lvl, S, 0, 0), ss);
for (x = 1; x < gridSize - 1; x++) {
NormCopy(FACE_getIENo(f, lvl, S, x), FACE_getIFNo(f, lvl, S, x, 0));
}
}
}
static void ccgSubSurf__calcVertNormals_edges_accumulate_cb(
void *__restrict userdata, const int ptrIdx, const TaskParallelTLS *__restrict /*tls*/)
{
CCGSubSurfCalcSubdivData *data = static_cast<CCGSubSurfCalcSubdivData *>(userdata);
CCGSubSurf *ss = data->ss;
CCGEdge *e = data->effectedE[ptrIdx];
const int subdivLevels = ss->subdivLevels;
const int lvl = ss->subdivLevels;
const int edgeSize = ccg_edgesize(lvl);
const int normalDataOffset = ss->normalDataOffset;
const int vertDataSize = ss->meshIFC.vertDataSize;
if (e->numFaces) {
CCGFace *fLast = e->faces[e->numFaces - 1];
int x, i;
for (i = 0; i < e->numFaces - 1; i++) {
CCGFace *f = e->faces[i];
const int f_ed_idx = ccg_face_getEdgeIndex(f, e);
const int f_ed_idx_last = ccg_face_getEdgeIndex(fLast, e);
for (x = 1; x < edgeSize - 1; x++) {
NormAdd(
_face_getIFNoEdge(
fLast, e, f_ed_idx_last, lvl, x, 0, subdivLevels, vertDataSize, normalDataOffset),
_face_getIFNoEdge(
f, e, f_ed_idx, lvl, x, 0, subdivLevels, vertDataSize, normalDataOffset));
}
}
for (i = 0; i < e->numFaces - 1; i++) {
CCGFace *f = e->faces[i];
const int f_ed_idx = ccg_face_getEdgeIndex(f, e);
const int f_ed_idx_last = ccg_face_getEdgeIndex(fLast, e);
for (x = 1; x < edgeSize - 1; x++) {
NormCopy(
_face_getIFNoEdge(
f, e, f_ed_idx, lvl, x, 0, subdivLevels, vertDataSize, normalDataOffset),
_face_getIFNoEdge(
fLast, e, f_ed_idx_last, lvl, x, 0, subdivLevels, vertDataSize, normalDataOffset));
}
}
}
}
static void ccgSubSurf__calcVertNormals(CCGSubSurf *ss,
CCGVert **effectedV,
CCGEdge **effectedE,
CCGFace **effectedF,
int numEffectedV,
int numEffectedE,
int numEffectedF)
{
int i, ptrIdx;
const int subdivLevels = ss->subdivLevels;
const int lvl = ss->subdivLevels;
const int edgeSize = ccg_edgesize(lvl);
const int gridSize = ccg_gridsize(lvl);
const int normalDataOffset = ss->normalDataOffset;
const int vertDataSize = ss->meshIFC.vertDataSize;
CCGSubSurfCalcSubdivData data{};
data.ss = ss;
data.effectedV = effectedV;
data.effectedE = effectedE;
data.effectedF = effectedF;
data.numEffectedV = numEffectedV;
data.numEffectedE = numEffectedE;
data.numEffectedF = numEffectedF;
{
TaskParallelSettings settings;
BLI_parallel_range_settings_defaults(&settings);
settings.min_iter_per_thread = CCG_TASK_LIMIT;
BLI_task_parallel_range(
0, numEffectedF, &data, ccgSubSurf__calcVertNormals_faces_accumulate_cb, &settings);
}
/* XXX can I reduce the number of normalization calls here? */
for (ptrIdx = 0; ptrIdx < numEffectedV; ptrIdx++) {
CCGVert *v = effectedV[ptrIdx];
float *no = VERT_getNo(v, lvl);
NormZero(no);
for (i = 0; i < v->numFaces; i++) {
CCGFace *f = v->faces[i];
NormAdd(no, FACE_getIFNo(f, lvl, ccg_face_getVertIndex(f, v), gridSize - 1, gridSize - 1));
}
if (UNLIKELY(v->numFaces == 0)) {
NormCopy(no, VERT_getCo(v, lvl));
}
Normalize(no);
for (i = 0; i < v->numFaces; i++) {
CCGFace *f = v->faces[i];
NormCopy(FACE_getIFNo(f, lvl, ccg_face_getVertIndex(f, v), gridSize - 1, gridSize - 1), no);
}
}
{
TaskParallelSettings settings;
BLI_parallel_range_settings_defaults(&settings);
settings.min_iter_per_thread = CCG_TASK_LIMIT;
BLI_task_parallel_range(
0, numEffectedE, &data, ccgSubSurf__calcVertNormals_edges_accumulate_cb, &settings);
}
{
TaskParallelSettings settings;
BLI_parallel_range_settings_defaults(&settings);
settings.min_iter_per_thread = CCG_TASK_LIMIT;
BLI_task_parallel_range(
0, numEffectedF, &data, ccgSubSurf__calcVertNormals_faces_finalize_cb, &settings);
}
for (ptrIdx = 0; ptrIdx < numEffectedE; ptrIdx++) {
CCGEdge *e = effectedE[ptrIdx];
if (e->numFaces) {
CCGFace *f = e->faces[0];
int x;
const int f_ed_idx = ccg_face_getEdgeIndex(f, e);
for (x = 0; x < edgeSize; x++) {
NormCopy(EDGE_getNo(e, lvl, x),
_face_getIFNoEdge(
f, e, f_ed_idx, lvl, x, 0, subdivLevels, vertDataSize, normalDataOffset));
}
}
else {
/* set to zero here otherwise the normals are uninitialized memory
* render: tests/animation/knight.blend with valgrind.
* we could be more clever and interpolate vertex normals but these are
* most likely not used so just zero out. */
int x;
for (x = 0; x < edgeSize; x++) {
float *no = EDGE_getNo(e, lvl, x);
NormCopy(no, EDGE_getCo(e, lvl, x));
Normalize(no);
}
}
}
}
static void ccgSubSurf__calcSubdivLevel_interior_faces_edges_midpoints_cb(
void *__restrict userdata, const int ptrIdx, const TaskParallelTLS *__restrict /*tls*/)
{
CCGSubSurfCalcSubdivData *data = static_cast<CCGSubSurfCalcSubdivData *>(userdata);
CCGSubSurf *ss = data->ss;
CCGFace *f = data->effectedF[ptrIdx];
const int subdivLevels = ss->subdivLevels;
const int curLvl = data->curLvl;
const int nextLvl = curLvl + 1;
const int gridSize = ccg_gridsize(curLvl);
const int vertDataSize = ss->meshIFC.vertDataSize;
int S, x, y;
/* interior face midpoints
* - old interior face points
*/
for (S = 0; S < f->numVerts; S++) {
for (y = 0; y < gridSize - 1; y++) {
for (x = 0; x < gridSize - 1; x++) {
int fx = 1 + 2 * x;
int fy = 1 + 2 * y;
const float *co0 = FACE_getIFCo(f, curLvl, S, x + 0, y + 0);
const float *co1 = FACE_getIFCo(f, curLvl, S, x + 1, y + 0);
const float *co2 = FACE_getIFCo(f, curLvl, S, x + 1, y + 1);
const float *co3 = FACE_getIFCo(f, curLvl, S, x + 0, y + 1);
float *co = FACE_getIFCo(f, nextLvl, S, fx, fy);
VertDataAvg4(co, co0, co1, co2, co3, ss);
}
}
}
/* interior edge midpoints
* - old interior edge points
* - new interior face midpoints
*/
for (S = 0; S < f->numVerts; S++) {
for (x = 0; x < gridSize - 1; x++) {
int fx = x * 2 + 1;
const float *co0 = FACE_getIECo(f, curLvl, S, x + 0);
const float *co1 = FACE_getIECo(f, curLvl, S, x + 1);
const float *co2 = FACE_getIFCo(f, nextLvl, (S + 1) % f->numVerts, 1, fx);
const float *co3 = FACE_getIFCo(f, nextLvl, S, fx, 1);
float *co = FACE_getIECo(f, nextLvl, S, fx);
VertDataAvg4(co, co0, co1, co2, co3, ss);
}
/* interior face interior edge midpoints
* - old interior face points
* - new interior face midpoints
*/
/* vertical */
for (x = 1; x < gridSize - 1; x++) {
for (y = 0; y < gridSize - 1; y++) {
int fx = x * 2;
int fy = y * 2 + 1;
const float *co0 = FACE_getIFCo(f, curLvl, S, x, y + 0);
const float *co1 = FACE_getIFCo(f, curLvl, S, x, y + 1);
const float *co2 = FACE_getIFCo(f, nextLvl, S, fx - 1, fy);
const float *co3 = FACE_getIFCo(f, nextLvl, S, fx + 1, fy);
float *co = FACE_getIFCo(f, nextLvl, S, fx, fy);
VertDataAvg4(co, co0, co1, co2, co3, ss);
}
}
/* horizontal */
for (y = 1; y < gridSize - 1; y++) {
for (x = 0; x < gridSize - 1; x++) {
int fx = x * 2 + 1;
int fy = y * 2;
const float *co0 = FACE_getIFCo(f, curLvl, S, x + 0, y);
const float *co1 = FACE_getIFCo(f, curLvl, S, x + 1, y);
const float *co2 = FACE_getIFCo(f, nextLvl, S, fx, fy - 1);
const float *co3 = FACE_getIFCo(f, nextLvl, S, fx, fy + 1);
float *co = FACE_getIFCo(f, nextLvl, S, fx, fy);
VertDataAvg4(co, co0, co1, co2, co3, ss);
}
}
}
}
static void ccgSubSurf__calcSubdivLevel_interior_faces_edges_centerpoints_shift_cb(
void *__restrict userdata, const int ptrIdx, const TaskParallelTLS *__restrict /*tls*/)
{
CCGSubSurfCalcSubdivData *data = static_cast<CCGSubSurfCalcSubdivData *>(userdata);
CCGSubSurf *ss = data->ss;
CCGFace *f = data->effectedF[ptrIdx];
const int subdivLevels = ss->subdivLevels;
const int curLvl = data->curLvl;
const int nextLvl = curLvl + 1;
const int gridSize = ccg_gridsize(curLvl);
const int vertDataSize = ss->meshIFC.vertDataSize;
float *q_thread = static_cast<float *>(alloca(vertDataSize));
float *r_thread = static_cast<float *>(alloca(vertDataSize));
int S, x, y;
/* interior center point shift
* - old face center point (shifting)
* - old interior edge points
* - new interior face midpoints
*/
VertDataZero(q_thread, ss);
for (S = 0; S < f->numVerts; S++) {
VertDataAdd(q_thread, FACE_getIFCo(f, nextLvl, S, 1, 1), ss);
}
VertDataMulN(q_thread, 1.0f / f->numVerts, ss);
VertDataZero(r_thread, ss);
for (S = 0; S < f->numVerts; S++) {
VertDataAdd(r_thread, FACE_getIECo(f, curLvl, S, 1), ss);
}
VertDataMulN(r_thread, 1.0f / f->numVerts, ss);
VertDataMulN((float *)FACE_getCenterData(f), f->numVerts - 2.0f, ss);
VertDataAdd((float *)FACE_getCenterData(f), q_thread, ss);
VertDataAdd((float *)FACE_getCenterData(f), r_thread, ss);
VertDataMulN((float *)FACE_getCenterData(f), 1.0f / f->numVerts, ss);
for (S = 0; S < f->numVerts; S++) {
/* interior face shift
* - old interior face point (shifting)
* - new interior edge midpoints
* - new interior face midpoints
*/
for (x = 1; x < gridSize - 1; x++) {
for (y = 1; y < gridSize - 1; y++) {
int fx = x * 2;
int fy = y * 2;
const float *co = FACE_getIFCo(f, curLvl, S, x, y);
float *nCo = FACE_getIFCo(f, nextLvl, S, fx, fy);
VertDataAvg4(q_thread,
FACE_getIFCo(f, nextLvl, S, fx - 1, fy - 1),
FACE_getIFCo(f, nextLvl, S, fx + 1, fy - 1),
FACE_getIFCo(f, nextLvl, S, fx + 1, fy + 1),
FACE_getIFCo(f, nextLvl, S, fx - 1, fy + 1),
ss);
VertDataAvg4(r_thread,
FACE_getIFCo(f, nextLvl, S, fx - 1, fy + 0),
FACE_getIFCo(f, nextLvl, S, fx + 1, fy + 0),
FACE_getIFCo(f, nextLvl, S, fx + 0, fy - 1),
FACE_getIFCo(f, nextLvl, S, fx + 0, fy + 1),
ss);
VertDataCopy(nCo, co, ss);
VertDataSub(nCo, q_thread, ss);
VertDataMulN(nCo, 0.25f, ss);
VertDataAdd(nCo, r_thread, ss);
}
}
/* interior edge interior shift
* - old interior edge point (shifting)
* - new interior edge midpoints
* - new interior face midpoints
*/
for (x = 1; x < gridSize - 1; x++) {
int fx = x * 2;
const float *co = FACE_getIECo(f, curLvl, S, x);
float *nCo = FACE_getIECo(f, nextLvl, S, fx);
VertDataAvg4(q_thread,
FACE_getIFCo(f, nextLvl, (S + 1) % f->numVerts, 1, fx - 1),
FACE_getIFCo(f, nextLvl, (S + 1) % f->numVerts, 1, fx + 1),
FACE_getIFCo(f, nextLvl, S, fx + 1, +1),
FACE_getIFCo(f, nextLvl, S, fx - 1, +1),
ss);
VertDataAvg4(r_thread,
FACE_getIECo(f, nextLvl, S, fx - 1),
FACE_getIECo(f, nextLvl, S, fx + 1),
FACE_getIFCo(f, nextLvl, (S + 1) % f->numVerts, 1, fx),
FACE_getIFCo(f, nextLvl, S, fx, 1),
ss);
VertDataCopy(nCo, co, ss);
VertDataSub(nCo, q_thread, ss);
VertDataMulN(nCo, 0.25f, ss);
VertDataAdd(nCo, r_thread, ss);
}
}
}
static void ccgSubSurf__calcSubdivLevel_verts_copydata_cb(
void *__restrict userdata, const int ptrIdx, const TaskParallelTLS *__restrict /*tls*/)
{
CCGSubSurfCalcSubdivData *data = static_cast<CCGSubSurfCalcSubdivData *>(userdata);
CCGSubSurf *ss = data->ss;
CCGFace *f = data->effectedF[ptrIdx];
const int subdivLevels = ss->subdivLevels;
const int nextLvl = data->curLvl + 1;
const int gridSize = ccg_gridsize(nextLvl);
const int cornerIdx = gridSize - 1;
const int vertDataSize = ss->meshIFC.vertDataSize;
int S, x;
for (S = 0; S < f->numVerts; S++) {
CCGEdge *e = FACE_getEdges(f)[S];
CCGEdge *prevE = FACE_getEdges(f)[(S + f->numVerts - 1) % f->numVerts];
VertDataCopy(FACE_getIFCo(f, nextLvl, S, 0, 0), (float *)FACE_getCenterData(f), ss);
VertDataCopy(FACE_getIECo(f, nextLvl, S, 0), (float *)FACE_getCenterData(f), ss);
VertDataCopy(FACE_getIFCo(f, nextLvl, S, cornerIdx, cornerIdx),
VERT_getCo(FACE_getVerts(f)[S], nextLvl),
ss);
VertDataCopy(FACE_getIECo(f, nextLvl, S, cornerIdx),
EDGE_getCo(FACE_getEdges(f)[S], nextLvl, cornerIdx),
ss);
for (x = 1; x < gridSize - 1; x++) {
float *co = FACE_getIECo(f, nextLvl, S, x);
VertDataCopy(FACE_getIFCo(f, nextLvl, S, x, 0), co, ss);
VertDataCopy(FACE_getIFCo(f, nextLvl, (S + 1) % f->numVerts, 0, x), co, ss);
}
for (x = 0; x < gridSize - 1; x++) {
int eI = gridSize - 1 - x;
VertDataCopy(FACE_getIFCo(f, nextLvl, S, cornerIdx, x),
static_cast<const float *>(
_edge_getCoVert(e, FACE_getVerts(f)[S], nextLvl, eI, vertDataSize)),
ss);
VertDataCopy(FACE_getIFCo(f, nextLvl, S, x, cornerIdx),
static_cast<const float *>(
_edge_getCoVert(prevE, FACE_getVerts(f)[S], nextLvl, eI, vertDataSize)),
ss);
}
}
}
static void ccgSubSurf__calcSubdivLevel(CCGSubSurf *ss,
CCGVert **effectedV,
CCGEdge **effectedE,
CCGFace **effectedF,
const int numEffectedV,
const int numEffectedE,
const int numEffectedF,
const int curLvl)
{
const int subdivLevels = ss->subdivLevels;
const int nextLvl = curLvl + 1;
int edgeSize = ccg_edgesize(curLvl);
int ptrIdx, i;
const int vertDataSize = ss->meshIFC.vertDataSize;
float *q = static_cast<float *>(ss->q), *r = static_cast<float *>(ss->r);
CCGSubSurfCalcSubdivData data{};
data.ss = ss;
data.effectedV = effectedV;
data.effectedE = effectedE;
data.effectedF = effectedF;
data.numEffectedV = numEffectedV;
data.numEffectedE = numEffectedE;
data.numEffectedF = numEffectedF;
data.curLvl = curLvl;
{
TaskParallelSettings settings;
BLI_parallel_range_settings_defaults(&settings);
settings.min_iter_per_thread = CCG_TASK_LIMIT;
BLI_task_parallel_range(0,
numEffectedF,
&data,
ccgSubSurf__calcSubdivLevel_interior_faces_edges_midpoints_cb,
&settings);
}
/* exterior edge midpoints
* - old exterior edge points
* - new interior face midpoints
*/
/* Not worth parallelizing. */
for (ptrIdx = 0; ptrIdx < numEffectedE; ptrIdx++) {
CCGEdge *e = effectedE[ptrIdx];
float sharpness = EDGE_getSharpness(e, curLvl);
int x, j;
if (_edge_isBoundary(e) || sharpness > 1.0f) {
for (x = 0; x < edgeSize - 1; x++) {
int fx = x * 2 + 1;
const float *co0 = EDGE_getCo(e, curLvl, x + 0);
const float *co1 = EDGE_getCo(e, curLvl, x + 1);
float *co = EDGE_getCo(e, nextLvl, fx);
VertDataCopy(co, co0, ss);
VertDataAdd(co, co1, ss);
VertDataMulN(co, 0.5f, ss);
}
}
else {
for (x = 0; x < edgeSize - 1; x++) {
int fx = x * 2 + 1;
const float *co0 = EDGE_getCo(e, curLvl, x + 0);
const float *co1 = EDGE_getCo(e, curLvl, x + 1);
float *co = EDGE_getCo(e, nextLvl, fx);
int numFaces = 0;
VertDataCopy(q, co0, ss);
VertDataAdd(q, co1, ss);
for (j = 0; j < e->numFaces; j++) {
CCGFace *f = e->faces[j];
const int f_ed_idx = ccg_face_getEdgeIndex(f, e);
VertDataAdd(q,
static_cast<const float *>(ccg_face_getIFCoEdge(
f, e, f_ed_idx, nextLvl, fx, 1, subdivLevels, vertDataSize)),
ss);
numFaces++;
}
VertDataMulN(q, 1.0f / (2.0f + numFaces), ss);
VertDataCopy(r, co0, ss);
VertDataAdd(r, co1, ss);
VertDataMulN(r, 0.5f, ss);
VertDataCopy(co, q, ss);
VertDataSub(r, q, ss);
VertDataMulN(r, sharpness, ss);
VertDataAdd(co, r, ss);
}
}
}
/* exterior vertex shift
* - old vertex points (shifting)
* - old exterior edge points
* - new interior face midpoints
*/
/* Not worth parallelizing. */
for (ptrIdx = 0; ptrIdx < numEffectedV; ptrIdx++) {
CCGVert *v = effectedV[ptrIdx];
const float *co = VERT_getCo(v, curLvl);
float *nCo = VERT_getCo(v, nextLvl);
int sharpCount = 0, allSharp = 1;
float avgSharpness = 0.0;
int j, seam = VERT_seam(v), seamEdges = 0;
for (j = 0; j < v->numEdges; j++) {
CCGEdge *e = v->edges[j];
float sharpness = EDGE_getSharpness(e, curLvl);
if (seam && _edge_isBoundary(e)) {
seamEdges++;
}
if (sharpness != 0.0f) {
sharpCount++;
avgSharpness += sharpness;
}
else {
allSharp = 0;
}
}
if (sharpCount) {
avgSharpness /= sharpCount;
avgSharpness = std::min(avgSharpness, 1.0f);
}
if (seamEdges < 2 || seamEdges != v->numEdges) {
seam = 0;
}
if (!v->numEdges || ss->meshIFC.simpleSubdiv) {
VertDataCopy(nCo, co, ss);
}
else if (_vert_isBoundary(v)) {
int numBoundary = 0;
VertDataZero(r, ss);
for (j = 0; j < v->numEdges; j++) {
CCGEdge *e = v->edges[j];
if (_edge_isBoundary(e)) {
VertDataAdd(
r, static_cast<const float *>(_edge_getCoVert(e, v, curLvl, 1, vertDataSize)), ss);
numBoundary++;
}
}
VertDataCopy(nCo, co, ss);
VertDataMulN(nCo, 0.75f, ss);
VertDataMulN(r, 0.25f / numBoundary, ss);
VertDataAdd(nCo, r, ss);
}
else {
const int cornerIdx = (1 + (1 << (curLvl))) - 2;
int numEdges = 0, numFaces = 0;
VertDataZero(q, ss);
for (j = 0; j < v->numFaces; j++) {
CCGFace *f = v->faces[j];
VertDataAdd(
q, FACE_getIFCo(f, nextLvl, ccg_face_getVertIndex(f, v), cornerIdx, cornerIdx), ss);
numFaces++;
}
VertDataMulN(q, 1.0f / numFaces, ss);
VertDataZero(r, ss);
for (j = 0; j < v->numEdges; j++) {
CCGEdge *e = v->edges[j];
VertDataAdd(
r, static_cast<const float *>(_edge_getCoVert(e, v, curLvl, 1, vertDataSize)), ss);
numEdges++;
}
VertDataMulN(r, 1.0f / numEdges, ss);
VertDataCopy(nCo, co, ss);
VertDataMulN(nCo, numEdges - 2.0f, ss);
VertDataAdd(nCo, q, ss);
VertDataAdd(nCo, r, ss);
VertDataMulN(nCo, 1.0f / numEdges, ss);
}
if ((sharpCount > 1 && v->numFaces) || seam) {
VertDataZero(q, ss);
if (seam) {
avgSharpness = 1.0f;
sharpCount = seamEdges;
allSharp = 1;
}
for (j = 0; j < v->numEdges; j++) {
CCGEdge *e = v->edges[j];
float sharpness = EDGE_getSharpness(e, curLvl);
if (seam) {
if (_edge_isBoundary(e)) {
VertDataAdd(
q, static_cast<const float *>(_edge_getCoVert(e, v, curLvl, 1, vertDataSize)), ss);
}
}
else if (sharpness != 0.0f) {
VertDataAdd(
q, static_cast<const float *>(_edge_getCoVert(e, v, curLvl, 1, vertDataSize)), ss);
}
}
VertDataMulN(q, float(1) / sharpCount, ss);
if (sharpCount != 2 || allSharp) {
/* q = q + (co - q) * avgSharpness */
VertDataCopy(r, co, ss);
VertDataSub(r, q, ss);
VertDataMulN(r, avgSharpness, ss);
VertDataAdd(q, r, ss);
}
/* r = co * 0.75 + q * 0.25 */
VertDataCopy(r, co, ss);
VertDataMulN(r, 0.75f, ss);
VertDataMulN(q, 0.25f, ss);
VertDataAdd(r, q, ss);
/* nCo = nCo + (r - nCo) * avgSharpness */
VertDataSub(r, nCo, ss);
VertDataMulN(r, avgSharpness, ss);
VertDataAdd(nCo, r, ss);
}
}
/* exterior edge interior shift
* - old exterior edge midpoints (shifting)
* - old exterior edge midpoints
* - new interior face midpoints
*/
/* Not worth parallelizing. */
for (ptrIdx = 0; ptrIdx < numEffectedE; ptrIdx++) {
CCGEdge *e = effectedE[ptrIdx];
float sharpness = EDGE_getSharpness(e, curLvl);
int sharpCount = 0;
float avgSharpness = 0.0;
int x, j;
if (sharpness != 0.0f) {
sharpCount = 2;
avgSharpness += sharpness;
avgSharpness = std::min(avgSharpness, 1.0f);
}
else {
sharpCount = 0;
avgSharpness = 0;
}
if (_edge_isBoundary(e)) {
for (x = 1; x < edgeSize - 1; x++) {
int fx = x * 2;
const float *co = EDGE_getCo(e, curLvl, x);
float *nCo = EDGE_getCo(e, nextLvl, fx);
/* Average previous level's endpoints */
VertDataCopy(r, EDGE_getCo(e, curLvl, x - 1), ss);
VertDataAdd(r, EDGE_getCo(e, curLvl, x + 1), ss);
VertDataMulN(r, 0.5f, ss);
/* nCo = nCo * 0.75 + r * 0.25 */
VertDataCopy(nCo, co, ss);
VertDataMulN(nCo, 0.75f, ss);
VertDataMulN(r, 0.25f, ss);
VertDataAdd(nCo, r, ss);
}
}
else {
for (x = 1; x < edgeSize - 1; x++) {
int fx = x * 2;
const float *co = EDGE_getCo(e, curLvl, x);
float *nCo = EDGE_getCo(e, nextLvl, fx);
int numFaces = 0;
VertDataZero(q, ss);
VertDataZero(r, ss);
VertDataAdd(r, EDGE_getCo(e, curLvl, x - 1), ss);
VertDataAdd(r, EDGE_getCo(e, curLvl, x + 1), ss);
for (j = 0; j < e->numFaces; j++) {
CCGFace *f = e->faces[j];
int f_ed_idx = ccg_face_getEdgeIndex(f, e);
VertDataAdd(q,
static_cast<const float *>(ccg_face_getIFCoEdge(
f, e, f_ed_idx, nextLvl, fx - 1, 1, subdivLevels, vertDataSize)),
ss);
VertDataAdd(q,
static_cast<const float *>(ccg_face_getIFCoEdge(
f, e, f_ed_idx, nextLvl, fx + 1, 1, subdivLevels, vertDataSize)),
ss);
VertDataAdd(r,
static_cast<const float *>(ccg_face_getIFCoEdge(
f, e, f_ed_idx, curLvl, x, 1, subdivLevels, vertDataSize)),
ss);
numFaces++;
}
VertDataMulN(q, 1.0f / (numFaces * 2.0f), ss);
VertDataMulN(r, 1.0f / (2.0f + numFaces), ss);
VertDataCopy(nCo, co, ss);
VertDataMulN(nCo, float(numFaces), ss);
VertDataAdd(nCo, q, ss);
VertDataAdd(nCo, r, ss);
VertDataMulN(nCo, 1.0f / (2 + numFaces), ss);
if (sharpCount == 2) {
VertDataCopy(q, co, ss);
VertDataMulN(q, 6.0f, ss);
VertDataAdd(q, EDGE_getCo(e, curLvl, x - 1), ss);
VertDataAdd(q, EDGE_getCo(e, curLvl, x + 1), ss);
VertDataMulN(q, 1 / 8.0f, ss);
VertDataSub(q, nCo, ss);
VertDataMulN(q, avgSharpness, ss);
VertDataAdd(nCo, q, ss);
}
}
}
}
{
TaskParallelSettings settings;
BLI_parallel_range_settings_defaults(&settings);
settings.min_iter_per_thread = CCG_TASK_LIMIT;
BLI_task_parallel_range(0,
numEffectedF,
&data,
ccgSubSurf__calcSubdivLevel_interior_faces_edges_centerpoints_shift_cb,
&settings);
}
/* copy down */
edgeSize = ccg_edgesize(nextLvl);
/* Not worth parallelizing. */
for (i = 0; i < numEffectedE; i++) {
CCGEdge *e = effectedE[i];
VertDataCopy(EDGE_getCo(e, nextLvl, 0), VERT_getCo(e->v0, nextLvl), ss);
VertDataCopy(EDGE_getCo(e, nextLvl, edgeSize - 1), VERT_getCo(e->v1, nextLvl), ss);
}
{
TaskParallelSettings settings;
BLI_parallel_range_settings_defaults(&settings);
settings.min_iter_per_thread = CCG_TASK_LIMIT;
BLI_task_parallel_range(
0, numEffectedF, &data, ccgSubSurf__calcSubdivLevel_verts_copydata_cb, &settings);
}
}
void ccgSubSurf__sync_legacy(CCGSubSurf *ss)
{
CCGVert **effectedV;
CCGEdge **effectedE;
CCGFace **effectedF;
int numEffectedV, numEffectedE, numEffectedF;
int subdivLevels = ss->subdivLevels;
int vertDataSize = ss->meshIFC.vertDataSize;
int i, j, ptrIdx, S;
int curLvl, nextLvl;
void *q = ss->q, *r = ss->r;
effectedV = MEM_malloc_arrayN<CCGVert *>(size_t(ss->vMap->numEntries), "CCGSubsurf effectedV");
effectedE = MEM_malloc_arrayN<CCGEdge *>(size_t(ss->eMap->numEntries), "CCGSubsurf effectedE");
effectedF = MEM_malloc_arrayN<CCGFace *>(size_t(ss->fMap->numEntries), "CCGSubsurf effectedF");
numEffectedV = numEffectedE = numEffectedF = 0;
for (i = 0; i < ss->vMap->curSize; i++) {
CCGVert *v = (CCGVert *)ss->vMap->buckets[i];
for (; v; v = v->next) {
if (v->flags & Vert_eEffected) {
effectedV[numEffectedV++] = v;
for (j = 0; j < v->numEdges; j++) {
CCGEdge *e = v->edges[j];
if (!(e->flags & Edge_eEffected)) {
effectedE[numEffectedE++] = e;
e->flags |= Edge_eEffected;
}
}
for (j = 0; j < v->numFaces; j++) {
CCGFace *f = v->faces[j];
if (!(f->flags & Face_eEffected)) {
effectedF[numEffectedF++] = f;
f->flags |= Face_eEffected;
}
}
}
}
}
curLvl = 0;
nextLvl = curLvl + 1;
for (ptrIdx = 0; ptrIdx < numEffectedF; ptrIdx++) {
CCGFace *f = effectedF[ptrIdx];
void *co = FACE_getCenterData(f);
VertDataZero(static_cast<float *>(co), ss);
for (i = 0; i < f->numVerts; i++) {
VertDataAdd(static_cast<float *>(co), VERT_getCo(FACE_getVerts(f)[i], curLvl), ss);
}
VertDataMulN(static_cast<float *>(co), 1.0f / f->numVerts, ss);
f->flags = 0;
}
for (ptrIdx = 0; ptrIdx < numEffectedE; ptrIdx++) {
CCGEdge *e = effectedE[ptrIdx];
void *co = EDGE_getCo(e, nextLvl, 1);
float sharpness = EDGE_getSharpness(e, curLvl);
if (_edge_isBoundary(e) || sharpness >= 1.0f) {
VertDataCopy(static_cast<float *>(co), VERT_getCo(e->v0, curLvl), ss);
VertDataAdd(static_cast<float *>(co), VERT_getCo(e->v1, curLvl), ss);
VertDataMulN(static_cast<float *>(co), 0.5f, ss);
}
else {
int numFaces = 0;
VertDataCopy(static_cast<float *>(q), VERT_getCo(e->v0, curLvl), ss);
VertDataAdd(static_cast<float *>(q), VERT_getCo(e->v1, curLvl), ss);
for (i = 0; i < e->numFaces; i++) {
CCGFace *f = e->faces[i];
VertDataAdd(static_cast<float *>(q), (float *)FACE_getCenterData(f), ss);
numFaces++;
}
VertDataMulN(static_cast<float *>(q), 1.0f / (2.0f + numFaces), ss);
VertDataCopy(static_cast<float *>(r), VERT_getCo(e->v0, curLvl), ss);
VertDataAdd(static_cast<float *>(r), VERT_getCo(e->v1, curLvl), ss);
VertDataMulN(static_cast<float *>(r), 0.5f, ss);
VertDataCopy(static_cast<float *>(co), static_cast<const float *>(q), ss);
VertDataSub(static_cast<float *>(r), static_cast<const float *>(q), ss);
VertDataMulN(static_cast<float *>(r), sharpness, ss);
VertDataAdd(static_cast<float *>(co), static_cast<const float *>(r), ss);
}
/* edge flags cleared later */
}
for (ptrIdx = 0; ptrIdx < numEffectedV; ptrIdx++) {
CCGVert *v = effectedV[ptrIdx];
void *co = VERT_getCo(v, curLvl);
void *nCo = VERT_getCo(v, nextLvl);
int sharpCount = 0, allSharp = 1;
float avgSharpness = 0.0;
int seam = VERT_seam(v), seamEdges = 0;
for (i = 0; i < v->numEdges; i++) {
CCGEdge *e = v->edges[i];
float sharpness = EDGE_getSharpness(e, curLvl);
if (seam && _edge_isBoundary(e)) {
seamEdges++;
}
if (sharpness != 0.0f) {
sharpCount++;
avgSharpness += sharpness;
}
else {
allSharp = 0;
}
}
if (sharpCount) {
avgSharpness /= sharpCount;
avgSharpness = std::min(avgSharpness, 1.0f);
}
if (seamEdges < 2 || seamEdges != v->numEdges) {
seam = 0;
}
if (!v->numEdges || ss->meshIFC.simpleSubdiv) {
VertDataCopy(static_cast<float *>(nCo), static_cast<const float *>(co), ss);
}
else if (_vert_isBoundary(v)) {
int numBoundary = 0;
VertDataZero(static_cast<float *>(r), ss);
for (i = 0; i < v->numEdges; i++) {
CCGEdge *e = v->edges[i];
if (_edge_isBoundary(e)) {
VertDataAdd(static_cast<float *>(r), VERT_getCo(_edge_getOtherVert(e, v), curLvl), ss);
numBoundary++;
}
}
VertDataCopy(static_cast<float *>(nCo), static_cast<const float *>(co), ss);
VertDataMulN(static_cast<float *>(nCo), 0.75f, ss);
VertDataMulN(static_cast<float *>(r), 0.25f / numBoundary, ss);
VertDataAdd(static_cast<float *>(nCo), static_cast<const float *>(r), ss);
}
else {
int numEdges = 0, numFaces = 0;
VertDataZero(static_cast<float *>(q), ss);
for (i = 0; i < v->numFaces; i++) {
CCGFace *f = v->faces[i];
VertDataAdd(static_cast<float *>(q), (float *)FACE_getCenterData(f), ss);
numFaces++;
}
VertDataMulN(static_cast<float *>(q), 1.0f / numFaces, ss);
VertDataZero(static_cast<float *>(r), ss);
for (i = 0; i < v->numEdges; i++) {
CCGEdge *e = v->edges[i];
VertDataAdd(static_cast<float *>(r), VERT_getCo(_edge_getOtherVert(e, v), curLvl), ss);
numEdges++;
}
VertDataMulN(static_cast<float *>(r), 1.0f / numEdges, ss);
VertDataCopy(static_cast<float *>(nCo), static_cast<const float *>(co), ss);
VertDataMulN(static_cast<float *>(nCo), numEdges - 2.0f, ss);
VertDataAdd(static_cast<float *>(nCo), static_cast<const float *>(q), ss);
VertDataAdd(static_cast<float *>(nCo), static_cast<const float *>(r), ss);
VertDataMulN(static_cast<float *>(nCo), 1.0f / numEdges, ss);
}
if (sharpCount > 1 || seam) {
VertDataZero(static_cast<float *>(q), ss);
if (seam) {
avgSharpness = 1.0f;
sharpCount = seamEdges;
allSharp = 1;
}
for (i = 0; i < v->numEdges; i++) {
CCGEdge *e = v->edges[i];
float sharpness = EDGE_getSharpness(e, curLvl);
if (seam) {
if (_edge_isBoundary(e)) {
CCGVert *oV = _edge_getOtherVert(e, v);
VertDataAdd(static_cast<float *>(q), VERT_getCo(oV, curLvl), ss);
}
}
else if (sharpness != 0.0f) {
CCGVert *oV = _edge_getOtherVert(e, v);
VertDataAdd(static_cast<float *>(q), VERT_getCo(oV, curLvl), ss);
}
}
VertDataMulN(static_cast<float *>(q), float(1) / sharpCount, ss);
if (sharpCount != 2 || allSharp) {
/* q = q + (co - q) * avgSharpness */
VertDataCopy(static_cast<float *>(r), static_cast<const float *>(co), ss);
VertDataSub(static_cast<float *>(r), static_cast<const float *>(q), ss);
VertDataMulN(static_cast<float *>(r), avgSharpness, ss);
VertDataAdd(static_cast<float *>(q), static_cast<const float *>(r), ss);
}
/* r = co * 0.75 + q * 0.25 */
VertDataCopy(static_cast<float *>(r), static_cast<const float *>(co), ss);
VertDataMulN(static_cast<float *>(r), 0.75f, ss);
VertDataMulN(static_cast<float *>(q), 0.25f, ss);
VertDataAdd(static_cast<float *>(r), static_cast<const float *>(q), ss);
/* nCo = nCo + (r - nCo) * avgSharpness */
VertDataSub(static_cast<float *>(r), static_cast<const float *>(nCo), ss);
VertDataMulN(static_cast<float *>(r), avgSharpness, ss);
VertDataAdd(static_cast<float *>(nCo), static_cast<const float *>(r), ss);
}
/* vert flags cleared later */
}
if (ss->useAgeCounts) {
for (i = 0; i < numEffectedV; i++) {
CCGVert *v = effectedV[i];
uint8_t *user_data = static_cast<uint8_t *>(ccgSubSurf_getVertUserData(ss, v));
*((int *)&user_data[ss->vertUserAgeOffset]) = ss->currentAge;
}
for (i = 0; i < numEffectedE; i++) {
CCGEdge *e = effectedE[i];
uint8_t *user_data = static_cast<uint8_t *>(ccgSubSurf_getEdgeUserData(ss, e));
*((int *)&user_data[ss->edgeUserAgeOffset]) = ss->currentAge;
}
for (i = 0; i < numEffectedF; i++) {
CCGFace *f = effectedF[i];
uint8_t *user_data = static_cast<uint8_t *>(ccgSubSurf_getFaceUserData(ss, f));
*((int *)&user_data[ss->faceUserAgeOffset]) = ss->currentAge;
}
}
for (i = 0; i < numEffectedE; i++) {
CCGEdge *e = effectedE[i];
VertDataCopy(EDGE_getCo(e, nextLvl, 0), VERT_getCo(e->v0, nextLvl), ss);
VertDataCopy(EDGE_getCo(e, nextLvl, 2), VERT_getCo(e->v1, nextLvl), ss);
}
for (i = 0; i < numEffectedF; i++) {
CCGFace *f = effectedF[i];
for (S = 0; S < f->numVerts; S++) {
CCGEdge *e = FACE_getEdges(f)[S];
CCGEdge *prevE = FACE_getEdges(f)[(S + f->numVerts - 1) % f->numVerts];
VertDataCopy(FACE_getIFCo(f, nextLvl, S, 0, 0), (float *)FACE_getCenterData(f), ss);
VertDataCopy(FACE_getIECo(f, nextLvl, S, 0), (float *)FACE_getCenterData(f), ss);
VertDataCopy(
FACE_getIFCo(f, nextLvl, S, 1, 1), VERT_getCo(FACE_getVerts(f)[S], nextLvl), ss);
VertDataCopy(
FACE_getIECo(f, nextLvl, S, 1), EDGE_getCo(FACE_getEdges(f)[S], nextLvl, 1), ss);
VertDataCopy(FACE_getIFCo(f, nextLvl, S, 1, 0),
static_cast<const float *>(
_edge_getCoVert(e, FACE_getVerts(f)[S], nextLvl, 1, vertDataSize)),
ss);
VertDataCopy(FACE_getIFCo(f, nextLvl, S, 0, 1),
static_cast<const float *>(
_edge_getCoVert(prevE, FACE_getVerts(f)[S], nextLvl, 1, vertDataSize)),
ss);
}
}
for (curLvl = 1; curLvl < subdivLevels; curLvl++) {
ccgSubSurf__calcSubdivLevel(
ss, effectedV, effectedE, effectedF, numEffectedV, numEffectedE, numEffectedF, curLvl);
}
if (ss->calcVertNormals) {
ccgSubSurf__calcVertNormals(
ss, effectedV, effectedE, effectedF, numEffectedV, numEffectedE, numEffectedF);
}
for (ptrIdx = 0; ptrIdx < numEffectedV; ptrIdx++) {
CCGVert *v = effectedV[ptrIdx];
v->flags = 0;
}
for (ptrIdx = 0; ptrIdx < numEffectedE; ptrIdx++) {
CCGEdge *e = effectedE[ptrIdx];
e->flags = 0;
}
MEM_freeN(effectedF);
MEM_freeN(effectedE);
MEM_freeN(effectedV);
#ifdef DUMP_RESULT_GRIDS
ccgSubSurf__dumpCoords(ss);
#endif
}
/* ** Public API exposed to other areas which depends on old CCG code. ** */
CCGError ccgSubSurf_updateNormals(CCGSubSurf *ss, CCGFace **effectedF, int numEffectedF)
{
CCGVert **effectedV;
CCGEdge **effectedE;
int i, numEffectedV, numEffectedE, freeF;
ccgSubSurf__allFaces(ss, &effectedF, &numEffectedF, &freeF);
ccgSubSurf__effectedFaceNeighbors(
ss, effectedF, numEffectedF, &effectedV, &numEffectedV, &effectedE, &numEffectedE);
if (ss->calcVertNormals) {
ccgSubSurf__calcVertNormals(
ss, effectedV, effectedE, effectedF, numEffectedV, numEffectedE, numEffectedF);
}
for (i = 0; i < numEffectedV; i++) {
effectedV[i]->flags = 0;
}
for (i = 0; i < numEffectedE; i++) {
effectedE[i]->flags = 0;
}
for (i = 0; i < numEffectedF; i++) {
effectedF[i]->flags = 0;
}
MEM_freeN(effectedE);
MEM_freeN(effectedV);
if (freeF) {
MEM_freeN(effectedF);
}
return eCCGError_None;
}
CCGError ccgSubSurf_updateLevels(CCGSubSurf *ss, int lvl, CCGFace **effectedF, int numEffectedF)
{
CCGVert **effectedV;
CCGEdge **effectedE;
int numEffectedV, numEffectedE, freeF, i;
int curLvl, subdivLevels = ss->subdivLevels;
ccgSubSurf__allFaces(ss, &effectedF, &numEffectedF, &freeF);
ccgSubSurf__effectedFaceNeighbors(
ss, effectedF, numEffectedF, &effectedV, &numEffectedV, &effectedE, &numEffectedE);
for (curLvl = lvl; curLvl < subdivLevels; curLvl++) {
ccgSubSurf__calcSubdivLevel(
ss, effectedV, effectedE, effectedF, numEffectedV, numEffectedE, numEffectedF, curLvl);
}
for (i = 0; i < numEffectedV; i++) {
effectedV[i]->flags = 0;
}
for (i = 0; i < numEffectedE; i++) {
effectedE[i]->flags = 0;
}
for (i = 0; i < numEffectedF; i++) {
effectedF[i]->flags = 0;
}
MEM_freeN(effectedE);
MEM_freeN(effectedV);
if (freeF) {
MEM_freeN(effectedF);
}
return eCCGError_None;
}

View File

@@ -1,314 +0,0 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup bke
*/
#include <cstdlib>
#include <cstring>
#include "MEM_guardedalloc.h"
#include "BLI_utildefines.h" /* for BLI_assert */
#include "CCGSubSurf.h"
#include "CCGSubSurf_intern.h"
/**
* Hash implementation.
*/
static int kHashSizes[] = {
1, 3, 5, 11, 17, 37, 67, 131, 257, 521,
1031, 2053, 4099, 8209, 16411, 32771, 65537, 131101, 262147, 524309,
1048583, 2097169, 4194319, 8388617, 16777259, 33554467, 67108879, 134217757, 268435459,
};
/* -------------------------------------------------------------------- */
/** \name Generic Hash Functions
* \{ */
EHash *ccg_ehash_new(int estimatedNumEntries,
CCGAllocatorIFC *allocatorIFC,
CCGAllocatorHDL allocator)
{
EHash *eh = static_cast<EHash *>(allocatorIFC->alloc(allocator, sizeof(*eh)));
eh->allocatorIFC = *allocatorIFC;
eh->allocator = allocator;
eh->numEntries = 0;
eh->curSizeIdx = 0;
while (kHashSizes[eh->curSizeIdx] < estimatedNumEntries) {
eh->curSizeIdx++;
}
eh->curSize = kHashSizes[eh->curSizeIdx];
eh->buckets = EHASH_alloc(eh, eh->curSize * sizeof(*eh->buckets));
memset(eh->buckets, 0, eh->curSize * sizeof(*eh->buckets));
return eh;
}
void ccg_ehash_free(EHash *eh, EHEntryFreeFP freeEntry, void *user_data)
{
int numBuckets = eh->curSize;
while (numBuckets--) {
EHEntry *entry = eh->buckets[numBuckets];
while (entry) {
EHEntry *next = entry->next;
freeEntry(entry, user_data);
entry = next;
}
}
EHASH_free(eh, eh->buckets);
EHASH_free(eh, eh);
}
void ccg_ehash_insert(EHash *eh, EHEntry *entry)
{
int numBuckets = eh->curSize;
int hash = EHASH_hash(eh, entry->key);
entry->next = eh->buckets[hash];
eh->buckets[hash] = entry;
eh->numEntries++;
if (UNLIKELY(eh->numEntries > (numBuckets * 3))) {
EHEntry **oldBuckets = eh->buckets;
eh->curSize = kHashSizes[++eh->curSizeIdx];
eh->buckets = EHASH_alloc(eh, eh->curSize * sizeof(*eh->buckets));
memset(eh->buckets, 0, eh->curSize * sizeof(*eh->buckets));
while (numBuckets--) {
for (entry = oldBuckets[numBuckets]; entry;) {
EHEntry *next = entry->next;
hash = EHASH_hash(eh, entry->key);
entry->next = eh->buckets[hash];
eh->buckets[hash] = entry;
entry = next;
}
}
EHASH_free(eh, oldBuckets);
}
}
void *ccg_ehash_lookupWithPrev(EHash *eh, void *key, void ***prevp_r)
{
int hash = EHASH_hash(eh, key);
void **prevp = (void **)&eh->buckets[hash];
EHEntry *entry;
for (; (entry = static_cast<EHEntry *>(*prevp)); prevp = (void **)&entry->next) {
if (entry->key == key) {
*prevp_r = prevp;
return entry;
}
}
return nullptr;
}
void *ccg_ehash_lookup(EHash *eh, void *key)
{
int hash = EHASH_hash(eh, key);
EHEntry *entry;
for (entry = eh->buckets[hash]; entry; entry = entry->next) {
if (entry->key == key) {
break;
}
}
return entry;
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Hash Elements Iteration
* \{ */
void ccg_ehashIterator_init(EHash *eh, EHashIterator *ehi)
{
/* fill all members */
ehi->eh = eh;
ehi->curBucket = -1;
ehi->curEntry = nullptr;
while (!ehi->curEntry) {
ehi->curBucket++;
if (ehi->curBucket == ehi->eh->curSize) {
break;
}
ehi->curEntry = ehi->eh->buckets[ehi->curBucket];
}
}
void *ccg_ehashIterator_getCurrent(EHashIterator *ehi)
{
return ehi->curEntry;
}
void ccg_ehashIterator_next(EHashIterator *ehi)
{
if (ehi->curEntry) {
ehi->curEntry = ehi->curEntry->next;
while (!ehi->curEntry) {
ehi->curBucket++;
if (ehi->curBucket == ehi->eh->curSize) {
break;
}
ehi->curEntry = ehi->eh->buckets[ehi->curBucket];
}
}
}
int ccg_ehashIterator_isStopped(EHashIterator *ehi)
{
return !ehi->curEntry;
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Standard allocator implementation
* \{ */
static void *_stdAllocator_alloc(CCGAllocatorHDL /*a*/, int numBytes)
{
return MEM_mallocN(numBytes, "CCG standard alloc");
}
static void *_stdAllocator_realloc(CCGAllocatorHDL /*a*/, void *ptr, int newSize, int /*oldSize*/)
{
return MEM_reallocN(ptr, newSize);
}
static void _stdAllocator_free(CCGAllocatorHDL /*a*/, void *ptr)
{
MEM_freeN(ptr);
}
CCGAllocatorIFC *ccg_getStandardAllocatorIFC()
{
static CCGAllocatorIFC ifc;
ifc.alloc = _stdAllocator_alloc;
ifc.realloc = _stdAllocator_realloc;
ifc.free = _stdAllocator_free;
ifc.release = nullptr;
return &ifc;
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name * Catmull-Clark Gridding Subdivision Surface
* \{ */
#ifdef DUMP_RESULT_GRIDS
void ccgSubSurf__dumpCoords(CCGSubSurf *ss)
{
int vertDataSize = ss->meshIFC.vertDataSize;
int subdivLevels = ss->subdivLevels;
int gridSize = ccg_gridsize(subdivLevels);
int edgeSize = ccg_edgesize(subdivLevels);
int i, index, S;
for (i = 0, index = 0; i < ss->vMap->curSize; i++) {
CCGVert *v = (CCGVert *)ss->vMap->buckets[i];
for (; v; v = v->next, index++) {
float *co = VERT_getCo(v, subdivLevels);
printf("vertex index=%d, co=(%f, %f, %f)\n", index, co[0], co[1], co[2]);
}
}
for (i = 0, index = 0; i < ss->eMap->curSize; i++) {
CCGEdge *e = (CCGEdge *)ss->eMap->buckets[i];
for (; e; e = e->next, index++) {
int x;
float *co = VERT_getCo(e->v0, subdivLevels);
printf("edge index=%d, start_co=(%f, %f, %f)\n", index, co[0], co[1], co[2]);
for (x = 0; x < edgeSize; x++) {
float *co = EDGE_getCo(e, subdivLevels, x);
printf("edge index=%d, seg=%d, co=(%f, %f, %f)\n", index, x, co[0], co[1], co[2]);
}
co = VERT_getCo(e->v1, subdivLevels);
printf("edge index=%d, end_co=(%f, %f, %f)\n", index, co[0], co[1], co[2]);
}
}
for (i = 0, index = 0; i < ss->fMap->curSize; i++) {
CCGFace *f = (CCGFace *)ss->fMap->buckets[i];
for (; f; f = f->next, index++) {
for (S = 0; S < f->numVerts; S++) {
CCGVert *v = FACE_getVerts(f)[S];
float *co = VERT_getCo(v, subdivLevels);
printf("face index=%d, vertex=%d, coord=(%f, %f, %f)\n", index, S, co[0], co[1], co[2]);
}
}
}
for (i = 0, index = 0; i < ss->fMap->curSize; i++) {
CCGFace *f = (CCGFace *)ss->fMap->buckets[i];
for (; f; f = f->next, index++) {
for (S = 0; S < f->numVerts; S++) {
CCGEdge *e = FACE_getEdges(f)[S];
float *co1 = VERT_getCo(e->v0, subdivLevels);
float *co2 = VERT_getCo(e->v1, subdivLevels);
printf("face index=%d, edge=%d, coord1=(%f, %f, %f), coord2=(%f, %f, %f)\n",
index,
S,
co1[0],
co1[1],
co1[2],
co2[0],
co2[1],
co2[2]);
}
}
}
for (i = 0, index = 0; i < ss->fMap->curSize; i++) {
CCGFace *f = (CCGFace *)ss->fMap->buckets[i];
for (; f; f = f->next, index++) {
for (S = 0; S < f->numVerts; S++) {
int x, y;
for (x = 0; x < gridSize; x++) {
for (y = 0; y < gridSize; y++) {
float *co = FACE_getIFCo(f, subdivLevels, S, x, y);
printf("face index=%d. corner=%d, x=%d, y=%d, coord=(%f, %f, %f)\n",
index,
S,
x,
y,
co[0],
co[1],
co[2]);
}
}
for (x = 0; x < gridSize; x++) {
float *co = FACE_getIECo(f, subdivLevels, S, x);
printf("face index=%d. corner=%d, ie_index=%d, coord=(%f, %f, %f)\n",
index,
S,
x,
co[0],
co[1],
co[2]);
}
}
}
}
}
#endif /* DUMP_RESULT_GRIDS */
/** \} */

View File

@@ -51,6 +51,7 @@
#include "BKE_attribute_legacy_convert.hh"
#include "BKE_attribute_math.hh"
#include "BKE_attribute_storage.hh"
#include "BKE_ccg.hh"
#include "BKE_customdata.hh"
#include "BKE_customdata_file.h"
#include "BKE_deform.hh"
@@ -58,7 +59,6 @@
#include "BKE_main.hh"
#include "BKE_mesh_remap.hh"
#include "BKE_multires.hh"
#include "BKE_subsurf.hh"
#include "BLO_read_write.hh"
@@ -5190,7 +5190,7 @@ static void write_grid_paint_mask(BlendWriter *writer,
for (int i = 0; i < count; i++) {
const GridPaintMask *gpm = &grid_paint_mask[i];
if (gpm->data) {
const uint32_t gridsize = uint32_t(BKE_ccg_gridsize(gpm->level));
const uint32_t gridsize = uint32_t(CCG_grid_size(gpm->level));
BLO_write_float_array(writer, gridsize * gridsize, gpm->data);
}
}
@@ -5303,7 +5303,7 @@ static void blend_read_paint_mask(BlendDataReader *reader,
for (int i = 0; i < count; i++) {
GridPaintMask *gpm = &grid_paint_mask[i];
if (gpm->data) {
const int gridsize = BKE_ccg_gridsize(gpm->level);
const int gridsize = CCG_grid_size(gpm->level);
BLO_read_float_array(reader, gridsize * gridsize, &gpm->data);
}
}

View File

@@ -25,6 +25,7 @@
#include "BLI_math_vector_types.hh"
#include "BLI_memarena.h"
#include "BLI_multi_value_map.hh"
#include "BLI_ordered_edge.hh"
#include "BLI_polyfill_2d.h"
#include "BLI_string.h"
#include "BLI_string_utf8.h"

View File

@@ -1,364 +0,0 @@
/* SPDX-FileCopyrightText: 2005 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#include "BLI_math_vector.h"
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
#include "BKE_customdata.hh"
#include "BKE_mesh_legacy_derived_mesh.hh"
/* -------------------------------------------------------------------- */
static float *dm_getVertArray(DerivedMesh *dm)
{
float(*positions)[3] = (float(*)[3])CustomData_get_layer_named_for_write(
&dm->vertData, CD_PROP_FLOAT3, "position", dm->getNumVerts(dm));
if (!positions) {
positions = (float(*)[3])CustomData_add_layer_named(
&dm->vertData, CD_PROP_FLOAT3, CD_SET_DEFAULT, dm->getNumVerts(dm), "position");
CustomData_set_layer_flag(&dm->vertData, CD_PROP_FLOAT3, CD_FLAG_TEMPORARY);
dm->copyVertArray(dm, positions);
}
return (float *)positions;
}
static blender::int2 *dm_getEdgeArray(DerivedMesh *dm)
{
blender::int2 *edge = (blender::int2 *)CustomData_get_layer_named_for_write(
&dm->edgeData, CD_PROP_INT32_2D, ".edge_verts", dm->getNumEdges(dm));
if (!edge) {
edge = (blender::int2 *)CustomData_add_layer_named(
&dm->edgeData, CD_PROP_INT32_2D, CD_SET_DEFAULT, dm->getNumEdges(dm), ".edge_verts");
CustomData_set_layer_flag(&dm->edgeData, CD_PROP_INT32_2D, CD_FLAG_TEMPORARY);
dm->copyEdgeArray(dm, edge);
}
return edge;
}
static int *dm_getCornerVertArray(DerivedMesh *dm)
{
int *corner_verts = (int *)CustomData_get_layer_named_for_write(
&dm->loopData, CD_PROP_INT32, ".corner_vert", dm->getNumLoops(dm));
if (!corner_verts) {
corner_verts = (int *)CustomData_add_layer_named(
&dm->loopData, CD_PROP_INT32, CD_SET_DEFAULT, dm->getNumLoops(dm), ".corner_vert");
dm->copyCornerVertArray(dm, corner_verts);
}
return corner_verts;
}
static int *dm_getCornerEdgeArray(DerivedMesh *dm)
{
int *corner_edges = (int *)CustomData_get_layer_named(
&dm->loopData, CD_PROP_INT32, ".corner_edge");
if (!corner_edges) {
corner_edges = (int *)CustomData_add_layer_named(
&dm->loopData, CD_PROP_INT32, CD_SET_DEFAULT, dm->getNumLoops(dm), ".corner_edge");
dm->copyCornerEdgeArray(dm, corner_edges);
}
return corner_edges;
}
static int *dm_getPolyArray(DerivedMesh *dm)
{
if (!dm->face_offsets) {
dm->face_offsets = MEM_calloc_arrayN<int>(dm->getNumPolys(dm) + 1, __func__);
dm->copyPolyArray(dm, dm->face_offsets);
}
return dm->face_offsets;
}
void DM_init_funcs(DerivedMesh *dm)
{
/* default function implementations */
dm->getVertArray = dm_getVertArray;
dm->getEdgeArray = dm_getEdgeArray;
dm->getCornerVertArray = dm_getCornerVertArray;
dm->getCornerEdgeArray = dm_getCornerEdgeArray;
dm->getPolyArray = dm_getPolyArray;
dm->getVertDataArray = DM_get_vert_data_layer;
dm->getEdgeDataArray = DM_get_edge_data_layer;
dm->getPolyDataArray = DM_get_poly_data_layer;
dm->getLoopDataArray = DM_get_loop_data_layer;
}
void DM_init(DerivedMesh *dm,
DerivedMeshType type,
int numVerts,
int numEdges,
int numTessFaces,
int numLoops,
int numPolys)
{
dm->type = type;
dm->numVertData = numVerts;
dm->numEdgeData = numEdges;
dm->numTessFaceData = numTessFaces;
dm->numLoopData = numLoops;
dm->numPolyData = numPolys;
DM_init_funcs(dm);
/* Don't use #CustomData_reset because we don't want to touch custom-data. */
copy_vn_i(dm->vertData.typemap, CD_NUMTYPES, -1);
copy_vn_i(dm->edgeData.typemap, CD_NUMTYPES, -1);
copy_vn_i(dm->faceData.typemap, CD_NUMTYPES, -1);
copy_vn_i(dm->loopData.typemap, CD_NUMTYPES, -1);
copy_vn_i(dm->polyData.typemap, CD_NUMTYPES, -1);
}
void DM_from_template(DerivedMesh *dm,
DerivedMesh *source,
DerivedMeshType type,
int numVerts,
int numEdges,
int numTessFaces,
int numLoops,
int numPolys)
{
const CustomData_MeshMasks *mask = &CD_MASK_DERIVEDMESH;
CustomData_init_layout_from(
&source->vertData, &dm->vertData, mask->vmask, CD_SET_DEFAULT, numVerts);
CustomData_init_layout_from(
&source->edgeData, &dm->edgeData, mask->emask, CD_SET_DEFAULT, numEdges);
CustomData_init_layout_from(
&source->faceData, &dm->faceData, mask->fmask, CD_SET_DEFAULT, numTessFaces);
CustomData_init_layout_from(
&source->loopData, &dm->loopData, mask->lmask, CD_SET_DEFAULT, numLoops);
CustomData_init_layout_from(
&source->polyData, &dm->polyData, mask->pmask, CD_SET_DEFAULT, numPolys);
dm->face_offsets = static_cast<int *>(MEM_dupallocN(source->face_offsets));
dm->type = type;
dm->numVertData = numVerts;
dm->numEdgeData = numEdges;
dm->numTessFaceData = numTessFaces;
dm->numLoopData = numLoops;
dm->numPolyData = numPolys;
DM_init_funcs(dm);
}
void DM_release(DerivedMesh *dm)
{
CustomData_free(&dm->vertData);
CustomData_free(&dm->edgeData);
CustomData_free(&dm->faceData);
CustomData_free(&dm->loopData);
CustomData_free(&dm->polyData);
MEM_SAFE_FREE(dm->face_offsets);
}
void DM_set_only_copy(DerivedMesh *dm, const CustomData_MeshMasks *mask)
{
CustomData_set_only_copy(&dm->vertData, mask->vmask);
CustomData_set_only_copy(&dm->edgeData, mask->emask);
CustomData_set_only_copy(&dm->faceData, mask->fmask);
/* this wasn't in 2.63 and is disabled for 2.64 because it gives problems with
* weight paint mode when there are modifiers applied, needs further investigation,
* see replies to r50969, Campbell */
#if 0
CustomData_set_only_copy(&dm->loopData, mask->lmask);
Custom(&dm->polyData, mask->pmask);
#endif
}
void *DM_get_vert_data_layer(DerivedMesh *dm, const eCustomDataType type)
{
return CustomData_get_layer_for_write(&dm->vertData, type, dm->getNumVerts(dm));
}
void *DM_get_edge_data_layer(DerivedMesh *dm, const eCustomDataType type)
{
return CustomData_get_layer_for_write(&dm->edgeData, type, dm->getNumEdges(dm));
}
void *DM_get_poly_data_layer(DerivedMesh *dm, const eCustomDataType type)
{
return CustomData_get_layer_for_write(&dm->polyData, type, dm->getNumPolys(dm));
}
void *DM_get_loop_data_layer(DerivedMesh *dm, const eCustomDataType type)
{
return CustomData_get_layer_for_write(&dm->loopData, type, dm->getNumLoops(dm));
}
void DM_copy_vert_data(
const DerivedMesh *source, DerivedMesh *dest, int source_index, int dest_index, int count)
{
CustomData_copy_data(&source->vertData, &dest->vertData, source_index, dest_index, count);
}
void DM_interp_vert_data(const DerivedMesh *source,
DerivedMesh *dest,
int *src_indices,
float *weights,
int count,
int dest_index)
{
CustomData_interp(
&source->vertData, &dest->vertData, src_indices, weights, nullptr, count, dest_index);
}
struct CDDerivedMesh {
DerivedMesh dm;
/* these point to data in the DerivedMesh custom data layers,
* they are only here for efficiency and convenience */
float (*vert_positions)[3];
blender::int2 *medge;
MFace *mface;
int *corner_verts;
int *corner_edges;
};
/**************** DerivedMesh interface functions ****************/
static int cdDM_getNumVerts(DerivedMesh *dm)
{
return dm->numVertData;
}
static int cdDM_getNumEdges(DerivedMesh *dm)
{
return dm->numEdgeData;
}
static int cdDM_getNumLoops(DerivedMesh *dm)
{
return dm->numLoopData;
}
static int cdDM_getNumPolys(DerivedMesh *dm)
{
return dm->numPolyData;
}
static void cdDM_copyVertArray(DerivedMesh *dm, float (*r_positions)[3])
{
CDDerivedMesh *cddm = (CDDerivedMesh *)dm;
memcpy(r_positions, cddm->vert_positions, sizeof(float[3]) * dm->numVertData);
}
static void cdDM_copyEdgeArray(DerivedMesh *dm, blender::int2 *r_edge)
{
CDDerivedMesh *cddm = (CDDerivedMesh *)dm;
memcpy(r_edge, cddm->medge, sizeof(*r_edge) * dm->numEdgeData);
}
static void cdDM_copyCornerVertArray(DerivedMesh *dm, int *r_corner_verts)
{
CDDerivedMesh *cddm = (CDDerivedMesh *)dm;
memcpy(r_corner_verts, cddm->corner_verts, sizeof(*r_corner_verts) * dm->numLoopData);
}
static void cdDM_copyCornerEdgeArray(DerivedMesh *dm, int *r_corner_edges)
{
CDDerivedMesh *cddm = (CDDerivedMesh *)dm;
memcpy(r_corner_edges, cddm->corner_edges, sizeof(*r_corner_edges) * dm->numLoopData);
}
static void cdDM_copyPolyArray(DerivedMesh *dm, int *r_face_offsets)
{
memcpy(r_face_offsets, dm->face_offsets, sizeof(int) * (dm->numPolyData + 1));
}
static void cdDM_release(DerivedMesh *dm)
{
CDDerivedMesh *cddm = (CDDerivedMesh *)dm;
DM_release(dm);
MEM_freeN(cddm);
}
/**************** CDDM interface functions ****************/
static CDDerivedMesh *cdDM_create(const char *desc)
{
CDDerivedMesh *cddm = MEM_callocN<CDDerivedMesh>(desc);
DerivedMesh *dm = &cddm->dm;
dm->getNumVerts = cdDM_getNumVerts;
dm->getNumEdges = cdDM_getNumEdges;
dm->getNumLoops = cdDM_getNumLoops;
dm->getNumPolys = cdDM_getNumPolys;
dm->copyVertArray = cdDM_copyVertArray;
dm->copyEdgeArray = cdDM_copyEdgeArray;
dm->copyCornerVertArray = cdDM_copyCornerVertArray;
dm->copyCornerEdgeArray = cdDM_copyCornerEdgeArray;
dm->copyPolyArray = cdDM_copyPolyArray;
dm->getVertDataArray = DM_get_vert_data_layer;
dm->getEdgeDataArray = DM_get_edge_data_layer;
dm->release = cdDM_release;
return cddm;
}
static DerivedMesh *cdDM_from_mesh_ex(Mesh *mesh, const CustomData_MeshMasks *mask)
{
CDDerivedMesh *cddm = cdDM_create(__func__);
DerivedMesh *dm = &cddm->dm;
CustomData_MeshMasks cddata_masks = *mask;
cddata_masks.lmask &= ~CD_MASK_MDISPS;
/* This does a referenced copy, with an exception for fluid-simulation. */
DM_init(dm,
DM_TYPE_CDDM,
mesh->verts_num,
mesh->edges_num,
0 /* `mesh->totface` */,
mesh->corners_num,
mesh->faces_num);
CustomData_merge(&mesh->vert_data, &dm->vertData, cddata_masks.vmask, mesh->verts_num);
CustomData_merge(&mesh->edge_data, &dm->edgeData, cddata_masks.emask, mesh->edges_num);
CustomData_merge(&mesh->fdata_legacy,
&dm->faceData,
cddata_masks.fmask | CD_MASK_ORIGINDEX,
0 /* `mesh->totface` */);
CustomData_merge(&mesh->corner_data, &dm->loopData, cddata_masks.lmask, mesh->corners_num);
CustomData_merge(&mesh->face_data, &dm->polyData, cddata_masks.pmask, mesh->faces_num);
cddm->vert_positions = static_cast<float(*)[3]>(CustomData_get_layer_named_for_write(
&dm->vertData, CD_PROP_FLOAT3, "position", mesh->verts_num));
cddm->medge = static_cast<blender::int2 *>(CustomData_get_layer_named_for_write(
&dm->edgeData, CD_PROP_INT32_2D, ".edge_verts", mesh->edges_num));
cddm->corner_verts = static_cast<int *>(CustomData_get_layer_named_for_write(
&dm->loopData, CD_PROP_INT32, ".corner_vert", mesh->corners_num));
cddm->corner_edges = static_cast<int *>(CustomData_get_layer_named_for_write(
&dm->loopData, CD_PROP_INT32, ".corner_edge", mesh->corners_num));
dm->face_offsets = static_cast<int *>(MEM_dupallocN(mesh->face_offset_indices));
#if 0
cddm->mface = CustomData_get_layer(&dm->faceData, CD_MFACE);
#else
cddm->mface = nullptr;
#endif
/* commented since even when CD_ORIGINDEX was first added this line fails
* on the default cube, (after editmode toggle too) - campbell */
#if 0
BLI_assert(CustomData_has_layer(&cddm->dm.faceData, CD_ORIGINDEX));
#endif
return dm;
}
DerivedMesh *CDDM_from_mesh(Mesh *mesh)
{
return cdDM_from_mesh_ex(mesh, &CD_MASK_MESH);
}

View File

@@ -40,6 +40,7 @@
#include "BLI_string.h"
#include "BLI_string_utf8.h"
#include "BLI_string_utils.hh"
#include "BLI_threads.h"
#include "BLI_utildefines.h"
#include "BLT_translation.hh"

View File

@@ -26,7 +26,6 @@
#include "BKE_ccg.hh"
#include "BKE_editmesh.hh"
#include "BKE_mesh.h"
#include "BKE_mesh_legacy_derived_mesh.hh"
#include "BKE_mesh_runtime.hh"
#include "BKE_mesh_types.hh"
#include "BKE_modifier.hh"
@@ -35,12 +34,9 @@
#include "BKE_paint_bvh.hh"
#include "BKE_scene.hh"
#include "BKE_subdiv_ccg.hh"
#include "BKE_subsurf.hh"
#include "BKE_object.hh"
#include "CCGSubSurf.h"
#include "DEG_depsgraph_query.hh"
#include <cmath>
@@ -59,9 +55,6 @@ enum DispOp {
ADD_DISPLACEMENTS,
};
static void multiresModifier_disp_run(
DerivedMesh *dm, Mesh *mesh, DerivedMesh *dm2, DispOp op, CCGElem **oldGridData, int totlvl);
/** Custom-data. */
void multires_customdata_delete(Mesh *mesh)
@@ -88,80 +81,15 @@ void multires_customdata_delete(Mesh *mesh)
}
}
/** Grid hiding */
static BLI_bitmap *multires_mdisps_upsample_hidden(BLI_bitmap *lo_hidden,
const int lo_level,
const int hi_level,
/* assumed to be at hi_level (or null) */
const BLI_bitmap *prev_hidden)
{
const int hi_gridsize = BKE_ccg_gridsize(hi_level);
const int lo_gridsize = BKE_ccg_gridsize(lo_level);
BLI_assert(lo_level <= hi_level);
/* fast case */
if (lo_level == hi_level) {
return static_cast<BLI_bitmap *>(MEM_dupallocN(lo_hidden));
}
BLI_bitmap *subd = BLI_BITMAP_NEW(square_i(hi_gridsize), "MDisps.hidden upsample");
const int factor = BKE_ccg_factor(lo_level, hi_level);
const int offset = 1 << (hi_level - lo_level - 1);
/* low-res blocks */
for (int yl = 0; yl < lo_gridsize; yl++) {
for (int xl = 0; xl < lo_gridsize; xl++) {
const int lo_val = BLI_BITMAP_TEST(lo_hidden, yl * lo_gridsize + xl);
/* high-res blocks */
for (int yo = -offset; yo <= offset; yo++) {
const int yh = yl * factor + yo;
if (yh < 0 || yh >= hi_gridsize) {
continue;
}
for (int xo = -offset; xo <= offset; xo++) {
const int xh = xl * factor + xo;
if (xh < 0 || xh >= hi_gridsize) {
continue;
}
const int hi_ndx = yh * hi_gridsize + xh;
if (prev_hidden) {
/* If prev_hidden is available, copy it to
* subd, except when the equivalent element in
* lo_hidden is different */
if (lo_val != prev_hidden[hi_ndx]) {
BLI_BITMAP_SET(subd, hi_ndx, lo_val);
}
else {
BLI_BITMAP_SET(subd, hi_ndx, prev_hidden[hi_ndx]);
}
}
else {
BLI_BITMAP_SET(subd, hi_ndx, lo_val);
}
}
}
}
}
return subd;
}
static BLI_bitmap *multires_mdisps_downsample_hidden(const BLI_bitmap *old_hidden,
const int old_level,
const int new_level)
{
const int new_gridsize = BKE_ccg_gridsize(new_level);
const int old_gridsize = BKE_ccg_gridsize(old_level);
const int new_gridsize = CCG_grid_size(new_level);
const int old_gridsize = CCG_grid_size(old_level);
BLI_assert(new_level <= old_level);
const int factor = BKE_ccg_factor(new_level, old_level);
const int factor = CCG_grid_factor(new_level, old_level);
BLI_bitmap *new_hidden = BLI_BITMAP_NEW(square_i(new_gridsize), "downsample hidden");
for (int y = 0; y < new_gridsize; y++) {
@@ -175,46 +103,6 @@ static BLI_bitmap *multires_mdisps_downsample_hidden(const BLI_bitmap *old_hidde
return new_hidden;
}
static void multires_output_hidden_to_ccgdm(CCGDerivedMesh *ccgdm, Mesh *mesh, const int level)
{
const blender::OffsetIndices faces = mesh->faces();
const MDisps *mdisps = static_cast<const MDisps *>(
CustomData_get_layer(&mesh->corner_data, CD_MDISPS));
BLI_bitmap **grid_hidden = ccgdm->gridHidden;
const int *gridOffset = ccgdm->dm.getGridOffset(&ccgdm->dm);
for (const int i : faces.index_range()) {
for (int j = 0; j < faces[i].size(); j++) {
int g = gridOffset[i] + j;
const MDisps *md = &mdisps[g];
if (BLI_bitmap *gh = md->hidden) {
grid_hidden[g] = multires_mdisps_downsample_hidden(gh, md->level, level);
}
}
}
}
/* subdivide mdisps.hidden if needed (assumes that md.level reflects
* the current level of md.hidden) */
static void multires_mdisps_subdivide_hidden(MDisps *md, const int new_level)
{
BLI_assert(md->hidden);
/* nothing to do if already subdivided enough */
if (md->level >= new_level) {
return;
}
BLI_bitmap *subd = multires_mdisps_upsample_hidden(md->hidden, md->level, new_level, nullptr);
/* swap in the subdivided data */
MEM_freeN(md->hidden);
md->hidden = subd;
}
Mesh *BKE_multires_create_mesh(Depsgraph *depsgraph, Object *object, MultiresModifierData *mmd)
{
Object *object_eval = DEG_get_evaluated(depsgraph, object);
@@ -534,28 +422,6 @@ static void multires_set_tot_mdisps(Mesh *mesh, const int lvl)
}
}
static void multires_reallocate_mdisps(const int totloop, MDisps *mdisps, const int lvl)
{
/* reallocate displacements to be filled in */
for (int i = 0; i < totloop; i++) {
const int totdisp = multires_grid_tot[lvl];
float(*disps)[3] = MEM_calloc_arrayN<float[3]>(totdisp, __func__);
if (mdisps[i].disps) {
MEM_freeN(mdisps[i].disps);
}
if (mdisps[i].level && mdisps[i].hidden) {
multires_mdisps_subdivide_hidden(&mdisps[i], lvl);
}
mdisps[i].disps = disps;
mdisps[i].totdisp = totdisp;
mdisps[i].level = lvl;
}
}
static void multires_copy_grid(float (*gridA)[3],
float (*gridB)[3],
const int sizeA,
@@ -588,7 +454,7 @@ static void multires_copy_grid(float (*gridA)[3],
static void multires_grid_paint_mask_downsample(GridPaintMask *gpm, const int level)
{
if (level < gpm->level) {
const int gridsize = BKE_ccg_gridsize(level);
const int gridsize = CCG_grid_size(level);
float *data = MEM_calloc_arrayN<float>(size_t(square_i(gridsize)), __func__);
for (int y = 0; y < gridsize; y++) {
@@ -687,290 +553,6 @@ void multiresModifier_del_levels(MultiresModifierData *mmd,
multires_set_tot_level(ob, mmd, lvl);
}
static DerivedMesh *subsurf_dm_create_local(Scene *scene,
Object *ob,
DerivedMesh *dm,
const int lvl,
const bool is_simple,
const bool is_optimal,
const bool is_plain_uv,
const bool alloc_paint_mask,
const bool for_render,
SubsurfFlags flags)
{
SubsurfModifierData smd = {{nullptr}};
smd.levels = smd.renderLevels = lvl;
smd.quality = 3;
if (!is_plain_uv) {
smd.uv_smooth = SUBSURF_UV_SMOOTH_PRESERVE_BOUNDARIES;
}
else {
smd.uv_smooth = SUBSURF_UV_SMOOTH_NONE;
}
if (is_simple) {
smd.subdivType = ME_SIMPLE_SUBSURF;
}
if (is_optimal) {
smd.flags |= eSubsurfModifierFlag_ControlEdges;
}
if (ob->mode & OB_MODE_EDIT) {
flags |= SUBSURF_IN_EDIT_MODE;
}
if (alloc_paint_mask) {
flags |= SUBSURF_ALLOC_PAINT_MASK;
}
if (for_render) {
flags |= SUBSURF_USE_RENDER_PARAMS;
}
return subsurf_make_derived_from_derived(dm, &smd, scene, nullptr, flags);
}
static void grid_tangent(
const CCGKey &key, const int x, const int y, const int axis, CCGElem *grid, float t[3])
{
if (axis == 0) {
if (x == key.grid_size - 1) {
if (y == key.grid_size - 1) {
sub_v3_v3v3(
t, CCG_grid_elem_co(key, grid, x, y - 1), CCG_grid_elem_co(key, grid, x - 1, y - 1));
}
else {
sub_v3_v3v3(t, CCG_grid_elem_co(key, grid, x, y), CCG_grid_elem_co(key, grid, x - 1, y));
}
}
else {
sub_v3_v3v3(t, CCG_grid_elem_co(key, grid, x + 1, y), CCG_grid_elem_co(key, grid, x, y));
}
}
else if (axis == 1) {
if (y == key.grid_size - 1) {
if (x == key.grid_size - 1) {
sub_v3_v3v3(
t, CCG_grid_elem_co(key, grid, x - 1, y), CCG_grid_elem_co(key, grid, x - 1, (y - 1)));
}
else {
sub_v3_v3v3(t, CCG_grid_elem_co(key, grid, x, y), CCG_grid_elem_co(key, grid, x, (y - 1)));
}
}
else {
sub_v3_v3v3(t, CCG_grid_elem_co(key, grid, x, (y + 1)), CCG_grid_elem_co(key, grid, x, y));
}
}
}
/* Construct 3x3 tangent-space matrix in 'mat' */
static void grid_tangent_matrix(
float mat[3][3], const CCGKey &key, const int x, const int y, CCGElem *grid)
{
grid_tangent(key, x, y, 0, grid, mat[0]);
normalize_v3(mat[0]);
grid_tangent(key, x, y, 1, grid, mat[1]);
normalize_v3(mat[1]);
copy_v3_v3(mat[2], CCG_grid_elem_no(key, grid, x, y));
}
struct MultiresThreadedData {
DispOp op;
CCGElem **gridData, **subGridData;
CCGKey *key;
CCGKey *sub_key;
blender::OffsetIndices<int> faces;
MDisps *mdisps;
GridPaintMask *grid_paint_mask;
int *gridOffset;
int gridSize, dGridSize, dSkip;
float (*smat)[3];
};
static void multires_disp_run_cb(void *__restrict userdata,
const int pidx,
const TaskParallelTLS *__restrict /*tls*/)
{
MultiresThreadedData *tdata = static_cast<MultiresThreadedData *>(userdata);
const DispOp op = tdata->op;
CCGElem **gridData = tdata->gridData;
CCGElem **subGridData = tdata->subGridData;
const CCGKey key = *tdata->key;
const blender::OffsetIndices<int> faces = tdata->faces;
MDisps *mdisps = tdata->mdisps;
GridPaintMask *grid_paint_mask = tdata->grid_paint_mask;
const int *gridOffset = tdata->gridOffset;
const int gridSize = tdata->gridSize;
const int dGridSize = tdata->dGridSize;
const int dSkip = tdata->dSkip;
const int numVerts = faces[pidx].size();
int gIndex = gridOffset[pidx];
for (int S = 0; S < numVerts; S++, gIndex++) {
GridPaintMask *gpm = grid_paint_mask ? &grid_paint_mask[gIndex] : nullptr;
MDisps *mdisp = &mdisps[faces[pidx][S]];
CCGElem *grid = gridData[gIndex];
CCGElem *subgrid = subGridData[gIndex];
float(*dispgrid)[3] = mdisp->disps;
/* if needed, reallocate multires paint mask */
if (gpm && gpm->level < key.level) {
gpm->level = key.level;
if (gpm->data) {
MEM_freeN(gpm->data);
}
gpm->data = MEM_calloc_arrayN<float>(key.grid_area, "gpm.data");
}
for (int y = 0; y < gridSize; y++) {
for (int x = 0; x < gridSize; x++) {
float *co = CCG_grid_elem_co(key, grid, x, y);
float *sco = CCG_grid_elem_co(key, subgrid, x, y);
float *data = dispgrid[dGridSize * y * dSkip + x * dSkip];
float mat[3][3], disp[3], d[3];
/* construct tangent space matrix */
grid_tangent_matrix(mat, key, x, y, subgrid);
switch (op) {
case APPLY_DISPLACEMENTS:
/* Convert displacement to object space
* and add to grid points */
mul_v3_m3v3(disp, mat, data);
add_v3_v3v3(co, sco, disp);
break;
case CALC_DISPLACEMENTS:
/* Calculate displacement between new and old
* grid points and convert to tangent space */
sub_v3_v3v3(disp, co, sco);
invert_m3(mat);
mul_v3_m3v3(data, mat, disp);
break;
case ADD_DISPLACEMENTS:
/* Convert subdivided displacements to tangent
* space and add to the original displacements */
invert_m3(mat);
mul_v3_m3v3(d, mat, co);
add_v3_v3(data, d);
break;
}
if (gpm) {
float mask;
switch (op) {
case APPLY_DISPLACEMENTS:
/* Copy mask from gpm to DM */
CCG_grid_elem_mask(key, grid, x, y) = paint_grid_paint_mask(gpm, key.level, x, y);
break;
case CALC_DISPLACEMENTS:
/* Copy mask from DM to gpm */
mask = CCG_grid_elem_mask(key, grid, x, y);
gpm->data[y * gridSize + x] = std::clamp(mask, 0.0f, 1.0f);
break;
case ADD_DISPLACEMENTS:
/* Add mask displacement to gpm */
gpm->data[y * gridSize + x] += CCG_grid_elem_mask(key, grid, x, y);
break;
}
}
}
}
}
}
/* XXX WARNING: subsurf elements from dm and oldGridData *must* be of the same format (size),
* because this code uses CCGKey's info from dm to access oldGridData's normals
* (through the call to grid_tangent_matrix())! */
static void multiresModifier_disp_run(DerivedMesh *dm,
Mesh *mesh,
DerivedMesh *dm2,
DispOp op,
CCGElem **oldGridData,
const int totlvl)
{
CCGDerivedMesh *ccgdm = reinterpret_cast<CCGDerivedMesh *>(dm);
blender::OffsetIndices faces = mesh->faces();
MDisps *mdisps = static_cast<MDisps *>(
CustomData_get_layer_for_write(&mesh->corner_data, CD_MDISPS, mesh->corners_num));
GridPaintMask *grid_paint_mask = nullptr;
int totloop, faces_num;
/* this happens in the dm made by bmesh_mdisps_space_set */
if (dm2 && CustomData_has_layer(&dm2->loopData, CD_MDISPS)) {
faces = blender::OffsetIndices(
blender::Span(dm2->getPolyArray(dm2), dm2->getNumPolys(dm2) + 1));
mdisps = static_cast<MDisps *>(
CustomData_get_layer_for_write(&dm2->loopData, CD_MDISPS, dm2->getNumLoops(dm)));
totloop = dm2->numLoopData;
faces_num = dm2->numPolyData;
}
else {
totloop = mesh->corners_num;
faces_num = mesh->faces_num;
}
if (!mdisps) {
if (op == CALC_DISPLACEMENTS) {
mdisps = static_cast<MDisps *>(
CustomData_add_layer(&mesh->corner_data, CD_MDISPS, CD_SET_DEFAULT, mesh->corners_num));
}
else {
return;
}
}
// numGrids = dm->getNumGrids(dm); /* UNUSED */
const int gridSize = dm->getGridSize(dm);
CCGElem **gridData = dm->getGridData(dm);
int *gridOffset = dm->getGridOffset(dm);
CCGKey key;
dm->getGridKey(dm, &key);
CCGElem **subGridData = (oldGridData) ? oldGridData : gridData;
const int dGridSize = multires_side_tot[totlvl];
const int dSkip = (dGridSize - 1) / (gridSize - 1);
/* multires paint masks */
if (key.has_mask) {
grid_paint_mask = static_cast<GridPaintMask *>(
CustomData_get_layer_for_write(&mesh->corner_data, CD_GRID_PAINT_MASK, mesh->corners_num));
}
/* when adding new faces in edit mode, need to allocate disps */
for (int i = 0; i < totloop; i++) {
if (mdisps[i].disps == nullptr) {
multires_reallocate_mdisps(totloop, mdisps, totlvl);
break;
}
}
TaskParallelSettings settings;
BLI_parallel_range_settings_defaults(&settings);
settings.min_iter_per_thread = CCG_TASK_LIMIT;
MultiresThreadedData data{};
data.op = op;
data.gridData = gridData;
data.subGridData = subGridData;
data.key = &key;
data.faces = faces;
data.mdisps = mdisps;
data.grid_paint_mask = grid_paint_mask;
data.gridOffset = gridOffset;
data.gridSize = gridSize;
data.dGridSize = dGridSize;
data.dSkip = dSkip;
BLI_task_parallel_range(0, faces_num, &data, multires_disp_run_cb, &settings);
if (op == APPLY_DISPLACEMENTS) {
ccgSubSurf_stitchFaces(ccgdm->ss, 0, nullptr, 0);
ccgSubSurf_updateNormals(ccgdm->ss, nullptr, 0);
}
}
void multires_stitch_grids(Object *ob)
{
using namespace blender;
@@ -990,79 +572,6 @@ void multires_stitch_grids(Object *ob)
BKE_subdiv_ccg_average_stitch_faces(*subdiv_ccg, IndexMask(subdiv_ccg->faces.size()));
}
DerivedMesh *multires_make_derived_from_derived(DerivedMesh *dm,
MultiresModifierData *mmd,
Scene *scene,
Object *ob,
const MultiresFlags flags)
{
Mesh *mesh = static_cast<Mesh *>(ob->data);
const bool render = uint8_t(flags & MultiresFlags::UseRenderParams) != 0;
const bool ignore_simplify = uint8_t(flags & MultiresFlags::IgnoreSimplify) != 0;
const int lvl = multires_get_level(scene, ob, mmd, render, ignore_simplify);
if (lvl == 0) {
return dm;
}
const SubsurfFlags subsurf_flags = ignore_simplify ? SUBSURF_IGNORE_SIMPLIFY : SubsurfFlags(0);
DerivedMesh *result = subsurf_dm_create_local(scene,
ob,
dm,
lvl,
false,
mmd->flags & eMultiresModifierFlag_ControlEdges,
mmd->uv_smooth == SUBSURF_UV_SMOOTH_NONE,
uint8_t(flags & MultiresFlags::AllocPaintMask),
render,
subsurf_flags);
CCGDerivedMesh *ccgdm = nullptr;
if (!uint8_t(flags & MultiresFlags::UseLocalMMD)) {
ccgdm = reinterpret_cast<CCGDerivedMesh *>(result);
ccgdm->multires.ob = ob;
ccgdm->multires.mmd = mmd;
ccgdm->multires.local_mmd = 0;
ccgdm->multires.lvl = lvl;
ccgdm->multires.totlvl = mmd->totlvl;
}
const int numGrids = result->getNumGrids(result);
const int gridSize = result->getGridSize(result);
CCGElem **gridData = result->getGridData(result);
CCGKey key;
result->getGridKey(result, &key);
CCGElem **subGridData = MEM_malloc_arrayN<CCGElem *>(size_t(numGrids), "subGridData*");
for (int i = 0; i < numGrids; i++) {
subGridData[i] = static_cast<CCGElem *>(
MEM_malloc_arrayN(gridSize * gridSize, key.elem_size, "subGridData"));
memcpy(subGridData[i], gridData[i], key.elem_size * gridSize * gridSize);
}
multires_set_tot_mdisps(mesh, mmd->totlvl);
multiresModifier_ensure_external_read(mesh, mmd);
/* Run displacement. */
multiresModifier_disp_run(
result, static_cast<Mesh *>(ob->data), dm, APPLY_DISPLACEMENTS, subGridData, mmd->totlvl);
/* copy hidden elements for this level */
if (ccgdm) {
multires_output_hidden_to_ccgdm(ccgdm, mesh, lvl);
}
for (int i = 0; i < numGrids; i++) {
MEM_freeN(static_cast<void *>(subGridData[i]));
}
MEM_freeN(subGridData);
return result;
}
void old_mdisps_bilinear(float out[3], float (*disps)[3], const int st, float u, float v)
{
const int st_max = st - 1;
@@ -1297,97 +806,3 @@ void multiresModifier_ensure_external_read(Mesh *mesh, const MultiresModifierDat
{
multires_ensure_external_read(mesh, mmd->totlvl);
}
/***************** Multires interpolation stuff *****************/
int mdisp_rot_face_to_crn(
const int face_size, const int face_side, const float u, const float v, float *x, float *y)
{
const float offset = face_side * 0.5f - 0.5f;
int S = 0;
if (face_size == 4) {
if (u <= offset && v <= offset) {
S = 0;
}
else if (u > offset && v <= offset) {
S = 1;
}
else if (u > offset && v > offset) {
S = 2;
}
else if (u <= offset && v >= offset) {
S = 3;
}
if (S == 0) {
*y = offset - u;
*x = offset - v;
}
else if (S == 1) {
*x = u - offset;
*y = offset - v;
}
else if (S == 2) {
*y = u - offset;
*x = v - offset;
}
else if (S == 3) {
*x = offset - u;
*y = v - offset;
}
}
else if (face_size == 3) {
int grid_size = offset;
float w = (face_side - 1) - u - v;
float W1, W2;
if (u >= v && u >= w) {
S = 0;
W1 = w;
W2 = v;
}
else if (v >= u && v >= w) {
S = 1;
W1 = u;
W2 = w;
}
else {
S = 2;
W1 = v;
W2 = u;
}
W1 /= (face_side - 1);
W2 /= (face_side - 1);
*x = (1 - (2 * W1) / (1 - W2)) * grid_size;
*y = (1 - (2 * W2) / (1 - W1)) * grid_size;
}
else {
/* the complicated ngon case: find the actual coordinate from
* the barycentric coordinates and finally find the closest vertex
* should work reliably for convex cases only but better than nothing */
#if 0
int minS, i;
float mindist = FLT_MAX;
for (i = 0; i < poly->totloop; i++) {
float len = len_v3v3(nullptr, positions[corner_verts[poly->loopstart + i]]);
if (len < mindist) {
mindist = len;
minS = i;
}
}
S = minS;
#endif
/* temp not implemented yet and also not working properly in current master.
* (was worked around by subdividing once) */
S = 0;
*x = 0;
*y = 0;
}
return S;
}

View File

@@ -21,10 +21,10 @@
#include "BLI_math_vector.h"
#include "BKE_attribute.hh"
#include "BKE_ccg.hh"
#include "BKE_customdata.hh"
#include "BKE_mesh.hh"
#include "BKE_multires.hh"
#include "BKE_subsurf.hh"
#include "bmesh.hh"
@@ -647,8 +647,8 @@ static void store_grid_data(MultiresUnsubdivideContext *context,
}
/* Write the 4 grids of the current quad with the right orientation into the face_grid buffer. */
const int grid_size = BKE_ccg_gridsize(context->num_original_levels);
const int face_grid_size = BKE_ccg_gridsize(context->num_original_levels + 1);
const int grid_size = CCG_grid_size(context->num_original_levels);
const int face_grid_size = CCG_grid_size(context->num_original_levels + 1);
const int face_grid_area = face_grid_size * face_grid_size;
float(*face_grid)[3] = MEM_calloc_arrayN<float[3]>(face_grid_area, "face_grid");
@@ -700,8 +700,8 @@ static void multires_unsubdivide_extract_single_grid_from_face_edge(
BMEdge *initial_edge_x;
BMEdge *initial_edge_y;
const int grid_size = BKE_ccg_gridsize(context->num_new_levels);
const int unsubdiv_grid_size = grid->grid_size = BKE_ccg_gridsize(context->num_total_levels);
const int grid_size = CCG_grid_size(context->num_new_levels);
const int unsubdiv_grid_size = grid->grid_size = CCG_grid_size(context->num_total_levels);
BLI_assert(grid->grid_co == nullptr);
grid->grid_size = unsubdiv_grid_size;
grid->grid_co = MEM_calloc_arrayN<float[3]>(
@@ -1171,7 +1171,7 @@ static void multires_create_grids_in_unsubdivided_base_mesh(MultiresUnsubdivideC
MDisps *mdisps = static_cast<MDisps *>(CustomData_add_layer(
&base_mesh->corner_data, CD_MDISPS, CD_SET_DEFAULT, base_mesh->corners_num));
const int totdisp = pow_i(BKE_ccg_gridsize(context->num_total_levels), 2);
const int totdisp = pow_i(CCG_grid_size(context->num_total_levels), 2);
const int totloop = base_mesh->corners_num;
BLI_assert(base_mesh->corners_num == context->num_grids);

View File

@@ -125,7 +125,6 @@
#include "BKE_softbody.h"
#include "BKE_speaker.h"
#include "BKE_subdiv_ccg.hh"
#include "BKE_subsurf.hh"
#include "BKE_vfont.hh"
#include "BKE_volume.hh"

View File

@@ -65,7 +65,6 @@
#include "BKE_paint_types.hh"
#include "BKE_scene.hh"
#include "BKE_subdiv_ccg.hh"
#include "BKE_subsurf.hh"
#include "DEG_depsgraph.hh"
#include "DEG_depsgraph_query.hh"
@@ -2105,8 +2104,8 @@ bool paint_is_bmesh_face_hidden(const BMFace *f)
float paint_grid_paint_mask(const GridPaintMask *gpm, uint level, uint x, uint y)
{
int factor = BKE_ccg_factor(level, gpm->level);
int gridsize = BKE_ccg_gridsize(gpm->level);
int factor = CCG_grid_factor(level, gpm->level);
int gridsize = CCG_grid_size(gpm->level);
return gpm->data[(y * factor) * gridsize + (x * factor)];
}
@@ -2770,7 +2769,7 @@ void BKE_sculpt_mask_layers_ensure(Depsgraph *depsgraph,
* isn't one already */
if (mmd && !CustomData_has_layer(&mesh->corner_data, CD_GRID_PAINT_MASK)) {
int level = max_ii(1, mmd->sculptlvl);
int gridsize = BKE_ccg_gridsize(level);
int gridsize = CCG_grid_size(level);
int gridarea = gridsize * gridsize;
GridPaintMask *gmask = static_cast<GridPaintMask *>(CustomData_add_layer(

View File

@@ -37,7 +37,6 @@
#include "BKE_mesh_wrapper.hh"
#include "BKE_subdiv.hh"
#include "BKE_subdiv_deform.hh"
#include "BKE_subsurf.hh"
#include "DEG_depsgraph_query.hh"

View File

@@ -1443,37 +1443,6 @@ void BKE_subdiv_ccg_neighbor_coords_get(const SubdivCCG &subdiv_ccg,
#endif
}
const int *BKE_subdiv_ccg_start_face_grid_index_ensure(SubdivCCG &subdiv_ccg)
{
#ifdef WITH_OPENSUBDIV
if (subdiv_ccg.cache_.start_face_grid_index.is_empty()) {
const Subdiv *subdiv = subdiv_ccg.subdiv;
const blender::opensubdiv::TopologyRefinerImpl *topology_refiner = subdiv->topology_refiner;
if (topology_refiner == nullptr) {
return nullptr;
}
const int num_coarse_faces = topology_refiner->base_level().GetNumFaces();
subdiv_ccg.cache_.start_face_grid_index.reinitialize(num_coarse_faces);
int start_grid_index = 0;
for (int face_index = 0; face_index < num_coarse_faces; face_index++) {
const int num_face_grids = topology_refiner->base_level().GetFaceVertices(face_index).size();
subdiv_ccg.cache_.start_face_grid_index[face_index] = start_grid_index;
start_grid_index += num_face_grids;
}
}
#endif
return subdiv_ccg.cache_.start_face_grid_index.data();
}
const int *BKE_subdiv_ccg_start_face_grid_index_get(const SubdivCCG &subdiv_ccg)
{
return subdiv_ccg.cache_.start_face_grid_index.data();
}
static void adjacent_vertices_index_from_adjacent_edge(const SubdivCCG &subdiv_ccg,
const SubdivCCGCoord &coord,
const blender::Span<int> corner_verts,
@@ -1600,15 +1569,6 @@ static void subdiv_ccg_coord_to_ptex_coord(const SubdivCCG &subdiv_ccg,
}
}
float3 BKE_subdiv_ccg_eval_limit_point(const SubdivCCG &subdiv_ccg, const SubdivCCGCoord &coord)
{
Subdiv *subdiv = subdiv_ccg.subdiv;
int ptex_face_index;
float u, v;
subdiv_ccg_coord_to_ptex_coord(subdiv_ccg, coord, ptex_face_index, u, v);
return eval_limit_point(subdiv, ptex_face_index, u, v);
}
void BKE_subdiv_ccg_eval_limit_positions(const SubdivCCG &subdiv_ccg,
const CCGKey &key,
const int grid_index,
@@ -1621,7 +1581,12 @@ void BKE_subdiv_ccg_eval_limit_positions(const SubdivCCG &subdiv_ccg,
const int i = CCG_grid_xy_to_index(key.grid_size, x, y);
coord.x = x;
coord.y = y;
r_limit_positions[i] = BKE_subdiv_ccg_eval_limit_point(subdiv_ccg, coord);
int ptex_face_index;
float u, v;
subdiv_ccg_coord_to_ptex_coord(subdiv_ccg, coord, ptex_face_index, u, v);
r_limit_positions[i] = eval_limit_point(subdiv_ccg.subdiv, ptex_face_index, u, v);
}
}
}

View File

@@ -4,8 +4,8 @@
#include "testing/testing.h"
#include "BKE_ccg.hh"
#include "BKE_subdiv_ccg.hh"
#include "BKE_subsurf.hh"
namespace blender::bke::tests {
TEST(subdiv_ccg_coord, to_index)
@@ -20,7 +20,7 @@ TEST(subdiv_ccg_coord, to_index)
key.normal_offset = -1;
key.mask_offset = -1;
key.grid_size = BKE_ccg_gridsize(key.level); /* 3 */
key.grid_size = CCG_grid_size(key.level); /* 3 */
key.grid_area = key.grid_size * key.grid_size; /* 9 */
key.grid_bytes = key.grid_area * key.elem_size;
@@ -46,7 +46,7 @@ TEST(subdiv_ccg_coord, constructor)
key.normal_offset = -1;
key.mask_offset = -1;
key.grid_size = BKE_ccg_gridsize(key.level); /* 3 */
key.grid_size = CCG_grid_size(key.level); /* 3 */
key.grid_area = key.grid_size * key.grid_size; /* 9 */
key.grid_bytes = key.grid_area * key.elem_size;

View File

@@ -28,7 +28,7 @@ BLI_INLINE void ptex_face_uv_to_grid_uv(const float ptex_u,
BLI_INLINE float2 ptex_face_uv_to_grid_uv(const float2 &ptex_uv)
{
return 1.0f - float2(ptex_uv.y, ptex_uv.x);
return float2(1.0f - ptex_uv.y, 1.0f - ptex_uv.x);
}
BLI_INLINE void grid_uv_to_ptex_face_uv(const float grid_u,

View File

@@ -1,1753 +0,0 @@
/* SPDX-FileCopyrightText: 2005 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup bke
*/
#include <cfloat>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include "atomic_ops.h"
#include "MEM_guardedalloc.h"
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
#include "DNA_modifier_types.h"
#include "DNA_object_types.h"
#include "DNA_scene_types.h"
#include "BLI_listbase.h"
#include "BLI_math_vector.h"
#include "BLI_memarena.h"
#include "BLI_ordered_edge.hh"
#include "BLI_set.hh"
#include "BLI_task.h"
#include "BLI_threads.h"
#include "BLI_utildefines.h"
#include "BLI_vector.hh"
#include "BKE_ccg.hh"
#include "BKE_customdata.hh"
#include "BKE_mesh_legacy_derived_mesh.hh"
#include "BKE_mesh_mapping.hh"
#include "BKE_multires.hh"
#include "BKE_scene.hh"
#include "BKE_subsurf.hh"
#include "CCGSubSurf.h"
static CCGDerivedMesh *getCCGDerivedMesh(CCGSubSurf *ss,
int drawInteriorEdges,
int useSubsurfUv,
DerivedMesh *dm);
///
static void *arena_alloc(CCGAllocatorHDL a, int numBytes)
{
return BLI_memarena_alloc(reinterpret_cast<MemArena *>(a), numBytes);
}
static void *arena_realloc(CCGAllocatorHDL a, void *ptr, int newSize, int oldSize)
{
void *p2 = BLI_memarena_alloc(reinterpret_cast<MemArena *>(a), newSize);
if (ptr) {
memcpy(p2, ptr, oldSize);
}
return p2;
}
static void arena_free(CCGAllocatorHDL /*a*/, void * /*ptr*/)
{
/* do nothing */
}
static void arena_release(CCGAllocatorHDL a)
{
BLI_memarena_free(reinterpret_cast<MemArena *>(a));
}
enum CCGFlags {
CCG_USE_AGING = 1,
CCG_USE_ARENA = 2,
CCG_CALC_NORMALS = 4,
/* add an extra four bytes for a mask layer */
CCG_ALLOC_MASK = 8,
CCG_SIMPLE_SUBDIV = 16,
};
ENUM_OPERATORS(CCGFlags, CCG_SIMPLE_SUBDIV);
static CCGSubSurf *_getSubSurf(CCGSubSurf *prevSS, int subdivLevels, int numLayers, CCGFlags flags)
{
CCGMeshIFC ifc;
CCGSubSurf *ccgSS;
int useAging = !!(flags & CCG_USE_AGING);
int useArena = flags & CCG_USE_ARENA;
int normalOffset = 0;
/* (subdivLevels == 0) is not allowed */
subdivLevels = std::max(subdivLevels, 1);
if (prevSS) {
int oldUseAging;
ccgSubSurf_getUseAgeCounts(prevSS, &oldUseAging, nullptr, nullptr, nullptr);
if ((oldUseAging != useAging) ||
(ccgSubSurf_getSimpleSubdiv(prevSS) != !!(flags & CCG_SIMPLE_SUBDIV)))
{
ccgSubSurf_free(prevSS);
}
else {
ccgSubSurf_setSubdivisionLevels(prevSS, subdivLevels);
return prevSS;
}
}
if (useAging) {
ifc.vertUserSize = ifc.edgeUserSize = ifc.faceUserSize = 12;
}
else {
ifc.vertUserSize = ifc.edgeUserSize = ifc.faceUserSize = 8;
}
ifc.numLayers = numLayers;
ifc.vertDataSize = sizeof(float) * numLayers;
normalOffset += sizeof(float) * numLayers;
if (flags & CCG_CALC_NORMALS) {
ifc.vertDataSize += sizeof(float[3]);
}
if (flags & CCG_ALLOC_MASK) {
ifc.vertDataSize += sizeof(float);
}
ifc.simpleSubdiv = !!(flags & CCG_SIMPLE_SUBDIV);
if (useArena) {
CCGAllocatorIFC allocatorIFC;
CCGAllocatorHDL allocator = BLI_memarena_new(MEM_SIZE_OPTIMAL(1 << 16), "subsurf arena");
allocatorIFC.alloc = arena_alloc;
allocatorIFC.realloc = arena_realloc;
allocatorIFC.free = arena_free;
allocatorIFC.release = arena_release;
ccgSS = ccgSubSurf_new(&ifc, subdivLevels, &allocatorIFC, allocator);
}
else {
ccgSS = ccgSubSurf_new(&ifc, subdivLevels, nullptr, nullptr);
}
if (useAging) {
ccgSubSurf_setUseAgeCounts(ccgSS, 1, 8, 8, 8);
}
if (flags & CCG_ALLOC_MASK) {
normalOffset += sizeof(float);
/* mask is allocated after regular layers */
ccgSubSurf_setAllocMask(ccgSS, 1, sizeof(float) * numLayers);
}
if (flags & CCG_CALC_NORMALS) {
ccgSubSurf_setCalcVertexNormals(ccgSS, 1, normalOffset);
}
else {
ccgSubSurf_setCalcVertexNormals(ccgSS, 0, 0);
}
return ccgSS;
}
static int getEdgeIndex(CCGSubSurf *ss, CCGEdge *e, int x, int edgeSize)
{
CCGVert *v0 = ccgSubSurf_getEdgeVert0(e);
CCGVert *v1 = ccgSubSurf_getEdgeVert1(e);
int v0idx = *((int *)ccgSubSurf_getVertUserData(ss, v0));
int v1idx = *((int *)ccgSubSurf_getVertUserData(ss, v1));
int edgeBase = *((int *)ccgSubSurf_getEdgeUserData(ss, e));
if (x == 0) {
return v0idx;
}
if (x == edgeSize - 1) {
return v1idx;
}
return edgeBase + x - 1;
}
static int getFaceIndex(
CCGSubSurf *ss, CCGFace *f, int S, int x, int y, int edgeSize, int gridSize)
{
int faceBase = *((int *)ccgSubSurf_getFaceUserData(ss, f));
int numVerts = ccgSubSurf_getFaceNumVerts(f);
if (x == gridSize - 1 && y == gridSize - 1) {
CCGVert *v = ccgSubSurf_getFaceVert(f, S);
return *((int *)ccgSubSurf_getVertUserData(ss, v));
}
if (x == gridSize - 1) {
CCGVert *v = ccgSubSurf_getFaceVert(f, S);
CCGEdge *e = ccgSubSurf_getFaceEdge(f, S);
int edgeBase = *((int *)ccgSubSurf_getEdgeUserData(ss, e));
if (v == ccgSubSurf_getEdgeVert0(e)) {
return edgeBase + (gridSize - 1 - y) - 1;
}
return edgeBase + (edgeSize - 2 - 1) - ((gridSize - 1 - y) - 1);
}
if (y == gridSize - 1) {
CCGVert *v = ccgSubSurf_getFaceVert(f, S);
CCGEdge *e = ccgSubSurf_getFaceEdge(f, (S + numVerts - 1) % numVerts);
int edgeBase = *((int *)ccgSubSurf_getEdgeUserData(ss, e));
if (v == ccgSubSurf_getEdgeVert0(e)) {
return edgeBase + (gridSize - 1 - x) - 1;
}
return edgeBase + (edgeSize - 2 - 1) - ((gridSize - 1 - x) - 1);
}
if (x == 0 && y == 0) {
return faceBase;
}
if (x == 0) {
S = (S + numVerts - 1) % numVerts;
return faceBase + 1 + (gridSize - 2) * S + (y - 1);
}
if (y == 0) {
return faceBase + 1 + (gridSize - 2) * S + (x - 1);
}
return faceBase + 1 + (gridSize - 2) * numVerts + S * (gridSize - 2) * (gridSize - 2) +
(y - 1) * (gridSize - 2) + (x - 1);
}
static void get_face_uv_map_vert(UvVertMap *vmap,
const blender::OffsetIndices<int> faces,
const int *face_verts,
int fi,
CCGVertHDL *fverts)
{
UvMapVert *v, *nv;
int j, nverts = faces[fi].size();
for (j = 0; j < nverts; j++) {
for (nv = v = BKE_mesh_uv_vert_map_get_vert(vmap, face_verts[j]); v; v = v->next) {
if (v->separate) {
nv = v;
}
if (v->face_index == fi) {
break;
}
}
fverts[j] = POINTER_FROM_UINT(faces[nv->face_index].start() + nv->loop_of_face_index);
}
}
static int ss_sync_from_uv(CCGSubSurf *ss,
CCGSubSurf *origss,
DerivedMesh *dm,
const float (*mloopuv)[2])
{
const blender::OffsetIndices faces(blender::Span(dm->getPolyArray(dm), dm->getNumPolys(dm) + 1));
int *corner_verts = dm->getCornerVertArray(dm);
int totvert = dm->getNumVerts(dm);
int totface = dm->getNumPolys(dm);
int i, seam;
UvMapVert *v;
UvVertMap *vmap;
blender::Vector<CCGVertHDL, 16> fverts;
float uv[3] = {0.0f, 0.0f, 0.0f}; /* only first 2 values are written into */
const int corners_num = faces.total_size();
/* previous behavior here is without accounting for winding, however this causes stretching in
* UV map in really simple cases with mirror + subsurf, see second part of #44530.
* Also, initially intention is to treat merged vertices from mirror modifier as seams.
* This fixes a very old regression (2.49 was correct here) */
vmap = BKE_mesh_uv_vert_map_create(
faces,
{corner_verts, corners_num},
{reinterpret_cast<const blender::float2 *>(mloopuv), corners_num},
totvert,
blender::float2(STD_UV_CONNECT_LIMIT),
true);
if (!vmap) {
return 0;
}
ccgSubSurf_initFullSync(ss);
/* create vertices */
for (i = 0; i < totvert; i++) {
if (!BKE_mesh_uv_vert_map_get_vert(vmap, i)) {
continue;
}
for (v = BKE_mesh_uv_vert_map_get_vert(vmap, i)->next; v; v = v->next) {
if (v->separate) {
break;
}
}
seam = (v != nullptr);
for (v = BKE_mesh_uv_vert_map_get_vert(vmap, i); v; v = v->next) {
if (v->separate) {
CCGVert *ssv;
int loopid = faces[v->face_index].start() + v->loop_of_face_index;
CCGVertHDL vhdl = POINTER_FROM_INT(loopid);
copy_v2_v2(uv, mloopuv[loopid]);
ccgSubSurf_syncVert(ss, vhdl, uv, seam, &ssv);
}
}
}
/* create edges */
blender::Set<blender::OrderedEdge> eset;
eset.reserve(totface);
for (i = 0; i < totface; i++) {
const blender::IndexRange face = faces[i];
int nverts = face.size();
int j, j_next;
CCGFace *origf = ccgSubSurf_getFace(origss, POINTER_FROM_INT(i));
// uint *fv = &face.v1;
fverts.reinitialize(nverts);
get_face_uv_map_vert(vmap, faces, &corner_verts[face.start()], i, fverts.data());
for (j = 0, j_next = nverts - 1; j < nverts; j_next = j++) {
uint v0 = POINTER_AS_UINT(fverts[j_next]);
uint v1 = POINTER_AS_UINT(fverts[j]);
if (eset.add({v0, v1})) {
CCGEdge *e, *orige = ccgSubSurf_getFaceEdge(origf, j_next);
CCGEdgeHDL ehdl = POINTER_FROM_INT(face[j_next]);
float crease = ccgSubSurf_getEdgeCrease(orige);
ccgSubSurf_syncEdge(ss, ehdl, fverts[j_next], fverts[j], crease, &e);
}
}
}
/* create faces */
for (i = 0; i < totface; i++) {
const blender::IndexRange face = faces[i];
int nverts = face.size();
CCGFace *f;
fverts.reinitialize(nverts);
get_face_uv_map_vert(vmap, faces, &corner_verts[face.start()], i, fverts.data());
ccgSubSurf_syncFace(ss, POINTER_FROM_INT(i), nverts, fverts.data(), &f);
}
BKE_mesh_uv_vert_map_free(vmap);
ccgSubSurf_processSync(ss);
return 1;
}
static void set_subsurf_legacy_uv(CCGSubSurf *ss, DerivedMesh *dm, DerivedMesh *result, int n)
{
CCGFaceIterator fi;
int index, gridSize, gridFaces, /*edgeSize,*/ totface, x, y, S;
const float(*dmloopuv)[2] = static_cast<const float(*)[2]>(
CustomData_get_layer_n(&dm->loopData, CD_PROP_FLOAT2, n));
/* need to update both CD_MTFACE & CD_PROP_FLOAT2, hrmf, we could get away with
* just tface except applying the modifier then looses subsurf UV */
MTFace *tface = static_cast<MTFace *>(
CustomData_get_layer_n_for_write(&result->faceData, CD_MTFACE, n, result->numTessFaceData));
float(*mloopuv)[2] = static_cast<float(*)[2]>(CustomData_get_layer_n_for_write(
&result->loopData, CD_PROP_FLOAT2, n, result->getNumLoops(result)));
if (!dmloopuv || (!tface && !mloopuv)) {
return;
}
/* create a CCGSubSurf from uv's */
CCGSubSurf *uvss = _getSubSurf(nullptr, ccgSubSurf_getSubdivisionLevels(ss), 2, CCG_USE_ARENA);
if (!ss_sync_from_uv(uvss, ss, dm, dmloopuv)) {
ccgSubSurf_free(uvss);
return;
}
/* get some info from CCGSubSurf */
totface = ccgSubSurf_getNumFaces(uvss);
// edgeSize = ccgSubSurf_getEdgeSize(uvss); /* UNUSED */
gridSize = ccgSubSurf_getGridSize(uvss);
gridFaces = gridSize - 1;
/* make a map from original faces to CCGFaces */
CCGFace **faceMap = MEM_malloc_arrayN<CCGFace *>(size_t(totface), "facemapuv");
for (ccgSubSurf_initFaceIterator(uvss, &fi); !ccgFaceIterator_isStopped(&fi);
ccgFaceIterator_next(&fi))
{
CCGFace *f = ccgFaceIterator_getCurrent(&fi);
faceMap[POINTER_AS_INT(ccgSubSurf_getFaceFaceHandle(f))] = f;
}
/* load coordinates from uvss into tface */
MTFace *tf = tface;
float(*mluv)[2] = mloopuv;
for (index = 0; index < totface; index++) {
CCGFace *f = faceMap[index];
int numVerts = ccgSubSurf_getFaceNumVerts(f);
for (S = 0; S < numVerts; S++) {
float(*faceGridData)[2] = static_cast<float(*)[2]>(
ccgSubSurf_getFaceGridDataArray(uvss, f, S));
for (y = 0; y < gridFaces; y++) {
for (x = 0; x < gridFaces; x++) {
float *a = faceGridData[(y + 0) * gridSize + x + 0];
float *b = faceGridData[(y + 0) * gridSize + x + 1];
float *c = faceGridData[(y + 1) * gridSize + x + 1];
float *d = faceGridData[(y + 1) * gridSize + x + 0];
if (tf) {
copy_v2_v2(tf->uv[0], a);
copy_v2_v2(tf->uv[1], d);
copy_v2_v2(tf->uv[2], c);
copy_v2_v2(tf->uv[3], b);
tf++;
}
if (mluv) {
copy_v2_v2(mluv[0], a);
copy_v2_v2(mluv[1], d);
copy_v2_v2(mluv[2], c);
copy_v2_v2(mluv[3], b);
mluv += 4;
}
}
}
}
}
ccgSubSurf_free(uvss);
MEM_freeN(faceMap);
}
static void set_subsurf_uv(CCGSubSurf *ss, DerivedMesh *dm, DerivedMesh *result, int layer_index)
{
set_subsurf_legacy_uv(ss, dm, result, layer_index);
}
/* face weighting */
#define SUB_ELEMS_FACE 50
typedef float FaceVertWeight[SUB_ELEMS_FACE][SUB_ELEMS_FACE];
struct FaceVertWeightEntry {
FaceVertWeight *weight;
float *w;
int valid;
};
struct WeightTable {
FaceVertWeightEntry *weight_table;
int len;
};
static float *get_ss_weights(WeightTable *wtable, int gridCuts, int faceLen)
{
int x, y, i, j;
float *w, w1, w2, w4, fac, fac2, fx, fy;
if (wtable->len <= faceLen) {
void *tmp = MEM_calloc_arrayN<FaceVertWeightEntry>(size_t(faceLen) + 1,
"weight table alloc 2");
if (wtable->len) {
memcpy(tmp, wtable->weight_table, sizeof(FaceVertWeightEntry) * wtable->len);
MEM_freeN(wtable->weight_table);
}
wtable->weight_table = static_cast<FaceVertWeightEntry *>(tmp);
wtable->len = faceLen + 1;
}
if (!wtable->weight_table[faceLen].valid) {
wtable->weight_table[faceLen].valid = 1;
wtable->weight_table[faceLen].w = w = MEM_calloc_arrayN<float>(
size_t(faceLen) * size_t(faceLen) * size_t(gridCuts + 2) * size_t(gridCuts + 2),
"weight table alloc");
fac = 1.0f / float(faceLen);
for (i = 0; i < faceLen; i++) {
for (x = 0; x < gridCuts + 2; x++) {
for (y = 0; y < gridCuts + 2; y++) {
fx = 0.5f - float(x) / float(gridCuts + 1) / 2.0f;
fy = 0.5f - float(y) / float(gridCuts + 1) / 2.0f;
fac2 = faceLen - 4;
w1 = (1.0f - fx) * (1.0f - fy) + (-fac2 * fx * fy * fac);
w2 = (1.0f - fx + fac2 * fx * -fac) * (fy);
w4 = (fx) * (1.0f - fy + -fac2 * fy * fac);
/* These values aren't used for triangles and cause divide by zero. */
if (faceLen > 3) {
fac2 = 1.0f - (w1 + w2 + w4);
fac2 = fac2 / float(faceLen - 3);
for (j = 0; j < faceLen; j++) {
w[j] = fac2;
}
}
w[i] = w1;
w[(i - 1 + faceLen) % faceLen] = w2;
w[(i + 1) % faceLen] = w4;
w += faceLen;
}
}
}
}
return wtable->weight_table[faceLen].w;
}
static void free_ss_weights(WeightTable *wtable)
{
int i;
for (i = 0; i < wtable->len; i++) {
if (wtable->weight_table[i].valid) {
MEM_freeN(wtable->weight_table[i].w);
}
}
if (wtable->weight_table) {
MEM_freeN(wtable->weight_table);
}
}
static void ss_sync_ccg_from_derivedmesh(CCGSubSurf *ss,
DerivedMesh *dm,
float (*vertexCos)[3],
int useFlatSubdiv)
{
float creaseFactor = float(ccgSubSurf_getSubdivisionLevels(ss));
blender::Vector<CCGVertHDL, 16> fverts;
float(*positions)[3] = (float(*)[3])dm->getVertArray(dm);
const blender::int2 *edges = reinterpret_cast<const blender::int2 *>(dm->getEdgeArray(dm));
int *corner_verts = dm->getCornerVertArray(dm);
const blender::OffsetIndices faces(blender::Span(dm->getPolyArray(dm), dm->getNumPolys(dm) + 1));
int totvert = dm->getNumVerts(dm);
int totedge = dm->getNumEdges(dm);
int i, j;
int *index;
ccgSubSurf_initFullSync(ss);
index = (int *)dm->getVertDataArray(dm, CD_ORIGINDEX);
for (i = 0; i < totvert; i++) {
CCGVert *v;
if (vertexCos) {
ccgSubSurf_syncVert(ss, POINTER_FROM_INT(i), vertexCos[i], 0, &v);
}
else {
ccgSubSurf_syncVert(ss, POINTER_FROM_INT(i), positions[i], 0, &v);
}
((int *)ccgSubSurf_getVertUserData(ss, v))[1] = (index) ? *index++ : i;
}
index = (int *)dm->getEdgeDataArray(dm, CD_ORIGINDEX);
const float *creases = (const float *)CustomData_get_layer_named(
&dm->edgeData, CD_PROP_FLOAT, "crease_edge");
for (i = 0; i < totedge; i++) {
CCGEdge *e;
float crease;
crease = useFlatSubdiv ? creaseFactor : (creases ? creases[i] * creaseFactor : 0.0f);
ccgSubSurf_syncEdge(ss,
POINTER_FROM_INT(i),
POINTER_FROM_UINT(edges[i][0]),
POINTER_FROM_UINT(edges[i][1]),
crease,
&e);
((int *)ccgSubSurf_getEdgeUserData(ss, e))[1] = (index) ? *index++ : i;
}
index = (int *)dm->getPolyDataArray(dm, CD_ORIGINDEX);
for (i = 0; i < dm->numPolyData; i++) {
const blender::IndexRange face = faces[i];
CCGFace *f;
fverts.reinitialize(face.size());
for (j = 0; j < face.size(); j++) {
fverts[j] = POINTER_FROM_UINT(corner_verts[face[j]]);
}
/* This is very bad, means mesh is internally inconsistent.
* it is not really possible to continue without modifying
* other parts of code significantly to handle missing faces.
* since this really shouldn't even be possible we just bail. */
if (ccgSubSurf_syncFace(ss, POINTER_FROM_INT(i), face.size(), fverts.data(), &f) ==
eCCGError_InvalidValue)
{
static int hasGivenError = 0;
if (!hasGivenError) {
// XXX error("Unrecoverable error in SubSurf calculation,"
// " mesh is inconsistent.");
hasGivenError = 1;
}
return;
}
((int *)ccgSubSurf_getFaceUserData(ss, f))[1] = (index) ? *index++ : i;
}
ccgSubSurf_processSync(ss);
}
static void ss_sync_from_derivedmesh(CCGSubSurf *ss,
DerivedMesh *dm,
float (*vertexCos)[3],
int use_flat_subdiv,
bool /*use_subdiv_uvs*/)
{
ss_sync_ccg_from_derivedmesh(ss, dm, vertexCos, use_flat_subdiv);
}
/***/
static int ccgDM_getVertMapIndex(CCGSubSurf *ss, CCGVert *v)
{
return ((int *)ccgSubSurf_getVertUserData(ss, v))[1];
}
static int ccgDM_getEdgeMapIndex(CCGSubSurf *ss, CCGEdge *e)
{
return ((int *)ccgSubSurf_getEdgeUserData(ss, e))[1];
}
static int ccgDM_getFaceMapIndex(CCGSubSurf *ss, CCGFace *f)
{
return ((int *)ccgSubSurf_getFaceUserData(ss, f))[1];
}
static int ccgDM_getNumVerts(DerivedMesh *dm)
{
CCGDerivedMesh *ccgdm = (CCGDerivedMesh *)dm;
return ccgSubSurf_getNumFinalVerts(ccgdm->ss);
}
static int ccgDM_getNumEdges(DerivedMesh *dm)
{
CCGDerivedMesh *ccgdm = (CCGDerivedMesh *)dm;
return ccgSubSurf_getNumFinalEdges(ccgdm->ss);
}
static int ccgDM_getNumPolys(DerivedMesh *dm)
{
CCGDerivedMesh *ccgdm = (CCGDerivedMesh *)dm;
return ccgSubSurf_getNumFinalFaces(ccgdm->ss);
}
static int ccgDM_getNumLoops(DerivedMesh *dm)
{
CCGDerivedMesh *ccgdm = (CCGDerivedMesh *)dm;
/* All subsurf faces are quads */
return 4 * ccgSubSurf_getNumFinalFaces(ccgdm->ss);
}
/* utility function */
BLI_INLINE void ccgDM_to_MVert(float mv[3], const CCGKey *key, CCGElem *elem)
{
copy_v3_v3(mv, CCG_elem_co(*key, elem));
}
static void ccgDM_copyFinalVertArray(DerivedMesh *dm, float (*r_positions)[3])
{
CCGDerivedMesh *ccgdm = (CCGDerivedMesh *)dm;
CCGSubSurf *ss = ccgdm->ss;
CCGElem *vd;
CCGKey key;
int index;
int totvert, totedge, totface;
int gridSize = ccgSubSurf_getGridSize(ss);
int edgeSize = ccgSubSurf_getEdgeSize(ss);
uint i = 0;
CCG_key_top_level(&key, ss);
totface = ccgSubSurf_getNumFaces(ss);
for (index = 0; index < totface; index++) {
CCGFace *f = ccgdm->faceMap[index].face;
int x, y, S, numVerts = ccgSubSurf_getFaceNumVerts(f);
vd = static_cast<CCGElem *>(ccgSubSurf_getFaceCenterData(f));
ccgDM_to_MVert(r_positions[i++], &key, vd);
for (S = 0; S < numVerts; S++) {
for (x = 1; x < gridSize - 1; x++) {
vd = static_cast<CCGElem *>(ccgSubSurf_getFaceGridEdgeData(ss, f, S, x));
ccgDM_to_MVert(r_positions[i++], &key, vd);
}
}
for (S = 0; S < numVerts; S++) {
for (y = 1; y < gridSize - 1; y++) {
for (x = 1; x < gridSize - 1; x++) {
vd = static_cast<CCGElem *>(ccgSubSurf_getFaceGridData(ss, f, S, x, y));
ccgDM_to_MVert(r_positions[i++], &key, vd);
}
}
}
}
totedge = ccgSubSurf_getNumEdges(ss);
for (index = 0; index < totedge; index++) {
CCGEdge *e = ccgdm->edgeMap[index].edge;
int x;
for (x = 1; x < edgeSize - 1; x++) {
/* NOTE(@ideasman42): This gives errors with `--debug-fpe` the normals don't seem to be
* unit length. This is most likely caused by edges with no faces which are now zeroed out,
* see comment in: `ccgSubSurf__calcVertNormals()`. */
vd = static_cast<CCGElem *>(ccgSubSurf_getEdgeData(ss, e, x));
ccgDM_to_MVert(r_positions[i++], &key, vd);
}
}
totvert = ccgSubSurf_getNumVerts(ss);
for (index = 0; index < totvert; index++) {
CCGVert *v = ccgdm->vertMap[index].vert;
vd = static_cast<CCGElem *>(ccgSubSurf_getVertData(ss, v));
ccgDM_to_MVert(r_positions[i++], &key, vd);
}
}
/* utility function */
BLI_INLINE void ccgDM_to_MEdge(blender::int2 *edge, const int v1, const int v2)
{
edge->x = v1;
edge->y = v2;
}
static void ccgDM_copyFinalEdgeArray(DerivedMesh *dm, blender::int2 *edges)
{
CCGDerivedMesh *ccgdm = (CCGDerivedMesh *)dm;
CCGSubSurf *ss = ccgdm->ss;
int index;
int totedge, totface;
int gridSize = ccgSubSurf_getGridSize(ss);
int edgeSize = ccgSubSurf_getEdgeSize(ss);
uint i = 0;
totface = ccgSubSurf_getNumFaces(ss);
for (index = 0; index < totface; index++) {
CCGFace *f = ccgdm->faceMap[index].face;
int x, y, S, numVerts = ccgSubSurf_getFaceNumVerts(f);
for (S = 0; S < numVerts; S++) {
for (x = 0; x < gridSize - 1; x++) {
ccgDM_to_MEdge(&edges[i++],
getFaceIndex(ss, f, S, x, 0, edgeSize, gridSize),
getFaceIndex(ss, f, S, x + 1, 0, edgeSize, gridSize));
}
for (x = 1; x < gridSize - 1; x++) {
for (y = 0; y < gridSize - 1; y++) {
ccgDM_to_MEdge(&edges[i++],
getFaceIndex(ss, f, S, x, y, edgeSize, gridSize),
getFaceIndex(ss, f, S, x, y + 1, edgeSize, gridSize));
ccgDM_to_MEdge(&edges[i++],
getFaceIndex(ss, f, S, y, x, edgeSize, gridSize),
getFaceIndex(ss, f, S, y + 1, x, edgeSize, gridSize));
}
}
}
}
totedge = ccgSubSurf_getNumEdges(ss);
for (index = 0; index < totedge; index++) {
CCGEdge *e = ccgdm->edgeMap[index].edge;
for (int x = 0; x < edgeSize - 1; x++) {
ccgDM_to_MEdge(
&edges[i++], getEdgeIndex(ss, e, x, edgeSize), getEdgeIndex(ss, e, x + 1, edgeSize));
}
}
}
struct CopyFinalLoopArrayData {
CCGDerivedMesh *ccgdm;
int *corner_verts;
int *corner_edges;
int grid_size;
int *grid_offset;
int edge_size;
};
static void copyFinalLoopArray_task_cb(void *__restrict userdata,
const int iter,
const TaskParallelTLS *__restrict /*tls*/)
{
CopyFinalLoopArrayData *data = static_cast<CopyFinalLoopArrayData *>(userdata);
CCGDerivedMesh *ccgdm = data->ccgdm;
CCGSubSurf *ss = ccgdm->ss;
const int grid_size = data->grid_size;
const int edge_size = data->edge_size;
CCGFace *f = ccgdm->faceMap[iter].face;
const int num_verts = ccgSubSurf_getFaceNumVerts(f);
const int grid_index = data->grid_offset[iter];
int *corner_verts = data->corner_verts;
int *corner_edges = data->corner_edges;
size_t loop_i = 4 * size_t(grid_index) * (grid_size - 1) * (grid_size - 1);
for (int S = 0; S < num_verts; S++) {
for (int y = 0; y < grid_size - 1; y++) {
for (int x = 0; x < grid_size - 1; x++) {
const int v1 = getFaceIndex(ss, f, S, x + 0, y + 0, edge_size, grid_size);
const int v2 = getFaceIndex(ss, f, S, x + 0, y + 1, edge_size, grid_size);
const int v3 = getFaceIndex(ss, f, S, x + 1, y + 1, edge_size, grid_size);
const int v4 = getFaceIndex(ss, f, S, x + 1, y + 0, edge_size, grid_size);
if (corner_verts) {
corner_verts[loop_i + 0] = v1;
corner_verts[loop_i + 1] = v2;
corner_verts[loop_i + 2] = v3;
corner_verts[loop_i + 3] = v4;
}
if (corner_edges) {
corner_edges[loop_i + 0] = ccgdm->ehash->index_of({v1, v2});
corner_edges[loop_i + 1] = ccgdm->ehash->index_of({v2, v3});
corner_edges[loop_i + 2] = ccgdm->ehash->index_of({v3, v4});
corner_edges[loop_i + 3] = ccgdm->ehash->index_of({v4, v1});
}
loop_i += 4;
}
}
}
}
static void ccgDM_copyFinalCornerVertArray(DerivedMesh *dm, int *r_corner_verts)
{
CCGDerivedMesh *ccgdm = (CCGDerivedMesh *)dm;
CCGSubSurf *ss = ccgdm->ss;
CopyFinalLoopArrayData data;
data.ccgdm = ccgdm;
data.corner_verts = r_corner_verts;
data.corner_edges = nullptr;
data.grid_size = ccgSubSurf_getGridSize(ss);
data.grid_offset = dm->getGridOffset(dm);
data.edge_size = ccgSubSurf_getEdgeSize(ss);
TaskParallelSettings settings;
BLI_parallel_range_settings_defaults(&settings);
settings.min_iter_per_thread = 1;
BLI_task_parallel_range(
0, ccgSubSurf_getNumFaces(ss), &data, copyFinalLoopArray_task_cb, &settings);
}
static void ccgDM_copyFinalCornerEdgeArray(DerivedMesh *dm, int *r_corner_edges)
{
CCGDerivedMesh *ccgdm = (CCGDerivedMesh *)dm;
CCGSubSurf *ss = ccgdm->ss;
if (!ccgdm->ehash) {
BLI_mutex_lock(&ccgdm->loops_cache_lock);
if (!ccgdm->ehash) {
auto *ehash = new blender::VectorSet<blender::OrderedEdge>();
ehash->reserve(ccgdm->dm.numEdgeData);
const blender::int2 *medge = reinterpret_cast<const blender::int2 *>(
ccgdm->dm.getEdgeArray((DerivedMesh *)ccgdm));
for (int i = 0; i < ccgdm->dm.numEdgeData; i++) {
ehash->add({medge[i][0], medge[i][1]});
}
atomic_cas_ptr((void **)&ccgdm->ehash, ccgdm->ehash, ehash);
}
BLI_mutex_unlock(&ccgdm->loops_cache_lock);
}
CopyFinalLoopArrayData data;
data.ccgdm = ccgdm;
data.corner_verts = nullptr;
data.corner_edges = r_corner_edges;
data.grid_size = ccgSubSurf_getGridSize(ss);
data.grid_offset = dm->getGridOffset(dm);
data.edge_size = ccgSubSurf_getEdgeSize(ss);
TaskParallelSettings settings;
BLI_parallel_range_settings_defaults(&settings);
settings.min_iter_per_thread = 1;
BLI_task_parallel_range(
0, ccgSubSurf_getNumFaces(ss), &data, copyFinalLoopArray_task_cb, &settings);
}
static void ccgDM_copyFinalPolyArray(DerivedMesh *dm, int *r_face_offsets)
{
CCGDerivedMesh *ccgdm = (CCGDerivedMesh *)dm;
CCGSubSurf *ss = ccgdm->ss;
int index;
int totface;
int gridSize = ccgSubSurf_getGridSize(ss);
// int edgeSize = ccgSubSurf_getEdgeSize(ss); /* UNUSED. */
int i = 0, k = 0;
totface = ccgSubSurf_getNumFaces(ss);
for (index = 0; index < totface; index++) {
CCGFace *f = ccgdm->faceMap[index].face;
int x, y, S, numVerts = ccgSubSurf_getFaceNumVerts(f);
for (S = 0; S < numVerts; S++) {
for (y = 0; y < gridSize - 1; y++) {
for (x = 0; x < gridSize - 1; x++) {
r_face_offsets[i] = k;
k += 4;
i++;
}
}
}
}
r_face_offsets[i] = k;
}
static void ccgDM_release(DerivedMesh *dm)
{
CCGDerivedMesh *ccgdm = (CCGDerivedMesh *)dm;
DM_release(dm);
delete ccgdm->ehash;
if (ccgdm->gridFaces) {
MEM_freeN(ccgdm->gridFaces);
}
if (ccgdm->gridData) {
MEM_freeN(ccgdm->gridData);
}
if (ccgdm->gridOffset) {
MEM_freeN(ccgdm->gridOffset);
}
if (ccgdm->gridHidden) {
/* Using dm->getNumGrids(dm) accesses freed memory */
uint numGrids = ccgdm->numGrid;
for (uint i = 0; i < numGrids; i++) {
if (ccgdm->gridHidden[i]) {
MEM_freeN(ccgdm->gridHidden[i]);
}
}
MEM_freeN(ccgdm->gridHidden);
}
if (ccgdm->freeSS) {
ccgSubSurf_free(ccgdm->ss);
}
MEM_freeN(ccgdm->vertMap);
MEM_freeN(ccgdm->edgeMap);
MEM_freeN(ccgdm->faceMap);
BLI_mutex_end(&ccgdm->loops_cache_lock);
BLI_rw_mutex_end(&ccgdm->origindex_cache_rwlock);
MEM_freeN(ccgdm);
}
static void *ccgDM_get_vert_data_layer(DerivedMesh *dm, const eCustomDataType type)
{
if (type == CD_ORIGINDEX) {
/* create origindex on demand to save memory */
CCGDerivedMesh *ccgdm = (CCGDerivedMesh *)dm;
CCGSubSurf *ss = ccgdm->ss;
int *origindex;
int a, index, totnone, totorig;
/* Avoid re-creation if the layer exists already */
BLI_rw_mutex_lock(&ccgdm->origindex_cache_rwlock, THREAD_LOCK_READ);
origindex = static_cast<int *>(DM_get_vert_data_layer(dm, CD_ORIGINDEX));
BLI_rw_mutex_unlock(&ccgdm->origindex_cache_rwlock);
if (origindex) {
return origindex;
}
BLI_rw_mutex_lock(&ccgdm->origindex_cache_rwlock, THREAD_LOCK_WRITE);
origindex = static_cast<int *>(
CustomData_add_layer(&dm->vertData, CD_ORIGINDEX, CD_SET_DEFAULT, dm->numVertData));
totorig = ccgSubSurf_getNumVerts(ss);
totnone = dm->numVertData - totorig;
/* original vertices are at the end */
for (a = 0; a < totnone; a++) {
origindex[a] = ORIGINDEX_NONE;
}
for (index = 0; index < totorig; index++, a++) {
CCGVert *v = ccgdm->vertMap[index].vert;
origindex[a] = ccgDM_getVertMapIndex(ccgdm->ss, v);
}
BLI_rw_mutex_unlock(&ccgdm->origindex_cache_rwlock);
return origindex;
}
return DM_get_vert_data_layer(dm, type);
}
static void *ccgDM_get_edge_data_layer(DerivedMesh *dm, const eCustomDataType type)
{
if (type == CD_ORIGINDEX) {
/* create origindex on demand to save memory */
CCGDerivedMesh *ccgdm = (CCGDerivedMesh *)dm;
CCGSubSurf *ss = ccgdm->ss;
int *origindex;
int a, i, index, totnone, totorig, totedge;
int edgeSize = ccgSubSurf_getEdgeSize(ss);
/* Avoid re-creation if the layer exists already */
origindex = static_cast<int *>(DM_get_edge_data_layer(dm, CD_ORIGINDEX));
if (origindex) {
return origindex;
}
origindex = static_cast<int *>(
CustomData_add_layer(&dm->edgeData, CD_ORIGINDEX, CD_SET_DEFAULT, dm->numEdgeData));
totedge = ccgSubSurf_getNumEdges(ss);
totorig = totedge * (edgeSize - 1);
totnone = dm->numEdgeData - totorig;
/* original edges are at the end */
for (a = 0; a < totnone; a++) {
origindex[a] = ORIGINDEX_NONE;
}
for (index = 0; index < totedge; index++) {
CCGEdge *e = ccgdm->edgeMap[index].edge;
int mapIndex = ccgDM_getEdgeMapIndex(ss, e);
for (i = 0; i < edgeSize - 1; i++, a++) {
origindex[a] = mapIndex;
}
}
return origindex;
}
return DM_get_edge_data_layer(dm, type);
}
static void *ccgDM_get_poly_data_layer(DerivedMesh *dm, const eCustomDataType type)
{
if (type == CD_ORIGINDEX) {
/* create origindex on demand to save memory */
CCGDerivedMesh *ccgdm = (CCGDerivedMesh *)dm;
CCGSubSurf *ss = ccgdm->ss;
int *origindex;
int a, i, index, totface;
int gridFaces = ccgSubSurf_getGridSize(ss) - 1;
/* Avoid re-creation if the layer exists already */
origindex = static_cast<int *>(DM_get_poly_data_layer(dm, CD_ORIGINDEX));
if (origindex) {
return origindex;
}
origindex = static_cast<int *>(
CustomData_add_layer(&dm->polyData, CD_ORIGINDEX, CD_SET_DEFAULT, dm->numPolyData));
totface = ccgSubSurf_getNumFaces(ss);
for (a = 0, index = 0; index < totface; index++) {
CCGFace *f = ccgdm->faceMap[index].face;
int numVerts = ccgSubSurf_getFaceNumVerts(f);
int mapIndex = ccgDM_getFaceMapIndex(ss, f);
for (i = 0; i < gridFaces * gridFaces * numVerts; i++, a++) {
origindex[a] = mapIndex;
}
}
return origindex;
}
return DM_get_poly_data_layer(dm, type);
}
static int ccgDM_getNumGrids(DerivedMesh *dm)
{
CCGDerivedMesh *ccgdm = (CCGDerivedMesh *)dm;
int index, numFaces, numGrids;
numFaces = ccgSubSurf_getNumFaces(ccgdm->ss);
numGrids = 0;
for (index = 0; index < numFaces; index++) {
CCGFace *f = ccgdm->faceMap[index].face;
numGrids += ccgSubSurf_getFaceNumVerts(f);
}
return numGrids;
}
static int ccgDM_getGridSize(DerivedMesh *dm)
{
CCGDerivedMesh *ccgdm = (CCGDerivedMesh *)dm;
return ccgSubSurf_getGridSize(ccgdm->ss);
}
static void ccgdm_create_grids(DerivedMesh *dm)
{
CCGDerivedMesh *ccgdm = (CCGDerivedMesh *)dm;
CCGSubSurf *ss = ccgdm->ss;
CCGElem **gridData;
CCGFace **gridFaces;
int *gridOffset;
int index, numFaces, numGrids, S, gIndex /*, gridSize */;
if (ccgdm->gridData) {
return;
}
numGrids = ccgDM_getNumGrids(dm);
numFaces = ccgSubSurf_getNumFaces(ss);
// gridSize = ccgDM_getGridSize(dm); /* UNUSED */
/* compute offset into grid array for each face */
gridOffset = MEM_malloc_arrayN<int>(size_t(numFaces), "ccgdm.gridOffset");
for (gIndex = 0, index = 0; index < numFaces; index++) {
CCGFace *f = ccgdm->faceMap[index].face;
int numVerts = ccgSubSurf_getFaceNumVerts(f);
gridOffset[index] = gIndex;
gIndex += numVerts;
}
/* compute grid data */
gridData = MEM_malloc_arrayN<CCGElem *>(size_t(numGrids), "ccgdm.gridData");
gridFaces = MEM_malloc_arrayN<CCGFace *>(size_t(numGrids), "ccgdm.gridFaces");
ccgdm->gridHidden = MEM_calloc_arrayN<uint *>(numGrids, "ccgdm.gridHidden");
for (gIndex = 0, index = 0; index < numFaces; index++) {
CCGFace *f = ccgdm->faceMap[index].face;
int numVerts = ccgSubSurf_getFaceNumVerts(f);
for (S = 0; S < numVerts; S++, gIndex++) {
gridData[gIndex] = static_cast<CCGElem *>(ccgSubSurf_getFaceGridDataArray(ss, f, S));
gridFaces[gIndex] = f;
}
}
ccgdm->gridData = gridData;
ccgdm->gridFaces = gridFaces;
ccgdm->gridOffset = gridOffset;
ccgdm->numGrid = numGrids;
}
static CCGElem **ccgDM_getGridData(DerivedMesh *dm)
{
CCGDerivedMesh *ccgdm = (CCGDerivedMesh *)dm;
ccgdm_create_grids(dm);
return ccgdm->gridData;
}
static int *ccgDM_getGridOffset(DerivedMesh *dm)
{
CCGDerivedMesh *ccgdm = (CCGDerivedMesh *)dm;
ccgdm_create_grids(dm);
return ccgdm->gridOffset;
}
static void ccgDM_getGridKey(DerivedMesh *dm, CCGKey *key)
{
CCGDerivedMesh *ccgdm = (CCGDerivedMesh *)dm;
CCG_key_top_level(key, ccgdm->ss);
}
static void set_default_ccgdm_callbacks(CCGDerivedMesh *ccgdm)
{
ccgdm->dm.getNumVerts = ccgDM_getNumVerts;
ccgdm->dm.getNumEdges = ccgDM_getNumEdges;
ccgdm->dm.getNumLoops = ccgDM_getNumLoops;
ccgdm->dm.getNumPolys = ccgDM_getNumPolys;
ccgdm->dm.copyVertArray = ccgDM_copyFinalVertArray;
ccgdm->dm.copyEdgeArray = ccgDM_copyFinalEdgeArray;
ccgdm->dm.copyCornerVertArray = ccgDM_copyFinalCornerVertArray;
ccgdm->dm.copyCornerEdgeArray = ccgDM_copyFinalCornerEdgeArray;
ccgdm->dm.copyPolyArray = ccgDM_copyFinalPolyArray;
ccgdm->dm.getVertDataArray = ccgDM_get_vert_data_layer;
ccgdm->dm.getEdgeDataArray = ccgDM_get_edge_data_layer;
ccgdm->dm.getPolyDataArray = ccgDM_get_poly_data_layer;
ccgdm->dm.getNumGrids = ccgDM_getNumGrids;
ccgdm->dm.getGridSize = ccgDM_getGridSize;
ccgdm->dm.getGridData = ccgDM_getGridData;
ccgdm->dm.getGridOffset = ccgDM_getGridOffset;
ccgdm->dm.getGridKey = ccgDM_getGridKey;
ccgdm->dm.release = ccgDM_release;
}
static void create_ccgdm_maps(CCGDerivedMesh *ccgdm, CCGSubSurf *ss)
{
CCGVertIterator vi;
CCGEdgeIterator ei;
CCGFaceIterator fi;
int totvert, totedge, totface;
totvert = ccgSubSurf_getNumVerts(ss);
ccgdm->vertMap = MEM_malloc_arrayN<std::remove_pointer_t<decltype(CCGDerivedMesh::vertMap)>>(
size_t(totvert), "vertMap");
for (ccgSubSurf_initVertIterator(ss, &vi); !ccgVertIterator_isStopped(&vi);
ccgVertIterator_next(&vi))
{
CCGVert *v = ccgVertIterator_getCurrent(&vi);
ccgdm->vertMap[POINTER_AS_INT(ccgSubSurf_getVertVertHandle(v))].vert = v;
}
totedge = ccgSubSurf_getNumEdges(ss);
ccgdm->edgeMap = MEM_malloc_arrayN<std::remove_pointer_t<decltype(CCGDerivedMesh::edgeMap)>>(
size_t(totedge), "edgeMap");
for (ccgSubSurf_initEdgeIterator(ss, &ei); !ccgEdgeIterator_isStopped(&ei);
ccgEdgeIterator_next(&ei))
{
CCGEdge *e = ccgEdgeIterator_getCurrent(&ei);
ccgdm->edgeMap[POINTER_AS_INT(ccgSubSurf_getEdgeEdgeHandle(e))].edge = e;
}
totface = ccgSubSurf_getNumFaces(ss);
ccgdm->faceMap = MEM_malloc_arrayN<std::remove_pointer_t<decltype(CCGDerivedMesh::faceMap)>>(
size_t(totface), "faceMap");
for (ccgSubSurf_initFaceIterator(ss, &fi); !ccgFaceIterator_isStopped(&fi);
ccgFaceIterator_next(&fi))
{
CCGFace *f = ccgFaceIterator_getCurrent(&fi);
ccgdm->faceMap[POINTER_AS_INT(ccgSubSurf_getFaceFaceHandle(f))].face = f;
}
}
/* Fill in all geometry arrays making it possible to access any
* hires data from the CPU.
*/
static void set_ccgdm_all_geometry(CCGDerivedMesh *ccgdm,
CCGSubSurf *ss,
DerivedMesh *dm,
bool useSubsurfUv)
{
const int totvert = ccgSubSurf_getNumVerts(ss);
const int totedge = ccgSubSurf_getNumEdges(ss);
const int totface = ccgSubSurf_getNumFaces(ss);
int index;
int i;
int vertNum = 0, edgeNum = 0, faceNum = 0;
int *polyidx = nullptr;
blender::Vector<int, 16> loopidx;
blender::Vector<int, 16> vertidx;
int loopindex, loopindex2;
int edgeSize;
int gridSize;
int gridFaces, gridCuts;
int gridSideEdges;
int gridInternalEdges;
WeightTable wtable = {nullptr};
bool has_edge_cd;
edgeSize = ccgSubSurf_getEdgeSize(ss);
gridSize = ccgSubSurf_getGridSize(ss);
gridFaces = gridSize - 1;
gridCuts = gridSize - 2;
// gridInternalVerts = gridSideVerts * gridSideVerts; /* As yet, unused. */
gridSideEdges = gridSize - 1;
gridInternalEdges = (gridSideEdges - 1) * gridSideEdges * 2;
const int *base_polyOrigIndex = static_cast<const int *>(
CustomData_get_layer(&dm->polyData, CD_ORIGINDEX));
int *vertOrigIndex = static_cast<int *>(DM_get_vert_data_layer(&ccgdm->dm, CD_ORIGINDEX));
int *edgeOrigIndex = static_cast<int *>(DM_get_edge_data_layer(&ccgdm->dm, CD_ORIGINDEX));
int *polyOrigIndex = static_cast<int *>(DM_get_poly_data_layer(&ccgdm->dm, CD_ORIGINDEX));
has_edge_cd = ((ccgdm->dm.edgeData.totlayer - (edgeOrigIndex ? 1 : 0)) != 0);
loopindex = loopindex2 = 0; /* current loop index */
for (index = 0; index < totface; index++) {
CCGFace *f = ccgdm->faceMap[index].face;
int numVerts = ccgSubSurf_getFaceNumVerts(f);
int numFinalEdges = numVerts * (gridSideEdges + gridInternalEdges);
int origIndex = POINTER_AS_INT(ccgSubSurf_getFaceFaceHandle(f));
int g2_wid = gridCuts + 2;
float *w, *w2;
int s, x, y;
w = get_ss_weights(&wtable, gridCuts, numVerts);
ccgdm->faceMap[index].startVert = vertNum;
ccgdm->faceMap[index].startEdge = edgeNum;
ccgdm->faceMap[index].startFace = faceNum;
/* set the face base vert */
*((int *)ccgSubSurf_getFaceUserData(ss, f)) = vertNum;
loopidx.reinitialize(numVerts);
for (s = 0; s < numVerts; s++) {
loopidx[s] = loopindex++;
}
vertidx.reinitialize(numVerts);
for (s = 0; s < numVerts; s++) {
CCGVert *v = ccgSubSurf_getFaceVert(f, s);
vertidx[s] = POINTER_AS_INT(ccgSubSurf_getVertVertHandle(v));
}
/* I think this is for interpolating the center vert? */
w2 = w; // + numVerts*(g2_wid-1) * (g2_wid-1); //numVerts*((g2_wid-1) * g2_wid+g2_wid-1);
DM_interp_vert_data(dm, &ccgdm->dm, vertidx.data(), w2, numVerts, vertNum);
if (vertOrigIndex) {
*vertOrigIndex = ORIGINDEX_NONE;
vertOrigIndex++;
}
vertNum++;
/* Interpolate per-vert data. */
for (s = 0; s < numVerts; s++) {
for (x = 1; x < gridFaces; x++) {
w2 = w + s * numVerts * g2_wid * g2_wid + x * numVerts;
DM_interp_vert_data(dm, &ccgdm->dm, vertidx.data(), w2, numVerts, vertNum);
if (vertOrigIndex) {
*vertOrigIndex = ORIGINDEX_NONE;
vertOrigIndex++;
}
vertNum++;
}
}
/* Interpolate per-vert data. */
for (s = 0; s < numVerts; s++) {
for (y = 1; y < gridFaces; y++) {
for (x = 1; x < gridFaces; x++) {
w2 = w + s * numVerts * g2_wid * g2_wid + (y * g2_wid + x) * numVerts;
DM_interp_vert_data(dm, &ccgdm->dm, vertidx.data(), w2, numVerts, vertNum);
if (vertOrigIndex) {
*vertOrigIndex = ORIGINDEX_NONE;
vertOrigIndex++;
}
vertNum++;
}
}
}
if (edgeOrigIndex) {
for (i = 0; i < numFinalEdges; i++) {
edgeOrigIndex[edgeNum + i] = ORIGINDEX_NONE;
}
}
for (s = 0; s < numVerts; s++) {
/* Interpolate per-face data. */
for (y = 0; y < gridFaces; y++) {
for (x = 0; x < gridFaces; x++) {
w2 = w + s * numVerts * g2_wid * g2_wid + (y * g2_wid + x) * numVerts;
CustomData_interp(&dm->loopData,
&ccgdm->dm.loopData,
loopidx.data(),
w2,
nullptr,
numVerts,
loopindex2);
loopindex2++;
w2 = w + s * numVerts * g2_wid * g2_wid + ((y + 1) * g2_wid + (x)) * numVerts;
CustomData_interp(&dm->loopData,
&ccgdm->dm.loopData,
loopidx.data(),
w2,
nullptr,
numVerts,
loopindex2);
loopindex2++;
w2 = w + s * numVerts * g2_wid * g2_wid + ((y + 1) * g2_wid + (x + 1)) * numVerts;
CustomData_interp(&dm->loopData,
&ccgdm->dm.loopData,
loopidx.data(),
w2,
nullptr,
numVerts,
loopindex2);
loopindex2++;
w2 = w + s * numVerts * g2_wid * g2_wid + ((y)*g2_wid + (x + 1)) * numVerts;
CustomData_interp(&dm->loopData,
&ccgdm->dm.loopData,
loopidx.data(),
w2,
nullptr,
numVerts,
loopindex2);
loopindex2++;
CustomData_copy_data(&dm->polyData, &ccgdm->dm.polyData, origIndex, faceNum, 1);
if (polyOrigIndex) {
*polyOrigIndex = base_polyOrigIndex ? base_polyOrigIndex[origIndex] : origIndex;
polyOrigIndex++;
}
/* This is a simple one to one mapping, here... */
if (polyidx) {
polyidx[faceNum] = faceNum;
}
faceNum++;
}
}
}
edgeNum += numFinalEdges;
}
for (index = 0; index < totedge; index++) {
CCGEdge *e = ccgdm->edgeMap[index].edge;
int numFinalEdges = edgeSize - 1;
int mapIndex = ccgDM_getEdgeMapIndex(ss, e);
int x;
int vertIdx[2];
int edgeIdx = POINTER_AS_INT(ccgSubSurf_getEdgeEdgeHandle(e));
CCGVert *v;
v = ccgSubSurf_getEdgeVert0(e);
vertIdx[0] = POINTER_AS_INT(ccgSubSurf_getVertVertHandle(v));
v = ccgSubSurf_getEdgeVert1(e);
vertIdx[1] = POINTER_AS_INT(ccgSubSurf_getVertVertHandle(v));
ccgdm->edgeMap[index].startVert = vertNum;
ccgdm->edgeMap[index].startEdge = edgeNum;
/* set the edge base vert */
*((int *)ccgSubSurf_getEdgeUserData(ss, e)) = vertNum;
for (x = 1; x < edgeSize - 1; x++) {
float w[2];
w[1] = float(x) / (edgeSize - 1);
w[0] = 1 - w[1];
DM_interp_vert_data(dm, &ccgdm->dm, vertIdx, w, 2, vertNum);
if (vertOrigIndex) {
*vertOrigIndex = ORIGINDEX_NONE;
vertOrigIndex++;
}
vertNum++;
}
if (has_edge_cd) {
BLI_assert(edgeIdx >= 0 && edgeIdx < dm->getNumEdges(dm));
for (i = 0; i < numFinalEdges; i++) {
CustomData_copy_data(&dm->edgeData, &ccgdm->dm.edgeData, edgeIdx, edgeNum + i, 1);
}
}
if (edgeOrigIndex) {
for (i = 0; i < numFinalEdges; i++) {
edgeOrigIndex[edgeNum + i] = mapIndex;
}
}
edgeNum += numFinalEdges;
}
if (useSubsurfUv) {
CustomData *loop_data = &ccgdm->dm.loopData;
CustomData *dmldata = &dm->loopData;
int numlayer = CustomData_number_of_layers(loop_data, CD_PROP_FLOAT2);
int dmnumlayer = CustomData_number_of_layers(dmldata, CD_PROP_FLOAT2);
for (i = 0; i < numlayer && i < dmnumlayer; i++) {
set_subsurf_uv(ss, dm, &ccgdm->dm, i);
}
}
for (index = 0; index < totvert; index++) {
CCGVert *v = ccgdm->vertMap[index].vert;
int mapIndex = ccgDM_getVertMapIndex(ccgdm->ss, v);
int vertIdx;
vertIdx = POINTER_AS_INT(ccgSubSurf_getVertVertHandle(v));
ccgdm->vertMap[index].startVert = vertNum;
/* set the vert base vert */
*((int *)ccgSubSurf_getVertUserData(ss, v)) = vertNum;
DM_copy_vert_data(dm, &ccgdm->dm, vertIdx, vertNum, 1);
if (vertOrigIndex) {
*vertOrigIndex = mapIndex;
vertOrigIndex++;
}
vertNum++;
}
free_ss_weights(&wtable);
BLI_assert(vertNum == ccgSubSurf_getNumFinalVerts(ss));
BLI_assert(edgeNum == ccgSubSurf_getNumFinalEdges(ss));
BLI_assert(loopindex2 == ccgSubSurf_getNumFinalFaces(ss) * 4);
BLI_assert(faceNum == ccgSubSurf_getNumFinalFaces(ss));
}
static CCGDerivedMesh *getCCGDerivedMesh(CCGSubSurf *ss,
int drawInteriorEdges,
int useSubsurfUv,
DerivedMesh *dm)
{
CCGDerivedMesh *ccgdm = MEM_callocN<CCGDerivedMesh>(__func__);
DM_from_template(&ccgdm->dm,
dm,
DM_TYPE_CCGDM,
ccgSubSurf_getNumFinalVerts(ss),
ccgSubSurf_getNumFinalEdges(ss),
0,
ccgSubSurf_getNumFinalFaces(ss) * 4,
ccgSubSurf_getNumFinalFaces(ss));
CustomData_free_layer_named(&ccgdm->dm.vertData, "position");
CustomData_free_layer_named(&ccgdm->dm.edgeData, ".edge_verts");
CustomData_free_layer_named(&ccgdm->dm.loopData, ".corner_vert");
CustomData_free_layer_named(&ccgdm->dm.loopData, ".corner_edge");
MEM_SAFE_FREE(ccgdm->dm.face_offsets);
create_ccgdm_maps(ccgdm, ss);
set_default_ccgdm_callbacks(ccgdm);
ccgdm->ss = ss;
ccgdm->drawInteriorEdges = drawInteriorEdges;
ccgdm->useSubsurfUv = useSubsurfUv;
set_ccgdm_all_geometry(ccgdm, ss, dm, useSubsurfUv != 0);
ccgdm->dm.numVertData = ccgSubSurf_getNumFinalVerts(ss);
ccgdm->dm.numEdgeData = ccgSubSurf_getNumFinalEdges(ss);
ccgdm->dm.numPolyData = ccgSubSurf_getNumFinalFaces(ss);
ccgdm->dm.numLoopData = ccgdm->dm.numPolyData * 4;
ccgdm->dm.numTessFaceData = 0;
BLI_mutex_init(&ccgdm->loops_cache_lock);
BLI_rw_mutex_init(&ccgdm->origindex_cache_rwlock);
return ccgdm;
}
/***/
DerivedMesh *subsurf_make_derived_from_derived(DerivedMesh *dm,
SubsurfModifierData *smd,
const Scene *scene,
float (*vertCos)[3],
SubsurfFlags flags)
{
const CCGFlags useSimple = (smd->subdivType == ME_SIMPLE_SUBSURF) ? CCG_SIMPLE_SUBDIV :
CCGFlags(0);
const CCGFlags useAging = (smd->flags & eSubsurfModifierFlag_DebugIncr) ? CCG_USE_AGING :
CCGFlags(0);
const int useSubsurfUv = (smd->uv_smooth != SUBSURF_UV_SMOOTH_NONE);
const int drawInteriorEdges = !(smd->flags & eSubsurfModifierFlag_ControlEdges);
const bool ignore_simplify = (flags & SUBSURF_IGNORE_SIMPLIFY);
CCGDerivedMesh *result;
/* NOTE: editmode calculation can only run once per
* modifier stack evaluation (uses freed cache) #36299. */
if (flags & SUBSURF_FOR_EDIT_MODE) {
int levels = (scene != nullptr && !ignore_simplify) ?
get_render_subsurf_level(&scene->r, smd->levels, false) :
smd->levels;
/* TODO(sergey): Same as emCache below. */
if ((flags & SUBSURF_IN_EDIT_MODE) && smd->mCache) {
ccgSubSurf_free(static_cast<CCGSubSurf *>(smd->mCache));
smd->mCache = nullptr;
}
smd->emCache = _getSubSurf(static_cast<CCGSubSurf *>(smd->emCache),
levels,
3,
useSimple | useAging | CCG_CALC_NORMALS);
ss_sync_from_derivedmesh(
static_cast<CCGSubSurf *>(smd->emCache), dm, vertCos, useSimple, useSubsurfUv);
result = getCCGDerivedMesh(
static_cast<CCGSubSurf *>(smd->emCache), drawInteriorEdges, useSubsurfUv, dm);
}
else if (flags & SUBSURF_USE_RENDER_PARAMS) {
/* Do not use cache in render mode. */
CCGSubSurf *ss;
int levels = (scene != nullptr && !ignore_simplify) ?
get_render_subsurf_level(&scene->r, smd->renderLevels, true) :
smd->renderLevels;
if (levels == 0) {
return dm;
}
ss = _getSubSurf(nullptr, levels, 3, useSimple | CCG_USE_ARENA | CCG_CALC_NORMALS);
ss_sync_from_derivedmesh(ss, dm, vertCos, useSimple, useSubsurfUv);
result = getCCGDerivedMesh(ss, drawInteriorEdges, useSubsurfUv, dm);
result->freeSS = 1;
}
else {
int useIncremental = (smd->flags & eSubsurfModifierFlag_Incremental);
int levels = (scene != nullptr && !ignore_simplify) ?
get_render_subsurf_level(&scene->r, smd->levels, false) :
smd->levels;
CCGSubSurf *ss;
/* NOTE(@zr): It is quite possible there is a much better place to do this. It
* depends a bit on how rigorously we expect this function to never
* be called in edit-mode. In semi-theory we could share a single
* cache, but the handles used inside and outside edit-mode are not
* the same so we would need some way of converting them. Its probably
* not worth the effort. But then why am I even writing this long
* comment that no one will read? Hmm.
*
* NOTE(@brecht): Addendum: we can't really ensure that this is never called in edit
* mode, so now we have a parameter to verify it.
*/
if (!(flags & SUBSURF_IN_EDIT_MODE) && smd->emCache) {
ccgSubSurf_free(static_cast<CCGSubSurf *>(smd->emCache));
smd->emCache = nullptr;
}
if (useIncremental && (flags & SUBSURF_IS_FINAL_CALC)) {
smd->mCache = ss = _getSubSurf(static_cast<CCGSubSurf *>(smd->mCache),
levels,
3,
useSimple | useAging | CCG_CALC_NORMALS);
ss_sync_from_derivedmesh(ss, dm, vertCos, useSimple, useSubsurfUv);
result = getCCGDerivedMesh(
static_cast<CCGSubSurf *>(smd->mCache), drawInteriorEdges, useSubsurfUv, dm);
}
else {
CCGFlags ccg_flags = useSimple | CCG_USE_ARENA | CCG_CALC_NORMALS;
CCGSubSurf *prevSS = nullptr;
if ((smd->mCache) && (flags & SUBSURF_IS_FINAL_CALC)) {
ccgSubSurf_free(static_cast<CCGSubSurf *>(smd->mCache));
smd->mCache = nullptr;
}
if (flags & SUBSURF_ALLOC_PAINT_MASK) {
ccg_flags |= CCG_ALLOC_MASK;
}
ss = _getSubSurf(prevSS, levels, 3, ccg_flags);
ss_sync_from_derivedmesh(ss, dm, vertCos, useSimple, useSubsurfUv);
result = getCCGDerivedMesh(ss, drawInteriorEdges, useSubsurfUv, dm);
if (flags & SUBSURF_IS_FINAL_CALC) {
smd->mCache = ss;
}
else {
result->freeSS = 1;
}
if (flags & SUBSURF_ALLOC_PAINT_MASK) {
ccgSubSurf_setNumLayers(ss, 4);
}
}
}
return (DerivedMesh *)result;
}
void subsurf_calculate_limit_positions(Mesh *mesh, float (*r_positions)[3])
{
/* Finds the subsurf limit positions for the verts in a mesh
* and puts them in an array of floats. Please note that the
* calculated vert positions is incorrect for the verts
* on the boundary of the mesh.
*/
CCGSubSurf *ss = _getSubSurf(nullptr, 1, 3, CCG_USE_ARENA);
float edge_sum[3], face_sum[3];
CCGVertIterator vi;
DerivedMesh *dm = CDDM_from_mesh(mesh);
ss_sync_from_derivedmesh(ss, dm, nullptr, 0, false);
for (ccgSubSurf_initVertIterator(ss, &vi); !ccgVertIterator_isStopped(&vi);
ccgVertIterator_next(&vi))
{
CCGVert *v = ccgVertIterator_getCurrent(&vi);
int idx = POINTER_AS_INT(ccgSubSurf_getVertVertHandle(v));
int N = ccgSubSurf_getVertNumEdges(v);
int numFaces = ccgSubSurf_getVertNumFaces(v);
int i;
zero_v3(edge_sum);
zero_v3(face_sum);
for (i = 0; i < N; i++) {
CCGEdge *e = ccgSubSurf_getVertEdge(v, i);
add_v3_v3v3(
edge_sum, edge_sum, static_cast<const float *>(ccgSubSurf_getEdgeData(ss, e, 1)));
}
for (i = 0; i < numFaces; i++) {
CCGFace *f = ccgSubSurf_getVertFace(v, i);
add_v3_v3(face_sum, static_cast<const float *>(ccgSubSurf_getFaceCenterData(f)));
}
/* NOTE(@brecht): ad-hoc correction for boundary vertices, to at least avoid them
* moving completely out of place. */
if (numFaces && numFaces != N) {
mul_v3_fl(face_sum, float(N) / float(numFaces));
}
const float *co = static_cast<const float *>(ccgSubSurf_getVertData(ss, v));
r_positions[idx][0] = (co[0] * N * N + edge_sum[0] * 4 + face_sum[0]) / (N * (N + 5));
r_positions[idx][1] = (co[1] * N * N + edge_sum[1] * 4 + face_sum[1]) / (N * (N + 5));
r_positions[idx][2] = (co[2] * N * N + edge_sum[2] * 4 + face_sum[2]) / (N * (N + 5));
}
ccgSubSurf_free(ss);
dm->release(dm);
}

View File

@@ -28,7 +28,6 @@
#include "BKE_object_deform.h"
#include "BKE_report.hh"
#include "BKE_subdiv_mesh.hh"
#include "BKE_subsurf.hh"
#include "DEG_depsgraph.hh"
#include "DEG_depsgraph_query.hh"

View File

@@ -25,11 +25,11 @@
#include "BKE_customdata.hh"
#include "BKE_global.hh"
#include "BKE_image.hh"
#include "BKE_mesh_legacy_derived_mesh.hh"
#include "BKE_modifier.hh"
#include "BKE_multires.hh"
#include "BKE_report.hh"
#include "BKE_scene.hh"
#include "BKE_subdiv.hh"
#include "RE_multires_bake.h"
#include "RE_pipeline.h"
@@ -69,11 +69,17 @@ static Vector<Image *> bake_object_image_get_array(Object &object)
/* holder of per-object data needed for bake job
* needed to make job totally thread-safe */
struct MultiresBakerJobData {
MultiresBakerJobData *next, *prev;
/* material aligned image array (for per-face bake image) */
MultiresBakerJobData *next = nullptr, *prev = nullptr;
/* Material aligned image array (for per-face bake image). */
Vector<Image *> ob_image;
DerivedMesh *lores_dm, *hires_dm;
int lvl, tot_lvl;
/* Base mesh at the input of the multiresolution modifier. */
Mesh *base_mesh = nullptr;
/* Multi-resolution modifier which is being baked. */
MultiresModifierData *multires_modifier = nullptr;
Set<Image *> images;
};
@@ -90,11 +96,9 @@ struct MultiresBakeJob {
/** mode of baking (displacement, normals, AO) */
short mode;
/** Use low-resolution mesh when baking displacement maps */
bool use_lores_mesh;
bool use_low_resolution_mesh;
/** Bias between object and start ray point when doing AO baking */
float bias;
/** Number of threads to be used for baking */
int threads;
};
static bool multiresbake_check(bContext *C, wmOperator *op)
@@ -205,60 +209,6 @@ static bool multiresbake_check(bContext *C, wmOperator *op)
return ok;
}
static DerivedMesh *multiresbake_create_loresdm(Scene *scene, Object *ob, int *lvl)
{
DerivedMesh *dm;
MultiresModifierData *mmd = get_multires_modifier(scene, ob, false);
Mesh *mesh = (Mesh *)ob->data;
MultiresModifierData tmp_mmd = dna::shallow_copy(*mmd);
*lvl = mmd->lvl;
if (mmd->lvl == 0) {
DerivedMesh *cddm = CDDM_from_mesh(mesh);
DM_set_only_copy(cddm, &CD_MASK_BAREMESH);
return cddm;
}
DerivedMesh *cddm = CDDM_from_mesh(mesh);
DM_set_only_copy(cddm, &CD_MASK_BAREMESH);
tmp_mmd.lvl = mmd->lvl;
tmp_mmd.sculptlvl = mmd->lvl;
dm = multires_make_derived_from_derived(
cddm, &tmp_mmd, scene, ob, MultiresFlags::IgnoreSimplify);
cddm->release(cddm);
return dm;
}
static DerivedMesh *multiresbake_create_hiresdm(Scene *scene, Object *ob, int *lvl)
{
Mesh *mesh = (Mesh *)ob->data;
MultiresModifierData *mmd = get_multires_modifier(scene, ob, false);
MultiresModifierData tmp_mmd = dna::shallow_copy(*mmd);
DerivedMesh *cddm = CDDM_from_mesh(mesh);
DerivedMesh *dm;
DM_set_only_copy(cddm, &CD_MASK_BAREMESH);
/* TODO: DM_set_only_copy wouldn't set mask for loop and poly data,
* but we really need BAREMESH only to save lots of memory
*/
CustomData_set_only_copy(&cddm->loopData, CD_MASK_BAREMESH.lmask);
CustomData_set_only_copy(&cddm->polyData, CD_MASK_BAREMESH.pmask);
*lvl = mmd->totlvl;
tmp_mmd.lvl = mmd->totlvl;
tmp_mmd.sculptlvl = mmd->totlvl;
dm = multires_make_derived_from_derived(
cddm, &tmp_mmd, scene, ob, MultiresFlags::IgnoreSimplify);
cddm->release(cddm);
return dm;
}
enum ClearFlag {
CLEAR_TANGENT_NORMAL = 1,
CLEAR_DISPLACEMENT = 2,
@@ -321,7 +271,6 @@ static void clear_images_poly(const Span<Image *> ob_image_array, const ClearFla
static wmOperatorStatus multiresbake_image_exec_locked(bContext *C, wmOperator *op)
{
Object *ob;
Scene *scene = CTX_data_scene(C);
int objects_baked = 0;
@@ -331,10 +280,10 @@ static wmOperatorStatus multiresbake_image_exec_locked(bContext *C, wmOperator *
if (scene->r.bake_flag & R_BAKE_CLEAR) { /* clear images */
CTX_DATA_BEGIN (C, Base *, base, selected_editable_bases) {
ClearFlag clear_flag = ClearFlag(0);
Object &object = *base->object;
BLI_assert(object.type == OB_MESH);
ob = base->object;
// mesh = (Mesh *)ob->data;
ClearFlag clear_flag = ClearFlag(0);
if (scene->r.bake_mode == RE_BAKE_NORMALS) {
clear_flag = CLEAR_TANGENT_NORMAL;
@@ -344,7 +293,7 @@ static wmOperatorStatus multiresbake_image_exec_locked(bContext *C, wmOperator *
}
{
const Vector<Image *> ob_image_array = bake_object_image_get_array(*ob);
const Vector<Image *> ob_image_array = bake_object_image_get_array(object);
clear_images_poly(ob_image_array, clear_flag);
}
}
@@ -352,37 +301,31 @@ static wmOperatorStatus multiresbake_image_exec_locked(bContext *C, wmOperator *
}
CTX_DATA_BEGIN (C, Base *, base, selected_editable_bases) {
MultiresBakeRender bkr = {nullptr};
Object &object = *base->object;
BLI_assert(object.type == OB_MESH);
ob = base->object;
MultiresBakeRender bake;
multires_flush_sculpt_updates(ob);
multires_flush_sculpt_updates(&object);
/* copy data stored in job descriptor */
bkr.scene = scene;
bkr.bake_margin = scene->r.bake_margin;
/* Copy data stored in job descriptor. */
bake.bake_margin = scene->r.bake_margin;
if (scene->r.bake_mode == RE_BAKE_NORMALS) {
bkr.bake_margin_type = R_BAKE_EXTEND;
bake.bake_margin_type = R_BAKE_EXTEND;
}
else {
bkr.bake_margin_type = scene->r.bake_margin_type;
bake.bake_margin_type = eBakeMarginType(scene->r.bake_margin_type);
}
bkr.mode = scene->r.bake_mode;
bkr.use_lores_mesh = scene->r.bake_flag & R_BAKE_LORES_MESH;
bkr.bias = scene->r.bake_biasdist;
bkr.threads = BKE_scene_num_threads(scene);
// bkr.reports= op->reports;
bake.mode = scene->r.bake_mode;
bake.use_low_resolution_mesh = scene->r.bake_flag & R_BAKE_LORES_MESH;
bake.bias = scene->r.bake_biasdist;
/* create low-resolution DM (to bake to) and hi-resolution DM (to bake from) */
bkr.ob_image = bake_object_image_get_array(*ob);
bake.ob_image = bake_object_image_get_array(object);
bkr.hires_dm = multiresbake_create_hiresdm(scene, ob, &bkr.tot_lvl);
bkr.lores_dm = multiresbake_create_loresdm(scene, ob, &bkr.lvl);
bake.base_mesh = static_cast<Mesh *>(object.data);
bake.multires_modifier = get_multires_modifier(scene, &object, false);
RE_multires_bake_images(&bkr);
bkr.lores_dm->release(bkr.lores_dm);
bkr.hires_dm->release(bkr.hires_dm);
RE_multires_bake_images(bake);
objects_baked++;
}
@@ -401,7 +344,6 @@ static wmOperatorStatus multiresbake_image_exec_locked(bContext *C, wmOperator *
static void init_multiresbake_job(bContext *C, MultiresBakeJob *bkj)
{
Scene *scene = CTX_data_scene(C);
Object *ob;
/* backup scene settings, so their changing in UI would take no effect on baker */
bkj->scene = scene;
@@ -413,27 +355,22 @@ static void init_multiresbake_job(bContext *C, MultiresBakeJob *bkj)
bkj->bake_margin_type = scene->r.bake_margin_type;
}
bkj->mode = scene->r.bake_mode;
bkj->use_lores_mesh = scene->r.bake_flag & R_BAKE_LORES_MESH;
bkj->use_low_resolution_mesh = scene->r.bake_flag & R_BAKE_LORES_MESH;
bkj->bake_clear = scene->r.bake_flag & R_BAKE_CLEAR;
bkj->bias = scene->r.bake_biasdist;
bkj->threads = BKE_scene_num_threads(scene);
// bkj->reports = op->reports;
CTX_DATA_BEGIN (C, Base *, base, selected_editable_bases) {
int lvl;
Object &object = *base->object;
BLI_assert(object.type == OB_MESH);
ob = base->object;
multires_flush_sculpt_updates(ob);
multires_flush_sculpt_updates(&object);
MultiresBakerJobData *data = MEM_new<MultiresBakerJobData>(__func__);
data->ob_image = bake_object_image_get_array(*ob);
data->ob_image = bake_object_image_get_array(object);
/* create low-resolution DM (to bake to) and hi-resolution DM (to bake from) */
data->hires_dm = multiresbake_create_hiresdm(scene, ob, &data->tot_lvl);
data->lores_dm = multiresbake_create_loresdm(scene, ob, &lvl);
data->lvl = lvl;
data->base_mesh = static_cast<Mesh *>(object.data);
data->multires_modifier = get_multires_modifier(scene, &object, false);
BLI_addtail(&bkj->data, data);
}
@@ -463,37 +400,31 @@ static void multiresbake_startjob(void *bkv, wmJobWorkerStatus *worker_status)
}
LISTBASE_FOREACH (MultiresBakerJobData *, data, &bkj->data) {
MultiresBakeRender bkr = {nullptr};
MultiresBakeRender bake;
/* copy data stored in job descriptor */
bkr.scene = bkj->scene;
bkr.bake_margin = bkj->bake_margin;
bkr.bake_margin_type = bkj->bake_margin_type;
bkr.mode = bkj->mode;
bkr.use_lores_mesh = bkj->use_lores_mesh;
// bkr.reports = bkj->reports;
bkr.ob_image = data->ob_image;
bake.bake_margin = bkj->bake_margin;
bake.bake_margin_type = eBakeMarginType(bkj->bake_margin_type);
bake.mode = bkj->mode;
bake.use_low_resolution_mesh = bkj->use_low_resolution_mesh;
bake.ob_image = data->ob_image;
/* create low-resolution DM (to bake to) and hi-resolution DM (to bake from) */
bkr.lores_dm = data->lores_dm;
bkr.hires_dm = data->hires_dm;
bkr.tot_lvl = data->tot_lvl;
bkr.lvl = data->lvl;
bake.base_mesh = data->base_mesh;
bake.multires_modifier = data->multires_modifier;
/* needed for proper progress bar */
bkr.tot_obj = tot_obj;
bkr.baked_objects = baked_objects;
bake.num_total_objects = tot_obj;
bake.num_baked_objects = baked_objects;
bkr.stop = &worker_status->stop;
bkr.do_update = &worker_status->do_update;
bkr.progress = &worker_status->progress;
bake.stop = &worker_status->stop;
bake.do_update = &worker_status->do_update;
bake.progress = &worker_status->progress;
bkr.bias = bkj->bias;
bkr.threads = bkj->threads;
bake.bias = bkj->bias;
RE_multires_bake_images(&bkr);
RE_multires_bake_images(bake);
data->images = bkr.images;
data->images = bake.images;
baked_objects++;
}
@@ -507,8 +438,6 @@ static void multiresbake_freejob(void *bkv)
data = static_cast<MultiresBakerJobData *>(bkj->data.first);
while (data) {
next = data->next;
data->lores_dm->release(data->lores_dm);
data->hires_dm->release(data->hires_dm);
/* delete here, since this delete will be called from main thread */
for (Image *image : data->images) {
@@ -586,7 +515,12 @@ static wmOperatorStatus objects_bake_render_modal(bContext *C,
static bool is_multires_bake(Scene *scene)
{
if (ELEM(scene->r.bake_mode, RE_BAKE_NORMALS, RE_BAKE_DISPLACEMENT, RE_BAKE_AO)) {
if (ELEM(scene->r.bake_mode,
RE_BAKE_NORMALS,
RE_BAKE_DISPLACEMENT,
RE_BAKE_VECTOR_DISPLACEMENT,
RE_BAKE_AO))
{
return scene->r.bake_flag & R_BAKE_MULTIRES;
}

View File

@@ -26,7 +26,6 @@
#include "BKE_paint.hh"
#include "BKE_paint_bvh.hh"
#include "BKE_subdiv_ccg.hh"
#include "BKE_subsurf.hh"
#include "DEG_depsgraph.hh"

View File

@@ -28,7 +28,6 @@
#include "BKE_paint.hh"
#include "BKE_paint_bvh.hh"
#include "BKE_subdiv_ccg.hh"
#include "BKE_subsurf.hh"
#include "RNA_access.hh"
#include "RNA_define.hh"

View File

@@ -62,7 +62,6 @@
#include "BKE_paint_types.hh"
#include "BKE_report.hh"
#include "BKE_subdiv_ccg.hh"
#include "BKE_subsurf.hh"
#include "BLI_math_rotation_legacy.hh"
#include "BLI_math_vector.hh"

View File

@@ -58,7 +58,6 @@
#include "BKE_paint_types.hh"
#include "BKE_scene.hh"
#include "BKE_subdiv_ccg.hh"
#include "BKE_subsurf.hh"
#include "BKE_undo_system.hh"
/* TODO(sergey): Ideally should be no direct call to such low level things. */

View File

@@ -615,8 +615,6 @@
.uv_smooth = SUBSURF_UV_SMOOTH_PRESERVE_BOUNDARIES, \
.quality = 3, \
.boundary_smooth = SUBSURF_BOUNDARY_SMOOTH_ALL, \
.emCache = NULL, \
.mCache = NULL, \
}
#define _DNA_DEFAULT_SurfaceModifierData \

View File

@@ -268,9 +268,6 @@ typedef struct SubsurfModifierData {
/** #eSubsurfBoundarySmooth. */
short boundary_smooth;
char _pad[2];
/* TODO(sergey): Get rid of those with the old CCG subdivision code. */
void *emCache, *mCache;
} SubsurfModifierData;
typedef struct LatticeModifierData {

View File

@@ -6753,6 +6753,15 @@ static void rna_def_scene_render_data(BlenderRNA *brna)
//{RE_BAKE_AO, "AO", 0, "Ambient Occlusion", "Bake ambient occlusion"},
{RE_BAKE_NORMALS, "NORMALS", 0, "Normals", "Bake normals"},
{RE_BAKE_DISPLACEMENT, "DISPLACEMENT", 0, "Displacement", "Bake displacement"},
/* TODO(sergey): Uncomment once tangent space displacement is supported. */
/* Use C++ style comment because #if 0 breaks indentation. */
// {RE_BAKE_VECTOR_DISPLACEMENT,
// "VECTOR_DISPLACEMENT",
// 0,
// "Vector Displacement",
// "Bake vector displacement"},
{0, nullptr, 0, nullptr, nullptr},
};

View File

@@ -29,7 +29,6 @@
#include "BKE_subdiv_ccg.hh"
#include "BKE_subdiv_deform.hh"
#include "BKE_subdiv_mesh.hh"
#include "BKE_subsurf.hh"
#include "UI_interface.hh"
#include "UI_interface_layout.hh"

View File

@@ -50,8 +50,6 @@
#include "MOD_modifiertypes.hh"
#include "MOD_ui_common.hh"
#include "intern/CCGSubSurf.h"
static void init_data(ModifierData *md)
{
SubsurfModifierData *smd = (SubsurfModifierData *)md;
@@ -61,18 +59,6 @@ static void init_data(ModifierData *md)
MEMCPY_STRUCT_AFTER(smd, DNA_struct_default_get(SubsurfModifierData), modifier);
}
static void copy_data(const ModifierData *md, ModifierData *target, const int flag)
{
#if 0
const SubsurfModifierData *smd = (const SubsurfModifierData *)md;
#endif
SubsurfModifierData *tsmd = (SubsurfModifierData *)target;
BKE_modifier_copydata_generic(md, target, flag);
tsmd->emCache = tsmd->mCache = nullptr;
}
static void free_runtime_data(void *runtime_data_v)
{
if (runtime_data_v == nullptr) {
@@ -92,14 +78,6 @@ static void free_data(ModifierData *md)
{
SubsurfModifierData *smd = (SubsurfModifierData *)md;
if (smd->mCache) {
ccgSubSurf_free(static_cast<CCGSubSurf *>(smd->mCache));
smd->mCache = nullptr;
}
if (smd->emCache) {
ccgSubSurf_free(static_cast<CCGSubSurf *>(smd->emCache));
smd->emCache = nullptr;
}
free_runtime_data(smd->modifier.runtime);
}
@@ -477,13 +455,6 @@ static void panel_register(ARegionType *region_type)
modifier_panel_register(region_type, eModifierType_Subsurf, panel_draw);
}
static void blend_read(BlendDataReader * /*reader*/, ModifierData *md)
{
SubsurfModifierData *smd = (SubsurfModifierData *)md;
smd->emCache = smd->mCache = nullptr;
}
ModifierTypeInfo modifierType_Subsurf = {
/*idname*/ "Subdivision",
/*name*/ N_("Subdivision"),
@@ -496,7 +467,7 @@ ModifierTypeInfo modifierType_Subsurf = {
eModifierTypeFlag_AcceptsCVs,
/*icon*/ ICON_MOD_SUBSURF,
/*copy_data*/ copy_data,
/*copy_data*/ BKE_modifier_copydata_generic,
/*deform_verts*/ nullptr,
/*deform_matrices*/ deform_matrices,
@@ -517,6 +488,6 @@ ModifierTypeInfo modifierType_Subsurf = {
/*free_runtime_data*/ free_runtime_data,
/*panel_register*/ panel_register,
/*blend_write*/ nullptr,
/*blend_read*/ blend_read,
/*blend_read*/ nullptr,
/*foreach_cache*/ nullptr,
};

View File

@@ -8,38 +8,50 @@
#pragma once
#include "DNA_scene_types.h"
#include "BLI_set.hh"
#include "BLI_vector.hh"
#include "RE_pipeline.h"
struct Image;
struct DerivedMesh;
struct Mesh;
struct MultiresBakeRender;
struct Scene;
struct MultiresModifierData;
struct MultiresBakeRender {
Scene *scene;
DerivedMesh *lores_dm, *hires_dm;
int bake_margin;
char bake_margin_type;
int lvl, tot_lvl;
short mode;
bool use_lores_mesh; /* Use low-resolution mesh when baking displacement maps */
/* Base mesh at the input of the multiresolution modifier and data of the modifier which is being
* baked. */
Mesh *base_mesh = nullptr;
MultiresModifierData *multires_modifier = nullptr;
/* material aligned image array (for per-face bake image) */
int bake_margin = 0;
eBakeMarginType bake_margin_type = R_BAKE_ADJACENT_FACES;
short mode = RE_BAKE_NORMALS;
/* Use low-resolution mesh when baking displacement maps.
* When true displacement is calculated between the final position in the SubdivCCG and the
* corresponding location on the base mesh.
* When false displacement is calculated between the final position in the SubdivCCG and the
* multiresolution modifier calculated at the bake level, further subdivided (without adding
* displacement) to the final multi-resolution level. */
bool use_low_resolution_mesh = false;
/* Material aligned image array (for per-face bake image), */
blender::Vector<Image *> ob_image;
float bias; /* Bias between object and start ray point when doing AO baking */
/* Bias between object and start ray point when doing AO baking. */
float bias = 0;
int tot_obj;
blender::Set<Image *> images;
int baked_objects, baked_faces;
int num_total_objects = 0;
int num_baked_objects = 0;
int threads; /* Number of threads to be used for baking */
bool *stop;
bool *do_update;
float *progress;
bool *stop = nullptr;
bool *do_update = nullptr;
float *progress = nullptr;
};
void RE_multires_bake_images(struct MultiresBakeRender *bkr);
void RE_multires_bake_images(MultiresBakeRender &bake);

View File

@@ -492,6 +492,7 @@ blender::gpu::Texture *RE_pass_ensure_gpu_texture_cache(struct Render *re,
#define RE_BAKE_NORMALS 0
#define RE_BAKE_DISPLACEMENT 1
#define RE_BAKE_AO 2
#define RE_BAKE_VECTOR_DISPLACEMENT 3
void RE_GetCameraWindow(struct Render *re, const struct Object *camera, float r_winmat[4][4]);
/**

View File

@@ -7,7 +7,6 @@
* \ingroup bke
*/
struct DerivedMesh;
struct ImBuf;
struct Mesh;
@@ -26,6 +25,3 @@ void RE_generate_texturemargin_adjacentfaces(struct ImBuf *ibuf,
struct Mesh const *me,
char const *uv_layer,
const float uv_offset[2]);
void RE_generate_texturemargin_adjacentfaces_dm(
struct ImBuf *ibuf, char *mask, int margin, struct DerivedMesh *dm, const float uv_offset[2]);

View File

@@ -1,262 +1,556 @@
/* SPDX-FileCopyrightText: 2012 Blender Authors
/* SPDX-FileCopyrightText: 2012-2025 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup render
*
* Baker from the Multires
* =======================
*
* This file is an implementation of a special baking mode which bakes data (normals, displacement)
* from the highest multi-resolution level to the current viewport subdivision level.
*
* The initial historical reasoning for having such baker was:
* - Lower memory footprint than the regular baker.
* - Performance (due to lower overhead compared to the regular baker at that time).
* - Ease of use: no need to have explicit object to define cage.
* Over the time some of these points became less relevant, but the ease of use is still there.
*
* The general idea of the algorithm is pretty simple:
* - Rasterize UV of the mesh at the bake level.
* - For every UV pixel that is rasterized, figure out attributes on the bake level mesh and the
* highest subdivision multi-resolution level (such as normal, position).
* - Do the math (like convert normal to the tangent space),
* - Write pixel to the image.
*
* SubdivCCG is used to access attributes at the highest multi-resolution subdivision level.
*
* The core rasterization logic works on triangles and those triangles are fed to the rasterizer in
* a way that makes it easy to sample attributes in the SubdivCCG:
* - Triangle knows which CCG index it corresponds to (triangle never covers multiple grids).
* - It also knows UV coordinates of its vertices within that grid.
*
* The way triangles are calculated when baking to the base level is pretty straightforward:
* - Triangles are actually calculated from a quad.
* - Quad vertices align with the grid vertices.
* This means that the top level loop iterates over face corners, calculates quad for the grids,
* and passes it to the triangle rasterization.
*
* When baking to a non-0 subdivision level a special trick is used to know grid index and its UV
* coordinates in the base mesh: for every loop in the bake-level mesh the algorithm calculates
* this information using subdiv's foreach logic. This assumes that the bake level mesh is
* calculated using the same foreach logic.
*
* Use low resolution mesh
* -----------------------
*
* This is a special option for the displacement baker.
*
* When it is ON: displacement is calculated between the multi-resolution at the highest
* subdivision level and the bake-level mesh.
*
* When it is OFF: displacement is calculated between the multi-resolution at the highest
* subdivision level and a mesh which is created from the bake level mesh by subdividing it further
* to the subdivision level of the highest multi-resolution level.
*
* Possible optimizations
* ----------------------
*
* - Reuse mesh from the viewport as bake-level mesh.
*
* It could be a bit challenging since mesh could be in sculpt mode, where it has own SubdivCCG
* and does not have UV map on the subdivided state. Additionally, it will make it harder to
* calculate tangent space as well.
*/
#include <cstring>
#include "RE_multires_bake.h"
#include "MEM_guardedalloc.h"
#include <atomic>
#include "DNA_mesh_types.h"
#include "DNA_modifier_types.h"
#include "DNA_scene_types.h"
#include "BLI_array.hh"
#include "BLI_listbase.h"
#include "BLI_math_color.h"
#include "BLI_math_base.hh"
#include "BLI_math_geom.h"
#include "BLI_math_matrix.h"
#include "BLI_math_matrix.hh"
#include "BLI_math_vector.hh"
#include "BLI_span.hh"
#include "BLI_task.hh"
#include "BLI_threads.h"
#include "BKE_attribute.hh"
#include "BKE_ccg.hh"
#include "BKE_customdata.hh"
#include "BKE_global.hh"
#include "BKE_image.hh"
#include "BKE_lib_id.hh"
#include "BKE_mesh.hh"
#include "BKE_mesh_legacy_derived_mesh.hh"
#include "BKE_mesh_tangent.hh"
#include "BKE_multires.hh"
#include "BKE_subsurf.hh"
#include "DEG_depsgraph.hh"
#include "RE_multires_bake.h"
#include "RE_pipeline.h"
#include "RE_texture_margin.h"
#include "BKE_subdiv.hh"
#include "BKE_subdiv_ccg.hh"
#include "BKE_subdiv_eval.hh"
#include "BKE_subdiv_foreach.hh"
#include "BKE_subdiv_mesh.hh"
#include "IMB_imbuf.hh"
#include "IMB_imbuf_types.hh"
#include "DEG_depsgraph.hh"
#include "RE_texture_margin.h"
namespace blender::render {
namespace {
using MPassKnownData = void (*)(Span<float3> vert_positions,
Span<float3> vert_normals,
OffsetIndices<int> faces,
Span<int> corner_verts,
Span<int3> corner_tris,
Span<int> tri_faces,
Span<float2> uv_map,
DerivedMesh *hires_dm,
void *thread_data,
void *bake_data,
ImBuf *ibuf,
int face_index,
int lvl,
const float st[2],
float tangmat[3][3],
int x,
int y);
namespace subdiv = bke::subdiv;
using MInitBakeData = void *(*)(MultiresBakeRender &bake, ImBuf *ibuf);
using MFreeBakeData = void (*)(void *bake_data);
/* -------------------------------------------------------------------- */
/** \name Math utilities that should actually be in the BLI
* The only reason they are here is that there is currently no great place to put them to.
* \{ */
struct MultiresBakeResult {
float height_min, height_max;
template<class T> T interp_barycentric_triangle(const T data[3], const float2 &uv)
{
return data[0] * uv.x + data[1] * uv.y + data[2] * (1.0f - uv.x - uv.y);
}
template<class T>
T interp_bilinear_quad(
const float u, const float v, const T &p0, const T &p1, const T &p2, const T &p3)
{
const float w0 = (1 - u) * (1 - v);
const float w1 = u * (1 - v);
const float w2 = u * v;
const float w3 = (1 - u) * v;
return p0 * w0 + p1 * w1 + p2 * w2 + p3 * w3;
}
float2 resolve_tri_uv(const float2 &st, const float2 &st0, const float2 &st1, const float2 &st2)
{
float2 uv;
resolve_tri_uv_v2(uv, st, st0, st1, st2);
return uv;
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Implementation of data accessor from the subdiv
* \{ */
template<class T> class Grid {
Span<T> data_;
int side_size_;
T get_element(const int x, const int y) const
{
const int64_t index = int64_t(y) * side_size_ + x;
return data_[index];
}
public:
Grid(const Span<T> data, const int side_size) : data_(data), side_size_(side_size)
{
BLI_assert(data.size() == side_size_ * side_size_);
}
T sample(const float2 uv) const
{
const float2 xy = uv * (side_size_ - 1);
const int x0 = int(xy.x);
const int x1 = x0 >= (side_size_ - 1) ? (side_size_ - 1) : (x0 + 1);
const int y0 = int(xy.y);
const int y1 = y0 >= (side_size_ - 1) ? (side_size_ - 1) : (y0 + 1);
const float u = xy.x - x0;
const float v = xy.y - y0;
return interp_bilinear_quad(
u, v, get_element(x0, y0), get_element(x1, y0), get_element(x1, y1), get_element(x0, y1));
}
};
struct MResolvePixelData {
/* Data from low-resolution mesh. */
template<class T>
Grid<T> get_subdiv_ccg_grid(const SubdivCCG &subdiv_ccg, const int grid_index, const Span<T> data)
{
const int64_t offset = int64_t(grid_index) * subdiv_ccg.grid_area;
return Grid(Span(data.data() + offset, subdiv_ccg.grid_area), subdiv_ccg.grid_size);
}
float3 sample_position_on_subdiv_ccg(const SubdivCCG &subdiv_ccg,
const int grid_index,
const float2 grid_uv)
{
const Grid<float3> grid = get_subdiv_ccg_grid(
subdiv_ccg, grid_index, subdiv_ccg.positions.as_span());
return grid.sample(grid_uv);
}
float3 sample_normal_on_subdiv_ccg(const SubdivCCG &subdiv_ccg,
const int grid_index,
const float2 grid_uv)
{
/* TODO(sergey): Support flat normals.
* It seems that the baker always used smooth interpolation for CCG. */
const Grid<float3> grid = get_subdiv_ccg_grid(
subdiv_ccg, grid_index, subdiv_ccg.normals.as_span());
return grid.sample(grid_uv);
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Storage of mesh arrays, for quicker access without any lookup
* \{ */
struct MeshArrays {
Span<float3> vert_positions;
OffsetIndices<int> faces;
Span<float3> vert_normals;
Span<int> corner_verts;
Span<int3> corner_tris;
Span<float3> corner_normals;
Span<int> tri_faces;
Span<float3> vert_normals;
OffsetIndices<int> faces;
Span<float3> face_normals;
VArraySpan<bool> sharp_faces;
Span<float2> uv_map;
VArraySpan<float2> uv_map;
/* May be null. */
const int *material_indices;
const bool *sharp_faces;
VArraySpan<int> material_indices;
float uv_offset[2];
Span<float4> pvtangent;
int w, h;
int tri_index;
MeshArrays() = default;
DerivedMesh *hires_dm;
explicit MeshArrays(const Mesh &mesh)
{
bke::AttributeAccessor attributes = mesh.attributes();
int lvl;
void *thread_data;
void *bake_data;
const StringRef active_uv_map = CustomData_get_active_layer_name(&mesh.corner_data,
CD_PROP_FLOAT2);
vert_positions = mesh.vert_positions();
vert_normals = mesh.vert_normals();
corner_verts = mesh.corner_verts();
corner_tris = mesh.corner_tris();
corner_normals = mesh.corner_normals();
tri_faces = mesh.corner_tri_faces();
faces = mesh.faces();
face_normals = mesh.face_normals();
sharp_faces = *attributes.lookup_or_default<bool>("sharp_face", bke::AttrDomain::Face, false);
uv_map = *attributes.lookup<float2>(active_uv_map, bke::AttrDomain::Corner);
material_indices = *attributes.lookup_or_default<int>(
"material_index", bke::AttrDomain::Face, 0);
}
};
/* Calculate UV map coordinates at the center of the face (grid coordinates (0, 0)). */
static float2 face_center_tex_uv_calc(const MeshArrays &mesh_arrays, const int face_index)
{
const IndexRange &face = mesh_arrays.faces[face_index];
float2 tex_uv_acc(0.0f, 0.0f);
for (const int corner : face) {
tex_uv_acc += mesh_arrays.uv_map[corner];
}
return tex_uv_acc / face.size();
}
/* Calculate smooth normal coordinates at the center of the face (grid coordinates (0, 0)).
* NOTE: The returned value is not normalized to allow linear interpolation with other grid
* elements. */
static float3 face_center_smooth_normal_calc(const MeshArrays &mesh_arrays, const int face_index)
{
const IndexRange &face = mesh_arrays.faces[face_index];
float3 normal_acc(0.0f, 0.0f, 0.0f);
for (const int corner : face) {
normal_acc += mesh_arrays.vert_normals[mesh_arrays.corner_verts[corner]];
}
/* NOTE: No normalization here: do it after interpolation at the baking point.
*
* This preserves linearity of operation. If normalization is done here interpolation will go
* wrong. */
return normal_acc / face.size();
}
/* Calculate tangent space for the given mesh state. */
Array<float4> calc_uv_tangents(const MeshArrays &mesh_arrays)
{
Array<Array<float4>> tangent_data = bke::mesh::calc_uv_tangents(mesh_arrays.vert_positions,
mesh_arrays.faces,
mesh_arrays.corner_verts,
mesh_arrays.corner_tris,
mesh_arrays.tri_faces,
mesh_arrays.sharp_faces,
mesh_arrays.vert_normals,
mesh_arrays.face_normals,
mesh_arrays.corner_normals,
{mesh_arrays.uv_map});
return tangent_data[0];
}
/* Calculate tangent space at the center of the face (grid coordinates (0, 0)). */
static float4 face_center_uv_tangent_calc(const MeshArrays &mesh_arrays,
const Span<float4> uv_tangents,
const int face_index)
{
const IndexRange &face = mesh_arrays.faces[face_index];
float4 tex_uv_acc(0.0f, 0.0f, 0.0f, 0.0f);
for (const int corner : face) {
tex_uv_acc += uv_tangents[corner];
}
return tex_uv_acc / face.size();
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Common data types and utilities
* \{ */
struct ExtraBuffers {
Array<float> displacement_buffer;
Array<char> mask_buffer;
};
struct RasterizeTile {
ImBuf *ibuf = nullptr; /* Image buffer of the tile. */
ExtraBuffers *extra_buffers = nullptr;
float2 uv_offset; /* UV coordinate of the tile origin. */
};
struct RasterizeTriangle {
/* UV coordinates with the CCG. All vertices belong to the same grid. */
int grid_index;
float2 grid_uvs[3];
/* UV texture coordinates of the triangle vertices within the tile. */
float2 tex_uvs[3];
/* Positions and normals of the vertices, at the bake level. */
float3 positions[3];
float3 normals[3];
/* Triangle is shaded flat: it has the same normal at every point of its surface.
* Face normal is stored in all elements of the normals array. */
bool is_flat;
/* Optional tangents.
* The uv_tangents might be uninitialized if has_uv_tangents=false. */
bool has_uv_tangents;
float4 uv_tangents[3];
/* Corresponds to the same fields in the #RasterizeQuad.
* Refer to its documentation for more details. */
int bake_level_quad_index;
float2 bake_level_quad_uvs[3];
float3 get_position(const float2 &uv) const
{
return interp_barycentric_triangle(positions, uv);
}
float3 get_normal(const float2 &uv) const
{
if (is_flat) {
return normals[0];
}
return math::normalize(interp_barycentric_triangle(normals, uv));
}
float2 get_bake_level_quad_uv(const float2 &uv) const
{
return interp_barycentric_triangle(bake_level_quad_uvs, uv);
}
};
struct RasterizeQuad {
/* UV coordinates with the CCG. All vertices belong to the same grid. */
int grid_index;
float2 grid_uvs[4];
/* UV texture coordinates of the triangle vertices within the tile. */
float2 tex_uvs[4];
/* Positions and normals of the vertices, at the bake level. */
float3 positions[4];
float3 normals[4];
/* Quad is shaded flat: it has the same normal at every point of its surface.
* Face normal is stored in all elements of the normals array. */
bool is_flat;
/* Optional tangents.
* The uv_tangents might be uninitialized if has_uv_tangents=false. */
bool has_uv_tangents;
float4 uv_tangents[4];
/* Face index and UV coordinate within the face when baking to non-base mesh (with some
* subdivision levels applied).
*
* The fields are referring to the face as a quad because with one subdivision level applied all
* faces becomes quads.
*
* The uv origin is at the first face corner, U points in the direction to the next corner, the
* V points in the direction of the previous corner.
*
* When baking to the base level the fields values is unspecified. */
int bake_level_quad_index;
float2 bake_level_quad_uvs[4];
};
struct RasterizeResult {
float height_min = FLT_MAX;
float height_max = -FLT_MAX;
};
struct BakedImBuf {
Image *image;
ImBuf *ibuf;
MPassKnownData pass_data;
/* material aligned UV array */
Image **image_array;
ExtraBuffers extra_buffers;
float2 uv_offset;
};
using MFlushPixel = void (*)(const MResolvePixelData *data, int x, int y);
struct MultiresBakeResult {
Vector<BakedImBuf> baked_ibufs;
struct MBakeRast {
int w, h;
char *texels;
const MResolvePixelData *data;
MFlushPixel flush_pixel;
bool *do_update;
/* Minimum and maximum height during displacement baking. */
float height_min = FLT_MAX;
float height_max = -FLT_MAX;
};
struct MHeightBakeData {
float *heights;
DerivedMesh *ssdm;
const int *orig_index_mp_to_orig;
};
class MultiresBaker {
public:
virtual ~MultiresBaker() = default;
struct MNormalBakeData {
const int *orig_index_mp_to_orig;
};
virtual float3 bake_pixel(const RasterizeTriangle &triangle,
const float2 &bary_uv,
const float2 &grid_uv,
RasterizeResult &result) const = 0;
struct BakeImBufuserData {
float *displacement_buffer;
char *mask_buffer;
};
virtual void write_pixel(const RasterizeTile &tile,
const int2 &coord,
const float3 &value) const = 0;
static void multiresbake_get_normal(const MResolvePixelData *data,
const int tri_num,
const int vert_index,
float r_normal[3])
{
const int face_index = data->tri_faces[tri_num];
const bool smoothnormal = !(data->sharp_faces && data->sharp_faces[face_index]);
protected:
void write_pixel_to_image_buffer(ImBuf &ibuf, const int2 &coord, const float3 &value) const
{
const int64_t pixel = int64_t(ibuf.x) * coord.y + coord.x;
if (smoothnormal) {
const int vi = data->corner_verts[data->corner_tris[tri_num][vert_index]];
copy_v3_v3(r_normal, data->vert_normals[vi]);
}
else {
copy_v3_v3(r_normal, data->face_normals[face_index]);
}
}
static void init_bake_rast(MBakeRast *bake_rast,
const ImBuf *ibuf,
const MResolvePixelData *data,
MFlushPixel flush_pixel,
bool *do_update)
{
BakeImBufuserData *userdata = (BakeImBufuserData *)ibuf->userdata;
memset(bake_rast, 0, sizeof(MBakeRast));
bake_rast->texels = userdata->mask_buffer;
bake_rast->w = ibuf->x;
bake_rast->h = ibuf->y;
bake_rast->data = data;
bake_rast->flush_pixel = flush_pixel;
bake_rast->do_update = do_update;
}
static void flush_pixel(const MResolvePixelData *data, const int x, const int y)
{
const float st[2] = {(x + 0.5f) / data->w + data->uv_offset[0],
(y + 0.5f) / data->h + data->uv_offset[1]};
const float *st0, *st1, *st2;
float no0[3], no1[3], no2[3];
float fUV[2], from_tang[3][3], to_tang[3][3];
float u, v, w, sign;
int r;
st0 = data->uv_map[data->corner_tris[data->tri_index][0]];
st1 = data->uv_map[data->corner_tris[data->tri_index][1]];
st2 = data->uv_map[data->corner_tris[data->tri_index][2]];
multiresbake_get_normal(data, data->tri_index, 0, no0); /* can optimize these 3 into one call */
multiresbake_get_normal(data, data->tri_index, 1, no1);
multiresbake_get_normal(data, data->tri_index, 2, no2);
resolve_tri_uv_v2(fUV, st, st0, st1, st2);
u = fUV[0];
v = fUV[1];
w = 1 - u - v;
if (!data->pvtangent.is_empty()) {
const float4 &tang0 = data->pvtangent[data->corner_tris[data->tri_index][0]];
const float4 &tang1 = data->pvtangent[data->corner_tris[data->tri_index][1]];
const float4 &tang2 = data->pvtangent[data->corner_tris[data->tri_index][2]];
/* the sign is the same at all face vertices for any non-degenerate face.
* Just in case we clamp the interpolated value though. */
sign = (tang0[3] * u + tang1[3] * v + tang2[3] * w) < 0 ? (-1.0f) : 1.0f;
/* this sequence of math is designed specifically as is with great care
* to be compatible with our shader. Please don't change without good reason. */
for (r = 0; r < 3; r++) {
from_tang[0][r] = tang0[r] * u + tang1[r] * v + tang2[r] * w;
from_tang[2][r] = no0[r] * u + no1[r] * v + no2[r] * w;
if (ibuf.float_buffer.data) {
/* TODO(sergey): Properly tackle ibuf.channels. */
BLI_assert(ibuf.channels == 4);
float *rrgbf = ibuf.float_buffer.data + pixel * 4;
rrgbf[0] = value[0];
rrgbf[1] = value[1];
rrgbf[2] = value[2];
rrgbf[3] = 1.0f;
ibuf.userflags |= IB_RECT_INVALID;
}
cross_v3_v3v3(from_tang[1], from_tang[2], from_tang[0]); /* `B = sign * cross(N, T)` */
mul_v3_fl(from_tang[1], sign);
invert_m3_m3(to_tang, from_tang);
}
else {
zero_m3(to_tang);
}
if (ibuf.byte_buffer.data) {
uchar *rrgb = ibuf.byte_buffer.data + pixel * 4;
unit_float_to_uchar_clamp_v3(rrgb, value);
rrgb[3] = 255;
}
data->pass_data(data->vert_positions,
data->vert_normals,
data->faces,
data->corner_verts,
data->corner_tris,
data->tri_faces,
data->uv_map,
data->hires_dm,
data->thread_data,
data->bake_data,
data->ibuf,
data->tri_index,
data->lvl,
st,
to_tang,
x,
y);
ibuf.userflags |= IB_DISPLAY_BUFFER_INVALID;
}
};
static bool multiresbake_test_break(const MultiresBakeRender &bake)
{
if (!bake.stop) {
/* This means baker is executed outside from job system (for example, from Python API).
* In this case there is no need to cancel, as it will be quite strange to cancel out
* execution of a script. */
return false;
}
return *bake.stop || G.is_break;
}
static void set_rast_triangle(const MBakeRast *bake_rast, const int x, const int y)
static float2 get_tile_uv(Image &image, ImageTile &tile)
{
const int w = bake_rast->w;
const int h = bake_rast->h;
float uv_offset[2];
BKE_image_get_tile_uv(&image, tile.tile_number, uv_offset);
return uv_offset;
}
static bool need_tangent(const MultiresBakeRender &bake)
{
return bake.mode == RE_BAKE_NORMALS;
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Baking pipeline
* \{ */
static void flush_pixel(const MultiresBaker &baker,
const RasterizeTile &tile,
const RasterizeTriangle &triangle,
const int x,
const int y,
RasterizeResult &result)
{
const float2 st{(x + 0.5f) / tile.ibuf->x + tile.uv_offset[0],
(y + 0.5f) / tile.ibuf->y + tile.uv_offset[1]};
const float2 bary_uv = resolve_tri_uv(
st, triangle.tex_uvs[0], triangle.tex_uvs[1], triangle.tex_uvs[2]);
const float2 grid_uv = interp_barycentric_triangle(triangle.grid_uvs, bary_uv);
const float3 baked_pixel = baker.bake_pixel(triangle, bary_uv, grid_uv, result);
baker.write_pixel(tile, int2(x, y), baked_pixel);
}
static void set_rast_triangle(const MultiresBaker &baker,
const RasterizeTile &tile,
const RasterizeTriangle &triangle,
const int x,
const int y,
RasterizeResult &result)
{
const int w = tile.ibuf->x;
const int h = tile.ibuf->y;
if (x >= 0 && x < w && y >= 0 && y < h) {
if ((bake_rast->texels[y * w + x]) == 0) {
bake_rast->texels[y * w + x] = FILTER_MASK_USED;
flush_pixel(bake_rast->data, x, y);
if (bake_rast->do_update) {
*bake_rast->do_update = true;
}
const int64_t pixel = int64_t(y) * w + x;
if (tile.extra_buffers->mask_buffer[pixel] == FILTER_MASK_NULL) {
tile.extra_buffers->mask_buffer[pixel] = FILTER_MASK_USED;
flush_pixel(baker, tile, triangle, x, y, result);
}
}
}
static void rasterize_half(const MBakeRast *bake_rast,
static void rasterize_half(const MultiresBaker &baker,
const RasterizeTile &tile,
const RasterizeTriangle &triangle,
const float2 &s0,
const float2 &s1,
const float2 &l0,
const float2 &l1,
const int y0_in,
const int y1_in,
const bool is_mid_right)
const bool is_mid_right,
RasterizeResult &result)
{
const bool s_stable = fabsf(s1.y - s0.y) > FLT_EPSILON;
const bool l_stable = fabsf(l1.y - l0.y) > FLT_EPSILON;
const int w = bake_rast->w;
const int h = bake_rast->h;
const int w = tile.ibuf->x;
const int h = tile.ibuf->y;
if (y1_in <= 0 || y0_in >= h) {
return;
@@ -269,7 +563,7 @@ static void rasterize_half(const MBakeRast *bake_rast,
/*-b(x-x0) + a(y-y0) = 0 */
float x_l = s_stable ? (s0.x + (((s1.x - s0.x) * (y - s0.y)) / (s1.y - s0.y))) : s0.x;
float x_r = l_stable ? (l0.x + (((l1.x - l0.x) * (y - l0.y)) / (l1.y - l0.y))) : l0.x;
if (is_mid_right != 0) {
if (is_mid_right) {
std::swap(x_l, x_r);
}
@@ -281,18 +575,22 @@ static void rasterize_half(const MBakeRast *bake_rast,
iXr = iXr >= w ? w : iXr;
for (int x = iXl; x < iXr; x++) {
set_rast_triangle(bake_rast, x, y);
set_rast_triangle(baker, tile, triangle, x, y, result);
}
}
}
}
static void bake_rasterize(const MBakeRast *bake_rast,
const float2 &st0_in,
const float2 &st1_in,
const float2 &st2_in)
static void rasterize_triangle(const MultiresBaker &baker,
const RasterizeTile &tile,
const RasterizeTriangle &triangle,
RasterizeResult &result)
{
const float2 ibuf_size(bake_rast->w, bake_rast->h);
const float2 ibuf_size(tile.ibuf->x, tile.ibuf->y);
const float2 &st0_in = triangle.tex_uvs[0];
const float2 &st1_in = triangle.tex_uvs[1];
const float2 &st2_in = triangle.tex_uvs[2];
float2 p_low = st0_in * ibuf_size - 0.5f;
float2 p_mid = st1_in * ibuf_size - 0.5f;
@@ -326,794 +624,324 @@ static void bake_rasterize(const MBakeRast *bake_rast,
const int yhi_beg = int(ceilf(p_mid.y));
const int yhi = int(ceilf(p_high.y));
// if (fTmi>ceilf(fTlo))
rasterize_half(bake_rast, p_low, p_mid, p_low, p_high, ylo, yhi_beg, is_mid_right);
rasterize_half(bake_rast, p_mid, p_high, p_low, p_high, yhi_beg, yhi, is_mid_right);
rasterize_half(
baker, tile, triangle, p_low, p_mid, p_low, p_high, ylo, yhi_beg, is_mid_right, result);
rasterize_half(
baker, tile, triangle, p_mid, p_high, p_low, p_high, yhi_beg, yhi, is_mid_right, result);
}
static bool multiresbake_test_break(const MultiresBakeRender &bake)
static void rasterize_quad(const MultiresBaker &baker,
const RasterizeTile &tile,
const RasterizeQuad &quad,
RasterizeResult &result)
{
if (!bake.stop) {
/* This means baker is executed outside from job system. */
return false;
RasterizeTriangle triangle;
triangle.grid_index = quad.grid_index;
triangle.is_flat = quad.is_flat;
triangle.has_uv_tangents = quad.has_uv_tangents;
triangle.bake_level_quad_index = quad.bake_level_quad_index;
const int3 quad_split_data[2] = {{0, 1, 2}, {2, 3, 0}};
for (const int3 &triangle_idx : Span(quad_split_data, 2)) {
triangle.grid_uvs[0] = quad.grid_uvs[triangle_idx.x];
triangle.grid_uvs[1] = quad.grid_uvs[triangle_idx.y];
triangle.grid_uvs[2] = quad.grid_uvs[triangle_idx.z];
triangle.tex_uvs[0] = quad.tex_uvs[triangle_idx.x];
triangle.tex_uvs[1] = quad.tex_uvs[triangle_idx.y];
triangle.tex_uvs[2] = quad.tex_uvs[triangle_idx.z];
triangle.positions[0] = quad.positions[triangle_idx.x];
triangle.positions[1] = quad.positions[triangle_idx.y];
triangle.positions[2] = quad.positions[triangle_idx.z];
triangle.normals[0] = quad.normals[triangle_idx.x];
triangle.normals[1] = quad.normals[triangle_idx.y];
triangle.normals[2] = quad.normals[triangle_idx.z];
if (triangle.has_uv_tangents) {
triangle.uv_tangents[0] = quad.uv_tangents[triangle_idx.x];
triangle.uv_tangents[1] = quad.uv_tangents[triangle_idx.y];
triangle.uv_tangents[2] = quad.uv_tangents[triangle_idx.z];
}
triangle.bake_level_quad_uvs[0] = quad.bake_level_quad_uvs[triangle_idx.x];
triangle.bake_level_quad_uvs[1] = quad.bake_level_quad_uvs[triangle_idx.y];
triangle.bake_level_quad_uvs[2] = quad.bake_level_quad_uvs[triangle_idx.z];
rasterize_triangle(baker, tile, triangle, result);
}
return *bake.stop || G.is_break;
}
/* **** Threading routines **** */
/** \} */
struct MultiresBakeQueue {
int cur_tri;
int tot_tri;
SpinLock spin;
};
/* -------------------------------------------------------------------- */
/** \name Displacement Baker
* \{ */
struct MultiresBakeThread {
/* this data is actually shared between all the threads */
MultiresBakeQueue *queue;
MultiresBakeRender *bake;
Image *image;
void *bake_data;
int num_total_faces;
class MultiresBaseDisplacementBaker : public MultiresBaker {
/* SubdivCCG at the highest multi-resolution level. */
const SubdivCCG &high_subdiv_ccg_;
/* thread-specific data */
MBakeRast bake_rast;
MResolvePixelData data;
/* Baking happens to non-zero subdivision level. */
bool is_baking_to_subdivided_mesh_ = false;
/* displacement-specific data */
float height_min, height_max;
};
/* SubdivCCG created from the bake level mesh by subdividing it to the highest multi-resolution
* level. It is used as "reference" surface when baking with "Low Resolution Mesh" option
* disabled. */
std::unique_ptr<SubdivCCG> subdivided_ccg_;
static int multires_bake_queue_next_tri(MultiresBakeQueue *queue)
{
int face = -1;
/* TODO: it could worth making it so thread will handle neighbor faces
* for better memory cache utilization
*/
BLI_spin_lock(&queue->spin);
if (queue->cur_tri < queue->tot_tri) {
face = queue->cur_tri;
queue->cur_tri++;
}
BLI_spin_unlock(&queue->spin);
return face;
}
static void *do_multires_bake_thread(void *data_v)
{
MultiresBakeThread *handle = (MultiresBakeThread *)data_v;
MResolvePixelData *data = &handle->data;
MBakeRast *bake_rast = &handle->bake_rast;
MultiresBakeRender &bake = *handle->bake;
int tri_index;
while ((tri_index = multires_bake_queue_next_tri(handle->queue)) >= 0) {
const int3 &tri = data->corner_tris[tri_index];
const int face_i = data->tri_faces[tri_index];
const short mat_nr = data->material_indices == nullptr ? 0 : data->material_indices[face_i];
if (multiresbake_test_break(bake)) {
break;
}
Image *tri_image = mat_nr < bake.ob_image.size() ? bake.ob_image[mat_nr] : nullptr;
if (tri_image != handle->image) {
continue;
}
data->tri_index = tri_index;
float uv[3][2];
sub_v2_v2v2(uv[0], data->uv_map[tri[0]], data->uv_offset);
sub_v2_v2v2(uv[1], data->uv_map[tri[1]], data->uv_offset);
sub_v2_v2v2(uv[2], data->uv_map[tri[2]], data->uv_offset);
bake_rasterize(bake_rast, uv[0], uv[1], uv[2]);
/* tag image buffer for refresh */
if (data->ibuf->float_buffer.data) {
data->ibuf->userflags |= IB_RECT_INVALID;
}
data->ibuf->userflags |= IB_DISPLAY_BUFFER_INVALID;
/* update progress */
BLI_spin_lock(&handle->queue->spin);
bake.baked_faces++;
if (bake.do_update) {
*bake.do_update = true;
}
if (bake.progress) {
*bake.progress = (float(bake.baked_objects) +
float(bake.baked_faces) / handle->num_total_faces) /
bake.tot_obj;
}
BLI_spin_unlock(&handle->queue->spin);
}
return nullptr;
}
/* some of arrays inside ccgdm are lazy-initialized, which will generally
* require lock around accessing such data
* this function will ensure all arrays are allocated before threading started
*/
static void init_ccgdm_arrays(DerivedMesh *dm)
{
CCGElem **grid_data;
CCGKey key;
int grid_size;
const int *grid_offset;
grid_size = dm->getGridSize(dm);
grid_data = dm->getGridData(dm);
grid_offset = dm->getGridOffset(dm);
dm->getGridKey(dm, &key);
(void)grid_size;
(void)grid_data;
(void)grid_offset;
}
static void do_multires_bake(MultiresBakeRender &bake,
Image *image,
ImageTile *tile,
ImBuf *ibuf,
const bool require_tangent,
const MPassKnownData passKnownData,
const MInitBakeData initBakeData,
const MFreeBakeData freeBakeData,
MultiresBakeResult &result)
{
DerivedMesh *dm = bake.lores_dm;
const int lvl = bake.lvl;
if (dm->getNumPolys(dm) == 0) {
return;
}
const Span<float2> uv_map(
reinterpret_cast<const float2 *>(dm->getLoopDataArray(dm, CD_PROP_FLOAT2)),
dm->getNumLoops(dm));
Array<float4> pvtangent;
Mesh *temp_mesh = BKE_mesh_new_nomain(
dm->getNumVerts(dm), dm->getNumEdges(dm), dm->getNumPolys(dm), dm->getNumLoops(dm));
temp_mesh->vert_positions_for_write().copy_from(
{reinterpret_cast<const float3 *>(dm->getVertArray(dm)), temp_mesh->verts_num});
temp_mesh->edges_for_write().copy_from(
{reinterpret_cast<const int2 *>(dm->getEdgeArray(dm)), temp_mesh->edges_num});
temp_mesh->face_offsets_for_write().copy_from({dm->getPolyArray(dm), temp_mesh->faces_num + 1});
temp_mesh->corner_verts_for_write().copy_from(
{dm->getCornerVertArray(dm), temp_mesh->corners_num});
temp_mesh->corner_edges_for_write().copy_from(
{dm->getCornerEdgeArray(dm), temp_mesh->corners_num});
const Span<float3> positions = temp_mesh->vert_positions();
const OffsetIndices faces = temp_mesh->faces();
const Span<int> corner_verts = temp_mesh->corner_verts();
const Span<float3> vert_normals = temp_mesh->vert_normals();
const Span<float3> face_normals = temp_mesh->face_normals();
const Span<int3> corner_tris = temp_mesh->corner_tris();
const Span<int> tri_faces = temp_mesh->corner_tri_faces();
if (require_tangent) {
const bool *sharp_edges = static_cast<const bool *>(
CustomData_get_layer_named(&dm->edgeData, CD_PROP_BOOL, "sharp_edge"));
const bool *sharp_faces = static_cast<const bool *>(
CustomData_get_layer_named(&dm->polyData, CD_PROP_BOOL, "sharp_face"));
/* Copy sharp faces and edges, for corner normals domain and tangents
* to be computed correctly. */
if (sharp_edges != nullptr) {
bke::MutableAttributeAccessor attributes = temp_mesh->attributes_for_write();
attributes.add<bool>("sharp_edge",
bke::AttrDomain::Edge,
bke::AttributeInitVArray(VArray<bool>::from_span(
Span<bool>(sharp_edges, temp_mesh->edges_num))));
}
if (sharp_faces != nullptr) {
bke::MutableAttributeAccessor attributes = temp_mesh->attributes_for_write();
attributes.add<bool>("sharp_face",
bke::AttrDomain::Face,
bke::AttributeInitVArray(VArray<bool>::from_span(
Span<bool>(sharp_faces, temp_mesh->faces_num))));
}
const Span<float3> corner_normals = temp_mesh->corner_normals();
Array<Array<float4>> tangent_data = bke::mesh::calc_uv_tangents(
positions,
faces,
corner_verts,
corner_tris,
tri_faces,
sharp_faces ? Span(sharp_faces, faces.size()) : Span<bool>(),
vert_normals,
face_normals,
corner_normals,
{uv_map});
pvtangent = std::move(tangent_data[0]);
}
/* All threads share the same custom bake data. */
void *bake_data = nullptr;
if (initBakeData) {
bake_data = initBakeData(bake, ibuf);
}
ListBase threads;
const int tot_thread = bake.threads > 0 ? bake.threads : BLI_system_thread_count();
if (tot_thread > 1) {
BLI_threadpool_init(&threads, do_multires_bake_thread, tot_thread);
}
Array<MultiresBakeThread> handles(tot_thread);
init_ccgdm_arrays(bake.hires_dm);
/* Faces queue. */
MultiresBakeQueue queue;
queue.cur_tri = 0;
queue.tot_tri = corner_tris.size();
BLI_spin_init(&queue.spin);
/* Fill in threads handles. */
for (int i = 0; i < tot_thread; i++) {
MultiresBakeThread *handle = &handles[i];
handle->bake = &bake;
handle->image = image;
handle->num_total_faces = queue.tot_tri * BLI_listbase_count(&image->tiles);
handle->queue = &queue;
handle->data.vert_positions = positions;
handle->data.faces = faces;
handle->data.corner_verts = corner_verts;
handle->data.corner_tris = corner_tris;
handle->data.tri_faces = tri_faces;
handle->data.vert_normals = vert_normals;
handle->data.face_normals = face_normals;
handle->data.material_indices = static_cast<const int *>(
CustomData_get_layer_named(&dm->polyData, CD_PROP_INT32, "material_index"));
handle->data.sharp_faces = static_cast<const bool *>(
CustomData_get_layer_named(&dm->polyData, CD_PROP_BOOL, "sharp_face"));
handle->data.uv_map = uv_map;
BKE_image_get_tile_uv(image, tile->tile_number, handle->data.uv_offset);
handle->data.pvtangent = pvtangent;
handle->data.w = ibuf->x;
handle->data.h = ibuf->y;
handle->data.hires_dm = bake.hires_dm;
handle->data.lvl = lvl;
handle->data.pass_data = passKnownData;
handle->data.thread_data = handle;
handle->data.bake_data = bake_data;
handle->data.ibuf = ibuf;
handle->height_min = FLT_MAX;
handle->height_max = -FLT_MAX;
init_bake_rast(&handle->bake_rast, ibuf, &handle->data, flush_pixel, bake.do_update);
if (tot_thread > 1) {
BLI_threadpool_insert(&threads, handle);
public:
MultiresBaseDisplacementBaker(const MultiresBakeRender &bake,
const Mesh &bake_level_mesh,
const SubdivCCG &subdiv_ccg)
: high_subdiv_ccg_(subdiv_ccg)
{
if (!bake.use_low_resolution_mesh) {
create_subdivided_ccg(bake_level_mesh, *bake.multires_modifier);
is_baking_to_subdivided_mesh_ = bake.multires_modifier->lvl > 0;
}
}
/* Run threads. */
if (tot_thread > 1) {
BLI_threadpool_end(&threads);
}
else {
do_multires_bake_thread(handles.data());
}
for (int i = 0; i < tot_thread; i++) {
result.height_min = min_ff(result.height_min, handles[i].height_min);
result.height_max = max_ff(result.height_max, handles[i].height_max);
}
BLI_spin_end(&queue.spin);
/* Finalize baking. */
if (freeBakeData) {
freeBakeData(bake_data);
}
BKE_id_free(nullptr, temp_mesh);
}
/* mode = 0: interpolate normals,
* mode = 1: interpolate coord */
static void interp_bilinear_grid(
const CCGKey &key, CCGElem *grid, float crn_x, float crn_y, int mode, float res[3])
{
int x0, x1, y0, y1;
float u, v;
float data[4][3];
x0 = int(crn_x);
x1 = x0 >= (key.grid_size - 1) ? (key.grid_size - 1) : (x0 + 1);
y0 = int(crn_y);
y1 = y0 >= (key.grid_size - 1) ? (key.grid_size - 1) : (y0 + 1);
u = crn_x - x0;
v = crn_y - y0;
if (mode == 0) {
copy_v3_v3(data[0], CCG_grid_elem_no(key, grid, x0, y0));
copy_v3_v3(data[1], CCG_grid_elem_no(key, grid, x1, y0));
copy_v3_v3(data[2], CCG_grid_elem_no(key, grid, x1, y1));
copy_v3_v3(data[3], CCG_grid_elem_no(key, grid, x0, y1));
}
else {
copy_v3_v3(data[0], CCG_grid_elem_co(key, grid, x0, y0));
copy_v3_v3(data[1], CCG_grid_elem_co(key, grid, x1, y0));
copy_v3_v3(data[2], CCG_grid_elem_co(key, grid, x1, y1));
copy_v3_v3(data[3], CCG_grid_elem_co(key, grid, x0, y1));
}
interp_bilinear_quad_v3(data, u, v, res);
}
static void get_ccgdm_data(const OffsetIndices<int> lores_polys,
DerivedMesh *hidm,
const int *index_mp_to_orig,
const int lvl,
const int face_index,
const float u,
const float v,
float co[3],
float n[3])
{
CCGElem **grid_data;
CCGKey key;
float crn_x, crn_y;
int grid_size, S, face_side;
int *grid_offset, g_index;
grid_size = hidm->getGridSize(hidm);
grid_data = hidm->getGridData(hidm);
grid_offset = hidm->getGridOffset(hidm);
hidm->getGridKey(hidm, &key);
if (lvl == 0) {
face_side = (grid_size << 1) - 1;
g_index = grid_offset[face_index];
S = mdisp_rot_face_to_crn(lores_polys[face_index].size(),
face_side,
u * (face_side - 1),
v * (face_side - 1),
&crn_x,
&crn_y);
}
else {
/* number of faces per grid side */
int polys_per_grid_side = (1 << (lvl - 1));
/* get the original cage face index */
int cage_face_index = index_mp_to_orig ? index_mp_to_orig[face_index] : face_index;
/* local offset in total cage face grids
* `(1 << (2 * lvl))` is number of all faces for one cage face */
int loc_cage_poly_ofs = face_index % (1 << (2 * lvl));
/* local offset in the vertex grid itself */
int cell_index = loc_cage_poly_ofs % (polys_per_grid_side * polys_per_grid_side);
int cell_side = (grid_size - 1) / polys_per_grid_side;
/* row and column based on grid side */
int row = cell_index / polys_per_grid_side;
int col = cell_index % polys_per_grid_side;
/* S is the vertex whose grid we are examining */
S = face_index / (1 << (2 * (lvl - 1))) - grid_offset[cage_face_index];
/* get offset of grid data for original cage face */
g_index = grid_offset[cage_face_index];
crn_y = (row * cell_side) + u * cell_side;
crn_x = (col * cell_side) + v * cell_side;
}
CLAMP(crn_x, 0.0f, grid_size);
CLAMP(crn_y, 0.0f, grid_size);
if (n != nullptr) {
interp_bilinear_grid(key, grid_data[g_index + S], crn_x, crn_y, 0, n);
}
if (co != nullptr) {
interp_bilinear_grid(key, grid_data[g_index + S], crn_x, crn_y, 1, co);
}
}
/* mode = 0: interpolate normals,
* mode = 1: interpolate coord */
static void interp_bilinear_mpoly(const Span<float3> vert_positions,
const Span<float3> vert_normals,
const Span<int> corner_verts,
const IndexRange face,
const float u,
const float v,
const int mode,
float res[3])
{
float data[4][3];
if (mode == 0) {
copy_v3_v3(data[0], vert_normals[corner_verts[face[0]]]);
copy_v3_v3(data[1], vert_normals[corner_verts[face[1]]]);
copy_v3_v3(data[2], vert_normals[corner_verts[face[2]]]);
copy_v3_v3(data[3], vert_normals[corner_verts[face[3]]]);
}
else {
copy_v3_v3(data[0], vert_positions[corner_verts[face[0]]]);
copy_v3_v3(data[1], vert_positions[corner_verts[face[1]]]);
copy_v3_v3(data[2], vert_positions[corner_verts[face[2]]]);
copy_v3_v3(data[3], vert_positions[corner_verts[face[3]]]);
}
interp_bilinear_quad_v3(data, u, v, res);
}
static void interp_barycentric_corner_tri(const Span<float3> vert_positions,
const Span<float3> vert_normals,
const Span<int> corner_verts,
const int3 &corner_tri,
const float u,
const float v,
const int mode,
float res[3])
{
float data[3][3];
if (mode == 0) {
copy_v3_v3(data[0], vert_normals[corner_verts[corner_tri[0]]]);
copy_v3_v3(data[1], vert_normals[corner_verts[corner_tri[1]]]);
copy_v3_v3(data[2], vert_normals[corner_verts[corner_tri[2]]]);
}
else {
copy_v3_v3(data[0], vert_positions[corner_verts[corner_tri[0]]]);
copy_v3_v3(data[1], vert_positions[corner_verts[corner_tri[1]]]);
copy_v3_v3(data[2], vert_positions[corner_verts[corner_tri[2]]]);
}
interp_barycentric_tri_v3(data, u, v, res);
}
/* **************** Displacement Baker **************** */
static void *init_heights_data(MultiresBakeRender &bake, ImBuf *ibuf)
{
MHeightBakeData *height_data;
DerivedMesh *lodm = bake.lores_dm;
BakeImBufuserData *userdata = static_cast<BakeImBufuserData *>(ibuf->userdata);
if (userdata->displacement_buffer == nullptr) {
userdata->displacement_buffer = MEM_calloc_arrayN<float>(IMB_get_pixel_count(ibuf),
"MultiresBake heights");
}
height_data = MEM_callocN<MHeightBakeData>("MultiresBake heightData");
height_data->heights = userdata->displacement_buffer;
if (!bake.use_lores_mesh) {
SubsurfModifierData smd = {{nullptr}};
int ss_lvl = bake.tot_lvl - bake.lvl;
CLAMP(ss_lvl, 0, 6);
if (ss_lvl > 0) {
smd.levels = smd.renderLevels = ss_lvl;
smd.uv_smooth = SUBSURF_UV_SMOOTH_PRESERVE_BOUNDARIES;
smd.quality = 3;
height_data->ssdm = subsurf_make_derived_from_derived(
bake.lores_dm, &smd, bake.scene, nullptr, SubsurfFlags(0));
init_ccgdm_arrays(height_data->ssdm);
}
}
height_data->orig_index_mp_to_orig = static_cast<const int *>(
lodm->getPolyDataArray(lodm, CD_ORIGINDEX));
return (void *)height_data;
}
static void free_heights_data(void *bake_data)
{
MHeightBakeData *height_data = (MHeightBakeData *)bake_data;
if (height_data->ssdm) {
height_data->ssdm->release(height_data->ssdm);
}
MEM_freeN(height_data);
}
/* MultiresBake callback for heights baking
* general idea:
* - find coord of point with specified UV in hi-res mesh (let's call it p1)
* - find coord of point and normal with specified UV in lo-res mesh (or subdivided lo-res
* mesh to make texture smoother) let's call this point p0 and n.
* - height wound be dot(n, p1-p0) */
static void apply_heights_callback(const Span<float3> vert_positions,
const Span<float3> vert_normals,
const OffsetIndices<int> faces,
const Span<int> corner_verts,
const Span<int3> corner_tris,
const Span<int> tri_faces,
const Span<float2> uv_map,
DerivedMesh *hires_dm,
void *thread_data_v,
void *bake_data,
ImBuf *ibuf,
const int tri_index,
const int lvl,
const float st[2],
float /*tangmat*/[3][3],
const int x,
const int y)
{
const int3 &tri = corner_tris[tri_index];
const int face_i = tri_faces[tri_index];
const IndexRange face = faces[face_i];
MHeightBakeData *height_data = (MHeightBakeData *)bake_data;
MultiresBakeThread *thread_data = (MultiresBakeThread *)thread_data_v;
float uv[2];
const float *st0, *st1, *st2, *st3;
int pixel = ibuf->x * y + x;
float vec[3], p0[3], p1[3], n[3], len;
/* ideally we would work on triangles only, however, we rely on quads to get orthogonal
* coordinates for use in grid space (triangle barycentric is not orthogonal) */
if (face.size() == 4) {
st0 = uv_map[face[0]];
st1 = uv_map[face[1]];
st2 = uv_map[face[2]];
st3 = uv_map[face[3]];
resolve_quad_uv_v2(uv, st, st0, st1, st2, st3);
}
else {
st0 = uv_map[tri[0]];
st1 = uv_map[tri[1]];
st2 = uv_map[tri[2]];
resolve_tri_uv_v2(uv, st, st0, st1, st2);
}
clamp_v2(uv, 0.0f, 1.0f);
get_ccgdm_data(
faces, hires_dm, height_data->orig_index_mp_to_orig, lvl, face_i, uv[0], uv[1], p1, nullptr);
if (height_data->ssdm) {
get_ccgdm_data(faces,
height_data->ssdm,
height_data->orig_index_mp_to_orig,
0,
face_i,
uv[0],
uv[1],
p0,
n);
}
else {
if (face.size() == 4) {
interp_bilinear_mpoly(vert_positions, vert_normals, corner_verts, face, uv[0], uv[1], 1, p0);
interp_bilinear_mpoly(vert_positions, vert_normals, corner_verts, face, uv[0], uv[1], 0, n);
}
else {
interp_barycentric_corner_tri(
vert_positions, vert_normals, corner_verts, tri, uv[0], uv[1], 1, p0);
interp_barycentric_corner_tri(
vert_positions, vert_normals, corner_verts, tri, uv[0], uv[1], 0, n);
}
}
sub_v3_v3v3(vec, p1, p0);
len = dot_v3v3(n, vec);
height_data->heights[pixel] = len;
thread_data->height_min = min_ff(thread_data->height_min, len);
thread_data->height_max = max_ff(thread_data->height_max, len);
if (ibuf->float_buffer.data) {
float *rrgbf = ibuf->float_buffer.data + pixel * 4;
rrgbf[0] = rrgbf[1] = rrgbf[2] = len;
rrgbf[3] = 1.0f;
}
else {
uchar *rrgb = ibuf->byte_buffer.data + pixel * 4;
rrgb[0] = rrgb[1] = rrgb[2] = unit_float_to_uchar_clamp(len);
rrgb[3] = 255;
}
}
/* **************** Normal Maps Baker **************** */
static void *init_normal_data(MultiresBakeRender &bake, ImBuf * /*ibuf*/)
{
MNormalBakeData *normal_data;
DerivedMesh *lodm = bake.lores_dm;
normal_data = MEM_callocN<MNormalBakeData>("MultiresBake normalData");
normal_data->orig_index_mp_to_orig = static_cast<const int *>(
lodm->getPolyDataArray(lodm, CD_ORIGINDEX));
return (void *)normal_data;
}
static void free_normal_data(void *bake_data)
{
MNormalBakeData *normal_data = (MNormalBakeData *)bake_data;
MEM_freeN(normal_data);
}
/**
* MultiresBake callback for normals' baking.
*
* General idea:
* - Find coord and normal of point with specified UV in hi-res mesh.
* - Multiply it by tangmat.
* - Vector in color space would be `norm(vec) / 2 + (0.5, 0.5, 0.5)`.
*/
static void apply_tangmat_callback(const Span<float3> /*vert_positions*/,
const Span<float3> /*vert_normals*/,
const OffsetIndices<int> faces,
const Span<int> /*corner_verts*/,
const Span<int3> corner_tris,
const Span<int> tri_faces,
const Span<float2> uv_map,
DerivedMesh *hires_dm,
void * /*thread_data*/,
void *bake_data,
ImBuf *ibuf,
const int tri_index,
const int lvl,
const float st[2],
float tangmat[3][3],
const int x,
const int y)
{
const int3 &tri = corner_tris[tri_index];
const int face_i = tri_faces[tri_index];
const IndexRange face = faces[face_i];
MNormalBakeData *normal_data = (MNormalBakeData *)bake_data;
float uv[2];
const float *st0, *st1, *st2, *st3;
int pixel = ibuf->x * y + x;
float n[3], vec[3], tmp[3] = {0.5, 0.5, 0.5};
/* ideally we would work on triangles only, however, we rely on quads to get orthogonal
* coordinates for use in grid space (triangle barycentric is not orthogonal) */
if (face.size() == 4) {
st0 = uv_map[face[0]];
st1 = uv_map[face[1]];
st2 = uv_map[face[2]];
st3 = uv_map[face[3]];
resolve_quad_uv_v2(uv, st, st0, st1, st2, st3);
}
else {
st0 = uv_map[tri[0]];
st1 = uv_map[tri[1]];
st2 = uv_map[tri[2]];
resolve_tri_uv_v2(uv, st, st0, st1, st2);
}
clamp_v2(uv, 0.0f, 1.0f);
get_ccgdm_data(
faces, hires_dm, normal_data->orig_index_mp_to_orig, lvl, face_i, uv[0], uv[1], nullptr, n);
mul_v3_m3v3(vec, tangmat, n);
normalize_v3_length(vec, 0.5);
add_v3_v3(vec, tmp);
if (ibuf->float_buffer.data) {
float *rrgbf = ibuf->float_buffer.data + pixel * 4;
rrgbf[0] = vec[0];
rrgbf[1] = vec[1];
rrgbf[2] = vec[2];
rrgbf[3] = 1.0f;
}
else {
uchar *rrgb = ibuf->byte_buffer.data + pixel * 4;
rgb_float_to_uchar(rrgb, vec);
rrgb[3] = 255;
}
}
/* ******$***************** Post processing ************************* */
static void bake_ibuf_filter(ImBuf *ibuf,
char *mask,
const int margin,
const char margin_type,
DerivedMesh *dm,
const float uv_offset[2])
{
/* must check before filtering */
const bool is_new_alpha = (ibuf->planes != R_IMF_PLANES_RGBA) && BKE_imbuf_alpha_test(ibuf);
if (margin) {
switch (margin_type) {
case R_BAKE_ADJACENT_FACES:
RE_generate_texturemargin_adjacentfaces_dm(ibuf, mask, margin, dm, uv_offset);
break;
default:
/* fall through */
case R_BAKE_EXTEND:
IMB_filter_extend(ibuf, mask, margin);
break;
}
}
/* if the bake results in new alpha then change the image setting */
if (is_new_alpha) {
ibuf->planes = R_IMF_PLANES_RGBA;
}
else {
if (margin && ibuf->planes != R_IMF_PLANES_RGBA) {
/* clear alpha added by filtering */
IMB_rectfill_alpha(ibuf, 1.0f);
}
}
}
static void bake_ibuf_normalize_displacement(ImBuf *ibuf,
const float *displacement,
const char *mask,
float displacement_min,
float displacement_max)
{
const float *current_displacement = displacement;
const char *current_mask = mask;
float max_distance;
max_distance = max_ff(fabsf(displacement_min), fabsf(displacement_max));
const size_t ibuf_pixel_count = IMB_get_pixel_count(ibuf);
for (size_t i = 0; i < ibuf_pixel_count; i++) {
if (*current_mask == FILTER_MASK_USED) {
float normalized_displacement;
if (max_distance > 1e-5f) {
normalized_displacement = (*current_displacement + max_distance) / (max_distance * 2);
protected:
void get_bake_level_position_and_normal(const RasterizeTriangle &triangle,
const float2 &bary_uv,
const float2 &grid_uv,
float3 &bake_level_position,
float3 &bake_level_normal) const
{
if (subdivided_ccg_) {
if (!is_baking_to_subdivided_mesh_) {
bake_level_position = sample_position_on_subdiv_ccg(
*subdivided_ccg_, triangle.grid_index, grid_uv);
bake_level_normal = sample_normal_on_subdiv_ccg(
*subdivided_ccg_, triangle.grid_index, grid_uv);
}
else {
normalized_displacement = 0.5f;
}
/* Map bake level face and UV coordinate within it to the grid index and its uv within
* the subdivided_ccg_.
*
* The fact that the bake mesh is subdivided at least once makes math a bit easier since
* all its faces are quads, which maps 1:1 to the PTex faces in the subdivided_ccg_. So the
* only trick comes in mapping UV coordinate within a quad face to a corner and UV
* coordinate within this corner. */
if (ibuf->float_buffer.data) {
/* currently baking happens to RGBA only */
float *fp = ibuf->float_buffer.data + i * 4;
fp[0] = fp[1] = fp[2] = normalized_displacement;
fp[3] = 1.0f;
}
const float2 quad_uv = triangle.get_bake_level_quad_uv(bary_uv);
float2 corner_uv;
const int corner = subdiv::rotate_quad_to_corner(
quad_uv.x, quad_uv.y, &corner_uv.x, &corner_uv.y);
if (ibuf->byte_buffer.data) {
uchar *cp = ibuf->byte_buffer.data + 4 * i;
cp[0] = cp[1] = cp[2] = unit_float_to_uchar_clamp(normalized_displacement);
cp[3] = 255;
const float2 quad_grid_uv = subdiv::ptex_face_uv_to_grid_uv(corner_uv);
const int quad_grid_index = triangle.bake_level_quad_index * 4 + corner;
bake_level_position = sample_position_on_subdiv_ccg(
*subdivided_ccg_, quad_grid_index, quad_grid_uv);
bake_level_normal = sample_normal_on_subdiv_ccg(
*subdivided_ccg_, quad_grid_index, quad_grid_uv);
}
}
current_displacement++;
current_mask++;
else {
bake_level_position = triangle.get_position(bary_uv);
bake_level_normal = triangle.get_normal(bary_uv);
}
}
}
/* **************** Common functions public API relates on **************** */
float3 get_high_level_position(const int grid_index, const float2 &grid_uv) const
{
return sample_position_on_subdiv_ccg(high_subdiv_ccg_, grid_index, grid_uv);
}
static void count_images(MultiresBakeRender &bake)
private:
subdiv::Subdiv *create_subdiv_for_subdivided_ccg(const Mesh &bake_level_mesh,
const MultiresModifierData &multires_modifier)
{
subdiv::Settings subdiv_settings;
BKE_multires_subdiv_settings_init(&subdiv_settings, &multires_modifier);
subdiv::Subdiv *subdiv = subdiv::update_from_mesh(nullptr, &subdiv_settings, &bake_level_mesh);
/* Initialization evaluation of the limit surface and the displacement. */
if (!subdiv::eval_begin_from_mesh(subdiv, &bake_level_mesh, subdiv::SUBDIV_EVALUATOR_TYPE_CPU))
{
subdiv::free(subdiv);
return nullptr;
}
return subdiv;
}
void create_subdivided_ccg(const Mesh &bake_level_mesh,
const MultiresModifierData &multires_modifier)
{
subdiv::Subdiv *subdiv = create_subdiv_for_subdivided_ccg(bake_level_mesh, multires_modifier);
if (!subdiv) {
return;
}
const int top_level = multires_modifier.totlvl;
const int bake_level = multires_modifier.lvl;
const int subdivide_level = top_level - bake_level;
if (subdivide_level == 0) {
subdiv::free(subdiv);
return;
}
SubdivToCCGSettings settings;
settings.resolution = (1 << subdivide_level) + 1;
settings.need_normal = true;
settings.need_mask = false;
subdivided_ccg_ = BKE_subdiv_to_ccg(*subdiv, settings, bake_level_mesh);
}
};
class MultiresDisplacementBaker : public MultiresBaseDisplacementBaker {
public:
MultiresDisplacementBaker(const MultiresBakeRender &bake,
const Mesh &bake_level_mesh,
const SubdivCCG &subdiv_ccg,
const ImBuf &ibuf,
ExtraBuffers &extra_buffers)
: MultiresBaseDisplacementBaker(bake, bake_level_mesh, subdiv_ccg)
{
extra_buffers.displacement_buffer.reinitialize(IMB_get_pixel_count(&ibuf));
extra_buffers.displacement_buffer.fill(0);
}
float3 bake_pixel(const RasterizeTriangle &triangle,
const float2 &bary_uv,
const float2 &grid_uv,
RasterizeResult &result) const override
{
float3 bake_level_position, bake_level_normal;
get_bake_level_position_and_normal(
triangle, bary_uv, grid_uv, bake_level_position, bake_level_normal);
const float3 high_level_position = get_high_level_position(triangle.grid_index, grid_uv);
const float length = math::dot(bake_level_normal, (high_level_position - bake_level_position));
result.height_min = math::min(result.height_min, length);
result.height_max = math::max(result.height_max, length);
return {length, length, length};
}
void write_pixel(const RasterizeTile &tile,
const int2 &coord,
const float3 &value) const override
{
const ImBuf &ibuf = *tile.ibuf;
const int64_t pixel = int64_t(ibuf.x) * coord.y + coord.x;
tile.extra_buffers->displacement_buffer[pixel] = value.x;
write_pixel_to_image_buffer(*tile.ibuf, coord, value);
}
};
class MultiresVectorDisplacementBaker : public MultiresBaseDisplacementBaker {
public:
MultiresVectorDisplacementBaker(const MultiresBakeRender &bake,
const Mesh &bake_level_mesh,
const SubdivCCG &subdiv_ccg)
: MultiresBaseDisplacementBaker(bake, bake_level_mesh, subdiv_ccg)
{
}
float3 bake_pixel(const RasterizeTriangle &triangle,
const float2 &bary_uv,
const float2 &grid_uv,
RasterizeResult & /*result*/) const override
{
float3 bake_level_position, bake_level_normal;
get_bake_level_position_and_normal(
triangle, bary_uv, grid_uv, bake_level_position, bake_level_normal);
const float3 high_level_position = get_high_level_position(triangle.grid_index, grid_uv);
return high_level_position - bake_level_position;
}
void write_pixel(const RasterizeTile &tile,
const int2 &coord,
const float3 &value) const override
{
write_pixel_to_image_buffer(*tile.ibuf, coord, value);
}
};
/** \} */
/* -------------------------------------------------------------------- */
/** \name Normal Maps Baker
* \{ */
class MultiresNormalsBaker : public MultiresBaker {
const SubdivCCG &subdiv_ccg_;
public:
explicit MultiresNormalsBaker(const SubdivCCG &subdiv_ccg) : subdiv_ccg_(subdiv_ccg) {}
float3 bake_pixel(const RasterizeTriangle &triangle,
const float2 &bary_uv,
const float2 &grid_uv,
RasterizeResult & /*result*/) const override
{
const float3x3 to_tangent = get_to_tangent_matrix(triangle, bary_uv);
const float3 normal = sample_normal_on_subdiv_ccg(subdiv_ccg_, triangle.grid_index, grid_uv);
return math::normalize(to_tangent * normal) * 0.5f + float3(0.5f, 0.5f, 0.5f);
}
void write_pixel(const RasterizeTile &tile,
const int2 &coord,
const float3 &value) const override
{
write_pixel_to_image_buffer(*tile.ibuf, coord, value);
}
private:
float3x3 get_to_tangent_matrix(const RasterizeTriangle &triangle, const float2 &bary_uv) const
{
if (!triangle.has_uv_tangents) {
return float3x3::identity();
}
const float u = bary_uv.x;
const float v = bary_uv.y;
const float w = 1 - u - v;
const float3 &no0 = triangle.normals[0];
const float3 &no1 = triangle.normals[1];
const float3 &no2 = triangle.normals[2];
const float4 &tang0 = triangle.uv_tangents[0];
const float4 &tang1 = triangle.uv_tangents[1];
const float4 &tang2 = triangle.uv_tangents[2];
/* The sign is the same at all face vertices for any non-degenerate face.
* Just in case we clamp the interpolated value though. */
const float sign = (tang0.w * u + tang1.w * v + tang2.w * w) < 0 ? (-1.0f) : 1.0f;
/* This sequence of math is designed specifically as is with great care to be compatible with
* our shader. Please don't change without good reason. */
float3x3 from_tang;
from_tang[0] = tang0.xyz() * u + tang1.xyz() * v + tang2.xyz() * w;
from_tang[2] = no0.xyz() * u + no1.xyz() * v + no2.xyz() * w;
from_tang[1] = sign * math::cross(from_tang[2], from_tang[0]); /* `B = sign * cross(N, T)` */
return math::invert(from_tang);
}
};
/** \} */
/* -------------------------------------------------------------------- */
/** \name Image initialization
* \{ */
static void initialize_images(MultiresBakeRender &bake)
{
bake.images.clear();
@@ -1124,125 +952,661 @@ static void count_images(MultiresBakeRender &bake)
}
}
static void bake_images(MultiresBakeRender &bake, MultiresBakeResult &result)
/** \} */
/* -------------------------------------------------------------------- */
/** \name Bake to base (non-subdivided) mesh
* \{ */
static std::unique_ptr<MultiresBaker> create_baker(const MultiresBakeRender &bake,
const Mesh &bake_level_mesh,
const SubdivCCG &subdiv_ccg,
const ImBuf &ibuf,
ExtraBuffers &extra_buffers)
{
/* construct bake result */
result.height_min = FLT_MAX;
result.height_max = -FLT_MAX;
switch (bake.mode) {
case RE_BAKE_NORMALS:
return std::make_unique<MultiresNormalsBaker>(subdiv_ccg);
case RE_BAKE_DISPLACEMENT:
return std::make_unique<MultiresDisplacementBaker>(
bake, bake_level_mesh, subdiv_ccg, ibuf, extra_buffers);
case RE_BAKE_VECTOR_DISPLACEMENT:
return std::make_unique<MultiresVectorDisplacementBaker>(bake, bake_level_mesh, subdiv_ccg);
}
BLI_assert_unreachable();
return nullptr;
}
for (Image *image : bake.images) {
LISTBASE_FOREACH (ImageTile *, tile, &image->tiles) {
ImageUser iuser;
BKE_imageuser_default(&iuser);
iuser.tile = tile->tile_number;
static void rasterize_base_face(const MultiresBaker &baker,
const RasterizeTile &tile,
const MeshArrays &mesh_arrays,
const Span<float4> uv_tangents,
const int face_index,
RasterizeResult &result)
{
const IndexRange &face = mesh_arrays.faces[face_index];
const Span<int> face_verts = mesh_arrays.corner_verts.slice(face);
ImBuf *ibuf = BKE_image_acquire_ibuf(image, &iuser, nullptr);
RasterizeQuad quad;
if (ibuf->x > 0 && ibuf->y > 0) {
BakeImBufuserData *userdata = MEM_callocN<BakeImBufuserData>("MultiresBake userdata");
userdata->mask_buffer = MEM_calloc_arrayN<char>(size_t(ibuf->y) * size_t(ibuf->x),
"MultiresBake imbuf mask");
ibuf->userdata = userdata;
/* - Grid coordinate (0, 0): face center.
* - Grid axis U points from the face center to the middle of the edge connecting corner to
* next_corner.
* - Grid axis V points from the face center to the middle of the edge connecting prev_corner to
* corner. */
quad.grid_uvs[0] = float2(0.0f, 0.0f);
quad.grid_uvs[1] = float2(1.0f, 0.0f);
quad.grid_uvs[2] = float2(1.0f, 1.0f);
quad.grid_uvs[3] = float2(0.0f, 1.0f);
switch (bake.mode) {
case RE_BAKE_NORMALS:
do_multires_bake(bake,
image,
tile,
ibuf,
true,
apply_tangmat_callback,
init_normal_data,
free_normal_data,
result);
break;
case RE_BAKE_DISPLACEMENT:
do_multires_bake(bake,
image,
tile,
ibuf,
false,
apply_heights_callback,
init_heights_data,
free_heights_data,
result);
break;
}
}
quad.tex_uvs[0] = face_center_tex_uv_calc(mesh_arrays, face_index);
quad.positions[0] = bke::mesh::face_center_calc(mesh_arrays.vert_positions, face_verts);
BKE_image_release_ibuf(image, ibuf, nullptr);
/* TODO(sergey): Support corner normals. */
quad.is_flat = mesh_arrays.sharp_faces[face_index];
if (quad.is_flat) {
quad.normals[0] = mesh_arrays.face_normals[face_index];
}
else {
quad.normals[0] = face_center_smooth_normal_calc(mesh_arrays, face_index);
}
quad.has_uv_tangents = !uv_tangents.is_empty();
if (quad.has_uv_tangents) {
quad.uv_tangents[0] = face_center_uv_tangent_calc(mesh_arrays, uv_tangents, face_index);
}
/* The exact values do not really matter here, these fields should not be used when baking to
* the base mesh. Assign some values that will make it easier to spot that their values are not
* valid if anyone accesses them by mistake. */
quad.bake_level_quad_index = -1;
quad.bake_level_quad_uvs[0] = float2(0.0f, 0.0f);
quad.bake_level_quad_uvs[1] = float2(0.0f, 0.0f);
quad.bake_level_quad_uvs[2] = float2(0.0f, 0.0f);
quad.bake_level_quad_uvs[3] = float2(0.0f, 0.0f);
for (const int corner : face) {
const int prev_corner = bke::mesh::face_corner_prev(face, corner);
const int next_corner = bke::mesh::face_corner_next(face, corner);
const float3 &position = mesh_arrays.vert_positions[mesh_arrays.corner_verts[corner]];
const float3 &next_position =
mesh_arrays.vert_positions[mesh_arrays.corner_verts[next_corner]];
const float3 &prev_position =
mesh_arrays.vert_positions[mesh_arrays.corner_verts[prev_corner]];
quad.grid_index = corner;
quad.tex_uvs[1] = (mesh_arrays.uv_map[corner] + mesh_arrays.uv_map[next_corner]) * 0.5f;
quad.tex_uvs[2] = mesh_arrays.uv_map[corner] - tile.uv_offset;
quad.tex_uvs[3] = (mesh_arrays.uv_map[prev_corner] + mesh_arrays.uv_map[corner]) * 0.5f;
quad.positions[1] = (position + next_position) * 0.5f;
quad.positions[2] = position;
quad.positions[3] = (prev_position + position) * 0.5f;
if (quad.is_flat) {
quad.normals[1] = quad.normals[0];
quad.normals[2] = quad.normals[0];
quad.normals[3] = quad.normals[0];
}
else {
const float3 &normal = mesh_arrays.vert_normals[mesh_arrays.corner_verts[corner]];
const float3 &next_normal = mesh_arrays.vert_normals[mesh_arrays.corner_verts[next_corner]];
const float3 &prev_normal = mesh_arrays.vert_normals[mesh_arrays.corner_verts[prev_corner]];
/* NOTE: No normalization here: do it after interpolation at the baking point.
*
* This preserves linearity of operation. If normalization is done here interpolation will go
* wrong. */
quad.normals[1] = (normal + next_normal) * 0.5f;
quad.normals[2] = normal;
quad.normals[3] = (prev_normal + normal) * 0.5f;
}
image->id.tag |= ID_TAG_DOIT;
if (quad.has_uv_tangents) {
const float4 &tangent = uv_tangents[corner];
const float4 &next_tangent = uv_tangents[next_corner];
const float4 &prev_tangent = uv_tangents[prev_corner];
quad.uv_tangents[1] = (tangent + next_tangent) * 0.5f;
quad.uv_tangents[2] = tangent;
quad.uv_tangents[3] = (prev_tangent + tangent) * 0.5f;
}
rasterize_quad(baker, tile, quad, result);
}
}
static void finish_images(MultiresBakeRender &bake, MultiresBakeResult &result)
static void bake_single_image_to_base_mesh(MultiresBakeRender &bake,
const Mesh &bake_level_mesh,
const SubdivCCG &subdiv_ccg,
Image &image,
ImageTile &image_tile,
ImBuf &ibuf,
ExtraBuffers &extra_buffers,
MultiresBakeResult &result)
{
const bool use_displacement_buffer = bake.mode == RE_BAKE_DISPLACEMENT;
std::unique_ptr<MultiresBaker> baker = create_baker(
bake, bake_level_mesh, subdiv_ccg, ibuf, extra_buffers);
if (!baker) {
return;
}
for (Image *image : bake.images) {
LISTBASE_FOREACH (ImageTile *, tile, &image->tiles) {
ImageUser iuser;
BKE_imageuser_default(&iuser);
iuser.tile = tile->tile_number;
MeshArrays mesh_arrays(bake_level_mesh);
Array<float4> uv_tangents;
if (need_tangent(bake)) {
uv_tangents = calc_uv_tangents(mesh_arrays);
}
ImBuf *ibuf = BKE_image_acquire_ibuf(image, &iuser, nullptr);
BakeImBufuserData *userdata = (BakeImBufuserData *)ibuf->userdata;
RasterizeTile tile;
tile.ibuf = &ibuf;
tile.extra_buffers = &extra_buffers;
tile.uv_offset = get_tile_uv(image, image_tile);
if (ibuf->x <= 0 || ibuf->y <= 0) {
SpinLock spin_lock;
BLI_spin_init(&spin_lock);
std::atomic<int> num_baked_faces = 0;
threading::parallel_for(mesh_arrays.faces.index_range(), 1, [&](const IndexRange range) {
for (const int64_t face_index : range) {
if (multiresbake_test_break(bake)) {
return;
}
/* Check whether the face is to be baked into the current image. */
const int mat_nr = mesh_arrays.material_indices[face_index];
const Image *face_image = mat_nr < bake.ob_image.size() ? bake.ob_image[mat_nr] : nullptr;
if (face_image != &image) {
continue;
}
if (use_displacement_buffer) {
bake_ibuf_normalize_displacement(ibuf,
userdata->displacement_buffer,
userdata->mask_buffer,
result.height_min,
result.height_max);
RasterizeResult rasterize_result;
rasterize_base_face(*baker, tile, mesh_arrays, uv_tangents, face_index, rasterize_result);
++num_baked_faces;
BLI_spin_lock(&spin_lock);
result.height_min = std::min(result.height_min, rasterize_result.height_min);
result.height_max = std::max(result.height_max, rasterize_result.height_max);
if (bake.do_update) {
*bake.do_update = true;
}
if (bake.progress) {
*bake.progress = (float(bake.num_baked_objects) +
float(num_baked_faces) / mesh_arrays.faces.size()) /
bake.num_total_objects;
}
BLI_spin_unlock(&spin_lock);
}
});
BLI_spin_end(&spin_lock);
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Bake to subdivided mesh (base mesh with some subdivision levels)
* \{ */
struct GridCoord {
int grid_index;
float2 uv;
};
struct SubdividedCornerGridCoordData {
MeshArrays coarse_mesh_arrays;
Array<GridCoord> corner_grid_coords;
};
static Array<GridCoord> get_subdivided_corner_grid_coords(MultiresBakeRender &bake,
const SubdivCCG &subdiv_ccg)
{
subdiv::ToMeshSettings mesh_settings;
mesh_settings.resolution = (1 << bake.multires_modifier->lvl) + 1;
SubdividedCornerGridCoordData data;
data.coarse_mesh_arrays = MeshArrays(*bake.base_mesh);
subdiv::ForeachContext foreach_context;
foreach_context.user_data = &data;
foreach_context.topology_info = [](const subdiv::ForeachContext *context,
const int /*num_vertices*/,
const int /*num_edges*/,
const int num_corners,
const int /*num_faces*/,
const int * /*subdiv_face_offset*/) -> bool {
SubdividedCornerGridCoordData *data = static_cast<SubdividedCornerGridCoordData *>(
context->user_data);
data->corner_grid_coords.reinitialize(num_corners);
return true;
};
foreach_context.loop = [](const subdiv::ForeachContext *context,
void * /*tls*/,
const int /*ptex_face_index*/,
const float u,
const float v,
const int /*coarse_corner_index*/,
const int coarse_face_index,
const int coarse_corner,
const int subdiv_corner_index,
const int /*subdiv_vertex_index*/,
const int /*subdiv_edge_index*/) {
SubdividedCornerGridCoordData *data = static_cast<SubdividedCornerGridCoordData *>(
context->user_data);
const float2 ptex_uv(u, v);
const IndexRange coarse_face = data->coarse_mesh_arrays.faces[coarse_face_index];
GridCoord &corner_grid_coord = data->corner_grid_coords[subdiv_corner_index];
corner_grid_coord.grid_index = coarse_face.start() + coarse_corner;
if (coarse_face.size() == 4) {
corner_grid_coord.uv = subdiv::ptex_face_uv_to_grid_uv(
subdiv::rotate_quad_to_corner(coarse_corner, ptex_uv));
}
else {
corner_grid_coord.uv = subdiv::ptex_face_uv_to_grid_uv(ptex_uv);
}
};
foreach_subdiv_geometry(subdiv_ccg.subdiv, &foreach_context, &mesh_settings, bake.base_mesh);
return data.corner_grid_coords;
}
static void rasterize_subdivided_face(const MultiresBaker &baker,
const RasterizeTile &tile,
const MeshArrays &mesh_arrays,
const Span<GridCoord> &corner_grid_coords,
const Span<float4> uv_tangents,
const int face_index,
RasterizeResult &result)
{
const IndexRange &face = mesh_arrays.faces[face_index];
/* This code operates with mesh with at leats one subdivision level applied. Such mesh only has
* quad faces as per how subdivision works. */
BLI_assert(face.size() == 4);
RasterizeQuad quad;
/* TODO(sergey): Support corner normals. */
quad.is_flat = mesh_arrays.sharp_faces[face_index];
quad.has_uv_tangents = !uv_tangents.is_empty();
quad.grid_index = corner_grid_coords[face.start()].grid_index;
for (int i = 0; i < 4; ++i) {
const int corner = face[i];
const int vertex = mesh_arrays.corner_verts[corner];
BLI_assert(corner_grid_coords[corner].grid_index == quad.grid_index);
quad.grid_uvs[i] = corner_grid_coords[corner].uv;
quad.tex_uvs[i] = mesh_arrays.uv_map[corner];
quad.positions[i] = mesh_arrays.vert_positions[vertex];
if (!quad.is_flat) {
quad.normals[i] = mesh_arrays.vert_normals[vertex];
}
if (quad.has_uv_tangents) {
quad.uv_tangents[i] = uv_tangents[corner];
}
}
if (quad.is_flat) {
quad.normals[0] = mesh_arrays.face_normals[face_index];
quad.normals[1] = quad.normals[0];
quad.normals[2] = quad.normals[0];
quad.normals[3] = quad.normals[0];
}
/* Fill in bake level face information.
* The face index is known explicitly. The UV coordinates are such that first face corner is
* (0, 0), next corner is (1, 0), and previous corner is (0, 1). */
quad.bake_level_quad_index = face_index;
quad.bake_level_quad_uvs[0] = float2(0.0f, 0.0f);
quad.bake_level_quad_uvs[1] = float2(1.0f, 0.0f);
quad.bake_level_quad_uvs[2] = float2(1.0f, 1.0f);
quad.bake_level_quad_uvs[3] = float2(0.0f, 1.0f);
rasterize_quad(baker, tile, quad, result);
}
static void bake_single_image_to_subdivided_mesh(MultiresBakeRender &bake,
const Mesh &bake_level_mesh,
const SubdivCCG &subdiv_ccg,
Image &image,
ImageTile &image_tile,
ImBuf &ibuf,
ExtraBuffers &extra_buffers,
MultiresBakeResult &result)
{
std::unique_ptr<MultiresBaker> baker = create_baker(
bake, bake_level_mesh, subdiv_ccg, ibuf, extra_buffers);
if (!baker) {
return;
}
MeshArrays mesh_arrays(bake_level_mesh);
Array<float4> uv_tangents;
if (need_tangent(bake)) {
uv_tangents = calc_uv_tangents(mesh_arrays);
}
RasterizeTile tile;
tile.ibuf = &ibuf;
tile.extra_buffers = &extra_buffers;
tile.uv_offset = get_tile_uv(image, image_tile);
const Array<GridCoord> corner_grid_coords = get_subdivided_corner_grid_coords(bake, subdiv_ccg);
SpinLock spin_lock;
BLI_spin_init(&spin_lock);
std::atomic<int> num_baked_faces = 0;
threading::parallel_for(mesh_arrays.faces.index_range(), 1, [&](const IndexRange range) {
for (const int64_t face_index : range) {
if (multiresbake_test_break(bake)) {
return;
}
float uv_offset[2];
BKE_image_get_tile_uv(image, tile->tile_number, uv_offset);
bake_ibuf_filter(ibuf,
userdata->mask_buffer,
bake.bake_margin,
bake.bake_margin_type,
bake.lores_dm,
uv_offset);
ibuf->userflags |= IB_DISPLAY_BUFFER_INVALID;
BKE_image_mark_dirty(image, ibuf);
if (ibuf->float_buffer.data) {
ibuf->userflags |= IB_RECT_INVALID;
/* Check whether the face is to be baked into the current image. */
const int mat_nr = mesh_arrays.material_indices[face_index];
const Image *face_image = mat_nr < bake.ob_image.size() ? bake.ob_image[mat_nr] : nullptr;
if (face_image != &image) {
continue;
}
if (ibuf->userdata) {
if (userdata->displacement_buffer) {
MEM_freeN(userdata->displacement_buffer);
}
RasterizeResult rasterize_result;
rasterize_subdivided_face(*baker,
tile,
mesh_arrays,
corner_grid_coords,
uv_tangents,
face_index,
rasterize_result);
MEM_freeN(userdata->mask_buffer);
MEM_freeN(userdata);
ibuf->userdata = nullptr;
++num_baked_faces;
BLI_spin_lock(&spin_lock);
result.height_min = std::min(result.height_min, rasterize_result.height_min);
result.height_max = std::max(result.height_max, rasterize_result.height_max);
if (bake.do_update) {
*bake.do_update = true;
}
if (bake.progress) {
*bake.progress = (float(bake.num_baked_objects) +
float(num_baked_faces) / mesh_arrays.faces.size()) /
bake.num_total_objects;
}
BLI_spin_unlock(&spin_lock);
}
});
BLI_spin_end(&spin_lock);
}
BKE_image_release_ibuf(image, ibuf, nullptr);
DEG_id_tag_update(&image->id, 0);
/** \} */
/* -------------------------------------------------------------------- */
/** \name Image baking entry point
* \{ */
static void bake_single_image(MultiresBakeRender &bake,
const Mesh &bake_level_mesh,
const SubdivCCG &subdiv_ccg,
Image &image,
ImageTile &image_tile,
ImBuf &ibuf,
ExtraBuffers &extra_buffers,
MultiresBakeResult &result)
{
if (bake.multires_modifier->lvl == 0) {
bake_single_image_to_base_mesh(
bake, bake_level_mesh, subdiv_ccg, image, image_tile, ibuf, extra_buffers, result);
return;
}
bake_single_image_to_subdivided_mesh(
bake, bake_level_mesh, subdiv_ccg, image, image_tile, ibuf, extra_buffers, result);
}
static void bake_images(MultiresBakeRender &bake,
const Mesh &bake_level_mesh,
const SubdivCCG &subdiv_ccg,
MultiresBakeResult &result)
{
for (Image *image : bake.images) {
LISTBASE_FOREACH (ImageTile *, image_tile, &image->tiles) {
ImageUser iuser;
BKE_imageuser_default(&iuser);
iuser.tile = image_tile->tile_number;
ImBuf *ibuf = BKE_image_acquire_ibuf(image, &iuser, nullptr);
if (ibuf && ibuf->x > 0 && ibuf->y > 0) {
result.baked_ibufs.append({});
BakedImBuf &baked_ibuf = result.baked_ibufs.last();
baked_ibuf.image = image;
baked_ibuf.ibuf = ibuf;
baked_ibuf.uv_offset = get_tile_uv(*image, *image_tile);
ExtraBuffers &extra_buffers = baked_ibuf.extra_buffers;
extra_buffers.mask_buffer.reinitialize(int64_t(ibuf->y) * ibuf->x);
extra_buffers.mask_buffer.fill(FILTER_MASK_NULL);
bake_single_image(
bake, bake_level_mesh, subdiv_ccg, *image, *image_tile, *ibuf, extra_buffers, result);
}
}
}
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Image postprocessing
* \{ */
static void bake_ibuf_normalize_displacement(ImBuf &ibuf,
const MutableSpan<float> displacement,
const Span<char> mask,
const float displacement_min,
const float displacement_max)
{
const float *current_displacement = displacement.data();
const char *current_mask = mask.data();
const float max_distance = math::max(math::abs(displacement_min), math::abs(displacement_max));
if (max_distance <= 1e-5f) {
const float col[4] = {0.5f, 0.5f, 0.5f, 1.0f};
IMB_rectfill(&ibuf, col);
return;
}
/* TODO(sergey): Look into multi-threading this loop. */
const size_t ibuf_pixel_count = IMB_get_pixel_count(&ibuf);
for (size_t i = 0; i < ibuf_pixel_count; i++) {
if (*current_mask == FILTER_MASK_USED) {
const float normalized_displacement = (*current_displacement + max_distance) /
(max_distance * 2);
if (ibuf.float_buffer.data) {
/* TODO(sergey): Properly tackle ibuf.channels. */
BLI_assert(ibuf.channels == 4);
float *fp = ibuf.float_buffer.data + int64_t(i) * 4;
fp[0] = fp[1] = fp[2] = normalized_displacement;
fp[3] = 1.0f;
}
if (ibuf.byte_buffer.data) {
uchar *cp = ibuf.byte_buffer.data + int64_t(i) * 4;
cp[0] = cp[1] = cp[2] = unit_float_to_uchar_clamp(normalized_displacement);
cp[3] = 255;
}
}
current_displacement++;
current_mask++;
}
}
static void bake_ibuf_filter(ImBuf &ibuf,
const MutableSpan<char> mask,
const Mesh &bake_level_mesh,
const int margin,
const eBakeMarginType margin_type,
const float2 uv_offset)
{
/* NOTE: Must check before filtering. */
const bool is_new_alpha = (ibuf.planes != R_IMF_PLANES_RGBA) && BKE_imbuf_alpha_test(&ibuf);
if (margin) {
switch (margin_type) {
case R_BAKE_ADJACENT_FACES: {
const char *active_uv_map = CustomData_get_active_layer_name(&bake_level_mesh.corner_data,
CD_PROP_FLOAT2);
RE_generate_texturemargin_adjacentfaces(
&ibuf, mask.data(), margin, &bake_level_mesh, active_uv_map, uv_offset);
break;
}
default:
/* Fall through. */
case R_BAKE_EXTEND:
IMB_filter_extend(&ibuf, mask.data(), margin);
break;
}
}
/* If the bake results in new alpha then change the image setting. */
if (is_new_alpha) {
ibuf.planes = R_IMF_PLANES_RGBA;
}
else {
if (margin && ibuf.planes != R_IMF_PLANES_RGBA) {
/* Clear alpha added by filtering. */
IMB_rectfill_alpha(&ibuf, 1.0f);
}
}
}
static void finish_images(MultiresBakeRender &bake,
const Mesh &bake_level_mesh,
MultiresBakeResult &result)
{
const bool use_displacement_buffer = bake.mode == RE_BAKE_DISPLACEMENT;
for (BakedImBuf &baked_ibuf : result.baked_ibufs) {
Image *image = baked_ibuf.image;
ImBuf *ibuf = baked_ibuf.ibuf;
if (use_displacement_buffer) {
bake_ibuf_normalize_displacement(*ibuf,
baked_ibuf.extra_buffers.displacement_buffer,
baked_ibuf.extra_buffers.mask_buffer,
result.height_min,
result.height_max);
}
bake_ibuf_filter(*ibuf,
baked_ibuf.extra_buffers.mask_buffer,
bake_level_mesh,
bake.bake_margin,
bake.bake_margin_type,
baked_ibuf.uv_offset);
ibuf->userflags |= IB_DISPLAY_BUFFER_INVALID;
BKE_image_mark_dirty(image, ibuf);
if (ibuf->float_buffer.data) {
ibuf->userflags |= IB_RECT_INVALID;
}
BKE_image_release_ibuf(image, ibuf, nullptr);
DEG_id_tag_update(&image->id, 0);
}
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Helpers to create mesh and CCG at requested levels
* \{ */
static subdiv::Subdiv *create_subdiv(const Mesh &mesh,
const MultiresModifierData &multires_modifier)
{
subdiv::Settings subdiv_settings;
BKE_multires_subdiv_settings_init(&subdiv_settings, &multires_modifier);
subdiv::Subdiv *subdiv = subdiv::update_from_mesh(nullptr, &subdiv_settings, &mesh);
subdiv::displacement_attach_from_multires(subdiv, &mesh, &multires_modifier);
/* Initialization evaluation of the limit surface and the displacement. */
if (!subdiv::eval_begin_from_mesh(subdiv, &mesh, subdiv::SUBDIV_EVALUATOR_TYPE_CPU)) {
subdiv::free(subdiv);
return nullptr;
}
subdiv::eval_init_displacement(subdiv);
return subdiv;
}
static std::unique_ptr<SubdivCCG> create_subdiv_ccg(const Mesh &mesh,
const MultiresModifierData &multires_modifier)
{
subdiv::Subdiv *subdiv = create_subdiv(mesh, multires_modifier);
if (!subdiv) {
return nullptr;
}
SubdivToCCGSettings settings;
settings.resolution = (1 << multires_modifier.totlvl) + 1;
settings.need_normal = true;
settings.need_mask = false;
return BKE_subdiv_to_ccg(*subdiv, settings, mesh);
}
static Mesh *create_bake_level_mesh(const Mesh &base_mesh,
const MultiresModifierData &multires_modifier)
{
subdiv::Subdiv *subdiv = create_subdiv(base_mesh, multires_modifier);
if (!subdiv) {
return nullptr;
}
subdiv::ToMeshSettings mesh_settings;
mesh_settings.resolution = (1 << multires_modifier.lvl) + 1;
subdiv::displacement_attach_from_multires(subdiv, &base_mesh, &multires_modifier);
Mesh *result = subdiv::subdiv_to_mesh(subdiv, &mesh_settings, &base_mesh);
subdiv::free(subdiv);
return result;
}
/** \} */
} // namespace
} // namespace blender::render
void RE_multires_bake_images(MultiresBakeRender *bkr)
void RE_multires_bake_images(MultiresBakeRender &bake)
{
blender::render::MultiresBakeResult result;
using namespace blender::render;
blender::render::count_images(*bkr);
blender::render::bake_images(*bkr, result);
blender::render::finish_images(*bkr, result);
std::unique_ptr<SubdivCCG> subdiv_ccg = create_subdiv_ccg(*bake.base_mesh,
*bake.multires_modifier);
Mesh *bake_level_mesh = bake.base_mesh;
if (bake.multires_modifier->lvl != 0) {
bake_level_mesh = create_bake_level_mesh(*bake.base_mesh, *bake.multires_modifier);
}
MultiresBakeResult result;
initialize_images(bake);
bake_images(bake, *bake_level_mesh, *subdiv_ccg, result);
finish_images(bake, *bake_level_mesh, result);
if (bake_level_mesh != bake.base_mesh) {
BKE_id_free(nullptr, bake_level_mesh);
}
}

View File

@@ -15,7 +15,6 @@
#include "BKE_attribute.hh"
#include "BKE_customdata.hh"
#include "BKE_mesh.hh"
#include "BKE_mesh_legacy_derived_mesh.hh"
#include "BKE_mesh_mapping.hh"
#include "IMB_imbuf.hh"
@@ -595,22 +594,3 @@ void RE_generate_texturemargin_adjacentfaces(ImBuf *ibuf,
uv_map,
uv_offset);
}
void RE_generate_texturemargin_adjacentfaces_dm(
ImBuf *ibuf, char *mask, const int margin, DerivedMesh *dm, const float uv_offset[2])
{
const blender::float2 *mloopuv = static_cast<const blender::float2 *>(
dm->getLoopDataArray(dm, CD_PROP_FLOAT2));
blender::render::texturemargin::generate_margin(
ibuf,
mask,
margin,
{reinterpret_cast<const blender::float3 *>(dm->getVertArray(dm)), dm->getNumVerts(dm)},
dm->getNumEdges(dm),
blender::Span(dm->getPolyArray(dm), dm->getNumPolys(dm) + 1),
{dm->getCornerEdgeArray(dm), dm->getNumLoops(dm)},
{dm->getCornerVertArray(dm), dm->getNumLoops(dm)},
{mloopuv, dm->getNumLoops(dm)},
uv_offset);
}

Binary file not shown.