Cycles: new Microfacet-based Hair BSDF with elliptical cross-section support #105600

Merged
Weizhen Huang merged 114 commits from weizhen/blender:microfacet_hair into main 2023-08-18 12:46:20 +02:00
59 changed files with 634 additions and 350 deletions
Showing only changes of commit a903e7bb32 - Show all commits

View File

@ -2,4 +2,4 @@ ${CommitTitle}
${CommitBody}
Pull Request #${PullRequestIndex}
Pull Request: https://projects.blender.org/blender/blender/pulls/${PullRequestIndex}

View File

@ -1,3 +1,3 @@
${PullRequestTitle}
Pull Request #${PullRequestIndex}
Pull Request: https://projects.blender.org/blender/blender/pulls/${PullRequestIndex}

View File

@ -1673,7 +1673,7 @@ class CyclesPreferences(bpy.types.AddonPreferences):
driver_version = "470"
col.label(text=iface_("Requires NVIDIA GPU with compute capability %s") % compute_capability,
icon='BLANK1', translate=False)
col.label(text="and NVIDIA driver version %s or newer" % driver_version,
col.label(text=iface_("and NVIDIA driver version %s or newer") % driver_version,
icon='BLANK1', translate=False)
elif device_type == 'HIP':
if True:
@ -1719,7 +1719,8 @@ class CyclesPreferences(bpy.types.AddonPreferences):
.replace('(TM)', unicodedata.lookup('TRADE MARK SIGN'))
.replace('(tm)', unicodedata.lookup('TRADE MARK SIGN'))
.replace('(R)', unicodedata.lookup('REGISTERED SIGN'))
.replace('(C)', unicodedata.lookup('COPYRIGHT SIGN'))
.replace('(C)', unicodedata.lookup('COPYRIGHT SIGN')),
translate=False
)
def draw_impl(self, layout, context):

View File

