This repository has been archived on 2023-10-09. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
blender-archive/source/blender/blenkernel/intern/multires_reshape.hh
Hans Goudey 16fbadde36 Mesh: Replace MLoop struct with generic attributes
Implements #102359.

Split the `MLoop` struct into two separate integer arrays called
`corner_verts` and `corner_edges`, referring to the vertex each corner
is attached to and the next edge around the face at each corner. These
arrays can be sliced to give access to the edges or vertices in a face.
Then they are often referred to as "poly_verts" or "poly_edges".

The main benefits are halving the necessary memory bandwidth when only
one array is used and simplifications from using regular integer indices
instead of a special-purpose struct.

The commit also starts a renaming from "loop" to "corner" in mesh code.

Like the other mesh struct of array refactors, forward compatibility is
kept by writing files with the older format. This will be done until 4.0
to ease the transition process.

Looking at a small portion of the patch should give a good impression
for the rest of the changes. I tried to make the changes as small as
possible so it's easy to tell the correctness from the diff. Though I
found Blender developers have been very inventive over the last decade
when finding different ways to loop over the corners in a face.

For performance, nearly every piece of code that deals with `Mesh` is
slightly impacted. Any algorithm that is memory bottle-necked should
see an improvement. For example, here is a comparison of interpolating
a vertex float attribute to face corners (Ryzen 3700x):

**Before** (Average: 3.7 ms, Min: 3.4 ms)
```
threading::parallel_for(loops.index_range(), 4096, [&](IndexRange range) {
  for (const int64_t i : range) {
    dst[i] = src[loops[i].v];
  }
});
```

**After** (Average: 2.9 ms, Min: 2.6 ms)
```
array_utils::gather(src, corner_verts, dst);
```

That's an improvement of 28% to the average timings, and it's also a
simplification, since an index-based routine can be used instead.
For more examples using the new arrays, see the design task.

Pull Request: blender/blender#104424
2023-03-20 15:55:13 +01:00

383 lines
13 KiB
C++

