GPv3: Initial sculpt mode #119338
|
@ -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;
|
||||
|
|
|
@ -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"); });
|
||||
})();
|
||||
|
|
|
@ -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>
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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. */
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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). */
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
};
|
||||
|
||||
/** \} */
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
};
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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)));
|
||||
}
|
||||
|
|
|
@ -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));
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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];
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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. */
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -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. */
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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_);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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*/,
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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)),
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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)) {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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*/,
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
/** \} */
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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
Loading…
Reference in New Issue