GPv3: Initial sculpt mode #119338

Merged
Lukas Tönne merged 28 commits from LukasTonne/blender:gp3-sculpt-mode into main 2024-04-11 09:40:00 +02:00
108 changed files with 2203 additions and 1450 deletions
Showing only changes of commit 2511d1f8a0 - Show all commits

View File

@ -1,9 +1,9 @@
/* Override RTD theme */
.rst-versions {
display: none;
border-top: 0px;
overflow: visible;
}
.version-btn.vdeact {
cursor: default;
color: dimgray;
@ -12,6 +12,7 @@
.version-btn.vdeact::after {
content: "";
}
#versionwrap {
display: flex;
padding-top: 2px;
@ -19,6 +20,7 @@
justify-content: center;
flex-wrap: wrap;
}
.version-btn {
display: inline-block;
background-color: #272525;
@ -34,31 +36,39 @@
z-index: 400;
transition: border-color 0.4s;
}
.version-btn::after {
content:"\f0d8";
content: "\f0d8";
display: inline;
font: normal normal normal 16px/1 FontAwesome;
color: #8d8c8c;
vertical-align: top;
padding-left: 0.5em;
}
.version-btn-open::after {
color: gray;
}
.version-btn:hover, .version-btn:focus {
.version-btn:hover,
.version-btn:focus {
border-color: #525252;
}
.version-btn-open {
color: gray;
border: solid 1px gray;
}
.version-btn.wait {
cursor: wait;
}
.version-btn.disabled {
cursor: not-allowed;
color: dimgray;
}
.version-dialog {
display: none;
position: absolute;
@ -74,6 +84,7 @@
overflow-y: auto;
cursor: default;
}
.version-title {
padding: 5px;
color: black;
@ -82,6 +93,7 @@
background-color: #27ae60;
border-bottom: solid 1.5px #444;
}
.version-list {
margin-bottom: 4px;
text-align: center;
@ -89,7 +101,10 @@
border: solid 1px gray;
border-radius: 0px 0px 3px 3px;
}
.version-list a, .version-list span, .version-list li {
.version-list a,
.version-list span,
.version-list li {
position: relative;
display: block;
font-size: 98%;
@ -99,21 +114,28 @@
padding: 4px 0px;
color: #404040;
}
.version-list li {
background-color: #ede9e9;
color: #404040;
padding: 1px;
}
.version-list li:hover, .version-list li a:focus {
.version-list li:hover,
.version-list li a:focus {
background-color: #b9cfda;
}
.version-list li.selected, .version-list li.selected:hover {
.version-list li.selected,
.version-list li.selected:hover {
background-color: #8d8c8c;
}
.version-list li.selected span {
cursor: default;
outline-color: red;
}
.version-arrow {
position: absolute;
width: 8px;

View File

@ -1,63 +1,60 @@
(function() { // switch: v1.2
(function() { // switch: v1.4
"use strict";
var versionsFileUrl = "https://docs.blender.org/PROD/versions.json"
var all_versions;
var Popover = function() {
function Popover(id)
class Popover {
constructor(id)
{
this.isOpen = false;
this.type = (id === "version-popover");
this.$btn = $('#' + id);
this.$dialog = this.$btn.next();
this.$list = this.$dialog.children("ul");
this.btn = document.querySelector('#' + id);
this.dialog = this.btn.nextElementSibling;
this.list = this.dialog.querySelector("ul");
this.sel = null;
this.beforeInit();
}
Popover.prototype = {
beforeInit : function() {
var that = this;
this.$btn.on("click", function(e) {
const that = this;
this.btnClickHandler = function(e) {
that.init();
e.preventDefault();
e.stopPropagation();
};
this.btnKeyHandler = function(e) {
if (that.btnKeyFilter(e)) {
that.init();
e.preventDefault();
e.stopPropagation();
});
this.$btn.on("keydown", function(e) {
if (that.btnKeyFilter(e)) {
that.init();
e.preventDefault();
e.stopPropagation();
}
});
},
init : function() {
this.$btn.off("click");
this.$btn.off("keydown");
}
};
this.btn.addEventListener("click", this.btnClickHandler);
this.btn.addEventListener("keydown", this.btnKeyHandler);
}
init()
{
this.btn.removeEventListener("click", this.btnClickHandler);
this.btn.removeEventListener("keydown", this.btnKeyHandler);
new Promise((resolve, reject) => {
if (all_versions === undefined) {
this.$btn.addClass("wait");
this.loadVL(this);
this.btn.classList.add("wait");
fetch(versionsFileUrl)
.then((response) => response.json())
.then((data) => {
all_versions = data;
resolve();
})
.catch(() => {
console.error("Version Switch Error: versions.json could not be loaded.");
this.btn.classList.remove("disabled");
});
}
else {
this.afterLoad();
resolve();
}
},
loadVL : function(that) {
$.getJSON(versionsFileUrl, function(data) {
all_versions = data;
that.afterLoad();
return true;
}).fail(function() {
console.log("Version Switch Error: versions.json could not be loaded.");
that.$btn.addClass("disabled");
return false;
});
},
afterLoad : function() {
var release = DOCUMENTATION_OPTIONS.VERSION;
}).then(() => {
let release = DOCUMENTATION_OPTIONS.VERSION;
const m = release.match(/\d\.\d+/g);
if (m) {
release = m[0];
@ -65,259 +62,274 @@ var Popover = function() {
this.warnOld(release, all_versions);
var version = this.getNamed(release);
var list = this.buildList(version);
const version = this.getNamed(release);
this.buildList(version);
this.$list.children(":first-child").remove();
this.$list.append(list);
var that = this;
this.$list.on("keydown", function(e) {
this.list.firstElementChild.remove();
const that = this;
this.list.addEventListener("keydown", function(e) {
that.keyMove(e);
});
this.$btn.removeClass("wait");
this.btn.classList.remove("wait");
this.btnOpenHandler();
this.$btn.on("mousedown", function(e) {
this.btn.addEventListener("mousedown", function(e) {
that.btnOpenHandler();
e.preventDefault()
});
this.$btn.on("keydown", function(e) {
this.btn.addEventListener("keydown", function(e) {
if (that.btnKeyFilter(e)) {
that.btnOpenHandler();
}
});
},
warnOld : function(release, all_versions) {
// Note this is effectively disabled now, two issues must fixed:
// * versions.js does not contain a current entry, because that leads to
// duplicate version numbers in the menu. These need to be deduplicated.
// * It only shows the warning after opening the menu to switch version
// when versions.js is loaded. This is too late to be useful.
var current = all_versions.current
if (!current)
{
// console.log("Version Switch Error: no 'current' in version.json.");
return;
}
const m = current.match(/\d\.\d+/g);
if (m) {
current = parseFloat(m[0]);
}
if (release < current) {
var currentURL = window.location.pathname.replace(release, current);
var warning = $('<div class="admonition warning"> ' +
'<p class="first admonition-title">Note</p> ' +
'<p class="last"> ' +
'You are not using the most up to date version of the documentation. ' +
'<a href="#"></a> is the newest version.' +
'</p>' +
'</div>');
warning.find('a').attr('href', currentURL).text(current);
var body = $("div.body");
if (!body.length) {
body = $("div.document");
}
body.prepend(warning);
}
},
buildList : function(v) {
var url = new URL(window.location.href);
let pathSplit = [ "", "api", v ];
if (url.pathname.startsWith("/api/")) {
pathSplit.push(url.pathname.split('/').slice(3).join('/'));
}
else {
pathSplit.push(url.pathname.substring(1));
}
if (this.type) {
var dyn = all_versions;
var cur = v;
}
var buf = [];
var that = this;
$.each(dyn, function(ix, title) {
buf.push("<li");
if (ix === cur) {
buf.push(
' class="selected" tabindex="-1" role="presentation"><span tabindex="-1" role="menuitem" aria-current="page">' +
title + '</spanp></li>');
}
else {
pathSplit[2 + that.type] = ix;
var href = new URL(url);
href.pathname = pathSplit.join('/');
buf.push(' tabindex="-1" role="presentation"><a href ="' + href + '" tabindex="-1">' +
title + '</a></li>');
}
});
return buf.join('');
},
getNamed : function(v) {
$.each(all_versions, function(ix, title) {
if (ix === "master" || ix === "main" || ix === "latest") {
var m = title.match(/\d\.\d[\w\d\.]*/)[0];
if (parseFloat(m) == v) {
v = ix;
return false;
}
}
});
return v;
},
dialogToggle : function(speed) {
var wasClose = !this.isOpen;
var that = this;
if (!this.isOpen) {
this.$btn.addClass("version-btn-open");
this.$btn.attr("aria-pressed", true);
this.$dialog.attr("aria-hidden", false);
this.$dialog.fadeIn(speed, function() {
that.$btn.parent().on("focusout", function(e) {
that.focusoutHandler();
e.stopImmediatePropagation();
})
that.$btn.parent().on("mouseleave", function(e) {
that.mouseoutHandler();
e.stopImmediatePropagation();
});
});
this.isOpen = true;
}
else {
this.$btn.removeClass("version-btn-open");
this.$btn.attr("aria-pressed", false);
this.$dialog.attr("aria-hidden", true);
this.$btn.parent().off("focusout");
this.$btn.parent().off("mouseleave");
this.$dialog.fadeOut(speed, function() {
if (this.$sel) {
this.$sel.attr("tabindex", -1);
}
that.$btn.attr("tabindex", 0);
if (document.activeElement !== null && document.activeElement !== document &&
document.activeElement !== document.body) {
that.$btn.focus();
}
});
this.isOpen = false;
}
if (wasClose) {
if (this.$sel) {
this.$sel.attr("tabindex", -1);
}
if (document.activeElement !== null && document.activeElement !== document &&
document.activeElement !== document.body) {
var $nw = this.listEnter();
$nw.attr("tabindex", 0);
$nw.focus();
this.$sel = $nw;
}
}
},
btnOpenHandler : function() {
this.dialogToggle(300);
},
focusoutHandler : function() {
var list = this.$list;
var that = this;
setTimeout(function() {
if (list.find(":focus").length === 0) {
that.dialogToggle(200);
}
}, 200);
},
mouseoutHandler : function() {
this.dialogToggle(200);
},
btnKeyFilter : function(e) {
if (e.ctrlKey || e.shiftKey) {
return false;
}
if (e.key === " " || e.key === "Enter" || (e.key === "ArrowDown" && e.altKey) ||
e.key === "ArrowDown" || e.key === "ArrowUp") {
return true;
}
return false;
},
keyMove : function(e) {
if (e.ctrlKey || e.shiftKey) {
return true;
}
var p = true;
var $nw = $(e.target);
switch (e.key) {
case "ArrowUp":
$nw = this.listPrev($nw);
break;
case "ArrowDown":
$nw = this.listNext($nw);
break;
case "Home":
$nw = this.listFirst();
break;
case "End":
$nw = this.listLast();
break;
case "Escape":
$nw = this.listExit();
break;
case "ArrowLeft":
$nw = this.listExit();
break;
case "ArrowRight":
$nw = this.listExit();
break;
default:
p = false;
}
if (p) {
$nw.attr("tabindex", 0);
$nw.focus();
if (this.$sel) {
this.$sel.attr("tabindex", -1);
}
this.$sel = $nw;
e.preventDefault();
e.stopPropagation();
}
},
listPrev : function($nw) {
if ($nw.parent().prev().length !== 0) {
return $nw.parent().prev().children(":first-child");
}
else {
return this.listLast();
}
},
listNext : function($nw) {
if ($nw.parent().next().length !== 0) {
return $nw.parent().next().children(":first-child");
}
else {
return this.listFirst();
}
},
listFirst : function() {
return this.$list.children(":first-child").children(":first-child");
},
listLast : function() {
return this.$list.children(":last-child").children(":first-child");
},
listExit : function() {
this.mouseoutHandler();
return this.$btn;
},
listEnter : function() {
return this.$list.children(":first-child").children(":first-child");
});
}
warnOld(release, all_versions)
{
// Note this is effectively disabled now, two issues must fixed:
// * versions.js does not contain a current entry, because that leads to
// duplicate version numbers in the menu. These need to be deduplicated.
// * It only shows the warning after opening the menu to switch version
// when versions.js is loaded. This is too late to be useful.
let current = all_versions.current
if (!current) {
// console.log("Version Switch Error: no 'current' in version.json.");
return;
}
};
return Popover
}();
const m = current.match(/\d\.\d+/g);
if (m) {
current = parseFloat(m[0]);
}
if (release < current) {
const currentURL = window.location.pathname.replace(release, current);
const warning =
document.querySelector("template#version-warning").firstElementChild.cloneNode(true);
const link = warning.querySelector('a');
link.setAttribute('href', currentURL);
link.textContent = current;
$(document).ready(function() {
var lng_popover = new Popover("version-popover");
});
let body = document.querySelector("div.body");
if (!body.length) {
body = document.querySelector("div.document");
}
body.prepend(warning);
}
}
buildList(v)
{
const url = new URL(window.location.href);
let pathSplit = [ "", "api", v ];
if (url.pathname.startsWith("/api/")) {
pathSplit.push(url.pathname.split('/').slice(4).join('/'));
}
else {
pathSplit.push(url.pathname.substring(1));
}
let dyn, cur;
if (this.type) {
dyn = all_versions;
cur = v;
}
const that = this;
const template = document.querySelector("template#version-entry").content;
for (let [ix, title] of Object.entries(dyn)) {
let clone;
if (ix === cur) {
clone = template.querySelector("li.selected").cloneNode(true);
clone.querySelector("span").innerHTML = title;
}
else {
pathSplit[1 + that.type] = ix;
let href = new URL(url);
href.pathname = pathSplit.join('/');
clone = template.firstElementChild.cloneNode(true);
const link = clone.querySelector("a");
link.href = href;
link.innerHTML = title;
}
that.list.append(clone);
};
return this.list;
}
getNamed(v)
{
for (let [ix, title] of Object.entries(all_versions)) {
if (ix === "master" || ix === "main" || ix === "latest") {
const m = title.match(/\d\.\d[\w\d\.]*/)[0];
if (parseFloat(m) == v) {
v = ix;
return false;
}
}
};
return v;
}
dialogToggle(speed)
{
const wasClose = !this.isOpen;
const that = this;
if (!this.isOpen) {
this.btn.classList.add("version-btn-open");
this.btn.setAttribute("aria-pressed", true);
this.dialog.setAttribute("aria-hidden", false);
this.dialog.style.display = "block";
this.dialog.animate({opacity : [ 0, 1 ], easing : [ 'ease-in', 'ease-out' ]}, speed)
.finished.then(() => {
this.focusoutHandlerPrime = function(e) {
that.focusoutHandler();
e.stopImmediatePropagation();
};
this.mouseoutHandlerPrime = function(e) {
that.mouseoutHandler();
e.stopImmediatePropagation();
};
this.btn.parentNode.addEventListener("focusout", this.focusoutHandlerPrime);
this.btn.parentNode.addEventListener("mouseleave", this.mouseoutHandlerPrime);
});
this.isOpen = true;
}
else {
this.btn.classList.remove("version-btn-open");
this.btn.setAttribute("aria-pressed", false);
this.dialog.setAttribute("aria-hidden", true);
this.btn.parentNode.removeEventListener("focusout", this.focusoutHandlerPrime);
this.btn.parentNode.removeEventListener("mouseleave", this.mouseoutHandlerPrime);
this.dialog.animate({opacity : [ 1, 0 ], easing : [ 'ease-in', 'ease-out' ]}, speed)
.finished.then(() => {
this.dialog.style.display = "none";
if (this.sel) {
this.sel.setAttribute("tabindex", -1);
}
this.btn.setAttribute("tabindex", 0);
if (document.activeElement !== null && document.activeElement !== document &&
document.activeElement !== document.body)
{
this.btn.focus();
}
});
this.isOpen = false;
}
if (wasClose) {
if (this.sel) {
this.sel.setAttribute("tabindex", -1);
}
if (document.activeElement !== null && document.activeElement !== document &&
document.activeElement !== document.body)
{
const nw = this.listEnter();
nw.setAttribute("tabindex", 0);
nw.focus();
this.sel = nw;
}
}
}
btnOpenHandler()
{
this.dialogToggle(300);
}
focusoutHandler()
{
const list = this.list;
const that = this;
setTimeout(function() {
if (!list.querySelector(":focus")) {
that.dialogToggle(200);
}
}, 200);
}
mouseoutHandler()
{
this.dialogToggle(200);
}
btnKeyFilter(e)
{
if (e.ctrlKey || e.shiftKey) {
return false;
}
if (e.key === " " || e.key === "Enter" || (e.key === "ArrowDown" && e.altKey) ||
e.key === "ArrowDown" || e.key === "ArrowUp")
{
return true;
}
return false;
}
keyMove(e)
{
if (e.ctrlKey || e.shiftKey) {
return true;
}
let nw = e.target;
switch (e.key) {
case "ArrowUp":
nw = this.listPrev(nw);
break;
case "ArrowDown":
nw = this.listNext(nw);
break;
case "Home":
nw = this.listFirst();
break;
case "End":
nw = this.listLast();
break;
case "Escape":
nw = this.listExit();
break;
case "ArrowLeft":
nw = this.listExit();
break;
case "ArrowRight":
nw = this.listExit();
break;
default:
return false;
}
nw.setAttribute("tabindex", 0);
nw.focus();
if (this.sel) {
this.sel.setAttribute("tabindex", -1);
}
this.sel = nw;
e.preventDefault();
e.stopPropagation();
}
listPrev(nw)
{
if (nw.parentNode.previousElementSibling.length !== 0) {
return nw.parentNode.previousElementSibling.firstElementChild;
}
else {
return this.listLast();
}
}
listNext(nw)
{
if (nw.parentNode.nextElementSibling.length !== 0) {
return nw.parentNode.nextElementSibling.firstElementChild;
}
else {
return this.listFirst();
}
}
listFirst()
{
return this.list.firstElementChild.firstElementChild;
}
listLast()
{
return this.list.lastElementChild.firstElementChild;
}
listExit()
{
this.mouseoutHandler();
return this.btn;
}
listEnter()
{
return this.list.firstElementChild.firstElementChild;
}
}
document.addEventListener('DOMContentLoaded', () => { new Popover("version-popover"); });
})();

View File

@ -1,19 +1,30 @@
<div class="rst-versions" data-toggle="rst-versions" role="note" aria-label="document versions">
<ul id="versionwrap" role="presentation">
<li role="presentation">
<span id="version-popover" class="version-btn" tabindex="0" role="button"
aria-label="versions selector" aria-haspopup="true" aria-controls="version-vsnlist"
aria-disabled="true">
<span id="version-popover" class="version-btn" tabindex="0" role="button" aria-label="versions selector"
aria-haspopup="true" aria-controls="version-vsnlist" aria-disabled="true">
{{ release }}
</span>
<div class="version-dialog" aria-hidden="true">
<div class="version-arrow" aria-hidden="true"></div>
<div class="version-title">Versions</div>
<ul id="version-vsnlist" class="version-list" role="menu"
aria-labelledby="version-popover" aria-hidden="true">
<ul id="version-vsnlist" class="version-list" role="menu" aria-labelledby="version-popover" aria-hidden="true">
<li role="presentation">Loading...</li>
</ul>
</div>
</li>
<template id="version-entry">
<li tabindex="-1" role="presentation"><a tabindex="-1" role="menuitem"></a></li>
<li class="selected" tabindex="-1" role="presentation"><span tabindex="-1" aria-current="page"></span></li>
</template>
</ul>
</div>
<template id="version-warning">
<div class="admonition warning">
<p class="first admonition-title">Note</p>
<p class="last">
You are not using the most up to date version of the documentation.
<a href="#"></a> is the newest version.
</p>
</div>
</template>
</div>

View File

@ -9,11 +9,13 @@
CCL_NAMESPACE_BEGIN
/* Transform vector to spot light's local coordinate system. */
ccl_device float3 spot_light_to_local(const ccl_global KernelSpotLight *spot, const float3 ray)
ccl_device float3 spot_light_to_local(const ccl_global KernelLight *klight, const float3 ray)
{
return safe_normalize(make_float3(dot(ray, spot->scaled_axis_u),
dot(ray, spot->scaled_axis_v),
dot(ray, spot->dir * spot->inv_len_z)));
const Transform itfm = klight->itfm;
float3 transformed_ray = safe_normalize(transform_direction(&itfm, ray));
transformed_ray.z = -transformed_ray.z;
return transformed_ray;
}
/* Compute spot light attenuation of a ray given in local coordinate system. */
@ -58,7 +60,7 @@ ccl_device_inline bool spot_light_sample(const ccl_global KernelLight *klight,
ls->t = FLT_MAX;
if (d_sq > r_sq) {
/* Outside sphere. */
const float one_minus_cos_half_spot_spread = 1.0f - klight->spot.cos_half_spot_angle;
const float one_minus_cos_half_spot_spread = 1.0f - klight->spot.cos_half_larger_spread;
const float one_minus_cos_half_angle = sin_sqr_to_one_minus_cos(r_sq / d_sq);
if (in_volume_segment || one_minus_cos_half_angle < one_minus_cos_half_spot_spread) {
@ -92,7 +94,7 @@ ccl_device_inline bool spot_light_sample(const ccl_global KernelLight *klight,
}
/* Attenuation. */
const float3 local_ray = spot_light_to_local(&klight->spot, -ls->D);
const float3 local_ray = spot_light_to_local(klight, -ls->D);
if (d_sq > r_sq) {
ls->eval_fac *= spot_light_attenuation(&klight->spot, local_ray);
}
@ -128,7 +130,7 @@ ccl_device_inline bool spot_light_sample(const ccl_global KernelLight *klight,
ls->Ng = -ls->D;
/* Attenuation. */
const float3 local_ray = spot_light_to_local(&klight->spot, -ls->D);
const float3 local_ray = spot_light_to_local(klight, -ls->D);
ls->eval_fac *= spot_light_attenuation(&klight->spot, local_ray);
if (!in_volume_segment && ls->eval_fac == 0.0f) {
return false;
@ -145,7 +147,7 @@ ccl_device_inline bool spot_light_sample(const ccl_global KernelLight *klight,
return true;
}
ccl_device_forceinline float spot_light_pdf(const float cos_half_spread,
ccl_device_forceinline float spot_light_pdf(const ccl_global KernelSpotLight *spot,
const float d_sq,
const float r_sq,
const float3 N,
@ -153,7 +155,8 @@ ccl_device_forceinline float spot_light_pdf(const float cos_half_spread,
const uint32_t path_flag)
{
if (d_sq > r_sq) {
return M_1_2PI_F / min(sin_sqr_to_one_minus_cos(r_sq / d_sq), 1.0f - cos_half_spread);
return M_1_2PI_F /
min(sin_sqr_to_one_minus_cos(r_sq / d_sq), 1.0f - spot->cos_half_larger_spread);
}
const bool has_transmission = (path_flag & PATH_RAY_MIS_HAD_TRANSMISSION);
@ -181,7 +184,7 @@ ccl_device_forceinline void spot_light_mnee_sample_update(const ccl_global Kerne
/* NOTE : preserve pdf in area measure. */
const float jacobian_solid_angle_to_area = 0.5f * fabsf(d_sq - r_sq - t_sq) /
(radius * ls->t * t_sq);
ls->pdf = spot_light_pdf(klight->spot.cos_half_spot_angle, d_sq, r_sq, N, ls->D, path_flag) *
ls->pdf = spot_light_pdf(&klight->spot, d_sq, r_sq, N, ls->D, path_flag) *
jacobian_solid_angle_to_area;
ls->Ng = normalize(ls->P - klight->co);
@ -196,7 +199,7 @@ ccl_device_forceinline void spot_light_mnee_sample_update(const ccl_global Kerne
}
/* Attenuation. */
const float3 local_ray = spot_light_to_local(&klight->spot, -ls->D);
const float3 local_ray = spot_light_to_local(klight, -ls->D);
if (use_attenuation) {
ls->eval_fac *= spot_light_attenuation(&klight->spot, local_ray);
}
@ -232,7 +235,7 @@ ccl_device_inline bool spot_light_sample_from_intersection(
ls->eval_fac = klight->spot.eval_fac;
if (klight->spot.is_sphere) {
ls->pdf = spot_light_pdf(klight->spot.cos_half_spot_angle, d_sq, r_sq, N, ray_D, path_flag);
ls->pdf = spot_light_pdf(&klight->spot, d_sq, r_sq, N, ray_D, path_flag);
ls->Ng = normalize(ls->P - klight->co);
}
else {
@ -248,7 +251,7 @@ ccl_device_inline bool spot_light_sample_from_intersection(
}
/* Attenuation. */
const float3 local_ray = spot_light_to_local(&klight->spot, -ray_D);
const float3 local_ray = spot_light_to_local(klight, -ray_D);
if (!klight->spot.is_sphere || d_sq > r_sq) {
ls->eval_fac *= spot_light_attenuation(&klight->spot, local_ray);
}

View File

@ -1367,16 +1367,15 @@ typedef struct KernelCurveSegment {
static_assert_align(KernelCurveSegment, 8);
typedef struct KernelSpotLight {
packed_float3 scaled_axis_u;
float radius;
packed_float3 scaled_axis_v;
float eval_fac;
packed_float3 dir;
float radius;
float eval_fac;
float cos_half_spot_angle;
float half_cot_half_spot_angle;
float inv_len_z;
float spot_smooth;
int is_sphere;
/* For non-uniform object scaling, the actual spread might be different. */
float cos_half_larger_spread;
} KernelSpotLight;
/* PointLight is SpotLight with only radius and invarea being used. */

View File

@ -1346,23 +1346,22 @@ void LightManager::device_update_lights(Device *device, DeviceScene *dscene, Sce
klights[light_index].area.normalize_spread = normalize_spread;
}
if (light->light_type == LIGHT_SPOT) {
/* Scale axes to accommodate non-uniform scaling. */
float3 scaled_axis_u = light->get_axisu() / len_squared(light->get_axisu());
float3 scaled_axis_v = light->get_axisv() / len_squared(light->get_axisv());
float len_z;
/* Keep direction normalized. */
float3 dir = safe_normalize_len(light->get_dir(), &len_z);
const float cos_half_spot_angle = cosf(light->spot_angle * 0.5f);
const float spot_smooth = 1.0f / ((1.0f - cos_half_spot_angle) * light->spot_smooth);
const float tan_half_spot_angle = tanf(light->spot_angle * 0.5f);
float cos_half_spot_angle = cosf(light->spot_angle * 0.5f);
float spot_smooth = 1.0f / ((1.0f - cos_half_spot_angle) * light->spot_smooth);
const float len_w_sq = len_squared(light->get_dir());
const float len_u_sq = len_squared(light->get_axisu());
const float len_v_sq = len_squared(light->get_axisv());
const float tan_sq = sqr(tan_half_spot_angle);
klights[light_index].spot.scaled_axis_u = scaled_axis_u;
klights[light_index].spot.scaled_axis_v = scaled_axis_v;
klights[light_index].spot.dir = dir;
klights[light_index].spot.dir = safe_normalize(light->get_dir());
klights[light_index].spot.cos_half_spot_angle = cos_half_spot_angle;
klights[light_index].spot.half_cot_half_spot_angle = 0.5f / tanf(light->spot_angle * 0.5f);
klights[light_index].spot.inv_len_z = 1.0f / len_z;
klights[light_index].spot.half_cot_half_spot_angle = 0.5f / tan_half_spot_angle;
klights[light_index].spot.spot_smooth = spot_smooth;
/* Choose the angle which spans a larger cone. */
klights[light_index].spot.cos_half_larger_spread = inversesqrtf(
1.0f + tan_sq * fmaxf(len_u_sq, len_v_sq) / len_w_sq);
}
klights[light_index].shader_id = shader_id;

View File

@ -425,10 +425,13 @@ def enable(module_name, *, default_set=False, persistent=False, handle_error=Non
if bl_info is not None:
# Use `_init` to detect when `bl_info` was generated from the manifest, see: `_bl_info_from_extension`.
if type(bl_info) is dict and "_init" not in bl_info:
print(
"Add-on \"%s\" has a \"bl_info\" which will be ignored in favor of \"%s\"" %
(module_name, _ext_manifest_filename_toml)
)
# This print is noisy, hide behind a debug flag.
# Once `bl_info` is fully deprecated this should be changed to always print a warning.
if _bpy.app.debug_python:
print(
"Add-on \"%s\" has a \"bl_info\" which will be ignored in favor of \"%s\"" %
(module_name, _ext_manifest_filename_toml)
)
# Always remove as this is not expected to exist and will be lazily initialized.
del mod.bl_info
@ -490,7 +493,7 @@ def disable(module_name, *, default_set=False, handle_error=None):
# the add-on in the user preferences.
if mod and getattr(mod, "__addon_enabled__", False) is not False:
mod.__addon_enabled__ = False
mod.__addon_persistent = False
mod.__addon_persistent__ = False
try:
mod.unregister()
@ -503,7 +506,7 @@ def disable(module_name, *, default_set=False, handle_error=None):
print(
"addon_utils.disable: %s not %s" % (
module_name,
"disabled" if mod is None else "loaded")
"loaded" if mod is None else "enabled")
)
# could be in more than once, unlikely but better do this just in case.

View File

@ -198,6 +198,7 @@ class RENDER_PT_eevee_next_horizon_scan(RenderButtonsPanel, Panel):
col.prop(props, "horizon_quality", text="Precision")
col.prop(props, "horizon_thickness", text="Thickness")
col.prop(props, "horizon_bias", text="Bias")
col.prop(props, "horizon_resolution", text="Resolution")
class RENDER_PT_eevee_motion_blur(RenderButtonsPanel, Panel):

View File

@ -29,7 +29,7 @@ extern "C" {
/* Blender file format version. */
#define BLENDER_FILE_VERSION BLENDER_VERSION
#define BLENDER_FILE_SUBVERSION 9
#define BLENDER_FILE_SUBVERSION 10
/* Minimum Blender version that supports reading file written with the current
* version. Older Blender versions will test this and cancel loading the file, showing a warning to

View File

@ -488,11 +488,11 @@ static void libblock_remap_data(
Main *bmain, ID *id, eIDRemapType remap_type, IDRemapper &id_remapper, const int remap_flags)
{
IDRemap id_remap_data = {
/*.type=*/remap_type,
/*.bmain=*/bmain,
/*.id_remapper=*/id_remapper,
/*.id_owner=*/nullptr,
/*.flag=*/remap_flags,
/*type*/ remap_type,
/*bmain*/ bmain,
/*id_remapper*/ id_remapper,
/*id_owner*/ nullptr,
/*flag*/ remap_flags,
};
const bool include_ui = (remap_flags & ID_REMAP_FORCE_UI_POINTERS) != 0;

View File

@ -73,6 +73,7 @@ void parallel_for_weighted_impl(IndexRange range,
int64_t grain_size,
FunctionRef<void(IndexRange)> function,
FunctionRef<void(IndexRange, MutableSpan<int64_t>)> task_sizes_fn);
void memory_bandwidth_bound_task_impl(FunctionRef<void()> function);
} // namespace detail
template<typename Function>
@ -247,4 +248,26 @@ template<typename Function> inline void isolate_task(const Function &function)
#endif
}
/**
* Should surround parallel code that is highly bandwidth intensive, e.g. it just fills a buffer
* with no or just few additional operations. If the buffers are large, it's benefitial to limit
* the number of threads doing the work because that just creates more overhead on the hardware
* level and doesn't provide a notable performance benefit beyond a certain point.
*/
template<typename Function>
inline void memory_bandwidth_bound_task(const int64_t approximate_bytes_touched,
const Function &function)
{
/* Don't limit threading when all touched memory can stay in the CPU cache, because there a much
* higher memory bandwidth is available compared to accessing RAM. This value is supposed to be
* on the order of the L3 cache size. Accessing that value is not quite straight forward and even
* if it was, it's not clear if using the exact cache size would be benefitial because there is
* often more stuff going on on the CPU at the same time. */
if (approximate_bytes_touched <= 8 * 1024 * 1024) {
function();
return;
}
detail::memory_bandwidth_bound_task_impl(function);
}
} // namespace blender::threading

View File

@ -24,10 +24,12 @@ OffsetIndices<int> accumulate_counts_to_offsets(MutableSpan<int> counts_to_offse
void fill_constant_group_size(const int size, const int start_offset, MutableSpan<int> offsets)
{
threading::parallel_for(offsets.index_range(), 1024, [&](const IndexRange range) {
for (const int64_t i : range) {
offsets[i] = size * i + start_offset;
}
threading::memory_bandwidth_bound_task(offsets.size_in_bytes(), [&]() {
threading::parallel_for(offsets.index_range(), 1024, [&](const IndexRange range) {
for (const int64_t i : range) {
offsets[i] = size * i + start_offset;
}
});
});
}
@ -52,11 +54,14 @@ void gather_group_sizes(const OffsetIndices<int> offsets,
const Span<int> indices,
MutableSpan<int> sizes)
{
threading::parallel_for(indices.index_range(), 4096, [&](const IndexRange range) {
for (const int i : range) {
sizes[i] = offsets[indices[i]].size();
}
});
threading::memory_bandwidth_bound_task(
sizes.size_in_bytes() + offsets.data().size_in_bytes() + indices.size_in_bytes(), [&]() {
threading::parallel_for(indices.index_range(), 4096, [&](const IndexRange range) {
for (const int i : range) {
sizes[i] = offsets[indices[i]].size();
}
});
});
}
OffsetIndices<int> gather_selected_offsets(const OffsetIndices<int> src_offsets,

View File

@ -223,4 +223,32 @@ void parallel_for_weighted_impl(
});
}
void memory_bandwidth_bound_task_impl(const FunctionRef<void()> function)
{
#ifdef WITH_TBB
/* This is the maximum number of threads that may perform these memory bandwidth bound tasks at
* the same time. Often fewer threads are already enough to use up the full bandwidth capacity.
* Additional threads usually have a negilible benefit and can even make performance worse.
*
* It's better to use fewer threads here so that the CPU cores can do other tasks at the same
* time which may be more compute intensive. */
const int num_threads = 8;
if (num_threads >= BLI_task_scheduler_num_threads()) {
/* Avoid overhead of using a task arena when it would not have any effect anyway. */
function();
return;
}
static tbb::task_arena arena{num_threads};
/* Make sure the lazy threading hints are send now, because they shouldn't be send out of an
* isolated region. */
lazy_threading::send_hint();
lazy_threading::ReceiverIsolation isolation;
arena.execute(function);
#else
function();
#endif
}
} // namespace blender::threading::detail

View File

@ -3056,6 +3056,14 @@ void blo_do_versions_400(FileData *fd, Library * /*lib*/, Main *bmain)
}
}
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 402, 10)) {
if (!DNA_struct_member_exists(fd->filesdna, "SceneEEVEE", "int", "gtao_resolution")) {
LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) {
scene->eevee.gtao_resolution = 2;
}
}
}
/**
* Always bump subversion in BKE_blender_version.h when adding versioning
* code here, and wrap it inside a MAIN_VERSION_FILE_ATLEAST check.

View File

@ -498,6 +498,7 @@ set(GLSL_SRC
engines/eevee_next/shaders/eevee_film_cryptomatte_post_comp.glsl
engines/eevee_next/shaders/eevee_film_frag.glsl
engines/eevee_next/shaders/eevee_film_lib.glsl
engines/eevee_next/shaders/eevee_filter_lib.glsl
engines/eevee_next/shaders/eevee_forward_lib.glsl
engines/eevee_next/shaders/eevee_gbuffer_lib.glsl
engines/eevee_next/shaders/eevee_gbuffer_closure_test.glsl
@ -511,6 +512,7 @@ set(GLSL_SRC
engines/eevee_next/shaders/eevee_hiz_debug_frag.glsl
engines/eevee_next/shaders/eevee_hiz_update_comp.glsl
engines/eevee_next/shaders/eevee_horizon_denoise_comp.glsl
engines/eevee_next/shaders/eevee_horizon_resolve_comp.glsl
engines/eevee_next/shaders/eevee_horizon_scan_eval_lib.glsl
engines/eevee_next/shaders/eevee_horizon_scan_comp.glsl
engines/eevee_next/shaders/eevee_horizon_scan_lib.glsl
@ -529,6 +531,7 @@ set(GLSL_SRC
engines/eevee_next/shaders/eevee_lightprobe_irradiance_ray_comp.glsl
engines/eevee_next/shaders/eevee_lightprobe_irradiance_offset_comp.glsl
engines/eevee_next/shaders/eevee_lightprobe_irradiance_load_comp.glsl
engines/eevee_next/shaders/eevee_lightprobe_irradiance_world_comp.glsl
engines/eevee_next/shaders/eevee_lightprobe_lib.glsl
engines/eevee_next/shaders/eevee_lightprobe_volume_eval_lib.glsl
engines/eevee_next/shaders/eevee_lookdev_display_frag.glsl
@ -558,11 +561,11 @@ set(GLSL_SRC
engines/eevee_next/shaders/eevee_ray_types_lib.glsl
engines/eevee_next/shaders/eevee_reflection_probe_convolve_comp.glsl
engines/eevee_next/shaders/eevee_reflection_probe_eval_lib.glsl
engines/eevee_next/shaders/eevee_reflection_probe_irradiance_comp.glsl
engines/eevee_next/shaders/eevee_reflection_probe_lib.glsl
engines/eevee_next/shaders/eevee_reflection_probe_mapping_lib.glsl
engines/eevee_next/shaders/eevee_reflection_probe_remap_comp.glsl
engines/eevee_next/shaders/eevee_reflection_probe_select_comp.glsl
engines/eevee_next/shaders/eevee_reflection_probe_update_irradiance_comp.glsl
engines/eevee_next/shaders/eevee_renderpass_lib.glsl
engines/eevee_next/shaders/eevee_sampling_lib.glsl
engines/eevee_next/shaders/eevee_shadow_debug_frag.glsl

View File

@ -13,6 +13,10 @@
# pragma once
#endif
#ifndef SQUARE
# define SQUARE(x) ((x) * (x))
#endif
/* Look Up Tables. */
#define LUT_WORKGROUP_SIZE 16
@ -30,14 +34,17 @@
#define CULLING_TILE_GROUP_SIZE 256
/* Reflection Probes. */
#define SPHERE_PROBE_REMAP_GROUP_SIZE 32
#define SPHERE_PROBE_GROUP_SIZE 16
#define SPHERE_PROBE_SELECT_GROUP_SIZE 64
#define SPHERE_PROBE_MIPMAP_LEVELS 5
#define SPHERE_PROBE_SH_GROUP_SIZE 512
#define SPHERE_PROBE_SH_GROUP_SIZE 256
#define SPHERE_PROBE_SH_SAMPLES_PER_GROUP 64
/* Must be power of two for correct partitioning. */
#define SPHERE_PROBE_ATLAS_MAX_SUBDIV 10
#define SPHERE_PROBE_ATLAS_RES (1 << SPHERE_PROBE_ATLAS_MAX_SUBDIV)
/* Maximum number of thread-groups dispatched for remapping a probe to octahedral mapping. */
#define SPHERE_PROBE_MAX_HARMONIC SQUARE(SPHERE_PROBE_ATLAS_RES / SPHERE_PROBE_REMAP_GROUP_SIZE)
/* Start and end value for mixing sphere probe and volume probes. */
#define SPHERE_PROBE_MIX_START_ROUGHNESS 0.7
#define SPHERE_PROBE_MIX_END_ROUGHNESS 0.9

View File

@ -70,8 +70,6 @@ void VolumeProbeModule::init()
/* Clear the pool to avoid any interpolation to undefined values. */
irradiance_atlas_tx_.clear(float4(0.0f));
}
inst_.sphere_probes.tag_world_irradiance_for_update();
}
if (irradiance_atlas_tx_.is_valid() == false) {
@ -188,6 +186,7 @@ void VolumeProbeModule::set_view(View & /*view*/)
}
/* Then create brick & grid infos UBOs content. */
int world_grid_index = 0;
{
/* Stable sorting of grids. */
std::sort(
@ -226,6 +225,8 @@ void VolumeProbeModule::set_view(View & /*view*/)
}
/* Insert world grid last. */
world_grid_index = grids_len++;
VolumeProbeData grid;
grid.world_to_grid_transposed = float3x4::identity();
grid.grid_size = int3(1);
@ -233,7 +234,8 @@ void VolumeProbeModule::set_view(View & /*view*/)
grid.normal_bias = 0.0f;
grid.view_bias = 0.0f;
grid.facing_bias = 0.0f;
grids_infos_buf_[grids_len++] = grid;
grids_infos_buf_[world_grid_index] = grid;
bricks_infos_buf_.append(world_brick_index_);
if (grids_len < IRRADIANCE_GRID_MAX) {
@ -245,6 +247,25 @@ void VolumeProbeModule::set_view(View & /*view*/)
grids_infos_buf_.push_update();
}
/* Upload data for world. */
if (do_update_world_) {
grid_upload_ps_.init();
grid_upload_ps_.shader_set(inst_.shaders.static_shader_get(LIGHTPROBE_IRRADIANCE_WORLD));
grid_upload_ps_.bind_ssbo("harmonic_buf", &inst_.sphere_probes.spherical_harmonics_buf());
grid_upload_ps_.bind_ubo("grids_infos_buf", &grids_infos_buf_);
grid_upload_ps_.bind_ssbo("bricks_infos_buf", &bricks_infos_buf_);
grid_upload_ps_.push_constant("grid_index", world_grid_index);
grid_upload_ps_.bind_image("irradiance_atlas_img", &irradiance_atlas_tx_);
/* Sync with extraction. */
grid_upload_ps_.barrier(GPU_BARRIER_SHADER_STORAGE);
/* Only upload one brick. */
grid_upload_ps_.dispatch(int3(1));
/* Sync with next load. */
grid_upload_ps_.barrier(GPU_BARRIER_TEXTURE_FETCH);
inst_.manager->submit(grid_upload_ps_);
}
/* Upload data for each grid that need to be inserted in the atlas.
* Upload by order of dependency. */
/* Start at world index to not load any other grid (+1 because we decrement at loop start). */

View File

@ -192,10 +192,6 @@ class VolumeProbeModule {
public:
IrradianceBake bake;
/** True if world irradiance need to be updated. */
/* TODO(fclem): move to private once world irradiance extraction is moved to irradiance cache. */
bool do_update_world_ = true;
private:
Instance &inst_;
@ -222,12 +218,22 @@ class VolumeProbeModule {
bool display_grids_enabled_ = false;
PassSimple display_grids_ps_ = {"VolumeProbeModule.Display Grids"};
/** True if world irradiance need to be updated. */
bool do_update_world_ = true;
public:
VolumeProbeModule(Instance &inst) : bake(inst), inst_(inst){};
~VolumeProbeModule(){};
void init();
void sync();
/* Tag all grids for reupload in set_view and composite them with the world irradiance. */
void update_world_irradiance()
{
do_update_world_ = true;
}
void set_view(View &view);
void viewport_draw(View &view, GPUFrameBuffer *view_fb);

View File

@ -210,7 +210,6 @@ void LightProbeModule::sync_world(const ::World *world, bool has_update)
if (has_update) {
world_sphere_.do_render = true;
sph_module.tag_world_irradiance_for_update();
}
}

View File

@ -60,19 +60,14 @@ void RayTraceModule::sync()
GPUShader *sh = inst_.shaders.static_shader_get(RAY_TILE_COMPACT);
pass.init();
pass.specialize_constant(sh, "closure_index", &data_.closure_index);
pass.specialize_constant(sh, "resolution_scale", &data_.resolution_scale);
pass.shader_set(sh);
pass.bind_image("tile_raytrace_denoise_img", &tile_raytrace_denoise_tx_);
pass.bind_image("tile_raytrace_tracing_img", &tile_raytrace_tracing_tx_);
pass.bind_image("tile_horizon_denoise_img", &tile_horizon_denoise_tx_);
pass.bind_image("tile_horizon_tracing_img", &tile_horizon_tracing_tx_);
pass.bind_ssbo("raytrace_tracing_dispatch_buf", &raytrace_tracing_dispatch_buf_);
pass.bind_ssbo("raytrace_denoise_dispatch_buf", &raytrace_denoise_dispatch_buf_);
pass.bind_ssbo("horizon_tracing_dispatch_buf", &horizon_tracing_dispatch_buf_);
pass.bind_ssbo("horizon_denoise_dispatch_buf", &horizon_denoise_dispatch_buf_);
pass.bind_ssbo("raytrace_tracing_tiles_buf", &raytrace_tracing_tiles_buf_);
pass.bind_ssbo("raytrace_denoise_tiles_buf", &raytrace_denoise_tiles_buf_);
pass.bind_ssbo("horizon_tracing_tiles_buf", &horizon_tracing_tiles_buf_);
pass.bind_ssbo("horizon_denoise_tiles_buf", &horizon_denoise_tiles_buf_);
pass.bind_resources(inst_.uniform_data);
pass.dispatch(&tile_compact_dispatch_size_);
pass.barrier(GPU_BARRIER_SHADER_STORAGE);
@ -220,6 +215,24 @@ void RayTraceModule::sync()
pass.dispatch(raytrace_denoise_dispatch_buf_);
pass.barrier(GPU_BARRIER_SHADER_IMAGE_ACCESS);
}
{
PassSimple &pass = horizon_schedule_ps_;
/* Reuse tile compaction shader but feed it with horizon scan specific buffers. */
GPUShader *sh = inst_.shaders.static_shader_get(RAY_TILE_COMPACT);
pass.init();
pass.specialize_constant(sh, "closure_index", 0);
pass.specialize_constant(sh, "resolution_scale", &data_.horizon_resolution_scale);
pass.shader_set(sh);
pass.bind_image("tile_raytrace_denoise_img", &tile_horizon_denoise_tx_);
pass.bind_image("tile_raytrace_tracing_img", &tile_horizon_tracing_tx_);
pass.bind_ssbo("raytrace_tracing_dispatch_buf", &horizon_tracing_dispatch_buf_);
pass.bind_ssbo("raytrace_denoise_dispatch_buf", &horizon_denoise_dispatch_buf_);
pass.bind_ssbo("raytrace_tracing_tiles_buf", &horizon_tracing_tiles_buf_);
pass.bind_ssbo("raytrace_denoise_tiles_buf", &horizon_denoise_tiles_buf_);
pass.bind_resources(inst_.uniform_data);
pass.dispatch(&horizon_schedule_dispatch_size_);
pass.barrier(GPU_BARRIER_SHADER_STORAGE);
}
{
PassSimple &pass = horizon_setup_ps_;
pass.init();
@ -232,39 +245,64 @@ void RayTraceModule::sync()
pass.bind_image("out_normal_img", &downsampled_in_normal_tx_);
pass.bind_resources(inst_.uniform_data);
pass.bind_resources(inst_.gbuffer);
pass.dispatch(&tracing_dispatch_size_);
pass.dispatch(&horizon_tracing_dispatch_size_);
pass.barrier(GPU_BARRIER_SHADER_IMAGE_ACCESS);
}
{
PassSimple &pass = horizon_scan_ps_;
pass.init();
GPUShader *sh = inst_.shaders.static_shader_get(HORIZON_SCAN);
pass.specialize_constant(sh, "closure_index", &data_.closure_index);
pass.shader_set(sh);
pass.bind_image("horizon_radiance_img", &horizon_radiance_tx_);
pass.bind_image("horizon_occlusion_img", &horizon_occlusion_tx_);
pass.bind_ssbo("tiles_coord_buf", &horizon_tracing_tiles_buf_);
pass.bind_texture("screen_radiance_tx", &downsampled_in_radiance_tx_);
pass.bind_texture("screen_normal_tx", &downsampled_in_normal_tx_);
pass.bind_image("horizon_radiance_0_img", &horizon_radiance_tx_[0]);
pass.bind_image("horizon_radiance_1_img", &horizon_radiance_tx_[1]);
pass.bind_image("horizon_radiance_2_img", &horizon_radiance_tx_[2]);
pass.bind_image("horizon_radiance_3_img", &horizon_radiance_tx_[3]);
pass.bind_ssbo("tiles_coord_buf", &horizon_tracing_tiles_buf_);
pass.bind_texture(RBUFS_UTILITY_TEX_SLOT, inst_.pipelines.utility_tx);
pass.bind_resources(inst_.uniform_data);
pass.bind_resources(inst_.hiz_buffer.front);
pass.bind_resources(inst_.sampling);
pass.bind_resources(inst_.gbuffer);
pass.dispatch(horizon_tracing_dispatch_buf_);
pass.barrier(GPU_BARRIER_SHADER_IMAGE_ACCESS);
pass.barrier(GPU_BARRIER_TEXTURE_FETCH);
}
{
PassSimple &pass = horizon_denoise_ps_;
pass.init();
GPUShader *sh = inst_.shaders.static_shader_get(HORIZON_DENOISE);
pass.specialize_constant(sh, "closure_index", &data_.closure_index);
pass.shader_set(sh);
pass.bind_texture("in_sh_0_tx", &horizon_radiance_tx_[0]);
pass.bind_texture("in_sh_1_tx", &horizon_radiance_tx_[1]);
pass.bind_texture("in_sh_2_tx", &horizon_radiance_tx_[2]);
pass.bind_texture("in_sh_3_tx", &horizon_radiance_tx_[3]);
pass.bind_texture("screen_normal_tx", &downsampled_in_normal_tx_);
pass.bind_image("out_sh_0_img", &horizon_radiance_denoised_tx_[0]);
pass.bind_image("out_sh_1_img", &horizon_radiance_denoised_tx_[1]);
pass.bind_image("out_sh_2_img", &horizon_radiance_denoised_tx_[2]);
pass.bind_image("out_sh_3_img", &horizon_radiance_denoised_tx_[3]);
pass.bind_ssbo("tiles_coord_buf", &horizon_tracing_tiles_buf_);
pass.bind_resources(inst_.uniform_data);
pass.bind_resources(inst_.sampling);
pass.bind_resources(inst_.hiz_buffer.front);
pass.dispatch(horizon_tracing_dispatch_buf_);
pass.barrier(GPU_BARRIER_TEXTURE_FETCH);
}
{
PassSimple &pass = horizon_resolve_ps_;
pass.init();
GPUShader *sh = inst_.shaders.static_shader_get(HORIZON_RESOLVE);
pass.shader_set(sh);
pass.bind_texture("depth_tx", &depth_tx);
pass.bind_image("horizon_radiance_img", &horizon_radiance_tx_);
pass.bind_image("horizon_occlusion_img", &horizon_occlusion_tx_);
pass.bind_image("radiance_img", &horizon_scan_output_tx_);
pass.bind_image("tile_mask_img", &tile_horizon_denoise_tx_);
pass.bind_texture("horizon_radiance_0_tx", &horizon_radiance_denoised_tx_[0]);
pass.bind_texture("horizon_radiance_1_tx", &horizon_radiance_denoised_tx_[1]);
pass.bind_texture("horizon_radiance_2_tx", &horizon_radiance_denoised_tx_[2]);
pass.bind_texture("horizon_radiance_3_tx", &horizon_radiance_denoised_tx_[3]);
pass.bind_texture("screen_normal_tx", &downsampled_in_normal_tx_);
pass.bind_image("closure0_img", &horizon_scan_output_tx_[0]);
pass.bind_image("closure1_img", &horizon_scan_output_tx_[1]);
pass.bind_image("closure2_img", &horizon_scan_output_tx_[2]);
pass.bind_ssbo("tiles_coord_buf", &horizon_denoise_tiles_buf_);
pass.bind_resources(inst_.uniform_data);
pass.bind_resources(inst_.sampling);
@ -297,19 +335,26 @@ RayTraceResult RayTraceModule::render(RayTraceBuffer &rt_buffer,
bool use_horizon_scan = options.screen_trace_max_roughness < 1.0f;
const int resolution_scale = max_ii(1, power_of_2_max_i(options.resolution_scale));
const int horizon_resolution_scale = max_ii(
1, power_of_2_max_i(inst_.scene->eevee.gtao_resolution));
const int2 extent = inst_.film.render_extent_get();
const int2 tracing_res = math::divide_ceil(extent, int2(resolution_scale));
const int2 tracing_res_horizon = math::divide_ceil(extent, int2(horizon_resolution_scale));
const int2 dummy_extent(1, 1);
const int2 group_size(RAYTRACE_GROUP_SIZE);
const int2 denoise_tiles = divide_ceil(extent, group_size);
const int2 raytrace_tiles = divide_ceil(tracing_res, group_size);
const int2 raytrace_tiles_horizon = divide_ceil(tracing_res_horizon, group_size);
const int denoise_tile_count = denoise_tiles.x * denoise_tiles.y;
const int raytrace_tile_count = raytrace_tiles.x * raytrace_tiles.y;
const int raytrace_tile_count_horizon = raytrace_tiles_horizon.x * raytrace_tiles_horizon.y;
tile_classify_dispatch_size_ = int3(denoise_tiles, 1);
horizon_schedule_dispatch_size_ = int3(divide_ceil(raytrace_tiles_horizon, group_size), 1);
tile_compact_dispatch_size_ = int3(divide_ceil(raytrace_tiles, group_size), 1);
tracing_dispatch_size_ = int3(divide_ceil(tracing_res, group_size), 1);
tracing_dispatch_size_ = int3(raytrace_tiles, 1);
horizon_tracing_dispatch_size_ = int3(raytrace_tiles_horizon, 1);
/* TODO(fclem): Use real max closure count from shader. */
const int closure_count = 3;
@ -317,15 +362,16 @@ RayTraceResult RayTraceModule::render(RayTraceBuffer &rt_buffer,
eGPUTextureUsage usage_rw = GPU_TEXTURE_USAGE_SHADER_READ | GPU_TEXTURE_USAGE_SHADER_WRITE;
tile_raytrace_denoise_tx_.ensure_2d_array(format, denoise_tiles, closure_count, usage_rw);
tile_raytrace_tracing_tx_.ensure_2d_array(format, raytrace_tiles, closure_count, usage_rw);
tile_horizon_denoise_tx_.ensure_2d_array(format, denoise_tiles, closure_count, usage_rw);
tile_horizon_tracing_tx_.ensure_2d_array(format, raytrace_tiles, closure_count, usage_rw);
/* Kept as 2D array for compatibility with the tile compaction shader. */
tile_horizon_denoise_tx_.ensure_2d_array(format, denoise_tiles, 1, usage_rw);
tile_horizon_tracing_tx_.ensure_2d_array(format, raytrace_tiles_horizon, 1, usage_rw);
tile_raytrace_denoise_tx_.clear(uint4(0u));
tile_raytrace_tracing_tx_.clear(uint4(0u));
tile_horizon_denoise_tx_.clear(uint4(0u));
tile_horizon_tracing_tx_.clear(uint4(0u));
horizon_tracing_tiles_buf_.resize(ceil_to_multiple_u(raytrace_tile_count, 512));
horizon_tracing_tiles_buf_.resize(ceil_to_multiple_u(raytrace_tile_count_horizon, 512));
horizon_denoise_tiles_buf_.resize(ceil_to_multiple_u(denoise_tile_count, 512));
raytrace_tracing_tiles_buf_.resize(ceil_to_multiple_u(raytrace_tile_count, 512));
raytrace_denoise_tiles_buf_.resize(ceil_to_multiple_u(denoise_tile_count, 512));
@ -344,6 +390,9 @@ RayTraceResult RayTraceModule::render(RayTraceBuffer &rt_buffer,
data_.full_resolution = extent;
data_.full_resolution_inv = 1.0f / float2(extent);
data_.horizon_resolution_scale = horizon_resolution_scale;
data_.horizon_resolution_bias = int2(inst_.sampling.rng_2d_get(SAMPLING_RAYTRACE_V) *
horizon_resolution_scale);
/* TODO(fclem): Eventually all uniform data is setup here. */
inst_.uniform_data.push_update();
@ -352,15 +401,9 @@ RayTraceResult RayTraceModule::render(RayTraceBuffer &rt_buffer,
DRW_stats_group_start("Raytracing");
if (use_horizon_scan) {
downsampled_in_radiance_tx_.acquire(tracing_res, RAYTRACE_RADIANCE_FORMAT, usage_rw);
downsampled_in_normal_tx_.acquire(tracing_res, GPU_RGBA8, usage_rw);
const bool has_active_closure = active_closures != CLOSURE_NONE;
screen_radiance_front_tx_ = screen_radiance_front_tx;
inst_.manager->submit(horizon_setup_ps_, render_view);
}
if (active_closures != CLOSURE_NONE) {
if (has_active_closure) {
inst_.manager->submit(tile_classify_ps_);
}
@ -375,12 +418,47 @@ RayTraceResult RayTraceModule::render(RayTraceBuffer &rt_buffer,
screen_radiance_front_tx,
screen_radiance_persmat,
main_view,
render_view,
use_horizon_scan);
render_view);
}
downsampled_in_radiance_tx_.release();
downsampled_in_normal_tx_.release();
if (has_active_closure) {
if (use_horizon_scan) {
DRW_stats_group_start("Horizon Scan");
screen_radiance_front_tx_ = screen_radiance_front_tx;
downsampled_in_radiance_tx_.acquire(tracing_res_horizon, RAYTRACE_RADIANCE_FORMAT, usage_rw);
downsampled_in_normal_tx_.acquire(tracing_res_horizon, GPU_RGB10_A2, usage_rw);
horizon_radiance_tx_[0].acquire(tracing_res_horizon, GPU_RGBA16F, usage_rw);
horizon_radiance_denoised_tx_[0].acquire(tracing_res_horizon, GPU_RGBA16F, usage_rw);
for (int i : IndexRange(1, 3)) {
horizon_radiance_tx_[i].acquire(tracing_res_horizon, GPU_RGBA8, usage_rw);
horizon_radiance_denoised_tx_[i].acquire(tracing_res_horizon, GPU_RGBA8, usage_rw);
}
for (int i : IndexRange(3)) {
horizon_scan_output_tx_[i] = result.closures[i].get();
}
horizon_tracing_dispatch_buf_.clear_to_zero();
horizon_denoise_dispatch_buf_.clear_to_zero();
inst_.manager->submit(horizon_schedule_ps_);
inst_.manager->submit(horizon_setup_ps_, render_view);
inst_.manager->submit(horizon_scan_ps_, render_view);
inst_.manager->submit(horizon_denoise_ps_, render_view);
inst_.manager->submit(horizon_resolve_ps_, render_view);
for (int i : IndexRange(4)) {
horizon_radiance_tx_[i].release();
horizon_radiance_denoised_tx_[i].release();
}
downsampled_in_radiance_tx_.release();
downsampled_in_normal_tx_.release();
DRW_stats_group_end();
}
}
DRW_stats_group_end();
@ -397,8 +475,7 @@ RayTraceResultTexture RayTraceModule::trace(
const float4x4 &screen_radiance_persmat,
/* TODO(fclem): Maybe wrap these two in some other class. */
View &main_view,
View &render_view,
bool use_horizon_scan)
View &render_view)
{
RayTraceBuffer::DenoiseBuffer *denoise_buf = &rt_buffer.closures[closure_index];
@ -452,8 +529,6 @@ RayTraceResultTexture RayTraceModule::trace(
/* Ray setup. */
raytrace_tracing_dispatch_buf_.clear_to_zero();
raytrace_denoise_dispatch_buf_.clear_to_zero();
horizon_tracing_dispatch_buf_.clear_to_zero();
horizon_denoise_dispatch_buf_.clear_to_zero();
inst_.manager->submit(tile_compact_ps_);
{
@ -555,20 +630,6 @@ RayTraceResultTexture RayTraceModule::trace(
denoise_variance_tx_.release();
if (use_horizon_scan) {
horizon_occlusion_tx_.acquire(tracing_res, GPU_R8, usage_rw);
horizon_radiance_tx_.acquire(tracing_res, RAYTRACE_RADIANCE_FORMAT, usage_rw);
inst_.manager->submit(horizon_scan_ps_, render_view);
horizon_scan_output_tx_ = result.get();
inst_.manager->submit(horizon_denoise_ps_, render_view);
horizon_occlusion_tx_.release();
horizon_radiance_tx_.release();
}
DRW_stats_group_end();
return result;

View File

@ -120,16 +120,20 @@ class RayTraceModule {
draw::PassSimple denoise_spatial_ps_ = {"DenoiseSpatial"};
draw::PassSimple denoise_temporal_ps_ = {"DenoiseTemporal"};
draw::PassSimple denoise_bilateral_ps_ = {"DenoiseBilateral"};
draw::PassSimple horizon_schedule_ps_ = {"HorizonScan.Schedule"};
draw::PassSimple horizon_setup_ps_ = {"HorizonScan.Setup"};
draw::PassSimple horizon_scan_ps_ = {"HorizonScan.Trace"};
draw::PassSimple horizon_denoise_ps_ = {"HorizonScan.Denoise"};
draw::PassSimple horizon_resolve_ps_ = {"HorizonScan.Resolve"};
/** Dispatch with enough tiles for the whole screen. */
int3 tile_classify_dispatch_size_ = int3(1);
/** Dispatch with enough tiles for the tile mask. */
int3 tile_compact_dispatch_size_ = int3(1);
int3 horizon_schedule_dispatch_size_ = int3(1);
/** Dispatch with enough tiles for the tracing resolution. */
int3 tracing_dispatch_size_ = int3(1);
int3 horizon_tracing_dispatch_size_ = int3(1);
/** 2D tile mask to check which unused adjacent tile we need to clear and which tile we need to
* dispatch for each work type. */
Texture tile_raytrace_denoise_tx_ = {"tile_raytrace_denoise_tx_"};
@ -145,7 +149,7 @@ class RayTraceModule {
/** Indirect dispatch denoise full-resolution tiles. */
DispatchIndirectBuf horizon_denoise_dispatch_buf_ = {"horizon_denoise_dispatch_buf_"};
/** Pointer to the texture to store the result of horizon scan in. */
GPUTexture *horizon_scan_output_tx_ = nullptr;
GPUTexture *horizon_scan_output_tx_[3] = {nullptr};
/** Tile buffer that contains tile coordinates. */
RayTraceTileBuf raytrace_tracing_tiles_buf_ = {"raytrace_tracing_tiles_buf_"};
RayTraceTileBuf raytrace_denoise_tiles_buf_ = {"raytrace_denoise_tiles_buf_"};
@ -157,10 +161,9 @@ class RayTraceModule {
TextureFromPool ray_time_tx_ = {"ray_data_tx"};
/** Texture containing the ray hit radiance (tracing-res). */
TextureFromPool ray_radiance_tx_ = {"ray_radiance_tx"};
/** Texture containing the horizon visibility mask. */
TextureFromPool horizon_occlusion_tx_ = {"horizon_occlusion_tx_"};
/** Texture containing the horizon local radiance. */
TextureFromPool horizon_radiance_tx_ = {"horizon_radiance_tx_"};
TextureFromPool horizon_radiance_tx_[4] = {{"horizon_radiance_tx_"}};
TextureFromPool horizon_radiance_denoised_tx_[4] = {{"horizon_radiance_denoised_tx_"}};
/** Texture containing the input screen radiance but re-projected. */
TextureFromPool downsampled_in_radiance_tx_ = {"downsampled_in_radiance_tx_"};
/** Texture containing the view space normal. The BSDF normal is arbitrarily chosen. */
@ -241,8 +244,7 @@ class RayTraceModule {
const float4x4 &screen_radiance_persmat,
/* TODO(fclem): Maybe wrap these two in some other class. */
View &main_view,
View &render_view,
bool use_horizon_scan);
View &render_view);
};
/** \} */

View File

@ -37,12 +37,16 @@ void SphereProbeModule::begin_sync()
const RaytraceEEVEE &options = instance_.scene->eevee.ray_tracing_options;
float probe_brightness_clamp = (options.sample_clamp > 0.0) ? options.sample_clamp : 1e20;
GPUShader *shader = instance_.shaders.static_shader_get(SPHERE_PROBE_REMAP);
PassSimple &pass = remap_ps_;
pass.init();
pass.shader_set(instance_.shaders.static_shader_get(SPHERE_PROBE_REMAP));
pass.specialize_constant(shader, "extract_sh", &extract_sh_);
pass.shader_set(shader);
pass.bind_texture("cubemap_tx", &cubemap_tx_);
pass.bind_texture("atlas_tx", &probes_tx_);
pass.bind_image("atlas_img", &probes_tx_);
pass.bind_ssbo("out_sh", &tmp_spherical_harmonics_);
pass.push_constant("probe_coord_packed", reinterpret_cast<int4 *>(&probe_sampling_coord_));
pass.push_constant("write_coord_packed", reinterpret_cast<int4 *>(&probe_write_coord_));
pass.push_constant("world_coord_packed", reinterpret_cast<int4 *>(&world_data.atlas_coord));
@ -64,13 +68,14 @@ void SphereProbeModule::begin_sync()
pass.dispatch(&dispatch_probe_convolve_);
}
{
PassSimple &pass = update_irradiance_ps_;
PassSimple &pass = sum_sh_ps_;
pass.init();
pass.shader_set(instance_.shaders.static_shader_get(SPHERE_PROBE_UPDATE_IRRADIANCE));
pass.push_constant("world_coord_packed", reinterpret_cast<int4 *>(&world_data.atlas_coord));
pass.bind_image("irradiance_atlas_img", &instance_.volume_probes.irradiance_atlas_tx_);
pass.bind_texture("reflection_probes_tx", &probes_tx_);
pass.dispatch(int2(1, 1));
pass.shader_set(instance_.shaders.static_shader_get(SPHERE_PROBE_IRRADIANCE));
pass.push_constant("probe_remap_dispatch_size", &dispatch_probe_pack_);
pass.bind_ssbo("in_sh", &tmp_spherical_harmonics_);
pass.bind_ssbo("out_sh", &spherical_harmonics_);
pass.barrier(GPU_BARRIER_SHADER_STORAGE);
pass.dispatch(1);
}
{
PassSimple &pass = select_ps_;
@ -155,7 +160,7 @@ void SphereProbeModule::ensure_cubemap_render_target(int resolution)
/* TODO(fclem): deallocate it. */
}
SphereProbeModule::UpdateInfo SphereProbeModule::update_info_from_probe(const SphereProbe &probe)
SphereProbeModule::UpdateInfo SphereProbeModule::update_info_from_probe(SphereProbe &probe)
{
SphereProbeModule::UpdateInfo info = {};
info.atlas_coord = probe.atlas_coord;
@ -163,22 +168,21 @@ SphereProbeModule::UpdateInfo SphereProbeModule::update_info_from_probe(const Sp
info.clipping_distances = probe.clipping_distances;
info.probe_pos = probe.location;
info.do_render = probe.do_render;
info.do_world_irradiance_update = false;
probe.do_render = false;
probe.use_for_render = true;
ensure_cubemap_render_target(info.cube_target_extent);
return info;
}
std::optional<SphereProbeModule::UpdateInfo> SphereProbeModule::world_update_info_pop()
{
SphereProbe &world_probe = instance_.light_probes.world_sphere_;
if (!world_probe.do_render && !do_world_irradiance_update) {
return std::nullopt;
if (world_probe.do_render) {
return update_info_from_probe(world_probe);
}
SphereProbeModule::UpdateInfo info = update_info_from_probe(world_probe);
info.do_world_irradiance_update = do_world_irradiance_update;
world_probe.do_render = false;
do_world_irradiance_update = false;
ensure_cubemap_render_target(info.cube_target_extent);
return info;
return std::nullopt;
}
std::optional<SphereProbeModule::UpdateInfo> SphereProbeModule::probe_update_info_pop()
@ -192,23 +196,22 @@ std::optional<SphereProbeModule::UpdateInfo> SphereProbeModule::probe_update_inf
if (!probe.do_render) {
continue;
}
SphereProbeModule::UpdateInfo info = update_info_from_probe(probe);
probe.do_render = false;
probe.use_for_render = true;
ensure_cubemap_render_target(info.cube_target_extent);
return info;
return update_info_from_probe(probe);
}
return std::nullopt;
}
void SphereProbeModule::remap_to_octahedral_projection(const SphereProbeAtlasCoord &atlas_coord)
void SphereProbeModule::remap_to_octahedral_projection(const SphereProbeAtlasCoord &atlas_coord,
bool extract_spherical_harmonics)
{
/* Update shader parameters that change per dispatch. */
probe_sampling_coord_ = atlas_coord.as_sampling_coord();
probe_write_coord_ = atlas_coord.as_write_coord(0);
int resolution = probe_write_coord_.extent;
dispatch_probe_pack_ = int3(int2(ceil_division(resolution, SPHERE_PROBE_GROUP_SIZE)), 1);
dispatch_probe_pack_ = int3(
int2(math::divide_ceil(int2(resolution), int2(SPHERE_PROBE_REMAP_GROUP_SIZE))), 1);
extract_sh_ = extract_spherical_harmonics;
instance_.manager->submit(remap_ps_);
/* Populate the mip levels */
@ -219,20 +222,21 @@ void SphereProbeModule::remap_to_octahedral_projection(const SphereProbeAtlasCoo
probe_read_coord_ = atlas_coord.as_write_coord(i);
probe_write_coord_ = atlas_coord.as_write_coord(i + 1);
int out_mip_res = probe_write_coord_.extent;
dispatch_probe_convolve_ = int3(int2(ceil_division(out_mip_res, SPHERE_PROBE_GROUP_SIZE)), 1);
dispatch_probe_convolve_ = int3(
math::divide_ceil(int2(out_mip_res), int2(SPHERE_PROBE_GROUP_SIZE)), 1);
instance_.manager->submit(convolve_ps_);
}
if (extract_spherical_harmonics) {
instance_.manager->submit(sum_sh_ps_);
/* All volume probe that needs to composite the world probe need to be updated. */
instance_.volume_probes.update_world_irradiance();
}
/* Sync with atlas usage for shading. */
GPU_memory_barrier(GPU_BARRIER_TEXTURE_FETCH);
}
void SphereProbeModule::update_world_irradiance()
{
instance_.manager->submit(update_irradiance_ps_);
/* All volume probe that needs to composite the world probe need to be updated. */
instance_.volume_probes.do_update_world_ = true;
}
void SphereProbeModule::set_view(View & /*view*/)
{
Vector<SphereProbe *> probe_active;

View File

@ -44,8 +44,8 @@ class SphereProbeModule {
/** Copy the rendered cube-map to the atlas texture. */
PassSimple remap_ps_ = {"Probe.CubemapToOctahedral"};
/** Extract irradiance information from the world. */
PassSimple update_irradiance_ps_ = {"Probe.UpdateIrradiance"};
/** Sum irradiance information optionally extracted during `remap_ps_`. */
PassSimple sum_sh_ps_ = {"Probe.SumSphericalHarmonics"};
/** Copy volume probe irradiance for the center of sphere probes. */
PassSimple select_ps_ = {"Probe.Select"};
/** Convolve the octahedral map to fill the Mip-map levels. */
@ -55,6 +55,8 @@ class SphereProbeModule {
/** Output mip level for the convolution. */
GPUTexture *convolve_output_ = nullptr;
int convolve_lod_ = 0;
/* True if we extract spherical harmonic during `remap_ps_`. */
bool extract_sh_ = false;
int3 dispatch_probe_pack_ = int3(1);
int3 dispatch_probe_convolve_ = int3(1);
@ -78,6 +80,12 @@ class SphereProbeModule {
/** Number of the probe to process in the select phase. */
int reflection_probe_count_ = 0;
/** Intermediate buffer to store spherical harmonics. */
StorageArrayBuffer<SphereProbeHarmonic, SPHERE_PROBE_MAX_HARMONIC, true>
tmp_spherical_harmonics_ = {"tmp_spherical_harmonics_"};
/** Final buffer containing the spherical harmonics for the world. */
StorageBuffer<SphereProbeHarmonic, true> spherical_harmonics_ = {"spherical_harmonics_"};
/**
* True if the next redraw will trigger a light-probe sphere update.
* As syncing the draw passes for rendering has a significant overhead,
@ -120,9 +128,9 @@ class SphereProbeModule {
*/
int probe_render_extent() const;
void tag_world_irradiance_for_update()
StorageBuffer<SphereProbeHarmonic, true> &spherical_harmonics_buf()
{
do_world_irradiance_update = true;
return spherical_harmonics_;
}
private:
@ -154,10 +162,9 @@ class SphereProbeModule {
SphereProbeAtlasCoord atlas_coord;
bool do_render;
bool do_world_irradiance_update;
};
UpdateInfo update_info_from_probe(const SphereProbe &probe);
UpdateInfo update_info_from_probe(SphereProbe &probe);
/**
* Pop the next reflection probe that requires to be updated.
@ -166,10 +173,13 @@ class SphereProbeModule {
std::optional<UpdateInfo> probe_update_info_pop();
/**
* Internal processing passes.
* Remap the rendered cube-map `cubemap_tx_` to a octahedral map inside the atlas at the given
* coordinate.
* If `extract_spherical_harmonics` is true, it will extract the spherical harmonics into
* `spherical_harmonics_`.
*/
void remap_to_octahedral_projection(const SphereProbeAtlasCoord &atlas_coord);
void update_world_irradiance();
void remap_to_octahedral_projection(const SphereProbeAtlasCoord &atlas_coord,
bool extract_spherical_harmonics);
void sync_display(Vector<SphereProbe *> &probe_active);
};

View File

@ -119,6 +119,8 @@ const char *ShaderModule::static_shader_create_info_name_get(eShaderType shader_
return "eevee_hiz_update_layer";
case HORIZON_DENOISE:
return "eevee_horizon_denoise";
case HORIZON_RESOLVE:
return "eevee_horizon_resolve";
case HORIZON_SCAN:
return "eevee_horizon_scan";
case HORIZON_SETUP:
@ -215,12 +217,14 @@ const char *ShaderModule::static_shader_create_info_name_get(eShaderType shader_
return "eevee_lightprobe_irradiance_ray";
case LIGHTPROBE_IRRADIANCE_LOAD:
return "eevee_lightprobe_irradiance_load";
case LIGHTPROBE_IRRADIANCE_WORLD:
return "eevee_lightprobe_irradiance_world";
case SPHERE_PROBE_CONVOLVE:
return "eevee_reflection_probe_convolve";
case SPHERE_PROBE_REMAP:
return "eevee_reflection_probe_remap";
case SPHERE_PROBE_UPDATE_IRRADIANCE:
return "eevee_reflection_probe_update_irradiance";
case SPHERE_PROBE_IRRADIANCE:
return "eevee_reflection_probe_irradiance";
case SPHERE_PROBE_SELECT:
return "eevee_reflection_probe_select";
case SHADOW_CLIPMAP_CLEAR:

View File

@ -73,6 +73,7 @@ enum eShaderType {
HIZ_DEBUG,
HORIZON_DENOISE,
HORIZON_RESOLVE,
HORIZON_SCAN,
HORIZON_SETUP,
@ -86,6 +87,7 @@ enum eShaderType {
LIGHTPROBE_IRRADIANCE_OFFSET,
LIGHTPROBE_IRRADIANCE_RAY,
LIGHTPROBE_IRRADIANCE_LOAD,
LIGHTPROBE_IRRADIANCE_WORLD,
LOOKDEV_DISPLAY,
@ -107,7 +109,7 @@ enum eShaderType {
SPHERE_PROBE_CONVOLVE,
SPHERE_PROBE_REMAP,
SPHERE_PROBE_SELECT,
SPHERE_PROBE_UPDATE_IRRADIANCE,
SPHERE_PROBE_IRRADIANCE,
SHADOW_CLIPMAP_CLEAR,
SHADOW_DEBUG,

View File

@ -1105,6 +1105,16 @@ struct SphereProbeDisplayData {
};
BLI_STATIC_ASSERT_ALIGN(SphereProbeDisplayData, 16)
/* Used for sphere probe spherical harmonics extraction. Output one for each thread-group
* and do a sum afterward. Reduces bandwidth usage. */
struct SphereProbeHarmonic {
float4 L0_M0;
float4 L1_Mn1;
float4 L1_M0;
float4 L1_Mp1;
};
BLI_STATIC_ASSERT_ALIGN(SphereProbeHarmonic, 16)
/** \} */
/* -------------------------------------------------------------------- */
@ -1323,6 +1333,9 @@ struct RayTraceData {
int resolution_scale;
/** View space thickness the objects. */
float thickness;
/** Scale and bias to go from horizon-trace resolution to input resolution. */
int2 horizon_resolution_bias;
int horizon_resolution_scale;
/** Determine how fast the sample steps are getting bigger. */
float quality;
/** Maximum brightness during lighting evaluation. */
@ -1613,6 +1626,5 @@ using VelocityIndexBuf = draw::StorageArrayBuffer<VelocityIndex, 16>;
using VelocityObjectBuf = draw::StorageArrayBuffer<float4x4, 16>;
using CryptomatteObjectBuf = draw::StorageArrayBuffer<float2, 16>;
using ClipPlaneBuf = draw::UniformBuffer<ClipPlaneData>;
} // namespace blender::eevee
#endif

View File

@ -287,11 +287,7 @@ void CaptureView::render_world()
inst_.pipelines.world.render(view);
}
inst_.sphere_probes.remap_to_octahedral_projection(update_info->atlas_coord);
}
if (update_info->do_world_irradiance_update) {
inst_.sphere_probes.update_world_irradiance();
inst_.sphere_probes.remap_to_octahedral_projection(update_info->atlas_coord, true);
}
GPU_debug_group_end();
@ -349,7 +345,7 @@ void CaptureView::render_probes()
inst_.render_buffers.release();
inst_.gbuffer.release();
GPU_debug_group_end();
inst_.sphere_probes.remap_to_octahedral_projection(update_info->atlas_coord);
inst_.sphere_probes.remap_to_octahedral_projection(update_info->atlas_coord, false);
}
if (inst_.pipelines.data.is_probe_reflection) {

View File

@ -31,22 +31,15 @@ void main()
noise.y = utility_tx_fetch(utility_tx, vec2(texel), UTIL_BLUE_NOISE_LAYER).r;
noise = fract(noise + sampling_rng_2D_get(SAMPLING_AO_U));
ClosureOcclusion occlusion;
occlusion.N = vN;
HorizonScanResult scan = horizon_scan_eval(vP,
vN,
noise,
uniform_buf.ao.pixel_size,
uniform_buf.ao.distance,
uniform_buf.ao.thickness,
uniform_buf.ao.angle_bias,
10,
false);
HorizonScanContext ctx;
ctx.occlusion = occlusion;
horizon_scan_eval(vP,
ctx,
noise,
uniform_buf.ao.pixel_size,
uniform_buf.ao.distance,
uniform_buf.ao.thickness,
uniform_buf.ao.angle_bias,
10,
false);
imageStore(
out_ao_img, ivec3(texel, out_ao_img_layer_index), vec4(saturate(ctx.occlusion_result.r)));
imageStore(out_ao_img, ivec3(texel, out_ao_img_layer_index), vec4(saturate(scan.result)));
}

View File

@ -0,0 +1,59 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/**
* Filtering utilities.
*/
#pragma BLENDER_REQUIRE(gpu_shader_math_base_lib.glsl)
/**
* Return the factor to filter_gaussian_weight. This is handy utility function to compute your
* gaussian parameter in a documented manner.
* - `linear_distance` is the distance at which the filter will have covered the given amount of
* `standard_deviation`. Must not be null.
* - `standard_deviation` is the shape of the bell. Higher values sharpens the filter.
*
* https://en.wikipedia.org/wiki/Standard_deviation#/media/File:Standard_deviation_diagram.svg
*
* Example: for a 5px 1d gaussian filter, one would set `linear_distance` of 2.5.
* `standard_deviation = 1.0` will cover 68% of the gaussian weight inside the 5px radius.
* `standard_deviation = 2.0` will cover 95% of the gaussian weight inside the 5px radius.
*/
float filter_gaussian_factor(float linear_distance, float standard_deviation)
{
/* Account for `filter_gaussian_factor` using `exp2` for speed (`exp(x) = exp2(x / log(2))`). */
const float log_2_inv = 1.442695041;
return log_2_inv * standard_deviation / square(linear_distance);
}
/**
* Gaussian distance weighting. Allow weighting based on distance without null weight whatever the
* distance. `factor` is supposed to be a scaling parameter given by `filter_gaussian_factor`.
*/
float filter_gaussian_weight(float factor, float square_distance)
{
/* Using exp2 since it is faster on GPU. `filter_gaussian_factor` account for that. */
return exp2(-factor * square_distance);
}
/**
* Planar distance weighting. Allow to weight based on geometric neighborhood.
*/
float filter_planar_weight(vec3 plane_N, vec3 plane_P, vec3 P, float scale)
{
vec4 plane_eq = vec4(plane_N, -dot(plane_N, plane_P));
float plane_distance = dot(plane_eq, vec4(P, 1.0));
return filter_gaussian_weight(scale, square(plane_distance));
}
/**
* Angle weighting. Mostly used for normals.
* Expects both normals to be normalized.
*/
float filter_angle_weight(vec3 center_N, vec3 sample_N)
{
float facing_ratio = dot(center_N, sample_N);
return saturate(pow8f(facing_ratio));
}

View File

@ -6,161 +6,103 @@
#pragma BLENDER_REQUIRE(eevee_sampling_lib.glsl)
#pragma BLENDER_REQUIRE(gpu_shader_utildefines_lib.glsl)
#pragma BLENDER_REQUIRE(gpu_shader_math_vector_lib.glsl)
#pragma BLENDER_REQUIRE(eevee_gbuffer_lib.glsl)
#pragma BLENDER_REQUIRE(eevee_lightprobe_eval_lib.glsl)
#pragma BLENDER_REQUIRE(eevee_closure_lib.glsl)
#pragma BLENDER_REQUIRE(eevee_spherical_harmonics_lib.glsl)
#pragma BLENDER_REQUIRE(eevee_filter_lib.glsl)
float bilateral_depth_weight(vec3 center_N, vec3 center_P, vec3 sample_P)
vec3 sample_normal_get(ivec2 texel, out bool is_processed)
{
vec4 center_plane_eq = vec4(center_N, -dot(center_N, center_P));
/* Only compare distance to the center plane formed by the normal. */
float depth_delta = dot(center_plane_eq, vec4(sample_P, 1.0));
/* TODO(fclem): Scene parameter. This is dependent on scene scale. */
const float scale = 10000.0;
float weight = exp2(-scale * square(depth_delta));
return weight;
vec4 normal = texelFetch(screen_normal_tx, texel, 0);
is_processed = (normal.w != 0.0);
return normal.xyz * 2.0 - 1.0;
}
float bilateral_spatial_weight(float sigma, vec2 offset_from_center)
float sample_weight_get(
vec3 center_N, vec3 center_P, ivec2 sample_texel, vec2 sample_uv, ivec2 sample_offset)
{
/* From https://github.com/tranvansang/bilateral-filter/blob/master/fshader.frag */
float fac = -1.0 / square(sigma);
/* Take two standard deviation. */
fac *= 2.0;
float weight = exp2(fac * length_squared(offset_from_center));
return weight;
ivec2 sample_texel_fullres = sample_texel * uniform_buf.raytrace.horizon_resolution_scale +
uniform_buf.raytrace.horizon_resolution_bias;
float sample_depth = texelFetch(hiz_tx, sample_texel_fullres, 0).r;
bool is_valid;
vec3 sample_N = sample_normal_get(sample_texel, is_valid);
vec3 sample_P = drw_point_screen_to_world(vec3(sample_uv, sample_depth));
if (!is_valid) {
return 0.0;
}
float gauss = filter_gaussian_factor(1.5, 1.5);
/* TODO(fclem): Scene parameter. 100.0 is dependent on scene scale. */
float depth_weight = filter_planar_weight(center_N, center_P, sample_P, 100.0);
float spatial_weight = filter_gaussian_weight(gauss, length_squared(vec2(sample_offset)));
float normal_weight = filter_angle_weight(center_N, sample_N);
return depth_weight * spatial_weight * normal_weight;
}
float bilateral_normal_weight(vec3 center_N, vec3 sample_N)
SphericalHarmonicL1 load_spherical_harmonic(ivec2 texel)
{
float facing_ratio = dot(center_N, sample_N);
float weight = saturate(pow8f(facing_ratio));
return weight;
}
/* In order to remove some more fireflies, "tone-map" the color samples during the accumulation. */
vec3 to_accumulation_space(vec3 color)
{
/* This 4 factor is to avoid killing too much energy. */
/* TODO(fclem): Parameter? */
color /= 4.0;
color = color / (1.0 + reduce_add(color));
return color;
}
vec3 from_accumulation_space(vec3 color)
{
color = color / (1.0 - reduce_add(color));
color *= 4.0;
return color;
}
vec3 load_normal(ivec2 texel)
{
return gbuffer_read(gbuf_header_tx, gbuf_closure_tx, gbuf_normal_tx, texel).surface_N;
SphericalHarmonicL1 sh;
sh.L0.M0 = texelFetch(in_sh_0_tx, texel, 0);
sh.L1.Mn1 = texelFetch(in_sh_1_tx, texel, 0);
sh.L1.M0 = texelFetch(in_sh_2_tx, texel, 0);
sh.L1.Mp1 = texelFetch(in_sh_3_tx, texel, 0);
sh = spherical_harmonics_decompress(sh);
return sh;
}
void main()
{
const uint tile_size = RAYTRACE_GROUP_SIZE;
uvec2 tile_coord = unpackUvec2x16(tiles_coord_buf[gl_WorkGroupID.x]);
ivec2 texel = ivec2(gl_LocalInvocationID.xy + tile_coord * tile_size);
ivec2 texel_fullres = ivec2(gl_LocalInvocationID.xy + tile_coord * tile_size);
ivec2 texel = (texel_fullres) / uniform_buf.raytrace.resolution_scale;
vec2 texel_size = 1.0 / vec2(textureSize(in_sh_0_tx, 0).xy);
ivec2 texel_fullres = texel * uniform_buf.raytrace.horizon_resolution_scale +
uniform_buf.raytrace.horizon_resolution_bias;
ivec2 extent = textureSize(gbuf_header_tx, 0).xy;
if (any(greaterThanEqual(texel_fullres, extent))) {
return;
}
vec2 center_uv = (vec2(texel_fullres) + 0.5) * uniform_buf.raytrace.full_resolution_inv;
float center_depth = texelFetch(depth_tx, texel_fullres, 0).r;
bool is_valid;
float center_depth = texelFetch(hiz_tx, texel_fullres, 0).r;
vec2 center_uv = vec2(texel) * texel_size;
vec3 center_P = drw_point_screen_to_world(vec3(center_uv, center_depth));
vec3 center_N = sample_normal_get(texel, is_valid);
if (center_depth == 1.0) {
/* Do not trace for background */
if (!is_valid) {
#if 0 /* This is not needed as the next stage doesn't do bilinear filtering. */
imageStore(out_sh_0_img, texel, vec4(0.0));
imageStore(out_sh_1_img, texel, vec4(0.0));
imageStore(out_sh_2_img, texel, vec4(0.0));
imageStore(out_sh_3_img, texel, vec4(0.0));
#endif
return;
}
ClosureUndetermined closure_center = gbuffer_read_bin(
gbuf_header_tx, gbuf_closure_tx, gbuf_normal_tx, texel_fullres, closure_index);
if (closure_center.type == CLOSURE_NONE_ID) {
return;
}
vec3 center_N = closure_center.N;
float roughness = closure_apparent_roughness_get(closure_center);
float mix_fac = saturate(roughness * uniform_buf.raytrace.roughness_mask_scale -
uniform_buf.raytrace.roughness_mask_bias);
bool use_raytrace = mix_fac < 1.0;
bool use_horizon = mix_fac > 0.0;
if (use_horizon == false) {
return;
}
vec3 accum_radiance = vec3(0.0);
float accum_occlusion = 0.0;
SphericalHarmonicL1 accum_sh = spherical_harmonics_L1_new();
float accum_weight = 0.0;
for (int x = -1; x <= 1; x++) {
for (int y = -1; y <= 1; y++) {
ivec2 offset = ivec2(x, y);
ivec2 sample_texel = texel + ivec2(x, y);
ivec2 sample_texel_fullres = sample_texel * uniform_buf.raytrace.resolution_scale +
uniform_buf.raytrace.resolution_bias;
ivec3 sample_tile = ivec3(sample_texel_fullres / RAYTRACE_GROUP_SIZE, closure_index);
/* Make sure the sample has been processed and do not contain garbage data. */
if (imageLoad(tile_mask_img, sample_tile).r == 0u) {
continue;
/* 3x3 filter. */
for (int y = -1; y <= 1; y++) {
for (int x = -1; x <= 1; x++) {
ivec2 sample_offset = ivec2(x, y);
ivec2 sample_texel = texel + sample_offset;
vec2 sample_uv = (vec2(sample_texel) + 0.5) * texel_size;
float sample_weight = sample_weight_get(
center_N, center_P, sample_texel, sample_uv, sample_offset);
/* We need to avoid sampling if there no weight as the texture values could be undefined
* (is_valid is false). */
if (sample_weight > 0.0) {
SphericalHarmonicL1 sample_sh = load_spherical_harmonic(sample_texel);
accum_sh = spherical_harmonics_madd(sample_sh, sample_weight, accum_sh);
accum_weight += sample_weight;
}
float sample_depth = texelFetch(depth_tx, sample_texel_fullres, 0).r;
vec2 sample_uv = (vec2(sample_texel_fullres) + 0.5) *
uniform_buf.raytrace.full_resolution_inv;
vec3 sample_P = drw_point_screen_to_world(vec3(sample_uv, sample_depth));
/* Background case. */
if (sample_depth == 0.0) {
continue;
}
vec3 sample_N = load_normal(sample_texel_fullres);
float depth_weight = bilateral_depth_weight(center_N, center_P, sample_P);
float spatial_weight = bilateral_spatial_weight(1.5, vec2(offset));
float normal_weight = bilateral_normal_weight(center_N, sample_N);
float weight = depth_weight * spatial_weight * normal_weight;
vec3 radiance = imageLoad(horizon_radiance_img, sample_texel).rgb;
/* Do not gather unprocessed pixels. */
if (all(equal(radiance, FLT_11_11_10_MAX))) {
continue;
}
float occlusion = imageLoad(horizon_occlusion_img, sample_texel).r;
accum_radiance += to_accumulation_space(radiance) * weight;
accum_occlusion += occlusion * weight;
accum_weight += weight;
}
}
float occlusion = accum_occlusion * safe_rcp(accum_weight);
vec3 radiance = from_accumulation_space(accum_radiance * safe_rcp(accum_weight));
accum_sh = spherical_harmonics_mul(accum_sh, safe_rcp(accum_weight));
vec3 P = center_P;
vec3 N = center_N;
vec3 Ng = center_N;
vec3 V = drw_world_incident_vector(P);
/* Fallback to nearest light-probe. */
LightProbeSample samp = lightprobe_load(P, Ng, V);
vec3 radiance_probe = spherical_harmonics_evaluate_lambert(N, samp.volume_irradiance);
/* Apply missing distant lighting. */
radiance += occlusion * radiance_probe;
accum_sh = spherical_harmonics_compress(accum_sh);
vec4 radiance_horizon = vec4(radiance, 0.0);
vec4 radiance_raytrace = use_raytrace ? imageLoad(radiance_img, texel_fullres) : vec4(0.0);
vec4 radiance_mixed = mix(radiance_raytrace, radiance_horizon, mix_fac);
imageStore(radiance_img, texel_fullres, radiance_mixed);
imageStore(out_sh_0_img, texel, accum_sh.L0.M0);
imageStore(out_sh_1_img, texel, accum_sh.L1.Mn1);
imageStore(out_sh_2_img, texel, accum_sh.L1.M0);
imageStore(out_sh_3_img, texel, accum_sh.L1.Mp1);
}

View File

@ -0,0 +1,205 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma BLENDER_REQUIRE(draw_view_lib.glsl)
#pragma BLENDER_REQUIRE(eevee_sampling_lib.glsl)
#pragma BLENDER_REQUIRE(gpu_shader_utildefines_lib.glsl)
#pragma BLENDER_REQUIRE(gpu_shader_math_vector_lib.glsl)
#pragma BLENDER_REQUIRE(eevee_gbuffer_lib.glsl)
#pragma BLENDER_REQUIRE(eevee_lightprobe_eval_lib.glsl)
#pragma BLENDER_REQUIRE(eevee_closure_lib.glsl)
#pragma BLENDER_REQUIRE(eevee_filter_lib.glsl)
vec3 sample_normal_get(ivec2 texel, out bool is_processed)
{
vec4 normal = texelFetch(screen_normal_tx, texel, 0);
is_processed = (normal.w != 0.0);
return drw_normal_view_to_world(normal.xyz * 2.0 - 1.0);
}
float sample_weight_get(vec3 center_N, vec3 center_P, ivec2 center_texel, ivec2 sample_offset)
{
ivec2 sample_texel = center_texel + sample_offset;
ivec2 sample_texel_fullres = sample_texel * uniform_buf.raytrace.horizon_resolution_scale +
uniform_buf.raytrace.horizon_resolution_bias;
vec2 sample_uv = (vec2(sample_texel_fullres) + 0.5) * uniform_buf.raytrace.full_resolution_inv;
float sample_depth = texelFetch(depth_tx, sample_texel_fullres, 0).r;
bool is_valid;
vec3 sample_N = sample_normal_get(sample_texel, is_valid);
vec3 sample_P = drw_point_screen_to_world(vec3(sample_uv, sample_depth));
if (!is_valid) {
return 0.0;
}
/* TODO(fclem): Scene parameter. 10000.0 is dependent on scene scale. */
float depth_weight = filter_planar_weight(center_N, center_P, sample_P, 10000.0);
float normal_weight = filter_angle_weight(center_N, sample_N);
return depth_weight * normal_weight;
}
SphericalHarmonicL1 load_spherical_harmonic(ivec2 texel, bool valid)
{
if (!valid) {
/* We need to avoid sampling if there no weight as the texture values could be undefined
* (is_valid is false). */
return spherical_harmonics_L1_new();
}
SphericalHarmonicL1 sh;
sh.L0.M0 = texelFetch(horizon_radiance_0_tx, texel, 0);
sh.L1.Mn1 = texelFetch(horizon_radiance_1_tx, texel, 0);
sh.L1.M0 = texelFetch(horizon_radiance_2_tx, texel, 0);
sh.L1.Mp1 = texelFetch(horizon_radiance_3_tx, texel, 0);
return spherical_harmonics_decompress(sh);
}
void main()
{
const uint tile_size = RAYTRACE_GROUP_SIZE;
uvec2 tile_coord = unpackUvec2x16(tiles_coord_buf[gl_WorkGroupID.x]);
ivec2 texel_fullres = ivec2(gl_LocalInvocationID.xy + tile_coord * tile_size);
ivec2 texel = max(ivec2(0), texel_fullres - uniform_buf.raytrace.horizon_resolution_bias) /
uniform_buf.raytrace.horizon_resolution_scale;
ivec2 extent = textureSize(gbuf_header_tx, 0).xy;
if (any(greaterThanEqual(texel_fullres, extent))) {
return;
}
GBufferReader gbuf = gbuffer_read(
gbuf_header_tx, gbuf_closure_tx, gbuf_normal_tx, texel_fullres);
if (gbuf.header == 0u) {
return;
}
vec2 center_uv = (vec2(texel_fullres) + 0.5) * uniform_buf.raytrace.full_resolution_inv;
float center_depth = texelFetch(depth_tx, texel_fullres, 0).r;
vec3 center_P = drw_point_screen_to_world(vec3(center_uv, center_depth));
vec3 center_N = gbuf.surface_N;
SphericalHarmonicL1 accum_sh;
if (uniform_buf.raytrace.horizon_resolution_scale == 1) {
accum_sh = load_spherical_harmonic(texel, true);
}
else {
vec2 interp = vec2(texel_fullres - texel * uniform_buf.raytrace.horizon_resolution_scale -
uniform_buf.raytrace.horizon_resolution_bias) /
vec2(uniform_buf.raytrace.horizon_resolution_scale);
vec4 interp4 = vec4(interp, 1.0 - interp);
vec4 bilinear_weight = interp4.zxzx * interp4.wwyy;
vec4 bilateral_weights;
bilateral_weights.x = sample_weight_get(center_N, center_P, texel, ivec2(0, 0));
bilateral_weights.y = sample_weight_get(center_N, center_P, texel, ivec2(1, 0));
bilateral_weights.z = sample_weight_get(center_N, center_P, texel, ivec2(0, 1));
bilateral_weights.w = sample_weight_get(center_N, center_P, texel, ivec2(1, 1));
vec4 weights = bilateral_weights * bilinear_weight;
SphericalHarmonicL1 sh_00 = load_spherical_harmonic(texel + ivec2(0, 0), weights.x > 0.0);
SphericalHarmonicL1 sh_10 = load_spherical_harmonic(texel + ivec2(1, 0), weights.y > 0.0);
SphericalHarmonicL1 sh_01 = load_spherical_harmonic(texel + ivec2(0, 1), weights.z > 0.0);
SphericalHarmonicL1 sh_11 = load_spherical_harmonic(texel + ivec2(1, 1), weights.w > 0.0);
/* Avoid another division at the end. Normalize the weights upfront. */
weights *= safe_rcp(reduce_add(weights));
accum_sh = spherical_harmonics_mul(sh_00, weights.x);
accum_sh = spherical_harmonics_madd(sh_10, weights.y, accum_sh);
accum_sh = spherical_harmonics_madd(sh_01, weights.z, accum_sh);
accum_sh = spherical_harmonics_madd(sh_11, weights.w, accum_sh);
}
vec3 P = center_P;
vec3 Ng = center_N;
vec3 V = drw_world_incident_vector(P);
LightProbeSample samp = lightprobe_load(P, Ng, V);
for (int i = 0; i < GBUFFER_LAYER_MAX && i < gbuf.closure_count; i++) {
ClosureUndetermined cl = gbuffer_closure_get(gbuf, i);
float roughness = closure_apparent_roughness_get(cl);
float mix_fac = saturate(roughness * uniform_buf.raytrace.roughness_mask_scale -
uniform_buf.raytrace.roughness_mask_bias);
bool use_raytrace = mix_fac < 1.0;
bool use_horizon = mix_fac > 0.0;
if (!use_horizon) {
continue;
}
vec3 N = cl.N;
vec3 L;
switch (cl.type) {
case CLOSURE_BSDF_MICROFACET_GGX_REFLECTION_ID:
L = lightprobe_reflection_dominant_dir(cl.N, V, roughness);
break;
case CLOSURE_BSDF_MICROFACET_GGX_REFRACTION_ID:
L = lightprobe_refraction_dominant_dir(cl.N, V, to_closure_refraction(cl).ior, roughness);
break;
case CLOSURE_BSDF_TRANSLUCENT_ID:
L = -N;
break;
default:
L = N;
break;
}
vec3 vL = drw_normal_world_to_view(L);
/* Evaluate lighting from horizon scan. */
/* TODO(fclem): Evaluate depending on BSDF. */
vec3 radiance = spherical_harmonics_evaluate_lambert(vL, accum_sh);
/* Evaluate visibility from horizon scan. */
SphericalHarmonicL1 sh_visibility = spherical_harmonics_swizzle_wwww(accum_sh);
float occlusion = spherical_harmonics_evaluate_lambert(vL, sh_visibility).x;
/* FIXME(fclem): Tried to match the old occlusion look. I don't know why it's needed. */
occlusion *= 0.5;
/* TODO(fclem): Ideally, we should just combine both local and distant irradiance and evaluate
* once. Unfortunately, I couldn't find a way to do the same (1.0 - occlusion) with the
* spherical harmonic coefficients. */
float visibility = saturate(1.0 - occlusion);
/* Apply missing distant lighting. */
vec3 radiance_probe = spherical_harmonics_evaluate_lambert(L, samp.volume_irradiance);
radiance += visibility * radiance_probe;
int layer_index = gbuffer_closure_get_bin_index(gbuf, i);
vec4 radiance_horizon = vec4(radiance, 0.0);
vec4 radiance_raytrace = vec4(0.0);
if (use_raytrace) {
/* TODO(fclem): Layered texture. */
if (layer_index == 0) {
radiance_raytrace = imageLoad(closure0_img, texel_fullres);
}
else if (layer_index == 1) {
radiance_raytrace = imageLoad(closure1_img, texel_fullres);
}
else if (layer_index == 2) {
radiance_raytrace = imageLoad(closure2_img, texel_fullres);
}
}
vec4 radiance_mixed = mix(radiance_raytrace, radiance_horizon, mix_fac);
/* TODO(fclem): Layered texture. */
if (layer_index == 0) {
imageStore(closure0_img, texel_fullres, radiance_mixed);
}
else if (layer_index == 1) {
imageStore(closure1_img, texel_fullres, radiance_mixed);
}
else if (layer_index == 2) {
imageStore(closure2_img, texel_fullres, radiance_mixed);
}
}
}

View File

@ -6,7 +6,6 @@
#pragma BLENDER_REQUIRE(eevee_sampling_lib.glsl)
#pragma BLENDER_REQUIRE(gpu_shader_utildefines_lib.glsl)
#pragma BLENDER_REQUIRE(gpu_shader_math_vector_lib.glsl)
#pragma BLENDER_REQUIRE(eevee_gbuffer_lib.glsl)
#pragma BLENDER_REQUIRE(eevee_horizon_scan_eval_lib.glsl)
void main()
@ -15,48 +14,52 @@ void main()
uvec2 tile_coord = unpackUvec2x16(tiles_coord_buf[gl_WorkGroupID.x]);
ivec2 texel = ivec2(gl_LocalInvocationID.xy + tile_coord * tile_size);
ivec2 texel_fullres = texel * uniform_buf.raytrace.resolution_scale +
uniform_buf.raytrace.resolution_bias;
ivec2 texel_fullres = texel * uniform_buf.raytrace.horizon_resolution_scale +
uniform_buf.raytrace.horizon_resolution_bias;
/* Avoid tracing the outside border if dispatch is too big. */
ivec2 extent = textureSize(gbuf_header_tx, 0).xy;
if (any(greaterThanEqual(texel_fullres, extent))) {
if (any(greaterThanEqual(texel * uniform_buf.raytrace.horizon_resolution_scale, extent))) {
return;
}
/* Avoid loading texels outside texture range.
* This can happen even after the check above in non-power-of-2 textures. */
texel_fullres = min(texel_fullres, extent - 1);
/* Do not trace where nothing was rendered. */
if (texelFetch(gbuf_header_tx, texel_fullres, 0).r == 0u) {
#if 0 /* This is not needed as the next stage doesn't do bilinear filtering. */
imageStore(horizon_radiance_0_img, texel, vec4(0.0));
imageStore(horizon_radiance_1_img, texel, vec4(0.0));
imageStore(horizon_radiance_2_img, texel, vec4(0.0));
imageStore(horizon_radiance_3_img, texel, vec4(0.0));
#endif
return;
}
vec2 uv = (vec2(texel_fullres) + 0.5) * uniform_buf.raytrace.full_resolution_inv;
float depth = texelFetch(hiz_tx, texel_fullres, 0).r;
if (depth == 1.0) {
/* Do not trace for background */
imageStore(horizon_radiance_img, texel, vec4(FLT_11_11_10_MAX, 0.0));
return;
}
HorizonScanContext ctx;
ctx.closure = gbuffer_read_bin(
gbuf_header_tx, gbuf_closure_tx, gbuf_normal_tx, texel_fullres, closure_index);
ctx.closure.N = drw_normal_world_to_view(ctx.closure.N);
if (ctx.closure.type == CLOSURE_NONE_ID) {
imageStore(horizon_radiance_img, texel, vec4(FLT_11_11_10_MAX, 0.0));
return;
}
vec3 vP = drw_point_screen_to_view(vec3(uv, depth));
vec3 vN = horizon_scan_sample_normal(uv);
vec2 noise = utility_tx_fetch(utility_tx, vec2(texel), UTIL_BLUE_NOISE_LAYER).rg;
noise = fract(noise + sampling_rng_2D_get(SAMPLING_AO_U));
horizon_scan_eval(vP,
ctx,
noise,
uniform_buf.ao.pixel_size,
1.0e16,
uniform_buf.ao.thickness,
uniform_buf.ao.angle_bias,
8,
false);
HorizonScanResult scan = horizon_scan_eval(vP,
vN,
noise,
uniform_buf.ao.pixel_size,
1.0e16,
uniform_buf.ao.thickness,
uniform_buf.ao.angle_bias,
8,
false);
imageStore(horizon_radiance_img, texel, ctx.closure_result);
imageStore(horizon_occlusion_img, texel, ctx.closure_result.wwww);
scan.result = spherical_harmonics_compress(scan.result);
imageStore(horizon_radiance_0_img, texel, scan.result.L0.M0);
imageStore(horizon_radiance_1_img, texel, scan.result.L1.Mn1);
imageStore(horizon_radiance_2_img, texel, scan.result.L1.M0);
imageStore(horizon_radiance_3_img, texel, scan.result.L1.Mp1);
}

View File

@ -22,7 +22,9 @@
#pragma BLENDER_REQUIRE(eevee_bxdf_lib.glsl)
#pragma BLENDER_REQUIRE(eevee_spherical_harmonics_lib.glsl)
#if defined(MAT_DEFERRED) || defined(MAT_FORWARD)
#ifdef HORIZON_OCCLUSION
/* Do nothing. */
#elif defined(MAT_DEFERRED) || defined(MAT_FORWARD)
/* Enable AO node computation for material shaders. */
# define HORIZON_OCCLUSION
#else
@ -47,138 +49,6 @@ vec3 horizon_scan_sample_normal(vec2 uv)
#endif
}
/* Note: Expects all normals to be in view-space. */
struct HorizonScanContextCommon {
float N_angle;
float N_length;
uint bitmask;
float weight_slice;
float weight_accum;
vec3 light_slice;
vec4 light_accum;
};
struct HorizonScanContext {
#ifdef HORIZON_OCCLUSION
ClosureOcclusion occlusion;
HorizonScanContextCommon occlusion_common;
vec4 occlusion_result;
#endif
#ifdef HORIZON_CLOSURE
ClosureUndetermined closure;
HorizonScanContextCommon closure_common;
vec4 closure_result;
#endif
};
void horizon_scan_context_accumulation_reset(inout HorizonScanContext context)
{
#ifdef HORIZON_OCCLUSION
context.occlusion_common.light_accum = vec4(0.0);
context.occlusion_common.weight_accum = 0.0;
#endif
#ifdef HORIZON_CLOSURE
context.closure_common.light_accum = vec4(0.0);
context.closure_common.weight_accum = 0.0;
#endif
}
void horizon_scan_context_slice_start(
inout HorizonScanContextCommon context, vec3 vN, vec3 vV, vec3 vT, vec3 vB)
{
context.bitmask = 0u;
context.weight_slice = 0.0;
context.light_slice = vec3(0.0);
horizon_scan_projected_normal_to_plane_angle_and_length(
vN, vV, vT, vB, context.N_length, context.N_angle);
}
void horizon_scan_context_slice_start(inout HorizonScanContext context, vec3 vV, vec3 vT, vec3 vB)
{
#ifdef HORIZON_OCCLUSION
horizon_scan_context_slice_start(context.occlusion_common, context.occlusion.N, vV, vT, vB);
#endif
#ifdef HORIZON_CLOSURE
horizon_scan_context_slice_start(context.closure_common, context.closure.N, vV, vT, vB);
#endif
}
void horizon_scan_context_sample_finish(inout HorizonScanContextCommon context,
vec3 sample_radiance,
float sample_weight,
vec2 sample_theta,
float angle_bias)
{
/* Angular bias shrinks the visibility bitmask around the projected normal. */
sample_theta = (sample_theta - context.N_angle) * angle_bias;
uint sample_bitmask = horizon_scan_angles_to_bitmask(sample_theta);
sample_weight *= horizon_scan_bitmask_to_visibility_uniform(sample_bitmask & ~context.bitmask);
context.weight_slice += sample_weight;
context.light_slice += sample_radiance * sample_weight;
context.bitmask |= sample_bitmask;
}
void horizon_scan_context_sample_finish(
inout HorizonScanContext ctx, vec3 L, vec3 V, vec2 sample_uv, vec2 theta, float bias)
{
vec3 sample_radiance = horizon_scan_sample_radiance(sample_uv);
/* Take emitter surface normal into consideration. */
vec3 sample_normal = horizon_scan_sample_normal(sample_uv);
/* Discard back-facing samples.
* The paper suggests a smooth test which is not physically correct since we
* already consider the sample reflected radiance.
* Set the weight to allow energy conservation. If we modulate the radiance, we loose energy. */
float weight = step(dot(sample_normal, L), 0.0);
#ifdef HORIZON_OCCLUSION
horizon_scan_context_sample_finish(ctx.occlusion_common, sample_radiance, 1.0, theta, bias);
#endif
#ifdef HORIZON_CLOSURE
weight *= bsdf_lambert(ctx.closure.N, L);
horizon_scan_context_sample_finish(ctx.closure_common, sample_radiance, weight, theta, bias);
#endif
}
void horizon_scan_context_slice_finish(inout HorizonScanContextCommon context)
{
/* Use uniform visibility since this is what we use for near field lighting.
* Also the lighting we are going to mask is already containing the cosine lobe. */
float slice_occlusion = horizon_scan_bitmask_to_visibility_uniform(~context.bitmask);
/* Normalize radiance since BxDF is applied when merging direct and indirect light. */
context.light_slice *= safe_rcp(context.weight_slice) * (1.0 - slice_occlusion);
/* Correct normal not on plane (Eq. 8 of GTAO paper). */
context.light_accum += vec4(context.light_slice, slice_occlusion) * context.N_length;
context.weight_accum += context.N_length;
}
void horizon_scan_context_slice_finish(inout HorizonScanContext context)
{
#ifdef HORIZON_OCCLUSION
float occlusion = horizon_scan_bitmask_to_occlusion_cosine(context.occlusion_common.bitmask);
context.occlusion_common.light_accum += vec4(occlusion) * context.occlusion_common.N_length;
context.occlusion_common.weight_accum += context.occlusion_common.N_length;
#endif
#ifdef HORIZON_CLOSURE
horizon_scan_context_slice_finish(context.closure_common);
#endif
}
void horizon_scan_context_accumulation_finish(HorizonScanContextCommon context, out vec4 result)
{
result = context.light_accum * safe_rcp(context.weight_accum);
}
void horizon_scan_context_accumulation_finish(inout HorizonScanContext context)
{
#ifdef HORIZON_OCCLUSION
horizon_scan_context_accumulation_finish(context.occlusion_common, context.occlusion_result);
#endif
#ifdef HORIZON_CLOSURE
horizon_scan_context_accumulation_finish(context.closure_common, context.closure_result);
#endif
}
/**
* Returns the start and end point of a ray clipped to its intersection
* with a sphere.
@ -209,27 +79,38 @@ void horizon_scan_occluder_intersection_ray_sphere_clip(Ray ray,
P_exit = ray.origin + ray.direction * t_exit;
}
struct HorizonScanResult {
#ifdef HORIZON_OCCLUSION
float result;
#endif
#ifdef HORIZON_CLOSURE
SphericalHarmonicL1 result;
#endif
};
/**
* Scans the horizon in many directions and returns the indirect lighting radiance.
* Returned lighting is stored inside the context in `_accum` members already normalized.
* If `reversed` is set to true, the input normal must be negated.
*/
void horizon_scan_eval(vec3 vP,
inout HorizonScanContext context,
vec2 noise,
vec2 pixel_size,
float search_distance,
float global_thickness,
float angle_bias,
const int sample_count,
const bool reversed)
HorizonScanResult horizon_scan_eval(vec3 vP,
vec3 vN,
vec2 noise,
vec2 pixel_size,
float search_distance,
float global_thickness,
float angle_bias,
const int sample_count,
const bool reversed)
{
vec3 vV = drw_view_incident_vector(vP);
const int slice_len = 2;
vec2 v_dir = sample_circle(noise.x * (0.5 / float(slice_len)));
horizon_scan_context_accumulation_reset(context);
float weight_accum = 0.0;
float occlusion_accum = 0.0;
SphericalHarmonicL1 sh_accum = spherical_harmonics_L1_new();
for (int slice = 0; slice < slice_len; slice++) {
#if 0 /* For debug purpose. For when slice_len is greater than 2. */
@ -240,7 +121,18 @@ void horizon_scan_eval(vec3 vP,
vec3 vB = normalize(cross(vV, vec3(v_dir, 0.0)));
vec3 vT = cross(vB, vV);
horizon_scan_context_slice_start(context, vV, vT, vB);
/* Bitmask representing the occluded sectors on the slice. */
uint slice_bitmask = 0u;
/* Angle between vN and the horizon slice plane. */
float vN_angle;
/* Length of vN projected onto the horizon slice plane. */
float vN_length;
horizon_scan_projected_normal_to_plane_angle_and_length(vN, vV, vT, vB, vN_length, vN_angle);
SphericalHarmonicL1 sh_slice = spherical_harmonics_L1_new();
float weight_slice;
/* For both sides of the view vector. */
for (int side = 0; side < 2; side++) {
@ -306,15 +198,54 @@ void horizon_scan_eval(vec3 vP,
/* If we are tracing backward, the angles are negative. Swizzle to keep correct order. */
theta = (side == 0) ? theta.xy : -theta.yx;
horizon_scan_context_sample_finish(context, vL_front, vV, sample_uv, theta, angle_bias);
vec3 sample_radiance = horizon_scan_sample_radiance(sample_uv);
/* Take emitter surface normal into consideration. */
vec3 sample_normal = horizon_scan_sample_normal(sample_uv);
/* Discard back-facing samples.
* The 2 factor is to avoid loosing too much energy (which is something not
* explained in the paper...). Likely to be wrong, but we need a soft falloff. */
float facing_weight = saturate(-dot(sample_normal, vL_front) * 2.0);
float sample_weight = facing_weight * bsdf_lambert(vN, vL_front);
/* Angular bias shrinks the visibility bitmask around the projected normal. */
vec2 biased_theta = (theta - vN_angle) * angle_bias;
uint sample_bitmask = horizon_scan_angles_to_bitmask(biased_theta);
float weight_bitmask = horizon_scan_bitmask_to_visibility_uniform(sample_bitmask &
~slice_bitmask);
sample_radiance *= facing_weight * weight_bitmask;
/* Encoding using front sample direction gives better result than
* `normalize(vL_front + vL_back)` */
spherical_harmonics_encode_signal_sample(
vL_front, vec4(sample_radiance, weight_bitmask), sh_slice);
slice_bitmask |= sample_bitmask;
}
}
horizon_scan_context_slice_finish(context);
float occlusion_slice = horizon_scan_bitmask_to_occlusion_cosine(slice_bitmask);
/* Correct normal not on plane (Eq. 8 of GTAO paper). */
occlusion_accum += occlusion_slice * vN_length;
/* Use uniform visibility since this is what we use for near field lighting. */
sh_accum = spherical_harmonics_madd(sh_slice, vN_length, sh_accum);
weight_accum += vN_length;
/* Rotate 90 degrees. */
v_dir = orthogonal(v_dir);
}
horizon_scan_context_accumulation_finish(context);
float weight_rcp = safe_rcp(weight_accum);
HorizonScanResult res;
#ifdef HORIZON_OCCLUSION
res.result = occlusion_accum * weight_rcp;
#endif
#ifdef HORIZON_CLOSURE
/* Weight by area of the sphere. This is expected for correct SH evaluation. */
res.result = spherical_harmonics_mul(sh_accum, weight_rcp * 4.0 * M_PI);
#endif
return res;
}

View File

@ -16,8 +16,17 @@
void main()
{
ivec2 texel = ivec2(gl_GlobalInvocationID.xy);
ivec2 texel_fullres = texel * uniform_buf.raytrace.resolution_scale +
uniform_buf.raytrace.resolution_bias;
ivec2 texel_fullres = texel * uniform_buf.raytrace.horizon_resolution_scale +
uniform_buf.raytrace.horizon_resolution_bias;
/* Return early for padding threads so we can use imageStoreFast. */
if (any(greaterThanEqual(texel, imageSize(out_radiance_img).xy))) {
return;
}
/* Avoid loading texels outside texture range. */
ivec2 extent = textureSize(gbuf_header_tx, 0).xy;
texel_fullres = min(texel_fullres, extent - 1);
/* Load Gbuffer. */
GBufferReader gbuf = gbuffer_read(
@ -25,12 +34,16 @@ void main()
/* Export normal. */
vec3 N = gbuf.surface_N;
/* Background has invalid data. */
/* FIXME: This is zero for opaque layer when we are processing the refraction layer. */
if (is_zero(N)) {
/* Avoid NaN. But should be fixed in any case. */
N = vec3(1.0, 0.0, 0.0);
}
vec3 vN = drw_normal_world_to_view(N);
imageStore(out_normal_img, texel, vec4(vN * 0.5 + 0.5, 0.0));
/* Tag processed pixel in the normal buffer for denoising speed. */
bool is_processed = gbuf.header != 0u;
imageStore(out_normal_img, texel, vec4(vN * 0.5 + 0.5, float(is_processed)));
/* Re-project radiance. */
vec2 uv = (vec2(texel_fullres) + 0.5) / vec2(textureSize(depth_tx, 0).xy);

View File

@ -0,0 +1,30 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/**
* Load the extracted spherical harmonics from the world into the probe volume atlas.
*
* The whole thread group will load the same data and write a brick worth of data.
*/
void atlas_store(vec4 sh_coefficient, ivec2 atlas_coord, int layer)
{
imageStore(irradiance_atlas_img,
ivec3(atlas_coord, layer * IRRADIANCE_GRID_BRICK_SIZE) + ivec3(gl_LocalInvocationID),
sh_coefficient);
}
void main()
{
int brick_index = grids_infos_buf[grid_index].brick_offset;
/* Brick coordinate in the destination atlas. */
IrradianceBrick brick = irradiance_brick_unpack(bricks_infos_buf[brick_index]);
ivec2 output_coord = ivec2(brick.atlas_coord);
atlas_store(harmonic_buf.L0_M0, output_coord, 0);
atlas_store(harmonic_buf.L1_Mn1, output_coord, 1);
atlas_store(harmonic_buf.L1_M0, output_coord, 2);
atlas_store(harmonic_buf.L1_Mp1, output_coord, 3);
}

View File

@ -22,34 +22,7 @@
#pragma BLENDER_REQUIRE(eevee_sampling_lib.glsl)
#pragma BLENDER_REQUIRE(eevee_gbuffer_lib.glsl)
#pragma BLENDER_REQUIRE(eevee_closure_lib.glsl)
float bilateral_depth_weight(vec3 center_N, vec3 center_P, vec3 sample_P)
{
vec4 center_plane_eq = vec4(center_N, -dot(center_N, center_P));
/* Only compare distance to the center plane formed by the normal. */
float depth_delta = dot(center_plane_eq, vec4(sample_P, 1.0));
/* TODO(fclem): Scene parameter. This is dependent on scene scale. */
const float scale = 10000.0;
float weight = exp2(-scale * square(depth_delta));
return weight;
}
float bilateral_spatial_weight(float sigma, vec2 offset_from_center)
{
/* From https://github.com/tranvansang/bilateral-filter/blob/master/fshader.frag */
float fac = -1.0 / square(sigma);
/* Take two standard deviation. */
fac *= 2.0;
float weight = exp2(fac * length_squared(offset_from_center));
return weight;
}
float bilateral_normal_weight(vec3 center_N, vec3 sample_N)
{
float facing_ratio = dot(center_N, sample_N);
float weight = saturate(pow8f(facing_ratio));
return weight;
}
#pragma BLENDER_REQUIRE(eevee_filter_lib.glsl)
/* In order to remove some more fireflies, "tone-map" the color samples during the accumulation. */
vec3 to_accumulation_space(vec3 color)
@ -141,9 +114,12 @@ void main()
continue;
}
float depth_weight = bilateral_depth_weight(center_closure.N, center_P, sample_P);
float spatial_weight = bilateral_spatial_weight(filter_size, vec2(offset));
float normal_weight = bilateral_normal_weight(center_closure.N, sample_closure.N);
float gauss = filter_gaussian_factor(filter_size, 1.5);
/* TODO(fclem): Scene parameter. 10000.0 is dependent on scene scale. */
float depth_weight = filter_planar_weight(center_closure.N, center_P, sample_P, 10000.0);
float spatial_weight = filter_gaussian_weight(gauss, length_squared(vec2(offset)));
float normal_weight = filter_angle_weight(center_closure.N, sample_closure.N);
float weight = depth_weight * spatial_weight * normal_weight;
accum_radiance += to_accumulation_space(radiance) * weight;

View File

@ -14,7 +14,7 @@
#pragma BLENDER_REQUIRE(eevee_closure_lib.glsl)
shared uint tile_contains_ray_tracing[GBUFFER_LAYER_MAX];
shared uint tile_contains_horizon_scan[GBUFFER_LAYER_MAX];
shared uint tile_contains_horizon_scan;
/* Returns a blend factor between different tracing method. */
float ray_roughness_factor(RayTraceData raytrace, float roughness)
@ -26,9 +26,9 @@ void main()
{
if (gl_LocalInvocationIndex == 0u) {
/* Init shared variables. */
tile_contains_horizon_scan = 0;
for (int i = 0; i < GBUFFER_LAYER_MAX; i++) {
tile_contains_ray_tracing[i] = 0;
tile_contains_horizon_scan[i] = 0;
}
}
@ -51,7 +51,7 @@ void main()
/* We don't care about race condition here. */
if (ray_roughness_fac > 0.0) {
tile_contains_horizon_scan[i] = 1;
tile_contains_horizon_scan = 1;
}
if (ray_roughness_fac < 1.0) {
tile_contains_ray_tracing[i] = 1;
@ -70,11 +70,12 @@ void main()
imageStore(tile_raytrace_denoise_img, ivec3(denoise_tile_co, i), uvec4(1));
imageStore(tile_raytrace_tracing_img, ivec3(tracing_tile_co, i), uvec4(1));
}
}
if (tile_contains_horizon_scan[i] > 0) {
imageStore(tile_horizon_denoise_img, ivec3(denoise_tile_co, i), uvec4(1));
imageStore(tile_horizon_tracing_img, ivec3(tracing_tile_co, i), uvec4(1));
}
if (tile_contains_horizon_scan > 0) {
ivec2 tracing_tile_co = denoise_tile_co / uniform_buf.raytrace.horizon_resolution_scale;
imageStore(tile_horizon_denoise_img, ivec3(denoise_tile_co, 0), uvec4(1));
imageStore(tile_horizon_tracing_img, ivec3(tracing_tile_co, 0), uvec4(1));
}
}
}

View File

@ -19,13 +19,9 @@ void main()
if (all(equal(tile, ivec2(0)))) {
raytrace_tracing_dispatch_buf.num_groups_y = 1;
raytrace_denoise_dispatch_buf.num_groups_y = 1;
horizon_tracing_dispatch_buf.num_groups_y = 1;
horizon_denoise_dispatch_buf.num_groups_y = 1;
raytrace_tracing_dispatch_buf.num_groups_z = 1;
raytrace_denoise_dispatch_buf.num_groups_z = 1;
horizon_tracing_dispatch_buf.num_groups_z = 1;
horizon_denoise_dispatch_buf.num_groups_z = 1;
}
if (!in_image_range(tile, tile_raytrace_tracing_img)) {
@ -34,8 +30,6 @@ void main()
/* True if this tile is shooting and tracing rays. */
bool is_ray_tracing = imageLoad(tile_raytrace_tracing_img, ivec3(tile, closure_index)).r != 0;
/* True if this tile is using horizon scan. */
bool is_horizon_tracing = imageLoad(tile_horizon_tracing_img, ivec3(tile, closure_index)).r != 0;
/* True if an adjacent tile is ray tracing and will need this tile data for denoising. */
bool tile_is_ray_sampled = false;
@ -54,23 +48,6 @@ void main()
}
}
/* True if an adjacent tile is horizon tracing and will need this tile data for denoising. */
bool tile_is_horizon_sampled = false;
/* Could be optimized if that becomes an issue (3x3 cross gather + 3x3 "X" shape scatter). */
for (int x_tile = -1; x_tile <= 1; x_tile++) {
for (int y_tile = -1; y_tile <= 1; y_tile++) {
ivec2 tile_adj = tile + ivec2(x_tile, y_tile);
bool is_center_tile = (x_tile == 0 && y_tile == 0);
if (in_image_range(tile_adj, tile_horizon_tracing_img) && !is_center_tile) {
if (imageLoad(tile_horizon_tracing_img, ivec3(tile_adj, closure_index)).r != 0) {
/* This tile will sample the target tracing tile. Make sure it is cleared. */
tile_is_horizon_sampled = true;
break;
}
}
}
}
/* TODO(fclem): we might want to dispatch another type of shader only for clearing. */
if (is_ray_tracing || tile_is_ray_sampled) {
/* Dispatch trace resolution tracing tile. */
@ -78,15 +55,7 @@ void main()
raytrace_tracing_tiles_buf[tile_index] = packUvec2x16(uvec2(tile));
}
/* TODO(fclem): we might want to dispatch another type of shader only for clearing. */
if (is_horizon_tracing || tile_is_horizon_sampled) {
/* Dispatch trace resolution tracing tile. */
uint tile_index = atomicAdd(horizon_tracing_dispatch_buf.num_groups_x, 1u);
horizon_tracing_tiles_buf[tile_index] = packUvec2x16(uvec2(tile));
}
/* Dispatch denoise tiles. */
int resolution_scale = uniform_buf.raytrace.resolution_scale;
if (is_ray_tracing) {
for (int x_tile = 0; x_tile < resolution_scale; x_tile++) {
for (int y_tile = 0; y_tile < resolution_scale; y_tile++) {
@ -100,17 +69,4 @@ void main()
}
}
}
if (is_horizon_tracing) {
for (int x_tile = 0; x_tile < resolution_scale; x_tile++) {
for (int y_tile = 0; y_tile < resolution_scale; y_tile++) {
ivec2 tile_adj = tile * resolution_scale + ivec2(x_tile, y_tile);
if (in_image_range(tile_adj, tile_horizon_denoise_img)) {
if (imageLoad(tile_horizon_denoise_img, ivec3(tile_adj, closure_index)).r != 0) {
uint tile_index = atomicAdd(horizon_denoise_dispatch_buf.num_groups_x, 1u);
horizon_denoise_tiles_buf[tile_index] = packUvec2x16(uvec2(tile_adj));
}
}
}
}
}
}

View File

@ -0,0 +1,82 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/* Sum all spherical harmonic coefficients extracting during remapping to octahedral map.
* Dispatch only one thread-group that sums. */
#pragma BLENDER_REQUIRE(eevee_reflection_probe_lib.glsl)
#pragma BLENDER_REQUIRE(eevee_reflection_probe_mapping_lib.glsl)
#pragma BLENDER_REQUIRE(eevee_spherical_harmonics_lib.glsl)
#pragma BLENDER_REQUIRE(eevee_sampling_lib.glsl)
shared vec4 local_sh_coefs[gl_WorkGroupSize.x][4];
void spherical_harmonic_lds_store(uint index, SphericalHarmonicL1 sh)
{
local_sh_coefs[index][0] = sh.L0.M0;
local_sh_coefs[index][1] = sh.L1.Mn1;
local_sh_coefs[index][2] = sh.L1.M0;
local_sh_coefs[index][3] = sh.L1.Mp1;
}
SphericalHarmonicL1 spherical_harmonic_lds_load(uint index)
{
SphericalHarmonicL1 sh;
sh.L0.M0 = local_sh_coefs[index][0];
sh.L1.Mn1 = local_sh_coefs[index][1];
sh.L1.M0 = local_sh_coefs[index][2];
sh.L1.Mp1 = local_sh_coefs[index][3];
return sh;
}
void main()
{
SphericalHarmonicL1 sh;
sh.L0.M0 = vec4(0.0);
sh.L1.Mn1 = vec4(0.0);
sh.L1.M0 = vec4(0.0);
sh.L1.Mp1 = vec4(0.0);
/* First sum onto the local memory. */
uint valid_data_len = probe_remap_dispatch_size.x * probe_remap_dispatch_size.y;
const uint iter_count = uint(SPHERE_PROBE_MAX_HARMONIC) / gl_WorkGroupSize.x;
for (uint i = 0; i < iter_count; i++) {
uint index = gl_WorkGroupSize.x * i + gl_LocalInvocationIndex;
if (index >= valid_data_len) {
break;
}
SphericalHarmonicL1 sh_sample;
sh_sample.L0.M0 = in_sh[index].L0_M0;
sh_sample.L1.Mn1 = in_sh[index].L1_Mn1;
sh_sample.L1.M0 = in_sh[index].L1_M0;
sh_sample.L1.Mp1 = in_sh[index].L1_Mp1;
sh = spherical_harmonics_add(sh, sh_sample);
}
/* Then sum across invocations. */
const uint local_index = gl_LocalInvocationIndex;
local_sh_coefs[local_index][0] = sh.L0.M0;
local_sh_coefs[local_index][1] = sh.L1.Mn1;
local_sh_coefs[local_index][2] = sh.L1.M0;
local_sh_coefs[local_index][3] = sh.L1.Mp1;
/* Parallel sum. */
const uint group_size = gl_WorkGroupSize.x * gl_WorkGroupSize.y;
for (uint stride = group_size / 2; stride > 0; stride /= 2) {
barrier();
if (local_index < stride) {
for (int i = 0; i < 4; i++) {
local_sh_coefs[local_index][i] += local_sh_coefs[local_index + stride][i];
}
}
}
barrier();
if (gl_LocalInvocationIndex == 0u) {
out_sh.L0_M0 = local_sh_coefs[0][0];
out_sh.L1_Mn1 = local_sh_coefs[0][1];
out_sh.L1_M0 = local_sh_coefs[0][2];
out_sh.L1_Mp1 = local_sh_coefs[0][3];
}
}

View File

@ -6,6 +6,63 @@
#pragma BLENDER_REQUIRE(eevee_reflection_probe_mapping_lib.glsl)
#pragma BLENDER_REQUIRE(eevee_colorspace_lib.glsl)
#pragma BLENDER_REQUIRE(eevee_spherical_harmonics_lib.glsl)
shared vec4 local_radiance[gl_WorkGroupSize.x * gl_WorkGroupSize.y];
float triangle_solid_angle(vec3 A, vec3 B, vec3 C)
{
return 2.0 * atan(abs(dot(A, cross(B, C))), (1.0 + dot(B, C) + dot(A, C) + dot(A, B)));
}
float quad_solid_angle(vec3 A, vec3 B, vec3 C, vec3 D)
{
return triangle_solid_angle(A, B, C) + triangle_solid_angle(C, B, D);
}
float octahedral_texel_solid_angle(ivec2 local_texel,
SphereProbePixelArea write_co,
SphereProbeUvArea sample_co)
{
if (any(equal(local_texel, ivec2(write_co.extent - 1)))) {
/* Do not weight these border pixels that are redundant. */
return 0.0;
}
/* Since we are puting texel centers on the edges of the octahedron, the shape of a texel can be
* anything from a simple quad (at the Z=0 poles), to a 4 pointed start (at the Z=+-1 poles)
* passing by arrow tail shapes (at the X=0 and Y=0 edges). So while it would be more correct to
* account for all these shapes (using 8 triangles), it proves to be quite involved with all the
* corner cases. Instead, we compute the area as if the texels were not aligned with the edges.
* This simplify things at the cost of making the weighting a tiny bit off for every pixels.
* The sum of all texels is still giving 4 PI. */
vec3 v00 = sphere_probe_texel_to_direction(local_texel + ivec2(-1, -1), write_co, sample_co);
vec3 v10 = sphere_probe_texel_to_direction(local_texel + ivec2(+0, -1), write_co, sample_co);
vec3 v20 = sphere_probe_texel_to_direction(local_texel + ivec2(-1, -1), write_co, sample_co);
vec3 v01 = sphere_probe_texel_to_direction(local_texel + ivec2(-1, +0), write_co, sample_co);
vec3 v11 = sphere_probe_texel_to_direction(local_texel + ivec2(+0, +0), write_co, sample_co);
vec3 v21 = sphere_probe_texel_to_direction(local_texel + ivec2(+1, +0), write_co, sample_co);
vec3 v02 = sphere_probe_texel_to_direction(local_texel + ivec2(-1, +1), write_co, sample_co);
vec3 v12 = sphere_probe_texel_to_direction(local_texel + ivec2(+0, +1), write_co, sample_co);
vec3 v22 = sphere_probe_texel_to_direction(local_texel + ivec2(+1, +1), write_co, sample_co);
/* The solid angle functions expect normalized vectors. */
v00 = normalize(v00);
v10 = normalize(v10);
v20 = normalize(v20);
v01 = normalize(v01);
v11 = normalize(v11);
v21 = normalize(v21);
v02 = normalize(v02);
v12 = normalize(v12);
v22 = normalize(v22);
#if 0 /* Has artifacts, is marginaly more correct. */
/* For some reason quad_solid_angle(v10, v20, v11, v21) gives some strange artifacts at Z=0. */
return 0.25 * (quad_solid_angle(v00, v10, v01, v11) + quad_solid_angle(v10, v20, v11, v21) +
quad_solid_angle(v01, v11, v02, v12) + quad_solid_angle(v11, v21, v12, v22));
#else
/* Choosing the positive quad (0,0) > (+1,+1) for stability. */
return quad_solid_angle(v11, v21, v12, v22);
#endif
}
void main()
{
@ -16,11 +73,6 @@ void main()
/* Texel in probe. */
ivec2 local_texel = ivec2(gl_GlobalInvocationID.xy);
/* Exit when pixel being written doesn't fit in the area reserved for the probe. */
if (any(greaterThanEqual(local_texel, ivec2(write_coord.extent)))) {
return;
}
vec2 wrapped_uv;
vec3 direction = sphere_probe_texel_to_direction(
local_texel, write_coord, sample_coord, wrapped_uv);
@ -30,7 +82,7 @@ void main()
float opacity = 1.0 - radiance_and_transmittance.a;
/* Composite world into reflection probes. */
bool is_world = all(equal(write_coord_packed, world_coord_packed));
bool is_world = all(equal(probe_coord_packed, world_coord_packed));
if (!is_world && opacity != 1.0) {
vec2 world_uv = wrapped_uv * world_coord.scale + world_coord.offset;
vec4 world_radiance = textureLod(atlas_tx, vec3(world_uv, world_coord.layer), 0.0);
@ -39,6 +91,53 @@ void main()
radiance = colorspace_brightness_clamp_max(radiance, probe_brightness_clamp);
ivec3 texel = ivec3(local_texel + write_coord.offset, write_coord.layer);
imageStore(atlas_img, texel, vec4(radiance, 1.0));
if (!any(greaterThanEqual(local_texel, ivec2(write_coord.extent)))) {
ivec3 texel = ivec3(local_texel + write_coord.offset, write_coord.layer);
imageStore(atlas_img, texel, vec4(radiance, 1.0));
}
if (extract_sh) {
float sample_weight = octahedral_texel_solid_angle(local_texel, write_coord, sample_coord);
const uint local_index = gl_LocalInvocationIndex;
const uint group_size = gl_WorkGroupSize.x * gl_WorkGroupSize.y;
/* Parallel sum. Result is stored inside local_radiance[0]. */
local_radiance[local_index] = radiance.xyzz * sample_weight;
for (uint stride = group_size / 2; stride > 0; stride /= 2) {
barrier();
if (local_index < stride) {
local_radiance[local_index] += local_radiance[local_index + stride];
}
}
barrier();
if (gl_LocalInvocationIndex == 0u) {
/* Find the middle point of the whole thread-group. Use it as light vector.
* Note that this is an approximation since the footprint of a thread-group is not
* necessarily a convex polygons (with center of gravity at midpoint).
* But the actual error introduce by this approximation is not perceivable. */
ivec2 max_group_texel = local_texel + ivec2(gl_WorkGroupSize.xy);
/* Min direction is the local direction since this is only ran by thread 0. */
vec3 min_direction = normalize(direction);
vec3 max_direction = normalize(
sphere_probe_texel_to_direction(max_group_texel, write_coord, sample_coord));
vec3 L = normalize(min_direction + max_direction);
/* Convert radiance to spherical harmonics. */
SphericalHarmonicL1 sh;
sh.L0.M0 = vec4(0.0);
sh.L1.Mn1 = vec4(0.0);
sh.L1.M0 = vec4(0.0);
sh.L1.Mp1 = vec4(0.0);
/* TODO(fclem): Cleanup: Should spherical_harmonics_encode_signal_sample return a new sh
* instead of adding to it? */
spherical_harmonics_encode_signal_sample(L, local_radiance[0], sh);
/* Outputs one SH for each threadgroup. */
uint work_group_index = gl_NumWorkGroups.x * gl_WorkGroupID.y + gl_WorkGroupID.x;
out_sh[work_group_index].L0_M0 = sh.L0.M0;
out_sh[work_group_index].L1_Mn1 = sh.L1.Mn1;
out_sh[work_group_index].L1_M0 = sh.L1.M0;
out_sh[work_group_index].L1_Mp1 = sh.L1.Mp1;
}
}
}

View File

@ -1,76 +0,0 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/* Shader to extract spherical harmonics cooefs from octahedral mapped reflection probe. */
#pragma BLENDER_REQUIRE(eevee_reflection_probe_lib.glsl)
#pragma BLENDER_REQUIRE(eevee_reflection_probe_mapping_lib.glsl)
#pragma BLENDER_REQUIRE(eevee_spherical_harmonics_lib.glsl)
#pragma BLENDER_REQUIRE(eevee_sampling_lib.glsl)
void atlas_store(vec4 sh_coefficient, ivec2 atlas_coord, int layer)
{
for (int x = 0; x < IRRADIANCE_GRID_BRICK_SIZE; x++) {
for (int y = 0; y < IRRADIANCE_GRID_BRICK_SIZE; y++) {
for (int z = 0; z < IRRADIANCE_GRID_BRICK_SIZE; z++) {
ivec3 brick_coord = ivec3(x, y, z);
imageStore(irradiance_atlas_img,
ivec3(atlas_coord, layer * IRRADIANCE_GRID_BRICK_SIZE) + brick_coord,
sh_coefficient);
}
}
}
}
shared vec4 cooefs[gl_WorkGroupSize.x][4];
void main()
{
SphericalHarmonicL1 cooef;
cooef.L0.M0 = vec4(0.0);
cooef.L1.Mn1 = vec4(0.0);
cooef.L1.M0 = vec4(0.0);
cooef.L1.Mp1 = vec4(0.0);
SphereProbeUvArea atlas_coord = reinterpret_as_atlas_coord(world_coord_packed);
float layer_mipmap = 2;
/* Perform multiple sample. */
uint store_index = gl_LocalInvocationID.x;
float total_samples = float(gl_WorkGroupSize.x * SPHERE_PROBE_SH_SAMPLES_PER_GROUP);
float sample_weight = 4.0 * M_PI / total_samples;
float sample_offset = float(gl_LocalInvocationID.x * SPHERE_PROBE_SH_SAMPLES_PER_GROUP);
for (int sample_index = 0; sample_index < SPHERE_PROBE_SH_SAMPLES_PER_GROUP; sample_index++) {
vec2 rand = fract(hammersley_2d(sample_index + sample_offset, total_samples));
vec3 direction = sample_sphere(rand);
vec4 light = reflection_probes_sample(direction, layer_mipmap, atlas_coord);
spherical_harmonics_encode_signal_sample(direction, light * sample_weight, cooef);
}
cooefs[store_index][0] = cooef.L0.M0;
cooefs[store_index][1] = cooef.L1.Mn1;
cooefs[store_index][2] = cooef.L1.M0;
cooefs[store_index][3] = cooef.L1.Mp1;
barrier();
if (gl_LocalInvocationIndex == 0u) {
/* Join results */
vec4 result[4];
result[0] = vec4(0.0);
result[1] = vec4(0.0);
result[2] = vec4(0.0);
result[3] = vec4(0.0);
for (uint i = 0; i < gl_WorkGroupSize.x; i++) {
result[0] += cooefs[i][0];
result[1] += cooefs[i][1];
result[2] += cooefs[i][2];
result[3] += cooefs[i][3];
}
ivec2 atlas_coord = ivec2(0, 0);
atlas_store(result[0], atlas_coord, 0);
atlas_store(result[1], atlas_coord, 1);
atlas_store(result[2], atlas_coord, 2);
atlas_store(result[3], atlas_coord, 3);
}
}

View File

@ -102,6 +102,102 @@ struct SphericalHarmonicL2 {
SphericalHarmonicBandL2 L2;
};
SphericalHarmonicBandL0 spherical_harmonics_band_L0_new()
{
SphericalHarmonicBandL0 L0;
L0.M0 = vec4(0.0);
return L0;
}
SphericalHarmonicBandL1 spherical_harmonics_band_L1_new()
{
SphericalHarmonicBandL1 L1;
L1.Mn1 = vec4(0.0);
L1.M0 = vec4(0.0);
L1.Mp1 = vec4(0.0);
return L1;
}
SphericalHarmonicBandL2 spherical_harmonics_band_L2_new()
{
SphericalHarmonicBandL2 L2;
L2.Mn2 = vec4(0.0);
L2.Mn1 = vec4(0.0);
L2.M0 = vec4(0.0);
L2.Mp1 = vec4(0.0);
L2.Mp2 = vec4(0.0);
return L2;
}
SphericalHarmonicL0 spherical_harmonics_L0_new()
{
SphericalHarmonicL0 sh;
sh.L0 = spherical_harmonics_band_L0_new();
return sh;
}
SphericalHarmonicL1 spherical_harmonics_L1_new()
{
SphericalHarmonicL1 sh;
sh.L0 = spherical_harmonics_band_L0_new();
sh.L1 = spherical_harmonics_band_L1_new();
return sh;
}
SphericalHarmonicL2 spherical_harmonics_L2_new()
{
SphericalHarmonicL2 sh;
sh.L0 = spherical_harmonics_band_L0_new();
sh.L1 = spherical_harmonics_band_L1_new();
sh.L2 = spherical_harmonics_band_L2_new();
return sh;
}
SphericalHarmonicBandL0 spherical_harmonics_band_L0_swizzle_wwww(SphericalHarmonicBandL0 L0)
{
L0.M0 = L0.M0.wwww;
return L0;
}
SphericalHarmonicBandL1 spherical_harmonics_band_L1_swizzle_wwww(SphericalHarmonicBandL1 L1)
{
L1.Mn1 = L1.Mn1.wwww;
L1.M0 = L1.M0.wwww;
L1.Mp1 = L1.Mp1.wwww;
return L1;
}
SphericalHarmonicBandL2 spherical_harmonics_band_L2_swizzle_wwww(SphericalHarmonicBandL2 L2)
{
L2.Mn2 = L2.Mn2.wwww;
L2.Mn1 = L2.Mn1.wwww;
L2.M0 = L2.M0.wwww;
L2.Mp1 = L2.Mp1.wwww;
L2.Mp2 = L2.Mp2.wwww;
return L2;
}
SphericalHarmonicL0 spherical_harmonics_swizzle_wwww(SphericalHarmonicL0 sh)
{
sh.L0 = spherical_harmonics_band_L0_swizzle_wwww(sh.L0);
return sh;
}
SphericalHarmonicL1 spherical_harmonics_swizzle_wwww(SphericalHarmonicL1 sh)
{
sh.L0 = spherical_harmonics_band_L0_swizzle_wwww(sh.L0);
sh.L1 = spherical_harmonics_band_L1_swizzle_wwww(sh.L1);
return sh;
}
SphericalHarmonicL2 spherical_harmonics_swizzle_wwww(SphericalHarmonicL2 sh)
{
sh.L0 = spherical_harmonics_band_L0_swizzle_wwww(sh.L0);
sh.L1 = spherical_harmonics_band_L1_swizzle_wwww(sh.L1);
sh.L2 = spherical_harmonics_band_L2_swizzle_wwww(sh.L2);
return sh;
}
/** \} */
/* -------------------------------------------------------------------- */
@ -562,3 +658,52 @@ SphericalHarmonicL2 spherical_harmonics_add(SphericalHarmonicL2 a, SphericalHarm
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Dot
* \{ */
vec4 spherical_harmonics_dot(SphericalHarmonicL1 a, SphericalHarmonicL1 b)
{
/* Convert coefficients to per channel column. */
mat4x4 a_mat = transpose(mat4x4(a.L0.M0, a.L1.Mn1, a.L1.M0, a.L1.Mp1));
mat4x4 b_mat = transpose(mat4x4(b.L0.M0, b.L1.Mn1, b.L1.M0, b.L1.Mp1));
vec4 result;
result[0] = dot(a_mat[0], b_mat[0]);
result[1] = dot(a_mat[1], b_mat[1]);
result[2] = dot(a_mat[2], b_mat[2]);
result[3] = dot(a_mat[3], b_mat[3]);
return result;
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Compression
*
* Described by Josh Hobson in "The indirect Lighting Pipeline of God of War" p. 120
* \{ */
SphericalHarmonicL1 spherical_harmonics_compress(SphericalHarmonicL1 sh)
{
SphericalHarmonicL1 result;
result.L0 = sh.L0;
vec4 fac = safe_rcp(sh.L0.M0 * M_SQRT3);
result.L1.Mn1 = (sh.L1.Mn1 * fac) * 0.5 + 0.5;
result.L1.M0 = (sh.L1.M0 * fac) * 0.5 + 0.5;
result.L1.Mp1 = (sh.L1.Mp1 * fac) * 0.5 + 0.5;
return result;
}
SphericalHarmonicL1 spherical_harmonics_decompress(SphericalHarmonicL1 sh)
{
SphericalHarmonicL1 result;
result.L0 = sh.L0;
vec4 fac = sh.L0.M0 * M_SQRT3;
result.L1.Mn1 = (sh.L1.Mn1 * 2.0 - 1.0) * fac;
result.L1.M0 = (sh.L1.M0 * 2.0 - 1.0) * fac;
result.L1.Mp1 = (sh.L1.Mp1 * 2.0 - 1.0) * fac;
return result;
}
/** \} */

View File

@ -10,6 +10,7 @@
#pragma BLENDER_REQUIRE(draw_view_lib.glsl)
#pragma BLENDER_REQUIRE(eevee_light_lib.glsl)
#pragma BLENDER_REQUIRE(eevee_shadow_lib.glsl)
#pragma BLENDER_REQUIRE(eevee_spherical_harmonics_lib.glsl)
/* Based on Frosbite Unified Volumetric.
* https://www.ea.com/frostbite/news/physically-based-unified-volumetric-rendering-in-frostbite */
@ -68,6 +69,22 @@ float volume_phase_function(vec3 V, vec3 L, float g)
return (1 - sqr_g) / max(1e-8, 4.0 * M_PI * pow(1 + sqr_g - 2 * g * cos_theta, 3.0 / 2.0));
}
SphericalHarmonicL1 volume_phase_function_as_sh_L1(vec3 V, float g)
{
/* Compute rotated zonal harmonic.
* From Bartlomiej Wronsky
* "Volumetric Fog: Unified compute shader based solution to atmospheric scattering" page 55
* Siggraph 2014
* https://bartwronski.files.wordpress.com/2014/08/bwronski_volumetric_fog_siggraph2014.pdf
*/
SphericalHarmonicL1 sh;
sh.L0.M0 = spherical_harmonics_L0_M0(V) * vec4(1.0);
sh.L1.Mn1 = spherical_harmonics_L1_Mn1(V) * vec4(g);
sh.L1.M0 = spherical_harmonics_L1_M0(V) * vec4(g);
sh.L1.Mp1 = spherical_harmonics_L1_Mp1(V) * vec4(g);
return sh;
}
vec3 volume_light(LightData light, const bool is_directional, LightVector lv)
{
float power = 1.0;
@ -153,16 +170,6 @@ vec3 volume_shadow(
#endif /* VOLUME_SHADOW */
}
vec3 volume_irradiance(vec3 P)
{
#ifdef VOLUME_IRRADIANCE
SphericalHarmonicL1 irradiance = lightprobe_irradiance_sample(P);
return irradiance.L0.M0.rgb * M_PI;
#else
return vec3(0.0);
#endif
}
struct VolumeResolveSample {
vec3 transmittance;
vec3 scattering;

View File

@ -78,10 +78,13 @@ void main()
float s_anisotropy = phase.x / max(1.0, phase.y);
#ifdef VOLUME_LIGHTING
scattering += volume_irradiance(P) * s_scattering * volume_phase_function_isotropic();
SphericalHarmonicL1 phase_sh = volume_phase_function_as_sh_L1(V, s_anisotropy);
SphericalHarmonicL1 volume_radiance_sh = lightprobe_irradiance_sample(P);
vec3 light_scattering = spherical_harmonics_dot(volume_radiance_sh, phase_sh).xyz;
LIGHT_FOREACH_BEGIN_DIRECTIONAL (light_cull_buf, l_idx) {
scattering += volume_scatter_light_eval(true, P, V, l_idx, s_anisotropy) * s_scattering;
light_scattering += volume_scatter_light_eval(true, P, V, l_idx, s_anisotropy);
}
LIGHT_FOREACH_END
@ -89,10 +92,11 @@ void main()
uniform_buf.volumes.viewport_size_inv;
LIGHT_FOREACH_BEGIN_LOCAL (light_cull_buf, light_zbin_buf, light_tile_buf, pixel, vP.z, l_idx) {
scattering += volume_scatter_light_eval(false, P, V, l_idx, s_anisotropy) * s_scattering;
light_scattering += volume_scatter_light_eval(false, P, V, l_idx, s_anisotropy);
}
LIGHT_FOREACH_END
scattering += light_scattering * s_scattering;
#endif
/* Catch NaNs. */

View File

@ -169,6 +169,20 @@ GPU_SHADER_CREATE_INFO(eevee_lightprobe_irradiance_offset)
/** \name Runtime
* \{ */
GPU_SHADER_CREATE_INFO(eevee_lightprobe_irradiance_world)
.local_group_size(IRRADIANCE_GRID_BRICK_SIZE,
IRRADIANCE_GRID_BRICK_SIZE,
IRRADIANCE_GRID_BRICK_SIZE)
.define("IRRADIANCE_GRID_UPLOAD")
.additional_info("eevee_shared")
.push_constant(Type::INT, "grid_index")
.storage_buf(0, Qualifier::READ, "uint", "bricks_infos_buf[]")
.storage_buf(1, Qualifier::READ, "SphereProbeHarmonic", "harmonic_buf")
.uniform_buf(0, "VolumeProbeData", "grids_infos_buf[IRRADIANCE_GRID_MAX]")
.image(0, GPU_RGBA16F, Qualifier::READ_WRITE, ImageType::FLOAT_3D, "irradiance_atlas_img")
.compute_source("eevee_lightprobe_irradiance_world_comp.glsl")
.do_static_compilation(true);
GPU_SHADER_CREATE_INFO(eevee_lightprobe_irradiance_load)
.local_group_size(IRRADIANCE_GRID_BRICK_SIZE,
IRRADIANCE_GRID_BRICK_SIZE,

View File

@ -18,29 +18,28 @@ GPU_SHADER_CREATE_INFO(eevee_reflection_probe_data)
/* Sample cubemap and remap into an octahedral texture. */
GPU_SHADER_CREATE_INFO(eevee_reflection_probe_remap)
.local_group_size(SPHERE_PROBE_GROUP_SIZE, SPHERE_PROBE_GROUP_SIZE)
.local_group_size(SPHERE_PROBE_REMAP_GROUP_SIZE, SPHERE_PROBE_REMAP_GROUP_SIZE)
.specialization_constant(Type::BOOL, "extract_sh", true)
.push_constant(Type::IVEC4, "probe_coord_packed")
.push_constant(Type::IVEC4, "write_coord_packed")
.push_constant(Type::IVEC4, "world_coord_packed")
.push_constant(Type::FLOAT, "probe_brightness_clamp")
.sampler(0, ImageType::FLOAT_CUBE, "cubemap_tx")
.sampler(1, ImageType::FLOAT_2D_ARRAY, "atlas_tx")
.storage_buf(0, Qualifier::WRITE, "SphereProbeHarmonic", "out_sh[SPHERE_PROBE_MAX_HARMONIC]")
.image(0, GPU_RGBA16F, Qualifier::WRITE, ImageType::FLOAT_2D_ARRAY, "atlas_img")
.compute_source("eevee_reflection_probe_remap_comp.glsl")
.additional_info("eevee_shared")
.do_static_compilation(true);
/* Extract spherical harmonics band L0 + L1 from octahedral mapped reflection probe and update the
* world brick of the irradiance cache. */
GPU_SHADER_CREATE_INFO(eevee_reflection_probe_update_irradiance)
.local_group_size(SPHERE_PROBE_SH_GROUP_SIZE, 1)
.define("SPHERE_PROBE")
.push_constant(Type::IVEC4, "world_coord_packed")
.sampler(0, ImageType::FLOAT_2D_ARRAY, "reflection_probes_tx")
.image(0, GPU_RGBA16F, Qualifier::READ_WRITE, ImageType::FLOAT_3D, "irradiance_atlas_img")
GPU_SHADER_CREATE_INFO(eevee_reflection_probe_irradiance)
.local_group_size(SPHERE_PROBE_SH_GROUP_SIZE)
.push_constant(Type::IVEC3, "probe_remap_dispatch_size")
.storage_buf(0, Qualifier::READ, "SphereProbeHarmonic", "in_sh[SPHERE_PROBE_MAX_HARMONIC]")
.storage_buf(1, Qualifier::WRITE, "SphereProbeHarmonic", "out_sh")
.additional_info("eevee_shared")
.compute_source("eevee_reflection_probe_update_irradiance_comp.glsl")
.do_static_compilation(true);
.do_static_compilation(true)
.compute_source("eevee_reflection_probe_irradiance_comp.glsl");
GPU_SHADER_CREATE_INFO(eevee_reflection_probe_select)
.local_group_size(SPHERE_PROBE_SELECT_GROUP_SIZE)

View File

@ -32,17 +32,12 @@ GPU_SHADER_CREATE_INFO(eevee_ray_tile_compact)
.typedef_source("draw_shader_shared.h")
.image_in(0, RAYTRACE_TILEMASK_FORMAT, ImageType::UINT_2D_ARRAY, "tile_raytrace_denoise_img")
.image_in(1, RAYTRACE_TILEMASK_FORMAT, ImageType::UINT_2D_ARRAY, "tile_raytrace_tracing_img")
.image_in(2, RAYTRACE_TILEMASK_FORMAT, ImageType::UINT_2D_ARRAY, "tile_horizon_denoise_img")
.image_in(3, RAYTRACE_TILEMASK_FORMAT, ImageType::UINT_2D_ARRAY, "tile_horizon_tracing_img")
.storage_buf(0, Qualifier::READ_WRITE, "DispatchCommand", "raytrace_tracing_dispatch_buf")
.storage_buf(1, Qualifier::READ_WRITE, "DispatchCommand", "raytrace_denoise_dispatch_buf")
.storage_buf(2, Qualifier::READ_WRITE, "DispatchCommand", "horizon_tracing_dispatch_buf")
.storage_buf(3, Qualifier::READ_WRITE, "DispatchCommand", "horizon_denoise_dispatch_buf")
.storage_buf(4, Qualifier::WRITE, "uint", "raytrace_tracing_tiles_buf[]")
.storage_buf(5, Qualifier::WRITE, "uint", "raytrace_denoise_tiles_buf[]")
.storage_buf(6, Qualifier::WRITE, "uint", "horizon_tracing_tiles_buf[]")
.storage_buf(7, Qualifier::WRITE, "uint", "horizon_denoise_tiles_buf[]")
.specialization_constant(Type::INT, "closure_index", 0)
.specialization_constant(Type::INT, "resolution_scale", 2)
.compute_source("eevee_ray_tile_compact_comp.glsl");
GPU_SHADER_CREATE_INFO(eevee_ray_generate)
@ -188,7 +183,7 @@ GPU_SHADER_CREATE_INFO(eevee_horizon_setup)
.sampler(0, ImageType::DEPTH_2D, "depth_tx")
.sampler(1, ImageType::FLOAT_2D, "in_radiance_tx")
.image(2, RAYTRACE_RADIANCE_FORMAT, Qualifier::WRITE, ImageType::FLOAT_2D, "out_radiance_img")
.image(3, GPU_RGBA8, Qualifier::WRITE, ImageType::FLOAT_2D, "out_normal_img")
.image(3, GPU_RGB10_A2, Qualifier::WRITE, ImageType::FLOAT_2D, "out_normal_img")
.compute_source("eevee_horizon_setup_comp.glsl");
GPU_SHADER_CREATE_INFO(eevee_horizon_scan)
@ -203,14 +198,31 @@ GPU_SHADER_CREATE_INFO(eevee_horizon_scan)
"draw_view")
.sampler(0, ImageType::FLOAT_2D, "screen_radiance_tx")
.sampler(1, ImageType::FLOAT_2D, "screen_normal_tx")
.image(
2, RAYTRACE_RADIANCE_FORMAT, Qualifier::WRITE, ImageType::FLOAT_2D, "horizon_radiance_img")
.image(3, GPU_R8, Qualifier::WRITE, ImageType::FLOAT_2D, "horizon_occlusion_img")
.image(2, GPU_RGBA16F, Qualifier::WRITE, ImageType::FLOAT_2D, "horizon_radiance_0_img")
.image(3, GPU_RGBA8, Qualifier::WRITE, ImageType::FLOAT_2D, "horizon_radiance_1_img")
.image(4, GPU_RGBA8, Qualifier::WRITE, ImageType::FLOAT_2D, "horizon_radiance_2_img")
.image(5, GPU_RGBA8, Qualifier::WRITE, ImageType::FLOAT_2D, "horizon_radiance_3_img")
.storage_buf(7, Qualifier::READ, "uint", "tiles_coord_buf[]")
.specialization_constant(Type::INT, "closure_index", 0)
.compute_source("eevee_horizon_scan_comp.glsl");
GPU_SHADER_CREATE_INFO(eevee_horizon_denoise)
.do_static_compilation(true)
.local_group_size(RAYTRACE_GROUP_SIZE, RAYTRACE_GROUP_SIZE)
.additional_info(
"eevee_shared", "eevee_global_ubo", "eevee_sampling_data", "eevee_hiz_data", "draw_view")
.sampler(2, ImageType::FLOAT_2D, "in_sh_0_tx")
.sampler(4, ImageType::FLOAT_2D, "in_sh_1_tx")
.sampler(5, ImageType::FLOAT_2D, "in_sh_2_tx")
.sampler(6, ImageType::FLOAT_2D, "in_sh_3_tx")
.sampler(7, ImageType::FLOAT_2D, "screen_normal_tx")
.image(2, GPU_RGBA16F, Qualifier::WRITE, ImageType::FLOAT_2D, "out_sh_0_img")
.image(3, GPU_RGBA8, Qualifier::WRITE, ImageType::FLOAT_2D, "out_sh_1_img")
.image(4, GPU_RGBA8, Qualifier::WRITE, ImageType::FLOAT_2D, "out_sh_2_img")
.image(5, GPU_RGBA8, Qualifier::WRITE, ImageType::FLOAT_2D, "out_sh_3_img")
.storage_buf(7, Qualifier::READ, "uint", "tiles_coord_buf[]")
.compute_source("eevee_horizon_denoise_comp.glsl");
GPU_SHADER_CREATE_INFO(eevee_horizon_resolve)
.do_static_compilation(true)
.local_group_size(RAYTRACE_GROUP_SIZE, RAYTRACE_GROUP_SIZE)
.additional_info("eevee_shared",
@ -220,14 +232,16 @@ GPU_SHADER_CREATE_INFO(eevee_horizon_denoise)
"eevee_lightprobe_data",
"draw_view")
.sampler(1, ImageType::DEPTH_2D, "depth_tx")
.image(
2, RAYTRACE_RADIANCE_FORMAT, Qualifier::READ, ImageType::FLOAT_2D, "horizon_radiance_img")
.image(3, GPU_R8, Qualifier::READ, ImageType::FLOAT_2D, "horizon_occlusion_img")
.image(4, RAYTRACE_RADIANCE_FORMAT, Qualifier::READ_WRITE, ImageType::FLOAT_2D, "radiance_img")
.image(6, RAYTRACE_TILEMASK_FORMAT, Qualifier::READ, ImageType::UINT_2D_ARRAY, "tile_mask_img")
.sampler(2, ImageType::FLOAT_2D, "horizon_radiance_0_tx")
.sampler(3, ImageType::FLOAT_2D, "horizon_radiance_1_tx")
.sampler(4, ImageType::FLOAT_2D, "horizon_radiance_2_tx")
.sampler(5, ImageType::FLOAT_2D, "horizon_radiance_3_tx")
.sampler(8, ImageType::FLOAT_2D, "screen_normal_tx")
.image(3, RAYTRACE_RADIANCE_FORMAT, Qualifier::READ_WRITE, ImageType::FLOAT_2D, "closure0_img")
.image(4, RAYTRACE_RADIANCE_FORMAT, Qualifier::READ_WRITE, ImageType::FLOAT_2D, "closure1_img")
.image(5, RAYTRACE_RADIANCE_FORMAT, Qualifier::READ_WRITE, ImageType::FLOAT_2D, "closure2_img")
.storage_buf(7, Qualifier::READ, "uint", "tiles_coord_buf[]")
.specialization_constant(Type::INT, "closure_index", 0)
.compute_source("eevee_horizon_denoise_comp.glsl");
.compute_source("eevee_horizon_resolve_comp.glsl");
#undef image_out
#undef image_in

View File

@ -217,12 +217,14 @@ struct MeshExtractLooseGeom {
};
struct SortedFaceData {
/** The first triangle index for each polygon, sorted into slices by material. */
Array<int> tri_first_index;
/* The total number of visible triangles (a sum of the values in #mat_tri_counts). */
int visible_tris_num;
/** The number of visible triangles assigned to each material. */
Array<int> mat_tri_len;
/* The total number of visible triangles (a sum of the values in #mat_tri_len). */
int visible_tri_len;
Array<int> tris_num_by_material;
/**
* The first triangle index for each face, sorted into slices by material.
*/
Array<int> face_tri_offsets;
};
/**

View File

@ -400,24 +400,24 @@ BLI_INLINE void extract_task_range_run_iter(const MeshRenderData &mr,
case MR_ITER_CORNER_TRI:
range_data.elems = is_mesh ? mr.corner_tris.data() : (void *)mr.edit_bmesh->looptris;
func = is_mesh ? extract_range_iter_corner_tri_mesh : extract_range_iter_looptri_bm;
stop = mr.tri_len;
stop = mr.corner_tris_num;
break;
case MR_ITER_POLY:
range_data.elems = is_mesh ? mr.faces.data().data() : (void *)mr.bm->ftable;
func = is_mesh ? extract_range_iter_face_mesh : extract_range_iter_face_bm;
stop = mr.face_len;
stop = mr.faces_num;
break;
case MR_ITER_LOOSE_EDGE:
range_data.loose_elems = mr.loose_edges.data();
range_data.elems = is_mesh ? mr.edges.data() : (void *)mr.bm->etable;
func = is_mesh ? extract_range_iter_loose_edge_mesh : extract_range_iter_loose_edge_bm;
stop = mr.edge_loose_len;
stop = mr.loose_edges_num;
break;
case MR_ITER_LOOSE_VERT:
range_data.loose_elems = mr.loose_verts.data();
range_data.elems = is_mesh ? mr.vert_positions.data() : (void *)mr.bm->vtable;
func = is_mesh ? extract_range_iter_loose_vert_mesh : extract_range_iter_loose_vert_bm;
stop = mr.vert_loose_len;
stop = mr.loose_verts_num;
break;
default:
BLI_assert(false);
@ -721,7 +721,7 @@ void mesh_buffer_cache_create_requested(TaskGraph *task_graph,
task_graph, *mr, mbc, iter_type, data_flag);
/* Simple heuristic. */
const bool use_thread = (mr->loop_len + mr->loop_loose_len) > MIN_RANGE_LEN;
const bool use_thread = (mr->corners_num + mr->loose_indices_num) > MIN_RANGE_LEN;
if (use_thread) {
/* First run the requested extractors that do not support asynchronous ranges. */

View File

@ -82,14 +82,14 @@ static void mesh_render_data_loose_verts_bm(const MeshRenderData &mr,
BMIter iter;
BMVert *vert;
int count = 0;
Array<int> loose_verts(mr.vert_len);
Array<int> loose_verts(mr.verts_num);
BM_ITER_MESH_INDEX (vert, &iter, &bm, BM_VERTS_OF_MESH, i) {
if (vert->e == nullptr) {
loose_verts[count] = i;
count++;
}
}
if (count < mr.vert_len) {
if (count < mr.verts_num) {
cache.loose_geom.verts = loose_verts.as_span().take_front(count);
}
else {
@ -105,14 +105,14 @@ static void mesh_render_data_loose_edges_bm(const MeshRenderData &mr,
BMIter iter;
BMEdge *edge;
int count = 0;
Array<int> loose_edges(mr.edge_len);
Array<int> loose_edges(mr.edges_num);
BM_ITER_MESH_INDEX (edge, &iter, &bm, BM_EDGES_OF_MESH, i) {
if (edge->l == nullptr) {
loose_edges[count] = i;
count++;
}
}
if (count < mr.edge_len) {
if (count < mr.edges_num) {
cache.loose_geom.edges = loose_edges.as_span().take_front(count);
}
else {
@ -154,10 +154,10 @@ void mesh_render_data_update_loose_geom(MeshRenderData &mr,
mesh_render_data_loose_geom_ensure(mr, cache);
mr.loose_edges = cache.loose_geom.edges;
mr.loose_verts = cache.loose_geom.verts;
mr.vert_loose_len = cache.loose_geom.verts.size();
mr.edge_loose_len = cache.loose_geom.edges.size();
mr.loose_verts_num = cache.loose_geom.verts.size();
mr.loose_edges_num = cache.loose_geom.edges.size();
mr.loop_loose_len = mr.vert_loose_len + (mr.edge_loose_len * 2);
mr.loose_indices_num = mr.loose_verts_num + (mr.loose_edges_num * 2);
}
}
@ -207,7 +207,7 @@ static void accumululate_material_counts_mesh(
std::plus<int>());
}
else {
all_tri_counts.local().first() = poly_to_tri_count(mr.face_len, mr.loop_len);
all_tri_counts.local().first() = poly_to_tri_count(mr.faces_num, mr.corners_num);
}
return;
}
@ -237,7 +237,7 @@ static void accumululate_material_counts_mesh(
static Array<int> mesh_render_data_mat_tri_len_build(const MeshRenderData &mr)
{
threading::EnumerableThreadSpecific<Array<int>> all_tri_counts(
[&]() { return Array<int>(mr.mat_len, 0); });
[&]() { return Array<int>(mr.materials_num, 0); });
if (mr.extract_type == MR_EXTRACT_BMESH) {
accumululate_material_counts_bm(*mr.bm, all_tri_counts);
@ -246,38 +246,38 @@ static Array<int> mesh_render_data_mat_tri_len_build(const MeshRenderData &mr)
accumululate_material_counts_mesh(mr, all_tri_counts);
}
Array<int> &mat_tri_len = all_tri_counts.local();
Array<int> &tris_num_by_material = all_tri_counts.local();
for (const Array<int> &counts : all_tri_counts) {
if (&counts != &mat_tri_len) {
for (const int i : mat_tri_len.index_range()) {
mat_tri_len[i] += counts[i];
if (&counts != &tris_num_by_material) {
for (const int i : tris_num_by_material.index_range()) {
tris_num_by_material[i] += counts[i];
}
}
}
return std::move(mat_tri_len);
return std::move(tris_num_by_material);
}
static void mesh_render_data_faces_sorted_build(MeshRenderData &mr, MeshBufferCache &cache)
{
cache.face_sorted.mat_tri_len = mesh_render_data_mat_tri_len_build(mr);
const Span<int> mat_tri_len = cache.face_sorted.mat_tri_len;
cache.face_sorted.tris_num_by_material = mesh_render_data_mat_tri_len_build(mr);
const Span<int> tris_num_by_material = cache.face_sorted.tris_num_by_material;
/* Apply offset. */
int visible_tri_len = 0;
Array<int, 32> mat_tri_offs(mr.mat_len);
int visible_tris_num = 0;
Array<int, 32> mat_tri_offs(mr.materials_num);
{
for (int i = 0; i < mr.mat_len; i++) {
mat_tri_offs[i] = visible_tri_len;
visible_tri_len += mat_tri_len[i];
for (int i = 0; i < mr.materials_num; i++) {
mat_tri_offs[i] = visible_tris_num;
visible_tris_num += tris_num_by_material[i];
}
}
cache.face_sorted.visible_tri_len = visible_tri_len;
cache.face_sorted.visible_tris_num = visible_tris_num;
cache.face_sorted.tri_first_index.reinitialize(mr.face_len);
MutableSpan<int> tri_first_index = cache.face_sorted.tri_first_index;
cache.face_sorted.face_tri_offsets.reinitialize(mr.faces_num);
MutableSpan<int> face_tri_offsets = cache.face_sorted.face_tri_offsets;
/* Sort per material. */
int mat_last = mr.mat_len - 1;
int mat_last = mr.materials_num - 1;
if (mr.extract_type == MR_EXTRACT_BMESH) {
BMIter iter;
BMFace *f;
@ -285,25 +285,25 @@ static void mesh_render_data_faces_sorted_build(MeshRenderData &mr, MeshBufferCa
BM_ITER_MESH_INDEX (f, &iter, mr.bm, BM_FACES_OF_MESH, i) {
if (!BM_elem_flag_test(f, BM_ELEM_HIDDEN)) {
const int mat = clamp_i(f->mat_nr, 0, mat_last);
tri_first_index[i] = mat_tri_offs[mat];
face_tri_offsets[i] = mat_tri_offs[mat];
mat_tri_offs[mat] += f->len - 2;
}
else {
tri_first_index[i] = -1;
face_tri_offsets[i] = -1;
}
}
}
else {
for (int i = 0; i < mr.face_len; i++) {
for (int i = 0; i < mr.faces_num; i++) {
if (!(mr.use_hide && !mr.hide_poly.is_empty() && mr.hide_poly[i])) {
const int mat = mr.material_indices.is_empty() ?
0 :
clamp_i(mr.material_indices[i], 0, mat_last);
tri_first_index[i] = mat_tri_offs[mat];
face_tri_offsets[i] = mat_tri_offs[mat];
mat_tri_offs[mat] += mr.faces[i].size() - 2;
}
else {
tri_first_index[i] = -1;
face_tri_offsets[i] = -1;
}
}
}
@ -311,7 +311,7 @@ static void mesh_render_data_faces_sorted_build(MeshRenderData &mr, MeshBufferCa
static void mesh_render_data_faces_sorted_ensure(MeshRenderData &mr, MeshBufferCache &cache)
{
if (!cache.face_sorted.tri_first_index.is_empty()) {
if (!cache.face_sorted.face_tri_offsets.is_empty()) {
return;
}
mesh_render_data_faces_sorted_build(mr, cache);
@ -513,7 +513,7 @@ void mesh_render_data_update_normals(MeshRenderData &mr, const eMRDataType data_
face_normals = reinterpret_cast<const float(*)[3]>(mr.bm_face_normals.data());
}
mr.bm_loop_normals.reinitialize(mr.loop_len);
mr.bm_loop_normals.reinitialize(mr.corners_num);
const int clnors_offset = CustomData_get_offset(&mr.bm->ldata, CD_CUSTOMLOOPNORMAL);
BM_loops_calc_normal_vcos(mr.bm,
vert_coords,
@ -552,7 +552,7 @@ MeshRenderData *mesh_render_data_create(Object *object,
{
MeshRenderData *mr = MEM_new<MeshRenderData>(__func__);
mr->toolsettings = ts;
mr->mat_len = mesh_render_mat_len_get(object, mesh);
mr->materials_num = mesh_render_mat_len_get(object, mesh);
mr->object_to_world = object_to_world;
@ -656,11 +656,11 @@ MeshRenderData *mesh_render_data_create(Object *object,
if (mr->extract_type != MR_EXTRACT_BMESH) {
/* Mesh */
mr->vert_len = mr->mesh->verts_num;
mr->edge_len = mr->mesh->edges_num;
mr->loop_len = mr->mesh->corners_num;
mr->face_len = mr->mesh->faces_num;
mr->tri_len = poly_to_tri_count(mr->face_len, mr->loop_len);
mr->verts_num = mr->mesh->verts_num;
mr->edges_num = mr->mesh->edges_num;
mr->faces_num = mr->mesh->faces_num;
mr->corners_num = mr->mesh->corners_num;
mr->corner_tris_num = poly_to_tri_count(mr->faces_num, mr->corners_num);
mr->vert_positions = mr->mesh->vert_positions();
mr->edges = mr->mesh->edges();
@ -699,11 +699,11 @@ MeshRenderData *mesh_render_data_create(Object *object,
/* #BMesh */
BMesh *bm = mr->bm;
mr->vert_len = bm->totvert;
mr->edge_len = bm->totedge;
mr->loop_len = bm->totloop;
mr->face_len = bm->totface;
mr->tri_len = poly_to_tri_count(mr->face_len, mr->loop_len);
mr->verts_num = bm->totvert;
mr->edges_num = bm->totedge;
mr->faces_num = bm->totface;
mr->corners_num = bm->totloop;
mr->corner_tris_num = poly_to_tri_count(mr->faces_num, mr->corners_num);
mr->normals_domain = bmesh_normals_domain(bm);
}

View File

@ -276,6 +276,7 @@ class PassBase {
/**
* Record a compute dispatch call.
*/
void dispatch(int group_len);
void dispatch(int2 group_len);
void dispatch(int3 group_len);
void dispatch(int3 *group_len);
@ -802,6 +803,12 @@ inline void PassBase<T>::draw_procedural_indirect(
/** \name Compute Dispatch Implementation
* \{ */
template<class T> inline void PassBase<T>::dispatch(int group_len)
{
BLI_assert(shader_);
create_command(Type::Dispatch).dispatch = {int3(group_len, 1, 1)};
}
template<class T> inline void PassBase<T>::dispatch(int2 group_len)
{
BLI_assert(shader_);

View File

@ -48,12 +48,17 @@ enum eMRExtractType {
struct MeshRenderData {
eMRExtractType extract_type;
int face_len, edge_len, vert_len, loop_len;
int edge_loose_len;
int vert_loose_len;
int loop_loose_len;
int tri_len;
int mat_len;
int verts_num;
int edges_num;
int faces_num;
int corners_num;
int loose_edges_num;
int loose_verts_num;
int loose_indices_num;
int corner_tris_num;
int materials_num;
bool use_hide;
bool use_subsurf_fdots;
@ -91,6 +96,7 @@ struct MeshRenderData {
OffsetIndices<int> faces;
Span<int> corner_verts;
Span<int> corner_edges;
BMVert *eve_act;
BMEdge *eed_act;
BMFace *efa_act;

View File

@ -28,7 +28,7 @@ static void extract_edituv_tris_init(const MeshRenderData &mr,
void *tls_data)
{
MeshExtract_EditUvElem_Data *data = static_cast<MeshExtract_EditUvElem_Data *>(tls_data);
GPU_indexbuf_init(&data->elb, GPU_PRIM_TRIS, mr.tri_len, mr.loop_len);
GPU_indexbuf_init(&data->elb, GPU_PRIM_TRIS, mr.corner_tris_num, mr.corners_num);
data->sync_selection = (mr.toolsettings->uv_flag & UV_SYNC_SELECTION) != 0;
}
@ -172,7 +172,7 @@ static void extract_edituv_lines_init(const MeshRenderData &mr,
void *tls_data)
{
MeshExtract_EditUvElem_Data *data = static_cast<MeshExtract_EditUvElem_Data *>(tls_data);
GPU_indexbuf_init(&data->elb, GPU_PRIM_LINES, mr.loop_len, mr.loop_len);
GPU_indexbuf_init(&data->elb, GPU_PRIM_LINES, mr.corners_num, mr.corners_num);
data->sync_selection = (mr.toolsettings->uv_flag & UV_SYNC_SELECTION) != 0;
}
@ -354,7 +354,7 @@ static void extract_edituv_points_init(const MeshRenderData &mr,
void *tls_data)
{
MeshExtract_EditUvElem_Data *data = static_cast<MeshExtract_EditUvElem_Data *>(tls_data);
GPU_indexbuf_init(&data->elb, GPU_PRIM_POINTS, mr.loop_len, mr.loop_len);
GPU_indexbuf_init(&data->elb, GPU_PRIM_POINTS, mr.corners_num, mr.corners_num);
data->sync_selection = (mr.toolsettings->uv_flag & UV_SYNC_SELECTION) != 0;
}
@ -508,7 +508,7 @@ static void extract_edituv_fdots_init(const MeshRenderData &mr,
void *tls_data)
{
MeshExtract_EditUvElem_Data *data = static_cast<MeshExtract_EditUvElem_Data *>(tls_data);
GPU_indexbuf_init(&data->elb, GPU_PRIM_POINTS, mr.face_len, mr.face_len);
GPU_indexbuf_init(&data->elb, GPU_PRIM_POINTS, mr.faces_num, mr.faces_num);
data->sync_selection = (mr.toolsettings->uv_flag & UV_SYNC_SELECTION) != 0;
}

View File

@ -21,7 +21,7 @@ static void extract_fdots_init(const MeshRenderData &mr,
void *tls_data)
{
GPUIndexBufBuilder *elb = static_cast<GPUIndexBufBuilder *>(tls_data);
GPU_indexbuf_init(elb, GPU_PRIM_POINTS, mr.face_len, mr.face_len);
GPU_indexbuf_init(elb, GPU_PRIM_POINTS, mr.faces_num, mr.faces_num);
}
static void extract_fdots_iter_face_bm(const MeshRenderData & /*mr*/,

View File

@ -49,8 +49,8 @@ static void extract_lines_init(const MeshRenderData &mr,
/* Put loose edges at the end. */
GPU_indexbuf_init(&data->elb,
GPU_PRIM_LINES,
mr.edge_len + mr.edge_loose_len,
mr.loop_len + mr.loop_loose_len);
mr.edges_num + mr.loose_edges_num,
mr.corners_num + mr.loose_indices_num);
if (mr.extract_type == MR_EXTRACT_MESH) {
data->optimal_display_edges = mr.mesh->runtime->subsurf_optimal_display_edges;
@ -123,9 +123,9 @@ static void extract_lines_iter_loose_edge_bm(const MeshRenderData &mr,
{
MeshExtract_LinesData *data = static_cast<MeshExtract_LinesData *>(tls_data);
GPUIndexBufBuilder *elb = &data->elb;
const int l_index_offset = mr.edge_len + loose_edge_i;
const int l_index_offset = mr.edges_num + loose_edge_i;
if (!BM_elem_flag_test(eed, BM_ELEM_HIDDEN)) {
const int l_index = mr.loop_len + loose_edge_i * 2;
const int l_index = mr.corners_num + loose_edge_i * 2;
GPU_indexbuf_set_line_verts(elb, l_index_offset, l_index, l_index + 1);
}
else {
@ -142,10 +142,10 @@ static void extract_lines_iter_loose_edge_mesh(const MeshRenderData &mr,
{
MeshExtract_LinesData *data = static_cast<MeshExtract_LinesData *>(tls_data);
GPUIndexBufBuilder *elb = &data->elb;
const int l_index_offset = mr.edge_len + loose_edge_i;
const int l_index_offset = mr.edges_num + loose_edge_i;
const int e_index = mr.loose_edges[loose_edge_i];
if (is_edge_visible(data, e_index)) {
const int l_index = mr.loop_len + loose_edge_i * 2;
const int l_index = mr.corners_num + loose_edge_i * 2;
GPU_indexbuf_set_line_verts(elb, l_index_offset, l_index, l_index + 1);
}
else {
@ -302,8 +302,8 @@ static void extract_lines_loose_subbuffer(const MeshRenderData &mr, MeshBatchCac
{
BLI_assert(cache.final.buff.ibo.lines);
/* Multiply by 2 because these are edges indices. */
const int start = mr.edge_len * 2;
const int len = mr.edge_loose_len * 2;
const int start = mr.edges_num * 2;
const int len = mr.loose_edges_num * 2;
GPU_indexbuf_create_subrange_in_place(
cache.final.buff.ibo.lines_loose, cache.final.buff.ibo.lines, start, len);
cache.no_loose_wire = (len == 0);

View File

@ -54,10 +54,10 @@ static void extract_lines_adjacency_init(const MeshRenderData &mr,
/* Similar to poly_to_tri_count().
* There is always (loop + triangle - 1) edges inside a face.
* Accumulate for all faces and you get : */
uint tess_edge_len = mr.loop_len + mr.tri_len - mr.face_len;
uint tess_edge_len = mr.corners_num + mr.corner_tris_num - mr.faces_num;
MeshExtract_LineAdjacency_Data *data = static_cast<MeshExtract_LineAdjacency_Data *>(tls_data);
line_adjacency_data_init(data, mr.vert_len, mr.loop_len, tess_edge_len);
line_adjacency_data_init(data, mr.verts_num, mr.corners_num, tess_edge_len);
}
BLI_INLINE void lines_adjacency_triangle(

View File

@ -33,8 +33,8 @@ static void extract_lines_paint_mask_init(const MeshRenderData &mr,
void *tls_data)
{
MeshExtract_LinePaintMask_Data *data = static_cast<MeshExtract_LinePaintMask_Data *>(tls_data);
data->select_map = BLI_BITMAP_NEW(mr.edge_len, __func__);
GPU_indexbuf_init(&data->elb, GPU_PRIM_LINES, mr.edge_len, mr.loop_len);
data->select_map = BLI_BITMAP_NEW(mr.edges_num, __func__);
GPU_indexbuf_init(&data->elb, GPU_PRIM_LINES, mr.edges_num, mr.corners_num);
}
static void extract_lines_paint_mask_iter_face_mesh(const MeshRenderData &mr,
@ -93,7 +93,7 @@ static void extract_lines_paint_mask_init_subdiv(const DRWSubdivCache &subdiv_ca
void *tls_data)
{
MeshExtract_LinePaintMask_Data *data = static_cast<MeshExtract_LinePaintMask_Data *>(tls_data);
data->select_map = BLI_BITMAP_NEW(mr.edge_len, __func__);
data->select_map = BLI_BITMAP_NEW(mr.edges_num, __func__);
GPU_indexbuf_init(&data->elb,
GPU_PRIM_LINES,
subdiv_cache.num_subdiv_edges,

View File

@ -23,7 +23,7 @@ static void extract_points_init(const MeshRenderData &mr,
void *tls_data)
{
GPUIndexBufBuilder *elb = static_cast<GPUIndexBufBuilder *>(tls_data);
GPU_indexbuf_init(elb, GPU_PRIM_POINTS, mr.vert_len, mr.loop_len + mr.loop_loose_len);
GPU_indexbuf_init(elb, GPU_PRIM_POINTS, mr.verts_num, mr.corners_num + mr.loose_indices_num);
}
BLI_INLINE void vert_set_bm(GPUIndexBufBuilder *elb, const BMVert *eve, int l_index)
@ -83,8 +83,8 @@ static void extract_points_iter_loose_edge_bm(const MeshRenderData &mr,
void *_userdata)
{
GPUIndexBufBuilder *elb = static_cast<GPUIndexBufBuilder *>(_userdata);
vert_set_bm(elb, eed->v1, mr.loop_len + (loose_edge_i * 2));
vert_set_bm(elb, eed->v2, mr.loop_len + (loose_edge_i * 2) + 1);
vert_set_bm(elb, eed->v1, mr.corners_num + (loose_edge_i * 2));
vert_set_bm(elb, eed->v2, mr.corners_num + (loose_edge_i * 2) + 1);
}
static void extract_points_iter_loose_edge_mesh(const MeshRenderData &mr,
@ -93,8 +93,8 @@ static void extract_points_iter_loose_edge_mesh(const MeshRenderData &mr,
void *_userdata)
{
GPUIndexBufBuilder *elb = static_cast<GPUIndexBufBuilder *>(_userdata);
vert_set_mesh(elb, mr, edge[0], mr.loop_len + (loose_edge_i * 2));
vert_set_mesh(elb, mr, edge[1], mr.loop_len + (loose_edge_i * 2) + 1);
vert_set_mesh(elb, mr, edge[0], mr.corners_num + (loose_edge_i * 2));
vert_set_mesh(elb, mr, edge[1], mr.corners_num + (loose_edge_i * 2) + 1);
}
static void extract_points_iter_loose_vert_bm(const MeshRenderData &mr,
@ -103,7 +103,7 @@ static void extract_points_iter_loose_vert_bm(const MeshRenderData &mr,
void *_userdata)
{
GPUIndexBufBuilder *elb = static_cast<GPUIndexBufBuilder *>(_userdata);
const int offset = mr.loop_len + (mr.edge_loose_len * 2);
const int offset = mr.corners_num + (mr.loose_edges_num * 2);
vert_set_bm(elb, eve, offset + loose_vert_i);
}
@ -112,7 +112,7 @@ static void extract_points_iter_loose_vert_mesh(const MeshRenderData &mr,
void *_userdata)
{
GPUIndexBufBuilder *elb = static_cast<GPUIndexBufBuilder *>(_userdata);
const int offset = mr.loop_len + (mr.edge_loose_len * 2);
const int offset = mr.corners_num + (mr.loose_edges_num * 2);
vert_set_mesh(elb, mr, mr.loose_verts[loose_vert_i], offset + loose_vert_i);
}
@ -142,7 +142,7 @@ static void extract_points_init_subdiv(const DRWSubdivCache &subdiv_cache,
GPUIndexBufBuilder *elb = static_cast<GPUIndexBufBuilder *>(data);
GPU_indexbuf_init(elb,
GPU_PRIM_POINTS,
mr.vert_len,
mr.verts_num,
subdiv_cache.num_subdiv_loops + subdiv_cache.loose_geom.loop_len);
}
@ -210,8 +210,8 @@ static void extract_points_loose_geom_subdiv(const DRWSubdivCache &subdiv_cache,
void *data)
{
const DRWSubdivLooseGeom &loose_geom = subdiv_cache.loose_geom;
const int loop_loose_len = loose_geom.loop_len;
if (loop_loose_len == 0) {
const int loose_indices_num = loose_geom.loop_len;
if (loose_indices_num == 0) {
return;
}

View File

@ -33,7 +33,7 @@ static void extract_tris_init(const MeshRenderData &mr,
void *tls_data)
{
GPUIndexBufBuilder *elb = static_cast<GPUIndexBufBuilder *>(tls_data);
GPU_indexbuf_init(elb, GPU_PRIM_TRIS, mr.face_sorted->visible_tri_len, mr.loop_len);
GPU_indexbuf_init(elb, GPU_PRIM_TRIS, mr.face_sorted->visible_tris_num, mr.corners_num);
}
static void extract_tris_iter_face_bm(const MeshRenderData &mr,
@ -41,8 +41,8 @@ static void extract_tris_iter_face_bm(const MeshRenderData &mr,
const int f_index,
void *_data)
{
int tri_first_index = mr.face_sorted->tri_first_index[f_index];
if (tri_first_index == -1) {
int tri_offset = mr.face_sorted->face_tri_offsets[f_index];
if (tri_offset == -1) {
return;
}
@ -53,7 +53,7 @@ static void extract_tris_iter_face_bm(const MeshRenderData &mr,
int tri_len = f->len - 2;
for (int offs = 0; offs < tri_len; offs++) {
BMLoop **elt = looptris[tri_first_index_real + offs];
int tri_index = tri_first_index + offs;
int tri_index = tri_offset + offs;
GPU_indexbuf_set_tri_verts(elb,
tri_index,
BM_elem_index_get(elt[0]),
@ -66,8 +66,8 @@ static void extract_tris_iter_face_mesh(const MeshRenderData &mr,
const int face_index,
void *_data)
{
int tri_first_index = mr.face_sorted->tri_first_index[face_index];
if (tri_first_index == -1) {
int tri_offset = mr.face_sorted->face_tri_offsets[face_index];
if (tri_offset == -1) {
return;
}
@ -79,7 +79,7 @@ static void extract_tris_iter_face_mesh(const MeshRenderData &mr,
int tri_len = face.size() - 2;
for (int offs = 0; offs < tri_len; offs++) {
const int3 &tri = mr.corner_tris[tri_first_index_real + offs];
int tri_index = tri_first_index + offs;
int tri_index = tri_offset + offs;
GPU_indexbuf_set_tri_verts(elb, tri_index, tri[0], tri[1], tri[2]);
}
}
@ -97,13 +97,13 @@ static void extract_tris_finish(const MeshRenderData &mr,
* is created before the surfaces-per-material. */
if (mr.use_final_mesh && cache.tris_per_mat) {
int mat_start = 0;
for (int i = 0; i < mr.mat_len; i++) {
for (int i = 0; i < mr.materials_num; i++) {
/* These IBOs have not been queried yet but we create them just in case they are needed
* later since they are not tracked by mesh_buffer_cache_create_requested(). */
if (cache.tris_per_mat[i] == nullptr) {
cache.tris_per_mat[i] = GPU_indexbuf_calloc();
}
const int mat_tri_len = mr.face_sorted->mat_tri_len[i];
const int mat_tri_len = mr.face_sorted->tris_num_by_material[i];
/* Multiply by 3 because these are triangle indices. */
const int start = mat_start * 3;
const int len = mat_tri_len * 3;
@ -166,7 +166,7 @@ static void extract_tris_single_mat_init(const MeshRenderData &mr,
void *tls_data)
{
GPUIndexBufBuilder *elb = static_cast<GPUIndexBufBuilder *>(tls_data);
GPU_indexbuf_init(elb, GPU_PRIM_TRIS, mr.tri_len, mr.loop_len);
GPU_indexbuf_init(elb, GPU_PRIM_TRIS, mr.corner_tris_num, mr.corners_num);
}
static void extract_tris_single_mat_iter_looptri_bm(const MeshRenderData & /*mr*/,
@ -215,14 +215,14 @@ static void extract_tris_single_mat_finish(const MeshRenderData &mr,
/* Create ibo sub-ranges. Always do this to avoid error when the standard surface batch
* is created before the surfaces-per-material. */
if (mr.use_final_mesh && cache.tris_per_mat) {
for (int i = 0; i < mr.mat_len; i++) {
for (int i = 0; i < mr.materials_num; i++) {
/* These IBOs have not been queried yet but we create them just in case they are needed
* later since they are not tracked by mesh_buffer_cache_create_requested(). */
if (cache.tris_per_mat[i] == nullptr) {
cache.tris_per_mat[i] = GPU_indexbuf_calloc();
}
/* Multiply by 3 because these are triangle indices. */
const int len = mr.tri_len * 3;
const int len = mr.corner_tris_num * 3;
GPU_indexbuf_create_subrange_in_place(cache.tris_per_mat[i], ibo, 0, len);
}
}

View File

@ -254,7 +254,7 @@ static void extract_attr_init(
{
const DRW_AttributeRequest &request = cache.attr_used.requests[index];
GPUVertBuf *vbo = static_cast<GPUVertBuf *>(buf);
init_vbo_for_attribute(mr, vbo, request, false, uint32_t(mr.loop_len));
init_vbo_for_attribute(mr, vbo, request, false, uint32_t(mr.corners_num));
extract_attribute(mr, request, *vbo);
}
@ -357,9 +357,9 @@ static void extract_mesh_attr_viewer_init(const MeshRenderData &mr,
}
GPU_vertbuf_init_with_format(vbo, &format);
GPU_vertbuf_data_alloc(vbo, mr.loop_len);
GPU_vertbuf_data_alloc(vbo, mr.corners_num);
MutableSpan<ColorGeometry4f> attr{static_cast<ColorGeometry4f *>(GPU_vertbuf_get_data(vbo)),
mr.loop_len};
mr.corners_num};
const StringRefNull attr_name = ".viewer";
const bke::AttributeAccessor attributes = mr.mesh->attributes();

View File

@ -69,15 +69,15 @@ static void extract_edge_fac_init(const MeshRenderData &mr,
}
GPU_vertbuf_init_with_format(vbo, &format);
GPU_vertbuf_data_alloc(vbo, mr.loop_len + mr.loop_loose_len);
GPU_vertbuf_data_alloc(vbo, mr.corners_num + mr.loose_indices_num);
MeshExtract_EdgeFac_Data *data = static_cast<MeshExtract_EdgeFac_Data *>(tls_data);
if (mr.extract_type == MR_EXTRACT_MESH) {
data->use_edge_render = !mr.mesh->runtime->subsurf_optimal_display_edges.is_empty();
data->edge_loop_count = MEM_cnew_array<uint8_t>(mr.edge_len, __func__);
data->edge_loop_count = MEM_cnew_array<uint8_t>(mr.edges_num, __func__);
data->edge_pdata = (MEdgeDataPrev *)MEM_malloc_arrayN(
mr.edge_len, sizeof(MEdgeDataPrev), __func__);
mr.edges_num, sizeof(MEdgeDataPrev), __func__);
}
else {
/* HACK to bypass non-manifold check in mesh_edge_fac_finish(). */
@ -166,8 +166,8 @@ static void extract_edge_fac_iter_loose_edge_bm(const MeshRenderData &mr,
void *_data)
{
MeshExtract_EdgeFac_Data *data = static_cast<MeshExtract_EdgeFac_Data *>(_data);
data->vbo_data[mr.loop_len + (loose_edge_i * 2) + 0] = 0;
data->vbo_data[mr.loop_len + (loose_edge_i * 2) + 1] = 0;
data->vbo_data[mr.corners_num + (loose_edge_i * 2) + 0] = 0;
data->vbo_data[mr.corners_num + (loose_edge_i * 2) + 1] = 0;
}
static void extract_edge_fac_iter_loose_edge_mesh(const MeshRenderData &mr,
@ -177,8 +177,8 @@ static void extract_edge_fac_iter_loose_edge_mesh(const MeshRenderData &mr,
{
MeshExtract_EdgeFac_Data *data = static_cast<MeshExtract_EdgeFac_Data *>(_data);
data->vbo_data[mr.loop_len + loose_edge_i * 2 + 0] = 0;
data->vbo_data[mr.loop_len + loose_edge_i * 2 + 1] = 0;
data->vbo_data[mr.corners_num + loose_edge_i * 2 + 0] = 0;
data->vbo_data[mr.corners_num + loose_edge_i * 2 + 1] = 0;
}
static void extract_edge_fac_finish(const MeshRenderData &mr,
@ -201,7 +201,7 @@ static void extract_edge_fac_finish(const MeshRenderData &mr,
data->vbo_data = static_cast<uchar *>(GPU_vertbuf_steal_data(vbo));
GPU_vertbuf_clear(vbo);
int buf_len = mr.loop_len + mr.loop_loose_len;
int buf_len = mr.corners_num + mr.loose_indices_num;
GPU_vertbuf_init_with_format(vbo, &format);
GPU_vertbuf_data_alloc(vbo, buf_len);

View File

@ -123,7 +123,7 @@ static void extract_edit_data_init(const MeshRenderData &mr,
GPUVertBuf *vbo = static_cast<GPUVertBuf *>(buf);
GPUVertFormat *format = get_edit_data_format();
GPU_vertbuf_init_with_format(vbo, format);
GPU_vertbuf_data_alloc(vbo, mr.loop_len + mr.loop_loose_len);
GPU_vertbuf_data_alloc(vbo, mr.corners_num + mr.loose_indices_num);
EditLoopData *vbo_data = (EditLoopData *)GPU_vertbuf_get_data(vbo);
*(EditLoopData **)tls_data = vbo_data;
}
@ -178,7 +178,7 @@ static void extract_edit_data_iter_loose_edge_bm(const MeshRenderData &mr,
void *_data)
{
EditLoopData *vbo_data = *(EditLoopData **)_data;
EditLoopData *data = vbo_data + mr.loop_len + (loose_edge_i * 2);
EditLoopData *data = vbo_data + mr.corners_num + (loose_edge_i * 2);
memset(data, 0x0, sizeof(*data) * 2);
mesh_render_data_edge_flag(mr, eed, &data[0]);
data[1] = data[0];
@ -192,7 +192,7 @@ static void extract_edit_data_iter_loose_edge_mesh(const MeshRenderData &mr,
void *_data)
{
EditLoopData *vbo_data = *(EditLoopData **)_data;
EditLoopData *data = vbo_data + mr.loop_len + loose_edge_i * 2;
EditLoopData *data = vbo_data + mr.corners_num + loose_edge_i * 2;
memset(data, 0x0, sizeof(*data) * 2);
const int e_index = mr.loose_edges[loose_edge_i];
BMEdge *eed = bm_original_edge_get(mr, e_index);
@ -216,7 +216,7 @@ static void extract_edit_data_iter_loose_vert_bm(const MeshRenderData &mr,
void *_data)
{
EditLoopData *vbo_data = *(EditLoopData **)_data;
const int offset = mr.loop_len + (mr.edge_loose_len * 2);
const int offset = mr.corners_num + (mr.loose_edges_num * 2);
EditLoopData *data = vbo_data + offset + loose_vert_i;
memset(data, 0x0, sizeof(*data));
mesh_render_data_vert_flag(mr, eve, data);
@ -227,7 +227,7 @@ static void extract_edit_data_iter_loose_vert_mesh(const MeshRenderData &mr,
void *_data)
{
EditLoopData *vbo_data = *(EditLoopData **)_data;
const int offset = mr.loop_len + (mr.edge_loose_len * 2);
const int offset = mr.corners_num + (mr.loose_edges_num * 2);
EditLoopData *data = vbo_data + offset + loose_vert_i;
memset(data, 0x0, sizeof(*data));

View File

@ -51,7 +51,7 @@ static void extract_edituv_data_init(const MeshRenderData &mr,
{
GPUVertBuf *vbo = static_cast<GPUVertBuf *>(buf);
MeshExtract_EditUVData_Data *data = static_cast<MeshExtract_EditUVData_Data *>(tls_data);
extract_edituv_data_init_common(mr, vbo, data, mr.loop_len);
extract_edituv_data_init_common(mr, vbo, data, mr.corners_num);
}
static void extract_edituv_data_iter_face_bm(const MeshRenderData &mr,

View File

@ -100,7 +100,7 @@ static void extract_edituv_stretch_angle_init(const MeshRenderData &mr,
}
GPU_vertbuf_init_with_format(vbo, &format);
GPU_vertbuf_data_alloc(vbo, mr.loop_len);
GPU_vertbuf_data_alloc(vbo, mr.corners_num);
MeshExtract_StretchAngle_Data *data = static_cast<MeshExtract_StretchAngle_Data *>(tls_data);
data->vbo_data = (UVStretchAngle *)GPU_vertbuf_get_data(vbo);

View File

@ -34,7 +34,7 @@ static void extract_edituv_stretch_area_init(const MeshRenderData &mr,
}
GPU_vertbuf_init_with_format(vbo, &format);
GPU_vertbuf_data_alloc(vbo, mr.loop_len);
GPU_vertbuf_data_alloc(vbo, mr.corners_num);
}
BLI_INLINE float area_ratio_get(float area, float uvarea)
@ -77,7 +77,7 @@ static void compute_area_ratio(const MeshRenderData &mr,
BLI_assert(mr.extract_type == MR_EXTRACT_MESH);
const float2 *uv_data = (const float2 *)CustomData_get_layer(&mr.mesh->corner_data,
CD_PROP_FLOAT2);
for (int face_index = 0; face_index < mr.face_len; face_index++) {
for (int face_index = 0; face_index < mr.faces_num; face_index++) {
const IndexRange face = mr.faces[face_index];
const float area = bke::mesh::face_area_calc(mr.vert_positions, mr.corner_verts.slice(face));
float uvarea = area_poly_v2(reinterpret_cast<const float(*)[2]>(&uv_data[face.start()]),
@ -98,7 +98,7 @@ static void extract_edituv_stretch_area_finish(const MeshRenderData &mr,
void * /*data*/)
{
GPUVertBuf *vbo = static_cast<GPUVertBuf *>(buf);
float *area_ratio = static_cast<float *>(MEM_mallocN(sizeof(float) * mr.face_len, __func__));
float *area_ratio = static_cast<float *>(MEM_mallocN(sizeof(float) * mr.faces_num, __func__));
compute_area_ratio(mr, area_ratio, cache.tot_area, cache.tot_uv_area);
/* Copy face data for each loop. */
@ -116,7 +116,7 @@ static void extract_edituv_stretch_area_finish(const MeshRenderData &mr,
}
else {
BLI_assert(mr.extract_type == MR_EXTRACT_MESH);
for (int face_index = 0; face_index < mr.face_len; face_index++) {
for (int face_index = 0; face_index < mr.faces_num; face_index++) {
for (const int l_index : mr.faces[face_index]) {
loop_stretch[l_index] = area_ratio[face_index];
}
@ -148,7 +148,7 @@ static void extract_edituv_stretch_area_init_subdiv(const DRWSubdivCache &subdiv
/* We use the same format as we just copy data around. */
GPU_vertbuf_init_with_format(coarse_data, &format);
GPU_vertbuf_data_alloc(coarse_data, mr.loop_len);
GPU_vertbuf_data_alloc(coarse_data, mr.corners_num);
compute_area_ratio(mr,
static_cast<float *>(GPU_vertbuf_get_data(coarse_data)),

View File

@ -33,7 +33,7 @@ static void extract_fdots_edituv_data_init(const MeshRenderData &mr,
}
GPU_vertbuf_init_with_format(vbo, &format);
GPU_vertbuf_data_alloc(vbo, mr.face_len);
GPU_vertbuf_data_alloc(vbo, mr.faces_num);
MeshExtract_EditUVFdotData_Data *data = static_cast<MeshExtract_EditUVFdotData_Data *>(tls_data);
data->vbo_data = (EditLoopData *)GPU_vertbuf_get_data(vbo);

View File

@ -31,7 +31,7 @@ static void extract_fdots_nor_init(const MeshRenderData &mr,
}
GPU_vertbuf_init_with_format(vbo, &format);
GPU_vertbuf_data_alloc(vbo, mr.face_len);
GPU_vertbuf_data_alloc(vbo, mr.faces_num);
}
static void extract_fdots_nor_finish(const MeshRenderData &mr,
@ -46,7 +46,7 @@ static void extract_fdots_nor_finish(const MeshRenderData &mr,
/* Quicker than doing it for each loop. */
if (mr.extract_type == MR_EXTRACT_BMESH) {
for (int f = 0; f < mr.face_len; f++) {
for (int f = 0; f < mr.faces_num; f++) {
efa = BM_face_at_index(mr.bm, f);
const bool is_face_hidden = BM_elem_flag_test(efa, BM_ELEM_HIDDEN);
if (is_face_hidden || (mr.p_origindex && mr.p_origindex[f] == ORIGINDEX_NONE)) {
@ -63,7 +63,7 @@ static void extract_fdots_nor_finish(const MeshRenderData &mr,
}
}
else {
for (int f = 0; f < mr.face_len; f++) {
for (int f = 0; f < mr.faces_num; f++) {
efa = bm_original_face_get(mr, f);
const bool is_face_hidden = efa && BM_elem_flag_test(efa, BM_ELEM_HIDDEN);
if (is_face_hidden || (mr.p_origindex && mr.p_origindex[f] == ORIGINDEX_NONE)) {
@ -111,7 +111,7 @@ static void extract_fdots_nor_hq_init(const MeshRenderData &mr,
}
GPU_vertbuf_init_with_format(vbo, &format);
GPU_vertbuf_data_alloc(vbo, mr.face_len);
GPU_vertbuf_data_alloc(vbo, mr.faces_num);
}
static void extract_fdots_nor_hq_finish(const MeshRenderData &mr,
@ -126,7 +126,7 @@ static void extract_fdots_nor_hq_finish(const MeshRenderData &mr,
/* Quicker than doing it for each loop. */
if (mr.extract_type == MR_EXTRACT_BMESH) {
for (int f = 0; f < mr.face_len; f++) {
for (int f = 0; f < mr.faces_num; f++) {
efa = BM_face_at_index(mr.bm, f);
const bool is_face_hidden = BM_elem_flag_test(efa, BM_ELEM_HIDDEN);
if (is_face_hidden || (mr.p_origindex && mr.p_origindex[f] == ORIGINDEX_NONE)) {
@ -143,7 +143,7 @@ static void extract_fdots_nor_hq_finish(const MeshRenderData &mr,
}
}
else {
for (int f = 0; f < mr.face_len; f++) {
for (int f = 0; f < mr.faces_num; f++) {
efa = bm_original_face_get(mr, f);
const bool is_face_hidden = efa && BM_elem_flag_test(efa, BM_ELEM_HIDDEN);
if (is_face_hidden || (mr.p_origindex && mr.p_origindex[f] == ORIGINDEX_NONE)) {

View File

@ -44,7 +44,7 @@ static void extract_fdots_pos_init(const MeshRenderData &mr,
GPUVertBuf *vbo = static_cast<GPUVertBuf *>(buf);
GPUVertFormat *format = get_fdots_pos_format();
GPU_vertbuf_init_with_format(vbo, format);
GPU_vertbuf_data_alloc(vbo, mr.face_len);
GPU_vertbuf_data_alloc(vbo, mr.faces_num);
void *vbo_data = GPU_vertbuf_get_data(vbo);
*(float(**)[3])tls_data = static_cast<float(*)[3]>(vbo_data);
}

View File

@ -36,11 +36,11 @@ static void extract_fdots_uv_init(const MeshRenderData &mr,
}
GPU_vertbuf_init_with_format(vbo, &format);
GPU_vertbuf_data_alloc(vbo, mr.face_len);
GPU_vertbuf_data_alloc(vbo, mr.faces_num);
if (!mr.use_subsurf_fdots) {
/* Clear so we can accumulate on it. */
memset(GPU_vertbuf_get_data(vbo), 0x0, mr.face_len * GPU_vertbuf_get_format(vbo)->stride);
memset(GPU_vertbuf_get_data(vbo), 0x0, mr.faces_num * GPU_vertbuf_get_format(vbo)->stride);
}
MeshExtract_FdotUV_Data *data = static_cast<MeshExtract_FdotUV_Data *>(tls_data);

View File

@ -139,7 +139,7 @@ static void extract_paint_overlay_flags(const MeshRenderData &mr, MutableSpan<GP
}
if (mr.edit_bmesh && mr.v_origindex) {
const Span<int> corner_verts = mr.corner_verts;
const Span<int> orig_indices(mr.v_origindex, mr.vert_len);
const Span<int> orig_indices(mr.v_origindex, mr.verts_num);
for (const int face : range) {
for (const int corner : faces[face]) {
if (orig_indices[corner_verts[corner]] == ORIGINDEX_NONE) {
@ -163,10 +163,11 @@ static void extract_lnor_init(const MeshRenderData &mr,
GPU_vertformat_alias_add(&format, "lnor");
}
GPU_vertbuf_init_with_format(vbo, &format);
GPU_vertbuf_data_alloc(vbo, mr.loop_len);
GPU_vertbuf_data_alloc(vbo, mr.corners_num);
if (mr.extract_type == MR_EXTRACT_MESH) {
MutableSpan vbo_data(static_cast<GPUPackedNormal *>(GPU_vertbuf_get_data(vbo)), mr.loop_len);
MutableSpan vbo_data(static_cast<GPUPackedNormal *>(GPU_vertbuf_get_data(vbo)),
mr.corners_num);
extract_normals_mesh(mr, vbo_data);
extract_paint_overlay_flags(mr, vbo_data);
}
@ -258,10 +259,10 @@ static void extract_lnor_hq_init(const MeshRenderData &mr,
GPU_vertformat_alias_add(&format, "lnor");
}
GPU_vertbuf_init_with_format(vbo, &format);
GPU_vertbuf_data_alloc(vbo, mr.loop_len);
GPU_vertbuf_data_alloc(vbo, mr.corners_num);
if (mr.extract_type == MR_EXTRACT_MESH) {
MutableSpan vbo_data(static_cast<short4 *>(GPU_vertbuf_get_data(vbo)), mr.loop_len);
MutableSpan vbo_data(static_cast<short4 *>(GPU_vertbuf_get_data(vbo)), mr.corners_num);
extract_normals_mesh(mr, vbo_data);
extract_paint_overlay_flags(mr, vbo_data);
}

View File

@ -38,7 +38,7 @@ static void extract_mesh_analysis_init(const MeshRenderData &mr,
}
GPU_vertbuf_init_with_format(vbo, &format);
GPU_vertbuf_data_alloc(vbo, mr.loop_len);
GPU_vertbuf_data_alloc(vbo, mr.corners_num);
}
static void axis_from_enum_v3(float v[3], const char axis)
@ -143,7 +143,7 @@ static void statvis_calc_thickness(const MeshRenderData &mr, float *r_thickness)
{
const float eps_offset = 0.00002f; /* values <= 0.00001 give errors */
/* cheating to avoid another allocation */
float *face_dists = r_thickness + (mr.loop_len - mr.face_len);
float *face_dists = r_thickness + (mr.corners_num - mr.faces_num);
BMEditMesh *em = mr.edit_bmesh;
const float scale = 1.0f / mat4_to_scale(mr.object_to_world.ptr());
const MeshStatVis *statvis = &mr.toolsettings->statvis;
@ -155,7 +155,7 @@ static void statvis_calc_thickness(const MeshRenderData &mr, float *r_thickness)
BLI_assert(samples <= 32);
BLI_assert(min <= max);
copy_vn_fl(face_dists, mr.face_len, max);
copy_vn_fl(face_dists, mr.faces_num, max);
BLI_jitter_init(jit_ofs, samples);
for (int j = 0; j < samples; j++) {
@ -168,7 +168,7 @@ static void statvis_calc_thickness(const MeshRenderData &mr, float *r_thickness)
BMBVHTree *bmtree = BKE_bmbvh_new_from_editmesh(em, 0, nullptr, false);
BMLoop *(*looptris)[3] = em->looptris;
for (int i = 0; i < mr.tri_len; i++) {
for (int i = 0; i < mr.corner_tris_num; i++) {
BMLoop **ltri = looptris[i];
const int index = BM_elem_index_get(ltri[0]->f);
const float *cos[3] = {
@ -307,7 +307,7 @@ static void statvis_calc_intersect(const MeshRenderData &mr, float *r_intersect)
{
BMEditMesh *em = mr.edit_bmesh;
for (int l_index = 0; l_index < mr.loop_len; l_index++) {
for (int l_index = 0; l_index < mr.corners_num; l_index++) {
r_intersect[l_index] = -1.0f;
}
@ -504,8 +504,8 @@ static void statvis_calc_sharp(const MeshRenderData &mr, float *r_sharp)
const float minmax_irange = 1.0f / (max - min);
/* Can we avoid this extra allocation? */
float *vert_angles = (float *)MEM_mallocN(sizeof(float) * mr.vert_len, __func__);
copy_vn_fl(vert_angles, mr.vert_len, -M_PI);
float *vert_angles = (float *)MEM_mallocN(sizeof(float) * mr.verts_num, __func__);
copy_vn_fl(vert_angles, mr.verts_num, -M_PI);
if (mr.extract_type == MR_EXTRACT_BMESH) {
BMIter iter;
@ -535,9 +535,9 @@ static void statvis_calc_sharp(const MeshRenderData &mr, float *r_sharp)
/* first assign float values to verts */
Map<OrderedEdge, int> eh;
eh.reserve(mr.edge_len);
eh.reserve(mr.edges_num);
for (int face_index = 0; face_index < mr.face_len; face_index++) {
for (int face_index = 0; face_index < mr.faces_num; face_index++) {
const IndexRange face = mr.faces[face_index];
for (const int corner : face) {
const int vert_curr = mr.corner_verts[corner];
@ -580,7 +580,7 @@ static void statvis_calc_sharp(const MeshRenderData &mr, float *r_sharp)
*col2 = max_ff(*col2, angle);
}
for (int l_index = 0; l_index < mr.loop_len; l_index++) {
for (int l_index = 0; l_index < mr.corners_num; l_index++) {
const int vert = mr.corner_verts[l_index];
r_sharp[l_index] = sharp_remap(vert_angles[vert], min, max, minmax_irange);
}

View File

@ -35,7 +35,7 @@ static void extract_orco_init(const MeshRenderData &mr,
}
GPU_vertbuf_init_with_format(vbo, &format);
GPU_vertbuf_data_alloc(vbo, mr.loop_len);
GPU_vertbuf_data_alloc(vbo, mr.corners_num);
CustomData *cd_vdata = &mr.mesh->vert_data;

View File

@ -43,7 +43,7 @@ static void extract_pos_init(const MeshRenderData &mr,
GPU_vertformat_attr_add(&format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT);
}
GPU_vertbuf_init_with_format(vbo, &format);
GPU_vertbuf_data_alloc(vbo, mr.loop_len + mr.loop_loose_len);
GPU_vertbuf_data_alloc(vbo, mr.corners_num + mr.loose_indices_num);
MutableSpan vbo_data(static_cast<float3 *>(GPU_vertbuf_get_data(vbo)),
GPU_vertbuf_get_vertex_len(vbo));
@ -53,7 +53,7 @@ static void extract_pos_init(const MeshRenderData &mr,
extract_mesh_loose_edge_positions(mr.vert_positions,
mr.edges,
mr.loose_edges,
vbo_data.slice(mr.loop_len, mr.loose_edges.size() * 2));
vbo_data.slice(mr.corners_num, mr.loose_edges.size() * 2));
array_utils::gather(
mr.vert_positions, mr.loose_verts, vbo_data.take_back(mr.loose_verts.size()));
}
@ -82,7 +82,7 @@ static void extract_pos_iter_loose_edge_bm(const MeshRenderData &mr,
void *_data)
{
float3 *data = *static_cast<float3 **>(_data);
int index = mr.loop_len + loose_edge_i * 2;
int index = mr.corners_num + loose_edge_i * 2;
data[index + 0] = bm_vert_co_get(mr, eed->v1);
data[index + 1] = bm_vert_co_get(mr, eed->v2);
}
@ -93,7 +93,7 @@ static void extract_pos_iter_loose_vert_bm(const MeshRenderData &mr,
void *_data)
{
float3 *data = *static_cast<float3 **>(_data);
const int offset = mr.loop_len + (mr.edge_loose_len * 2);
const int offset = mr.corners_num + (mr.loose_edges_num * 2);
const int index = offset + loose_vert_i;
data[index] = bm_vert_co_get(mr, eve);
}
@ -120,7 +120,7 @@ static GPUVertFormat *get_custom_normals_format()
static void extract_vertex_flags(const MeshRenderData &mr, char *flags)
{
for (int i = 0; i < mr.vert_len; i++) {
for (int i = 0; i < mr.verts_num; i++) {
char *flag = &flags[i];
const bool vert_hidden = !mr.hide_vert.is_empty() && mr.hide_vert[i];
/* Flag for paint mode overlay. */
@ -159,7 +159,7 @@ static void extract_pos_init_subdiv(const DRWSubdivCache &subdiv_cache,
GPU_vertformat_attr_add(&flag_format, "flag", GPU_COMP_I32, 1, GPU_FETCH_INT);
}
GPU_vertbuf_init_with_format(flags_buffer, &flag_format);
GPU_vertbuf_data_alloc(flags_buffer, divide_ceil_u(mr.vert_len, 4));
GPU_vertbuf_data_alloc(flags_buffer, divide_ceil_u(mr.verts_num, 4));
char *flags = static_cast<char *>(GPU_vertbuf_get_data(flags_buffer));
extract_vertex_flags(mr, flags);
GPU_vertbuf_tag_dirty(flags_buffer);

View File

@ -42,7 +42,7 @@ static void extract_sculpt_data_init(const MeshRenderData &mr,
GPUVertFormat *format = get_sculpt_data_format();
GPU_vertbuf_init_with_format(vbo, format);
GPU_vertbuf_data_alloc(vbo, mr.loop_len);
GPU_vertbuf_data_alloc(vbo, mr.corners_num);
struct gpuSculptData {
uint8_t face_set_color[4];
@ -85,7 +85,7 @@ static void extract_sculpt_data_init(const MeshRenderData &mr,
const VArray<int> face_set = *attributes.lookup<int>(".sculpt_face_set",
bke::AttrDomain::Face);
for (int face_index = 0; face_index < mr.face_len; face_index++) {
for (int face_index = 0; face_index < mr.faces_num; face_index++) {
for (const int corner : mr.faces[face_index]) {
float v_mask = 0.0f;
if (mask) {

View File

@ -35,7 +35,7 @@ static void extract_select_idx_init(const MeshRenderData &mr,
void *buf,
void *tls_data)
{
extract_select_idx_init_impl(mr, mr.loop_len + mr.loop_loose_len, buf, tls_data);
extract_select_idx_init_impl(mr, mr.corners_num + mr.loose_indices_num, buf, tls_data);
}
/* TODO: Use #glVertexID to get loop index and use the data structure on the CPU to retrieve the
@ -87,8 +87,8 @@ static void extract_edge_idx_iter_loose_edge_bm(const MeshRenderData &mr,
const int loose_edge_i,
void *data)
{
(*(int32_t **)data)[mr.loop_len + loose_edge_i * 2 + 0] = BM_elem_index_get(eed);
(*(int32_t **)data)[mr.loop_len + loose_edge_i * 2 + 1] = BM_elem_index_get(eed);
(*(int32_t **)data)[mr.corners_num + loose_edge_i * 2 + 0] = BM_elem_index_get(eed);
(*(int32_t **)data)[mr.corners_num + loose_edge_i * 2 + 1] = BM_elem_index_get(eed);
}
static void extract_vert_idx_iter_loose_edge_bm(const MeshRenderData &mr,
@ -96,8 +96,8 @@ static void extract_vert_idx_iter_loose_edge_bm(const MeshRenderData &mr,
const int loose_edge_i,
void *data)
{
(*(int32_t **)data)[mr.loop_len + loose_edge_i * 2 + 0] = BM_elem_index_get(eed->v1);
(*(int32_t **)data)[mr.loop_len + loose_edge_i * 2 + 1] = BM_elem_index_get(eed->v2);
(*(int32_t **)data)[mr.corners_num + loose_edge_i * 2 + 0] = BM_elem_index_get(eed->v1);
(*(int32_t **)data)[mr.corners_num + loose_edge_i * 2 + 1] = BM_elem_index_get(eed->v2);
}
static void extract_vert_idx_iter_loose_vert_bm(const MeshRenderData &mr,
@ -105,7 +105,7 @@ static void extract_vert_idx_iter_loose_vert_bm(const MeshRenderData &mr,
const int loose_vert_i,
void *data)
{
const int offset = mr.loop_len + (mr.edge_loose_len * 2);
const int offset = mr.corners_num + (mr.loose_edges_num * 2);
(*(int32_t **)data)[offset + loose_vert_i] = BM_elem_index_get(eve);
}
@ -146,8 +146,8 @@ static void extract_edge_idx_iter_loose_edge_mesh(const MeshRenderData &mr,
{
const int e_index = mr.loose_edges[loose_edge_i];
const int e_orig = (mr.e_origindex) ? mr.e_origindex[e_index] : e_index;
(*(int32_t **)data)[mr.loop_len + loose_edge_i * 2 + 0] = e_orig;
(*(int32_t **)data)[mr.loop_len + loose_edge_i * 2 + 1] = e_orig;
(*(int32_t **)data)[mr.corners_num + loose_edge_i * 2 + 0] = e_orig;
(*(int32_t **)data)[mr.corners_num + loose_edge_i * 2 + 1] = e_orig;
}
static void extract_vert_idx_iter_loose_edge_mesh(const MeshRenderData &mr,
@ -157,15 +157,15 @@ static void extract_vert_idx_iter_loose_edge_mesh(const MeshRenderData &mr,
{
int v1_orig = (mr.v_origindex) ? mr.v_origindex[edge[0]] : edge[0];
int v2_orig = (mr.v_origindex) ? mr.v_origindex[edge[1]] : edge[1];
(*(int32_t **)data)[mr.loop_len + loose_edge_i * 2 + 0] = v1_orig;
(*(int32_t **)data)[mr.loop_len + loose_edge_i * 2 + 1] = v2_orig;
(*(int32_t **)data)[mr.corners_num + loose_edge_i * 2 + 0] = v1_orig;
(*(int32_t **)data)[mr.corners_num + loose_edge_i * 2 + 1] = v2_orig;
}
static void extract_vert_idx_iter_loose_vert_mesh(const MeshRenderData &mr,
const int loose_vert_i,
void *data)
{
const int offset = mr.loop_len + (mr.edge_loose_len * 2);
const int offset = mr.corners_num + (mr.loose_edges_num * 2);
const int v_index = mr.loose_verts[loose_vert_i];
const int v_orig = (mr.v_origindex) ? mr.v_origindex[v_index] : v_index;
@ -360,7 +360,7 @@ static void extract_fdot_idx_init(const MeshRenderData &mr,
void *buf,
void *tls_data)
{
extract_select_idx_init_impl(mr, mr.face_len, buf, tls_data);
extract_select_idx_init_impl(mr, mr.faces_num, buf, tls_data);
}
static void extract_fdot_idx_iter_face_bm(const MeshRenderData & /*mr*/,

View File

@ -80,11 +80,11 @@ static void extract_tan_init_common(const MeshRenderData &mr,
}
if (use_orco_tan && orco == nullptr) {
/* If `orco` is not available compute it ourselves */
orco_allocated = (float(*)[3])MEM_mallocN(sizeof(*orco) * mr.vert_len, __func__);
orco_allocated = (float(*)[3])MEM_mallocN(sizeof(*orco) * mr.verts_num, __func__);
if (mr.extract_type == MR_EXTRACT_BMESH) {
BMesh *bm = mr.bm;
for (int v = 0; v < mr.vert_len; v++) {
for (int v = 0; v < mr.verts_num; v++) {
const BMVert *eve = BM_vert_at_index(bm, v);
/* Exceptional case where #bm_vert_co_get can be avoided, as we want the original coords.
* not the distorted ones. */
@ -92,11 +92,11 @@ static void extract_tan_init_common(const MeshRenderData &mr,
}
}
else {
for (int v = 0; v < mr.vert_len; v++) {
for (int v = 0; v < mr.verts_num; v++) {
copy_v3_v3(orco_allocated[v], mr.vert_positions[v]);
}
}
BKE_mesh_orco_verts_transform(mr.mesh, orco_allocated, mr.vert_len, false);
BKE_mesh_orco_verts_transform(mr.mesh, orco_allocated, mr.verts_num, false);
orco = orco_allocated;
}
@ -114,7 +114,7 @@ static void extract_tan_init_common(const MeshRenderData &mr,
reinterpret_cast<const float(*)[3]>(mr.corner_normals.data()),
orco,
r_loop_data,
mr.loop_len,
mr.corners_num,
&tangent_mask);
}
else {
@ -123,7 +123,7 @@ static void extract_tan_init_common(const MeshRenderData &mr,
mr.corner_verts.data(),
mr.corner_tris.data(),
mr.corner_tri_faces.data(),
mr.tri_len,
mr.corner_tris_num,
mr.sharp_faces,
cd_ldata,
calc_active_tangent,
@ -151,7 +151,7 @@ static void extract_tan_init_common(const MeshRenderData &mr,
MEM_SAFE_FREE(orco_allocated);
int v_len = mr.loop_len;
int v_len = mr.corners_num;
if (format->attr_len == 0) {
GPU_vertformat_attr_add(format, "dummy", GPU_COMP_F32, 1, GPU_FETCH_FLOAT);
/* VBO will not be used, only allocate minimum of memory. */
@ -197,7 +197,7 @@ static void extract_tan_ex_init(const MeshRenderData &mr,
const char *name = tangent_names[i];
const float(*layer_data)[4] = (const float(*)[4])CustomData_get_layer_named(
&corner_data, CD_TANGENT, name);
for (int corner = 0; corner < mr.loop_len; corner++) {
for (int corner = 0; corner < mr.corners_num; corner++) {
normal_float_to_short_v3(*tan_data, layer_data[corner]);
(*tan_data)[3] = (layer_data[corner][3] > 0.0f) ? SHRT_MAX : SHRT_MIN;
tan_data++;
@ -206,7 +206,7 @@ static void extract_tan_ex_init(const MeshRenderData &mr,
if (use_orco_tan) {
const float(*layer_data)[4] = (const float(*)[4])CustomData_get_layer_n(
&corner_data, CD_TANGENT, 0);
for (int corner = 0; corner < mr.loop_len; corner++) {
for (int corner = 0; corner < mr.corners_num; corner++) {
normal_float_to_short_v3(*tan_data, layer_data[corner]);
(*tan_data)[3] = (layer_data[corner][3] > 0.0f) ? SHRT_MAX : SHRT_MIN;
tan_data++;
@ -219,7 +219,7 @@ static void extract_tan_ex_init(const MeshRenderData &mr,
const char *name = tangent_names[i];
const float(*layer_data)[4] = (const float(*)[4])CustomData_get_layer_named(
&corner_data, CD_TANGENT, name);
for (int corner = 0; corner < mr.loop_len; corner++) {
for (int corner = 0; corner < mr.corners_num; corner++) {
*tan_data = GPU_normal_convert_i10_v3(layer_data[corner]);
tan_data->w = (layer_data[corner][3] > 0.0f) ? 1 : -2;
tan_data++;
@ -228,7 +228,7 @@ static void extract_tan_ex_init(const MeshRenderData &mr,
if (use_orco_tan) {
const float(*layer_data)[4] = (const float(*)[4])CustomData_get_layer_n(
&corner_data, CD_TANGENT, 0);
for (int corner = 0; corner < mr.loop_len; corner++) {
for (int corner = 0; corner < mr.corners_num; corner++) {
*tan_data = GPU_normal_convert_i10_v3(layer_data[corner]);
tan_data->w = (layer_data[corner][3] > 0.0f) ? 1 : -2;
tan_data++;
@ -236,7 +236,7 @@ static void extract_tan_ex_init(const MeshRenderData &mr,
}
}
CustomData_free(&corner_data, mr.loop_len);
CustomData_free(&corner_data, mr.corners_num);
}
static void extract_tan_init(const MeshRenderData &mr,
@ -298,7 +298,7 @@ static void extract_tan_init_subdiv(const DRWSubdivCache &subdiv_cache,
const char *name = tangent_names[i];
const float(*layer_data)[4] = (const float(*)[4])CustomData_get_layer_named(
&corner_data, CD_TANGENT, name);
for (int corner = 0; corner < mr.loop_len; corner++) {
for (int corner = 0; corner < mr.corners_num; corner++) {
copy_v3_v3(*tan_data, layer_data[corner]);
(*tan_data)[3] = (layer_data[corner][3] > 0.0f) ? 1.0f : -1.0f;
tan_data++;
@ -315,7 +315,7 @@ static void extract_tan_init_subdiv(const DRWSubdivCache &subdiv_cache,
float(*tan_data)[4] = (float(*)[4])GPU_vertbuf_get_data(coarse_vbo);
const float(*layer_data)[4] = (const float(*)[4])CustomData_get_layer_n(
&corner_data, CD_TANGENT, 0);
for (int corner = 0; corner < mr.loop_len; corner++) {
for (int corner = 0; corner < mr.corners_num; corner++) {
copy_v3_v3(*tan_data, layer_data[corner]);
(*tan_data)[3] = (layer_data[corner][3] > 0.0f) ? 1.0f : -1.0f;
tan_data++;
@ -329,7 +329,7 @@ static void extract_tan_init_subdiv(const DRWSubdivCache &subdiv_cache,
subdiv_cache, coarse_vbo, dst_buffer, GPU_COMP_F32, 4, dst_offset);
}
CustomData_free(&corner_data, mr.loop_len);
CustomData_free(&corner_data, mr.corners_num);
GPU_vertbuf_discard(coarse_vbo);
}

View File

@ -91,7 +91,7 @@ static void extract_uv_init(const MeshRenderData &mr,
CustomData *cd_ldata = (mr.extract_type == MR_EXTRACT_BMESH) ? &mr.bm->ldata :
&mr.mesh->corner_data;
int v_len = mr.loop_len;
int v_len = mr.corners_num;
uint32_t uv_layers = cache.cd_used.uv;
if (!mesh_extract_uv_format_init(&format, cache, cd_ldata, mr.extract_type, uv_layers)) {
/* VBO will not be used, only allocate minimum of memory. */
@ -122,9 +122,9 @@ static void extract_uv_init(const MeshRenderData &mr,
else {
const Span<float2> uv_map(
static_cast<const float2 *>(CustomData_get_layer_n(cd_ldata, CD_PROP_FLOAT2, i)),
mr.loop_len);
array_utils::copy(uv_map, uv_data.slice(vbo_index, mr.loop_len));
vbo_index += mr.loop_len;
mr.corners_num);
array_utils::copy(uv_map, uv_data.slice(vbo_index, mr.corners_num));
vbo_index += mr.corners_num;
}
}
}

View File

@ -27,10 +27,11 @@ static void extract_vnor_init(const MeshRenderData &mr,
GPU_vertformat_attr_add(&format, "vnor", GPU_COMP_I10, 4, GPU_FETCH_INT_TO_FLOAT_UNIT);
}
GPU_vertbuf_init_with_format(vbo, &format);
GPU_vertbuf_data_alloc(vbo, mr.loop_len);
GPU_vertbuf_data_alloc(vbo, mr.corners_num);
if (mr.extract_type == MR_EXTRACT_MESH) {
MutableSpan vbo_data(static_cast<GPUPackedNormal *>(GPU_vertbuf_get_data(vbo)), mr.loop_len);
MutableSpan vbo_data(static_cast<GPUPackedNormal *>(GPU_vertbuf_get_data(vbo)),
mr.corners_num);
extract_vert_normals(mr, vbo_data);
}
else {

View File

@ -93,7 +93,7 @@ static void extract_weights_init(const MeshRenderData &mr,
GPU_vertformat_attr_add(&format, "weight", GPU_COMP_F32, 1, GPU_FETCH_FLOAT);
}
GPU_vertbuf_init_with_format(vbo, &format);
GPU_vertbuf_data_alloc(vbo, mr.loop_len + mr.loop_loose_len);
GPU_vertbuf_data_alloc(vbo, mr.corners_num + mr.loose_indices_num);
MeshExtract_Weight_Data *data = static_cast<MeshExtract_Weight_Data *>(tls_data);
data->vbo_data = (float *)GPU_vertbuf_get_data(vbo);

View File

@ -3141,7 +3141,7 @@ void uiTemplatePreview(uiLayout *layout,
Tex *tex = (Tex *)id;
short *pr_texture = nullptr;
char _preview_id[UI_MAX_NAME_STR];
char _preview_id[sizeof(uiPreview::preview_id)];
if (id && !ELEM(GS(id->name), ID_MA, ID_TE, ID_WO, ID_LA, ID_LS)) {
RNA_warning("Expected ID of type material, texture, light, world or line style");

View File

@ -204,7 +204,7 @@ void MESH_OT_primitive_cube_add(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Add Cube";
ot->description = "Construct a cube mesh";
ot->description = "Construct a cube mesh that consists of six square faces";
ot->idname = "MESH_OT_primitive_cube_add";
/* api callbacks */
@ -508,7 +508,7 @@ void MESH_OT_primitive_grid_add(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Add Grid";
ot->description = "Construct a grid mesh";
ot->description = "Construct a subdivided plane mesh";
ot->idname = "MESH_OT_primitive_grid_add";
/* api callbacks */
@ -648,7 +648,9 @@ void MESH_OT_primitive_uv_sphere_add(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Add UV Sphere";
ot->description = "Construct a UV sphere mesh";
ot->description =
"Construct a spherical mesh with quad faces, except for triangle faces at the top and "
"bottom";
ot->idname = "MESH_OT_primitive_uv_sphere_add";
/* api callbacks */
@ -716,7 +718,7 @@ void MESH_OT_primitive_ico_sphere_add(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Add Ico Sphere";
ot->description = "Construct an Icosphere mesh";
ot->description = "Construct a spherical mesh that consists of equally sized triangles";
ot->idname = "MESH_OT_primitive_ico_sphere_add";
/* api callbacks */

View File

@ -430,8 +430,15 @@ bool ED_mesh_color_ensure(Mesh *mesh, const char *name)
{
using namespace blender;
BLI_assert(mesh->edit_mesh == nullptr);
if (mesh->attributes().contains(mesh->active_color_attribute)) {
return true;
const bke::AttributeAccessor attributes = mesh->attributes();
if (const std::optional<bke::AttributeMetaData> meta_data = attributes.lookup_meta_data(
mesh->active_color_attribute))
{
if ((ATTR_DOMAIN_AS_MASK(meta_data->domain) & ATTR_DOMAIN_MASK_COLOR) &&
(CD_TYPE_AS_MASK(meta_data->data_type) & CD_MASK_COLOR_ALL))
{
return true;
}
}
const std::string unique_name = BKE_id_attribute_calc_unique_name(mesh->id, name);

View File

@ -383,6 +383,7 @@ static int voxel_size_edit_modal(bContext *C, wmOperator *op, const wmEvent *eve
MEM_freeN(op->customdata);
ED_region_tag_redraw(region);
ED_workspace_status_text(C, nullptr);
WM_event_add_notifier(C, NC_GEOM | ND_DATA, nullptr);
return OPERATOR_FINISHED;
}

View File

@ -773,7 +773,7 @@ void PAINT_OT_hide_show(wmOperatorType *ot)
WM_operator_properties_border(ot);
hide_show_operator_properties(ot);
hide_show_operator_gesture_properties(ot);
gesture::operator_properties(ot);
gesture::operator_properties(ot, gesture::ShapeType::Box);
}
void PAINT_OT_hide_show_lasso_gesture(wmOperatorType *ot)
@ -793,7 +793,7 @@ void PAINT_OT_hide_show_lasso_gesture(wmOperatorType *ot)
WM_operator_properties_gesture_lasso(ot);
hide_show_operator_properties(ot);
hide_show_operator_gesture_properties(ot);
gesture::operator_properties(ot);
gesture::operator_properties(ot, gesture::ShapeType::Lasso);
}
static void invert_visibility_mesh(Object &object, const Span<PBVHNode *> nodes)

View File

@ -711,7 +711,7 @@ void PAINT_OT_mask_lasso_gesture(wmOperatorType *ot)
ot->flag = OPTYPE_REGISTER | OPTYPE_DEPENDS_ON_CURSOR;
WM_operator_properties_gesture_lasso(ot);
gesture::operator_properties(ot);
gesture::operator_properties(ot, gesture::ShapeType::Lasso);
paint_mask_gesture_operator_properties(ot);
}
@ -731,7 +731,7 @@ void PAINT_OT_mask_box_gesture(wmOperatorType *ot)
ot->flag = OPTYPE_REGISTER;
WM_operator_properties_border(ot);
gesture::operator_properties(ot);
gesture::operator_properties(ot, gesture::ShapeType::Box);
paint_mask_gesture_operator_properties(ot);
}
@ -751,7 +751,7 @@ void PAINT_OT_mask_line_gesture(wmOperatorType *ot)
ot->flag = OPTYPE_REGISTER;
WM_operator_properties_gesture_straightline(ot, WM_CURSOR_EDIT);
gesture::operator_properties(ot);
gesture::operator_properties(ot, gesture::ShapeType::Line);
paint_mask_gesture_operator_properties(ot);
}

View File

@ -598,6 +598,24 @@ void smooth_brush_toggle_on(const bContext *C, Paint *paint, StrokeCache *cache)
/** \} */
} // namespace blender::ed::sculpt_paint::vwpaint
static bool color_attribute_supported(const std::optional<bke::AttributeMetaData> meta_data)
{
if (!meta_data) {
return false;
}
if (!(ATTR_DOMAIN_AS_MASK(meta_data->domain) & ATTR_DOMAIN_MASK_COLOR) ||
!(CD_TYPE_AS_MASK(meta_data->data_type) & CD_MASK_COLOR_ALL))
{
return false;
}
return true;
}
static bool color_attribute_supported(const Mesh &mesh, const StringRef name)
{
return color_attribute_supported(mesh.attributes().lookup_meta_data(name));
}
bool vertex_paint_mode_poll(bContext *C)
{
const Object *ob = CTX_data_active_object(C);
@ -610,7 +628,7 @@ bool vertex_paint_mode_poll(bContext *C)
return false;
}
if (!mesh->attributes().contains(mesh->active_color_attribute)) {
if (!color_attribute_supported(*mesh, mesh->active_color_attribute)) {
return false;
}
@ -992,8 +1010,7 @@ static bool vpaint_stroke_test_start(bContext *C, wmOperator *op, const float mo
const std::optional<bke::AttributeMetaData> meta_data = *mesh->attributes().lookup_meta_data(
mesh->active_color_attribute);
if (!meta_data) {
if (!color_attribute_supported(meta_data)) {
return false;
}

View File

@ -1851,7 +1851,7 @@ void SCULPT_OT_face_set_lasso_gesture(wmOperatorType *ot)
ot->flag = OPTYPE_DEPENDS_ON_CURSOR;
WM_operator_properties_gesture_lasso(ot);
gesture::operator_properties(ot);
gesture::operator_properties(ot, gesture::ShapeType::Lasso);
}
void SCULPT_OT_face_set_box_gesture(wmOperatorType *ot)
@ -1869,7 +1869,7 @@ void SCULPT_OT_face_set_box_gesture(wmOperatorType *ot)
ot->flag = OPTYPE_REGISTER;
WM_operator_properties_border(ot);
gesture::operator_properties(ot);
gesture::operator_properties(ot, gesture::ShapeType::Box);
}
/** \} */

View File

@ -36,7 +36,7 @@
namespace blender::ed::sculpt_paint::gesture {
void operator_properties(wmOperatorType *ot)
void operator_properties(wmOperatorType *ot, ShapeType shapeType)
{
RNA_def_boolean(ot->srna,
"use_front_faces_only",
@ -44,12 +44,14 @@ void operator_properties(wmOperatorType *ot)
"Front Faces Only",
"Affect only faces facing towards the view");
RNA_def_boolean(ot->srna,
"use_limit_to_segment",
false,
"Limit to Segment",
"Apply the gesture action only to the area that is contained within the "
"segment without extending its effect to the entire line");
if (shapeType == ShapeType::Line) {
RNA_def_boolean(ot->srna,
"use_limit_to_segment",
false,
"Limit to Segment",
"Apply the gesture action only to the area that is contained within the "
"segment without extending its effect to the entire line");
}
}
static void init_common(bContext *C, wmOperator *op, GestureData &gesture_data)
@ -60,7 +62,6 @@ static void init_common(bContext *C, wmOperator *op, GestureData &gesture_data)
/* Operator properties. */
gesture_data.front_faces_only = RNA_boolean_get(op->ptr, "use_front_faces_only");
gesture_data.line.use_side_planes = RNA_boolean_get(op->ptr, "use_limit_to_segment");
gesture_data.selection_type = SelectionType::Inside;
/* SculptSession */
@ -219,6 +220,7 @@ std::unique_ptr<GestureData> init_from_line(bContext *C, wmOperator *op)
{
std::unique_ptr<GestureData> gesture_data = std::make_unique<GestureData>();
gesture_data->shape_type = ShapeType::Line;
gesture_data->line.use_side_planes = RNA_boolean_get(op->ptr, "use_limit_to_segment");
init_common(C, op, *gesture_data);

View File

@ -1768,7 +1768,7 @@ std::unique_ptr<GestureData> init_from_lasso(bContext *C, wmOperator *op);
std::unique_ptr<GestureData> init_from_line(bContext *C, wmOperator *op);
/* Common gesture operator properties. */
void operator_properties(wmOperatorType *ot);
void operator_properties(wmOperatorType *ot, ShapeType shapeType);
/* Apply the gesture action to the selected nodes. */
void apply(bContext &C, GestureData &gesture_data, wmOperator &op);

View File

@ -149,7 +149,7 @@ void SCULPT_OT_project_line_gesture(wmOperatorType *ot)
ot->flag = OPTYPE_REGISTER;
WM_operator_properties_gesture_straightline(ot, WM_CURSOR_EDIT);
gesture::operator_properties(ot);
gesture::operator_properties(ot, gesture::ShapeType::Line);
}
} // namespace blender::ed::sculpt_paint::project

View File

@ -37,23 +37,23 @@
#include "sculpt_intern.hh"
namespace blender::ed::sculpt_paint::trim {
enum eSculptTrimOperationType {
SCULPT_GESTURE_TRIM_INTERSECT,
SCULPT_GESTURE_TRIM_DIFFERENCE,
SCULPT_GESTURE_TRIM_UNION,
SCULPT_GESTURE_TRIM_JOIN,
enum class OperationType {
Intersect = 0,
Difference = 1,
Union = 2,
Join = 3,
};
/* Intersect is not exposed in the UI because it does not work correctly with symmetry (it deletes
* the symmetrical part of the mesh in the first symmetry pass). */
static EnumPropertyItem prop_trim_operation_types[] = {
{SCULPT_GESTURE_TRIM_DIFFERENCE,
static EnumPropertyItem operation_types[] = {
{int(OperationType::Difference),
"DIFFERENCE",
0,
"Difference",
"Use a difference boolean operation"},
{SCULPT_GESTURE_TRIM_UNION, "UNION", 0, "Union", "Use a union boolean operation"},
{SCULPT_GESTURE_TRIM_JOIN,
{int(OperationType::Union), "UNION", 0, "Union", "Use a union boolean operation"},
{int(OperationType::Join),
"JOIN",
0,
"Join",
@ -61,17 +61,17 @@ static EnumPropertyItem prop_trim_operation_types[] = {
{0, nullptr, 0, nullptr, nullptr},
};
enum eSculptTrimOrientationType {
SCULPT_GESTURE_TRIM_ORIENTATION_VIEW,
SCULPT_GESTURE_TRIM_ORIENTATION_SURFACE,
enum class OrientationType {
View = 0,
Surface = 1,
};
static EnumPropertyItem prop_trim_orientation_types[] = {
{SCULPT_GESTURE_TRIM_ORIENTATION_VIEW,
static EnumPropertyItem orientation_types[] = {
{int(OrientationType::View),
"VIEW",
0,
"View",
"Use the view to orientate the trimming shape"},
{SCULPT_GESTURE_TRIM_ORIENTATION_SURFACE,
{int(OrientationType::Surface),
"SURFACE",
0,
"Surface",
@ -79,22 +79,18 @@ static EnumPropertyItem prop_trim_orientation_types[] = {
{0, nullptr, 0, nullptr, nullptr},
};
enum eSculptTrimExtrudeMode {
SCULPT_GESTURE_TRIM_EXTRUDE_PROJECT,
SCULPT_GESTURE_TRIM_EXTRUDE_FIXED
enum class ExtrudeMode {
Project = 0,
Fixed = 1,
};
static EnumPropertyItem prop_trim_extrude_modes[] = {
{SCULPT_GESTURE_TRIM_EXTRUDE_PROJECT,
"PROJECT",
0,
"Project",
"Project back faces when extruding"},
{SCULPT_GESTURE_TRIM_EXTRUDE_FIXED, "FIXED", 0, "Fixed", "Extrude back faces by fixed amount"},
static EnumPropertyItem extrude_modes[] = {
{int(ExtrudeMode::Project), "PROJECT", 0, "Project", "Project back faces when extruding"},
{int(ExtrudeMode::Fixed), "FIXED", 0, "Fixed", "Extrude back faces by fixed amount"},
{0, nullptr, 0, nullptr, nullptr},
};
struct SculptGestureTrimOperation {
struct TrimOperation {
gesture::Operation op;
Mesh *mesh;
@ -105,15 +101,14 @@ struct SculptGestureTrimOperation {
bool use_cursor_depth;
eSculptTrimOperationType mode;
eSculptTrimOrientationType orientation;
eSculptTrimExtrudeMode extrude_mode;
OperationType mode;
OrientationType orientation;
ExtrudeMode extrude_mode;
};
static void sculpt_gesture_trim_normals_update(gesture::GestureData &gesture_data)
static void update_normals(gesture::GestureData &gesture_data)
{
SculptGestureTrimOperation *trim_operation = (SculptGestureTrimOperation *)
gesture_data.operation;
TrimOperation *trim_operation = (TrimOperation *)gesture_data.operation;
Mesh *trim_mesh = trim_operation->mesh;
const BMAllocTemplate allocsize = BMALLOC_TEMPLATE_FROM_ME(trim_mesh);
@ -145,25 +140,24 @@ static void sculpt_gesture_trim_normals_update(gesture::GestureData &gesture_dat
/* Get the origin and normal that are going to be used for calculating the depth and position the
* trimming geometry. */
static void sculpt_gesture_trim_shape_origin_normal_get(gesture::GestureData &gesture_data,
float *r_origin,
float *r_normal)
static void get_origin_and_normal(gesture::GestureData &gesture_data,
float *r_origin,
float *r_normal)
{
SculptGestureTrimOperation *trim_operation = (SculptGestureTrimOperation *)
gesture_data.operation;
TrimOperation *trim_operation = (TrimOperation *)gesture_data.operation;
/* Use the view origin and normal in world space. The trimming mesh coordinates are
* calculated in world space, aligned to the view, and then converted to object space to
* store them in the final trimming mesh which is going to be used in the boolean operation.
*/
switch (trim_operation->orientation) {
case SCULPT_GESTURE_TRIM_ORIENTATION_VIEW:
case OrientationType::View:
mul_v3_m4v3(r_origin,
gesture_data.vc.obact->object_to_world().ptr(),
gesture_data.ss->gesture_initial_location);
copy_v3_v3(r_normal, gesture_data.world_space_view_normal);
negate_v3(r_normal);
break;
case SCULPT_GESTURE_TRIM_ORIENTATION_SURFACE:
case OrientationType::Surface:
mul_v3_m4v3(r_origin,
gesture_data.vc.obact->object_to_world().ptr(),
gesture_data.ss->gesture_initial_location);
@ -175,10 +169,9 @@ static void sculpt_gesture_trim_shape_origin_normal_get(gesture::GestureData &ge
}
}
static void sculpt_gesture_trim_calculate_depth(gesture::GestureData &gesture_data)
static void calculate_depth(gesture::GestureData &gesture_data)
{
SculptGestureTrimOperation *trim_operation = (SculptGestureTrimOperation *)
gesture_data.operation;
TrimOperation *trim_operation = (TrimOperation *)gesture_data.operation;
SculptSession *ss = gesture_data.ss;
ViewContext *vc = &gesture_data.vc;
@ -188,7 +181,7 @@ static void sculpt_gesture_trim_calculate_depth(gesture::GestureData &gesture_da
float shape_plane[4];
float shape_origin[3];
float shape_normal[3];
sculpt_gesture_trim_shape_origin_normal_get(gesture_data, shape_origin, shape_normal);
get_origin_and_normal(gesture_data, shape_origin, shape_normal);
plane_from_point_normal_v3(shape_plane, shape_origin, shape_normal);
trim_operation->depth_front = FLT_MAX;
@ -215,7 +208,7 @@ static void sculpt_gesture_trim_calculate_depth(gesture::GestureData &gesture_da
ss->gesture_initial_location);
float mid_point_depth;
if (trim_operation->orientation == SCULPT_GESTURE_TRIM_ORIENTATION_VIEW) {
if (trim_operation->orientation == OrientationType::View) {
mid_point_depth = ss->gesture_initial_hit ?
dist_signed_to_plane_v3(world_space_gesture_initial_location,
shape_plane) :
@ -259,10 +252,9 @@ static void sculpt_gesture_trim_calculate_depth(gesture::GestureData &gesture_da
}
}
static void sculpt_gesture_trim_geometry_generate(gesture::GestureData &gesture_data)
static void generate_geometry(gesture::GestureData &gesture_data)
{
SculptGestureTrimOperation *trim_operation = (SculptGestureTrimOperation *)
gesture_data.operation;
TrimOperation *trim_operation = (TrimOperation *)gesture_data.operation;
ViewContext *vc = &gesture_data.vc;
ARegion *region = vc->region;
@ -293,21 +285,21 @@ static void sculpt_gesture_trim_geometry_generate(gesture::GestureData &gesture_
float shape_origin[3];
float shape_normal[3];
float shape_plane[4];
sculpt_gesture_trim_shape_origin_normal_get(gesture_data, shape_origin, shape_normal);
get_origin_and_normal(gesture_data, shape_origin, shape_normal);
plane_from_point_normal_v3(shape_plane, shape_origin, shape_normal);
const float(*ob_imat)[4] = vc->obact->world_to_object().ptr();
/* Write vertices coordinatesSCULPT_GESTURE_TRIM_DIFFERENCE for the front face. */
/* Write vertices coordinates OperationType::Difference for the front face. */
MutableSpan<float3> positions = trim_operation->mesh->vert_positions_for_write();
float depth_point[3];
/* Get origin point for SCULPT_GESTURE_TRIM_ORIENTATION_VIEW.
/* Get origin point for OrientationType::View.
* Note: for projection extrusion we add depth_front here
* instead of in the loop.
*/
if (trim_operation->extrude_mode == SCULPT_GESTURE_TRIM_EXTRUDE_FIXED) {
if (trim_operation->extrude_mode == ExtrudeMode::Fixed) {
copy_v3_v3(depth_point, shape_origin);
}
else {
@ -316,11 +308,11 @@ static void sculpt_gesture_trim_geometry_generate(gesture::GestureData &gesture_
for (const int i : screen_points.index_range()) {
float new_point[3];
if (trim_operation->orientation == SCULPT_GESTURE_TRIM_ORIENTATION_VIEW) {
if (trim_operation->orientation == OrientationType::View) {
ED_view3d_win_to_3d(vc->v3d, region, depth_point, screen_points[i], new_point);
/* For fixed mode we add the shape normal here to avoid projection errors. */
if (trim_operation->extrude_mode == SCULPT_GESTURE_TRIM_EXTRUDE_FIXED) {
if (trim_operation->extrude_mode == ExtrudeMode::Fixed) {
madd_v3_v3fl(new_point, shape_normal, depth_front);
}
}
@ -337,8 +329,8 @@ static void sculpt_gesture_trim_geometry_generate(gesture::GestureData &gesture_
for (const int i : screen_points.index_range()) {
float new_point[3];
if (trim_operation->extrude_mode == SCULPT_GESTURE_TRIM_EXTRUDE_PROJECT) {
if (trim_operation->orientation == SCULPT_GESTURE_TRIM_ORIENTATION_VIEW) {
if (trim_operation->extrude_mode == ExtrudeMode::Project) {
if (trim_operation->orientation == OrientationType::View) {
ED_view3d_win_to_3d(vc->v3d, region, depth_point, screen_points[i], new_point);
}
else {
@ -428,13 +420,12 @@ static void sculpt_gesture_trim_geometry_generate(gesture::GestureData &gesture_
bke::mesh_smooth_set(*trim_operation->mesh, false);
bke::mesh_calc_edges(*trim_operation->mesh, false, false);
sculpt_gesture_trim_normals_update(gesture_data);
update_normals(gesture_data);
}
static void sculpt_gesture_trim_geometry_free(gesture::GestureData &gesture_data)
static void free_geometry(gesture::GestureData &gesture_data)
{
SculptGestureTrimOperation *trim_operation = (SculptGestureTrimOperation *)
gesture_data.operation;
TrimOperation *trim_operation = (TrimOperation *)gesture_data.operation;
BKE_id_free(nullptr, trim_operation->mesh);
MEM_freeN(trim_operation->true_mesh_co);
}
@ -444,10 +435,9 @@ static int bm_face_isect_pair(BMFace *f, void * /*user_data*/)
return BM_elem_flag_test(f, BM_ELEM_DRAW) ? 1 : 0;
}
static void sculpt_gesture_apply_trim(gesture::GestureData &gesture_data)
static void apply_trim(gesture::GestureData &gesture_data)
{
SculptGestureTrimOperation *trim_operation = (SculptGestureTrimOperation *)
gesture_data.operation;
TrimOperation *trim_operation = (TrimOperation *)gesture_data.operation;
Mesh *sculpt_mesh = BKE_mesh_from_object(gesture_data.vc.obact);
Mesh *trim_mesh = trim_operation->mesh;
@ -497,19 +487,19 @@ static void sculpt_gesture_apply_trim(gesture::GestureData &gesture_data)
}
/* Join does not do a boolean operation, it just adds the geometry. */
if (trim_operation->mode != SCULPT_GESTURE_TRIM_JOIN) {
if (trim_operation->mode != OperationType::Join) {
int boolean_mode = 0;
switch (trim_operation->mode) {
case SCULPT_GESTURE_TRIM_INTERSECT:
case OperationType::Intersect:
boolean_mode = eBooleanModifierOp_Intersect;
break;
case SCULPT_GESTURE_TRIM_DIFFERENCE:
case OperationType::Difference:
boolean_mode = eBooleanModifierOp_Difference;
break;
case SCULPT_GESTURE_TRIM_UNION:
case OperationType::Union:
boolean_mode = eBooleanModifierOp_Union;
break;
case SCULPT_GESTURE_TRIM_JOIN:
case OperationType::Join:
BLI_assert(false);
break;
}
@ -536,34 +526,32 @@ static void sculpt_gesture_apply_trim(gesture::GestureData &gesture_data)
result, static_cast<Mesh *>(gesture_data.vc.obact->data), gesture_data.vc.obact);
}
static void sculpt_gesture_trim_begin(bContext &C, gesture::GestureData &gesture_data)
static void gesture_begin(bContext &C, gesture::GestureData &gesture_data)
{
Object *object = gesture_data.vc.obact;
SculptSession *ss = object->sculpt;
Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(&C);
sculpt_gesture_trim_calculate_depth(gesture_data);
sculpt_gesture_trim_geometry_generate(gesture_data);
calculate_depth(gesture_data);
generate_geometry(gesture_data);
SCULPT_topology_islands_invalidate(ss);
BKE_sculpt_update_object_for_edit(depsgraph, gesture_data.vc.obact, false);
undo::push_node(gesture_data.vc.obact, nullptr, undo::Type::Geometry);
}
static void sculpt_gesture_trim_apply_for_symmetry_pass(bContext & /*C*/,
gesture::GestureData &gesture_data)
static void gesture_apply_for_symmetry_pass(bContext & /*C*/, gesture::GestureData &gesture_data)
{
SculptGestureTrimOperation *trim_operation = (SculptGestureTrimOperation *)
gesture_data.operation;
TrimOperation *trim_operation = (TrimOperation *)gesture_data.operation;
Mesh *trim_mesh = trim_operation->mesh;
MutableSpan<float3> positions = trim_mesh->vert_positions_for_write();
for (int i = 0; i < trim_mesh->verts_num; i++) {
flip_v3_v3(positions[i], trim_operation->true_mesh_co[i], gesture_data.symmpass);
}
sculpt_gesture_trim_normals_update(gesture_data);
sculpt_gesture_apply_trim(gesture_data);
update_normals(gesture_data);
apply_trim(gesture_data);
}
static void sculpt_gesture_trim_end(bContext & /*C*/, gesture::GestureData &gesture_data)
static void gesture_end(bContext & /*C*/, gesture::GestureData &gesture_data)
{
Object *object = gesture_data.vc.obact;
Mesh *mesh = (Mesh *)object->data;
@ -574,43 +562,41 @@ static void sculpt_gesture_trim_end(bContext & /*C*/, gesture::GestureData &gest
face_set::initialize_none_to_id(mesh, next_face_set_id);
}
sculpt_gesture_trim_geometry_free(gesture_data);
free_geometry(gesture_data);
undo::push_node(gesture_data.vc.obact, nullptr, undo::Type::Geometry);
BKE_mesh_batch_cache_dirty_tag(mesh, BKE_MESH_BATCH_DIRTY_ALL);
DEG_id_tag_update(&gesture_data.vc.obact->id, ID_RECALC_GEOMETRY);
}
static void sculpt_gesture_init_trim_properties(gesture::GestureData &gesture_data, wmOperator &op)
static void init_operation(gesture::GestureData &gesture_data, wmOperator &op)
{
gesture_data.operation = reinterpret_cast<gesture::Operation *>(
MEM_cnew<SculptGestureTrimOperation>(__func__));
MEM_cnew<TrimOperation>(__func__));
SculptGestureTrimOperation *trim_operation = (SculptGestureTrimOperation *)
gesture_data.operation;
TrimOperation *trim_operation = (TrimOperation *)gesture_data.operation;
trim_operation->op.begin = sculpt_gesture_trim_begin;
trim_operation->op.apply_for_symmetry_pass = sculpt_gesture_trim_apply_for_symmetry_pass;
trim_operation->op.end = sculpt_gesture_trim_end;
trim_operation->op.begin = gesture_begin;
trim_operation->op.apply_for_symmetry_pass = gesture_apply_for_symmetry_pass;
trim_operation->op.end = gesture_end;
trim_operation->mode = eSculptTrimOperationType(RNA_enum_get(op.ptr, "trim_mode"));
trim_operation->mode = OperationType(RNA_enum_get(op.ptr, "trim_mode"));
trim_operation->use_cursor_depth = RNA_boolean_get(op.ptr, "use_cursor_depth");
trim_operation->orientation = eSculptTrimOrientationType(
RNA_enum_get(op.ptr, "trim_orientation"));
trim_operation->extrude_mode = eSculptTrimExtrudeMode(RNA_enum_get(op.ptr, "trim_extrude_mode"));
trim_operation->orientation = OrientationType(RNA_enum_get(op.ptr, "trim_orientation"));
trim_operation->extrude_mode = ExtrudeMode(RNA_enum_get(op.ptr, "trim_extrude_mode"));
/* If the cursor was not over the mesh, force the orientation to view. */
if (!gesture_data.ss->gesture_initial_hit) {
trim_operation->orientation = SCULPT_GESTURE_TRIM_ORIENTATION_VIEW;
trim_operation->orientation = OrientationType::View;
}
}
static void sculpt_trim_gesture_operator_properties(wmOperatorType *ot)
static void operator_properties(wmOperatorType *ot)
{
RNA_def_enum(ot->srna,
"trim_mode",
prop_trim_operation_types,
SCULPT_GESTURE_TRIM_DIFFERENCE,
operation_types,
int(OperationType::Difference),
"Trim Mode",
nullptr);
RNA_def_boolean(
@ -621,19 +607,19 @@ static void sculpt_trim_gesture_operator_properties(wmOperatorType *ot)
"Use cursor location and radius for the dimensions and position of the trimming shape");
RNA_def_enum(ot->srna,
"trim_orientation",
prop_trim_orientation_types,
SCULPT_GESTURE_TRIM_ORIENTATION_VIEW,
orientation_types,
int(OrientationType::View),
"Shape Orientation",
nullptr);
RNA_def_enum(ot->srna,
"trim_extrude_mode",
prop_trim_extrude_modes,
SCULPT_GESTURE_TRIM_EXTRUDE_FIXED,
extrude_modes,
int(ExtrudeMode::Fixed),
"Extrude Mode",
nullptr);
}
static int sculpt_trim_gesture_box_exec(bContext *C, wmOperator *op)
static int gesture_box_exec(bContext *C, wmOperator *op)
{
Object *object = CTX_data_active_object(C);
SculptSession *ss = object->sculpt;
@ -652,12 +638,12 @@ static int sculpt_trim_gesture_box_exec(bContext *C, wmOperator *op)
return OPERATOR_CANCELLED;
}
sculpt_gesture_init_trim_properties(*gesture_data, *op);
init_operation(*gesture_data, *op);
gesture::apply(*C, *gesture_data, *op);
return OPERATOR_FINISHED;
}
static int sculpt_trim_gesture_box_invoke(bContext *C, wmOperator *op, const wmEvent *event)
static int gesture_box_invoke(bContext *C, wmOperator *op, const wmEvent *event)
{
Object *ob = CTX_data_active_object(C);
SculptSession *ss = ob->sculpt;
@ -680,7 +666,7 @@ static int sculpt_trim_gesture_box_invoke(bContext *C, wmOperator *op, const wmE
return WM_gesture_box_invoke(C, op, event);
}
static int sculpt_trim_gesture_lasso_exec(bContext *C, wmOperator *op)
static int gesture_lasso_exec(bContext *C, wmOperator *op)
{
Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
Object *object = CTX_data_active_object(C);
@ -702,12 +688,12 @@ static int sculpt_trim_gesture_lasso_exec(bContext *C, wmOperator *op)
if (!gesture_data) {
return OPERATOR_CANCELLED;
}
sculpt_gesture_init_trim_properties(*gesture_data, *op);
init_operation(*gesture_data, *op);
gesture::apply(*C, *gesture_data, *op);
return OPERATOR_FINISHED;
}
static int sculpt_trim_gesture_lasso_invoke(bContext *C, wmOperator *op, const wmEvent *event)
static int gesture_lasso_invoke(bContext *C, wmOperator *op, const wmEvent *event)
{
Object *ob = CTX_data_active_object(C);
SculptSession *ss = ob->sculpt;
@ -736,9 +722,9 @@ void SCULPT_OT_trim_lasso_gesture(wmOperatorType *ot)
ot->idname = "SCULPT_OT_trim_lasso_gesture";
ot->description = "Trims the mesh within the lasso as you move the brush";
ot->invoke = sculpt_trim_gesture_lasso_invoke;
ot->invoke = gesture_lasso_invoke;
ot->modal = WM_gesture_lasso_modal;
ot->exec = sculpt_trim_gesture_lasso_exec;
ot->exec = gesture_lasso_exec;
ot->poll = SCULPT_mode_poll_view3d;
@ -746,9 +732,9 @@ void SCULPT_OT_trim_lasso_gesture(wmOperatorType *ot)
/* Properties. */
WM_operator_properties_gesture_lasso(ot);
gesture::operator_properties(ot);
gesture::operator_properties(ot, gesture::ShapeType::Lasso);
sculpt_trim_gesture_operator_properties(ot);
operator_properties(ot);
}
void SCULPT_OT_trim_box_gesture(wmOperatorType *ot)
@ -757,9 +743,9 @@ void SCULPT_OT_trim_box_gesture(wmOperatorType *ot)
ot->idname = "SCULPT_OT_trim_box_gesture";
ot->description = "Trims the mesh within the box as you move the brush";
ot->invoke = sculpt_trim_gesture_box_invoke;
ot->invoke = gesture_box_invoke;
ot->modal = WM_gesture_box_modal;
ot->exec = sculpt_trim_gesture_box_exec;
ot->exec = gesture_box_exec;
ot->poll = SCULPT_mode_poll_view3d;
@ -767,8 +753,8 @@ void SCULPT_OT_trim_box_gesture(wmOperatorType *ot)
/* Properties. */
WM_operator_properties_border(ot);
gesture::operator_properties(ot);
gesture::operator_properties(ot, gesture::ShapeType::Box);
sculpt_trim_gesture_operator_properties(ot);
operator_properties(ot);
}
} // namespace blender::ed::sculpt_paint::trim

View File

@ -726,6 +726,7 @@ static void buttons_area_listener(const wmSpaceTypeListenerParams *params)
buttons_area_redraw(area, BCONTEXT_PHYSICS);
/* Needed to refresh context path when changing active particle system index. */
buttons_area_redraw(area, BCONTEXT_PARTICLE);
buttons_area_redraw(area, BCONTEXT_TOOL);
break;
case ND_DRAW_ANIMVIZ:
buttons_area_redraw(area, BCONTEXT_OBJECT);

View File

@ -1604,7 +1604,7 @@ static void view3d_header_region_listener(const wmRegionListenerParams *params)
ED_region_tag_redraw(region);
break;
case NC_GEOM:
if (wmn->data == ND_VERTEX_GROUP) {
if (wmn->data == ND_VERTEX_GROUP || wmn->data == ND_DATA) {
ED_region_tag_redraw(region);
}
break;

View File

@ -195,7 +195,14 @@ void ED_uvedit_select_sync_flush(const ToolSettings *ts, BMEditMesh *em, const b
EDBM_deselect_flush(em);
}
else {
EDBM_select_flush(em);
if (ts->selectmode & SCE_SELECT_VERTEX) {
EDBM_select_flush(em);
}
else {
/* Use instead of #EDBM_select_flush so selecting edges doesn't
* flush vertex to face selection, see: #117320. */
EDBM_selectmode_flush(em);
}
}
}

View File

@ -22,13 +22,17 @@ static void calculate_uvs(Mesh *mesh,
const float dx = (size_x == 0.0f) ? 0.0f : 1.0f / size_x;
const float dy = (size_y == 0.0f) ? 0.0f : 1.0f / size_y;
threading::parallel_for(corner_verts.index_range(), 1024, [&](IndexRange range) {
for (const int i : range) {
const float3 &co = positions[corner_verts[i]];
uv_attribute.span[i].x = (co.x + size_x * 0.5f) * dx;
uv_attribute.span[i].y = (co.y + size_y * 0.5f) * dy;
}
});
threading::memory_bandwidth_bound_task(
uv_attribute.span.size_in_bytes() + positions.size_in_bytes() + corner_verts.size_in_bytes(),
[&]() {
threading::parallel_for(corner_verts.index_range(), 1024, [&](IndexRange range) {
for (const int i : range) {
const float3 &co = positions[corner_verts[i]];
uv_attribute.span[i].x = (co.x + size_x * 0.5f) * dx;
uv_attribute.span[i].y = (co.y + size_y * 0.5f) * dy;
}
});
});
uv_attribute.finish();
}
@ -59,18 +63,20 @@ Mesh *create_grid_mesh(const int verts_x,
const float dy = edges_y == 0 ? 0.0f : size_y / edges_y;
const float x_shift = edges_x / 2.0f;
const float y_shift = edges_y / 2.0f;
threading::parallel_for(IndexRange(verts_x), 512, [&](IndexRange x_range) {
for (const int x : x_range) {
const int y_offset = x * verts_y;
threading::parallel_for(IndexRange(verts_y), 512, [&](IndexRange y_range) {
for (const int y : y_range) {
const int vert_index = y_offset + y;
positions[vert_index].x = (x - x_shift) * dx;
positions[vert_index].y = (y - y_shift) * dy;
positions[vert_index].z = 0.0f;
}
});
}
threading::memory_bandwidth_bound_task(positions.size_in_bytes(), [&]() {
threading::parallel_for(IndexRange(verts_x), 512, [&](IndexRange x_range) {
for (const int x : x_range) {
const int y_offset = x * verts_y;
threading::parallel_for(IndexRange(verts_y), 512, [&](IndexRange y_range) {
for (const int y : y_range) {
const int vert_index = y_offset + y;
positions[vert_index].x = (x - x_shift) * dx;
positions[vert_index].y = (y - y_shift) * dy;
positions[vert_index].z = 0.0f;
}
});
}
});
});
}
@ -78,56 +84,63 @@ Mesh *create_grid_mesh(const int verts_x,
const int x_edges_start = verts_x * edges_y;
/* Build the horizontal edges in the X direction. */
threading::parallel_for(IndexRange(verts_x), 512, [&](IndexRange x_range) {
for (const int x : x_range) {
const int y_vert_offset = x * verts_y;
const int y_edge_offset = y_edges_start + x * edges_y;
threading::parallel_for(IndexRange(edges_y), 512, [&](IndexRange y_range) {
for (const int y : y_range) {
const int vert_index = y_vert_offset + y;
edges[y_edge_offset + y] = int2(vert_index, vert_index + 1);
}
});
}
threading::memory_bandwidth_bound_task(edges.size_in_bytes(), [&]() {
threading::parallel_for(IndexRange(verts_x), 512, [&](IndexRange x_range) {
for (const int x : x_range) {
const int y_vert_offset = x * verts_y;
const int y_edge_offset = y_edges_start + x * edges_y;
threading::parallel_for(IndexRange(edges_y), 512, [&](IndexRange y_range) {
for (const int y : y_range) {
const int vert_index = y_vert_offset + y;
edges[y_edge_offset + y] = int2(vert_index, vert_index + 1);
}
});
}
});
});
/* Build the vertical edges in the Y direction. */
threading::parallel_for(IndexRange(verts_y), 512, [&](IndexRange y_range) {
for (const int y : y_range) {
const int x_edge_offset = x_edges_start + y * edges_x;
threading::parallel_for(IndexRange(edges_x), 512, [&](IndexRange x_range) {
for (const int x : x_range) {
const int vert_index = x * verts_y + y;
edges[x_edge_offset + x] = int2(vert_index, vert_index + verts_y);
}
});
}
threading::memory_bandwidth_bound_task(edges.size_in_bytes(), [&]() {
threading::parallel_for(IndexRange(verts_y), 512, [&](IndexRange y_range) {
for (const int y : y_range) {
const int x_edge_offset = x_edges_start + y * edges_x;
threading::parallel_for(IndexRange(edges_x), 512, [&](IndexRange x_range) {
for (const int x : x_range) {
const int vert_index = x * verts_y + y;
edges[x_edge_offset + x] = int2(vert_index, vert_index + verts_y);
}
});
}
});
});
threading::parallel_for(IndexRange(edges_x), 512, [&](IndexRange x_range) {
for (const int x : x_range) {
const int y_offset = x * edges_y;
threading::parallel_for(IndexRange(edges_y), 512, [&](IndexRange y_range) {
for (const int y : y_range) {
const int face_index = y_offset + y;
const int loop_index = face_index * 4;
const int vert_index = x * verts_y + y;
threading::memory_bandwidth_bound_task(
corner_edges.size_in_bytes() + corner_verts.size_in_bytes(), [&]() {
threading::parallel_for(IndexRange(edges_x), 512, [&](IndexRange x_range) {
for (const int x : x_range) {
const int y_offset = x * edges_y;
threading::parallel_for(IndexRange(edges_y), 512, [&](IndexRange y_range) {
for (const int y : y_range) {
const int face_index = y_offset + y;
const int loop_index = face_index * 4;
const int vert_index = x * verts_y + y;
corner_verts[loop_index] = vert_index;
corner_edges[loop_index] = x_edges_start + edges_x * y + x;
corner_verts[loop_index] = vert_index;
corner_edges[loop_index] = x_edges_start + edges_x * y + x;
corner_verts[loop_index + 1] = vert_index + verts_y;
corner_edges[loop_index + 1] = y_edges_start + edges_y * (x + 1) + y;
corner_verts[loop_index + 1] = vert_index + verts_y;
corner_edges[loop_index + 1] = y_edges_start + edges_y * (x + 1) + y;
corner_verts[loop_index + 2] = vert_index + verts_y + 1;
corner_edges[loop_index + 2] = x_edges_start + edges_x * (y + 1) + x;
corner_verts[loop_index + 2] = vert_index + verts_y + 1;
corner_edges[loop_index + 2] = x_edges_start + edges_x * (y + 1) + x;
corner_verts[loop_index + 3] = vert_index + 1;
corner_edges[loop_index + 3] = y_edges_start + edges_y * x + y;
}
corner_verts[loop_index + 3] = vert_index + 1;
corner_edges[loop_index + 3] = y_edges_start + edges_y * x + y;
}
});
}
});
});
}
});
if (uv_map_id && mesh->faces_num != 0) {
calculate_uvs(mesh, positions, corner_verts, size_x, size_y, uv_map_id);

View File

@ -20,23 +20,25 @@ Mesh *create_line_mesh(const float3 start, const float3 delta, const int count)
MutableSpan<float3> positions = mesh->vert_positions_for_write();
MutableSpan<int2> edges = mesh->edges_for_write();
threading::parallel_invoke(
1024 < count,
[&]() {
threading::parallel_for(positions.index_range(), 4096, [&](IndexRange range) {
for (const int i : range) {
positions[i] = start + delta * i;
}
threading::memory_bandwidth_bound_task(positions.size_in_bytes() + edges.size_in_bytes(), [&]() {
threading::parallel_invoke(
1024 < count,
[&]() {
threading::parallel_for(positions.index_range(), 4096, [&](IndexRange range) {
for (const int i : range) {
positions[i] = start + delta * i;
}
});
},
[&]() {
threading::parallel_for(edges.index_range(), 4096, [&](IndexRange range) {
for (const int i : range) {
edges[i][0] = i;
edges[i][1] = i + 1;
}
});
});
},
[&]() {
threading::parallel_for(edges.index_range(), 4096, [&](IndexRange range) {
for (const int i : range) {
edges[i][0] = i;
edges[i][1] = i + 1;
}
});
});
});
mesh->tag_loose_verts_none();
mesh->tag_overlapping_none();

View File

@ -286,6 +286,24 @@ struct InstanceContext {
}
};
static int64_t get_final_points_num(const GatherTasks &tasks)
{
int64_t points_num = 0;
if (!tasks.pointcloud_tasks.is_empty()) {
const RealizePointCloudTask &task = tasks.pointcloud_tasks.last();
points_num += task.start_index + task.pointcloud_info->pointcloud->totpoint;
}
if (!tasks.mesh_tasks.is_empty()) {
const RealizeMeshTask &task = tasks.mesh_tasks.last();
points_num += task.start_indices.vertex + task.mesh_info->mesh->verts_num;
}
if (!tasks.curve_tasks.is_empty()) {
const RealizeCurveTask &task = tasks.curve_tasks.last();
points_num += task.start_indices.point + task.curve_info->curves->geometry.point_num;
}
return points_num;
}
static void copy_transformed_positions(const Span<float3> src,
const float4x4 &transform,
MutableSpan<float3> dst)
@ -1612,22 +1630,29 @@ bke::GeometrySet realize_instances(bke::GeometrySet geometry_set,
gather_realize_tasks_recursive(gather_info, geometry_set, transform, attribute_fallbacks);
bke::GeometrySet new_geometry_set;
execute_realize_pointcloud_tasks(options,
all_pointclouds_info,
gather_info.r_tasks.pointcloud_tasks,
all_pointclouds_info.attributes,
new_geometry_set);
execute_realize_mesh_tasks(options,
all_meshes_info,
gather_info.r_tasks.mesh_tasks,
all_meshes_info.attributes,
all_meshes_info.materials,
new_geometry_set);
execute_realize_curve_tasks(options,
all_curves_info,
gather_info.r_tasks.curve_tasks,
all_curves_info.attributes,
new_geometry_set);
const int64_t total_points_num = get_final_points_num(gather_info.r_tasks);
/* This doesn't have to be exact at all, it's just a rough estimate ot make decisions about
* multi-threading (overhead). */
const int64_t approximate_used_bytes_num = total_points_num * 32;
threading::memory_bandwidth_bound_task(approximate_used_bytes_num, [&]() {
execute_realize_pointcloud_tasks(options,
all_pointclouds_info,
gather_info.r_tasks.pointcloud_tasks,
all_pointclouds_info.attributes,
new_geometry_set);
execute_realize_mesh_tasks(options,
all_meshes_info,
gather_info.r_tasks.mesh_tasks,
all_meshes_info.attributes,
all_meshes_info.materials,
new_geometry_set);
execute_realize_curve_tasks(options,
all_curves_info,
gather_info.r_tasks.curve_tasks,
all_curves_info.attributes,
new_geometry_set);
});
if (gather_info.r_tasks.first_volume) {
new_geometry_set.add(*gather_info.r_tasks.first_volume);

View File

@ -52,26 +52,27 @@ static bool lineart_mod_is_disabled(GpencilModifierData *md)
return disabled;
}
static void clear_strokes(Object *ob, GpencilModifierData *md, int frame)
static bool clear_strokes(Object *ob, GpencilModifierData *md, int frame)
{
if (md->type != eGpencilModifierType_Lineart) {
return;
return false;
}
LineartGpencilModifierData *lmd = (LineartGpencilModifierData *)md;
bGPdata *gpd = static_cast<bGPdata *>(ob->data);
bGPDlayer *gpl = BKE_gpencil_layer_get_by_name(gpd, lmd->target_layer, 1);
if (!gpl) {
return;
return false;
}
bGPDframe *gpf = BKE_gpencil_layer_frame_find(gpl, frame);
if (!gpf) {
/* No greasepencil frame found. */
return;
return false;
}
BKE_gpencil_layer_frame_delete(gpl, gpf);
return true;
}
static bool bake_strokes(Object *ob,
@ -183,7 +184,9 @@ static bool lineart_gpencil_bake_single_target(LineartBakeJob *bj, Object *ob, i
if (bj->overwrite_frames) {
LISTBASE_FOREACH (GpencilModifierData *, md, &ob->greasepencil_modifiers) {
if (md->type == eGpencilModifierType_Lineart) {
clear_strokes(ob, md, frame);
if (clear_strokes(ob, md, frame)) {
touched = true;
}
}
}
}

View File

@ -176,6 +176,7 @@ vec3 orthogonal(vec3 v);
* \note Returned vector is always rotated 90 degrees counter clock wise.
*/
vec2 orthogonal(vec2 v);
ivec2 orthogonal(ivec2 v);
/**
* Return true if the difference between`a` and `b` is below the `epsilon` value.
@ -613,6 +614,10 @@ vec2 orthogonal(vec2 v)
{
return vec2(-v.y, v.x);
}
ivec2 orthogonal(ivec2 v)
{
return ivec2(-v.y, v.x);
}
bool is_equal(vec2 a, vec2 b, const float epsilon)
{

View File

@ -212,6 +212,7 @@
.gtao_quality = 0.25f, \
.gtao_thickness = 0.5f, \
.gtao_focus = 0.05f, \
.gtao_resolution = 2, \
\
.bokeh_overblur = 5.0f, \
.bokeh_max_size = 100.0f, \

Some files were not shown because too many files have changed in this diff Show More