/* SPDX-License-Identifier: GPL-2.0-or-later
* Copyright 2020 Blender Foundation. All rights reserved. */
/** \file
* \ingroup bke
*/
#pragma once
#include "BLI_span.hh"
#include "BLI_sys_types.h"
#include "BKE_multires.h"
struct Depsgraph;
struct GridPaintMask;
struct MDisps;
struct MEdge;
struct Mesh;
struct MPoly;
struct MultiresModifierData;
struct Object;
struct Subdiv;
struct SubdivCCG;
struct MultiresReshapeContext {
/* NOTE: Only available when context is initialized from object. */
Depsgraph *depsgraph;
Object *object;
MultiresModifierData *mmd;
/* Base mesh from original object.
* NOTE: Does NOT include any leading modifiers in it. */
Mesh *base_mesh;
const float (*base_positions)[3];
blender::Span<MEdge> base_edges;
blender::Span<MPoly> base_polys;
blender::Span<int> base_corner_verts;
blender::Span<int> base_corner_edges;
/* Subdivision surface created for multires modifier.
*
* The coarse mesh of this subdivision surface is a base mesh with all deformation modifiers
* leading multires applied on it. */
Subdiv *subdiv;
bool need_free_subdiv;
struct {
/* Level at which displacement is being assigned to.
* It will be propagated up from this level to top.level. */
int level;
/* Grid size for reshape.level. */
int grid_size;
} reshape;
struct {
/* Top level of the displacement grids.
* The displacement will be propagated up to this level. */
int level;
/* Grid size for top.level. */
int grid_size;
} top;
struct {
/* Copy of original displacement and painting masks. */
MDisps *mdisps;
GridPaintMask *grid_paint_masks;
} orig;
/* Number of grids which are required for base_mesh. */
int num_grids;
/* Destination displacement and mask.
* Points to a custom data on a destination mesh. */
MDisps *mdisps;
GridPaintMask *grid_paint_masks;
/* Indexed by face index, gives first grid index of the face. */
int *face_start_grid_index;
/* Indexed by grid index, contains face (poly) index in the base mesh from which the grid has
* been created (in other words, index of a poly which contains loop corresponding to the grid
* index). */
int *grid_to_face_index;
/* Indexed by ptex face index, gives first grid index of the ptex face.
*
* For non-quad base faces ptex face is created for every face corner, so it's similar to a
* grid in this case. In this case start grid index will be the only one for this ptex face.
*
* For quad base faces there is a single ptex face but 4 grids. So in this case there will be
* 4 grids for the ptex, starting at a value stored in this mapping. */
int *ptex_start_grid_index;
/* Indexed by base face index, returns first ptex face index corresponding
* to that base face. */
int *face_ptex_offset;
/* Vertex crease custom data layer, null if none is present. */
const float *cd_vertex_crease;
/* Edge crease custom data layer, null if none is present. */
const float *cd_edge_crease;
};
/**
* Coordinate which identifies element of a grid.
* This is directly related on how #CD_MDISPS stores displacement.
*/
struct GridCoord {
int grid_index;
float u, v;
};
/**
* Coordinate within ptex, which is what OpenSubdiv API operates on.
*/
struct PTexCoord {
int ptex_face_index;
float u, v;
};
/**
* Element of a grid data stored in the destination mesh.
* This is where reshaped coordinates and mask values will be written to.
*/
struct ReshapeGridElement {
float *displacement;
float *mask;
};
struct ReshapeConstGridElement {
float displacement[3];
float mask;
};
/* --------------------------------------------------------------------
* Construct/destruct reshape context.
*/
/**
* Create subdivision surface descriptor which is configured for surface evaluation at a given
* multi-res modifier.
*/
Subdiv *multires_reshape_create_subdiv(Depsgraph *depsgraph,
Object *object,
const MultiresModifierData *mmd);
/**
* \note Initialized base mesh to object's mesh, the Subdivision is created from the deformed
* mesh prior to the multi-res modifier if depsgraph is not NULL. If the depsgraph is NULL
* then Subdivision is created from base mesh (without any deformation applied).
*/
bool multires_reshape_context_create_from_object(MultiresReshapeContext *reshape_context,
Depsgraph *depsgraph,
Object *object,
MultiresModifierData *mmd);
bool multires_reshape_context_create_from_base_mesh(MultiresReshapeContext *reshape_context,
Depsgraph *depsgraph,
Object *object,
MultiresModifierData *mmd);
bool multires_reshape_context_create_from_ccg(MultiresReshapeContext *reshape_context,
SubdivCCG *subdiv_ccg,
Mesh *base_mesh,
int top_level);
bool multires_reshape_context_create_from_modifier(MultiresReshapeContext *reshape_context,
Object *object,
MultiresModifierData *mmd,
int top_level);
bool multires_reshape_context_create_from_subdiv(MultiresReshapeContext *reshape_context,
Object *object,
MultiresModifierData *mmd,
Subdiv *subdiv,
int top_level);
void multires_reshape_free_original_grids(MultiresReshapeContext *reshape_context);
void multires_reshape_context_free(MultiresReshapeContext *reshape_context);
/* --------------------------------------------------------------------
* Helper accessors.
*/
/**
* For the given grid index get index of face it was created for.
*/
int multires_reshape_grid_to_face_index(const MultiresReshapeContext *reshape_context,
int grid_index);
/**
* For the given grid index get corner of a face it was created for.
*/
int multires_reshape_grid_to_corner(const MultiresReshapeContext *reshape_context, int grid_index);
bool multires_reshape_is_quad_face(const MultiresReshapeContext *reshape_context, int face_index);
/**
* For the given grid index get index of corresponding PTEX face.
*/
int multires_reshape_grid_to_ptex_index(const MultiresReshapeContext *reshape_context,
int grid_index);
/**
* Convert normalized coordinate within a grid to a normalized coordinate within a PTEX face.
*/
PTexCoord multires_reshape_grid_coord_to_ptex(const MultiresReshapeContext *reshape_context,
const GridCoord *grid_coord);
/**
* Convert a normalized coordinate within a PTEX face to a normalized coordinate within a grid.
*/
GridCoord multires_reshape_ptex_coord_to_grid(const MultiresReshapeContext *reshape_context,
const PTexCoord *ptex_coord);
/**
* Calculate tangent matrix which converts displacement to a object vector.
* Is calculated for the given surface derivatives at a given base face corner.
*/
void multires_reshape_tangent_matrix_for_corner(const MultiresReshapeContext *reshape_context,
int face_index,
int corner,
const float dPdu[3],
const float dPdv[3],
float r_tangent_matrix[3][3]);
/**
* Get grid elements which are to be reshaped at a given or PTEX coordinate.
* The data is coming from final custom mdata layers.
*/
ReshapeGridElement multires_reshape_grid_element_for_grid_coord(
const MultiresReshapeContext *reshape_context, const GridCoord *grid_coord);
ReshapeGridElement multires_reshape_grid_element_for_ptex_coord(
const MultiresReshapeContext *reshape_context, const PTexCoord *ptex_coord);
/**
* Get original grid element for the given coordinate.
*/
ReshapeConstGridElement multires_reshape_orig_grid_element_for_grid_coord(
const MultiresReshapeContext *reshape_context, const GridCoord *grid_coord);
/* --------------------------------------------------------------------
* Sample limit surface of the base mesh.
*/
/**
* Evaluate limit surface created from base mesh.
* This is the limit surface which defines tangent space for MDisps.
*/
void multires_reshape_evaluate_limit_at_grid(const MultiresReshapeContext *reshape_context,
const GridCoord *grid_coord,
float r_P[3],
float r_tangent_matrix[3][3]);
/* --------------------------------------------------------------------
* Custom data preparation.
*/
/**
* Make sure custom data is allocated for the given level.
*/
void multires_reshape_ensure_grids(Mesh *mesh, int level);
/* --------------------------------------------------------------------
* Functions specific to reshaping from a set of vertices in a object position.
*/
/**
* Set displacement grids values at a reshape level to a object coordinates of the given source.
*
* \returns truth if all coordinates were assigned.
*
* False will be returned if the number of vertex coordinates did not match required number of
* vertices at a reshape level.
*/
bool multires_reshape_assign_final_coords_from_vertcos(
const MultiresReshapeContext *reshape_context,
const float (*vert_coords)[3],
int num_vert_coords);
/* --------------------------------------------------------------------
* Functions specific to reshaping from CCG.
*/
/**
* Store final object-space coordinates in the displacement grids.
* The reason why displacement grids are used for storage is based on memory
* footprint optimization.
*
* \note Displacement grids to be at least at a reshape level.
*
* \return truth if all coordinates have been updated.
*/
bool multires_reshape_assign_final_coords_from_ccg(const MultiresReshapeContext *reshape_context,
SubdivCCG *subdiv_ccg);
/* --------------------------------------------------------------------
* Functions specific to reshaping from MDISPS.
*/
/**
* Reads and writes to the current mesh #CD_MDISPS.
*/
void multires_reshape_assign_final_coords_from_mdisps(
const MultiresReshapeContext *reshape_context);
/**
* Reads from original #CD_MIDTSPS, writes to the current mesh #CD_MDISPS.
*/
void multires_reshape_assign_final_elements_from_orig_mdisps(
const MultiresReshapeContext *reshape_context);
/* --------------------------------------------------------------------
* Displacement smooth.
*/
/**
* Operates on a displacement grids (CD_MDISPS) which contains object space coordinates stored for
* the reshape level.
*
* The result is grids which are defining mesh with a smooth surface and details starting from
* reshape level up to top level added back from original displacement grids.
*/
void multires_reshape_smooth_object_grids_with_details(
const MultiresReshapeContext *reshape_context);
/**
* Operates on a displacement grids (CD_MDISPS) which contains object space-coordinates stored for
* the reshape level.
*
* Makes it so surface on top level looks smooth. Details are not preserved
*/
void multires_reshape_smooth_object_grids(const MultiresReshapeContext *reshape_context,
enum eMultiresSubdivideModeType mode);
/* --------------------------------------------------------------------
* Displacement, space conversion.
*/
/**
* Store original grid data, so then it's possible to calculate delta from it and add
* high-frequency content on top of reshaped grids.
*/
void multires_reshape_store_original_grids(MultiresReshapeContext *reshape_context);
void multires_reshape_object_grids_to_tangent_displacement(
const MultiresReshapeContext *reshape_context);
/* --------------------------------------------------------------------
* Apply base.
*/
/**
* Update mesh coordinates to the final positions of displacement in object space.
* This is effectively desired position of base mesh vertices after canceling out displacement.
*
* \note Expects that mesh's CD_MDISPS has been set to object space positions.
*/
void multires_reshape_apply_base_update_mesh_coords(MultiresReshapeContext *reshape_context);
/**
* Perform better fitting of the base mesh so its subdivided version brings vertices to their
* desired locations.
*/
void multires_reshape_apply_base_refit_base_mesh(MultiresReshapeContext *reshape_context);
/**
* Refine subdivision surface to the new positions of the base mesh.
*/
void multires_reshape_apply_base_refine_from_base(MultiresReshapeContext *reshape_context);
/**
* Refine subdivision surface to the new positions of the deformed mesh (base mesh with all
* modifiers leading the multi-res applied).
*
* \note Will re-evaluate all leading modifiers, so it's not cheap.
*/
void multires_reshape_apply_base_refine_from_deform(MultiresReshapeContext *reshape_context);