@ -35,7 +35,7 @@ struct BVHReferenceCompare {
/* Compare two references.
*
* Returns value is similar to return value of strcmp().
* Returns value is similar to return value of `strcmp()`.
*/
__forceinline int compare(const BVHReference &ra, const BVHReference &rb) const
{

View File

@ -230,9 +230,10 @@ ccl_device_inline float3 sample_wh(
const float3 wi_wm = make_float3(dot(wi, s), dot(wi, t), dot(wi, n));
float discard;
const float3 wh_wm = microfacet_sample_stretched<m_type>(
kg, wi_wm, roughness, roughness, rand.x, rand.y, &discard);
const float3 wh_wm =
(m_type == MicrofacetType::GGX) ?
microfacet_ggx_sample_vndf(wi_wm, roughness, roughness, rand.x, rand.y) :
microfacet_beckmann_sample_vndf(wi_wm, roughness, roughness, rand.x, rand.y);
const float3 wh = wh_wm.x * s + wh_wm.y * t + wh_wm.z * n;
return wh;
@ -372,14 +373,15 @@ ccl_device float3 bsdf_microfacet_hair_eval_r(ccl_private const ShaderClosure *s
/* Integrate using Composite Simpson's 1/3 rule. */
float integral = 0.0f;
for (size_t i = 0; i <= intervals; i++) {
const float gamma_m = gamma_m_min + i * res;
const float3 wm = sphg_dir(tilt, gamma_m, b);
if (microfacet_visible(wi, wo, make_float3(wm.x, 0.0f, wm.z), wh)) {
const float weight = (i == 0 || i == intervals) ? 0.5f : (i % 2 + 1);
integral += weight * bsdf_D<m_type>(roughness2, dot(wm, wh)) *
bsdf_G<m_type>(roughness2, dot(wi, wm), dot(wo, wm)) * arc_length(e2, gamma_m);
/* NOTE: using separable masking and shadowing as one factor cancels out in `sample()`. */
const float G = bsdf_G<m_type>(roughness2, dot(wm, wi)) *
bsdf_G<m_type>(roughness2, dot(wm, wo));
integral += weight * bsdf_D<m_type>(roughness2, dot(wm, wh)) * G * arc_length(e2, gamma_m);
}
}
@ -472,8 +474,8 @@ ccl_device float3 bsdf_microfacet_hair_eval_tt_trt(KernelGlobals kg,
const float cos_mi1 = dot(wi, wmi);
const float cos_mo1 = dot(-wt, wmi);
const float cos_mi2 = dot(-wt, wmt);
const float G1 = bsdf_G<m_type>(roughness2, cos_mi1, cos_mo1);
if (G1 == 0.0f || !microfacet_visible(wi, -wt, wmi_, wh1)) {
const float G1o = bsdf_G<m_type>(roughness2, cos_mo1);
if (!microfacet_visible(wi, -wt, wmi, wh1) || !microfacet_visible(wi, -wt, wmi_, wh1)) {
continue;
}
@ -498,10 +500,11 @@ ccl_device float3 bsdf_microfacet_hair_eval_tt_trt(KernelGlobals kg,
const float T2 = 1.0f - fresnel_dielectric_cos(cos_hi2, inv_eta);
const float D2 = bsdf_D<m_type>(roughness2, cos_mh2);
const float G2 = bsdf_G<m_type>(roughness2, cos_mi2, cos_mo2);
const float G2 = bsdf_G<m_type>(roughness2, cos_mi2) *
bsdf_G<m_type>(roughness2, cos_mo2);
const float3 result = weight * T1 * T2 * D2 * G2 * A_t / cos_mo1 * cos_mi1 * cos_hi2 *
cos_ho2 * sqr(rcp_norm_wh2) * bsdf_G1<m_type>(roughness2, cos_mo1);
const float3 result = weight * T1 * T2 * D2 * G1o * G2 * A_t / cos_mo1 * cos_mi1 *
cos_hi2 * cos_ho2 * sqr(rcp_norm_wh2);
if (isfinite_safe(result)) {
S_tt += bsdf->extra->TT * result * arc_length(e2, gamma_mt);
@ -527,9 +530,8 @@ ccl_device float3 bsdf_microfacet_hair_eval_tt_trt(KernelGlobals kg,
/* Total internal reflection. */
continue;
}
const float cos_mo2 = dot(-wtr, wmt);
const float G2 = bsdf_G<m_type>(roughness2, cos_mi2, cos_mo2);
if (G2 == 0.0f || !microfacet_visible(-wt, -wtr, wmt_, wh2)) {
if (!microfacet_visible(-wt, -wtr, wmt, wh2) || !microfacet_visible(-wt, -wtr, wmt_, wh2)) {
continue;
}
@ -538,12 +540,12 @@ ccl_device float3 bsdf_microfacet_hair_eval_tt_trt(KernelGlobals kg,
const float3 wmtr = sphg_dir(-tilt, gamma_mtr, b);
const float3 wmtr_ = sphg_dir(0.0f, gamma_mtr, b);
const float G3 = bsdf_G<m_type>(roughness2, dot(wtr, wmtr), dot(-wo, wmtr));
float3 wh3 = wtr + inv_eta * wo;
const float rcp_norm_wh3 = 1.0f / len(wh3);
wh3 *= rcp_norm_wh3;
const float cos_mh3 = dot(wmtr, wh3);
if (cos_mh3 < 0.0f || G3 == 0.0f || !microfacet_visible(wtr, -wo, wmtr_, wh3)) {
if (cos_mh3 < 0.0f || !microfacet_visible(wtr, -wo, wmtr, wh3) ||
!microfacet_visible(wtr, -wo, wmtr_, wh3)) {
continue;
}
@ -558,9 +560,14 @@ ccl_device float3 bsdf_microfacet_hair_eval_tt_trt(KernelGlobals kg,
2.0f * cosf(phi_tr - gamma_mt) :
-len(to_point(gamma_mtr, b) - to_point(gamma_mt, b))));
const float3 result = weight * T1 * R2 * T3 * D3 * G3 * A_t * A_tr / (cos_mo1 * cos_mo2) *
cos_mi1 * cos_mi2 * cos_hi3 * cos_ho3 * sqr(rcp_norm_wh3) *
bsdf_G<m_type>(roughness2, cos_mo1, cos_mo2);
const float cos_mo2 = dot(wmt, -wtr);
const float G2o = bsdf_G<m_type>(roughness2, cos_mo2);
const float G3 = bsdf_G<m_type>(roughness2, dot(wmtr, wtr)) *
bsdf_G<m_type>(roughness2, dot(wmtr, -wo));
const float3 result = weight * T1 * R2 * T3 * D3 * G1o * G2o * G3 * A_t * A_tr /
(cos_mo1 * cos_mo2) * cos_mi1 * cos_mi2 * cos_hi3 * cos_ho3 *
sqr(rcp_norm_wh3);
if (isfinite_safe(result)) {
S_trt += bsdf->extra->TRT * result * arc_length(e2, gamma_mtr);
@ -575,8 +582,7 @@ template<MicrofacetType m_type>
ccl_device int bsdf_microfacet_hair_sample(const KernelGlobals kg,
ccl_private const ShaderClosure *sc,
ccl_private ShaderData *sd,
float randu,
float randv,
float3 rand,
ccl_private Spectrum *eval,
ccl_private float3 *wo,
ccl_private float *pdf,
@ -630,10 +636,9 @@ ccl_device int bsdf_microfacet_hair_sample(const KernelGlobals kg,
const float roughness2 = sqr(roughness);
/* Generate samples. */
float sample_lobe = randu;
const float sample_h = randv;
const float2 sample_h1 = make_float2(lcg_step_float(&sd->lcg_state),
lcg_step_float(&sd->lcg_state));
float sample_lobe = rand.x;
const float sample_h = rand.y;
const float2 sample_h1 = make_float2(rand.z, lcg_step_float(&sd->lcg_state));
const float2 sample_h2 = make_float2(lcg_step_float(&sd->lcg_state),
lcg_step_float(&sd->lcg_state));
const float2 sample_h3 = make_float2(lcg_step_float(&sd->lcg_state),
@ -690,7 +695,7 @@ ccl_device int bsdf_microfacet_hair_sample(const KernelGlobals kg,
float3 TT = zero_float3();
float3 TRT = zero_float3();
if (dot(wt, wh2) < 0.0f && dot(wmt, wt) < 0.0f && microfacet_visible(-wt, -wtr, wmt_, wh2)) {
if (dot(wh2, -wt) > 0.0f && dot(wmt, -wt) > 0.0f && microfacet_visible(-wt, -wtr, wmt_, wh2)) {
const float3 mu_a = bsdf->sigma;
const float3 A_t = exp(mu_a / cos_theta(wt) *
(is_circular ?
@ -704,7 +709,7 @@ ccl_device int bsdf_microfacet_hair_sample(const KernelGlobals kg,
wtt = -refract_angle(-wt, wh2, cos_theta_t2, *eta);
if (dot(wtt, wmt) < 0.0f && cos_theta_t2 != 0.0f) {
if (dot(wmt, -wtt) > 0.0f && cos_theta_t2 != 0.0f) {
TT = bsdf->extra->TT * T1 * A_t * T2;
}
@ -721,7 +726,7 @@ ccl_device int bsdf_microfacet_hair_sample(const KernelGlobals kg,
wtrt = -refract_angle(wtr, wh3, cos_theta_t3, *eta);
if (cos_theta_t3 != 0.0f && dot(wtr, wh3) > 0.0f && dot(wmtr, wtr) > 0.0f &&
dot(wtrt, wmtr) < 0.0f &&
dot(wmtr, -wtrt) > 0.0f &&
microfacet_visible(wtr, -wtrt, make_float3(wmtr.x, 0.0f, wmtr.z), wh3)) {
const float3 T3 = make_float3(1.0f - R3);
@ -755,7 +760,7 @@ ccl_device int bsdf_microfacet_hair_sample(const KernelGlobals kg,
*eval = rgb_to_spectrum(R / r * total_energy);
if (microfacet_visible(wi, wr, wmi_, wh1)) {
visibility = bsdf_G1<m_type>(roughness2, dot(wr, wmi));
visibility = bsdf_G<m_type>(roughness2, dot(wmi, wr));
}
label |= LABEL_REFLECT;
@ -765,7 +770,8 @@ ccl_device int bsdf_microfacet_hair_sample(const KernelGlobals kg,
*eval = rgb_to_spectrum(TT / tt * total_energy);
if (microfacet_visible(wi, -wt, wmi_, wh1) && microfacet_visible(-wt, -wtt, wmt_, wh2)) {
visibility = bsdf_G<m_type>(roughness2, dot(-wt, wmi), dot(-wtt, wmt));
visibility = bsdf_G<m_type>(roughness2, dot(wmi, -wt)) *
bsdf_G<m_type>(roughness2, dot(wmt, -wtt));

I think this is only used if we actually sample TRRT? The compiler should probably optimize that by itself though I guess.

I think this is only used if we actually sample TRRT? The compiler should probably optimize that by itself though I guess.
}
label |= LABEL_TRANSMIT;
@ -775,8 +781,9 @@ ccl_device int bsdf_microfacet_hair_sample(const KernelGlobals kg,
*eval = rgb_to_spectrum(TRT / trt * total_energy);
if (microfacet_visible(wi, -wt, wmi_, wh1)) {
visibility = bsdf_G<m_type>(roughness2, dot(-wt, wmi), dot(-wtr, wmt)) *
bsdf_G1<m_type>(roughness2, dot(-wtrt, wmtr));
visibility = bsdf_G<m_type>(roughness2, dot(wmi, -wt)) *
bsdf_G<m_type>(roughness2, dot(wmt, -wtr)) *
bsdf_G<m_type>(roughness2, dot(wmtr, -wtrt));
}
label |= LABEL_TRANSMIT;
@ -845,8 +852,7 @@ ccl_device Spectrum bsdf_microfacet_hair_eval(KernelGlobals kg,
ccl_device int bsdf_microfacet_hair_sample(KernelGlobals kg,
ccl_private const ShaderClosure *sc,
ccl_private ShaderData *sd,
float randu,
float randv,
float3 rand,
ccl_private Spectrum *eval,
weizhen marked this conversation as resolved Outdated

Does this TODO refer to the PR, or it more general?
I think it's fine to keep it like this for now, just want to make sure it's not overlooked.

Does this TODO refer to the PR, or it more general? I think it's fine to keep it like this for now, just want to make sure it's not overlooked.

In the original implementation the pdf involves sampling microfacets, it is as costly as evaluating the BSDF itself, and it is also just an approximation, so not sure if it's worth it and how to do it better. As pdf is only used for MIS, it doesn't influence the correctness of the model, so left as a TODO as a future improvement.

In the original implementation the pdf involves sampling microfacets, it is as costly as evaluating the BSDF itself, and it is also just an approximation, so not sure if it's worth it and how to do it better. As pdf is only used for MIS, it doesn't influence the correctness of the model, so left as a TODO as a future improvement.
ccl_private float3 *wo,
ccl_private float *pdf,
@ -857,10 +863,10 @@ ccl_device int bsdf_microfacet_hair_sample(KernelGlobals kg,
if (bsdf->distribution_type == NODE_MICROFACET_HAIR_BECKMANN) {
return bsdf_microfacet_hair_sample<MicrofacetType::BECKMANN>(
kg, sc, sd, randu, randv, eval, wo, pdf, sampled_roughness, eta);
kg, sc, sd, rand, eval, wo, pdf, sampled_roughness, eta);
}
return bsdf_microfacet_hair_sample<MicrofacetType::GGX>(
kg, sc, sd, randu, randv, eval, wo, pdf, sampled_roughness, eta);
kg, sc, sd, rand, eval, wo, pdf, sampled_roughness, eta);
}
/* Implements Filter Glossy by capping the effective roughness. */

View File

@ -500,7 +500,7 @@ ccl_device int bsdf_microfacet_sample(ccl_private const ShaderClosure *sc,
float3 H;
float cos_NH, cos_HI;
float3 local_H, local_I, X, Y; /* Nneeded for anisotropic microfacets later. */
float3 local_H, local_I, X, Y; /* Needed for anisotropic microfacets later. */
if (m_singular) {
H = N;
cos_NH = 1.0f;

View File

@ -979,7 +979,7 @@ ccl_device_forceinline int kernel_path_mnee_sample(KernelGlobals kg,
for (int ci = 0; ci < sd_mnee->num_closure; ci++) {
ccl_private ShaderClosure *bsdf = &sd_mnee->closure[ci];
if (CLOSURE_IS_REFRACTIVE(bsdf->type)) {
/* Note that Glass closures are treates as refractive further below. */
/* Note that Glass closures are treated as refractive further below. */
found_refractive_microfacet_bsdf = true;
ccl_private MicrofacetBsdf *microfacet_bsdf = (ccl_private MicrofacetBsdf *)bsdf;

View File

@ -177,20 +177,53 @@ typedef enum {
typedef enum {
GHOST_kEventUnknown = 0,
GHOST_kEventCursorMove, /* Mouse move event. */
GHOST_kEventButtonDown, /* Mouse button event. */
GHOST_kEventButtonUp, /* Mouse button event. */
GHOST_kEventWheel, /* Mouse wheel event. */
GHOST_kEventTrackpad, /* Trackpad event. */
/** Mouse move event.
*
* \note #GHOST_GetEventData returns #GHOST_TEventCursorData.
*/
GHOST_kEventCursorMove,
/** Mouse button down event. */
GHOST_kEventButtonDown,
/** Mouse button up event. */
GHOST_kEventButtonUp,
/**
* Mouse wheel event.
*
* \note #GHOST_GetEventData returns #GHOST_TEventWheelData.
*/
GHOST_kEventWheel,
/**
* Trackpad event.
*
* \note #GHOST_GetEventData returns #GHOST_TEventTrackpadData.
*/
GHOST_kEventTrackpad,
#ifdef WITH_INPUT_NDOF
GHOST_kEventNDOFMotion, /* N degree of freedom device motion event. */
GHOST_kEventNDOFButton, /* N degree of freedom device button event. */
/**
* N degree of freedom device motion event.
*
* \note #GHOST_GetEventData returns #GHOST_TEventNDOFMotionData.
*/
GHOST_kEventNDOFMotion,
/**
* N degree of freedom device button event.
*
* \note #GHOST_GetEventData returns #GHOST_TEventNDOFButtonData.
*/
GHOST_kEventNDOFButton,
#endif
/**
* Keyboard up/down events.
*
* Includes repeat events, check #GHOST_TEventKeyData::is_repeat
* if detecting repeat events is needed.
*
* \note #GHOST_GetEventData returns #GHOST_TEventKeyData.
*/
GHOST_kEventKeyDown,
GHOST_kEventKeyUp,
// GHOST_kEventKeyAuto,
GHOST_kEventQuitRequest,

View File

@ -20,11 +20,13 @@ enum TransformType {
TRANSFORM_SRGB_TO_LINEAR,
TRANSFORM_SCALE,
TRANSFORM_EXPONENT,
TRANSFORM_NONE,
TRANSFORM_UNKNOWN,
};
#define COLORSPACE_LINEAR ((OCIO_ConstColorSpaceRcPtr *)1)
#define COLORSPACE_SRGB ((OCIO_ConstColorSpaceRcPtr *)2)
#define COLORSPACE_DATA ((OCIO_ConstColorSpaceRcPtr *)3)
typedef struct OCIO_PackedImageDescription {
float *data;
@ -165,6 +167,8 @@ OCIO_ConstColorSpaceRcPtr *FallbackImpl::configGetColorSpace(OCIO_ConstConfigRcP
return COLORSPACE_LINEAR;
else if (strcmp(name, "sRGB") == 0)
return COLORSPACE_SRGB;
else if (strcmp(name, "data") == 0)
return COLORSPACE_DATA;
return NULL;
}
@ -179,6 +183,9 @@ int FallbackImpl::configGetIndexForColorSpace(OCIO_ConstConfigRcPtr *config, con
else if (cs == COLORSPACE_SRGB) {
return 1;
}
else if (cs == COLORSPACE_DATA) {
return 2;
}
return -1;
}
@ -314,7 +321,10 @@ OCIO_ConstProcessorRcPtr *FallbackImpl::configGetProcessorWithNames(OCIO_ConstCo
OCIO_ConstColorSpaceRcPtr *cs_src = configGetColorSpace(config, srcName);
OCIO_ConstColorSpaceRcPtr *cs_dst = configGetColorSpace(config, dstName);
FallbackTransform transform;
if (cs_src == COLORSPACE_LINEAR && cs_dst == COLORSPACE_SRGB) {
if (cs_src == COLORSPACE_DATA || cs_dst == COLORSPACE_DATA) {
transform.type = TRANSFORM_NONE;
}
else if (cs_src == COLORSPACE_LINEAR && cs_dst == COLORSPACE_SRGB) {
transform.type = TRANSFORM_LINEAR_TO_SRGB;
}
else if (cs_src == COLORSPACE_SRGB && cs_dst == COLORSPACE_LINEAR) {
@ -433,6 +443,9 @@ const char *FallbackImpl::colorSpaceGetName(OCIO_ConstColorSpaceRcPtr *cs)
else if (cs == COLORSPACE_SRGB) {
return "sRGB";
}
else if (cs == COLORSPACE_DATA) {
return "data";
}
return NULL;
}

View File

@ -501,9 +501,7 @@ def dump_py_messages_from_files(msgs, reports, files, settings):
Recursively get strings, needed in case we have "Blah" + "Blah", passed as an argument in that case it won't
evaluate to a string. However, break on some kind of stopper nodes, like e.g. Subscript.
"""
# New in py 3.8: all constants are of type 'ast.Constant'.
# 'ast.Str' will have to be removed when we officially switch to this version.
if type(node) in {ast.Str, getattr(ast, "Constant", None)}:
if type(node) == ast.Constant:
eval_str = ast.literal_eval(node)
if eval_str and type(eval_str) == str:
yield (is_split, eval_str, (node,))
@ -692,6 +690,15 @@ def dump_py_messages_from_files(msgs, reports, files, settings):
else:
continue
# Skip function if it's marked as not translatable.
do_translate = True
for kw in node.keywords:
if kw.arg == "translate" and not kw.value.value:
do_translate = False
break
if not do_translate:
continue
func_args = func_translate_args.get(func_id, {})
# First try to get i18n contexts, for every possible msgid id.

View File

@ -13,8 +13,8 @@ from bpy.props import (
class SCENE_OT_freestyle_fill_range_by_selection(Operator):
"""Fill the Range Min/Max entries by the min/max distance between selected mesh objects and the source object """
"""(either a user-specified object or the active camera)"""
"""Fill the Range Min/Max entries by the min/max distance between selected mesh objects and the source object """ \
"""(either a user-specified object or the active camera)"""
bl_idname = "scene.freestyle_fill_range_by_selection"
bl_label = "Fill Range by Selection"
bl_options = {'INTERNAL'}

View File

@ -2,7 +2,10 @@
import bpy
from bpy.types import Menu
from bl_ui import node_add_menu
from bpy.app.translations import pgettext_iface as iface_
from bpy.app.translations import (
pgettext_iface as iface_,
contexts as i18n_contexts,
)
class NODE_MT_geometry_node_GEO_ATTRIBUTE(Menu):
@ -238,6 +241,7 @@ class NODE_MT_geometry_node_GEO_INPUT(Menu):
class NODE_MT_geometry_node_GEO_INPUT_CONSTANT(Menu):
bl_idname = "NODE_MT_geometry_node_GEO_INPUT_CONSTANT"
bl_label = "Constant"
bl_translation_context = i18n_contexts.id_nodetree
def draw(self, _context):
layout = self.layout

View File

@ -79,6 +79,7 @@ class TEXT_HT_footer(Header):
text=iface_("Text: External")
if text.library
else iface_("Text: Internal"),
translate=False
)

View File

@ -271,9 +271,20 @@ typedef struct bNodeType {
/** Check and update if internal ID data has changed. */
void (*group_update_func)(struct bNodeTree *ntree, struct bNode *node);
/** Initialize a new node instance of this type after creation. */
/**
* Initialize a new node instance of this type after creation.
*
* \note Assignments to `node->id` must not increment the user of the ID.
* This is handled by the caller of this callback.
*/
void (*initfunc)(struct bNodeTree *ntree, struct bNode *node);
/** Free the node instance. */
/**
* Free the node instance.
*
* \note Access to `node->id` must be avoided in this function as this is called
* while freeing #Main, the state of this ID is undefined.
* Higher level logic to remove the node handles the user-count.
*/
void (*freefunc)(struct bNode *node);
/** Make a copy of the node instance. */
void (*copyfunc)(struct bNodeTree *dest_ntree,

View File

@ -342,7 +342,7 @@ MaskLayer *BKE_mask_layer_new(Mask *mask, const char *name)
BLI_strncpy(masklay->name, name, sizeof(masklay->name));
}
else {
strcpy(masklay->name, "MaskLayer");
strcpy(masklay->name, DATA_("MaskLayer"));
}
BLI_addtail(&mask->masklayers, masklay);

View File

@ -2053,19 +2053,22 @@ bool nodeFindNodeTry(bNodeTree *ntree, bNodeSocket *sock, bNode **r_node, int *r
bNode *nodeFindRootParent(bNode *node)
{
if (node->parent) {
return nodeFindRootParent(node->parent);
bNode *parent_iter = node;
while (parent_iter->parent != nullptr) {
parent_iter = parent_iter->parent;
}
return node->type == NODE_FRAME ? node : nullptr;
if (parent_iter->type != NODE_FRAME) {
return nullptr;
}
return parent_iter;
}
bool nodeIsParentAndChild(const bNode *parent, const bNode *child)
{
if (parent == child) {
return true;
}
if (child->parent) {
return nodeIsParentAndChild(parent, child->parent);
for (const bNode *child_iter = child; child_iter; child_iter = child_iter->parent) {
if (child_iter == parent) {
return true;
}
}
return false;
}
@ -2551,26 +2554,26 @@ void nodeInternalRelink(bNodeTree *ntree, bNode *node)
void nodeToView(const bNode *node, const float x, const float y, float *rx, float *ry)
{
if (node->parent) {
nodeToView(node->parent, x + node->locx, y + node->locy, rx, ry);
}
else {
*rx = x + node->locx;
*ry = y + node->locy;
float mapping_x = 0.0f;
float mapping_y = 0.0f;
for (const bNode *node_iter = node; node_iter; node_iter = node_iter->parent) {
mapping_x += node_iter->locx;
mapping_y += node_iter->locy;
}
*rx = mapping_x + x;
*ry = mapping_y + y;
}
void nodeFromView(const bNode *node, const float x, const float y, float *rx, float *ry)
{
if (node->parent) {
nodeFromView(node->parent, x, y, rx, ry);
*rx -= node->locx;
*ry -= node->locy;
}
else {
*rx = x - node->locx;
*ry = y - node->locy;
float mapping_x = 0.0f;
float mapping_y = 0.0f;
for (const bNode *node_iter = node; node_iter; node_iter = node_iter->parent) {
mapping_x += node_iter->locx;
mapping_y += node_iter->locy;
}
*rx = -mapping_x + x;
*ry = -mapping_y + y;
}
void nodeAttachNode(bNodeTree *ntree, bNode *node, bNode *parent)

View File

@ -1517,10 +1517,9 @@ void BKE_tracking_marker_get_subframe_position(MovieTrackingTrack *track,
MovieTrackingMarker *marker_next = marker + 1;
if (marker_next->framenr == marker->framenr + 1) {
/* currently only do subframing inside tracked ranges, do not extrapolate tracked segments
/* Currently only do sub-framing inside tracked ranges, do not extrapolate tracked segments
* could be changed when / if mask parent would be interpolating position in-between
* tracked segments
*/
* tracked segments. */
float fac = (framenr - int(framenr)) / (marker_next->framenr - marker->framenr);

View File

@ -310,7 +310,7 @@ static bool tracking_check_marker_margin(const libmv_Marker *libmv_marker,
/** \} */
/* -------------------------------------------------------------------- */
/** \name Autotrack context initialization.
/** \name Auto-Track Context Initialization
* \{ */
static bool autotrack_is_marker_usable(const MovieTrackingMarker *marker)

View File

@ -383,7 +383,7 @@ static AVFrame *generate_video_frame(FFMpegContext *context, const uint8_t *pixe
rgb_frame = context->current_frame;
}
/* Copy the Blender pixels into the FFmpeg datastructure, taking care of endianness and flipping
/* Copy the Blender pixels into the FFMPEG data-structure, taking care of endianness and flipping
* the image vertically. */
int linesize = rgb_frame->linesize[0];
for (int y = 0; y < height; y++) {

View File

@ -130,6 +130,8 @@ const char *BLT_translate_do_new_dataname(const char *msgctxt, const char *msgid
#define BLT_I18NCONTEXT_VIRTUAL_REALITY "Virtual reality"
#define BLT_I18NCONTEXT_CONSTRAINT "Constraint"
#define BLT_I18NCONTEXT_COLOR "Color"
#define BLT_I18NCONTEXT_AMOUNT "Amount"
#define BLT_I18NCONTEXT_UNIT "Unit"
/* Helper for bpy.app.i18n object... */
typedef struct {
@ -198,6 +200,8 @@ typedef struct {
BLT_I18NCONTEXTS_ITEM(BLT_I18NCONTEXT_VIRTUAL_REALITY, "virtual_reality"), \
BLT_I18NCONTEXTS_ITEM(BLT_I18NCONTEXT_CONSTRAINT, "constraint"), \
BLT_I18NCONTEXTS_ITEM(BLT_I18NCONTEXT_COLOR, "color"), \
BLT_I18NCONTEXTS_ITEM(BLT_I18NCONTEXT_AMOUNT, "amount"), \
BLT_I18NCONTEXTS_ITEM(BLT_I18NCONTEXT_UNIT, "unit"), \
{ \
NULL, NULL, NULL \
} \

View File

@ -241,7 +241,7 @@ int EEVEE_temporal_sampling_init(EEVEE_ViewLayerData *UNUSED(sldata), EEVEE_Data
}
/**
* Reset for each "redraw". When rendering using ogl render,
* Reset for each "redraw". When rendering using OpenGL render,
* we accumulate the redraw inside the drawing loop in eevee_draw_scene().
*/
if (DRW_state_is_opengl_render()) {

View File

@ -2437,7 +2437,7 @@ static void draw_armature_pose(ArmatureDrawContext *ctx)
draw_bone_box(ctx, nullptr, pchan, arm, boneflag, constflag, select_id);
}
}
else {
else if (arm->drawtype == ARM_OCTA) {
draw_bone_update_disp_matrix_default(nullptr, pchan);
if (!is_pose_select || pchan_culling_test_octohedral(view, ob, pchan)) {
draw_bone_octahedral(ctx, nullptr, pchan, arm, boneflag, constflag, select_id);

View File

@ -106,7 +106,7 @@ void OVERLAY_edit_mesh_cache_init(OVERLAY_Data *vedata)
/* Complementary Depth Pass */
state = DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS_EQUAL | DRW_STATE_CULL_BACK;
if (show_retopology) {
/* Do not cull backfaces for retopology depth pass.
/* Do not cull back-faces for retopology depth pass.
* This prevents edit overlays from appearing behind any faces.
* Doing so reduces visual clutter. */
state &= ~DRW_STATE_CULL_BACK;
@ -162,9 +162,9 @@ void OVERLAY_edit_mesh_cache_init(OVERLAY_Data *vedata)
&pd->edit_mesh_faces_cage_grp[i];
state = state_common;
if (show_retopology) {
/* Cull backfaces for retopology face pass.
* This makes it so backfaces are not drawn.
* Doing so lets us distinguish backfaces from frontfaces. */
/* Cull back-faces for retopology face pass.
* This makes it so back-faces are not drawn.
* Doing so lets us distinguish back-faces from front-faces. */
state |= DRW_STATE_CULL_BACK;
}
DRW_PASS_CREATE(*edit_face_ps, state | pd->clipping_state);

View File

@ -577,7 +577,7 @@ static void write_render_z_output(struct RenderLayer *layer,
int pix_num = BLI_rcti_size_x(rect) * BLI_rcti_size_y(rect);
/* Convert ogl depth [0..1] to view Z [near..far] */
/* Convert GPU depth [0..1] to view Z [near..far] */
if (DRW_view_is_persp_get(nullptr)) {
for (float &z : MutableSpan(rp->rect, pix_num)) {
if (z == 1.0f) {

View File

@ -120,7 +120,7 @@ static void workbench_render_result_z(struct RenderLayer *rl,
int pix_num = BLI_rcti_size_x(rect) * BLI_rcti_size_y(rect);
/* Convert ogl depth [0..1] to view Z [near..far] */
/* Convert GPU depth [0..1] to view Z [near..far] */
if (DRW_view_is_persp_get(NULL)) {
for (int i = 0; i < pix_num; i++) {
if (rp->rect[i] == 1.0f) {

View File

@ -3877,7 +3877,7 @@ static void ui_but_update_ex(uiBut *but, const bool validate)
case UI_BTYPE_KEY_EVENT: {
const char *str;
if (but->flag & UI_SELECT) {
str = "Press a key";
str = IFACE_("Press a key");
}
else {
UI_GET_BUT_VALUE_INIT(but, value);
@ -3910,7 +3910,7 @@ static void ui_but_update_ex(uiBut *but, const bool validate)
(void)str; /* UNUSED */
}
else {
BLI_strncpy(but->drawstr, "Press a key", UI_MAX_DRAW_STR);
BLI_strncpy(but->drawstr, IFACE_("Press a key"), UI_MAX_DRAW_STR);
}
}
else {

View File

@ -2810,8 +2810,10 @@ void TEXT_OT_scroll(wmOperatorType *ot)
ot->flag = OPTYPE_BLOCKING | OPTYPE_GRAB_CURSOR_XY | OPTYPE_INTERNAL;
/* properties */
RNA_def_int(
PropertyRNA *prop;
prop = RNA_def_int(
ot->srna, "lines", 1, INT_MIN, INT_MAX, "Lines", "Number of lines to scroll", -100, 100);
RNA_def_property_translation_context(prop, BLT_I18NCONTEXT_ID_TEXT);
}
/** \} */
@ -2915,8 +2917,10 @@ void TEXT_OT_scroll_bar(wmOperatorType *ot)
ot->flag = OPTYPE_BLOCKING | OPTYPE_INTERNAL;
/* properties */
RNA_def_int(
PropertyRNA *prop;
prop = RNA_def_int(
ot->srna, "lines", 1, INT_MIN, INT_MAX, "Lines", "Number of lines to scroll", -100, 100);
RNA_def_property_translation_context(prop, BLT_I18NCONTEXT_ID_TEXT);
}
/** \} */

View File

@ -51,6 +51,7 @@ set(SRC
transform_convert_sequencer.c
transform_convert_sequencer_image.c
transform_convert_tracking.c
transform_convert_tracking_curves.c
transform_draw_cursors.c
transform_generics.c
transform_gizmo_2d.c

View File

@ -624,7 +624,9 @@ static bool transform_modal_item_poll(const wmOperator *op, int value)
return false;
}
if (value == TFM_MODAL_TRANSLATE && t->mode == TFM_TRANSLATION) {
return false;
/* The tracking transform in MovieClip has an alternate translate that modifies the offset
* of the tracks. */
return t->data_type == &TransConvertType_Tracking;
}
if (value == TFM_MODAL_ROTATE && t->mode == TFM_ROTATION) {
return false;
@ -987,16 +989,16 @@ int transformEvent(TransInfo *t, const wmEvent *event)
t->redraw |= TREDRAW_HARD;
handled = true;
}
else if (t->options & (CTX_MOVIECLIP | CTX_MASK)) {
restoreTransObjects(t);
t->flag ^= T_ALT_TRANSFORM;
t->redraw |= TREDRAW_HARD;
handled = true;
}
}
else {
if (t->mode == TFM_TRANSLATION) {
if (t->data_type == &TransConvertType_Tracking) {
restoreTransObjects(t);
t->flag ^= T_ALT_TRANSFORM;
t->redraw |= TREDRAW_HARD;
handled = true;
}
break;
}
restoreTransObjects(t);

View File

@ -932,6 +932,9 @@ static TransConvertTypeInfo *convert_type_get(const TransInfo *t, Object **r_obj
}
if (t->spacetype == SPACE_CLIP) {
if (t->options & CTX_MOVIECLIP) {
if (t->region->regiontype == RGN_TYPE_PREVIEW) {
return &TransConvertType_TrackingCurves;
}
return &TransConvertType_Tracking;
}
if (t->options & CTX_MASK) {

View File

@ -276,6 +276,10 @@ extern TransConvertTypeInfo TransConvertType_SequencerImage;
extern TransConvertTypeInfo TransConvertType_Tracking;
/* transform_convert_tracking_curves.c */
extern TransConvertTypeInfo TransConvertType_TrackingCurves;
#ifdef __cplusplus
}
#endif

View File

@ -9,26 +9,24 @@
#include "MEM_guardedalloc.h"
#include "BLI_listbase.h"
#include "BLI_math.h"
#include "BKE_context.h"
#include "BKE_main.h"
#include "BKE_movieclip.h"
#include "BKE_node.h"
#include "BKE_node_tree_update.h"
#include "BKE_tracking.h"
#include "ED_clip.h"
#include "WM_api.h"
#include "WM_types.h"
#include "transform.h"
#include "transform_convert.h"
typedef struct TransDataTracking {
int mode, flag;
int mode;
int flag;
/* tracks transformation from main window */
int area;
@ -41,18 +39,13 @@ typedef struct TransDataTracking {
int framenr;
MovieTrackingMarker *markers;
/* marker transformation from curves editor */
float *prev_pos, scale;
short coord;
MovieTrackingTrack *track;
MovieTrackingPlaneTrack *plane_track;
} TransDataTracking;
enum transDataTracking_Mode {
transDataTracking_ModeTracks = 0,
transDataTracking_ModeCurves = 1,
transDataTracking_ModePlaneTracks = 2,
transDataTracking_ModePlaneTracks = 1,
};
/* -------------------------------------------------------------------- */
@ -379,139 +372,8 @@ static void createTransTrackingTracksData(bContext *C, TransInfo *t)
}
}
static void markerToTransCurveDataInit(TransData *td,
TransData2D *td2d,
TransDataTracking *tdt,
MovieTrackingTrack *track,
MovieTrackingMarker *marker,
MovieTrackingMarker *prev_marker,
short coord,
float size)
{
float frames_delta = (marker->framenr - prev_marker->framenr);
tdt->flag = marker->flag;
marker->flag &= ~MARKER_TRACKED;
tdt->mode = transDataTracking_ModeCurves;
tdt->coord = coord;
tdt->scale = 1.0f / size * frames_delta;
tdt->prev_pos = prev_marker->pos;
tdt->track = track;
/* calculate values depending on marker's speed */
td2d->loc[0] = marker->framenr;
td2d->loc[1] = (marker->pos[coord] - prev_marker->pos[coord]) * size / frames_delta;
td2d->loc[2] = 0.0f;
td2d->loc2d = marker->pos; /* current location */
td->flag = 0;
td->loc = td2d->loc;
copy_v3_v3(td->center, td->loc);
copy_v3_v3(td->iloc, td->loc);
memset(td->axismtx, 0, sizeof(td->axismtx));
td->axismtx[2][2] = 1.0f;
td->ext = NULL;
td->val = NULL;
td->flag |= TD_SELECTED;
td->dist = 0.0;
unit_m3(td->mtx);
unit_m3(td->smtx);
}
static void createTransTrackingCurvesData(bContext *C, TransInfo *t)
{
TransData *td;
TransData2D *td2d;
SpaceClip *sc = CTX_wm_space_clip(C);
MovieClip *clip = ED_space_clip_get_clip(sc);
const MovieTrackingObject *tracking_object = BKE_tracking_object_get_active(&clip->tracking);
TransDataTracking *tdt;
int width, height;
BKE_movieclip_get_size(clip, &sc->user, &width, &height);
TransDataContainer *tc = TRANS_DATA_CONTAINER_FIRST_SINGLE(t);
/* count */
tc->data_len = 0;
if ((sc->flag & SC_SHOW_GRAPH_TRACKS_MOTION) == 0) {
return;
}
LISTBASE_FOREACH (MovieTrackingTrack *, track, &tracking_object->tracks) {
if (TRACK_VIEW_SELECTED(sc, track) && (track->flag & TRACK_LOCKED) == 0) {
for (int i = 1; i < track->markersnr; i++) {
const MovieTrackingMarker *marker = &track->markers[i];
const MovieTrackingMarker *prev_marker = &track->markers[i - 1];
if ((marker->flag & MARKER_DISABLED) || (prev_marker->flag & MARKER_DISABLED)) {
continue;
}
if (marker->flag & MARKER_GRAPH_SEL_X) {
tc->data_len += 1;
}
if (marker->flag & MARKER_GRAPH_SEL_Y) {
tc->data_len += 1;
}
}
}
}
if (tc->data_len == 0) {
return;
}
td = tc->data = MEM_callocN(tc->data_len * sizeof(TransData), "TransTracking TransData");
td2d = tc->data_2d = MEM_callocN(tc->data_len * sizeof(TransData2D),
"TransTracking TransData2D");
tc->custom.type.data = tdt = MEM_callocN(tc->data_len * sizeof(TransDataTracking),
"TransTracking TransDataTracking");
tc->custom.type.free_cb = transDataTrackingFree;
/* create actual data */
LISTBASE_FOREACH (MovieTrackingTrack *, track, &tracking_object->tracks) {
if (TRACK_VIEW_SELECTED(sc, track) && (track->flag & TRACK_LOCKED) == 0) {
for (int i = 1; i < track->markersnr; i++) {
MovieTrackingMarker *marker = &track->markers[i];
MovieTrackingMarker *prev_marker = &track->markers[i - 1];
if ((marker->flag & MARKER_DISABLED) || (prev_marker->flag & MARKER_DISABLED)) {
continue;
}
if (marker->flag & MARKER_GRAPH_SEL_X) {
markerToTransCurveDataInit(
td, td2d, tdt, track, marker, &track->markers[i - 1], 0, width);
td += 1;
td2d += 1;
tdt += 1;
}
if (marker->flag & MARKER_GRAPH_SEL_Y) {
markerToTransCurveDataInit(
td, td2d, tdt, track, marker, &track->markers[i - 1], 1, height);
td += 1;
td2d += 1;
tdt += 1;
}
}
}
}
}
static void createTransTrackingData(bContext *C, TransInfo *t)
{
ARegion *region = CTX_wm_region(C);
SpaceClip *sc = CTX_wm_space_clip(C);
MovieClip *clip = ED_space_clip_get_clip(sc);
int width, height;
@ -530,13 +392,7 @@ static void createTransTrackingData(bContext *C, TransInfo *t)
return;
}
if (region->regiontype == RGN_TYPE_PREVIEW) {
/* transformation was called from graph editor */
createTransTrackingCurvesData(C, t);
}
else {
createTransTrackingTracksData(C, t);
}
createTransTrackingTracksData(C, t);
}
/** \} */