Merge pull request #2082 from armory3d/blender2.9
Blender 2.9 LTS support
This commit is contained in:
commit
98eeccd71d
163
Shaders/std/sky.glsl
Normal file
163
Shaders/std/sky.glsl
Normal file
|
@ -0,0 +1,163 @@
|
|||
/* Various sky functions
|
||||
* =====================
|
||||
*
|
||||
* Nishita model is based on https://github.com/wwwtyro/glsl-atmosphere (Unlicense License)
|
||||
*
|
||||
* Changes to the original implementation:
|
||||
* - r and pSun parameters of nishita_atmosphere() are already normalized
|
||||
* - Some original parameters of nishita_atmosphere() are replaced with pre-defined values
|
||||
* - Implemented air, dust and ozone density node parameters (see Blender source)
|
||||
* - Replaced the inner integral calculation with a LUT lookup
|
||||
*
|
||||
* Reference for the sun's limb darkening and ozone calculations:
|
||||
* [Hill] Sebastien Hillaire. Physically Based Sky, Atmosphere and Cloud Rendering in Frostbite
|
||||
* (https://media.contentapi.ea.com/content/dam/eacom/frostbite/files/s2016-pbs-frostbite-sky-clouds-new.pdf)
|
||||
*
|
||||
* Cycles code used for reference: blender/intern/sky/source/sky_nishita.cpp
|
||||
* (https://github.com/blender/blender/blob/4429b4b77ef6754739a3c2b4fabd0537999e9bdc/intern/sky/source/sky_nishita.cpp)
|
||||
*/
|
||||
|
||||
#ifndef _SKY_GLSL_
|
||||
#define _SKY_GLSL_
|
||||
|
||||
uniform sampler2D nishitaLUT;
|
||||
uniform vec2 nishitaDensity;
|
||||
|
||||
#ifndef PI
|
||||
#define PI 3.141592
|
||||
#endif
|
||||
#ifndef HALF_PI
|
||||
#define HALF_PI 1.570796
|
||||
#endif
|
||||
|
||||
#define nishita_iSteps 16
|
||||
|
||||
// These values are taken from Cycles code if they
|
||||
// exist there, otherwise they are taken from the example
|
||||
// in the glsl-atmosphere repo
|
||||
#define nishita_sun_intensity 22.0
|
||||
#define nishita_atmo_radius 6420e3
|
||||
#define nishita_rayleigh_scale 8e3
|
||||
#define nishita_rayleigh_coeff vec3(5.5e-6, 13.0e-6, 22.4e-6)
|
||||
#define nishita_mie_scale 1.2e3
|
||||
#define nishita_mie_coeff 2e-5
|
||||
#define nishita_mie_dir 0.76 // Aerosols anisotropy ("direction")
|
||||
#define nishita_mie_dir_sq 0.5776 // Squared aerosols anisotropy
|
||||
|
||||
// The ozone absorption coefficients are taken from Cycles code.
|
||||
// Because Cycles calculates 21 wavelengths, we use the coefficients
|
||||
// which are closest to the RGB wavelengths (645nm, 510nm, 440nm).
|
||||
// Precalculating values by simulating Blender's spec_to_xyz() function
|
||||
// to include all 21 wavelengths gave unrealistic results
|
||||
#define nishita_ozone_coeff vec3(1.59051840791988e-6, 0.00000096707041180970, 0.00000007309568762914)
|
||||
|
||||
// Values from [Hill: 60]
|
||||
#define sun_limb_darkening_col vec3(0.397, 0.503, 0.652)
|
||||
|
||||
float random(vec2 coords) {
|
||||
return fract(sin(dot(coords.xy, vec2(12.9898,78.233))) * 43758.5453);
|
||||
}
|
||||
|
||||
vec3 nishita_lookupLUT(const float height, const float sunTheta) {
|
||||
vec2 coords = vec2(
|
||||
sqrt(height * (1 / nishita_atmo_radius)),
|
||||
0.5 + 0.5 * sign(sunTheta - HALF_PI) * sqrt(abs(sunTheta * (1 / HALF_PI) - 1))
|
||||
);
|
||||
return textureLod(nishitaLUT, coords, 0.0).rgb;
|
||||
}
|
||||
|
||||
/* See raySphereIntersection() in armory/Sources/renderpath/Nishita.hx */
|
||||
vec2 nishita_rsi(const vec3 r0, const vec3 rd, const float sr) {
|
||||
float a = dot(rd, rd);
|
||||
float b = 2.0 * dot(rd, r0);
|
||||
float c = dot(r0, r0) - (sr * sr);
|
||||
float d = (b*b) - 4.0*a*c;
|
||||
|
||||
// If d < 0.0 the ray does not intersect the sphere
|
||||
return (d < 0.0) ? vec2(1e5,-1e5) : vec2((-b - sqrt(d))/(2.0*a), (-b + sqrt(d))/(2.0*a));
|
||||
}
|
||||
|
||||
/*
|
||||
* r: normalized ray direction
|
||||
* r0: ray origin
|
||||
* pSun: normalized sun direction
|
||||
* rPlanet: planet radius
|
||||
*/
|
||||
vec3 nishita_atmosphere(const vec3 r, const vec3 r0, const vec3 pSun, const float rPlanet) {
|
||||
// Calculate the step size of the primary ray
|
||||
vec2 p = nishita_rsi(r0, r, nishita_atmo_radius);
|
||||
if (p.x > p.y) return vec3(0.0);
|
||||
p.y = min(p.y, nishita_rsi(r0, r, rPlanet).x);
|
||||
float iStepSize = (p.y - p.x) / float(nishita_iSteps);
|
||||
|
||||
// Primary ray time
|
||||
float iTime = 0.0;
|
||||
|
||||
// Accumulators for Rayleigh and Mie scattering.
|
||||
vec3 totalRlh = vec3(0,0,0);
|
||||
vec3 totalMie = vec3(0,0,0);
|
||||
|
||||
// Optical depth accumulators for the primary ray
|
||||
float iOdRlh = 0.0;
|
||||
float iOdMie = 0.0;
|
||||
|
||||
// Calculate the Rayleigh and Mie phases
|
||||
float mu = dot(r, pSun);
|
||||
float mumu = mu * mu;
|
||||
float pRlh = 3.0 / (16.0 * PI) * (1.0 + mumu);
|
||||
float pMie = 3.0 / (8.0 * PI) * ((1.0 - nishita_mie_dir_sq) * (mumu + 1.0)) / (pow(1.0 + nishita_mie_dir_sq - 2.0 * mu * nishita_mie_dir, 1.5) * (2.0 + nishita_mie_dir_sq));
|
||||
|
||||
// Sample the primary ray
|
||||
for (int i = 0; i < nishita_iSteps; i++) {
|
||||
|
||||
// Calculate the primary ray sample position and height
|
||||
vec3 iPos = r0 + r * (iTime + iStepSize * 0.5);
|
||||
float iHeight = length(iPos) - rPlanet;
|
||||
|
||||
// Calculate the optical depth of the Rayleigh and Mie scattering for this step
|
||||
float odStepRlh = exp(-iHeight / nishita_rayleigh_scale) * nishitaDensity.x * iStepSize;
|
||||
float odStepMie = exp(-iHeight / nishita_mie_scale) * nishitaDensity.y * iStepSize;
|
||||
|
||||
// Accumulate optical depth
|
||||
iOdRlh += odStepRlh;
|
||||
iOdMie += odStepMie;
|
||||
|
||||
// Idea behind this: "Rotate" everything by iPos (-> iPos is the new zenith) and then all calculations for the
|
||||
// inner integral only depend on the sample height (iHeight) and sunTheta (angle between sun and new zenith).
|
||||
float sunTheta = acos(dot(normalize(iPos), normalize(pSun)));
|
||||
vec3 jODepth = nishita_lookupLUT(iHeight, sunTheta);// * vec3(14000000 / 255, 14000000 / 255, 2000000 / 255);
|
||||
|
||||
// Apply dithering to reduce visible banding
|
||||
jODepth += mix(-1000, 1000, random(r.xy));
|
||||
|
||||
// Calculate attenuation
|
||||
vec3 attn = exp(-(
|
||||
nishita_mie_coeff * (iOdMie + jODepth.y)
|
||||
+ (nishita_rayleigh_coeff) * (iOdRlh + jODepth.x)
|
||||
+ nishita_ozone_coeff * jODepth.z
|
||||
));
|
||||
|
||||
// Accumulate scattering
|
||||
totalRlh += odStepRlh * attn;
|
||||
totalMie += odStepMie * attn;
|
||||
|
||||
iTime += iStepSize;
|
||||
}
|
||||
|
||||
return nishita_sun_intensity * (pRlh * nishita_rayleigh_coeff * totalRlh + pMie * nishita_mie_coeff * totalMie);
|
||||
}
|
||||
|
||||
vec3 sun_disk(const vec3 n, const vec3 light_dir, const float disk_size, const float intensity) {
|
||||
// Normalized SDF
|
||||
float dist = distance(n, light_dir) / disk_size;
|
||||
|
||||
// Darken the edges of the sun
|
||||
// [Hill: 28, 60] (according to [Nec96])
|
||||
float invDist = 1.0 - dist;
|
||||
float mu = sqrt(invDist * invDist);
|
||||
vec3 limb_darkening = 1.0 - (1.0 - pow(vec3(mu), sun_limb_darkening_col));
|
||||
|
||||
return 1 + (1.0 - step(1.0, dist)) * nishita_sun_intensity * intensity * limb_darkening;
|
||||
}
|
||||
|
||||
#endif
|
|
@ -72,4 +72,13 @@ class Helper {
|
|||
if (value <= leftMin) return rightMin;
|
||||
return map(value, leftMin, leftMax, rightMin, rightMax);
|
||||
}
|
||||
|
||||
/**
|
||||
Return the sign of the given value represented as `1.0` (positive value)
|
||||
or `-1.0` (negative value). The sign of `0` is `0`.
|
||||
**/
|
||||
public static inline function sign(value: Float): Float {
|
||||
if (value == 0) return 0;
|
||||
return (value < 0) ? -1.0 : 1.0;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,176 +11,204 @@ class Uniforms {
|
|||
|
||||
public static function register() {
|
||||
iron.object.Uniforms.externalTextureLinks = [textureLink];
|
||||
iron.object.Uniforms.externalVec2Links = [];
|
||||
iron.object.Uniforms.externalVec2Links = [vec2Link];
|
||||
iron.object.Uniforms.externalVec3Links = [vec3Link];
|
||||
iron.object.Uniforms.externalVec4Links = [];
|
||||
iron.object.Uniforms.externalFloatLinks = [floatLink];
|
||||
iron.object.Uniforms.externalIntLinks = [];
|
||||
}
|
||||
|
||||
public static function textureLink(object: Object, mat: MaterialData, link: String): kha.Image {
|
||||
#if arm_ltc
|
||||
if (link == "_ltcMat") {
|
||||
if (armory.data.ConstData.ltcMatTex == null) armory.data.ConstData.initLTC();
|
||||
return armory.data.ConstData.ltcMatTex;
|
||||
public static function textureLink(object: Object, mat: MaterialData, link: String): Null<kha.Image> {
|
||||
switch (link) {
|
||||
case "_nishitaLUT": {
|
||||
if (armory.renderpath.Nishita.data == null) armory.renderpath.Nishita.recompute(Scene.active.world);
|
||||
return armory.renderpath.Nishita.data.lut;
|
||||
}
|
||||
#if arm_ltc
|
||||
case "_ltcMat": {
|
||||
if (armory.data.ConstData.ltcMatTex == null) armory.data.ConstData.initLTC();
|
||||
return armory.data.ConstData.ltcMatTex;
|
||||
}
|
||||
case "_ltcMag": {
|
||||
if (armory.data.ConstData.ltcMagTex == null) armory.data.ConstData.initLTC();
|
||||
return armory.data.ConstData.ltcMagTex;
|
||||
}
|
||||
#end
|
||||
}
|
||||
else if (link == "_ltcMag") {
|
||||
if (armory.data.ConstData.ltcMagTex == null) armory.data.ConstData.initLTC();
|
||||
return armory.data.ConstData.ltcMagTex;
|
||||
}
|
||||
#end
|
||||
|
||||
var target = iron.RenderPath.active.renderTargets.get(link.endsWith("_depth") ? link.substr(0, link.length - 6) : link);
|
||||
return target != null ? target.image : null;
|
||||
}
|
||||
|
||||
public static function vec3Link(object: Object, mat: MaterialData, link: String): iron.math.Vec4 {
|
||||
public static function vec3Link(object: Object, mat: MaterialData, link: String): Null<iron.math.Vec4> {
|
||||
var v: Vec4 = null;
|
||||
#if arm_hosek
|
||||
if (link == "_hosekA") {
|
||||
if (armory.renderpath.HosekWilkie.data == null) {
|
||||
armory.renderpath.HosekWilkie.recompute(Scene.active.world);
|
||||
switch (link) {
|
||||
#if arm_hosek
|
||||
case "_hosekA": {
|
||||
if (armory.renderpath.HosekWilkie.data == null) {
|
||||
armory.renderpath.HosekWilkie.recompute(Scene.active.world);
|
||||
}
|
||||
if (armory.renderpath.HosekWilkie.data != null) {
|
||||
v = iron.object.Uniforms.helpVec;
|
||||
v.x = armory.renderpath.HosekWilkie.data.A.x;
|
||||
v.y = armory.renderpath.HosekWilkie.data.A.y;
|
||||
v.z = armory.renderpath.HosekWilkie.data.A.z;
|
||||
}
|
||||
}
|
||||
if (armory.renderpath.HosekWilkie.data != null) {
|
||||
case "_hosekB": {
|
||||
if (armory.renderpath.HosekWilkie.data == null) {
|
||||
armory.renderpath.HosekWilkie.recompute(Scene.active.world);
|
||||
}
|
||||
if (armory.renderpath.HosekWilkie.data != null) {
|
||||
v = iron.object.Uniforms.helpVec;
|
||||
v.x = armory.renderpath.HosekWilkie.data.B.x;
|
||||
v.y = armory.renderpath.HosekWilkie.data.B.y;
|
||||
v.z = armory.renderpath.HosekWilkie.data.B.z;
|
||||
}
|
||||
}
|
||||
case "_hosekC": {
|
||||
if (armory.renderpath.HosekWilkie.data == null) {
|
||||
armory.renderpath.HosekWilkie.recompute(Scene.active.world);
|
||||
}
|
||||
if (armory.renderpath.HosekWilkie.data != null) {
|
||||
v = iron.object.Uniforms.helpVec;
|
||||
v.x = armory.renderpath.HosekWilkie.data.C.x;
|
||||
v.y = armory.renderpath.HosekWilkie.data.C.y;
|
||||
v.z = armory.renderpath.HosekWilkie.data.C.z;
|
||||
}
|
||||
}
|
||||
case "_hosekD": {
|
||||
if (armory.renderpath.HosekWilkie.data == null) {
|
||||
armory.renderpath.HosekWilkie.recompute(Scene.active.world);
|
||||
}
|
||||
if (armory.renderpath.HosekWilkie.data != null) {
|
||||
v = iron.object.Uniforms.helpVec;
|
||||
v.x = armory.renderpath.HosekWilkie.data.D.x;
|
||||
v.y = armory.renderpath.HosekWilkie.data.D.y;
|
||||
v.z = armory.renderpath.HosekWilkie.data.D.z;
|
||||
}
|
||||
}
|
||||
case "_hosekE": {
|
||||
if (armory.renderpath.HosekWilkie.data == null) {
|
||||
armory.renderpath.HosekWilkie.recompute(Scene.active.world);
|
||||
}
|
||||
if (armory.renderpath.HosekWilkie.data != null) {
|
||||
v = iron.object.Uniforms.helpVec;
|
||||
v.x = armory.renderpath.HosekWilkie.data.E.x;
|
||||
v.y = armory.renderpath.HosekWilkie.data.E.y;
|
||||
v.z = armory.renderpath.HosekWilkie.data.E.z;
|
||||
}
|
||||
}
|
||||
case "_hosekF": {
|
||||
if (armory.renderpath.HosekWilkie.data == null) {
|
||||
armory.renderpath.HosekWilkie.recompute(Scene.active.world);
|
||||
}
|
||||
if (armory.renderpath.HosekWilkie.data != null) {
|
||||
v = iron.object.Uniforms.helpVec;
|
||||
v.x = armory.renderpath.HosekWilkie.data.F.x;
|
||||
v.y = armory.renderpath.HosekWilkie.data.F.y;
|
||||
v.z = armory.renderpath.HosekWilkie.data.F.z;
|
||||
}
|
||||
}
|
||||
case "_hosekG": {
|
||||
if (armory.renderpath.HosekWilkie.data == null) {
|
||||
armory.renderpath.HosekWilkie.recompute(Scene.active.world);
|
||||
}
|
||||
if (armory.renderpath.HosekWilkie.data != null) {
|
||||
v = iron.object.Uniforms.helpVec;
|
||||
v.x = armory.renderpath.HosekWilkie.data.G.x;
|
||||
v.y = armory.renderpath.HosekWilkie.data.G.y;
|
||||
v.z = armory.renderpath.HosekWilkie.data.G.z;
|
||||
}
|
||||
}
|
||||
case "_hosekH": {
|
||||
if (armory.renderpath.HosekWilkie.data == null) {
|
||||
armory.renderpath.HosekWilkie.recompute(Scene.active.world);
|
||||
}
|
||||
if (armory.renderpath.HosekWilkie.data != null) {
|
||||
v = iron.object.Uniforms.helpVec;
|
||||
v.x = armory.renderpath.HosekWilkie.data.H.x;
|
||||
v.y = armory.renderpath.HosekWilkie.data.H.y;
|
||||
v.z = armory.renderpath.HosekWilkie.data.H.z;
|
||||
}
|
||||
}
|
||||
case "_hosekI": {
|
||||
if (armory.renderpath.HosekWilkie.data == null) {
|
||||
armory.renderpath.HosekWilkie.recompute(Scene.active.world);
|
||||
}
|
||||
if (armory.renderpath.HosekWilkie.data != null) {
|
||||
v = iron.object.Uniforms.helpVec;
|
||||
v.x = armory.renderpath.HosekWilkie.data.I.x;
|
||||
v.y = armory.renderpath.HosekWilkie.data.I.y;
|
||||
v.z = armory.renderpath.HosekWilkie.data.I.z;
|
||||
}
|
||||
}
|
||||
case "_hosekZ": {
|
||||
if (armory.renderpath.HosekWilkie.data == null) {
|
||||
armory.renderpath.HosekWilkie.recompute(Scene.active.world);
|
||||
}
|
||||
if (armory.renderpath.HosekWilkie.data != null) {
|
||||
v = iron.object.Uniforms.helpVec;
|
||||
v.x = armory.renderpath.HosekWilkie.data.Z.x;
|
||||
v.y = armory.renderpath.HosekWilkie.data.Z.y;
|
||||
v.z = armory.renderpath.HosekWilkie.data.Z.z;
|
||||
}
|
||||
}
|
||||
#end
|
||||
#if rp_voxelao
|
||||
case "_cameraPositionSnap": {
|
||||
v = iron.object.Uniforms.helpVec;
|
||||
v.x = armory.renderpath.HosekWilkie.data.A.x;
|
||||
v.y = armory.renderpath.HosekWilkie.data.A.y;
|
||||
v.z = armory.renderpath.HosekWilkie.data.A.z;
|
||||
var camera = iron.Scene.active.camera;
|
||||
v.set(camera.transform.worldx(), camera.transform.worldy(), camera.transform.worldz());
|
||||
var l = camera.lookWorld();
|
||||
var e = Main.voxelgiHalfExtents;
|
||||
v.x += l.x * e * 0.9;
|
||||
v.y += l.y * e * 0.9;
|
||||
var f = Main.voxelgiVoxelSize * 8; // Snaps to 3 mip-maps range
|
||||
v.set(Math.floor(v.x / f) * f, Math.floor(v.y / f) * f, Math.floor(v.z / f) * f);
|
||||
}
|
||||
#end
|
||||
}
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
public static function vec2Link(object: Object, mat: MaterialData, link: String): Null<iron.math.Vec4> {
|
||||
var v: Vec4 = null;
|
||||
switch (link) {
|
||||
case "_nishitaDensity": {
|
||||
var w = Scene.active.world;
|
||||
if (w != null) {
|
||||
v = iron.object.Uniforms.helpVec;
|
||||
// We only need Rayleigh and Mie density in the sky shader -> Vec2
|
||||
v.x = w.raw.nishita_density[0];
|
||||
v.y = w.raw.nishita_density[1];
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (link == "_hosekB") {
|
||||
if (armory.renderpath.HosekWilkie.data == null) {
|
||||
armory.renderpath.HosekWilkie.recompute(Scene.active.world);
|
||||
}
|
||||
if (armory.renderpath.HosekWilkie.data != null) {
|
||||
v = iron.object.Uniforms.helpVec;
|
||||
v.x = armory.renderpath.HosekWilkie.data.B.x;
|
||||
v.y = armory.renderpath.HosekWilkie.data.B.y;
|
||||
v.z = armory.renderpath.HosekWilkie.data.B.z;
|
||||
}
|
||||
}
|
||||
else if (link == "_hosekC") {
|
||||
if (armory.renderpath.HosekWilkie.data == null) {
|
||||
armory.renderpath.HosekWilkie.recompute(Scene.active.world);
|
||||
}
|
||||
if (armory.renderpath.HosekWilkie.data != null) {
|
||||
v = iron.object.Uniforms.helpVec;
|
||||
v.x = armory.renderpath.HosekWilkie.data.C.x;
|
||||
v.y = armory.renderpath.HosekWilkie.data.C.y;
|
||||
v.z = armory.renderpath.HosekWilkie.data.C.z;
|
||||
}
|
||||
}
|
||||
else if (link == "_hosekD") {
|
||||
if (armory.renderpath.HosekWilkie.data == null) {
|
||||
armory.renderpath.HosekWilkie.recompute(Scene.active.world);
|
||||
}
|
||||
if (armory.renderpath.HosekWilkie.data != null) {
|
||||
v = iron.object.Uniforms.helpVec;
|
||||
v.x = armory.renderpath.HosekWilkie.data.D.x;
|
||||
v.y = armory.renderpath.HosekWilkie.data.D.y;
|
||||
v.z = armory.renderpath.HosekWilkie.data.D.z;
|
||||
}
|
||||
}
|
||||
else if (link == "_hosekE") {
|
||||
if (armory.renderpath.HosekWilkie.data == null) {
|
||||
armory.renderpath.HosekWilkie.recompute(Scene.active.world);
|
||||
}
|
||||
if (armory.renderpath.HosekWilkie.data != null) {
|
||||
v = iron.object.Uniforms.helpVec;
|
||||
v.x = armory.renderpath.HosekWilkie.data.E.x;
|
||||
v.y = armory.renderpath.HosekWilkie.data.E.y;
|
||||
v.z = armory.renderpath.HosekWilkie.data.E.z;
|
||||
}
|
||||
}
|
||||
else if (link == "_hosekF") {
|
||||
if (armory.renderpath.HosekWilkie.data == null) {
|
||||
armory.renderpath.HosekWilkie.recompute(Scene.active.world);
|
||||
}
|
||||
if (armory.renderpath.HosekWilkie.data != null) {
|
||||
v = iron.object.Uniforms.helpVec;
|
||||
v.x = armory.renderpath.HosekWilkie.data.F.x;
|
||||
v.y = armory.renderpath.HosekWilkie.data.F.y;
|
||||
v.z = armory.renderpath.HosekWilkie.data.F.z;
|
||||
}
|
||||
}
|
||||
else if (link == "_hosekG") {
|
||||
if (armory.renderpath.HosekWilkie.data == null) {
|
||||
armory.renderpath.HosekWilkie.recompute(Scene.active.world);
|
||||
}
|
||||
if (armory.renderpath.HosekWilkie.data != null) {
|
||||
v = iron.object.Uniforms.helpVec;
|
||||
v.x = armory.renderpath.HosekWilkie.data.G.x;
|
||||
v.y = armory.renderpath.HosekWilkie.data.G.y;
|
||||
v.z = armory.renderpath.HosekWilkie.data.G.z;
|
||||
}
|
||||
}
|
||||
else if (link == "_hosekH") {
|
||||
if (armory.renderpath.HosekWilkie.data == null) {
|
||||
armory.renderpath.HosekWilkie.recompute(Scene.active.world);
|
||||
}
|
||||
if (armory.renderpath.HosekWilkie.data != null) {
|
||||
v = iron.object.Uniforms.helpVec;
|
||||
v.x = armory.renderpath.HosekWilkie.data.H.x;
|
||||
v.y = armory.renderpath.HosekWilkie.data.H.y;
|
||||
v.z = armory.renderpath.HosekWilkie.data.H.z;
|
||||
}
|
||||
}
|
||||
else if (link == "_hosekI") {
|
||||
if (armory.renderpath.HosekWilkie.data == null) {
|
||||
armory.renderpath.HosekWilkie.recompute(Scene.active.world);
|
||||
}
|
||||
if (armory.renderpath.HosekWilkie.data != null) {
|
||||
v = iron.object.Uniforms.helpVec;
|
||||
v.x = armory.renderpath.HosekWilkie.data.I.x;
|
||||
v.y = armory.renderpath.HosekWilkie.data.I.y;
|
||||
v.z = armory.renderpath.HosekWilkie.data.I.z;
|
||||
}
|
||||
}
|
||||
else if (link == "_hosekZ") {
|
||||
if (armory.renderpath.HosekWilkie.data == null) {
|
||||
armory.renderpath.HosekWilkie.recompute(Scene.active.world);
|
||||
}
|
||||
if (armory.renderpath.HosekWilkie.data != null) {
|
||||
v = iron.object.Uniforms.helpVec;
|
||||
v.x = armory.renderpath.HosekWilkie.data.Z.x;
|
||||
v.y = armory.renderpath.HosekWilkie.data.Z.y;
|
||||
v.z = armory.renderpath.HosekWilkie.data.Z.z;
|
||||
}
|
||||
}
|
||||
#end
|
||||
#if rp_voxelao
|
||||
if (link == "_cameraPositionSnap") {
|
||||
v = iron.object.Uniforms.helpVec;
|
||||
var camera = iron.Scene.active.camera;
|
||||
v.set(camera.transform.worldx(), camera.transform.worldy(), camera.transform.worldz());
|
||||
var l = camera.lookWorld();
|
||||
var e = Main.voxelgiHalfExtents;
|
||||
v.x += l.x * e * 0.9;
|
||||
v.y += l.y * e * 0.9;
|
||||
var f = Main.voxelgiVoxelSize * 8; // Snaps to 3 mip-maps range
|
||||
v.set(Math.floor(v.x / f) * f, Math.floor(v.y / f) * f, Math.floor(v.z / f) * f);
|
||||
}
|
||||
#end
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
public static function floatLink(object: Object, mat: MaterialData, link: String): Null<kha.FastFloat> {
|
||||
#if rp_dynres
|
||||
if (link == "_dynamicScale") {
|
||||
return armory.renderpath.DynamicResolutionScale.dynamicScale;
|
||||
switch (link) {
|
||||
#if rp_dynres
|
||||
case "_dynamicScale": {
|
||||
return armory.renderpath.DynamicResolutionScale.dynamicScale;
|
||||
}
|
||||
#end
|
||||
#if arm_debug
|
||||
case "_debugFloat": {
|
||||
return armory.trait.internal.DebugConsole.debugFloat;
|
||||
}
|
||||
#end
|
||||
#if rp_voxelao
|
||||
case "_voxelBlend": { // Blend current and last voxels
|
||||
var freq = armory.renderpath.RenderPathCreator.voxelFreq;
|
||||
return (armory.renderpath.RenderPathCreator.voxelFrame % freq) / freq;
|
||||
}
|
||||
#end
|
||||
}
|
||||
#end
|
||||
#if arm_debug
|
||||
if (link == "_debugFloat") {
|
||||
return armory.trait.internal.DebugConsole.debugFloat;
|
||||
}
|
||||
#end
|
||||
#if rp_voxelao
|
||||
if (link == "_voxelBlend") { // Blend current and last voxels
|
||||
var freq = armory.renderpath.RenderPathCreator.voxelFreq;
|
||||
return (armory.renderpath.RenderPathCreator.voxelFrame % freq) / freq;
|
||||
}
|
||||
#end
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
190
Sources/armory/renderpath/Nishita.hx
Normal file
190
Sources/armory/renderpath/Nishita.hx
Normal file
|
@ -0,0 +1,190 @@
|
|||
package armory.renderpath;
|
||||
|
||||
import kha.FastFloat;
|
||||
import kha.arrays.Float32Array;
|
||||
import kha.graphics4.TextureFormat;
|
||||
import kha.graphics4.Usage;
|
||||
|
||||
import iron.data.WorldData;
|
||||
import iron.math.Vec2;
|
||||
import iron.math.Vec3;
|
||||
|
||||
import armory.math.Helper;
|
||||
|
||||
/**
|
||||
Utility class to control the Nishita sky model.
|
||||
**/
|
||||
class Nishita {
|
||||
|
||||
public static var data: NishitaData = null;
|
||||
|
||||
/**
|
||||
Recomputes the nishita lookup table after the density settings changed.
|
||||
Do not call this method on every frame (it's slow)!
|
||||
**/
|
||||
public static function recompute(world: WorldData) {
|
||||
if (world == null || world.raw.nishita_density == null) return;
|
||||
if (data == null) data = new NishitaData();
|
||||
|
||||
var density = world.raw.nishita_density;
|
||||
data.computeLUT(new Vec3(density[0], density[1], density[2]));
|
||||
}
|
||||
|
||||
/** Sets the sky's density parameters and calls `recompute()` afterwards. **/
|
||||
public static function setDensity(world: WorldData, densityAir: FastFloat, densityDust: FastFloat, densityOzone: FastFloat) {
|
||||
if (world == null) return;
|
||||
|
||||
if (world.raw.nishita_density == null) world.raw.nishita_density = new Float32Array(3);
|
||||
var density = world.raw.nishita_density;
|
||||
density[0] = Helper.clamp(densityAir, 0, 10);
|
||||
density[1] = Helper.clamp(densityDust, 0, 10);
|
||||
density[2] = Helper.clamp(densityOzone, 0, 10);
|
||||
|
||||
recompute(world);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
This class holds the precalculated result of the inner scattering integral
|
||||
of the Nishita sky model. The outer integral is calculated in
|
||||
[`armory/Shaders/std/sky.glsl`](https://github.com/armory3d/armory/blob/master/Shaders/std/sky.glsl).
|
||||
|
||||
@see `armory.renderpath.Nishita`
|
||||
**/
|
||||
class NishitaData {
|
||||
|
||||
public var lut: kha.Image;
|
||||
|
||||
/**
|
||||
The amount of individual sample heights stored in the LUT (and the width
|
||||
of the LUT image).
|
||||
**/
|
||||
public static var lutHeightSteps = 128;
|
||||
/**
|
||||
The amount of individual sun angle steps stored in the LUT (and the
|
||||
height of the LUT image).
|
||||
**/
|
||||
public static var lutAngleSteps = 128;
|
||||
|
||||
/**
|
||||
Amount of steps for calculating the inner scattering integral. Heigher
|
||||
values are more precise but take longer to compute.
|
||||
**/
|
||||
public static var jSteps = 8;
|
||||
|
||||
/** Radius of the atmosphere in meters. **/
|
||||
public static var radiusAtmo = 6420000;
|
||||
/**
|
||||
Radius of the planet in meters. The default value is the earth radius as
|
||||
defined in Cycles.
|
||||
**/
|
||||
public static var radiusPlanet = 6360000;
|
||||
|
||||
/** Rayleigh scattering scale parameter. **/
|
||||
public static var rayleighScale = 8e3;
|
||||
/** Mie scattering scale parameter. **/
|
||||
public static var mieScale = 1.2e3;
|
||||
|
||||
public function new() {}
|
||||
|
||||
/** Approximates the density of ozone for a given sample height. **/
|
||||
function getOzoneDensity(height: FastFloat): FastFloat {
|
||||
// Values are taken from Cycles code
|
||||
if (height < 10000.0 || height >= 40000.0) {
|
||||
return 0.0;
|
||||
}
|
||||
if (height < 25000.0) {
|
||||
return (height - 10000.0) / 15000.0;
|
||||
}
|
||||
return -((height - 40000.0) / 15000.0);
|
||||
}
|
||||
|
||||
/**
|
||||
Ray-sphere intersection test that assumes the sphere is centered at the
|
||||
origin. There is no intersection when result.x > result.y. Otherwise
|
||||
this function returns the distances to the two intersection points,
|
||||
which might be equal.
|
||||
**/
|
||||
function raySphereIntersection(rayOrigin: Vec3, rayDirection: Vec3, sphereRadius: Int): Vec2 {
|
||||
// Algorithm is described here: https://en.wikipedia.org/wiki/Line%E2%80%93sphere_intersection
|
||||
var a = rayDirection.dot(rayDirection);
|
||||
var b = 2.0 * rayDirection.dot(rayOrigin);
|
||||
var c = rayOrigin.dot(rayOrigin) - (sphereRadius * sphereRadius);
|
||||
var d = (b * b) - 4.0 * a * c;
|
||||
|
||||
// Ray does not intersect the sphere
|
||||
if (d < 0.0) return new Vec2(1e5, -1e5);
|
||||
|
||||
return new Vec2(
|
||||
(-b - Math.sqrt(d)) / (2.0 * a),
|
||||
(-b + Math.sqrt(d)) / (2.0 * a)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
Computes the LUT texture for the given density values.
|
||||
@param density 3D vector of air density, dust density, ozone density
|
||||
**/
|
||||
public function computeLUT(density: Vec3) {
|
||||
var imageData = new haxe.io.Float32Array(lutHeightSteps * lutAngleSteps * 4);
|
||||
|
||||
for (x in 0...lutHeightSteps) {
|
||||
var height = (x / (lutHeightSteps - 1));
|
||||
|
||||
// Use quadratic height for better horizon precision
|
||||
height *= height;
|
||||
height *= radiusAtmo; // Denormalize
|
||||
|
||||
for (y in 0...lutAngleSteps) {
|
||||
var sunTheta = y / (lutAngleSteps - 1) * 2 - 1;
|
||||
|
||||
// Improve horizon precision
|
||||
// See https://sebh.github.io/publications/egsr2020.pdf (5.3)
|
||||
sunTheta = Helper.sign(sunTheta) * sunTheta * sunTheta;
|
||||
sunTheta = sunTheta * Math.PI / 2 + Math.PI / 2; // Denormalize
|
||||
|
||||
var jODepth = sampleSecondaryRay(height, sunTheta, density);
|
||||
|
||||
var pixelIndex = (x + y * lutHeightSteps) * 4;
|
||||
imageData[pixelIndex + 0] = jODepth.x;
|
||||
imageData[pixelIndex + 1] = jODepth.y;
|
||||
imageData[pixelIndex + 2] = jODepth.z;
|
||||
imageData[pixelIndex + 3] = 1.0; // Unused
|
||||
}
|
||||
}
|
||||
|
||||
lut = kha.Image.fromBytes(imageData.view.buffer, lutHeightSteps, lutAngleSteps, TextureFormat.RGBA128, Usage.StaticUsage);
|
||||
}
|
||||
|
||||
/**
|
||||
Calculates the integral for the secondary ray.
|
||||
**/
|
||||
public function sampleSecondaryRay(height: FastFloat, sunTheta: FastFloat, density: Vec3): Vec3 {
|
||||
// Reconstruct values from the shader
|
||||
var iPos = new Vec3(0, 0, height + radiusPlanet);
|
||||
var pSun = new Vec3(0.0, Math.sin(sunTheta), Math.cos(sunTheta)).normalize();
|
||||
|
||||
var jTime: FastFloat = 0.0;
|
||||
var jStepSize: FastFloat = raySphereIntersection(iPos, pSun, radiusAtmo).y / jSteps;
|
||||
|
||||
// Optical depth accumulators for the secondary ray (Rayleigh, Mie, ozone)
|
||||
var jODepth = new Vec3();
|
||||
|
||||
for (i in 0...jSteps) {
|
||||
|
||||
// Calculate the secondary ray sample position and height
|
||||
var jPos = iPos.clone().add(pSun.clone().mult(jTime + jStepSize * 0.5));
|
||||
var jHeight = jPos.length() - radiusPlanet;
|
||||
|
||||
// Accumulate optical depth
|
||||
var optDepthRayleigh = Math.exp(-jHeight / rayleighScale) * density.x;
|
||||
var optDepthMie = Math.exp(-jHeight / mieScale) * density.y;
|
||||
var optDepthOzone = getOzoneDensity(jHeight) * density.z;
|
||||
jODepth.addf(optDepthRayleigh, optDepthMie, optDepthOzone);
|
||||
|
||||
jTime += jStepSize;
|
||||
}
|
||||
|
||||
return jODepth.mult(jStepSize);
|
||||
}
|
||||
}
|
|
@ -52,7 +52,6 @@ class PhysicsWorld extends Trait {
|
|||
public var rbMap: Map<Int, RigidBody>;
|
||||
public var conMap: Map<Int, PhysicsConstraint>;
|
||||
public var timeScale = 1.0;
|
||||
var timeStep = 1 / 60;
|
||||
var maxSteps = 1;
|
||||
public var solverIterations = 10;
|
||||
public var hitPointWorld = new Vec4();
|
||||
|
@ -67,7 +66,7 @@ class PhysicsWorld extends Trait {
|
|||
public static var physTime = 0.0;
|
||||
#end
|
||||
|
||||
public function new(timeScale = 1.0, timeStep = 1 / 60, solverIterations = 10) {
|
||||
public function new(timeScale = 1.0, maxSteps = 10, solverIterations = 10) {
|
||||
super();
|
||||
|
||||
if (nullvec) {
|
||||
|
@ -81,8 +80,7 @@ class PhysicsWorld extends Trait {
|
|||
sceneRemoved = false;
|
||||
|
||||
this.timeScale = timeScale;
|
||||
this.timeStep = timeStep;
|
||||
maxSteps = timeStep < 1 / 60 ? 10 : 1;
|
||||
this.maxSteps = maxSteps;
|
||||
this.solverIterations = solverIterations;
|
||||
|
||||
// First scene
|
||||
|
@ -269,7 +267,13 @@ class PhysicsWorld extends Trait {
|
|||
|
||||
if (preUpdates != null) for (f in preUpdates) f();
|
||||
|
||||
world.stepSimulation(timeStep, maxSteps, t);
|
||||
//Bullet physics fixed timescale
|
||||
var fixedTime = 1.0 / 60;
|
||||
|
||||
//This condition must be satisfied to not loose time
|
||||
var currMaxSteps = t < (fixedTime * maxSteps) ? maxSteps : 1;
|
||||
|
||||
world.stepSimulation(t, currMaxSteps, fixedTime);
|
||||
updateContacts();
|
||||
|
||||
for (rb in rbMap) @:privateAccess rb.physicsUpdate();
|
||||
|
|
|
@ -2635,7 +2635,7 @@ Make sure the mesh only has tris/quads.""")
|
|||
|
||||
rbw = self.scene.rigidbody_world
|
||||
if rbw is not None and rbw.enabled:
|
||||
out_trait['parameters'] = [str(rbw.time_scale), str(1 / rbw.steps_per_second), str(rbw.solver_iterations)]
|
||||
out_trait['parameters'] = [str(rbw.time_scale), str(rbw.substeps_per_frame), str(rbw.solver_iterations)]
|
||||
|
||||
self.output['traits'].append(out_trait)
|
||||
|
||||
|
@ -2882,6 +2882,7 @@ Make sure the mesh only has tris/quads.""")
|
|||
out_world['sun_direction'] = list(world.arm_envtex_sun_direction)
|
||||
out_world['turbidity'] = world.arm_envtex_turbidity
|
||||
out_world['ground_albedo'] = world.arm_envtex_ground_albedo
|
||||
out_world['nishita_density'] = list(world.arm_nishita_density)
|
||||
|
||||
disable_hdr = world.arm_envtex_name.endswith('.jpg')
|
||||
|
||||
|
@ -2896,16 +2897,9 @@ Make sure the mesh only has tris/quads.""")
|
|||
rpdat = arm.utils.get_rp()
|
||||
solid_mat = rpdat.arm_material_model == 'Solid'
|
||||
arm_irradiance = rpdat.arm_irradiance and not solid_mat
|
||||
arm_radiance = False
|
||||
radtex = world.arm_envtex_name.rsplit('.', 1)[0]
|
||||
arm_radiance = rpdat.arm_radiance
|
||||
radtex = world.arm_envtex_name.rsplit('.', 1)[0] # Remove file extension
|
||||
irrsharmonics = world.arm_envtex_irr_name
|
||||
|
||||
# Radiance
|
||||
if '_EnvTex' in world.world_defs:
|
||||
arm_radiance = rpdat.arm_radiance
|
||||
elif '_EnvSky' in world.world_defs:
|
||||
arm_radiance = rpdat.arm_radiance
|
||||
radtex = 'hosek'
|
||||
num_mips = world.arm_envtex_num_mips
|
||||
strength = world.arm_envtex_strength
|
||||
|
||||
|
|
|
@ -1 +1 @@
|
|||
__all__ = ('Operators', 'Properties', 'Utility', 'Keymap')
|
||||
__all__ = ('Operators', 'Panels', 'Properties', 'Preferences', 'Utility', 'Keymap')
|
BIN
blender/arm/lightmapper/assets/TLM_Overlay.png
Normal file
BIN
blender/arm/lightmapper/assets/TLM_Overlay.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 22 KiB |
Binary file not shown.
|
@ -9,6 +9,8 @@ def register():
|
|||
winman = bpy.context.window_manager
|
||||
keyman = winman.keyconfigs.addon.keymaps.new(name='Window', space_type='EMPTY', region_type="WINDOW")
|
||||
|
||||
|
||||
#TODO - In Armory3D, merge with keymap.py
|
||||
keyman.keymap_items.new('tlm.build_lightmaps', type='F6', value='PRESS')
|
||||
keyman.keymap_items.new('tlm.clean_lightmaps', type='F7', value='PRESS')
|
||||
tlm_keymaps.append(keyman)
|
||||
|
|
|
@ -6,10 +6,11 @@ classes = [
|
|||
tlm.TLM_BuildLightmaps,
|
||||
tlm.TLM_CleanLightmaps,
|
||||
tlm.TLM_ExploreLightmaps,
|
||||
tlm.TLM_EnableSelection,
|
||||
tlm.TLM_EnableSet,
|
||||
tlm.TLM_DisableSelection,
|
||||
tlm.TLM_RemoveLightmapUV,
|
||||
tlm.TLM_SelectLightmapped,
|
||||
tlm.TLM_ToggleTexelDensity,
|
||||
installopencv.TLM_Install_OpenCV,
|
||||
tlm.TLM_AtlasListNewItem,
|
||||
tlm.TLM_AtlastListDeleteItem,
|
||||
|
@ -20,6 +21,8 @@ classes = [
|
|||
tlm.TLM_StartServer,
|
||||
tlm.TLM_BuildEnvironmentProbes,
|
||||
tlm.TLM_CleanBuildEnvironmentProbes,
|
||||
tlm.TLM_PrepareUVMaps,
|
||||
tlm.TLM_LoadLightmaps,
|
||||
imagetools.TLM_ImageUpscale,
|
||||
imagetools.TLM_ImageDownscale
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import bpy, os, time
|
||||
import bpy, os, time, importlib
|
||||
|
||||
class TLM_ImageUpscale(bpy.types.Operator):
|
||||
bl_idname = "tlm.image_upscale"
|
||||
|
@ -8,6 +8,69 @@ class TLM_ImageUpscale(bpy.types.Operator):
|
|||
|
||||
def invoke(self, context, event):
|
||||
|
||||
cv2 = importlib.util.find_spec("cv2")
|
||||
|
||||
if cv2 is None:
|
||||
print("CV2 not found - Ignoring filtering")
|
||||
return 0
|
||||
else:
|
||||
cv2 = importlib.__import__("cv2")
|
||||
|
||||
for area in bpy.context.screen.areas:
|
||||
if area.type == "IMAGE_EDITOR":
|
||||
active_image = area.spaces.active.image
|
||||
|
||||
if active_image.source == "FILE":
|
||||
img_path = active_image.filepath_raw
|
||||
filename = os.path.basename(img_path)
|
||||
|
||||
basename = os.path.splitext(filename)[0]
|
||||
extension = os.path.splitext(filename)[1]
|
||||
|
||||
size_x = active_image.size[0]
|
||||
size_y = active_image.size[1]
|
||||
|
||||
dir_path = os.path.dirname(os.path.realpath(img_path))
|
||||
|
||||
#newfile = os.path.join(dir_path, basename + "_" + str(size_x) + "_" + str(size_y) + extension)
|
||||
newfile = os.path.join(dir_path, basename + extension)
|
||||
os.rename(img_path, newfile)
|
||||
|
||||
basefile = cv2.imread(newfile, cv2.IMREAD_UNCHANGED)
|
||||
|
||||
scale_percent = 200 # percent of original size
|
||||
width = int(basefile.shape[1] * scale_percent / 100)
|
||||
height = int(basefile.shape[0] * scale_percent / 100)
|
||||
dim = (width, height)
|
||||
|
||||
if active_image.TLM_ImageProperties.tlm_image_scale_method == "Nearest":
|
||||
interp = cv2.INTER_NEAREST
|
||||
elif active_image.TLM_ImageProperties.tlm_image_scale_method == "Area":
|
||||
interp = cv2.INTER_AREA
|
||||
elif active_image.TLM_ImageProperties.tlm_image_scale_method == "Linear":
|
||||
interp = cv2.INTER_LINEAR
|
||||
elif active_image.TLM_ImageProperties.tlm_image_scale_method == "Cubic":
|
||||
interp = cv2.INTER_CUBIC
|
||||
elif active_image.TLM_ImageProperties.tlm_image_scale_method == "Lanczos":
|
||||
interp = cv2.INTER_LANCZOS4
|
||||
|
||||
resized = cv2.resize(basefile, dim, interpolation = interp)
|
||||
|
||||
#resizedFile = os.path.join(dir_path, basename + "_" + str(width) + "_" + str(height) + extension)
|
||||
resizedFile = os.path.join(dir_path, basename + extension)
|
||||
|
||||
cv2.imwrite(resizedFile, resized)
|
||||
|
||||
active_image.filepath_raw = resizedFile
|
||||
bpy.ops.image.reload()
|
||||
|
||||
print(newfile)
|
||||
print(img_path)
|
||||
|
||||
else:
|
||||
|
||||
print("Please save image")
|
||||
|
||||
print("Upscale")
|
||||
|
||||
return {'RUNNING_MODAL'}
|
||||
|
@ -20,6 +83,111 @@ class TLM_ImageDownscale(bpy.types.Operator):
|
|||
|
||||
def invoke(self, context, event):
|
||||
|
||||
print("Downscale")
|
||||
cv2 = importlib.util.find_spec("cv2")
|
||||
|
||||
if cv2 is None:
|
||||
print("CV2 not found - Ignoring filtering")
|
||||
return 0
|
||||
else:
|
||||
cv2 = importlib.__import__("cv2")
|
||||
|
||||
for area in bpy.context.screen.areas:
|
||||
if area.type == "IMAGE_EDITOR":
|
||||
active_image = area.spaces.active.image
|
||||
|
||||
if active_image.source == "FILE":
|
||||
img_path = active_image.filepath_raw
|
||||
filename = os.path.basename(img_path)
|
||||
|
||||
basename = os.path.splitext(filename)[0]
|
||||
extension = os.path.splitext(filename)[1]
|
||||
|
||||
size_x = active_image.size[0]
|
||||
size_y = active_image.size[1]
|
||||
|
||||
dir_path = os.path.dirname(os.path.realpath(img_path))
|
||||
|
||||
#newfile = os.path.join(dir_path, basename + "_" + str(size_x) + "_" + str(size_y) + extension)
|
||||
newfile = os.path.join(dir_path, basename + extension)
|
||||
os.rename(img_path, newfile)
|
||||
|
||||
basefile = cv2.imread(newfile, cv2.IMREAD_UNCHANGED)
|
||||
|
||||
scale_percent = 50 # percent of original size
|
||||
width = int(basefile.shape[1] * scale_percent / 100)
|
||||
height = int(basefile.shape[0] * scale_percent / 100)
|
||||
dim = (width, height)
|
||||
|
||||
if dim[0] > 1 or dim[1] > 1:
|
||||
|
||||
if active_image.TLM_ImageProperties.tlm_image_scale_method == "Nearest":
|
||||
interp = cv2.INTER_NEAREST
|
||||
elif active_image.TLM_ImageProperties.tlm_image_scale_method == "Area":
|
||||
interp = cv2.INTER_AREA
|
||||
elif active_image.TLM_ImageProperties.tlm_image_scale_method == "Linear":
|
||||
interp = cv2.INTER_LINEAR
|
||||
elif active_image.TLM_ImageProperties.tlm_image_scale_method == "Cubic":
|
||||
interp = cv2.INTER_CUBIC
|
||||
elif active_image.TLM_ImageProperties.tlm_image_scale_method == "Lanczos":
|
||||
interp = cv2.INTER_LANCZOS4
|
||||
|
||||
resized = cv2.resize(basefile, dim, interpolation = interp)
|
||||
|
||||
#resizedFile = os.path.join(dir_path, basename + "_" + str(width) + "_" + str(height) + extension)
|
||||
resizedFile = os.path.join(dir_path, basename + extension)
|
||||
|
||||
cv2.imwrite(resizedFile, resized)
|
||||
|
||||
active_image.filepath_raw = resizedFile
|
||||
bpy.ops.image.reload()
|
||||
|
||||
print(newfile)
|
||||
print(img_path)
|
||||
|
||||
else:
|
||||
|
||||
print("Please save image")
|
||||
|
||||
print("Upscale")
|
||||
|
||||
return {'RUNNING_MODAL'}
|
||||
|
||||
class TLM_ImageSwitchUp(bpy.types.Operator):
|
||||
bl_idname = "tlm.image_switchup"
|
||||
bl_label = "Quickswitch Up"
|
||||
bl_description = "Switches to a cached upscaled image"
|
||||
bl_options = {'REGISTER', 'UNDO'}
|
||||
|
||||
def invoke(self, context, event):
|
||||
|
||||
for area in bpy.context.screen.areas:
|
||||
if area.type == "IMAGE_EDITOR":
|
||||
active_image = area.spaces.active.image
|
||||
|
||||
if active_image.source == "FILE":
|
||||
img_path = active_image.filepath_raw
|
||||
filename = os.path.basename(img_path)
|
||||
|
||||
print("Switch up")
|
||||
|
||||
return {'RUNNING_MODAL'}
|
||||
|
||||
class TLM_ImageSwitchDown(bpy.types.Operator):
|
||||
bl_idname = "tlm.image_switchdown"
|
||||
bl_label = "Quickswitch Down"
|
||||
bl_description = "Switches to a cached downscaled image"
|
||||
bl_options = {'REGISTER', 'UNDO'}
|
||||
|
||||
def invoke(self, context, event):
|
||||
|
||||
for area in bpy.context.screen.areas:
|
||||
if area.type == "IMAGE_EDITOR":
|
||||
active_image = area.spaces.active.image
|
||||
|
||||
if active_image.source == "FILE":
|
||||
img_path = active_image.filepath_raw
|
||||
filename = os.path.basename(img_path)
|
||||
|
||||
print("Switch Down")
|
||||
|
||||
return {'RUNNING_MODAL'}
|
|
@ -21,7 +21,10 @@ class TLM_Install_OpenCV(bpy.types.Operator):
|
|||
|
||||
print("Module OpenCV")
|
||||
|
||||
pythonbinpath = bpy.app.binary_path_python
|
||||
if (2, 91, 0) > bpy.app.version:
|
||||
pythonbinpath = bpy.app.binary_path_python
|
||||
else:
|
||||
pythonbinpath = sys.executable
|
||||
|
||||
if platform.system() == "Windows":
|
||||
pythonlibpath = os.path.join(os.path.dirname(os.path.dirname(pythonbinpath)), "lib")
|
||||
|
|
|
@ -1,9 +1,36 @@
|
|||
import bpy, os, time, blf, webbrowser, platform
|
||||
import bpy, os, time, blf, webbrowser, platform, numpy, bmesh
|
||||
import math, subprocess, multiprocessing
|
||||
from .. utility import utility
|
||||
from .. utility import build
|
||||
from .. utility.cycles import cache
|
||||
from .. network import server
|
||||
|
||||
def setObjectLightmapByWeight(minimumRes, maximumRes, objWeight):
|
||||
|
||||
availableResolutions = [32,64,128,256,512,1024,2048,4096,8192]
|
||||
|
||||
minRes = minimumRes
|
||||
minResIdx = availableResolutions.index(minRes)
|
||||
maxRes = maximumRes
|
||||
maxResIdx = availableResolutions.index(maxRes)
|
||||
|
||||
exampleWeight = objWeight
|
||||
|
||||
if minResIdx == maxResIdx:
|
||||
pass
|
||||
else:
|
||||
|
||||
increment = 1.0/(maxResIdx-minResIdx)
|
||||
|
||||
assortedRange = []
|
||||
|
||||
for a in numpy.arange(0.0, 1.0, increment):
|
||||
assortedRange.append(round(a, 2))
|
||||
|
||||
assortedRange.append(1.0)
|
||||
nearestWeight = min(assortedRange, key=lambda x:abs(x - exampleWeight))
|
||||
return (availableResolutions[assortedRange.index(nearestWeight) + minResIdx])
|
||||
|
||||
class TLM_BuildLightmaps(bpy.types.Operator):
|
||||
bl_idname = "tlm.build_lightmaps"
|
||||
bl_label = "Build Lightmaps"
|
||||
|
@ -52,13 +79,13 @@ class TLM_CleanLightmaps(bpy.types.Operator):
|
|||
for file in os.listdir(dirpath):
|
||||
os.remove(os.path.join(dirpath + "/" + file))
|
||||
|
||||
for obj in bpy.data.objects:
|
||||
if obj.type == "MESH":
|
||||
for obj in bpy.context.scene.objects:
|
||||
if obj.type == 'MESH' and obj.name in bpy.context.view_layer.objects:
|
||||
if obj.TLM_ObjectProperties.tlm_mesh_lightmap_use:
|
||||
cache.backup_material_restore(obj)
|
||||
|
||||
for obj in bpy.data.objects:
|
||||
if obj.type == "MESH":
|
||||
for obj in bpy.context.scene.objects:
|
||||
if obj.type == 'MESH' and obj.name in bpy.context.view_layer.objects:
|
||||
if obj.TLM_ObjectProperties.tlm_mesh_lightmap_use:
|
||||
cache.backup_material_rename(obj)
|
||||
|
||||
|
@ -75,8 +102,8 @@ class TLM_CleanLightmaps(bpy.types.Operator):
|
|||
if image.name.endswith("_baked"):
|
||||
bpy.data.images.remove(image, do_unlink=True)
|
||||
|
||||
for obj in bpy.data.objects:
|
||||
if obj.type == "MESH":
|
||||
for obj in bpy.context.scene.objects:
|
||||
if obj.type == 'MESH' and obj.name in bpy.context.view_layer.objects:
|
||||
if obj.TLM_ObjectProperties.tlm_mesh_lightmap_use:
|
||||
if obj.TLM_ObjectProperties.tlm_postpack_object:
|
||||
|
||||
|
@ -92,14 +119,17 @@ class TLM_CleanLightmaps(bpy.types.Operator):
|
|||
bpy.ops.object.select_all(action='DESELECT')
|
||||
obj.select_set(True)
|
||||
bpy.context.view_layer.objects.active = obj
|
||||
#print(x)
|
||||
|
||||
uv_layers = obj.data.uv_layers
|
||||
|
||||
if not obj.TLM_ObjectProperties.tlm_use_default_channel:
|
||||
uv_channel = obj.TLM_ObjectProperties.tlm_uv_channel
|
||||
else:
|
||||
uv_channel = "UVMap_Lightmap"
|
||||
|
||||
for i in range(0, len(uv_layers)):
|
||||
if uv_layers[i].name == 'UVMap_Lightmap':
|
||||
if uv_layers[i].name == uv_channel:
|
||||
uv_layers.active_index = i
|
||||
if bpy.context.scene.TLM_SceneProperties.tlm_verbose:
|
||||
print("Lightmap shift A")
|
||||
break
|
||||
|
||||
bpy.ops.object.mode_set(mode='EDIT')
|
||||
|
@ -111,9 +141,11 @@ class TLM_CleanLightmaps(bpy.types.Operator):
|
|||
bpy.ops.object.mode_set(mode='OBJECT')
|
||||
|
||||
if bpy.context.scene.TLM_SceneProperties.tlm_verbose:
|
||||
#print(obj.name + ": Active UV: " + obj.data.uv_layers[obj.data.uv_layers.active_index].name)
|
||||
print("Resized for obj: " + obj.name)
|
||||
|
||||
if "Lightmap" in obj:
|
||||
del obj["Lightmap"]
|
||||
|
||||
return {'FINISHED'}
|
||||
|
||||
class TLM_ExploreLightmaps(bpy.types.Operator):
|
||||
|
@ -153,63 +185,285 @@ class TLM_ExploreLightmaps(bpy.types.Operator):
|
|||
|
||||
return {'FINISHED'}
|
||||
|
||||
class TLM_EnableSelection(bpy.types.Operator):
|
||||
"""Enable for selection"""
|
||||
bl_idname = "tlm.enable_selection"
|
||||
bl_label = "Enable for selection"
|
||||
bl_description = "Enable for selection"
|
||||
class TLM_EnableSet(bpy.types.Operator):
|
||||
"""Enable for set"""
|
||||
bl_idname = "tlm.enable_set"
|
||||
bl_label = "Enable for set"
|
||||
bl_description = "Enable for set"
|
||||
bl_options = {'REGISTER', 'UNDO'}
|
||||
|
||||
def execute(self, context):
|
||||
|
||||
scene = context.scene
|
||||
|
||||
for obj in bpy.context.selected_objects:
|
||||
obj.TLM_ObjectProperties.tlm_mesh_lightmap_use = True
|
||||
weightList = {} #ObjName : [Dimension,Weight]
|
||||
max = 0
|
||||
|
||||
if scene.TLM_SceneProperties.tlm_override_object_settings:
|
||||
obj.TLM_ObjectProperties.tlm_mesh_lightmap_resolution = scene.TLM_SceneProperties.tlm_mesh_lightmap_resolution
|
||||
obj.TLM_ObjectProperties.tlm_mesh_lightmap_unwrap_mode = scene.TLM_SceneProperties.tlm_mesh_lightmap_unwrap_mode
|
||||
obj.TLM_ObjectProperties.tlm_mesh_unwrap_margin = scene.TLM_SceneProperties.tlm_mesh_unwrap_margin
|
||||
obj.TLM_ObjectProperties.tlm_postpack_object = scene.TLM_SceneProperties.tlm_postpack_object
|
||||
if bpy.context.scene.TLM_SceneProperties.tlm_utility_set == "Scene":
|
||||
for obj in bpy.context.scene.objects:
|
||||
if obj.type == "MESH":
|
||||
|
||||
if obj.TLM_ObjectProperties.tlm_mesh_lightmap_unwrap_mode == "AtlasGroupA":
|
||||
obj.TLM_ObjectProperties.tlm_atlas_pointer = scene.TLM_SceneProperties.tlm_atlas_pointer
|
||||
print("Enabling for scene: " + obj.name)
|
||||
|
||||
bpy.context.view_layer.objects.active = obj
|
||||
obj.select_set(True)
|
||||
bpy.ops.object.transform_apply(location=False, rotation=True, scale=True)
|
||||
|
||||
obj.TLM_ObjectProperties.tlm_mesh_lightmap_use = True
|
||||
|
||||
obj.TLM_ObjectProperties.tlm_mesh_lightmap_unwrap_mode = bpy.context.scene.TLM_SceneProperties.tlm_mesh_lightmap_unwrap_mode
|
||||
obj.TLM_ObjectProperties.tlm_mesh_unwrap_margin = bpy.context.scene.TLM_SceneProperties.tlm_mesh_unwrap_margin
|
||||
obj.TLM_ObjectProperties.tlm_postpack_object = bpy.context.scene.TLM_SceneProperties.tlm_postpack_object
|
||||
|
||||
obj.TLM_ObjectProperties.tlm_postatlas_pointer = scene.TLM_SceneProperties.tlm_postatlas_pointer
|
||||
if obj.TLM_ObjectProperties.tlm_mesh_lightmap_unwrap_mode == "AtlasGroupA":
|
||||
obj.TLM_ObjectProperties.tlm_atlas_pointer = bpy.context.scene.TLM_SceneProperties.tlm_atlas_pointer
|
||||
|
||||
obj.TLM_ObjectProperties.tlm_postatlas_pointer = bpy.context.scene.TLM_SceneProperties.tlm_postatlas_pointer
|
||||
|
||||
if bpy.context.scene.TLM_SceneProperties.tlm_resolution_weight == "Single":
|
||||
obj.TLM_ObjectProperties.tlm_mesh_lightmap_resolution = scene.TLM_SceneProperties.tlm_mesh_lightmap_resolution
|
||||
elif bpy.context.scene.TLM_SceneProperties.tlm_resolution_weight == "Dimension":
|
||||
obj_dimensions = obj.dimensions.x * obj.dimensions.y * obj.dimensions.z
|
||||
weightList[obj.name] = [obj_dimensions, 0]
|
||||
if obj_dimensions > max:
|
||||
max = obj_dimensions
|
||||
elif bpy.context.scene.TLM_SceneProperties.tlm_resolution_weight == "Surface":
|
||||
bm = bmesh.new()
|
||||
bm.from_mesh(obj.data)
|
||||
area = sum(f.calc_area() for f in bm.faces)
|
||||
weightList[obj.name] = [area, 0]
|
||||
if area > max:
|
||||
max = area
|
||||
elif bpy.context.scene.TLM_SceneProperties.tlm_resolution_weight == "Volume":
|
||||
bm = bmesh.new()
|
||||
bm.from_mesh(obj.data)
|
||||
volume = float( bm.calc_volume())
|
||||
weightList[obj.name] = [volume, 0]
|
||||
if volume > max:
|
||||
max = volume
|
||||
|
||||
elif bpy.context.scene.TLM_SceneProperties.tlm_utility_set == "Selection":
|
||||
for obj in bpy.context.selected_objects:
|
||||
if obj.type == "MESH":
|
||||
|
||||
print("Enabling for selection: " + obj.name)
|
||||
|
||||
bpy.context.view_layer.objects.active = obj
|
||||
obj.select_set(True)
|
||||
bpy.ops.object.transform_apply(location=False, rotation=True, scale=True)
|
||||
|
||||
obj.TLM_ObjectProperties.tlm_mesh_lightmap_use = True
|
||||
|
||||
obj.TLM_ObjectProperties.tlm_mesh_lightmap_unwrap_mode = bpy.context.scene.TLM_SceneProperties.tlm_mesh_lightmap_unwrap_mode
|
||||
obj.TLM_ObjectProperties.tlm_mesh_unwrap_margin = bpy.context.scene.TLM_SceneProperties.tlm_mesh_unwrap_margin
|
||||
obj.TLM_ObjectProperties.tlm_postpack_object = bpy.context.scene.TLM_SceneProperties.tlm_postpack_object
|
||||
|
||||
if obj.TLM_ObjectProperties.tlm_mesh_lightmap_unwrap_mode == "AtlasGroupA":
|
||||
obj.TLM_ObjectProperties.tlm_atlas_pointer = bpy.context.scene.TLM_SceneProperties.tlm_atlas_pointer
|
||||
|
||||
obj.TLM_ObjectProperties.tlm_postatlas_pointer = bpy.context.scene.TLM_SceneProperties.tlm_postatlas_pointer
|
||||
|
||||
if bpy.context.scene.TLM_SceneProperties.tlm_resolution_weight == "Single":
|
||||
obj.TLM_ObjectProperties.tlm_mesh_lightmap_resolution = scene.TLM_SceneProperties.tlm_mesh_lightmap_resolution
|
||||
elif bpy.context.scene.TLM_SceneProperties.tlm_resolution_weight == "Dimension":
|
||||
obj_dimensions = obj.dimensions.x * obj.dimensions.y * obj.dimensions.z
|
||||
weightList[obj.name] = [obj_dimensions, 0]
|
||||
if obj_dimensions > max:
|
||||
max = obj_dimensions
|
||||
elif bpy.context.scene.TLM_SceneProperties.tlm_resolution_weight == "Surface":
|
||||
bm = bmesh.new()
|
||||
bm.from_mesh(obj.data)
|
||||
area = sum(f.calc_area() for f in bm.faces)
|
||||
weightList[obj.name] = [area, 0]
|
||||
if area > max:
|
||||
max = area
|
||||
elif bpy.context.scene.TLM_SceneProperties.tlm_resolution_weight == "Volume":
|
||||
bm = bmesh.new()
|
||||
bm.from_mesh(obj.data)
|
||||
volume = float( bm.calc_volume())
|
||||
weightList[obj.name] = [volume, 0]
|
||||
if volume > max:
|
||||
max = volume
|
||||
|
||||
else: #Enabled
|
||||
for obj in bpy.context.scene.objects:
|
||||
if obj.type == "MESH":
|
||||
if obj.TLM_ObjectProperties.tlm_mesh_lightmap_use:
|
||||
|
||||
print("Enabling for designated: " + obj.name)
|
||||
|
||||
bpy.context.view_layer.objects.active = obj
|
||||
obj.select_set(True)
|
||||
bpy.ops.object.transform_apply(location=False, rotation=True, scale=True)
|
||||
|
||||
obj.TLM_ObjectProperties.tlm_mesh_lightmap_use = True
|
||||
|
||||
obj.TLM_ObjectProperties.tlm_mesh_lightmap_unwrap_mode = bpy.context.scene.TLM_SceneProperties.tlm_mesh_lightmap_unwrap_mode
|
||||
obj.TLM_ObjectProperties.tlm_mesh_unwrap_margin = bpy.context.scene.TLM_SceneProperties.tlm_mesh_unwrap_margin
|
||||
obj.TLM_ObjectProperties.tlm_postpack_object = bpy.context.scene.TLM_SceneProperties.tlm_postpack_object
|
||||
|
||||
if obj.TLM_ObjectProperties.tlm_mesh_lightmap_unwrap_mode == "AtlasGroupA":
|
||||
obj.TLM_ObjectProperties.tlm_atlas_pointer = bpy.context.scene.TLM_SceneProperties.tlm_atlas_pointer
|
||||
|
||||
obj.TLM_ObjectProperties.tlm_postatlas_pointer = bpy.context.scene.TLM_SceneProperties.tlm_postatlas_pointer
|
||||
|
||||
if bpy.context.scene.TLM_SceneProperties.tlm_resolution_weight == "Single":
|
||||
obj.TLM_ObjectProperties.tlm_mesh_lightmap_resolution = scene.TLM_SceneProperties.tlm_mesh_lightmap_resolution
|
||||
elif bpy.context.scene.TLM_SceneProperties.tlm_resolution_weight == "Dimension":
|
||||
obj_dimensions = obj.dimensions.x * obj.dimensions.y * obj.dimensions.z
|
||||
weightList[obj.name] = [obj_dimensions, 0]
|
||||
if obj_dimensions > max:
|
||||
max = obj_dimensions
|
||||
elif bpy.context.scene.TLM_SceneProperties.tlm_resolution_weight == "Surface":
|
||||
bm = bmesh.new()
|
||||
bm.from_mesh(obj.data)
|
||||
area = sum(f.calc_area() for f in bm.faces)
|
||||
weightList[obj.name] = [area, 0]
|
||||
if area > max:
|
||||
max = area
|
||||
elif bpy.context.scene.TLM_SceneProperties.tlm_resolution_weight == "Volume":
|
||||
bm = bmesh.new()
|
||||
bm.from_mesh(obj.data)
|
||||
volume = float( bm.calc_volume())
|
||||
weightList[obj.name] = [volume, 0]
|
||||
if volume > max:
|
||||
max = volume
|
||||
|
||||
if bpy.context.scene.TLM_SceneProperties.tlm_utility_set == "Scene":
|
||||
for obj in bpy.context.scene.objects:
|
||||
if obj.type == "MESH":
|
||||
|
||||
if bpy.context.scene.TLM_SceneProperties.tlm_resolution_weight != "Single":
|
||||
for key in weightList:
|
||||
weightList[obj.name][1] = weightList[obj.name][0] / max
|
||||
a = setObjectLightmapByWeight(int(bpy.context.scene.TLM_SceneProperties.tlm_resolution_min), int(bpy.context.scene.TLM_SceneProperties.tlm_resolution_max), weightList[obj.name][1])
|
||||
print(str(a) + "/" + str(weightList[obj.name][1]))
|
||||
print("Scale: " + str(weightList[obj.name][0]))
|
||||
print("Obj: " + obj.name)
|
||||
obj.TLM_ObjectProperties.tlm_mesh_lightmap_resolution = str(a)
|
||||
|
||||
elif bpy.context.scene.TLM_SceneProperties.tlm_utility_set == "Selection":
|
||||
for obj in bpy.context.selected_objects:
|
||||
if obj.type == "MESH":
|
||||
|
||||
if bpy.context.scene.TLM_SceneProperties.tlm_resolution_weight != "Single":
|
||||
for key in weightList:
|
||||
weightList[obj.name][1] = weightList[obj.name][0] / max
|
||||
a = setObjectLightmapByWeight(int(bpy.context.scene.TLM_SceneProperties.tlm_resolution_min), int(bpy.context.scene.TLM_SceneProperties.tlm_resolution_max), weightList[obj.name][1])
|
||||
print(str(a) + "/" + str(weightList[obj.name][1]))
|
||||
print("Scale: " + str(weightList[obj.name][0]))
|
||||
print("Obj: " + obj.name)
|
||||
obj.TLM_ObjectProperties.tlm_mesh_lightmap_resolution = str(a)
|
||||
|
||||
|
||||
else: #Enabled
|
||||
for obj in bpy.context.scene.objects:
|
||||
if obj.type == "MESH":
|
||||
if obj.TLM_ObjectProperties.tlm_mesh_lightmap_use:
|
||||
|
||||
if bpy.context.scene.TLM_SceneProperties.tlm_resolution_weight != "Single":
|
||||
for key in weightList:
|
||||
weightList[obj.name][1] = weightList[obj.name][0] / max
|
||||
a = setObjectLightmapByWeight(int(bpy.context.scene.TLM_SceneProperties.tlm_resolution_min), int(bpy.context.scene.TLM_SceneProperties.tlm_resolution_max), weightList[obj.name][1])
|
||||
print(str(a) + "/" + str(weightList[obj.name][1]))
|
||||
print("Scale: " + str(weightList[obj.name][0]))
|
||||
print("Obj: " + obj.name)
|
||||
print("")
|
||||
obj.TLM_ObjectProperties.tlm_mesh_lightmap_resolution = str(a)
|
||||
|
||||
return{'FINISHED'}
|
||||
|
||||
class TLM_DisableSelection(bpy.types.Operator):
|
||||
"""Disable for selection"""
|
||||
"""Disable for set"""
|
||||
bl_idname = "tlm.disable_selection"
|
||||
bl_label = "Disable for selection"
|
||||
bl_label = "Disable for set"
|
||||
bl_description = "Disable for selection"
|
||||
bl_options = {'REGISTER', 'UNDO'}
|
||||
|
||||
def execute(self, context):
|
||||
|
||||
for obj in bpy.context.selected_objects:
|
||||
obj.TLM_ObjectProperties.tlm_mesh_lightmap_use = False
|
||||
scene = context.scene
|
||||
|
||||
weightList = {} #ObjName : [Dimension,Weight]
|
||||
max = 0
|
||||
|
||||
if bpy.context.scene.TLM_SceneProperties.tlm_utility_set == "Scene":
|
||||
for obj in bpy.context.scene.objects:
|
||||
if obj.type == "MESH":
|
||||
|
||||
obj.TLM_ObjectProperties.tlm_mesh_lightmap_use = False
|
||||
|
||||
elif bpy.context.scene.TLM_SceneProperties.tlm_utility_set == "Selection":
|
||||
for obj in bpy.context.selected_objects:
|
||||
if obj.type == "MESH":
|
||||
|
||||
obj.TLM_ObjectProperties.tlm_mesh_lightmap_use = False
|
||||
|
||||
|
||||
else: #Enabled
|
||||
for obj in bpy.context.scene.objects:
|
||||
if obj.type == "MESH":
|
||||
if obj.TLM_ObjectProperties.tlm_mesh_lightmap_use:
|
||||
|
||||
obj.TLM_ObjectProperties.tlm_mesh_lightmap_use = False
|
||||
|
||||
|
||||
return{'FINISHED'}
|
||||
|
||||
class TLM_RemoveLightmapUV(bpy.types.Operator):
|
||||
"""Remove Lightmap UV for selection"""
|
||||
"""Remove Lightmap UV for set"""
|
||||
bl_idname = "tlm.remove_uv_selection"
|
||||
bl_label = "Remove Lightmap UV"
|
||||
bl_description = "Remove Lightmap UV for selection"
|
||||
bl_description = "Remove Lightmap UV for set"
|
||||
bl_options = {'REGISTER', 'UNDO'}
|
||||
|
||||
def execute(self, context):
|
||||
|
||||
for obj in bpy.context.selected_objects:
|
||||
if obj.type == "MESH":
|
||||
uv_layers = obj.data.uv_layers
|
||||
if bpy.context.scene.TLM_SceneProperties.tlm_utility_set == "Scene":
|
||||
for obj in bpy.context.scene.objects:
|
||||
if obj.type == "MESH":
|
||||
|
||||
for uvlayer in uv_layers:
|
||||
if uvlayer.name == "UVMap_Lightmap":
|
||||
uv_layers.remove(uvlayer)
|
||||
uv_layers = obj.data.uv_layers
|
||||
|
||||
if not obj.TLM_ObjectProperties.tlm_use_default_channel:
|
||||
uv_channel = obj.TLM_ObjectProperties.tlm_uv_channel
|
||||
else:
|
||||
uv_channel = "UVMap_Lightmap"
|
||||
|
||||
for uvlayer in uv_layers:
|
||||
if uvlayer.name == uv_channel:
|
||||
uv_layers.remove(uvlayer)
|
||||
|
||||
elif bpy.context.scene.TLM_SceneProperties.tlm_utility_set == "Selection":
|
||||
for obj in bpy.context.selected_objects:
|
||||
if obj.type == "MESH":
|
||||
|
||||
uv_layers = obj.data.uv_layers
|
||||
|
||||
if not obj.TLM_ObjectProperties.tlm_use_default_channel:
|
||||
uv_channel = obj.TLM_ObjectProperties.tlm_uv_channel
|
||||
else:
|
||||
uv_channel = "UVMap_Lightmap"
|
||||
|
||||
for uvlayer in uv_layers:
|
||||
if uvlayer.name == uv_channel:
|
||||
uv_layers.remove(uvlayer)
|
||||
|
||||
else: #Enabled
|
||||
for obj in bpy.context.scene.objects:
|
||||
if obj.type == "MESH":
|
||||
if obj.TLM_ObjectProperties.tlm_mesh_lightmap_use:
|
||||
|
||||
uv_layers = obj.data.uv_layers
|
||||
|
||||
if not obj.TLM_ObjectProperties.tlm_use_default_channel:
|
||||
uv_channel = obj.TLM_ObjectProperties.tlm_uv_channel
|
||||
else:
|
||||
uv_channel = "UVMap_Lightmap"
|
||||
|
||||
for uvlayer in uv_layers:
|
||||
if uvlayer.name == uv_channel:
|
||||
uv_layers.remove(uvlayer)
|
||||
|
||||
return{'FINISHED'}
|
||||
|
||||
|
@ -222,8 +476,8 @@ class TLM_SelectLightmapped(bpy.types.Operator):
|
|||
|
||||
def execute(self, context):
|
||||
|
||||
for obj in bpy.data.objects:
|
||||
if obj.type == "MESH":
|
||||
for obj in bpy.context.scene.objects:
|
||||
if obj.type == 'MESH' and obj.name in bpy.context.view_layer.objects:
|
||||
if obj.TLM_ObjectProperties.tlm_mesh_lightmap_use:
|
||||
|
||||
obj.select_set(True)
|
||||
|
@ -278,7 +532,7 @@ class TLM_AtlastListDeleteItem(bpy.types.Operator):
|
|||
list = scene.TLM_AtlasList
|
||||
index = scene.TLM_AtlasListItem
|
||||
|
||||
for obj in bpy.data.objects:
|
||||
for obj in bpy.context.scene.objects:
|
||||
|
||||
atlasName = scene.TLM_AtlasList[index].name
|
||||
|
||||
|
@ -310,7 +564,7 @@ class TLM_PostAtlastListDeleteItem(bpy.types.Operator):
|
|||
list = scene.TLM_PostAtlasList
|
||||
index = scene.TLM_PostAtlasListItem
|
||||
|
||||
for obj in bpy.data.objects:
|
||||
for obj in bpy.context.scene.objects:
|
||||
|
||||
atlasName = scene.TLM_PostAtlasList[index].name
|
||||
|
||||
|
@ -437,7 +691,7 @@ class TLM_BuildEnvironmentProbes(bpy.types.Operator):
|
|||
|
||||
def invoke(self, context, event):
|
||||
|
||||
for obj in bpy.data.objects:
|
||||
for obj in bpy.context.scene.objects:
|
||||
|
||||
if obj.type == "LIGHT_PROBE":
|
||||
if obj.data.type == "CUBEMAP":
|
||||
|
@ -500,7 +754,7 @@ class TLM_BuildEnvironmentProbes(bpy.types.Operator):
|
|||
cam.rotation_euler = positions[val]
|
||||
|
||||
filename = os.path.join(directory, val) + "_" + camobj_name + ".hdr"
|
||||
bpy.data.scenes['Scene'].render.filepath = filename
|
||||
bpy.context.scene.render.filepath = filename
|
||||
print("Writing out: " + val)
|
||||
bpy.ops.render.render(write_still=True)
|
||||
|
||||
|
@ -642,7 +896,7 @@ class TLM_BuildEnvironmentProbes(bpy.types.Operator):
|
|||
|
||||
subprocess.call([envpipe3], shell=True)
|
||||
|
||||
for obj in bpy.data.objects:
|
||||
for obj in bpy.context.scene.objects:
|
||||
obj.select_set(False)
|
||||
|
||||
cam_obj.select_set(True)
|
||||
|
@ -686,7 +940,92 @@ class TLM_MergeAdjacentActors(bpy.types.Operator):
|
|||
|
||||
scene = context.scene
|
||||
|
||||
|
||||
return {'FINISHED'}
|
||||
|
||||
class TLM_PrepareUVMaps(bpy.types.Operator):
|
||||
bl_idname = "tlm.prepare_uvmaps"
|
||||
bl_label = "Prepare UV maps"
|
||||
bl_description = "Prepare UV lightmaps for selected objects"
|
||||
bl_options = {'REGISTER', 'UNDO'}
|
||||
|
||||
def execute(self, context):
|
||||
|
||||
scene = context.scene
|
||||
|
||||
|
||||
|
||||
return {'FINISHED'}
|
||||
|
||||
class TLM_LoadLightmaps(bpy.types.Operator):
|
||||
bl_idname = "tlm.load_lightmaps"
|
||||
bl_label = "Load Lightmaps"
|
||||
bl_description = "Load lightmaps from selected folder"
|
||||
bl_options = {'REGISTER', 'UNDO'}
|
||||
|
||||
def execute(self, context):
|
||||
|
||||
scene = context.scene
|
||||
|
||||
utility.transfer_load()
|
||||
|
||||
build.finish_assemble()
|
||||
|
||||
return {'FINISHED'}
|
||||
|
||||
class TLM_ToggleTexelDensity(bpy.types.Operator):
|
||||
bl_idname = "tlm.toggle_texel_density"
|
||||
bl_label = "Toggle Texel Density"
|
||||
bl_description = "Toggle visualize lightmap texel density for selected objects"
|
||||
bl_options = {'REGISTER', 'UNDO'}
|
||||
|
||||
def execute(self, context):
|
||||
|
||||
scene = context.scene
|
||||
|
||||
for obj in bpy.context.selected_objects:
|
||||
if obj.type == "MESH":
|
||||
uv_layers = obj.data.uv_layers
|
||||
|
||||
#if the object has a td_vis in the uv maps, toggle off
|
||||
#else toggle on
|
||||
|
||||
if obj.TLM_ObjectProperties.tlm_use_default_channel:
|
||||
|
||||
for i in range(0, len(uv_layers)):
|
||||
if uv_layers[i].name == 'UVMap_Lightmap':
|
||||
uv_layers.active_index = i
|
||||
break
|
||||
else:
|
||||
|
||||
for i in range(0, len(uv_layers)):
|
||||
if uv_layers[i].name == obj.TLM_ObjectProperties.tlm_uv_channel:
|
||||
uv_layers.active_index = i
|
||||
break
|
||||
|
||||
#filepath = r"C:\path\to\image.png"
|
||||
|
||||
#img = bpy.data.images.load(filepath)
|
||||
|
||||
for area in bpy.context.screen.areas:
|
||||
if area.type == 'VIEW_3D':
|
||||
space_data = area.spaces.active
|
||||
bpy.ops.screen.area_dupli('INVOKE_DEFAULT')
|
||||
new_window = context.window_manager.windows[-1]
|
||||
|
||||
area = new_window.screen.areas[-1]
|
||||
area.type = 'VIEW_3D'
|
||||
#bg = space_data.background_images.new()
|
||||
print(bpy.context.object)
|
||||
bpy.ops.object.bake_td_uv_to_vc()
|
||||
|
||||
#bg.image = img
|
||||
break
|
||||
|
||||
|
||||
#set active uv_layer to
|
||||
|
||||
|
||||
print("TLM_Viz_Toggle")
|
||||
|
||||
return {'FINISHED'}
|
||||
|
||||
|
@ -698,7 +1037,4 @@ def TLM_HalfResolution():
|
|||
pass
|
||||
|
||||
def TLM_DivideLMGroups():
|
||||
pass
|
||||
|
||||
def TLM_LoadFromFolder():
|
||||
pass
|
0
blender/arm/lightmapper/panels/__init__.py
Normal file
0
blender/arm/lightmapper/panels/__init__.py
Normal file
66
blender/arm/lightmapper/panels/image.py
Normal file
66
blender/arm/lightmapper/panels/image.py
Normal file
|
@ -0,0 +1,66 @@
|
|||
import bpy, os, math, importlib
|
||||
|
||||
from bpy.types import Menu, Operator, Panel, UIList
|
||||
|
||||
from bpy.props import (
|
||||
StringProperty,
|
||||
BoolProperty,
|
||||
IntProperty,
|
||||
FloatProperty,
|
||||
FloatVectorProperty,
|
||||
EnumProperty,
|
||||
PointerProperty,
|
||||
)
|
||||
|
||||
class TLM_PT_Imagetools(bpy.types.Panel):
|
||||
bl_label = "TLM Imagetools"
|
||||
bl_space_type = "IMAGE_EDITOR"
|
||||
bl_region_type = 'UI'
|
||||
bl_category = "TLM Imagetools"
|
||||
|
||||
def draw_header(self, _):
|
||||
layout = self.layout
|
||||
row = layout.row(align=True)
|
||||
row.label(text ="Image Tools")
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
|
||||
activeImg = None
|
||||
|
||||
for area in bpy.context.screen.areas:
|
||||
if area.type == 'IMAGE_EDITOR':
|
||||
activeImg = area.spaces.active.image
|
||||
|
||||
if activeImg is not None and activeImg.name != "Render Result" and activeImg.name != "Viewer Node":
|
||||
|
||||
cv2 = importlib.util.find_spec("cv2")
|
||||
|
||||
if cv2 is None:
|
||||
row = layout.row(align=True)
|
||||
row.label(text ="OpenCV not installed.")
|
||||
else:
|
||||
|
||||
row = layout.row(align=True)
|
||||
row.label(text ="Method")
|
||||
row = layout.row(align=True)
|
||||
row.prop(activeImg.TLM_ImageProperties, "tlm_image_scale_engine")
|
||||
row = layout.row(align=True)
|
||||
row.prop(activeImg.TLM_ImageProperties, "tlm_image_cache_switch")
|
||||
row = layout.row(align=True)
|
||||
row.operator("tlm.image_upscale")
|
||||
if activeImg.TLM_ImageProperties.tlm_image_cache_switch:
|
||||
row = layout.row(align=True)
|
||||
row.label(text ="Switch up.")
|
||||
row = layout.row(align=True)
|
||||
row.operator("tlm.image_downscale")
|
||||
if activeImg.TLM_ImageProperties.tlm_image_cache_switch:
|
||||
row = layout.row(align=True)
|
||||
row.label(text ="Switch down.")
|
||||
if activeImg.TLM_ImageProperties.tlm_image_scale_engine == "OpenCV":
|
||||
row = layout.row(align=True)
|
||||
row.prop(activeImg.TLM_ImageProperties, "tlm_image_scale_method")
|
||||
|
||||
else:
|
||||
row = layout.row(align=True)
|
||||
row.label(text ="Select an image")
|
17
blender/arm/lightmapper/panels/light.py
Normal file
17
blender/arm/lightmapper/panels/light.py
Normal file
|
@ -0,0 +1,17 @@
|
|||
import bpy
|
||||
from bpy.props import *
|
||||
from bpy.types import Menu, Panel
|
||||
|
||||
class TLM_PT_LightMenu(bpy.types.Panel):
|
||||
bl_label = "The Lightmapper"
|
||||
bl_space_type = "PROPERTIES"
|
||||
bl_region_type = "WINDOW"
|
||||
bl_context = "light"
|
||||
bl_options = {'DEFAULT_CLOSED'}
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
scene = context.scene
|
||||
obj = bpy.context.object
|
||||
layout.use_property_split = True
|
||||
layout.use_property_decorate = False
|
118
blender/arm/lightmapper/panels/object.py
Normal file
118
blender/arm/lightmapper/panels/object.py
Normal file
|
@ -0,0 +1,118 @@
|
|||
import bpy
|
||||
from bpy.props import *
|
||||
from bpy.types import Menu, Panel
|
||||
|
||||
class TLM_PT_ObjectMenu(bpy.types.Panel):
|
||||
bl_label = "The Lightmapper"
|
||||
bl_space_type = "PROPERTIES"
|
||||
bl_region_type = "WINDOW"
|
||||
bl_context = "object"
|
||||
bl_options = {'DEFAULT_CLOSED'}
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
scene = context.scene
|
||||
obj = bpy.context.object
|
||||
layout.use_property_split = True
|
||||
layout.use_property_decorate = False
|
||||
|
||||
if obj.type == "MESH":
|
||||
row = layout.row(align=True)
|
||||
row.prop(obj.TLM_ObjectProperties, "tlm_mesh_lightmap_use")
|
||||
|
||||
if obj.TLM_ObjectProperties.tlm_mesh_lightmap_use:
|
||||
|
||||
row = layout.row()
|
||||
row.prop(obj.TLM_ObjectProperties, "tlm_use_default_channel")
|
||||
|
||||
if not obj.TLM_ObjectProperties.tlm_use_default_channel:
|
||||
|
||||
row = layout.row()
|
||||
row.prop_search(obj.TLM_ObjectProperties, "tlm_uv_channel", obj.data, "uv_layers", text='UV Channel')
|
||||
|
||||
row = layout.row()
|
||||
row.prop(obj.TLM_ObjectProperties, "tlm_mesh_lightmap_resolution")
|
||||
if obj.TLM_ObjectProperties.tlm_use_default_channel:
|
||||
row = layout.row()
|
||||
row.prop(obj.TLM_ObjectProperties, "tlm_mesh_lightmap_unwrap_mode")
|
||||
row = layout.row()
|
||||
if obj.TLM_ObjectProperties.tlm_mesh_lightmap_unwrap_mode == "AtlasGroupA":
|
||||
|
||||
if scene.TLM_AtlasListItem >= 0 and len(scene.TLM_AtlasList) > 0:
|
||||
row = layout.row()
|
||||
item = scene.TLM_AtlasList[scene.TLM_AtlasListItem]
|
||||
row.prop_search(obj.TLM_ObjectProperties, "tlm_atlas_pointer", scene, "TLM_AtlasList", text='Atlas Group')
|
||||
row = layout.row()
|
||||
else:
|
||||
row = layout.label(text="Add Atlas Groups from the scene lightmapping settings.")
|
||||
row = layout.row()
|
||||
|
||||
else:
|
||||
row = layout.row()
|
||||
row.prop(obj.TLM_ObjectProperties, "tlm_postpack_object")
|
||||
row = layout.row()
|
||||
|
||||
|
||||
if obj.TLM_ObjectProperties.tlm_postpack_object and obj.TLM_ObjectProperties.tlm_mesh_lightmap_unwrap_mode != "AtlasGroupA":
|
||||
if scene.TLM_PostAtlasListItem >= 0 and len(scene.TLM_PostAtlasList) > 0:
|
||||
row = layout.row()
|
||||
item = scene.TLM_PostAtlasList[scene.TLM_PostAtlasListItem]
|
||||
row.prop_search(obj.TLM_ObjectProperties, "tlm_postatlas_pointer", scene, "TLM_PostAtlasList", text='Atlas Group')
|
||||
row = layout.row()
|
||||
|
||||
else:
|
||||
row = layout.label(text="Add Atlas Groups from the scene lightmapping settings.")
|
||||
row = layout.row()
|
||||
|
||||
row.prop(obj.TLM_ObjectProperties, "tlm_mesh_unwrap_margin")
|
||||
row = layout.row()
|
||||
row.prop(obj.TLM_ObjectProperties, "tlm_mesh_filter_override")
|
||||
row = layout.row()
|
||||
if obj.TLM_ObjectProperties.tlm_mesh_filter_override:
|
||||
row = layout.row(align=True)
|
||||
row.prop(obj.TLM_ObjectProperties, "tlm_mesh_filtering_mode")
|
||||
row = layout.row(align=True)
|
||||
if obj.TLM_ObjectProperties.tlm_mesh_filtering_mode == "Gaussian":
|
||||
row.prop(obj.TLM_ObjectProperties, "tlm_mesh_filtering_gaussian_strength")
|
||||
row = layout.row(align=True)
|
||||
row.prop(obj.TLM_ObjectProperties, "tlm_mesh_filtering_iterations")
|
||||
elif obj.TLM_ObjectProperties.tlm_mesh_filtering_mode == "Box":
|
||||
row.prop(obj.TLM_ObjectProperties, "tlm_mesh_filtering_box_strength")
|
||||
row = layout.row(align=True)
|
||||
row.prop(obj.TLM_ObjectProperties, "tlm_mesh_filtering_iterations")
|
||||
elif obj.TLM_ObjectProperties.tlm_mesh_filtering_mode == "Bilateral":
|
||||
row.prop(obj.TLM_ObjectProperties, "tlm_mesh_filtering_bilateral_diameter")
|
||||
row = layout.row(align=True)
|
||||
row.prop(obj.TLM_ObjectProperties, "tlm_mesh_filtering_bilateral_color_deviation")
|
||||
row = layout.row(align=True)
|
||||
row.prop(obj.TLM_ObjectProperties, "tlm_mesh_filtering_bilateral_coordinate_deviation")
|
||||
row = layout.row(align=True)
|
||||
row.prop(obj.TLM_ObjectProperties, "tlm_mesh_filtering_iterations")
|
||||
else:
|
||||
row.prop(obj.TLM_ObjectProperties, "tlm_mesh_filtering_median_kernel", expand=True)
|
||||
row = layout.row(align=True)
|
||||
row.prop(obj.TLM_ObjectProperties, "tlm_mesh_filtering_iterations")
|
||||
|
||||
|
||||
class TLM_PT_MaterialMenu(bpy.types.Panel):
|
||||
bl_label = "The Lightmapper"
|
||||
bl_space_type = "PROPERTIES"
|
||||
bl_region_type = "WINDOW"
|
||||
bl_context = "material"
|
||||
bl_options = {'DEFAULT_CLOSED'}
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
scene = context.scene
|
||||
obj = bpy.context.object
|
||||
layout.use_property_split = True
|
||||
layout.use_property_decorate = False
|
||||
|
||||
mat = bpy.context.material
|
||||
if mat == None:
|
||||
return
|
||||
|
||||
if obj.type == "MESH":
|
||||
|
||||
row = layout.row()
|
||||
row.prop(mat, "TLM_ignore")
|
584
blender/arm/lightmapper/panels/scene.py
Normal file
584
blender/arm/lightmapper/panels/scene.py
Normal file
|
@ -0,0 +1,584 @@
|
|||
import bpy, importlib, math
|
||||
from bpy.props import *
|
||||
from bpy.types import Menu, Panel
|
||||
from .. utility import icon
|
||||
from .. properties.denoiser import oidn, optix
|
||||
|
||||
class TLM_PT_Settings(bpy.types.Panel):
|
||||
bl_label = "Settings"
|
||||
bl_space_type = "PROPERTIES"
|
||||
bl_region_type = "WINDOW"
|
||||
bl_context = "render"
|
||||
bl_options = {'DEFAULT_CLOSED'}
|
||||
bl_parent_id = "ARM_PT_BakePanel"
|
||||
|
||||
@classmethod
|
||||
def poll(self, context):
|
||||
scene = context.scene
|
||||
return scene.arm_bakemode == "Lightmap"
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
scene = context.scene
|
||||
layout.use_property_split = True
|
||||
layout.use_property_decorate = False
|
||||
sceneProperties = scene.TLM_SceneProperties
|
||||
|
||||
row = layout.row(align=True)
|
||||
|
||||
#We list LuxCoreRender as available, by default we assume Cycles exists
|
||||
row.prop(sceneProperties, "tlm_lightmap_engine")
|
||||
|
||||
if sceneProperties.tlm_lightmap_engine == "Cycles":
|
||||
|
||||
#CYCLES SETTINGS HERE
|
||||
engineProperties = scene.TLM_EngineProperties
|
||||
|
||||
row = layout.row(align=True)
|
||||
row.label(text="General Settings")
|
||||
row = layout.row(align=True)
|
||||
row.operator("tlm.build_lightmaps")
|
||||
row = layout.row(align=True)
|
||||
row.operator("tlm.clean_lightmaps")
|
||||
row = layout.row(align=True)
|
||||
row.operator("tlm.explore_lightmaps")
|
||||
row = layout.row(align=True)
|
||||
row.prop(sceneProperties, "tlm_apply_on_unwrap")
|
||||
row = layout.row(align=True)
|
||||
row.prop(sceneProperties, "tlm_headless")
|
||||
row = layout.row(align=True)
|
||||
row.prop(sceneProperties, "tlm_alert_on_finish")
|
||||
|
||||
if sceneProperties.tlm_alert_on_finish:
|
||||
row = layout.row(align=True)
|
||||
row.prop(sceneProperties, "tlm_alert_sound")
|
||||
|
||||
row = layout.row(align=True)
|
||||
row.prop(sceneProperties, "tlm_verbose")
|
||||
#row = layout.row(align=True)
|
||||
#row.prop(sceneProperties, "tlm_compile_statistics")
|
||||
row = layout.row(align=True)
|
||||
row.prop(sceneProperties, "tlm_override_bg_color")
|
||||
if sceneProperties.tlm_override_bg_color:
|
||||
row = layout.row(align=True)
|
||||
row.prop(sceneProperties, "tlm_override_color")
|
||||
row = layout.row(align=True)
|
||||
row.prop(sceneProperties, "tlm_reset_uv")
|
||||
|
||||
row = layout.row(align=True)
|
||||
try:
|
||||
if bpy.context.scene["TLM_Buildstat"] is not None:
|
||||
row.label(text="Last build completed in: " + str(bpy.context.scene["TLM_Buildstat"][0]))
|
||||
except:
|
||||
pass
|
||||
|
||||
row = layout.row(align=True)
|
||||
row.label(text="Cycles Settings")
|
||||
|
||||
row = layout.row(align=True)
|
||||
row.prop(engineProperties, "tlm_mode")
|
||||
row = layout.row(align=True)
|
||||
row.prop(engineProperties, "tlm_quality")
|
||||
row = layout.row(align=True)
|
||||
row.prop(engineProperties, "tlm_resolution_scale")
|
||||
row = layout.row(align=True)
|
||||
row.prop(engineProperties, "tlm_bake_mode")
|
||||
row = layout.row(align=True)
|
||||
row.prop(engineProperties, "tlm_target")
|
||||
row = layout.row(align=True)
|
||||
row.prop(engineProperties, "tlm_lighting_mode")
|
||||
# if scene.TLM_EngineProperties.tlm_lighting_mode == "combinedao" or scene.TLM_EngineProperties.tlm_lighting_mode == "indirectao":
|
||||
# row = layout.row(align=True)
|
||||
# row.prop(engineProperties, "tlm_premultiply_ao")
|
||||
if scene.TLM_EngineProperties.tlm_bake_mode == "Background":
|
||||
row = layout.row(align=True)
|
||||
row.label(text="Warning! Background mode is currently unstable", icon_value=2)
|
||||
row = layout.row(align=True)
|
||||
row.prop(sceneProperties, "tlm_network_render")
|
||||
if sceneProperties.tlm_network_render:
|
||||
row = layout.row(align=True)
|
||||
row.prop(sceneProperties, "tlm_network_paths")
|
||||
#row = layout.row(align=True)
|
||||
#row.prop(sceneProperties, "tlm_network_dir")
|
||||
row = layout.row(align=True)
|
||||
row.prop(engineProperties, "tlm_caching_mode")
|
||||
row = layout.row(align=True)
|
||||
row.prop(engineProperties, "tlm_directional_mode")
|
||||
row = layout.row(align=True)
|
||||
row.prop(engineProperties, "tlm_lightmap_savedir")
|
||||
row = layout.row(align=True)
|
||||
row.prop(engineProperties, "tlm_dilation_margin")
|
||||
row = layout.row(align=True)
|
||||
row.prop(engineProperties, "tlm_exposure_multiplier")
|
||||
row = layout.row(align=True)
|
||||
row.prop(engineProperties, "tlm_setting_supersample")
|
||||
row = layout.row(align=True)
|
||||
row.prop(sceneProperties, "tlm_metallic_clamp")
|
||||
row = layout.row(align=True)
|
||||
row.prop(sceneProperties, "tlm_texture_interpolation")
|
||||
row = layout.row(align=True)
|
||||
row.prop(sceneProperties, "tlm_texture_extrapolation")
|
||||
|
||||
|
||||
|
||||
# elif sceneProperties.tlm_lightmap_engine == "LuxCoreRender":
|
||||
|
||||
# engineProperties = scene.TLM_Engine2Properties
|
||||
# row = layout.row(align=True)
|
||||
# row.prop(engineProperties, "tlm_luxcore_dir")
|
||||
# row = layout.row(align=True)
|
||||
# row.operator("tlm.build_lightmaps")
|
||||
# #LUXCORE SETTINGS HERE
|
||||
# #luxcore_available = False
|
||||
|
||||
# #Look for Luxcorerender in the renderengine classes
|
||||
# # for engine in bpy.types.RenderEngine.__subclasses__():
|
||||
# # if engine.bl_idname == "LUXCORE":
|
||||
# # luxcore_available = True
|
||||
# # break
|
||||
|
||||
# # row = layout.row(align=True)
|
||||
# # if not luxcore_available:
|
||||
# # row.label(text="Please install BlendLuxCore.")
|
||||
# # else:
|
||||
# # row.label(text="LuxCoreRender not yet available.")
|
||||
|
||||
elif sceneProperties.tlm_lightmap_engine == "OctaneRender":
|
||||
|
||||
engineProperties = scene.TLM_Engine3Properties
|
||||
|
||||
#LUXCORE SETTINGS HERE
|
||||
octane_available = True
|
||||
|
||||
|
||||
|
||||
row = layout.row(align=True)
|
||||
row.operator("tlm.build_lightmaps")
|
||||
row = layout.row(align=True)
|
||||
row.operator("tlm.clean_lightmaps")
|
||||
row = layout.row(align=True)
|
||||
row.operator("tlm.explore_lightmaps")
|
||||
row = layout.row(align=True)
|
||||
row.prop(sceneProperties, "tlm_verbose")
|
||||
row = layout.row(align=True)
|
||||
row.prop(engineProperties, "tlm_lightmap_savedir")
|
||||
row = layout.row(align=True)
|
||||
|
||||
class TLM_PT_Denoise(bpy.types.Panel):
|
||||
bl_label = "Denoise"
|
||||
bl_space_type = "PROPERTIES"
|
||||
bl_region_type = "WINDOW"
|
||||
bl_context = "render"
|
||||
bl_options = {'DEFAULT_CLOSED'}
|
||||
bl_parent_id = "ARM_PT_BakePanel"
|
||||
|
||||
@classmethod
|
||||
def poll(self, context):
|
||||
scene = context.scene
|
||||
return scene.arm_bakemode == "Lightmap"
|
||||
|
||||
def draw_header(self, context):
|
||||
scene = context.scene
|
||||
sceneProperties = scene.TLM_SceneProperties
|
||||
self.layout.prop(sceneProperties, "tlm_denoise_use", text="")
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
scene = context.scene
|
||||
layout.use_property_split = True
|
||||
layout.use_property_decorate = False
|
||||
sceneProperties = scene.TLM_SceneProperties
|
||||
layout.active = sceneProperties.tlm_denoise_use
|
||||
|
||||
row = layout.row(align=True)
|
||||
|
||||
#row.prop(sceneProperties, "tlm_denoiser", expand=True)
|
||||
#row = layout.row(align=True)
|
||||
row.prop(sceneProperties, "tlm_denoise_engine", expand=True)
|
||||
row = layout.row(align=True)
|
||||
|
||||
if sceneProperties.tlm_denoise_engine == "Integrated":
|
||||
row.label(text="No options for Integrated.")
|
||||
elif sceneProperties.tlm_denoise_engine == "OIDN":
|
||||
denoiseProperties = scene.TLM_OIDNEngineProperties
|
||||
row.prop(denoiseProperties, "tlm_oidn_path")
|
||||
row = layout.row(align=True)
|
||||
row.prop(denoiseProperties, "tlm_oidn_verbose")
|
||||
row = layout.row(align=True)
|
||||
row.prop(denoiseProperties, "tlm_oidn_threads")
|
||||
row = layout.row(align=True)
|
||||
row.prop(denoiseProperties, "tlm_oidn_maxmem")
|
||||
row = layout.row(align=True)
|
||||
row.prop(denoiseProperties, "tlm_oidn_affinity")
|
||||
# row = layout.row(align=True)
|
||||
# row.prop(denoiseProperties, "tlm_denoise_ao")
|
||||
elif sceneProperties.tlm_denoise_engine == "Optix":
|
||||
denoiseProperties = scene.TLM_OptixEngineProperties
|
||||
row.prop(denoiseProperties, "tlm_optix_path")
|
||||
row = layout.row(align=True)
|
||||
row.prop(denoiseProperties, "tlm_optix_verbose")
|
||||
row = layout.row(align=True)
|
||||
row.prop(denoiseProperties, "tlm_optix_maxmem")
|
||||
#row = layout.row(align=True)
|
||||
#row.prop(denoiseProperties, "tlm_denoise_ao")
|
||||
|
||||
class TLM_PT_Filtering(bpy.types.Panel):
|
||||
bl_label = "Filtering"
|
||||
bl_space_type = "PROPERTIES"
|
||||
bl_region_type = "WINDOW"
|
||||
bl_context = "render"
|
||||
bl_options = {'DEFAULT_CLOSED'}
|
||||
bl_parent_id = "ARM_PT_BakePanel"
|
||||
|
||||
@classmethod
|
||||
def poll(self, context):
|
||||
scene = context.scene
|
||||
return scene.arm_bakemode == "Lightmap"
|
||||
|
||||
def draw_header(self, context):
|
||||
scene = context.scene
|
||||
sceneProperties = scene.TLM_SceneProperties
|
||||
self.layout.prop(sceneProperties, "tlm_filtering_use", text="")
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
scene = context.scene
|
||||
layout.use_property_split = True
|
||||
layout.use_property_decorate = False
|
||||
sceneProperties = scene.TLM_SceneProperties
|
||||
layout.active = sceneProperties.tlm_filtering_use
|
||||
#row = layout.row(align=True)
|
||||
#row.label(text="TODO MAKE CHECK")
|
||||
#row = layout.row(align=True)
|
||||
#row.prop(sceneProperties, "tlm_filtering_engine", expand=True)
|
||||
row = layout.row(align=True)
|
||||
|
||||
if sceneProperties.tlm_filtering_engine == "OpenCV":
|
||||
|
||||
cv2 = importlib.util.find_spec("cv2")
|
||||
|
||||
if cv2 is None:
|
||||
row = layout.row(align=True)
|
||||
row.label(text="OpenCV is not installed. Please install it as an administrator.")
|
||||
row = layout.row(align=True)
|
||||
row.operator("tlm.install_opencv_lightmaps")
|
||||
else:
|
||||
row = layout.row(align=True)
|
||||
row.prop(scene.TLM_SceneProperties, "tlm_filtering_mode")
|
||||
row = layout.row(align=True)
|
||||
if scene.TLM_SceneProperties.tlm_filtering_mode == "Gaussian":
|
||||
row.prop(scene.TLM_SceneProperties, "tlm_filtering_gaussian_strength")
|
||||
row = layout.row(align=True)
|
||||
row.prop(scene.TLM_SceneProperties, "tlm_filtering_iterations")
|
||||
elif scene.TLM_SceneProperties.tlm_filtering_mode == "Box":
|
||||
row.prop(scene.TLM_SceneProperties, "tlm_filtering_box_strength")
|
||||
row = layout.row(align=True)
|
||||
row.prop(scene.TLM_SceneProperties, "tlm_filtering_iterations")
|
||||
|
||||
elif scene.TLM_SceneProperties.tlm_filtering_mode == "Bilateral":
|
||||
row.prop(scene.TLM_SceneProperties, "tlm_filtering_bilateral_diameter")
|
||||
row = layout.row(align=True)
|
||||
row.prop(scene.TLM_SceneProperties, "tlm_filtering_bilateral_color_deviation")
|
||||
row = layout.row(align=True)
|
||||
row.prop(scene.TLM_SceneProperties, "tlm_filtering_bilateral_coordinate_deviation")
|
||||
row = layout.row(align=True)
|
||||
row.prop(scene.TLM_SceneProperties, "tlm_filtering_iterations")
|
||||
else:
|
||||
row.prop(scene.TLM_SceneProperties, "tlm_filtering_median_kernel", expand=True)
|
||||
row = layout.row(align=True)
|
||||
row.prop(scene.TLM_SceneProperties, "tlm_filtering_iterations")
|
||||
else:
|
||||
row = layout.row(align=True)
|
||||
row.prop(scene.TLM_SceneProperties, "tlm_numpy_filtering_mode")
|
||||
|
||||
|
||||
class TLM_PT_Encoding(bpy.types.Panel):
|
||||
bl_label = "Encoding"
|
||||
bl_space_type = "PROPERTIES"
|
||||
bl_region_type = "WINDOW"
|
||||
bl_context = "render"
|
||||
bl_options = {'DEFAULT_CLOSED'}
|
||||
bl_parent_id = "ARM_PT_BakePanel"
|
||||
|
||||
@classmethod
|
||||
def poll(self, context):
|
||||
scene = context.scene
|
||||
return scene.arm_bakemode == "Lightmap"
|
||||
|
||||
def draw_header(self, context):
|
||||
scene = context.scene
|
||||
sceneProperties = scene.TLM_SceneProperties
|
||||
self.layout.prop(sceneProperties, "tlm_encoding_use", text="")
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
scene = context.scene
|
||||
layout.use_property_split = True
|
||||
layout.use_property_decorate = False
|
||||
sceneProperties = scene.TLM_SceneProperties
|
||||
layout.active = sceneProperties.tlm_encoding_use
|
||||
|
||||
sceneProperties = scene.TLM_SceneProperties
|
||||
row = layout.row(align=True)
|
||||
|
||||
if scene.TLM_EngineProperties.tlm_bake_mode == "Background":
|
||||
row.label(text="Encoding options disabled in background mode")
|
||||
row = layout.row(align=True)
|
||||
|
||||
else:
|
||||
|
||||
row.prop(sceneProperties, "tlm_encoding_device", expand=True)
|
||||
row = layout.row(align=True)
|
||||
|
||||
if sceneProperties.tlm_encoding_device == "CPU":
|
||||
row.prop(sceneProperties, "tlm_encoding_mode_a", expand=True)
|
||||
else:
|
||||
row.prop(sceneProperties, "tlm_encoding_mode_b", expand=True)
|
||||
|
||||
if sceneProperties.tlm_encoding_device == "CPU":
|
||||
if sceneProperties.tlm_encoding_mode_a == "RGBM":
|
||||
row = layout.row(align=True)
|
||||
row.prop(sceneProperties, "tlm_encoding_range")
|
||||
row = layout.row(align=True)
|
||||
row.prop(sceneProperties, "tlm_decoder_setup")
|
||||
if sceneProperties.tlm_encoding_mode_a == "RGBD":
|
||||
pass
|
||||
if sceneProperties.tlm_encoding_mode_a == "HDR":
|
||||
row = layout.row(align=True)
|
||||
row.prop(sceneProperties, "tlm_format")
|
||||
else:
|
||||
|
||||
if sceneProperties.tlm_encoding_mode_b == "RGBM":
|
||||
row = layout.row(align=True)
|
||||
row.prop(sceneProperties, "tlm_encoding_range")
|
||||
row = layout.row(align=True)
|
||||
row.prop(sceneProperties, "tlm_decoder_setup")
|
||||
|
||||
if sceneProperties.tlm_encoding_mode_b == "LogLuv" and sceneProperties.tlm_encoding_device == "GPU":
|
||||
row = layout.row(align=True)
|
||||
row.prop(sceneProperties, "tlm_decoder_setup")
|
||||
if sceneProperties.tlm_encoding_mode_b == "HDR":
|
||||
row = layout.row(align=True)
|
||||
row.prop(sceneProperties, "tlm_format")
|
||||
|
||||
class TLM_PT_Utility(bpy.types.Panel):
|
||||
bl_label = "Utilities"
|
||||
bl_space_type = "PROPERTIES"
|
||||
bl_region_type = "WINDOW"
|
||||
bl_context = "render"
|
||||
bl_options = {'DEFAULT_CLOSED'}
|
||||
bl_parent_id = "ARM_PT_BakePanel"
|
||||
|
||||
@classmethod
|
||||
def poll(self, context):
|
||||
scene = context.scene
|
||||
return scene.arm_bakemode == "Lightmap"
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
scene = context.scene
|
||||
layout.use_property_split = True
|
||||
layout.use_property_decorate = False
|
||||
sceneProperties = scene.TLM_SceneProperties
|
||||
|
||||
row = layout.row(align=True)
|
||||
row.label(text="Enable Lightmaps for set")
|
||||
row = layout.row(align=True)
|
||||
row.operator("tlm.enable_set")
|
||||
row = layout.row(align=True)
|
||||
row.prop(sceneProperties, "tlm_utility_set")
|
||||
row = layout.row(align=True)
|
||||
#row.label(text="ABCD")
|
||||
row.prop(sceneProperties, "tlm_mesh_lightmap_unwrap_mode")
|
||||
|
||||
if sceneProperties.tlm_mesh_lightmap_unwrap_mode == "AtlasGroupA":
|
||||
|
||||
if scene.TLM_AtlasListItem >= 0 and len(scene.TLM_AtlasList) > 0:
|
||||
row = layout.row()
|
||||
item = scene.TLM_AtlasList[scene.TLM_AtlasListItem]
|
||||
row.prop_search(sceneProperties, "tlm_atlas_pointer", scene, "TLM_AtlasList", text='Atlas Group')
|
||||
else:
|
||||
row = layout.label(text="Add Atlas Groups from the scene lightmapping settings.")
|
||||
|
||||
else:
|
||||
|
||||
row = layout.row()
|
||||
row.prop(sceneProperties, "tlm_postpack_object")
|
||||
row = layout.row()
|
||||
|
||||
if sceneProperties.tlm_postpack_object and sceneProperties.tlm_mesh_lightmap_unwrap_mode != "AtlasGroupA":
|
||||
|
||||
if scene.TLM_PostAtlasListItem >= 0 and len(scene.TLM_PostAtlasList) > 0:
|
||||
row = layout.row()
|
||||
item = scene.TLM_PostAtlasList[scene.TLM_PostAtlasListItem]
|
||||
row.prop_search(sceneProperties, "tlm_postatlas_pointer", scene, "TLM_PostAtlasList", text='Atlas Group')
|
||||
row = layout.row()
|
||||
|
||||
else:
|
||||
row = layout.label(text="Add Atlas Groups from the scene lightmapping settings.")
|
||||
row = layout.row()
|
||||
|
||||
row.prop(sceneProperties, "tlm_mesh_unwrap_margin")
|
||||
row = layout.row()
|
||||
row.prop(sceneProperties, "tlm_resolution_weight")
|
||||
|
||||
if sceneProperties.tlm_resolution_weight == "Single":
|
||||
row = layout.row()
|
||||
row.prop(sceneProperties, "tlm_mesh_lightmap_resolution")
|
||||
else:
|
||||
row = layout.row()
|
||||
row.prop(sceneProperties, "tlm_resolution_min")
|
||||
row = layout.row()
|
||||
row.prop(sceneProperties, "tlm_resolution_max")
|
||||
|
||||
row = layout.row()
|
||||
row.operator("tlm.disable_selection")
|
||||
row = layout.row(align=True)
|
||||
row.operator("tlm.select_lightmapped_objects")
|
||||
row = layout.row(align=True)
|
||||
row.operator("tlm.remove_uv_selection")
|
||||
row = layout.row(align=True)
|
||||
|
||||
|
||||
row.label(text="Environment Probes")
|
||||
row = layout.row()
|
||||
row.operator("tlm.build_environmentprobe")
|
||||
row = layout.row()
|
||||
row.operator("tlm.clean_environmentprobe")
|
||||
row = layout.row()
|
||||
row.prop(sceneProperties, "tlm_environment_probe_engine")
|
||||
row = layout.row()
|
||||
row.prop(sceneProperties, "tlm_cmft_path")
|
||||
row = layout.row()
|
||||
row.prop(sceneProperties, "tlm_environment_probe_resolution")
|
||||
row = layout.row()
|
||||
row.prop(sceneProperties, "tlm_create_spherical")
|
||||
|
||||
if sceneProperties.tlm_create_spherical:
|
||||
|
||||
row = layout.row()
|
||||
row.prop(sceneProperties, "tlm_invert_direction")
|
||||
row = layout.row()
|
||||
row.prop(sceneProperties, "tlm_write_sh")
|
||||
row = layout.row()
|
||||
row.prop(sceneProperties, "tlm_write_radiance")
|
||||
|
||||
row = layout.row(align=True)
|
||||
row.label(text="Load lightmaps")
|
||||
row = layout.row()
|
||||
row.prop(sceneProperties, "tlm_load_folder")
|
||||
row = layout.row()
|
||||
row.operator("tlm.load_lightmaps")
|
||||
|
||||
class TLM_PT_Additional(bpy.types.Panel):
|
||||
bl_label = "Additional"
|
||||
bl_space_type = "PROPERTIES"
|
||||
bl_region_type = "WINDOW"
|
||||
bl_context = "render"
|
||||
bl_options = {'DEFAULT_CLOSED'}
|
||||
bl_parent_id = "ARM_PT_BakePanel"
|
||||
|
||||
@classmethod
|
||||
def poll(self, context):
|
||||
scene = context.scene
|
||||
return scene.arm_bakemode == "Lightmap"
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
scene = context.scene
|
||||
sceneProperties = scene.TLM_SceneProperties
|
||||
atlasListItem = scene.TLM_AtlasListItem
|
||||
atlasList = scene.TLM_AtlasList
|
||||
postatlasListItem = scene.TLM_PostAtlasListItem
|
||||
postatlasList = scene.TLM_PostAtlasList
|
||||
|
||||
layout.label(text="Network Rendering")
|
||||
row = layout.row()
|
||||
row.operator("tlm.start_server")
|
||||
layout.label(text="Atlas Groups")
|
||||
row = layout.row()
|
||||
row.prop(sceneProperties, "tlm_atlas_mode", expand=True)
|
||||
|
||||
if sceneProperties.tlm_atlas_mode == "Prepack":
|
||||
|
||||
rows = 2
|
||||
if len(atlasList) > 1:
|
||||
rows = 4
|
||||
row = layout.row()
|
||||
row.template_list("TLM_UL_AtlasList", "Atlas List", scene, "TLM_AtlasList", scene, "TLM_AtlasListItem", rows=rows)
|
||||
col = row.column(align=True)
|
||||
col.operator("tlm_atlaslist.new_item", icon='ADD', text="")
|
||||
col.operator("tlm_atlaslist.delete_item", icon='REMOVE', text="")
|
||||
|
||||
if atlasListItem >= 0 and len(atlasList) > 0:
|
||||
item = atlasList[atlasListItem]
|
||||
layout.prop(item, "tlm_atlas_lightmap_unwrap_mode")
|
||||
layout.prop(item, "tlm_atlas_lightmap_resolution")
|
||||
layout.prop(item, "tlm_atlas_unwrap_margin")
|
||||
|
||||
amount = 0
|
||||
|
||||
for obj in bpy.context.scene.objects:
|
||||
if obj.TLM_ObjectProperties.tlm_mesh_lightmap_use:
|
||||
if obj.TLM_ObjectProperties.tlm_mesh_lightmap_unwrap_mode == "AtlasGroupA":
|
||||
if obj.TLM_ObjectProperties.tlm_atlas_pointer == item.name:
|
||||
amount = amount + 1
|
||||
|
||||
layout.label(text="Objects: " + str(amount))
|
||||
|
||||
else:
|
||||
|
||||
layout.label(text="Postpacking is unstable.")
|
||||
|
||||
cv2 = importlib.util.find_spec("cv2")
|
||||
|
||||
if cv2 is None:
|
||||
|
||||
row = layout.row(align=True)
|
||||
row.label(text="OpenCV is not installed. Install it through preferences.")
|
||||
|
||||
else:
|
||||
|
||||
rows = 2
|
||||
if len(atlasList) > 1:
|
||||
rows = 4
|
||||
row = layout.row()
|
||||
row.template_list("TLM_UL_PostAtlasList", "PostList", scene, "TLM_PostAtlasList", scene, "TLM_PostAtlasListItem", rows=rows)
|
||||
col = row.column(align=True)
|
||||
col.operator("tlm_postatlaslist.new_item", icon='ADD', text="")
|
||||
col.operator("tlm_postatlaslist.delete_item", icon='REMOVE', text="")
|
||||
|
||||
if postatlasListItem >= 0 and len(postatlasList) > 0:
|
||||
item = postatlasList[postatlasListItem]
|
||||
layout.prop(item, "tlm_atlas_lightmap_resolution")
|
||||
|
||||
#Below list object counter
|
||||
amount = 0
|
||||
utilized = 0
|
||||
atlasUsedArea = 0
|
||||
atlasSize = item.tlm_atlas_lightmap_resolution
|
||||
|
||||
for obj in bpy.context.scene.objects:
|
||||
if obj.TLM_ObjectProperties.tlm_mesh_lightmap_use:
|
||||
if obj.TLM_ObjectProperties.tlm_postpack_object:
|
||||
if obj.TLM_ObjectProperties.tlm_postatlas_pointer == item.name:
|
||||
amount = amount + 1
|
||||
|
||||
atlasUsedArea += int(obj.TLM_ObjectProperties.tlm_mesh_lightmap_resolution) ** 2
|
||||
|
||||
row = layout.row()
|
||||
row.prop(item, "tlm_atlas_repack_on_cleanup")
|
||||
|
||||
#TODO SET A CHECK FOR THIS! ADD A CV2 CHECK TO UTILITY!
|
||||
cv2 = True
|
||||
|
||||
if cv2:
|
||||
row = layout.row()
|
||||
row.prop(item, "tlm_atlas_dilation")
|
||||
layout.label(text="Objects: " + str(amount))
|
||||
|
||||
utilized = atlasUsedArea / (int(atlasSize) ** 2)
|
||||
layout.label(text="Utilized: " + str(utilized * 100) + "%")
|
||||
|
||||
if (utilized * 100) > 100:
|
||||
layout.label(text="Warning! Overflow not yet supported")
|
17
blender/arm/lightmapper/panels/world.py
Normal file
17
blender/arm/lightmapper/panels/world.py
Normal file
|
@ -0,0 +1,17 @@
|
|||
import bpy
|
||||
from bpy.props import *
|
||||
from bpy.types import Menu, Panel
|
||||
|
||||
class TLM_PT_WorldMenu(bpy.types.Panel):
|
||||
bl_label = "The Lightmapper"
|
||||
bl_space_type = "PROPERTIES"
|
||||
bl_region_type = "WINDOW"
|
||||
bl_context = "world"
|
||||
bl_options = {'DEFAULT_CLOSED'}
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
scene = context.scene
|
||||
obj = bpy.context.object
|
||||
layout.use_property_split = True
|
||||
layout.use_property_decorate = False
|
16
blender/arm/lightmapper/preferences/__init__.py
Normal file
16
blender/arm/lightmapper/preferences/__init__.py
Normal file
|
@ -0,0 +1,16 @@
|
|||
import bpy
|
||||
from bpy.utils import register_class, unregister_class
|
||||
from . import addon_preferences
|
||||
#from . import build, clean, explore, encode, installopencv
|
||||
|
||||
classes = [
|
||||
addon_preferences.TLM_AddonPreferences
|
||||
]
|
||||
|
||||
def register():
|
||||
for cls in classes:
|
||||
register_class(cls)
|
||||
|
||||
def unregister():
|
||||
for cls in classes:
|
||||
unregister_class(cls)
|
75
blender/arm/lightmapper/preferences/addon_preferences.py
Normal file
75
blender/arm/lightmapper/preferences/addon_preferences.py
Normal file
|
@ -0,0 +1,75 @@
|
|||
import bpy, platform
|
||||
from os.path import basename, dirname
|
||||
from bpy.types import AddonPreferences
|
||||
from .. operators import installopencv
|
||||
import importlib
|
||||
|
||||
class TLM_AddonPreferences(AddonPreferences):
|
||||
|
||||
bl_idname = "thelightmapper"
|
||||
|
||||
def draw(self, context):
|
||||
|
||||
layout = self.layout
|
||||
|
||||
box = layout.box()
|
||||
row = box.row()
|
||||
row.label(text="OpenCV")
|
||||
|
||||
cv2 = importlib.util.find_spec("cv2")
|
||||
|
||||
if cv2 is not None:
|
||||
row.label(text="OpenCV installed")
|
||||
else:
|
||||
if platform.system() == "Windows":
|
||||
row.label(text="OpenCV not found - Install as administrator!", icon_value=2)
|
||||
else:
|
||||
row.label(text="OpenCV not found - Click to install!", icon_value=2)
|
||||
row = box.row()
|
||||
row.operator("tlm.install_opencv_lightmaps", icon="PREFERENCES")
|
||||
|
||||
box = layout.box()
|
||||
row = box.row()
|
||||
row.label(text="Blender Xatlas")
|
||||
if "blender_xatlas" in bpy.context.preferences.addons.keys():
|
||||
row.label(text="Blender Xatlas installed and available")
|
||||
else:
|
||||
row.label(text="Blender Xatlas not installed", icon_value=2)
|
||||
row = box.row()
|
||||
row.label(text="Github: https://github.com/mattedicksoncom/blender-xatlas")
|
||||
|
||||
box = layout.box()
|
||||
row = box.row()
|
||||
row.label(text="RizomUV Bridge")
|
||||
row.label(text="Coming soon")
|
||||
|
||||
box = layout.box()
|
||||
row = box.row()
|
||||
row.label(text="UVPackmaster")
|
||||
row.label(text="Coming soon")
|
||||
|
||||
texel_density_addon = False
|
||||
for addon in bpy.context.preferences.addons.keys():
|
||||
if addon.startswith("Texel_Density"):
|
||||
texel_density_addon = True
|
||||
|
||||
box = layout.box()
|
||||
row = box.row()
|
||||
row.label(text="Texel Density Checker")
|
||||
if texel_density_addon:
|
||||
row.label(text="Texel Density Checker installed and available")
|
||||
else:
|
||||
row.label(text="Texel Density Checker", icon_value=2)
|
||||
row.label(text="Coming soon")
|
||||
row = box.row()
|
||||
row.label(text="Github: https://github.com/mrven/Blender-Texel-Density-Checker")
|
||||
|
||||
box = layout.box()
|
||||
row = box.row()
|
||||
row.label(text="LuxCoreRender")
|
||||
row.label(text="Coming soon")
|
||||
|
||||
box = layout.box()
|
||||
row = box.row()
|
||||
row.label(text="OctaneRender")
|
||||
row.label(text="Coming soon")
|
|
@ -1,7 +1,7 @@
|
|||
import bpy
|
||||
from bpy.utils import register_class, unregister_class
|
||||
from . import scene, object, atlas
|
||||
from . renderer import cycles, luxcorerender
|
||||
from . import scene, object, atlas, image
|
||||
from . renderer import cycles, luxcorerender, octanerender
|
||||
from . denoiser import oidn, optix
|
||||
|
||||
classes = [
|
||||
|
@ -9,12 +9,14 @@ classes = [
|
|||
object.TLM_ObjectProperties,
|
||||
cycles.TLM_CyclesSceneProperties,
|
||||
luxcorerender.TLM_LuxCoreSceneProperties,
|
||||
octanerender.TLM_OctanerenderSceneProperties,
|
||||
oidn.TLM_OIDNEngineProperties,
|
||||
optix.TLM_OptixEngineProperties,
|
||||
atlas.TLM_AtlasListItem,
|
||||
atlas.TLM_UL_AtlasList,
|
||||
atlas.TLM_PostAtlasListItem,
|
||||
atlas.TLM_UL_PostAtlasList
|
||||
atlas.TLM_UL_PostAtlasList,
|
||||
image.TLM_ImageProperties
|
||||
]
|
||||
|
||||
def register():
|
||||
|
@ -25,12 +27,14 @@ def register():
|
|||
bpy.types.Object.TLM_ObjectProperties = bpy.props.PointerProperty(type=object.TLM_ObjectProperties)
|
||||
bpy.types.Scene.TLM_EngineProperties = bpy.props.PointerProperty(type=cycles.TLM_CyclesSceneProperties)
|
||||
bpy.types.Scene.TLM_Engine2Properties = bpy.props.PointerProperty(type=luxcorerender.TLM_LuxCoreSceneProperties)
|
||||
bpy.types.Scene.TLM_Engine3Properties = bpy.props.PointerProperty(type=octanerender.TLM_OctanerenderSceneProperties)
|
||||
bpy.types.Scene.TLM_OIDNEngineProperties = bpy.props.PointerProperty(type=oidn.TLM_OIDNEngineProperties)
|
||||
bpy.types.Scene.TLM_OptixEngineProperties = bpy.props.PointerProperty(type=optix.TLM_OptixEngineProperties)
|
||||
bpy.types.Scene.TLM_AtlasListItem = bpy.props.IntProperty(name="Index for my_list", default=0)
|
||||
bpy.types.Scene.TLM_AtlasList = bpy.props.CollectionProperty(type=atlas.TLM_AtlasListItem)
|
||||
bpy.types.Scene.TLM_PostAtlasListItem = bpy.props.IntProperty(name="Index for my_list", default=0)
|
||||
bpy.types.Scene.TLM_PostAtlasList = bpy.props.CollectionProperty(type=atlas.TLM_PostAtlasListItem)
|
||||
bpy.types.Image.TLM_ImageProperties = bpy.props.PointerProperty(type=image.TLM_ImageProperties)
|
||||
|
||||
bpy.types.Material.TLM_ignore = bpy.props.BoolProperty(name="Skip material", description="Ignore material for lightmapped object", default=False)
|
||||
|
||||
|
@ -42,9 +46,11 @@ def unregister():
|
|||
del bpy.types.Object.TLM_ObjectProperties
|
||||
del bpy.types.Scene.TLM_EngineProperties
|
||||
del bpy.types.Scene.TLM_Engine2Properties
|
||||
del bpy.types.Scene.TLM_Engine3Properties
|
||||
del bpy.types.Scene.TLM_OIDNEngineProperties
|
||||
del bpy.types.Scene.TLM_OptixEngineProperties
|
||||
del bpy.types.Scene.TLM_AtlasListItem
|
||||
del bpy.types.Scene.TLM_AtlasList
|
||||
del bpy.types.Scene.TLM_PostAtlasListItem
|
||||
del bpy.types.Scene.TLM_PostAtlasList
|
||||
del bpy.types.Scene.TLM_PostAtlasList
|
||||
del bpy.types.Image.TLM_ImageProperties
|
|
@ -34,12 +34,16 @@ class TLM_PostAtlasListItem(bpy.types.PropertyGroup):
|
|||
max=1.0,
|
||||
subtype='FACTOR')
|
||||
|
||||
tlm_atlas_lightmap_unwrap_mode : EnumProperty(
|
||||
items = [('Lightmap', 'Lightmap', 'TODO'),
|
||||
('SmartProject', 'Smart Project', 'TODO'),
|
||||
('Xatlas', 'Xatlas', 'TODO')],
|
||||
unwrap_modes = [('Lightmap', 'Lightmap', 'Use Blender Lightmap Pack algorithm'),
|
||||
('SmartProject', 'Smart Project', 'Use Blender Smart Project algorithm')]
|
||||
|
||||
if "blender_xatlas" in bpy.context.preferences.addons.keys():
|
||||
unwrap_modes.append(('Xatlas', 'Xatlas', 'Use Xatlas addon packing algorithm'))
|
||||
|
||||
tlm_postatlas_lightmap_unwrap_mode : EnumProperty(
|
||||
items = unwrap_modes,
|
||||
name = "Unwrap Mode",
|
||||
description="TODO",
|
||||
description="Atlas unwrapping method",
|
||||
default='SmartProject')
|
||||
|
||||
class TLM_UL_PostAtlasList(bpy.types.UIList):
|
||||
|
@ -51,7 +55,7 @@ class TLM_UL_PostAtlasList(bpy.types.UIList):
|
|||
#In list object counter
|
||||
amount = 0
|
||||
|
||||
for obj in bpy.data.objects:
|
||||
for obj in bpy.context.scene.objects:
|
||||
if obj.TLM_ObjectProperties.tlm_mesh_lightmap_use:
|
||||
if obj.TLM_ObjectProperties.tlm_postpack_object:
|
||||
if obj.TLM_ObjectProperties.tlm_postatlas_pointer == item.name:
|
||||
|
@ -69,9 +73,6 @@ class TLM_UL_PostAtlasList(bpy.types.UIList):
|
|||
layout.alignment = 'CENTER'
|
||||
layout.label(text="", icon = custom_icon)
|
||||
|
||||
|
||||
|
||||
|
||||
class TLM_AtlasListItem(bpy.types.PropertyGroup):
|
||||
obj: PointerProperty(type=bpy.types.Object, description="The object to bake")
|
||||
tlm_atlas_lightmap_resolution : EnumProperty(
|
||||
|
@ -95,12 +96,17 @@ class TLM_AtlasListItem(bpy.types.PropertyGroup):
|
|||
max=1.0,
|
||||
subtype='FACTOR')
|
||||
|
||||
unwrap_modes = [('Lightmap', 'Lightmap', 'Use Blender Lightmap Pack algorithm'),
|
||||
('SmartProject', 'Smart Project', 'Use Blender Smart Project algorithm'),
|
||||
('Copy', 'Copy existing', 'Use the existing UV channel')]
|
||||
|
||||
if "blender_xatlas" in bpy.context.preferences.addons.keys():
|
||||
unwrap_modes.append(('Xatlas', 'Xatlas', 'Use Xatlas addon packing algorithm'))
|
||||
|
||||
tlm_atlas_lightmap_unwrap_mode : EnumProperty(
|
||||
items = [('Lightmap', 'Lightmap', 'TODO'),
|
||||
('SmartProject', 'Smart Project', 'TODO'),
|
||||
('Xatlas', 'Xatlas', 'TODO')],
|
||||
items = unwrap_modes,
|
||||
name = "Unwrap Mode",
|
||||
description="TODO",
|
||||
description="Atlas unwrapping method",
|
||||
default='SmartProject')
|
||||
|
||||
class TLM_UL_AtlasList(bpy.types.UIList):
|
||||
|
@ -111,7 +117,7 @@ class TLM_UL_AtlasList(bpy.types.UIList):
|
|||
|
||||
amount = 0
|
||||
|
||||
for obj in bpy.data.objects:
|
||||
for obj in bpy.context.scene.objects:
|
||||
if obj.TLM_ObjectProperties.tlm_mesh_lightmap_use:
|
||||
if obj.TLM_ObjectProperties.tlm_mesh_lightmap_unwrap_mode == "AtlasGroupA":
|
||||
if obj.TLM_ObjectProperties.tlm_atlas_pointer == item.name:
|
||||
|
|
|
@ -1,10 +1,26 @@
|
|||
import bpy
|
||||
from bpy.props import *
|
||||
|
||||
class TLM_ObjectProperties(bpy.types.PropertyGroup):
|
||||
tlm_image_scale_method : EnumProperty(
|
||||
items = [('Native', 'Native', 'TODO'),
|
||||
('OpenCV', 'OpenCV', 'TODO')],
|
||||
class TLM_ImageProperties(bpy.types.PropertyGroup):
|
||||
tlm_image_scale_engine : EnumProperty(
|
||||
items = [('OpenCV', 'OpenCV', 'TODO')],
|
||||
name = "Scaling engine",
|
||||
description="TODO",
|
||||
default='Native')
|
||||
default='OpenCV')
|
||||
|
||||
#('Native', 'Native', 'TODO'),
|
||||
|
||||
tlm_image_scale_method : EnumProperty(
|
||||
items = [('Nearest', 'Nearest', 'TODO'),
|
||||
('Area', 'Area', 'TODO'),
|
||||
('Linear', 'Linear', 'TODO'),
|
||||
('Cubic', 'Cubic', 'TODO'),
|
||||
('Lanczos', 'Lanczos', 'TODO')],
|
||||
name = "Scaling method",
|
||||
description="TODO",
|
||||
default='Lanczos')
|
||||
|
||||
tlm_image_cache_switch : BoolProperty(
|
||||
name="Cache for quickswitch",
|
||||
description="Caches scaled images for quick switching",
|
||||
default=True)
|
|
@ -7,7 +7,7 @@ class TLM_ObjectProperties(bpy.types.PropertyGroup):
|
|||
|
||||
tlm_atlas_pointer : StringProperty(
|
||||
name = "Atlas Group",
|
||||
description = "Atlas Lightmap Group",
|
||||
description = "",
|
||||
default = "")
|
||||
|
||||
tlm_postatlas_pointer : StringProperty(
|
||||
|
@ -51,8 +51,7 @@ class TLM_ObjectProperties(bpy.types.PropertyGroup):
|
|||
|
||||
unwrap_modes = [('Lightmap', 'Lightmap', 'TODO'),
|
||||
('SmartProject', 'Smart Project', 'TODO'),
|
||||
('CopyExisting', 'Copy Existing', 'TODO'),
|
||||
('AtlasGroupA', 'Atlas Group (Prepack)', 'TODO')]
|
||||
('AtlasGroupA', 'Atlas Group (Prepack)', 'Attaches the object to a prepack Atlas group. Will overwrite UV map on build.')]
|
||||
|
||||
tlm_postpack_object : BoolProperty( #CHECK INSTEAD OF ATLASGROUPB
|
||||
name="Postpack object",
|
||||
|
@ -145,4 +144,14 @@ class TLM_ObjectProperties(bpy.types.PropertyGroup):
|
|||
name="Median kernel",
|
||||
default=3,
|
||||
min=1,
|
||||
max=5)
|
||||
max=5)
|
||||
|
||||
tlm_use_default_channel : BoolProperty(
|
||||
name="Use default UV channel",
|
||||
description="Will either use or create the default UV Channel 'UVMap_Lightmap' upon build.",
|
||||
default=True)
|
||||
|
||||
tlm_uv_channel : StringProperty(
|
||||
name = "UV Channel",
|
||||
description = "Use any custom UV Channel for the lightmap",
|
||||
default = "UVMap")
|
|
@ -21,6 +21,16 @@ class TLM_CyclesSceneProperties(bpy.types.PropertyGroup):
|
|||
description="Select baking quality",
|
||||
default="0")
|
||||
|
||||
targets = [('texture', 'Image texture', 'Build to image texture')]
|
||||
if (2, 92, 0) >= bpy.app.version:
|
||||
targets.append(('vertex', 'Vertex colors', 'Build to vertex colors'))
|
||||
|
||||
tlm_target : EnumProperty(
|
||||
items = targets,
|
||||
name = "Build Target",
|
||||
description="Select target to build to",
|
||||
default="texture")
|
||||
|
||||
tlm_resolution_scale : EnumProperty(
|
||||
items = [('1', '1/1', '1'),
|
||||
('2', '1/2', '2'),
|
||||
|
@ -45,10 +55,12 @@ class TLM_CyclesSceneProperties(bpy.types.PropertyGroup):
|
|||
description="Select bake mode",
|
||||
default="Foreground")
|
||||
|
||||
caching_modes = [('Copy', 'Copy', 'More overhead; allows for network.')]
|
||||
|
||||
#caching_modes.append(('Cache', 'Cache', 'Cache in separate blend'),('Node', 'Node restore', 'EXPERIMENTAL! Use with care'))
|
||||
|
||||
tlm_caching_mode : EnumProperty(
|
||||
items = [('Copy', 'Copy', 'More overhead; allows for network.'),
|
||||
('Cache', 'Cache', 'Cache in separate blend'),
|
||||
('Node', 'Node restore', 'EXPERIMENTAL! Use with care')],
|
||||
items = caching_modes,
|
||||
name = "Caching mode",
|
||||
description="Select cache mode",
|
||||
default="Copy")
|
||||
|
@ -88,8 +100,16 @@ class TLM_CyclesSceneProperties(bpy.types.PropertyGroup):
|
|||
|
||||
tlm_lighting_mode : EnumProperty(
|
||||
items = [('combined', 'Combined', 'Bake combined lighting'),
|
||||
('combinedao', 'Combined+AO', 'Bake combined lighting with Ambient Occlusion'),
|
||||
('indirect', 'Indirect', 'Bake indirect lighting'),
|
||||
('ao', 'AO', 'Bake only Ambient Occlusion')],
|
||||
# ('indirectao', 'Indirect+AO', 'Bake indirect lighting with Ambient Occlusion'),
|
||||
('ao', 'AO', 'Bake only Ambient Occlusion'),
|
||||
('complete', 'Complete', 'Bake complete map')],
|
||||
name = "Lighting mode",
|
||||
description="TODO.",
|
||||
default="combined")
|
||||
default="combined")
|
||||
|
||||
tlm_premultiply_ao : BoolProperty(
|
||||
name="Premultiply AO",
|
||||
description="Ambient Occlusion will be premultiplied together with lightmaps, requiring less textures.",
|
||||
default=True)
|
|
@ -0,0 +1,10 @@
|
|||
import bpy
|
||||
from bpy.props import *
|
||||
|
||||
class TLM_OctanerenderSceneProperties(bpy.types.PropertyGroup):
|
||||
|
||||
tlm_lightmap_savedir : StringProperty(
|
||||
name="Lightmap Directory",
|
||||
description="TODO",
|
||||
default="Lightmaps",
|
||||
subtype="FILE_PATH")
|
|
@ -1,11 +1,19 @@
|
|||
import bpy
|
||||
import bpy, os
|
||||
from bpy.props import *
|
||||
from .. utility import utility
|
||||
|
||||
def transfer_load():
|
||||
load_folder = bpy.context.scene.TLM_SceneProperties.tlm_load_folder
|
||||
lightmap_folder = os.path.join(os.path.dirname(bpy.data.filepath), bpy.context.scene.TLM_EngineProperties.tlm_lightmap_savedir)
|
||||
print(load_folder)
|
||||
print(lightmap_folder)
|
||||
#transfer_assets(True, load_folder, lightmap_folder)
|
||||
|
||||
class TLM_SceneProperties(bpy.types.PropertyGroup):
|
||||
|
||||
engines = [('Cycles', 'Cycles', 'Use Cycles for lightmapping')]
|
||||
|
||||
engines.append(('LuxCoreRender', 'LuxCoreRender', 'Use LuxCoreRender for lightmapping'))
|
||||
#engines.append(('LuxCoreRender', 'LuxCoreRender', 'Use LuxCoreRender for lightmapping'))
|
||||
#engines.append(('OctaneRender', 'Octane Render', 'Use Octane Render for lightmapping'))
|
||||
|
||||
tlm_atlas_pointer : StringProperty(
|
||||
|
@ -112,7 +120,7 @@ class TLM_SceneProperties(bpy.types.PropertyGroup):
|
|||
|
||||
#FILTERING SETTINGS GROUP
|
||||
tlm_filtering_use : BoolProperty(
|
||||
name="Enable Filtering",
|
||||
name="Enable denoising",
|
||||
description="Enable denoising for lightmaps",
|
||||
default=False)
|
||||
|
||||
|
@ -182,6 +190,17 @@ class TLM_SceneProperties(bpy.types.PropertyGroup):
|
|||
min=1,
|
||||
max=5)
|
||||
|
||||
tlm_clamp_hdr : BoolProperty(
|
||||
name="Enable HDR Clamp",
|
||||
description="Clamp HDR Value",
|
||||
default=False)
|
||||
|
||||
tlm_clamp_hdr_value : IntProperty(
|
||||
name="HDR Clamp value",
|
||||
default=10,
|
||||
min=0,
|
||||
max=20)
|
||||
|
||||
#Encoding properties
|
||||
tlm_encoding_use : BoolProperty(
|
||||
name="Enable encoding",
|
||||
|
@ -197,12 +216,13 @@ class TLM_SceneProperties(bpy.types.PropertyGroup):
|
|||
|
||||
encoding_modes_1 = [('RGBM', 'RGBM', '8-bit HDR encoding. Good for compatibility, good for memory but has banding issues.'),
|
||||
('RGBD', 'RGBD', '8-bit HDR encoding. Similar to RGBM.'),
|
||||
('HDR', 'HDR', '32-bit HDR encoding. Best quality, but high memory usage and not compatible with all devices.')]
|
||||
('HDR', 'HDR', '32-bit HDR encoding. Best quality, but high memory usage and not compatible with all devices.'),
|
||||
('SDR', 'SDR', '8-bit flat encoding.')]
|
||||
|
||||
encoding_modes_2 = [('RGBM', 'RGBM', '8-bit HDR encoding. Good for compatibility, good for memory but has banding issues.'),
|
||||
('RGBD', 'RGBD', '8-bit HDR encoding. Similar to RGBM.'),
|
||||
encoding_modes_2 = [('RGBD', 'RGBD', '8-bit HDR encoding. Similar to RGBM.'),
|
||||
('LogLuv', 'LogLuv', '8-bit HDR encoding. Different.'),
|
||||
('HDR', 'HDR', '32-bit HDR encoding. Best quality, but high memory usage and not compatible with all devices.')]
|
||||
('HDR', 'HDR', '32-bit HDR encoding. Best quality, but high memory usage and not compatible with all devices.'),
|
||||
('SDR', 'SDR', '8-bit flat encoding.')]
|
||||
|
||||
tlm_encoding_mode_a : EnumProperty(
|
||||
items = encoding_modes_1,
|
||||
|
@ -275,8 +295,7 @@ class TLM_SceneProperties(bpy.types.PropertyGroup):
|
|||
tlm_mesh_lightmap_unwrap_mode : EnumProperty(
|
||||
items = [('Lightmap', 'Lightmap', 'TODO'),
|
||||
('SmartProject', 'Smart Project', 'TODO'),
|
||||
('CopyExisting', 'Copy Existing', 'TODO'),
|
||||
('AtlasGroupA', 'Atlas Group (Prepack)', 'TODO'),
|
||||
('AtlasGroupA', 'Atlas Group (Prepack)', 'Attaches the object to a prepack Atlas group. Will overwrite UV map on build.'),
|
||||
('Xatlas', 'Xatlas', 'TODO')],
|
||||
name = "Unwrap Mode",
|
||||
description="TODO",
|
||||
|
@ -317,12 +336,30 @@ class TLM_SceneProperties(bpy.types.PropertyGroup):
|
|||
|
||||
tlm_metallic_clamp : EnumProperty(
|
||||
items = [('ignore', 'Ignore', 'Ignore clamping'),
|
||||
('skip', 'Skip', 'Skip baking metallic materials'),
|
||||
('zero', 'Zero', 'Set zero'),
|
||||
('limit', 'Limit', 'Clamp to 0.9')],
|
||||
name = "Metallic clamping",
|
||||
description="TODO.",
|
||||
default="ignore")
|
||||
|
||||
tlm_texture_interpolation : EnumProperty(
|
||||
items = [('Smart', 'Smart', 'Bicubic when magnifying.'),
|
||||
('Cubic', 'Cubic', 'Cubic interpolation'),
|
||||
('Closest', 'Closest', 'No interpolation'),
|
||||
('Linear', 'Linear', 'Linear')],
|
||||
name = "Texture interpolation",
|
||||
description="Texture interpolation.",
|
||||
default="Linear")
|
||||
|
||||
tlm_texture_extrapolation : EnumProperty(
|
||||
items = [('REPEAT', 'Repeat', 'Repeat in both direction.'),
|
||||
('EXTEND', 'Extend', 'Extend by repeating edge pixels.'),
|
||||
('CLIP', 'Clip', 'Clip to image size')],
|
||||
name = "Texture extrapolation",
|
||||
description="Texture extrapolation.",
|
||||
default="EXTEND")
|
||||
|
||||
tlm_verbose : BoolProperty(
|
||||
name="Verbose",
|
||||
description="Verbose console output",
|
||||
|
@ -409,4 +446,52 @@ class TLM_SceneProperties(bpy.types.PropertyGroup):
|
|||
('CYCLES', 'Cycles', 'TODO')],
|
||||
name = "Probe Render Engine",
|
||||
description="TODO",
|
||||
default='BLENDER_EEVEE')
|
||||
default='BLENDER_EEVEE')
|
||||
|
||||
tlm_load_folder : StringProperty(
|
||||
name="Load Folder",
|
||||
description="Load existing lightmaps from folder",
|
||||
subtype="DIR_PATH")
|
||||
|
||||
tlm_utility_set : EnumProperty(
|
||||
items = [('Scene', 'Scene', 'Set for all objects in the scene.'),
|
||||
('Selection', 'Selection', 'Set for selected objects.'),
|
||||
('Enabled', 'Enabled', 'Set for objects that has been enabled for lightmapping.')],
|
||||
name = "Set",
|
||||
description="Utility selection set",
|
||||
default='Scene')
|
||||
|
||||
tlm_resolution_weight : EnumProperty(
|
||||
items = [('Single', 'Single', 'Set a single resolution for all objects.'),
|
||||
('Dimension', 'Dimension', 'Distribute resolutions based on object dimensions.'),
|
||||
('Surface', 'Surface', 'Distribute resolutions based on mesh surface area.'),
|
||||
('Volume', 'Volume', 'Distribute resolutions based on mesh volume.')],
|
||||
name = "Resolution weight",
|
||||
description="Method for setting resolution value",
|
||||
default='Single')
|
||||
#Todo add vertex color option
|
||||
|
||||
tlm_resolution_min : EnumProperty(
|
||||
items = [('32', '32', 'TODO'),
|
||||
('64', '64', 'TODO'),
|
||||
('128', '128', 'TODO'),
|
||||
('256', '256', 'TODO'),
|
||||
('512', '512', 'TODO'),
|
||||
('1024', '1024', 'TODO'),
|
||||
('2048', '2048', 'TODO'),
|
||||
('4096', '4096', 'TODO')],
|
||||
name = "Minimum resolution",
|
||||
description="Minimum distributed resolution",
|
||||
default='32')
|
||||
|
||||
tlm_resolution_max : EnumProperty(
|
||||
items = [('64', '64', 'TODO'),
|
||||
('128', '128', 'TODO'),
|
||||
('256', '256', 'TODO'),
|
||||
('512', '512', 'TODO'),
|
||||
('1024', '1024', 'TODO'),
|
||||
('2048', '2048', 'TODO'),
|
||||
('4096', '4096', 'TODO')],
|
||||
name = "Maximum resolution",
|
||||
description="Maximum distributed resolution",
|
||||
default='256')
|
|
@ -1,17 +1,20 @@
|
|||
import bpy, os, subprocess, sys, platform, aud, json, datetime, socket
|
||||
import threading
|
||||
|
||||
from . import encoding, pack
|
||||
from . cycles import lightmap, prepare, nodes, cache
|
||||
from . luxcore import setup
|
||||
from . octane import configure, lightmap2
|
||||
from . denoiser import integrated, oidn, optix
|
||||
from . filtering import opencv
|
||||
from . gui import Viewport
|
||||
from .. network import client
|
||||
|
||||
from os import listdir
|
||||
from os.path import isfile, join
|
||||
from time import time, sleep
|
||||
from importlib import util
|
||||
|
||||
previous_settings = {}
|
||||
|
||||
postprocess_shutdown = False
|
||||
|
||||
def prepare_build(self=0, background_mode=False, shutdown_after_build=False):
|
||||
|
@ -21,15 +24,31 @@ def prepare_build(self=0, background_mode=False, shutdown_after_build=False):
|
|||
|
||||
print("Building lightmaps")
|
||||
|
||||
if bpy.context.scene.TLM_EngineProperties.tlm_lighting_mode == "combinedao":
|
||||
|
||||
scene = bpy.context.scene
|
||||
|
||||
if not "tlm_plus_mode" in bpy.app.driver_namespace or bpy.app.driver_namespace["tlm_plus_mode"] == 0:
|
||||
filepath = bpy.data.filepath
|
||||
dirpath = os.path.join(os.path.dirname(bpy.data.filepath), scene.TLM_EngineProperties.tlm_lightmap_savedir)
|
||||
if os.path.isdir(dirpath):
|
||||
for file in os.listdir(dirpath):
|
||||
os.remove(os.path.join(dirpath + "/" + file))
|
||||
bpy.app.driver_namespace["tlm_plus_mode"] = 1
|
||||
print("Plus Mode")
|
||||
|
||||
if bpy.context.scene.TLM_EngineProperties.tlm_bake_mode == "Foreground" or background_mode==True:
|
||||
|
||||
global start_time
|
||||
start_time = time()
|
||||
bpy.app.driver_namespace["tlm_start_time"] = time()
|
||||
|
||||
scene = bpy.context.scene
|
||||
sceneProperties = scene.TLM_SceneProperties
|
||||
|
||||
#Timer start here bound to global
|
||||
if not background_mode and bpy.context.scene.TLM_EngineProperties.tlm_lighting_mode != "combinedao":
|
||||
#pass
|
||||
setGui(1)
|
||||
|
||||
if check_save():
|
||||
print("Please save your file first")
|
||||
|
@ -52,12 +71,6 @@ def prepare_build(self=0, background_mode=False, shutdown_after_build=False):
|
|||
self.report({'INFO'}, "Error:Filtering - OpenCV not installed")
|
||||
return{'FINISHED'}
|
||||
|
||||
#TODO DO some resolution change
|
||||
#if checkAtlasSize():
|
||||
# print("Error: AtlasGroup overflow")
|
||||
# self.report({'INFO'}, "Error: AtlasGroup overflow - Too many objects")
|
||||
# return{'FINISHED'}
|
||||
|
||||
setMode()
|
||||
|
||||
dirpath = os.path.join(os.path.dirname(bpy.data.filepath), bpy.context.scene.TLM_EngineProperties.tlm_lightmap_savedir)
|
||||
|
@ -67,19 +80,6 @@ def prepare_build(self=0, background_mode=False, shutdown_after_build=False):
|
|||
#Naming check
|
||||
naming_check()
|
||||
|
||||
# if sceneProperties.tlm_reset_uv or sceneProperties.tlm_atlas_mode == "Postpack":
|
||||
# for obj in bpy.data.objects:
|
||||
# if obj.type == "MESH":
|
||||
# uv_layers = obj.data.uv_layers
|
||||
|
||||
|
||||
|
||||
#for uvlayer in uv_layers:
|
||||
# if uvlayer.name == "UVMap_Lightmap":
|
||||
# uv_layers.remove(uvlayer)
|
||||
|
||||
## RENDER DEPENDENCY FROM HERE
|
||||
|
||||
if sceneProperties.tlm_lightmap_engine == "Cycles":
|
||||
|
||||
prepare.init(self, previous_settings)
|
||||
|
@ -90,18 +90,14 @@ def prepare_build(self=0, background_mode=False, shutdown_after_build=False):
|
|||
|
||||
if sceneProperties.tlm_lightmap_engine == "OctaneRender":
|
||||
|
||||
pass
|
||||
|
||||
#Renderer - Store settings
|
||||
|
||||
#Renderer - Set settings
|
||||
|
||||
#Renderer - Config objects, lights, world
|
||||
configure.init(self, previous_settings)
|
||||
|
||||
begin_build()
|
||||
|
||||
else:
|
||||
|
||||
print("Baking in background")
|
||||
|
||||
filepath = bpy.data.filepath
|
||||
|
||||
bpy.ops.wm.save_as_mainfile(filepath=bpy.data.filepath)
|
||||
|
@ -111,22 +107,7 @@ def prepare_build(self=0, background_mode=False, shutdown_after_build=False):
|
|||
scene = bpy.context.scene
|
||||
sceneProperties = scene.TLM_SceneProperties
|
||||
|
||||
#We dynamically load the renderer and denoiser, instead of loading something we don't use
|
||||
|
||||
if sceneProperties.tlm_lightmap_engine == "Cycles":
|
||||
|
||||
pass
|
||||
|
||||
if sceneProperties.tlm_lightmap_engine == "LuxCoreRender":
|
||||
|
||||
pass
|
||||
|
||||
if sceneProperties.tlm_lightmap_engine == "OctaneRender":
|
||||
|
||||
pass
|
||||
|
||||
#Timer start here bound to global
|
||||
|
||||
if check_save():
|
||||
print("Please save your file first")
|
||||
self.report({'INFO'}, "Please save your file first")
|
||||
|
@ -168,23 +149,12 @@ def prepare_build(self=0, background_mode=False, shutdown_after_build=False):
|
|||
|
||||
client.connect_client(HOST, PORT, bpy.data.filepath, 0)
|
||||
|
||||
# with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
|
||||
# s.connect((HOST, PORT))
|
||||
# message = {
|
||||
# "call" : 1,
|
||||
# "command" : 1,
|
||||
# "enquiry" : 0,
|
||||
# "args" : bpy.data.filepath
|
||||
# }
|
||||
|
||||
# s.sendall(json.dumps(message).encode())
|
||||
# data = s.recv(1024)
|
||||
# print(data.decode())
|
||||
|
||||
finish_assemble()
|
||||
|
||||
else:
|
||||
|
||||
print("Background driver process")
|
||||
|
||||
bpy.app.driver_namespace["alpha"] = 0
|
||||
|
||||
bpy.app.driver_namespace["tlm_process"] = False
|
||||
|
@ -196,6 +166,8 @@ def prepare_build(self=0, background_mode=False, shutdown_after_build=False):
|
|||
|
||||
def distribute_building():
|
||||
|
||||
print("Distributing lightmap building")
|
||||
|
||||
#CHECK IF THERE'S AN EXISTING SUBPROCESS
|
||||
|
||||
if not os.path.isfile(os.path.join(os.path.dirname(bpy.data.filepath), bpy.context.scene.TLM_EngineProperties.tlm_lightmap_savedir, "process.tlm")):
|
||||
|
@ -215,8 +187,16 @@ def distribute_building():
|
|||
with open(os.path.join(write_directory, "process.tlm"), 'w') as file:
|
||||
json.dump(process_status, file, indent=2)
|
||||
|
||||
bpy.app.driver_namespace["tlm_process"] = subprocess.Popen([sys.executable,"-b", blendPath,"--python-expr",'import bpy; import thelightmapper; thelightmapper.addon.utility.build.prepare_build(0, True);'], shell=False, stderr=subprocess.DEVNULL, stdout=subprocess.DEVNULL)
|
||||
|
||||
if (2, 91, 0) > bpy.app.version:
|
||||
if bpy.context.scene.TLM_SceneProperties.tlm_verbose:
|
||||
bpy.app.driver_namespace["tlm_process"] = subprocess.Popen([sys.executable,"-b", blendPath,"--python-expr",'import bpy; import thelightmapper; thelightmapper.addon.utility.build.prepare_build(0, True);'], shell=False, stdout=subprocess.PIPE)
|
||||
else:
|
||||
bpy.app.driver_namespace["tlm_process"] = subprocess.Popen([sys.executable,"-b", blendPath,"--python-expr",'import bpy; import thelightmapper; thelightmapper.addon.utility.build.prepare_build(0, True);'], shell=False, stderr=subprocess.DEVNULL, stdout=subprocess.DEVNULL)
|
||||
else:
|
||||
if bpy.context.scene.TLM_SceneProperties.tlm_verbose:
|
||||
bpy.app.driver_namespace["tlm_process"] = subprocess.Popen([bpy.app.binary_path,"-b", blendPath,"--python-expr",'import bpy; import thelightmapper; thelightmapper.addon.utility.build.prepare_build(0, True);'], shell=False, stdout=subprocess.PIPE)
|
||||
else:
|
||||
bpy.app.driver_namespace["tlm_process"] = subprocess.Popen([bpy.app.binary_path,"-b", blendPath,"--python-expr",'import bpy; import thelightmapper; thelightmapper.addon.utility.build.prepare_build(0, True);'], shell=False, stderr=subprocess.DEVNULL, stdout=subprocess.DEVNULL)
|
||||
if bpy.context.scene.TLM_SceneProperties.tlm_verbose:
|
||||
print("Started process: " + str(bpy.app.driver_namespace["tlm_process"]) + " at " + str(datetime.datetime.now()))
|
||||
|
||||
|
@ -269,6 +249,10 @@ def finish_assemble(self=0):
|
|||
if sceneProperties.tlm_lightmap_engine == "OctaneRender":
|
||||
pass
|
||||
|
||||
if not 'start_time' in globals():
|
||||
global start_time
|
||||
start_time = time()
|
||||
|
||||
manage_build(True)
|
||||
|
||||
def begin_build():
|
||||
|
@ -288,7 +272,8 @@ def begin_build():
|
|||
pass
|
||||
|
||||
if sceneProperties.tlm_lightmap_engine == "OctaneRender":
|
||||
pass
|
||||
|
||||
lightmap2.bake()
|
||||
|
||||
#Denoiser
|
||||
if sceneProperties.tlm_denoise_use:
|
||||
|
@ -429,7 +414,7 @@ def begin_build():
|
|||
print("Encoding:" + str(file))
|
||||
encoding.encodeImageRGBMCPU(img, sceneProperties.tlm_encoding_range, dirpath, 0)
|
||||
|
||||
if sceneProperties.tlm_encoding_mode_b == "RGBD":
|
||||
if sceneProperties.tlm_encoding_mode_a == "RGBD":
|
||||
|
||||
if bpy.context.scene.TLM_SceneProperties.tlm_verbose:
|
||||
print("ENCODING RGBD")
|
||||
|
@ -455,6 +440,36 @@ def begin_build():
|
|||
print("Encoding:" + str(file))
|
||||
encoding.encodeImageRGBDCPU(img, sceneProperties.tlm_encoding_range, dirpath, 0)
|
||||
|
||||
if sceneProperties.tlm_encoding_mode_a == "SDR":
|
||||
|
||||
if bpy.context.scene.TLM_SceneProperties.tlm_verbose:
|
||||
print("EXR Format")
|
||||
|
||||
ren = bpy.context.scene.render
|
||||
ren.image_settings.file_format = "PNG"
|
||||
#ren.image_settings.exr_codec = "scene.TLM_SceneProperties.tlm_exr_codec"
|
||||
|
||||
end = "_baked"
|
||||
|
||||
baked_image_array = []
|
||||
|
||||
if sceneProperties.tlm_denoise_use:
|
||||
|
||||
end = "_denoised"
|
||||
|
||||
if sceneProperties.tlm_filtering_use:
|
||||
|
||||
end = "_filtered"
|
||||
|
||||
#For each image in folder ending in denoised/filtered
|
||||
dirfiles = [f for f in listdir(dirpath) if isfile(join(dirpath, f))]
|
||||
|
||||
for file in dirfiles:
|
||||
if file.endswith(end + ".hdr"):
|
||||
|
||||
img = bpy.data.images.load(os.path.join(dirpath,file))
|
||||
img.save_render(img.filepath_raw[:-4] + ".png")
|
||||
|
||||
else:
|
||||
|
||||
if sceneProperties.tlm_encoding_mode_b == "HDR":
|
||||
|
@ -562,6 +577,33 @@ def begin_build():
|
|||
print("Encoding:" + str(file))
|
||||
encoding.encodeImageRGBDGPU(img, sceneProperties.tlm_encoding_range, dirpath, 0)
|
||||
|
||||
if sceneProperties.tlm_encoding_mode_b == "PNG":
|
||||
|
||||
ren = bpy.context.scene.render
|
||||
ren.image_settings.file_format = "PNG"
|
||||
#ren.image_settings.exr_codec = "scene.TLM_SceneProperties.tlm_exr_codec"
|
||||
|
||||
end = "_baked"
|
||||
|
||||
baked_image_array = []
|
||||
|
||||
if sceneProperties.tlm_denoise_use:
|
||||
|
||||
end = "_denoised"
|
||||
|
||||
if sceneProperties.tlm_filtering_use:
|
||||
|
||||
end = "_filtered"
|
||||
|
||||
#For each image in folder ending in denoised/filtered
|
||||
dirfiles = [f for f in listdir(dirpath) if isfile(join(dirpath, f))]
|
||||
|
||||
for file in dirfiles:
|
||||
if file.endswith(end + ".hdr"):
|
||||
|
||||
img = bpy.data.images.load(os.path.join(dirpath,file))
|
||||
img.save_render(img.filepath_raw[:-4] + ".png")
|
||||
|
||||
manage_build()
|
||||
|
||||
def manage_build(background_pass=False):
|
||||
|
@ -610,6 +652,10 @@ def manage_build(background_pass=False):
|
|||
|
||||
formatEnc = "_encoded.png"
|
||||
|
||||
if sceneProperties.tlm_encoding_mode_a == "SDR":
|
||||
|
||||
formatEnc = ".png"
|
||||
|
||||
else:
|
||||
|
||||
print("GPU Encoding")
|
||||
|
@ -632,6 +678,10 @@ def manage_build(background_pass=False):
|
|||
|
||||
formatEnc = "_encoded.png"
|
||||
|
||||
if sceneProperties.tlm_encoding_mode_b == "SDR":
|
||||
|
||||
formatEnc = ".png"
|
||||
|
||||
if not background_pass:
|
||||
nodes.exchangeLightmapsToPostfix("_baked", end, formatEnc)
|
||||
|
||||
|
@ -653,13 +703,13 @@ def manage_build(background_pass=False):
|
|||
filepath = bpy.data.filepath
|
||||
dirpath = os.path.join(os.path.dirname(bpy.data.filepath), scene.TLM_EngineProperties.tlm_lightmap_savedir)
|
||||
|
||||
for obj in bpy.data.objects:
|
||||
if obj.type == "MESH":
|
||||
for obj in bpy.context.scene.objects:
|
||||
if obj.type == 'MESH' and obj.name in bpy.context.view_layer.objects:
|
||||
if obj.TLM_ObjectProperties.tlm_mesh_lightmap_use:
|
||||
cache.backup_material_restore(obj)
|
||||
|
||||
for obj in bpy.data.objects:
|
||||
if obj.type == "MESH":
|
||||
for obj in bpy.context.scene.objects:
|
||||
if obj.type == 'MESH' and obj.name in bpy.context.view_layer.objects:
|
||||
if obj.TLM_ObjectProperties.tlm_mesh_lightmap_use:
|
||||
cache.backup_material_rename(obj)
|
||||
|
||||
|
@ -672,77 +722,181 @@ def manage_build(background_pass=False):
|
|||
if "_Original" in mat.name:
|
||||
bpy.data.materials.remove(mat)
|
||||
|
||||
for obj in bpy.data.objects:
|
||||
|
||||
if obj.type == "MESH":
|
||||
for obj in bpy.context.scene.objects:
|
||||
if obj.type == 'MESH' and obj.name in bpy.context.view_layer.objects:
|
||||
if obj.TLM_ObjectProperties.tlm_mesh_lightmap_use:
|
||||
img_name = obj.name + '_baked'
|
||||
Lightmapimage = bpy.data.images[img_name]
|
||||
obj["Lightmap"] = Lightmapimage.filepath_raw
|
||||
|
||||
if obj.TLM_ObjectProperties.tlm_mesh_lightmap_unwrap_mode == "AtlasGroupA":
|
||||
atlasName = obj.TLM_ObjectProperties.tlm_atlas_pointer
|
||||
img_name = atlasName + '_baked'
|
||||
Lightmapimage = bpy.data.images[img_name]
|
||||
obj["Lightmap"] = Lightmapimage.filepath_raw
|
||||
elif obj.TLM_ObjectProperties.tlm_postpack_object:
|
||||
atlasName = obj.TLM_ObjectProperties.tlm_postatlas_pointer
|
||||
img_name = atlasName + '_baked' + ".hdr"
|
||||
Lightmapimage = bpy.data.images[img_name]
|
||||
obj["Lightmap"] = Lightmapimage.filepath_raw
|
||||
else:
|
||||
img_name = obj.name + '_baked'
|
||||
Lightmapimage = bpy.data.images[img_name]
|
||||
obj["Lightmap"] = Lightmapimage.filepath_raw
|
||||
|
||||
for image in bpy.data.images:
|
||||
if image.name.endswith("_baked"):
|
||||
bpy.data.images.remove(image, do_unlink=True)
|
||||
|
||||
total_time = sec_to_hours((time() - start_time))
|
||||
if bpy.context.scene.TLM_SceneProperties.tlm_verbose:
|
||||
print(total_time)
|
||||
if "tlm_plus_mode" in bpy.app.driver_namespace: #First DIR pass
|
||||
|
||||
bpy.context.scene["TLM_Buildstat"] = total_time
|
||||
if bpy.app.driver_namespace["tlm_plus_mode"] == 1: #First DIR pass
|
||||
|
||||
reset_settings(previous_settings["settings"])
|
||||
filepath = bpy.data.filepath
|
||||
dirpath = os.path.join(os.path.dirname(bpy.data.filepath), scene.TLM_EngineProperties.tlm_lightmap_savedir)
|
||||
|
||||
if sceneProperties.tlm_lightmap_engine == "LuxCoreRender":
|
||||
for obj in bpy.context.scene.objects:
|
||||
if obj.type == 'MESH' and obj.name in bpy.context.view_layer.objects:
|
||||
if obj.TLM_ObjectProperties.tlm_mesh_lightmap_use:
|
||||
cache.backup_material_restore(obj)
|
||||
|
||||
pass
|
||||
for obj in bpy.context.scene.objects:
|
||||
if obj.type == 'MESH' and obj.name in bpy.context.view_layer.objects:
|
||||
if obj.TLM_ObjectProperties.tlm_mesh_lightmap_use:
|
||||
cache.backup_material_rename(obj)
|
||||
|
||||
if sceneProperties.tlm_lightmap_engine == "OctaneRender":
|
||||
for mat in bpy.data.materials:
|
||||
if mat.users < 1:
|
||||
bpy.data.materials.remove(mat)
|
||||
|
||||
pass
|
||||
for mat in bpy.data.materials:
|
||||
if mat.name.startswith("."):
|
||||
if "_Original" in mat.name:
|
||||
bpy.data.materials.remove(mat)
|
||||
|
||||
if bpy.context.scene.TLM_EngineProperties.tlm_bake_mode == "Background":
|
||||
pass
|
||||
for image in bpy.data.images:
|
||||
if image.name.endswith("_baked"):
|
||||
bpy.data.images.remove(image, do_unlink=True)
|
||||
|
||||
if scene.TLM_SceneProperties.tlm_alert_on_finish:
|
||||
dirpath = os.path.join(os.path.dirname(bpy.data.filepath), bpy.context.scene.TLM_EngineProperties.tlm_lightmap_savedir)
|
||||
|
||||
alertSelect = scene.TLM_SceneProperties.tlm_alert_sound
|
||||
files = os.listdir(dirpath)
|
||||
|
||||
for index, file in enumerate(files):
|
||||
|
||||
filename = extension = os.path.splitext(file)[0]
|
||||
extension = os.path.splitext(file)[1]
|
||||
|
||||
os.rename(os.path.join(dirpath, file), os.path.join(dirpath, filename + "_dir" + extension))
|
||||
|
||||
print("First DIR pass complete")
|
||||
|
||||
bpy.app.driver_namespace["tlm_plus_mode"] = 2
|
||||
|
||||
prepare_build(self=0, background_mode=False, shutdown_after_build=False)
|
||||
|
||||
if not background_pass and bpy.context.scene.TLM_EngineProperties.tlm_lighting_mode != "combinedao":
|
||||
#pass
|
||||
setGui(0)
|
||||
|
||||
elif bpy.app.driver_namespace["tlm_plus_mode"] == 2:
|
||||
|
||||
filepath = bpy.data.filepath
|
||||
|
||||
dirpath = os.path.join(os.path.dirname(bpy.data.filepath), bpy.context.scene.TLM_EngineProperties.tlm_lightmap_savedir)
|
||||
|
||||
files = os.listdir(dirpath)
|
||||
|
||||
for index, file in enumerate(files):
|
||||
|
||||
filename = os.path.splitext(file)[0]
|
||||
extension = os.path.splitext(file)[1]
|
||||
|
||||
if not filename.endswith("_dir"):
|
||||
os.rename(os.path.join(dirpath, file), os.path.join(dirpath, filename + "_ao" + extension))
|
||||
|
||||
print("Second AO pass complete")
|
||||
|
||||
total_time = sec_to_hours((time() - start_time))
|
||||
if bpy.context.scene.TLM_SceneProperties.tlm_verbose:
|
||||
print(total_time)
|
||||
|
||||
bpy.context.scene["TLM_Buildstat"] = total_time
|
||||
|
||||
reset_settings(previous_settings["settings"])
|
||||
|
||||
bpy.app.driver_namespace["tlm_plus_mode"] = 0
|
||||
|
||||
if not background_pass:
|
||||
|
||||
#TODO CHANGE!
|
||||
|
||||
nodes.exchangeLightmapsToPostfix(end, end + "_dir", formatEnc)
|
||||
|
||||
nodes.applyAOPass()
|
||||
|
||||
if alertSelect == "dash":
|
||||
soundfile = "dash.ogg"
|
||||
elif alertSelect == "pingping":
|
||||
soundfile = "pingping.ogg"
|
||||
elif alertSelect == "gentle":
|
||||
soundfile = "gentle.ogg"
|
||||
else:
|
||||
soundfile = "noot.ogg"
|
||||
|
||||
scriptDir = os.path.dirname(os.path.realpath(__file__))
|
||||
sound_path = os.path.abspath(os.path.join(scriptDir, '..', 'assets/'+soundfile))
|
||||
total_time = sec_to_hours((time() - start_time))
|
||||
if bpy.context.scene.TLM_SceneProperties.tlm_verbose:
|
||||
print(total_time)
|
||||
|
||||
device = aud.Device()
|
||||
sound = aud.Sound.file(sound_path)
|
||||
device.play(sound)
|
||||
bpy.context.scene["TLM_Buildstat"] = total_time
|
||||
|
||||
print("Lightmap building finished")
|
||||
reset_settings(previous_settings["settings"])
|
||||
|
||||
if bpy.app.background:
|
||||
print("Lightmap building finished")
|
||||
|
||||
if bpy.context.scene.TLM_SceneProperties.tlm_verbose:
|
||||
print("Writing background process report")
|
||||
|
||||
write_directory = os.path.join(os.path.dirname(bpy.data.filepath), bpy.context.scene.TLM_EngineProperties.tlm_lightmap_savedir)
|
||||
if sceneProperties.tlm_lightmap_engine == "LuxCoreRender":
|
||||
|
||||
if os.path.exists(os.path.join(write_directory, "process.tlm")):
|
||||
pass
|
||||
|
||||
process_status = json.loads(open(os.path.join(write_directory, "process.tlm")).read())
|
||||
if sceneProperties.tlm_lightmap_engine == "OctaneRender":
|
||||
|
||||
process_status[1]["completed"] = True
|
||||
pass
|
||||
|
||||
with open(os.path.join(write_directory, "process.tlm"), 'w') as file:
|
||||
json.dump(process_status, file, indent=2)
|
||||
if bpy.context.scene.TLM_EngineProperties.tlm_bake_mode == "Background":
|
||||
pass
|
||||
|
||||
if postprocess_shutdown:
|
||||
sys.exit()
|
||||
if not background_pass and bpy.context.scene.TLM_EngineProperties.tlm_lighting_mode != "combinedao":
|
||||
#pass
|
||||
setGui(0)
|
||||
|
||||
if scene.TLM_SceneProperties.tlm_alert_on_finish:
|
||||
|
||||
alertSelect = scene.TLM_SceneProperties.tlm_alert_sound
|
||||
|
||||
if alertSelect == "dash":
|
||||
soundfile = "dash.ogg"
|
||||
elif alertSelect == "pingping":
|
||||
soundfile = "pingping.ogg"
|
||||
elif alertSelect == "gentle":
|
||||
soundfile = "gentle.ogg"
|
||||
else:
|
||||
soundfile = "noot.ogg"
|
||||
|
||||
scriptDir = os.path.dirname(os.path.realpath(__file__))
|
||||
sound_path = os.path.abspath(os.path.join(scriptDir, '..', 'assets/'+soundfile))
|
||||
|
||||
device = aud.Device()
|
||||
sound = aud.Sound.file(sound_path)
|
||||
device.play(sound)
|
||||
|
||||
if bpy.app.background:
|
||||
|
||||
if bpy.context.scene.TLM_SceneProperties.tlm_verbose:
|
||||
print("Writing background process report")
|
||||
|
||||
write_directory = os.path.join(os.path.dirname(bpy.data.filepath), bpy.context.scene.TLM_EngineProperties.tlm_lightmap_savedir)
|
||||
|
||||
if os.path.exists(os.path.join(write_directory, "process.tlm")):
|
||||
|
||||
process_status = json.loads(open(os.path.join(write_directory, "process.tlm")).read())
|
||||
|
||||
process_status[1]["completed"] = True
|
||||
|
||||
with open(os.path.join(write_directory, "process.tlm"), 'w') as file:
|
||||
json.dump(process_status, file, indent=2)
|
||||
|
||||
if postprocess_shutdown:
|
||||
sys.exit()
|
||||
|
||||
#TODO - SET BELOW TO UTILITY
|
||||
|
||||
|
@ -770,9 +924,8 @@ def reset_settings(prev_settings):
|
|||
|
||||
def naming_check():
|
||||
|
||||
for obj in bpy.data.objects:
|
||||
|
||||
if obj.type == "MESH":
|
||||
for obj in bpy.context.scene.objects:
|
||||
if obj.type == 'MESH' and obj.name in bpy.context.view_layer.objects:
|
||||
|
||||
if obj.TLM_ObjectProperties.tlm_mesh_lightmap_use:
|
||||
|
||||
|
@ -829,6 +982,9 @@ def check_save():
|
|||
|
||||
def check_denoiser():
|
||||
|
||||
if bpy.context.scene.TLM_SceneProperties.tlm_verbose:
|
||||
print("Checking denoiser path")
|
||||
|
||||
scene = bpy.context.scene
|
||||
|
||||
if scene.TLM_SceneProperties.tlm_denoise_use:
|
||||
|
@ -847,8 +1003,8 @@ def check_denoiser():
|
|||
return 0
|
||||
|
||||
def check_materials():
|
||||
for obj in bpy.data.objects:
|
||||
if obj.type == "MESH":
|
||||
for obj in bpy.context.scene.objects:
|
||||
if obj.type == 'MESH' and obj.name in bpy.context.view_layer.objects:
|
||||
if obj.TLM_ObjectProperties.tlm_mesh_lightmap_use:
|
||||
for slot in obj.material_slots:
|
||||
mat = slot.material
|
||||
|
@ -868,15 +1024,39 @@ def check_materials():
|
|||
def sec_to_hours(seconds):
|
||||
a=str(seconds//3600)
|
||||
b=str((seconds%3600)//60)
|
||||
c=str((seconds%3600)%60)
|
||||
c=str(round((seconds%3600)%60,1))
|
||||
d=["{} hours {} mins {} seconds".format(a, b, c)]
|
||||
return d
|
||||
|
||||
def setMode():
|
||||
|
||||
obj = bpy.context.scene.objects[0]
|
||||
bpy.context.view_layer.objects.active = obj
|
||||
obj.select_set(True)
|
||||
|
||||
bpy.ops.object.mode_set(mode='OBJECT')
|
||||
|
||||
#TODO Make some checks that returns to previous selection
|
||||
|
||||
def setGui(mode):
|
||||
|
||||
if mode == 0:
|
||||
|
||||
context = bpy.context
|
||||
driver = bpy.app.driver_namespace
|
||||
|
||||
if "TLM_UI" in driver:
|
||||
driver["TLM_UI"].remove_handle()
|
||||
|
||||
if mode == 1:
|
||||
|
||||
#bpy.context.area.tag_redraw()
|
||||
context = bpy.context
|
||||
driver = bpy.app.driver_namespace
|
||||
driver["TLM_UI"] = Viewport.ViewportDraw(context, "Building Lightmaps")
|
||||
|
||||
bpy.ops.wm.redraw_timer(type='DRAW_WIN_SWAP', iterations=1)
|
||||
|
||||
def checkAtlasSize():
|
||||
|
||||
overflow = False
|
||||
|
@ -897,7 +1077,7 @@ def checkAtlasSize():
|
|||
utilized = 0
|
||||
atlasUsedArea = 0
|
||||
|
||||
for obj in bpy.data.objects:
|
||||
for obj in bpy.context.scene.objects:
|
||||
if obj.TLM_ObjectProperties.tlm_mesh_lightmap_use:
|
||||
if obj.TLM_ObjectProperties.tlm_postpack_object:
|
||||
if obj.TLM_ObjectProperties.tlm_postatlas_pointer == atlas.name:
|
||||
|
@ -912,4 +1092,5 @@ def checkAtlasSize():
|
|||
if overflow == True:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
return False
|
||||
|
||||
|
|
|
@ -2,7 +2,6 @@ import bpy
|
|||
|
||||
#Todo - Check if already exists, in case multiple objects has the same material
|
||||
|
||||
|
||||
def backup_material_copy(slot):
|
||||
material = slot.material
|
||||
dup = material.copy()
|
||||
|
@ -16,25 +15,49 @@ def backup_material_cache_restore(slot, path):
|
|||
if bpy.context.scene.TLM_SceneProperties.tlm_verbose:
|
||||
print("Restore cache")
|
||||
|
||||
def backup_material_rename(obj):
|
||||
if "TLM_PrevMatArray" in obj:
|
||||
if bpy.context.scene.TLM_SceneProperties.tlm_verbose:
|
||||
print("Renaming material for: " + obj.name)
|
||||
# def backup_material_restore(obj): #??
|
||||
# if bpy.context.scene.TLM_SceneProperties.tlm_verbose:
|
||||
# print("Restoring material for: " + obj.name)
|
||||
|
||||
for slot in obj.material_slots:
|
||||
#Check if object has TLM_PrevMatArray
|
||||
# if yes
|
||||
# - check if array.len is bigger than 0:
|
||||
# if yes:
|
||||
# for slot in object:
|
||||
# originalMaterial = TLM_PrevMatArray[index]
|
||||
#
|
||||
#
|
||||
# if no:
|
||||
# - In which cases are these?
|
||||
|
||||
if slot.material is not None:
|
||||
if slot.material.name.endswith("_Original"):
|
||||
newname = slot.material.name[1:-9]
|
||||
if newname in bpy.data.materials:
|
||||
if bpy.context.scene.TLM_SceneProperties.tlm_verbose:
|
||||
print("Removing material: " + bpy.data.materials[newname].name)
|
||||
bpy.data.materials.remove(bpy.data.materials[newname])
|
||||
slot.material.name = newname
|
||||
# if no:
|
||||
# - In which cases are there not?
|
||||
# - If a lightmapped material was applied to a non-lightmap object?
|
||||
|
||||
del obj["TLM_PrevMatArray"]
|
||||
|
||||
def backup_material_restore(obj):
|
||||
# if bpy.data.materials[originalMaterial].users > 0: #TODO - Check if all lightmapped
|
||||
|
||||
# print("Material has multiple users")
|
||||
|
||||
# if originalMaterial in bpy.data.materials:
|
||||
# slot.material = bpy.data.materials[originalMaterial]
|
||||
# slot.material.use_fake_user = False
|
||||
# elif "." + originalMaterial + "_Original" in bpy.data.materials:
|
||||
# slot.material = bpy.data.materials["." + originalMaterial + "_Original"]
|
||||
# slot.material.use_fake_user = False
|
||||
|
||||
# else:
|
||||
|
||||
# print("Material has one user")
|
||||
|
||||
# if "." + originalMaterial + "_Original" in bpy.data.materials:
|
||||
# slot.material = bpy.data.materials["." + originalMaterial + "_Original"]
|
||||
# slot.material.use_fake_user = False
|
||||
# elif originalMaterial in bpy.data.materials:
|
||||
# slot.material = bpy.data.materials[originalMaterial]
|
||||
# slot.material.use_fake_user = False
|
||||
|
||||
def backup_material_restore(obj): #??
|
||||
if bpy.context.scene.TLM_SceneProperties.tlm_verbose:
|
||||
print("Restoring material for: " + obj.name)
|
||||
|
||||
|
@ -59,9 +82,43 @@ def backup_material_restore(obj):
|
|||
originalMaterial = ""
|
||||
|
||||
if slot.material is not None:
|
||||
#slot.material.user_clear() Seems to be bad; See: https://developer.blender.org/T49837
|
||||
bpy.data.materials.remove(slot.material)
|
||||
#if slot.material.users < 2:
|
||||
#slot.material.user_clear() #Seems to be bad; See: https://developer.blender.org/T49837
|
||||
#bpy.data.materials.remove(slot.material)
|
||||
|
||||
if "." + originalMaterial + "_Original" in bpy.data.materials:
|
||||
slot.material = bpy.data.materials["." + originalMaterial + "_Original"]
|
||||
slot.material.use_fake_user = False
|
||||
slot.material.use_fake_user = False
|
||||
|
||||
else:
|
||||
|
||||
print("No previous material for " + obj.name)
|
||||
|
||||
else:
|
||||
|
||||
print("No previous material for " + obj.name)
|
||||
|
||||
def backup_material_rename(obj): #??
|
||||
if bpy.context.scene.TLM_SceneProperties.tlm_verbose:
|
||||
print("Renaming material for: " + obj.name)
|
||||
|
||||
|
||||
if "TLM_PrevMatArray" in obj:
|
||||
|
||||
for slot in obj.material_slots:
|
||||
|
||||
if slot.material is not None:
|
||||
if slot.material.name.endswith("_Original"):
|
||||
newname = slot.material.name[1:-9]
|
||||
if newname in bpy.data.materials:
|
||||
if bpy.context.scene.TLM_SceneProperties.tlm_verbose:
|
||||
print("Removing material: " + bpy.data.materials[newname].name)
|
||||
#if bpy.data.materials[newname].users < 2:
|
||||
#bpy.data.materials.remove(bpy.data.materials[newname]) #TODO - Maybe remove this
|
||||
slot.material.name = newname
|
||||
|
||||
del obj["TLM_PrevMatArray"]
|
||||
|
||||
else:
|
||||
|
||||
print("No Previous material array for: " + obj.name)
|
|
@ -1,25 +1,26 @@
|
|||
import bpy, os
|
||||
from .. import build
|
||||
from time import time, sleep
|
||||
|
||||
def bake():
|
||||
def bake(plus_pass=0):
|
||||
|
||||
for obj in bpy.data.objects:
|
||||
for obj in bpy.context.scene.objects:
|
||||
bpy.ops.object.select_all(action='DESELECT')
|
||||
obj.select_set(False)
|
||||
|
||||
iterNum = 0
|
||||
currentIterNum = 0
|
||||
|
||||
for obj in bpy.data.objects:
|
||||
if obj.type == "MESH":
|
||||
for obj in bpy.context.scene.objects:
|
||||
if obj.type == 'MESH' and obj.name in bpy.context.view_layer.objects:
|
||||
if obj.TLM_ObjectProperties.tlm_mesh_lightmap_use:
|
||||
iterNum = iterNum + 1
|
||||
|
||||
if iterNum > 1:
|
||||
iterNum = iterNum - 1
|
||||
|
||||
for obj in bpy.data.objects:
|
||||
if obj.type == 'MESH':
|
||||
|
||||
for obj in bpy.context.scene.objects:
|
||||
if obj.type == 'MESH' and obj.name in bpy.context.view_layer.objects:
|
||||
if obj.TLM_ObjectProperties.tlm_mesh_lightmap_use:
|
||||
|
||||
scene = bpy.context.scene
|
||||
|
@ -32,19 +33,45 @@ def bake():
|
|||
obj.hide_render = False
|
||||
scene.render.bake.use_clear = False
|
||||
|
||||
if bpy.context.scene.TLM_SceneProperties.tlm_verbose:
|
||||
print("Baking " + str(currentIterNum) + "/" + str(iterNum) + " (" + str(round(currentIterNum/iterNum*100, 2)) + "%) : " + obj.name)
|
||||
#os.system("cls")
|
||||
|
||||
if scene.TLM_EngineProperties.tlm_lighting_mode == "combined" or scene.TLM_EngineProperties.tlm_lighting_mode == "combinedAO":
|
||||
#if bpy.context.scene.TLM_SceneProperties.tlm_verbose:
|
||||
print("Baking " + str(currentIterNum) + "/" + str(iterNum) + " (" + str(round(currentIterNum/iterNum*100, 2)) + "%) : " + obj.name)
|
||||
#elapsed = build.sec_to_hours((time() - bpy.app.driver_namespace["tlm_start_time"]))
|
||||
#print("Baked: " + str(currentIterNum) + " | Left: " + str(iterNum-currentIterNum))
|
||||
elapsedSeconds = time() - bpy.app.driver_namespace["tlm_start_time"]
|
||||
bakedObjects = currentIterNum
|
||||
bakedLeft = iterNum-currentIterNum
|
||||
if bakedObjects == 0:
|
||||
bakedObjects = 1
|
||||
averagePrBake = elapsedSeconds / bakedObjects
|
||||
remaining = averagePrBake * bakedLeft
|
||||
#print(time() - bpy.app.driver_namespace["tlm_start_time"])
|
||||
print("Elapsed time: " + str(round(elapsedSeconds, 2)) + "s | ETA remaining: " + str(round(remaining, 2)) + "s") #str(elapsed[0])
|
||||
#print("Averaged: " + str(averagePrBake))
|
||||
#print("Remaining: " + str(remaining))
|
||||
|
||||
if scene.TLM_EngineProperties.tlm_target == "vertex":
|
||||
scene.render.bake_target = "VERTEX_COLORS"
|
||||
|
||||
if scene.TLM_EngineProperties.tlm_lighting_mode == "combined":
|
||||
bpy.ops.object.bake(type="DIFFUSE", pass_filter={"DIRECT","INDIRECT"}, margin=scene.TLM_EngineProperties.tlm_dilation_margin, use_clear=False)
|
||||
elif scene.TLM_EngineProperties.tlm_lighting_mode == "indirect" or scene.TLM_EngineProperties.tlm_lighting_mode == "indirectAO":
|
||||
elif scene.TLM_EngineProperties.tlm_lighting_mode == "indirect":
|
||||
bpy.ops.object.bake(type="DIFFUSE", pass_filter={"INDIRECT"}, margin=scene.TLM_EngineProperties.tlm_dilation_margin, use_clear=False)
|
||||
elif scene.TLM_EngineProperties.tlm_lighting_mode == "ao":
|
||||
bpy.ops.object.bake(type="AO", margin=scene.TLM_EngineProperties.tlm_dilation_margin, use_clear=False)
|
||||
elif scene.TLM_EngineProperties.tlm_lighting_mode == "combinedao":
|
||||
|
||||
if bpy.app.driver_namespace["tlm_plus_mode"] == 1:
|
||||
bpy.ops.object.bake(type="DIFFUSE", pass_filter={"DIRECT","INDIRECT"}, margin=scene.TLM_EngineProperties.tlm_dilation_margin, use_clear=False)
|
||||
elif bpy.app.driver_namespace["tlm_plus_mode"] == 2:
|
||||
bpy.ops.object.bake(type="AO", margin=scene.TLM_EngineProperties.tlm_dilation_margin, use_clear=False)
|
||||
|
||||
elif scene.TLM_EngineProperties.tlm_lighting_mode == "complete":
|
||||
bpy.ops.object.bake(type="COMBINED", margin=scene.TLM_EngineProperties.tlm_dilation_margin, use_clear=False)
|
||||
bpy.ops.object.bake(type="COMBINED", margin=scene.TLM_EngineProperties.tlm_dilation_margin, use_clear=False)
|
||||
else:
|
||||
bpy.ops.object.bake(type="DIFFUSE", pass_filter={"DIRECT","INDIRECT"}, margin=scene.TLM_EngineProperties.tlm_dilation_margin, use_clear=False)
|
||||
|
||||
|
||||
bpy.ops.object.select_all(action='DESELECT')
|
||||
currentIterNum = currentIterNum + 1
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import bpy, os
|
||||
|
||||
def apply_lightmaps():
|
||||
for obj in bpy.data.objects:
|
||||
if obj.type == "MESH":
|
||||
for obj in bpy.context.scene.objects:
|
||||
if obj.type == 'MESH' and obj.name in bpy.context.view_layer.objects:
|
||||
if obj.TLM_ObjectProperties.tlm_mesh_lightmap_use:
|
||||
for slot in obj.material_slots:
|
||||
mat = slot.material
|
||||
|
@ -32,8 +32,12 @@ def apply_lightmaps():
|
|||
|
||||
|
||||
def apply_materials():
|
||||
for obj in bpy.data.objects:
|
||||
if obj.type == "MESH":
|
||||
|
||||
if bpy.context.scene.TLM_SceneProperties.tlm_verbose:
|
||||
print("Applying materials")
|
||||
|
||||
for obj in bpy.context.scene.objects:
|
||||
if obj.type == 'MESH' and obj.name in bpy.context.view_layer.objects:
|
||||
if obj.TLM_ObjectProperties.tlm_mesh_lightmap_use:
|
||||
|
||||
uv_layers = obj.data.uv_layers
|
||||
|
@ -90,7 +94,7 @@ def apply_materials():
|
|||
for node in nodes:
|
||||
if node.name == "Baked Image":
|
||||
lightmapNode = node
|
||||
lightmapNode.location = -800, 300
|
||||
lightmapNode.location = -1200, 300
|
||||
lightmapNode.name = "TLM_Lightmap"
|
||||
foundBakedNode = True
|
||||
|
||||
|
@ -98,9 +102,10 @@ def apply_materials():
|
|||
|
||||
if not foundBakedNode:
|
||||
lightmapNode = node_tree.nodes.new(type="ShaderNodeTexImage")
|
||||
lightmapNode.location = -300, 300
|
||||
lightmapNode.location = -1200, 300
|
||||
lightmapNode.name = "TLM_Lightmap"
|
||||
lightmapNode.interpolation = "Smart"
|
||||
lightmapNode.interpolation = bpy.context.scene.TLM_SceneProperties.tlm_texture_interpolation
|
||||
lightmapNode.extension = bpy.context.scene.TLM_SceneProperties.tlm_texture_extrapolation
|
||||
|
||||
if (obj.TLM_ObjectProperties.tlm_mesh_lightmap_unwrap_mode == "AtlasGroupA" and obj.TLM_ObjectProperties.tlm_atlas_pointer != ""):
|
||||
lightmapNode.image = bpy.data.images[obj.TLM_ObjectProperties.tlm_atlas_pointer + "_baked"]
|
||||
|
@ -118,37 +123,32 @@ def apply_materials():
|
|||
#Find mainnode
|
||||
mainNode = outputNode.inputs[0].links[0].from_node
|
||||
|
||||
#Clamp metallic
|
||||
|
||||
# if scene.TLM_SceneProperties.tlm_metallic_clamp != "ignore":
|
||||
# if mainNode.type == "BSDF_PRINCIPLED":
|
||||
|
||||
# if len(mainNode.inputs[4].links) == 0:
|
||||
|
||||
# if scene.TLM_SceneProperties.tlm_metallic_clamp == "zero":
|
||||
# mainNode.inputs[4].default_value = 0.0
|
||||
# else:
|
||||
# mainNode.inputs[4].default_value = 0.99
|
||||
|
||||
# else:
|
||||
|
||||
# pass
|
||||
|
||||
#Add all nodes first
|
||||
#Add lightmap multipliction texture
|
||||
mixNode = node_tree.nodes.new(type="ShaderNodeMixRGB")
|
||||
mixNode.name = "Lightmap_Multiplication"
|
||||
mixNode.location = -300, 300
|
||||
mixNode.location = -800, 300
|
||||
if scene.TLM_EngineProperties.tlm_lighting_mode == "indirect" or scene.TLM_EngineProperties.tlm_lighting_mode == "indirectAO":
|
||||
mixNode.blend_type = 'ADD'
|
||||
else:
|
||||
mixNode.blend_type = 'MULTIPLY'
|
||||
mixNode.inputs[0].default_value = 1.0
|
||||
|
||||
if scene.TLM_EngineProperties.tlm_lighting_mode == "complete":
|
||||
mixNode.inputs[0].default_value = 0.0
|
||||
else:
|
||||
mixNode.inputs[0].default_value = 1.0
|
||||
|
||||
UVLightmap = node_tree.nodes.new(type="ShaderNodeUVMap")
|
||||
UVLightmap.uv_map = "UVMap_Lightmap"
|
||||
|
||||
if not obj.TLM_ObjectProperties.tlm_use_default_channel:
|
||||
uv_channel = obj.TLM_ObjectProperties.tlm_uv_channel
|
||||
else:
|
||||
uv_channel = "UVMap_Lightmap"
|
||||
|
||||
UVLightmap.uv_map = uv_channel
|
||||
|
||||
UVLightmap.name = "Lightmap_UV"
|
||||
UVLightmap.location = -1000, 300
|
||||
UVLightmap.location = -1500, 300
|
||||
|
||||
if(scene.TLM_SceneProperties.tlm_decoder_setup):
|
||||
if scene.TLM_SceneProperties.tlm_encoding_device == "CPU":
|
||||
|
@ -196,7 +196,7 @@ def apply_materials():
|
|||
baseColorValue = mainNode.inputs[0].default_value
|
||||
baseColorNode = node_tree.nodes.new(type="ShaderNodeRGB")
|
||||
baseColorNode.outputs[0].default_value = baseColorValue
|
||||
baseColorNode.location = ((mainNode.location[0] - 500, mainNode.location[1] - 300))
|
||||
baseColorNode.location = ((mainNode.location[0] - 1100, mainNode.location[1] - 300))
|
||||
baseColorNode.name = "Lightmap_BasecolorNode_A"
|
||||
else:
|
||||
baseColorNode = mainNode.inputs[0].links[0].from_node
|
||||
|
@ -235,13 +235,19 @@ def apply_materials():
|
|||
mat.node_tree.links.new(mixNode.outputs[0], mainNode.inputs[0]) #Connect mixnode to pbr node
|
||||
mat.node_tree.links.new(UVLightmap.outputs[0], lightmapNode.inputs[0]) #Connect uvnode to lightmapnode
|
||||
|
||||
#If skip metallic
|
||||
if scene.TLM_SceneProperties.tlm_metallic_clamp == "skip":
|
||||
if mainNode.inputs[4].default_value > 0.1: #DELIMITER
|
||||
moutput = mainNode.inputs[0].links[0].from_node
|
||||
mat.node_tree.links.remove(moutput.outputs[0].links[0])
|
||||
|
||||
def exchangeLightmapsToPostfix(ext_postfix, new_postfix, formatHDR=".hdr"):
|
||||
|
||||
if bpy.context.scene.TLM_SceneProperties.tlm_verbose:
|
||||
print(ext_postfix, new_postfix, formatHDR)
|
||||
|
||||
for obj in bpy.data.objects:
|
||||
if obj.type == "MESH":
|
||||
for obj in bpy.context.scene.objects:
|
||||
if obj.type == 'MESH' and obj.name in bpy.context.view_layer.objects:
|
||||
if obj.TLM_ObjectProperties.tlm_mesh_lightmap_use:
|
||||
for slot in obj.material_slots:
|
||||
mat = slot.material
|
||||
|
@ -267,6 +273,53 @@ def exchangeLightmapsToPostfix(ext_postfix, new_postfix, formatHDR=".hdr"):
|
|||
for image in bpy.data.images:
|
||||
image.reload()
|
||||
|
||||
def applyAOPass():
|
||||
|
||||
for obj in bpy.context.scene.objects:
|
||||
if obj.type == 'MESH' and obj.name in bpy.context.view_layer.objects:
|
||||
if obj.TLM_ObjectProperties.tlm_mesh_lightmap_use:
|
||||
for slot in obj.material_slots:
|
||||
mat = slot.material
|
||||
node_tree = mat.node_tree
|
||||
nodes = mat.node_tree.nodes
|
||||
|
||||
for node in nodes:
|
||||
if node.name == "Baked Image" or node.name == "TLM_Lightmap":
|
||||
|
||||
filepath = bpy.data.filepath
|
||||
dirpath = os.path.join(os.path.dirname(bpy.data.filepath), bpy.context.scene.TLM_EngineProperties.tlm_lightmap_savedir)
|
||||
|
||||
LightmapPath = node.image.filepath_raw
|
||||
|
||||
filebase = os.path.basename(LightmapPath)
|
||||
filename = os.path.splitext(filebase)[0]
|
||||
extension = os.path.splitext(filebase)[1]
|
||||
AOImagefile = filename[:-4] + "_ao"
|
||||
AOImagePath = os.path.join(dirpath, AOImagefile + extension)
|
||||
|
||||
AOMap = nodes.new('ShaderNodeTexImage')
|
||||
AOMap.name = "TLM_AOMap"
|
||||
AOImage = bpy.data.images.load(AOImagePath)
|
||||
AOMap.image = AOImage
|
||||
AOMap.location = -800, 0
|
||||
|
||||
AOMult = nodes.new(type="ShaderNodeMixRGB")
|
||||
AOMult.name = "TLM_AOMult"
|
||||
AOMult.blend_type = 'MULTIPLY'
|
||||
AOMult.inputs[0].default_value = 1.0
|
||||
AOMult.location = -300, 300
|
||||
|
||||
multyNode = nodes["Lightmap_Multiplication"]
|
||||
mainNode = nodes["Principled BSDF"]
|
||||
UVMapNode = nodes["Lightmap_UV"]
|
||||
|
||||
node_tree.links.remove(multyNode.outputs[0].links[0])
|
||||
|
||||
node_tree.links.new(multyNode.outputs[0], AOMult.inputs[1])
|
||||
node_tree.links.new(AOMap.outputs[0], AOMult.inputs[2])
|
||||
node_tree.links.new(AOMult.outputs[0], mainNode.inputs[0])
|
||||
node_tree.links.new(UVMapNode.outputs[0], AOMap.inputs[0])
|
||||
|
||||
def load_library(asset_name):
|
||||
|
||||
scriptDir = os.path.dirname(os.path.realpath(__file__))
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import bpy
|
||||
import bpy, math
|
||||
|
||||
from . import cache
|
||||
from .. utility import *
|
||||
|
@ -31,13 +31,16 @@ def configure_lights():
|
|||
|
||||
def configure_meshes(self):
|
||||
|
||||
for obj in bpy.data.objects:
|
||||
if obj.type == "MESH":
|
||||
if bpy.context.scene.TLM_SceneProperties.tlm_verbose:
|
||||
print("Configuring meshes")
|
||||
|
||||
for obj in bpy.context.scene.objects:
|
||||
if obj.type == 'MESH' and obj.name in bpy.context.view_layer.objects:
|
||||
if obj.TLM_ObjectProperties.tlm_mesh_lightmap_use:
|
||||
cache.backup_material_restore(obj)
|
||||
|
||||
for obj in bpy.data.objects:
|
||||
if obj.type == "MESH":
|
||||
for obj in bpy.context.scene.objects:
|
||||
if obj.type == 'MESH' and obj.name in bpy.context.view_layer.objects:
|
||||
if obj.TLM_ObjectProperties.tlm_mesh_lightmap_use:
|
||||
cache.backup_material_rename(obj)
|
||||
|
||||
|
@ -59,9 +62,18 @@ def configure_meshes(self):
|
|||
|
||||
scene = bpy.context.scene
|
||||
|
||||
for obj in bpy.data.objects:
|
||||
if obj.type == "MESH":
|
||||
for obj in bpy.context.scene.objects:
|
||||
if obj.type == 'MESH' and obj.name in bpy.context.view_layer.objects:
|
||||
if obj.TLM_ObjectProperties.tlm_mesh_lightmap_use:
|
||||
|
||||
if scene.TLM_SceneProperties.tlm_apply_on_unwrap:
|
||||
if bpy.context.scene.TLM_SceneProperties.tlm_verbose:
|
||||
print("Applying transform to: " + obj.name)
|
||||
bpy.context.view_layer.objects.active = obj
|
||||
obj.select_set(True)
|
||||
bpy.ops.object.transform_apply(location=False, rotation=True, scale=True)
|
||||
|
||||
obj.hide_select = False #Remember to toggle this back
|
||||
for slot in obj.material_slots:
|
||||
if "." + slot.name + '_Original' in bpy.data.materials:
|
||||
if bpy.context.scene.TLM_SceneProperties.tlm_verbose:
|
||||
|
@ -77,33 +89,35 @@ def configure_meshes(self):
|
|||
|
||||
bpy.ops.object.select_all(action='DESELECT')
|
||||
|
||||
for obj in bpy.data.objects:
|
||||
for obj in bpy.context.scene.objects:
|
||||
|
||||
if obj.TLM_ObjectProperties.tlm_mesh_lightmap_unwrap_mode == "AtlasGroupA":
|
||||
|
||||
uv_layers = obj.data.uv_layers
|
||||
if not "UVMap_Lightmap" in uv_layers:
|
||||
|
||||
if not obj.TLM_ObjectProperties.tlm_use_default_channel:
|
||||
uv_channel = obj.TLM_ObjectProperties.tlm_uv_channel
|
||||
else:
|
||||
uv_channel = "UVMap_Lightmap"
|
||||
|
||||
if not uv_channel in uv_layers:
|
||||
if bpy.context.scene.TLM_SceneProperties.tlm_verbose:
|
||||
print("UVMap made A")
|
||||
uvmap = uv_layers.new(name="UVMap_Lightmap")
|
||||
print("UV map created for object: " + obj.name)
|
||||
uvmap = uv_layers.new(name=uv_channel)
|
||||
uv_layers.active_index = len(uv_layers) - 1
|
||||
else:
|
||||
print("Existing found...skipping")
|
||||
print("Existing UV map found for object: " + obj.name)
|
||||
for i in range(0, len(uv_layers)):
|
||||
if uv_layers[i].name == 'UVMap_Lightmap':
|
||||
uv_layers.active_index = i
|
||||
if bpy.context.scene.TLM_SceneProperties.tlm_verbose:
|
||||
print("Lightmap shift A")
|
||||
break
|
||||
|
||||
atlas_items.append(obj)
|
||||
obj.select_set(True)
|
||||
|
||||
if scene.TLM_SceneProperties.tlm_apply_on_unwrap:
|
||||
bpy.ops.object.transform_apply(location=True, rotation=True, scale=True)
|
||||
|
||||
if atlasgroup.tlm_atlas_lightmap_unwrap_mode == "SmartProject":
|
||||
if bpy.context.scene.TLM_SceneProperties.tlm_verbose:
|
||||
print("Smart Project A for: " + str(atlas_items))
|
||||
print("Atlasgroup Smart Project for: " + str(atlas_items))
|
||||
for obj in atlas_items:
|
||||
print(obj.name + ": Active UV: " + obj.data.uv_layers[obj.data.uv_layers.active_index].name)
|
||||
|
||||
|
@ -112,7 +126,12 @@ def configure_meshes(self):
|
|||
|
||||
bpy.ops.object.mode_set(mode='EDIT')
|
||||
bpy.ops.mesh.select_all(action='SELECT')
|
||||
bpy.ops.uv.smart_project(angle_limit=45.0, island_margin=atlasgroup.tlm_atlas_unwrap_margin, user_area_weight=1.0, use_aspect=True, stretch_to_bounds=False)
|
||||
#API changes in 2.91 causes errors:
|
||||
if (2, 91, 0) > bpy.app.version:
|
||||
bpy.ops.uv.smart_project(angle_limit=45.0, island_margin=obj.TLM_ObjectProperties.tlm_mesh_unwrap_margin, user_area_weight=1.0, use_aspect=True, stretch_to_bounds=False)
|
||||
else:
|
||||
angle = math.radians(45.0)
|
||||
bpy.ops.uv.smart_project(angle_limit=angle, island_margin=obj.TLM_ObjectProperties.tlm_mesh_unwrap_margin, area_weight=1.0, correct_aspect=True, scale_to_bounds=False)
|
||||
bpy.ops.mesh.select_all(action='DESELECT')
|
||||
bpy.ops.object.mode_set(mode='OBJECT')
|
||||
elif atlasgroup.tlm_atlas_lightmap_unwrap_mode == "Lightmap":
|
||||
|
@ -123,8 +142,9 @@ def configure_meshes(self):
|
|||
bpy.ops.object.mode_set(mode='OBJECT')
|
||||
|
||||
elif atlasgroup.tlm_atlas_lightmap_unwrap_mode == "Xatlas":
|
||||
|
||||
if bpy.context.scene.TLM_SceneProperties.tlm_verbose:
|
||||
print("Temporary skip: COPYING SMART PROJECT")
|
||||
print("Using Xatlas on Atlas Group: " + atlas)
|
||||
|
||||
for obj in atlas_items:
|
||||
obj.select_set(True)
|
||||
|
@ -139,176 +159,213 @@ def configure_meshes(self):
|
|||
|
||||
else:
|
||||
if bpy.context.scene.TLM_SceneProperties.tlm_verbose:
|
||||
print("Copied Existing A")
|
||||
print("Copied Existing UV Map for Atlas Group: " + atlas)
|
||||
|
||||
for obj in bpy.data.objects:
|
||||
if obj.type == "MESH":
|
||||
for obj in bpy.context.scene.objects:
|
||||
if obj.type == 'MESH' and obj.name in bpy.context.view_layer.objects:
|
||||
if obj.TLM_ObjectProperties.tlm_mesh_lightmap_use:
|
||||
iterNum = iterNum + 1
|
||||
|
||||
for obj in bpy.data.objects:
|
||||
if obj.type == "MESH":
|
||||
if obj.TLM_ObjectProperties.tlm_mesh_lightmap_use:
|
||||
for obj in bpy.context.scene.objects:
|
||||
if obj.name in bpy.context.view_layer.objects: #Possible fix for view layer error
|
||||
if obj.type == 'MESH' and obj.name in bpy.context.view_layer.objects:
|
||||
if obj.TLM_ObjectProperties.tlm_mesh_lightmap_use:
|
||||
|
||||
objWasHidden = False
|
||||
objWasHidden = False
|
||||
|
||||
#For some reason, a Blender bug might prevent invisible objects from being smart projected
|
||||
#We will turn the object temporarily visible
|
||||
obj.hide_viewport = False
|
||||
obj.hide_set(False)
|
||||
#For some reason, a Blender bug might prevent invisible objects from being smart projected
|
||||
#We will turn the object temporarily visible
|
||||
obj.hide_viewport = False
|
||||
obj.hide_set(False)
|
||||
|
||||
currentIterNum = currentIterNum + 1
|
||||
currentIterNum = currentIterNum + 1
|
||||
|
||||
#Configure selection
|
||||
bpy.ops.object.select_all(action='DESELECT')
|
||||
bpy.context.view_layer.objects.active = obj
|
||||
obj.select_set(True)
|
||||
obs = bpy.context.view_layer.objects
|
||||
active = obs.active
|
||||
#Configure selection
|
||||
bpy.ops.object.select_all(action='DESELECT')
|
||||
bpy.context.view_layer.objects.active = obj
|
||||
obj.select_set(True)
|
||||
|
||||
#Provide material if none exists
|
||||
preprocess_material(obj, scene)
|
||||
obs = bpy.context.view_layer.objects
|
||||
active = obs.active
|
||||
|
||||
#UV Layer management here
|
||||
if not obj.TLM_ObjectProperties.tlm_mesh_lightmap_unwrap_mode == "AtlasGroupA":
|
||||
uv_layers = obj.data.uv_layers
|
||||
if not "UVMap_Lightmap" in uv_layers:
|
||||
if bpy.context.scene.TLM_SceneProperties.tlm_verbose:
|
||||
print("UVMap made B")
|
||||
uvmap = uv_layers.new(name="UVMap_Lightmap")
|
||||
uv_layers.active_index = len(uv_layers) - 1
|
||||
#Provide material if none exists
|
||||
preprocess_material(obj, scene)
|
||||
|
||||
#If lightmap
|
||||
if obj.TLM_ObjectProperties.tlm_mesh_lightmap_unwrap_mode == "Lightmap":
|
||||
if scene.TLM_SceneProperties.tlm_apply_on_unwrap:
|
||||
bpy.ops.object.transform_apply(location=True, rotation=True, scale=True)
|
||||
bpy.ops.uv.lightmap_pack('EXEC_SCREEN', PREF_CONTEXT='ALL_FACES', PREF_MARGIN_DIV=obj.TLM_ObjectProperties.tlm_mesh_unwrap_margin)
|
||||
|
||||
#If smart project
|
||||
elif obj.TLM_ObjectProperties.tlm_mesh_lightmap_unwrap_mode == "SmartProject":
|
||||
#UV Layer management here
|
||||
if not obj.TLM_ObjectProperties.tlm_mesh_lightmap_unwrap_mode == "AtlasGroupA":
|
||||
uv_layers = obj.data.uv_layers
|
||||
|
||||
if not obj.TLM_ObjectProperties.tlm_use_default_channel:
|
||||
uv_channel = obj.TLM_ObjectProperties.tlm_uv_channel
|
||||
else:
|
||||
uv_channel = "UVMap_Lightmap"
|
||||
|
||||
if not uv_channel in uv_layers:
|
||||
if bpy.context.scene.TLM_SceneProperties.tlm_verbose:
|
||||
print("Smart Project B")
|
||||
if scene.TLM_SceneProperties.tlm_apply_on_unwrap:
|
||||
bpy.ops.object.transform_apply(location=True, rotation=True, scale=True)
|
||||
bpy.ops.object.select_all(action='DESELECT')
|
||||
obj.select_set(True)
|
||||
bpy.ops.object.mode_set(mode='EDIT')
|
||||
bpy.ops.mesh.select_all(action='DESELECT')
|
||||
bpy.ops.object.mode_set(mode='OBJECT')
|
||||
bpy.ops.uv.smart_project(angle_limit=45.0, island_margin=obj.TLM_ObjectProperties.tlm_mesh_unwrap_margin, user_area_weight=1.0, use_aspect=True, stretch_to_bounds=False)
|
||||
|
||||
elif obj.TLM_ObjectProperties.tlm_mesh_lightmap_unwrap_mode == "Xatlas":
|
||||
print("UV map created for obj: " + obj.name)
|
||||
uvmap = uv_layers.new(name=uv_channel)
|
||||
uv_layers.active_index = len(uv_layers) - 1
|
||||
|
||||
if scene.TLM_SceneProperties.tlm_apply_on_unwrap:
|
||||
bpy.ops.object.transform_apply(location=True, rotation=True, scale=True)
|
||||
#If lightmap
|
||||
if obj.TLM_ObjectProperties.tlm_mesh_lightmap_unwrap_mode == "Lightmap":
|
||||
bpy.ops.uv.lightmap_pack('EXEC_SCREEN', PREF_CONTEXT='ALL_FACES', PREF_MARGIN_DIV=obj.TLM_ObjectProperties.tlm_mesh_unwrap_margin)
|
||||
|
||||
#import blender_xatlas
|
||||
#blender_xatlas.Unwrap_Lightmap_Group_Xatlas_2(bpy.context)
|
||||
#If smart project
|
||||
elif obj.TLM_ObjectProperties.tlm_mesh_lightmap_unwrap_mode == "SmartProject":
|
||||
|
||||
#bpy.ops.object.setup_unwrap()
|
||||
Unwrap_Lightmap_Group_Xatlas_2_headless_call(obj)
|
||||
|
||||
elif obj.TLM_ObjectProperties.tlm_mesh_lightmap_unwrap_mode == "AtlasGroupA":
|
||||
|
||||
if bpy.context.scene.TLM_SceneProperties.tlm_verbose:
|
||||
print("ATLAS GROUP: " + obj.TLM_ObjectProperties.tlm_atlas_pointer)
|
||||
|
||||
else: #if copy existing
|
||||
|
||||
if bpy.context.scene.TLM_SceneProperties.tlm_verbose:
|
||||
print("Copied Existing B")
|
||||
|
||||
else:
|
||||
if bpy.context.scene.TLM_SceneProperties.tlm_verbose:
|
||||
print("Existing found...skipping")
|
||||
for i in range(0, len(uv_layers)):
|
||||
if uv_layers[i].name == 'UVMap_Lightmap':
|
||||
uv_layers.active_index = i
|
||||
if bpy.context.scene.TLM_SceneProperties.tlm_verbose:
|
||||
print("Lightmap shift B")
|
||||
break
|
||||
print("Smart Project B")
|
||||
bpy.ops.object.select_all(action='DESELECT')
|
||||
obj.select_set(True)
|
||||
bpy.ops.object.mode_set(mode='EDIT')
|
||||
bpy.ops.mesh.select_all(action='SELECT')
|
||||
#API changes in 2.91 causes errors:
|
||||
if (2, 91, 0) > bpy.app.version:
|
||||
bpy.ops.uv.smart_project(angle_limit=45.0, island_margin=obj.TLM_ObjectProperties.tlm_mesh_unwrap_margin, user_area_weight=1.0, use_aspect=True, stretch_to_bounds=False)
|
||||
else:
|
||||
angle = math.radians(45.0)
|
||||
bpy.ops.uv.smart_project(angle_limit=angle, island_margin=obj.TLM_ObjectProperties.tlm_mesh_unwrap_margin, area_weight=1.0, correct_aspect=True, scale_to_bounds=False)
|
||||
bpy.ops.mesh.select_all(action='DESELECT')
|
||||
bpy.ops.object.mode_set(mode='OBJECT')
|
||||
|
||||
elif obj.TLM_ObjectProperties.tlm_mesh_lightmap_unwrap_mode == "Xatlas":
|
||||
|
||||
Unwrap_Lightmap_Group_Xatlas_2_headless_call(obj)
|
||||
|
||||
#Sort out nodes
|
||||
for slot in obj.material_slots:
|
||||
elif obj.TLM_ObjectProperties.tlm_mesh_lightmap_unwrap_mode == "AtlasGroupA":
|
||||
|
||||
nodetree = slot.material.node_tree
|
||||
if bpy.context.scene.TLM_SceneProperties.tlm_verbose:
|
||||
print("ATLAS GROUP: " + obj.TLM_ObjectProperties.tlm_atlas_pointer)
|
||||
|
||||
else: #if copy existing
|
||||
|
||||
outputNode = nodetree.nodes[0] #Presumed to be material output node
|
||||
if bpy.context.scene.TLM_SceneProperties.tlm_verbose:
|
||||
print("Copied Existing UV Map for object: " + obj.name)
|
||||
|
||||
if(outputNode.type != "OUTPUT_MATERIAL"):
|
||||
else:
|
||||
if bpy.context.scene.TLM_SceneProperties.tlm_verbose:
|
||||
print("Existing UV map found for obj: " + obj.name)
|
||||
for i in range(0, len(uv_layers)):
|
||||
if uv_layers[i].name == uv_channel:
|
||||
uv_layers.active_index = i
|
||||
break
|
||||
|
||||
#print(x)
|
||||
|
||||
#Sort out nodes
|
||||
for slot in obj.material_slots:
|
||||
|
||||
nodetree = slot.material.node_tree
|
||||
|
||||
outputNode = nodetree.nodes[0] #Presumed to be material output node
|
||||
|
||||
if(outputNode.type != "OUTPUT_MATERIAL"):
|
||||
for node in nodetree.nodes:
|
||||
if node.type == "OUTPUT_MATERIAL":
|
||||
outputNode = node
|
||||
break
|
||||
|
||||
mainNode = outputNode.inputs[0].links[0].from_node
|
||||
|
||||
if mainNode.type not in ['BSDF_PRINCIPLED','BSDF_DIFFUSE','GROUP']:
|
||||
|
||||
#TODO! FIND THE PRINCIPLED PBR
|
||||
self.report({'INFO'}, "The primary material node is not supported. Seeking first principled.")
|
||||
|
||||
if len(find_node_by_type(nodetree.nodes, Node_Types.pbr_node)) > 0:
|
||||
mainNode = find_node_by_type(nodetree.nodes, Node_Types.pbr_node)[0]
|
||||
else:
|
||||
self.report({'INFO'}, "No principled found. Seeking diffuse")
|
||||
if len(find_node_by_type(nodetree.nodes, Node_Types.diffuse)) > 0:
|
||||
mainNode = find_node_by_type(nodetree.nodes, Node_Types.diffuse)[0]
|
||||
else:
|
||||
self.report({'INFO'}, "No supported nodes. Continuing anyway.")
|
||||
|
||||
if mainNode.type == 'GROUP':
|
||||
if mainNode.node_tree != "Armory PBR":
|
||||
if bpy.context.scene.TLM_SceneProperties.tlm_verbose:
|
||||
print("The material group is not supported!")
|
||||
|
||||
if (mainNode.type == "BSDF_PRINCIPLED"):
|
||||
if bpy.context.scene.TLM_SceneProperties.tlm_verbose:
|
||||
print("BSDF_Principled")
|
||||
if scene.TLM_EngineProperties.tlm_directional_mode == "None":
|
||||
if bpy.context.scene.TLM_SceneProperties.tlm_verbose:
|
||||
print("Directional mode")
|
||||
if not len(mainNode.inputs[19].links) == 0:
|
||||
if bpy.context.scene.TLM_SceneProperties.tlm_verbose:
|
||||
print("NOT LEN 0")
|
||||
ninput = mainNode.inputs[19].links[0]
|
||||
noutput = mainNode.inputs[19].links[0].from_node
|
||||
nodetree.links.remove(noutput.outputs[0].links[0])
|
||||
|
||||
#Clamp metallic
|
||||
if bpy.context.scene.TLM_SceneProperties.tlm_metallic_clamp == "limit":
|
||||
MainMetNodeSocket = mainNode.inputs[4]
|
||||
if not len(MainMetNodeSocket.links) == 0:
|
||||
nodes = nodetree.nodes
|
||||
MetClampNode = nodes.new('ShaderNodeClamp')
|
||||
MetClampNode.location = (-200,150)
|
||||
MetClampNode.inputs[2].default_value = 0.9
|
||||
minput = mainNode.inputs[4].links[0] #Metal input socket
|
||||
moutput = mainNode.inputs[4].links[0].from_node #Metal output node
|
||||
nodetree.links.remove(moutput.outputs[0].links[0]) #Works
|
||||
nodetree.links.new(moutput.outputs[0], MetClampNode.inputs[0]) #minput node to clamp node
|
||||
nodetree.links.new(MetClampNode.outputs[0],MainMetNodeSocket) #clamp node to metinput
|
||||
else:
|
||||
if mainNode.inputs[4].default_value > 0.9:
|
||||
mainNode.inputs[4].default_value = 0.9
|
||||
elif bpy.context.scene.TLM_SceneProperties.tlm_metallic_clamp == "zero":
|
||||
MainMetNodeSocket = mainNode.inputs[4]
|
||||
if not len(MainMetNodeSocket.links) == 0:
|
||||
nodes = nodetree.nodes
|
||||
MetClampNode = nodes.new('ShaderNodeClamp')
|
||||
MetClampNode.location = (-200,150)
|
||||
MetClampNode.inputs[2].default_value = 0.0
|
||||
minput = mainNode.inputs[4].links[0] #Metal input socket
|
||||
moutput = mainNode.inputs[4].links[0].from_node #Metal output node
|
||||
nodetree.links.remove(moutput.outputs[0].links[0]) #Works
|
||||
nodetree.links.new(moutput.outputs[0], MetClampNode.inputs[0]) #minput node to clamp node
|
||||
nodetree.links.new(MetClampNode.outputs[0],MainMetNodeSocket) #clamp node to metinput
|
||||
else:
|
||||
mainNode.inputs[4].default_value = 0.0
|
||||
|
||||
if (mainNode.type == "BSDF_DIFFUSE"):
|
||||
if bpy.context.scene.TLM_SceneProperties.tlm_verbose:
|
||||
print("BSDF_Diffuse")
|
||||
|
||||
# if (mainNode.type == "BSDF_DIFFUSE"):
|
||||
# if bpy.context.scene.TLM_SceneProperties.tlm_verbose:
|
||||
# print("BSDF_Diffuse")
|
||||
|
||||
#TODO FIX THIS PART!
|
||||
#THIS IS USED IN CASES WHERE FOR SOME REASON THE USER FORGETS TO CONNECT SOMETHING INTO THE OUTPUT MATERIAL
|
||||
for slot in obj.material_slots:
|
||||
|
||||
nodetree = bpy.data.materials[slot.name].node_tree
|
||||
nodes = nodetree.nodes
|
||||
|
||||
#First search to get the first output material type
|
||||
for node in nodetree.nodes:
|
||||
if node.type == "OUTPUT_MATERIAL":
|
||||
outputNode = node
|
||||
mainNode = node
|
||||
break
|
||||
|
||||
mainNode = outputNode.inputs[0].links[0].from_node
|
||||
#Fallback to get search
|
||||
if not mainNode.type == "OUTPUT_MATERIAL":
|
||||
mainNode = nodetree.nodes.get("Material Output")
|
||||
|
||||
if mainNode.type not in ['BSDF_PRINCIPLED','BSDF_DIFFUSE','GROUP']:
|
||||
#Last resort to first node in list
|
||||
if not mainNode.type == "OUTPUT_MATERIAL":
|
||||
mainNode = nodetree.nodes[0].inputs[0].links[0].from_node
|
||||
|
||||
#TODO! FIND THE PRINCIPLED PBR
|
||||
self.report({'INFO'}, "The primary material node is not supported. Seeking first principled.")
|
||||
# for node in nodes:
|
||||
# if "LM" in node.name:
|
||||
# nodetree.links.new(node.outputs[0], mainNode.inputs[0])
|
||||
|
||||
if len(find_node_by_type(nodetree.nodes, Node_Types.pbr_node)) > 0:
|
||||
mainNode = find_node_by_type(nodetree.nodes, Node_Types.pbr_node)[0]
|
||||
else:
|
||||
self.report({'INFO'}, "No principled found. Seeking diffuse")
|
||||
if len(find_node_by_type(nodetree.nodes, Node_Types.diffuse)) > 0:
|
||||
mainNode = find_node_by_type(nodetree.nodes, Node_Types.diffuse)[0]
|
||||
else:
|
||||
self.report({'INFO'}, "No supported nodes. Continuing anyway.")
|
||||
|
||||
if mainNode.type == 'GROUP':
|
||||
if mainNode.node_tree != "Armory PBR":
|
||||
if bpy.context.scene.TLM_SceneProperties.tlm_verbose:
|
||||
print("The material group is not supported!")
|
||||
|
||||
if (mainNode.type == "BSDF_PRINCIPLED"):
|
||||
if bpy.context.scene.TLM_SceneProperties.tlm_verbose:
|
||||
print("BSDF_Principled")
|
||||
if scene.TLM_EngineProperties.tlm_directional_mode == "None":
|
||||
if bpy.context.scene.TLM_SceneProperties.tlm_verbose:
|
||||
print("Directional mode")
|
||||
if not len(mainNode.inputs[19].links) == 0:
|
||||
if bpy.context.scene.TLM_SceneProperties.tlm_verbose:
|
||||
print("NOT LEN 0")
|
||||
ninput = mainNode.inputs[19].links[0]
|
||||
noutput = mainNode.inputs[19].links[0].from_node
|
||||
nodetree.links.remove(noutput.outputs[0].links[0])
|
||||
|
||||
#Clamp metallic
|
||||
if(mainNode.inputs[4].default_value == 1):
|
||||
mainNode.inputs[4].default_value = 0.0
|
||||
|
||||
if (mainNode.type == "BSDF_DIFFUSE"):
|
||||
if bpy.context.scene.TLM_SceneProperties.tlm_verbose:
|
||||
print("BSDF_Diffuse")
|
||||
|
||||
for slot in obj.material_slots:
|
||||
|
||||
nodetree = bpy.data.materials[slot.name].node_tree
|
||||
nodes = nodetree.nodes
|
||||
|
||||
#First search to get the first output material type
|
||||
for node in nodetree.nodes:
|
||||
if node.type == "OUTPUT_MATERIAL":
|
||||
mainNode = node
|
||||
break
|
||||
|
||||
#Fallback to get search
|
||||
if not mainNode.type == "OUTPUT_MATERIAL":
|
||||
mainNode = nodetree.nodes.get("Material Output")
|
||||
|
||||
#Last resort to first node in list
|
||||
if not mainNode.type == "OUTPUT_MATERIAL":
|
||||
mainNode = nodetree.nodes[0].inputs[0].links[0].from_node
|
||||
|
||||
for node in nodes:
|
||||
if "LM" in node.name:
|
||||
nodetree.links.new(node.outputs[0], mainNode.inputs[0])
|
||||
|
||||
for node in nodes:
|
||||
if "Lightmap" in node.name:
|
||||
nodes.remove(node)
|
||||
# for node in nodes:
|
||||
# if "Lightmap" in node.name:
|
||||
# nodes.remove(node)
|
||||
|
||||
def preprocess_material(obj, scene):
|
||||
if len(obj.material_slots) == 0:
|
||||
|
@ -537,7 +594,7 @@ def store_existing(prev_container):
|
|||
|
||||
selected = []
|
||||
|
||||
for obj in bpy.data.objects:
|
||||
for obj in bpy.context.scene.objects:
|
||||
if obj.select_get():
|
||||
selected.append(obj.name)
|
||||
|
||||
|
|
|
@ -22,7 +22,7 @@ class TLM_Integrated_Denoise:
|
|||
bpy.ops.object.camera_add()
|
||||
|
||||
#Just select the first camera we find, needed for the compositor
|
||||
for obj in bpy.data.objects:
|
||||
for obj in bpy.context.scene.objects:
|
||||
if obj.type == "CAMERA":
|
||||
bpy.context.scene.camera = obj
|
||||
return
|
||||
|
|
|
@ -332,6 +332,205 @@ def encodeImageRGBDGPU(image, maxRange, outDir, quality):
|
|||
#Todo - Find a way to save
|
||||
#bpy.ops.image.save_all_modified()
|
||||
|
||||
#TODO - FINISH THIS
|
||||
def encodeImageRGBMGPU(image, maxRange, outDir, quality):
|
||||
input_image = bpy.data.images[image.name]
|
||||
image_name = input_image.name
|
||||
|
||||
offscreen = gpu.types.GPUOffScreen(input_image.size[0], input_image.size[1])
|
||||
|
||||
image = input_image
|
||||
|
||||
vertex_shader = '''
|
||||
|
||||
uniform mat4 ModelViewProjectionMatrix;
|
||||
|
||||
in vec2 texCoord;
|
||||
in vec2 pos;
|
||||
out vec2 texCoord_interp;
|
||||
|
||||
void main()
|
||||
{
|
||||
//gl_Position = ModelViewProjectionMatrix * vec4(pos.xy, 0.0f, 1.0f);
|
||||
//gl_Position.z = 1.0;
|
||||
gl_Position = vec4(pos.xy, 100, 100);
|
||||
texCoord_interp = texCoord;
|
||||
}
|
||||
|
||||
'''
|
||||
fragment_shader = '''
|
||||
in vec2 texCoord_interp;
|
||||
out vec4 fragColor;
|
||||
|
||||
uniform sampler2D image;
|
||||
|
||||
//Code from here: https://github.com/BabylonJS/Babylon.js/blob/master/src/Shaders/ShadersInclude/helperFunctions.fx
|
||||
|
||||
const float PI = 3.1415926535897932384626433832795;
|
||||
const float HALF_MIN = 5.96046448e-08; // Smallest positive half.
|
||||
|
||||
const float LinearEncodePowerApprox = 2.2;
|
||||
const float GammaEncodePowerApprox = 1.0 / LinearEncodePowerApprox;
|
||||
const vec3 LuminanceEncodeApprox = vec3(0.2126, 0.7152, 0.0722);
|
||||
|
||||
const float Epsilon = 0.0000001;
|
||||
#define saturate(x) clamp(x, 0.0, 1.0)
|
||||
|
||||
float maxEps(float x) {
|
||||
return max(x, Epsilon);
|
||||
}
|
||||
|
||||
float toLinearSpace(float color)
|
||||
{
|
||||
return pow(color, LinearEncodePowerApprox);
|
||||
}
|
||||
|
||||
vec3 toLinearSpace(vec3 color)
|
||||
{
|
||||
return pow(color, vec3(LinearEncodePowerApprox));
|
||||
}
|
||||
|
||||
vec4 toLinearSpace(vec4 color)
|
||||
{
|
||||
return vec4(pow(color.rgb, vec3(LinearEncodePowerApprox)), color.a);
|
||||
}
|
||||
|
||||
vec3 toGammaSpace(vec3 color)
|
||||
{
|
||||
return pow(color, vec3(GammaEncodePowerApprox));
|
||||
}
|
||||
|
||||
vec4 toGammaSpace(vec4 color)
|
||||
{
|
||||
return vec4(pow(color.rgb, vec3(GammaEncodePowerApprox)), color.a);
|
||||
}
|
||||
|
||||
float toGammaSpace(float color)
|
||||
{
|
||||
return pow(color, GammaEncodePowerApprox);
|
||||
}
|
||||
|
||||
float square(float value)
|
||||
{
|
||||
return value * value;
|
||||
}
|
||||
|
||||
// Check if configurable value is needed.
|
||||
const float rgbdMaxRange = 255.0;
|
||||
|
||||
vec4 toRGBM(vec3 color) {
|
||||
|
||||
vec4 rgbm;
|
||||
color *= 1.0/6.0;
|
||||
rgbm.a = saturate( max( max( color.r, color.g ), max( color.b, 1e-6 ) ) );
|
||||
rgbm.a = clamp(floor(D) / 255.0, 0., 1.);
|
||||
rgbm.rgb = color / rgbm.a;
|
||||
|
||||
return
|
||||
|
||||
float maxRGB = maxEps(max(color.r, max(color.g, color.b)));
|
||||
float D = max(rgbdMaxRange / maxRGB, 1.);
|
||||
D = clamp(floor(D) / 255.0, 0., 1.);
|
||||
vec3 rgb = color.rgb * D;
|
||||
|
||||
// Helps with png quantization.
|
||||
rgb = toGammaSpace(rgb);
|
||||
|
||||
return vec4(rgb, D);
|
||||
}
|
||||
|
||||
vec3 fromRGBD(vec4 rgbd) {
|
||||
// Helps with png quantization.
|
||||
rgbd.rgb = toLinearSpace(rgbd.rgb);
|
||||
|
||||
// return rgbd.rgb * ((rgbdMaxRange / 255.0) / rgbd.a);
|
||||
|
||||
return rgbd.rgb / rgbd.a;
|
||||
}
|
||||
|
||||
void main()
|
||||
{
|
||||
|
||||
fragColor = toRGBM(texture(image, texCoord_interp).rgb);
|
||||
|
||||
}
|
||||
|
||||
'''
|
||||
|
||||
x_screen = 0
|
||||
off_x = -100
|
||||
off_y = -100
|
||||
y_screen_flip = 0
|
||||
sx = 200
|
||||
sy = 200
|
||||
|
||||
vertices = (
|
||||
(x_screen + off_x, y_screen_flip - off_y),
|
||||
(x_screen + off_x, y_screen_flip - sy - off_y),
|
||||
(x_screen + off_x + sx, y_screen_flip - sy - off_y),
|
||||
(x_screen + off_x + sx, y_screen_flip - off_x))
|
||||
|
||||
if input_image.colorspace_settings.name != 'Linear':
|
||||
input_image.colorspace_settings.name = 'Linear'
|
||||
|
||||
# Removing .exr or .hdr prefix
|
||||
if image_name[-4:] == '.exr' or image_name[-4:] == '.hdr':
|
||||
image_name = image_name[:-4]
|
||||
|
||||
target_image = bpy.data.images.get(image_name + '_encoded')
|
||||
if bpy.context.scene.TLM_SceneProperties.tlm_verbose:
|
||||
print(image_name + '_encoded')
|
||||
if not target_image:
|
||||
target_image = bpy.data.images.new(
|
||||
name = image_name + '_encoded',
|
||||
width = input_image.size[0],
|
||||
height = input_image.size[1],
|
||||
alpha = True,
|
||||
float_buffer = False
|
||||
)
|
||||
|
||||
shader = gpu.types.GPUShader(vertex_shader, fragment_shader)
|
||||
batch = batch_for_shader(
|
||||
shader, 'TRI_FAN',
|
||||
{
|
||||
"pos": vertices,
|
||||
"texCoord": ((0, 1), (0, 0), (1, 0), (1, 1)),
|
||||
},
|
||||
)
|
||||
|
||||
if image.gl_load():
|
||||
raise Exception()
|
||||
|
||||
with offscreen.bind():
|
||||
bgl.glActiveTexture(bgl.GL_TEXTURE0)
|
||||
bgl.glBindTexture(bgl.GL_TEXTURE_2D, image.bindcode)
|
||||
|
||||
shader.bind()
|
||||
shader.uniform_int("image", 0)
|
||||
batch.draw(shader)
|
||||
|
||||
buffer = bgl.Buffer(bgl.GL_BYTE, input_image.size[0] * input_image.size[1] * 4)
|
||||
bgl.glReadBuffer(bgl.GL_BACK)
|
||||
bgl.glReadPixels(0, 0, input_image.size[0], input_image.size[1], bgl.GL_RGBA, bgl.GL_UNSIGNED_BYTE, buffer)
|
||||
|
||||
offscreen.free()
|
||||
|
||||
target_image.pixels = [v / 255 for v in buffer]
|
||||
input_image = target_image
|
||||
|
||||
#Save LogLuv
|
||||
if bpy.context.scene.TLM_SceneProperties.tlm_verbose:
|
||||
print(input_image.name)
|
||||
input_image.filepath_raw = outDir + "/" + input_image.name + ".png"
|
||||
#input_image.filepath_raw = outDir + "_encoded.png"
|
||||
input_image.file_format = "PNG"
|
||||
bpy.context.scene.render.image_settings.quality = quality
|
||||
#input_image.save_render(filepath = input_image.filepath_raw, scene = bpy.context.scene)
|
||||
input_image.save()
|
||||
|
||||
#Todo - Find a way to save
|
||||
#bpy.ops.image.save_all_modified()
|
||||
|
||||
def encodeImageRGBMCPU(image, maxRange, outDir, quality):
|
||||
input_image = bpy.data.images[image.name]
|
||||
image_name = input_image.name
|
||||
|
@ -431,21 +630,6 @@ def encodeImageRGBDCPU(image, maxRange, outDir, quality):
|
|||
result_pixel[i+1] = math.pow(result_pixel[i+1] * D, 1/2.2)
|
||||
result_pixel[i+2] = math.pow(result_pixel[i+2] * D, 1/2.2)
|
||||
result_pixel[i+3] = D
|
||||
|
||||
|
||||
# for i in range(0,num_pixels,4):
|
||||
|
||||
# m = saturate(max(result_pixel[i], result_pixel[i+1], result_pixel[i+2], 1e-6))
|
||||
# d = max(maxRange / m, 1)
|
||||
# #d = saturate(math.floor(d) / 255.0)
|
||||
# d = np.clip((math.floor(d) / 255.0), 0.0, 1.0)
|
||||
|
||||
# #TODO TO GAMMA SPACE
|
||||
|
||||
# result_pixel[i] = math.pow(result_pixel[i] * d * 255 / maxRange, 1/2.2)
|
||||
# result_pixel[i+1] = math.pow(result_pixel[i+1] * d * 255 / maxRange, 1/2.2)
|
||||
# result_pixel[i+2] = math.pow(result_pixel[i+2] * d * 255 / maxRange, 1/2.2)
|
||||
# result_pixel[i+3] = d
|
||||
|
||||
target_image.pixels = result_pixel
|
||||
|
||||
|
@ -457,25 +641,4 @@ def encodeImageRGBDCPU(image, maxRange, outDir, quality):
|
|||
input_image.filepath_raw = outDir + "/" + input_image.name + ".png"
|
||||
input_image.file_format = "PNG"
|
||||
bpy.context.scene.render.image_settings.quality = quality
|
||||
input_image.save()
|
||||
|
||||
# const float rgbdMaxRange = 255.0;
|
||||
|
||||
# vec4 toRGBD(vec3 color) {
|
||||
# float maxRGB = maxEps(max(color.r, max(color.g, color.b)));
|
||||
# float D = max(rgbdMaxRange / maxRGB, 1.);
|
||||
# D = clamp(floor(D) / 255.0, 0., 1.);
|
||||
# vec3 rgb = color.rgb * D;
|
||||
|
||||
# // Helps with png quantization.
|
||||
# rgb = toGammaSpace(rgb);
|
||||
|
||||
# return vec4(rgb, D);
|
||||
# }
|
||||
|
||||
# const float Epsilon = 0.0000001;
|
||||
# #define saturate(x) clamp(x, 0.0, 1.0)
|
||||
|
||||
# float maxEps(float x) {
|
||||
# return max(x, Epsilon);
|
||||
# }
|
||||
input_image.save()
|
|
@ -62,7 +62,7 @@ class TLM_CV_Filtering:
|
|||
|
||||
#SEAM TESTING# #####################
|
||||
|
||||
if obj_name in bpy.data.objects:
|
||||
if obj_name in bpy.context.scene.objects:
|
||||
override = bpy.data.objects[obj_name].TLM_ObjectProperties.tlm_mesh_filter_override
|
||||
elif obj_name in scene.TLM_AtlasList:
|
||||
override = False
|
||||
|
|
67
blender/arm/lightmapper/utility/gui/Viewport.py
Normal file
67
blender/arm/lightmapper/utility/gui/Viewport.py
Normal file
|
@ -0,0 +1,67 @@
|
|||
import bpy, blf, bgl, os, gpu
|
||||
from gpu_extras.batch import batch_for_shader
|
||||
|
||||
class ViewportDraw:
|
||||
|
||||
def __init__(self, context, text):
|
||||
|
||||
bakefile = "TLM_Overlay.png"
|
||||
scriptDir = os.path.dirname(os.path.realpath(__file__))
|
||||
bakefile_path = os.path.abspath(os.path.join(scriptDir, '..', '..', 'assets/' + bakefile))
|
||||
|
||||
image_name = "TLM_Overlay.png"
|
||||
|
||||
bpy.ops.image.open(filepath=bakefile_path)
|
||||
|
||||
print("Self path: " + bakefile_path)
|
||||
|
||||
image = bpy.data.images[image_name]
|
||||
|
||||
x = 15
|
||||
y = 15
|
||||
w = 400
|
||||
h = 200
|
||||
|
||||
self.shader = gpu.shader.from_builtin('2D_IMAGE')
|
||||
self.batch = batch_for_shader(
|
||||
self.shader, 'TRI_FAN',
|
||||
{
|
||||
"pos": ((x, y), (x+w, y), (x+w, y+h), (x, y+h)),
|
||||
"texCoord": ((0, 0), (1, 0), (1, 1), (0, 1)),
|
||||
},
|
||||
)
|
||||
|
||||
if image.gl_load():
|
||||
raise Exception()
|
||||
|
||||
self.text = text
|
||||
self.image = image
|
||||
#self.handle = bpy.types.SpaceView3D.draw_handler_add(self.draw_text_callback, (context,), 'WINDOW', 'POST_PIXEL')
|
||||
self.handle2 = bpy.types.SpaceView3D.draw_handler_add(self.draw_image_callback, (context,), 'WINDOW', 'POST_PIXEL')
|
||||
|
||||
def draw_text_callback(self, context):
|
||||
|
||||
font_id = 0
|
||||
blf.position(font_id, 15, 15, 0)
|
||||
blf.size(font_id, 20, 72)
|
||||
blf.draw(font_id, "%s" % (self.text))
|
||||
|
||||
def draw_image_callback(self, context):
|
||||
|
||||
if self.image:
|
||||
bgl.glEnable(bgl.GL_BLEND)
|
||||
bgl.glActiveTexture(bgl.GL_TEXTURE0)
|
||||
bgl.glBindTexture(bgl.GL_TEXTURE_2D, self.image.bindcode)
|
||||
|
||||
self.shader.bind()
|
||||
self.shader.uniform_int("image", 0)
|
||||
self.batch.draw(self.shader)
|
||||
bgl.glDisable(bgl.GL_BLEND)
|
||||
|
||||
def update_text(self, text):
|
||||
|
||||
self.text = text
|
||||
|
||||
def remove_handle(self):
|
||||
#bpy.types.SpaceView3D.draw_handler_remove(self.handle, 'WINDOW')
|
||||
bpy.types.SpaceView3D.draw_handler_remove(self.handle2, 'WINDOW')
|
259
blender/arm/lightmapper/utility/luxcore/setup.py
Normal file
259
blender/arm/lightmapper/utility/luxcore/setup.py
Normal file
|
@ -0,0 +1,259 @@
|
|||
import bpy
|
||||
|
||||
from .. utility import *
|
||||
|
||||
def init(self, prev_container):
|
||||
|
||||
#TODO - JSON classes
|
||||
export.scene = """scene.camera.cliphither = 0.1
|
||||
scene.camera.clipyon = 100
|
||||
scene.camera.shutteropen = 0
|
||||
scene.camera.shutterclose = 1
|
||||
scene.camera.autovolume.enable = 1
|
||||
scene.camera.lookat.orig = 7.358891 -6.925791 4.958309
|
||||
scene.camera.lookat.target = 6.707333 -6.31162 4.513038
|
||||
scene.camera.up = -0.3240135 0.3054208 0.8953956
|
||||
scene.camera.screenwindow = -1 1 -0.5625 0.5625
|
||||
scene.camera.lensradius = 0
|
||||
scene.camera.focaldistance = 10
|
||||
scene.camera.autofocus.enable = 0
|
||||
scene.camera.type = "perspective"
|
||||
scene.camera.oculusrift.barrelpostpro.enable = 0
|
||||
scene.camera.fieldofview = 39.59776
|
||||
scene.camera.bokeh.blades = 0
|
||||
scene.camera.bokeh.power = 3
|
||||
scene.camera.bokeh.distribution.type = "NONE"
|
||||
scene.camera.bokeh.scale.x = 0.7071068
|
||||
scene.camera.bokeh.scale.y = 0.7071068
|
||||
scene.lights.__WORLD_BACKGROUND_LIGHT__.gain = 2e-05 2e-05 2e-05
|
||||
scene.lights.__WORLD_BACKGROUND_LIGHT__.transformation = 1 0 0 0 0 1 0 0 0 0 1 0 0 0 0 1
|
||||
scene.lights.__WORLD_BACKGROUND_LIGHT__.id = 0
|
||||
scene.lights.__WORLD_BACKGROUND_LIGHT__.temperature = -1
|
||||
scene.lights.__WORLD_BACKGROUND_LIGHT__.temperature.normalize = 0
|
||||
scene.lights.__WORLD_BACKGROUND_LIGHT__.visibility.indirect.diffuse.enable = 1
|
||||
scene.lights.__WORLD_BACKGROUND_LIGHT__.visibility.indirect.glossy.enable = 1
|
||||
scene.lights.__WORLD_BACKGROUND_LIGHT__.visibility.indirect.specular.enable = 1
|
||||
scene.lights.__WORLD_BACKGROUND_LIGHT__.type = "sky2"
|
||||
scene.lights.__WORLD_BACKGROUND_LIGHT__.dir = 0 0 1
|
||||
scene.lights.__WORLD_BACKGROUND_LIGHT__.turbidity = 2.2
|
||||
scene.lights.__WORLD_BACKGROUND_LIGHT__.groundalbedo = 0.5 0.5 0.5
|
||||
scene.lights.__WORLD_BACKGROUND_LIGHT__.ground.enable = 0
|
||||
scene.lights.__WORLD_BACKGROUND_LIGHT__.ground.color = 0.5 0.5 0.5
|
||||
scene.lights.__WORLD_BACKGROUND_LIGHT__.ground.autoscale = 1
|
||||
scene.lights.__WORLD_BACKGROUND_LIGHT__.distribution.width = 512
|
||||
scene.lights.__WORLD_BACKGROUND_LIGHT__.distribution.height = 256
|
||||
scene.lights.__WORLD_BACKGROUND_LIGHT__.visibilitymapcache.enable = 0
|
||||
scene.lights.2382361116072.gain = 1 1 1
|
||||
scene.lights.2382361116072.transformation = -0.2908646 0.9551712 -0.05518906 0 -0.7711008 -0.1998834 0.6045247 0 0.5663932 0.2183912 0.7946723 0 4.076245 1.005454 5.903862 1
|
||||
scene.lights.2382361116072.id = 0
|
||||
scene.lights.2382361116072.temperature = -1
|
||||
scene.lights.2382361116072.temperature.normalize = 0
|
||||
scene.lights.2382361116072.type = "sphere"
|
||||
scene.lights.2382361116072.color = 1 1 1
|
||||
scene.lights.2382361116072.power = 0
|
||||
scene.lights.2382361116072.normalizebycolor = 0
|
||||
scene.lights.2382361116072.efficency = 0
|
||||
scene.lights.2382361116072.position = 0 0 0
|
||||
scene.lights.2382361116072.radius = 0.1
|
||||
scene.materials.Material2382357175256.type = "disney"
|
||||
scene.materials.Material2382357175256.basecolor = "0.7 0.7 0.7"
|
||||
scene.materials.Material2382357175256.subsurface = "0"
|
||||
scene.materials.Material2382357175256.roughness = "0.2"
|
||||
scene.materials.Material2382357175256.metallic = "0"
|
||||
scene.materials.Material2382357175256.specular = "0.5"
|
||||
scene.materials.Material2382357175256.speculartint = "0"
|
||||
scene.materials.Material2382357175256.clearcoat = "0"
|
||||
scene.materials.Material2382357175256.clearcoatgloss = "1"
|
||||
scene.materials.Material2382357175256.anisotropic = "0"
|
||||
scene.materials.Material2382357175256.sheen = "0"
|
||||
scene.materials.Material2382357175256.sheentint = "0"
|
||||
scene.materials.Material2382357175256.transparency.shadow = 0 0 0
|
||||
scene.materials.Material2382357175256.id = 3364224
|
||||
scene.materials.Material2382357175256.emission.gain = 1 1 1
|
||||
scene.materials.Material2382357175256.emission.power = 0
|
||||
scene.materials.Material2382357175256.emission.normalizebycolor = 1
|
||||
scene.materials.Material2382357175256.emission.efficency = 0
|
||||
scene.materials.Material2382357175256.emission.theta = 90
|
||||
scene.materials.Material2382357175256.emission.id = 0
|
||||
scene.materials.Material2382357175256.emission.importance = 1
|
||||
scene.materials.Material2382357175256.emission.temperature = -1
|
||||
scene.materials.Material2382357175256.emission.temperature.normalize = 0
|
||||
scene.materials.Material2382357175256.emission.directlightsampling.type = "AUTO"
|
||||
scene.materials.Material2382357175256.visibility.indirect.diffuse.enable = 1
|
||||
scene.materials.Material2382357175256.visibility.indirect.glossy.enable = 1
|
||||
scene.materials.Material2382357175256.visibility.indirect.specular.enable = 1
|
||||
scene.materials.Material2382357175256.shadowcatcher.enable = 0
|
||||
scene.materials.Material2382357175256.shadowcatcher.onlyinfinitelights = 0
|
||||
scene.materials.Material2382357175256.photongi.enable = 1
|
||||
scene.materials.Material2382357175256.holdout.enable = 0
|
||||
scene.materials.Material__0012382357172440.type = "disney"
|
||||
scene.materials.Material__0012382357172440.basecolor = "0.7 0.7 0.7"
|
||||
scene.materials.Material__0012382357172440.subsurface = "0"
|
||||
scene.materials.Material__0012382357172440.roughness = "0.2"
|
||||
scene.materials.Material__0012382357172440.metallic = "0"
|
||||
scene.materials.Material__0012382357172440.specular = "0.5"
|
||||
scene.materials.Material__0012382357172440.speculartint = "0"
|
||||
scene.materials.Material__0012382357172440.clearcoat = "0"
|
||||
scene.materials.Material__0012382357172440.clearcoatgloss = "1"
|
||||
scene.materials.Material__0012382357172440.anisotropic = "0"
|
||||
scene.materials.Material__0012382357172440.sheen = "0"
|
||||
scene.materials.Material__0012382357172440.sheentint = "0"
|
||||
scene.materials.Material__0012382357172440.transparency.shadow = 0 0 0
|
||||
scene.materials.Material__0012382357172440.id = 6728256
|
||||
scene.materials.Material__0012382357172440.emission.gain = 1 1 1
|
||||
scene.materials.Material__0012382357172440.emission.power = 0
|
||||
scene.materials.Material__0012382357172440.emission.normalizebycolor = 1
|
||||
scene.materials.Material__0012382357172440.emission.efficency = 0
|
||||
scene.materials.Material__0012382357172440.emission.theta = 90
|
||||
scene.materials.Material__0012382357172440.emission.id = 0
|
||||
scene.materials.Material__0012382357172440.emission.importance = 1
|
||||
scene.materials.Material__0012382357172440.emission.temperature = -1
|
||||
scene.materials.Material__0012382357172440.emission.temperature.normalize = 0
|
||||
scene.materials.Material__0012382357172440.emission.directlightsampling.type = "AUTO"
|
||||
scene.materials.Material__0012382357172440.visibility.indirect.diffuse.enable = 1
|
||||
scene.materials.Material__0012382357172440.visibility.indirect.glossy.enable = 1
|
||||
scene.materials.Material__0012382357172440.visibility.indirect.specular.enable = 1
|
||||
scene.materials.Material__0012382357172440.shadowcatcher.enable = 0
|
||||
scene.materials.Material__0012382357172440.shadowcatcher.onlyinfinitelights = 0
|
||||
scene.materials.Material__0012382357172440.photongi.enable = 1
|
||||
scene.materials.Material__0012382357172440.holdout.enable = 0
|
||||
scene.objects.23823611086320.material = "Material2382357175256"
|
||||
scene.objects.23823611086320.ply = "mesh-00000.ply"
|
||||
scene.objects.23823611086320.camerainvisible = 0
|
||||
scene.objects.23823611086320.id = 1326487202
|
||||
scene.objects.23823611086320.appliedtransformation = 1 0 0 0 0 1 0 0 0 0 1 0 0 0 1 1
|
||||
scene.objects.23823611279760.material = "Material__0012382357172440"
|
||||
scene.objects.23823611279760.ply = "mesh-00001.ply"
|
||||
scene.objects.23823611279760.camerainvisible = 0
|
||||
scene.objects.23823611279760.id = 3772660237
|
||||
scene.objects.23823611279760.appliedtransformation = 5 0 0 0 0 5 0 0 0 0 5 0 0 0 0 1
|
||||
"""
|
||||
|
||||
export.config = """context.verbose = 1
|
||||
accelerator.type = "AUTO"
|
||||
accelerator.instances.enable = 1
|
||||
accelerator.motionblur.enable = 1
|
||||
accelerator.bvh.builder.type = "EMBREE_BINNED_SAH"
|
||||
accelerator.bvh.treetype = 4
|
||||
accelerator.bvh.costsamples = 0
|
||||
accelerator.bvh.isectcost = 80
|
||||
accelerator.bvh.travcost = 10
|
||||
accelerator.bvh.emptybonus = 0.5
|
||||
scene.epsilon.min = "1e-05"
|
||||
scene.epsilon.max = "0.1"
|
||||
scene.file = "scene.scn"
|
||||
images.scale = 1
|
||||
lightstrategy.type = "LOG_POWER"
|
||||
native.threads.count = 8
|
||||
renderengine.type = "BAKECPU"
|
||||
path.pathdepth.total = "7"
|
||||
path.pathdepth.diffuse = "5"
|
||||
path.pathdepth.glossy = "5"
|
||||
path.pathdepth.specular = "6"
|
||||
path.hybridbackforward.enable = "0"
|
||||
path.hybridbackforward.partition = "0.8"
|
||||
path.hybridbackforward.glossinessthreshold = "0.049"
|
||||
path.russianroulette.depth = 3
|
||||
path.russianroulette.cap = 0.5
|
||||
path.clamping.variance.maxvalue = 0
|
||||
path.forceblackbackground.enable = "0"
|
||||
sampler.type = "SOBOL"
|
||||
sampler.imagesamples.enable = 1
|
||||
sampler.sobol.adaptive.strength = "0.9"
|
||||
sampler.sobol.adaptive.userimportanceweight = 0.75
|
||||
sampler.sobol.bucketsize = "16"
|
||||
sampler.sobol.tilesize = "16"
|
||||
sampler.sobol.supersampling = "1"
|
||||
sampler.sobol.overlapping = "1"
|
||||
path.photongi.sampler.type = "METROPOLIS"
|
||||
path.photongi.photon.maxcount = 100000000
|
||||
path.photongi.photon.maxdepth = 4
|
||||
path.photongi.photon.time.start = 0
|
||||
path.photongi.photon.time.end = -1
|
||||
path.photongi.visibility.lookup.radius = 0
|
||||
path.photongi.visibility.lookup.normalangle = 10
|
||||
path.photongi.visibility.targethitrate = 0.99
|
||||
path.photongi.visibility.maxsamplecount = 1048576
|
||||
path.photongi.glossinessusagethreshold = 0.05
|
||||
path.photongi.indirect.enabled = 0
|
||||
path.photongi.indirect.maxsize = 0
|
||||
path.photongi.indirect.haltthreshold = 0.05
|
||||
path.photongi.indirect.lookup.radius = 0
|
||||
path.photongi.indirect.lookup.normalangle = 10
|
||||
path.photongi.indirect.usagethresholdscale = 8
|
||||
path.photongi.indirect.filter.radiusscale = 3
|
||||
path.photongi.caustic.enabled = 0
|
||||
path.photongi.caustic.maxsize = 100000
|
||||
path.photongi.caustic.updatespp = 8
|
||||
path.photongi.caustic.updatespp.radiusreduction = 0.96
|
||||
path.photongi.caustic.updatespp.minradius = 0.003
|
||||
path.photongi.caustic.lookup.radius = 0.15
|
||||
path.photongi.caustic.lookup.normalangle = 10
|
||||
path.photongi.debug.type = "none"
|
||||
path.photongi.persistent.file = ""
|
||||
path.photongi.persistent.safesave = 1
|
||||
film.filter.type = "BLACKMANHARRIS"
|
||||
film.filter.width = 2
|
||||
opencl.platform.index = -1
|
||||
film.width = 960
|
||||
film.height = 600
|
||||
film.safesave = 1
|
||||
film.noiseestimation.step = "32"
|
||||
film.noiseestimation.warmup = "8"
|
||||
film.noiseestimation.filter.scale = 4
|
||||
batch.haltnoisethreshold = 0.01
|
||||
batch.haltnoisethreshold.step = 64
|
||||
batch.haltnoisethreshold.warmup = 64
|
||||
batch.haltnoisethreshold.filter.enable = 1
|
||||
batch.haltnoisethreshold.stoprendering.enable = 1
|
||||
batch.halttime = "0"
|
||||
batch.haltspp = 32
|
||||
film.outputs.safesave = 1
|
||||
film.outputs.0.type = "RGB_IMAGEPIPELINE"
|
||||
film.outputs.0.filename = "RGB_IMAGEPIPELINE_0.png"
|
||||
film.outputs.0.index = "0"
|
||||
film.imagepipelines.000.0.type = "NOP"
|
||||
film.imagepipelines.000.1.type = "TONEMAP_LINEAR"
|
||||
film.imagepipelines.000.1.scale = "1"
|
||||
film.imagepipelines.000.2.type = "GAMMA_CORRECTION"
|
||||
film.imagepipelines.000.2.value = "2.2"
|
||||
film.imagepipelines.000.radiancescales.0.enabled = "1"
|
||||
film.imagepipelines.000.radiancescales.0.globalscale = "1"
|
||||
film.imagepipelines.000.radiancescales.0.rgbscale = "1" "1" "1"
|
||||
periodicsave.film.outputs.period = 0
|
||||
periodicsave.film.period = 0
|
||||
periodicsave.film.filename = "film.flm"
|
||||
periodicsave.resumerendering.period = 0
|
||||
periodicsave.resumerendering.filename = "rendering.rsm"
|
||||
resumerendering.filesafe = 1
|
||||
debug.renderconfig.parse.print = 0
|
||||
debug.scene.parse.print = 0
|
||||
screen.refresh.interval = 100
|
||||
screen.tool.type = "CAMERA_EDIT"
|
||||
screen.tiles.pending.show = 1
|
||||
screen.tiles.converged.show = 0
|
||||
screen.tiles.notconverged.show = 0
|
||||
screen.tiles.passcount.show = 0
|
||||
screen.tiles.error.show = 0
|
||||
bake.minmapautosize = 64
|
||||
bake.maxmapautosize = 1024
|
||||
bake.powerof2autosize.enable = 1
|
||||
bake.skipexistingmapfiles = 1
|
||||
film.imagepipelines.1.0.type = "NOP"
|
||||
bake.maps.0.type = "COMBINED"
|
||||
bake.maps.0.filename = "23823611086320.exr"
|
||||
bake.maps.0.imagepipelineindex = 1
|
||||
bake.maps.0.width = 512
|
||||
bake.maps.0.height = 512
|
||||
bake.maps.0.autosize.enabled = 1
|
||||
bake.maps.0.uvindex = 0
|
||||
bake.maps.0.objectnames = "23823611086320"
|
||||
bake.maps.1.type = "COMBINED"
|
||||
bake.maps.1.filename = "23823611279760.exr"
|
||||
bake.maps.1.imagepipelineindex = 1
|
||||
bake.maps.1.width = 512
|
||||
bake.maps.1.height = 512
|
||||
bake.maps.1.autosize.enabled = 1
|
||||
bake.maps.1.uvindex = 0
|
||||
bake.maps.1.objectnames = "23823611279760"
|
||||
"""
|
243
blender/arm/lightmapper/utility/octane/configure.py
Normal file
243
blender/arm/lightmapper/utility/octane/configure.py
Normal file
|
@ -0,0 +1,243 @@
|
|||
import bpy, math
|
||||
|
||||
#from . import cache
|
||||
from .. utility import *
|
||||
|
||||
def init(self, prev_container):
|
||||
|
||||
#store_existing(prev_container)
|
||||
|
||||
#set_settings()
|
||||
|
||||
configure_world()
|
||||
|
||||
configure_lights()
|
||||
|
||||
configure_meshes(self)
|
||||
|
||||
def configure_world():
|
||||
pass
|
||||
|
||||
def configure_lights():
|
||||
pass
|
||||
|
||||
def configure_meshes(self):
|
||||
|
||||
for mat in bpy.data.materials:
|
||||
if mat.users < 1:
|
||||
bpy.data.materials.remove(mat)
|
||||
|
||||
for mat in bpy.data.materials:
|
||||
if mat.name.startswith("."):
|
||||
if "_Original" in mat.name:
|
||||
bpy.data.materials.remove(mat)
|
||||
|
||||
for image in bpy.data.images:
|
||||
if image.name.endswith("_baked"):
|
||||
bpy.data.images.remove(image, do_unlink=True)
|
||||
|
||||
iterNum = 1
|
||||
currentIterNum = 0
|
||||
|
||||
scene = bpy.context.scene
|
||||
|
||||
for obj in scene.objects:
|
||||
if obj.type == "MESH":
|
||||
if obj.TLM_ObjectProperties.tlm_mesh_lightmap_use:
|
||||
obj.hide_select = False #Remember to toggle this back
|
||||
|
||||
currentIterNum = currentIterNum + 1
|
||||
|
||||
obj.octane.baking_group_id = 1 + currentIterNum #0 doesn't exist, 1 is neutral and 2 is first baked object
|
||||
|
||||
print("Obj: " + obj.name + " set to baking group: " + str(obj.octane.baking_group_id))
|
||||
|
||||
for slot in obj.material_slots:
|
||||
if "." + slot.name + '_Original' in bpy.data.materials:
|
||||
if bpy.context.scene.TLM_SceneProperties.tlm_verbose:
|
||||
print("The material: " + slot.name + " shifted to " + "." + slot.name + '_Original')
|
||||
slot.material = bpy.data.materials["." + slot.name + '_Original']
|
||||
|
||||
|
||||
objWasHidden = False
|
||||
|
||||
#For some reason, a Blender bug might prevent invisible objects from being smart projected
|
||||
#We will turn the object temporarily visible
|
||||
obj.hide_viewport = False
|
||||
obj.hide_set(False)
|
||||
|
||||
#Configure selection
|
||||
bpy.ops.object.select_all(action='DESELECT')
|
||||
bpy.context.view_layer.objects.active = obj
|
||||
obj.select_set(True)
|
||||
obs = bpy.context.view_layer.objects
|
||||
active = obs.active
|
||||
|
||||
uv_layers = obj.data.uv_layers
|
||||
if not obj.TLM_ObjectProperties.tlm_use_default_channel:
|
||||
uv_channel = obj.TLM_ObjectProperties.tlm_uv_channel
|
||||
else:
|
||||
uv_channel = "UVMap_Lightmap"
|
||||
|
||||
if not uv_channel in uv_layers:
|
||||
if bpy.context.scene.TLM_SceneProperties.tlm_verbose:
|
||||
print("UV map created for obj: " + obj.name)
|
||||
uvmap = uv_layers.new(name=uv_channel)
|
||||
uv_layers.active_index = len(uv_layers) - 1
|
||||
print("Setting active UV to: " + uv_layers.active_index)
|
||||
|
||||
#If lightmap
|
||||
if obj.TLM_ObjectProperties.tlm_mesh_lightmap_unwrap_mode == "Lightmap":
|
||||
bpy.ops.uv.lightmap_pack('EXEC_SCREEN', PREF_CONTEXT='ALL_FACES', PREF_MARGIN_DIV=obj.TLM_ObjectProperties.tlm_mesh_unwrap_margin)
|
||||
|
||||
#If smart project
|
||||
elif obj.TLM_ObjectProperties.tlm_mesh_lightmap_unwrap_mode == "SmartProject":
|
||||
|
||||
if bpy.context.scene.TLM_SceneProperties.tlm_verbose:
|
||||
print("Smart Project B")
|
||||
bpy.ops.object.select_all(action='DESELECT')
|
||||
obj.select_set(True)
|
||||
bpy.ops.object.mode_set(mode='EDIT')
|
||||
bpy.ops.mesh.select_all(action='SELECT')
|
||||
#API changes in 2.91 causes errors:
|
||||
if (2, 91, 0) > bpy.app.version:
|
||||
bpy.ops.uv.smart_project(angle_limit=45.0, island_margin=obj.TLM_ObjectProperties.tlm_mesh_unwrap_margin, user_area_weight=1.0, use_aspect=True, stretch_to_bounds=False)
|
||||
else:
|
||||
angle = math.radians(45.0)
|
||||
bpy.ops.uv.smart_project(angle_limit=angle, island_margin=obj.TLM_ObjectProperties.tlm_mesh_unwrap_margin, area_weight=1.0, correct_aspect=True, scale_to_bounds=False)
|
||||
bpy.ops.mesh.select_all(action='DESELECT')
|
||||
bpy.ops.object.mode_set(mode='OBJECT')
|
||||
|
||||
elif obj.TLM_ObjectProperties.tlm_mesh_lightmap_unwrap_mode == "Xatlas":
|
||||
|
||||
Unwrap_Lightmap_Group_Xatlas_2_headless_call(obj)
|
||||
|
||||
elif obj.TLM_ObjectProperties.tlm_mesh_lightmap_unwrap_mode == "AtlasGroupA":
|
||||
|
||||
if bpy.context.scene.TLM_SceneProperties.tlm_verbose:
|
||||
print("ATLAS GROUP: " + obj.TLM_ObjectProperties.tlm_atlas_pointer)
|
||||
|
||||
else: #if copy existing
|
||||
|
||||
if bpy.context.scene.TLM_SceneProperties.tlm_verbose:
|
||||
print("Copied Existing UV Map for object: " + obj.name)
|
||||
|
||||
else:
|
||||
if bpy.context.scene.TLM_SceneProperties.tlm_verbose:
|
||||
print("Existing UV map found for obj: " + obj.name)
|
||||
for i in range(0, len(uv_layers)):
|
||||
if uv_layers[i].name == uv_channel:
|
||||
uv_layers.active_index = i
|
||||
break
|
||||
|
||||
set_camera()
|
||||
|
||||
def set_camera():
|
||||
|
||||
cam_name = "TLM-BakeCam"
|
||||
|
||||
if not cam_name in bpy.context.scene:
|
||||
camera = bpy.data.cameras.new(cam_name)
|
||||
camobj_name = "TLM-BakeCam-obj"
|
||||
cam_obj = bpy.data.objects.new(camobj_name, camera)
|
||||
bpy.context.collection.objects.link(cam_obj)
|
||||
cam_obj.location = ((0,0,0))
|
||||
|
||||
bpy.context.scene.camera = cam_obj
|
||||
|
||||
def set_settings():
|
||||
|
||||
scene = bpy.context.scene
|
||||
cycles = scene.cycles
|
||||
scene.render.engine = "CYCLES"
|
||||
sceneProperties = scene.TLM_SceneProperties
|
||||
engineProperties = scene.TLM_EngineProperties
|
||||
cycles.device = scene.TLM_EngineProperties.tlm_mode
|
||||
|
||||
if cycles.device == "GPU":
|
||||
scene.render.tile_x = 256
|
||||
scene.render.tile_y = 256
|
||||
else:
|
||||
scene.render.tile_x = 32
|
||||
scene.render.tile_y = 32
|
||||
|
||||
if engineProperties.tlm_quality == "0":
|
||||
cycles.samples = 32
|
||||
cycles.max_bounces = 1
|
||||
cycles.diffuse_bounces = 1
|
||||
cycles.glossy_bounces = 1
|
||||
cycles.transparent_max_bounces = 1
|
||||
cycles.transmission_bounces = 1
|
||||
cycles.volume_bounces = 1
|
||||
cycles.caustics_reflective = False
|
||||
cycles.caustics_refractive = False
|
||||
elif engineProperties.tlm_quality == "1":
|
||||
cycles.samples = 64
|
||||
cycles.max_bounces = 2
|
||||
cycles.diffuse_bounces = 2
|
||||
cycles.glossy_bounces = 2
|
||||
cycles.transparent_max_bounces = 2
|
||||
cycles.transmission_bounces = 2
|
||||
cycles.volume_bounces = 2
|
||||
cycles.caustics_reflective = False
|
||||
cycles.caustics_refractive = False
|
||||
elif engineProperties.tlm_quality == "2":
|
||||
cycles.samples = 512
|
||||
cycles.max_bounces = 2
|
||||
cycles.diffuse_bounces = 2
|
||||
cycles.glossy_bounces = 2
|
||||
cycles.transparent_max_bounces = 2
|
||||
cycles.transmission_bounces = 2
|
||||
cycles.volume_bounces = 2
|
||||
cycles.caustics_reflective = False
|
||||
cycles.caustics_refractive = False
|
||||
elif engineProperties.tlm_quality == "3":
|
||||
cycles.samples = 1024
|
||||
cycles.max_bounces = 256
|
||||
cycles.diffuse_bounces = 256
|
||||
cycles.glossy_bounces = 256
|
||||
cycles.transparent_max_bounces = 256
|
||||
cycles.transmission_bounces = 256
|
||||
cycles.volume_bounces = 256
|
||||
cycles.caustics_reflective = False
|
||||
cycles.caustics_refractive = False
|
||||
elif engineProperties.tlm_quality == "4":
|
||||
cycles.samples = 2048
|
||||
cycles.max_bounces = 512
|
||||
cycles.diffuse_bounces = 512
|
||||
cycles.glossy_bounces = 512
|
||||
cycles.transparent_max_bounces = 512
|
||||
cycles.transmission_bounces = 512
|
||||
cycles.volume_bounces = 512
|
||||
cycles.caustics_reflective = True
|
||||
cycles.caustics_refractive = True
|
||||
else: #Custom
|
||||
pass
|
||||
|
||||
def store_existing(prev_container):
|
||||
|
||||
scene = bpy.context.scene
|
||||
cycles = scene.cycles
|
||||
|
||||
selected = []
|
||||
|
||||
for obj in bpy.context.scene.objects:
|
||||
if obj.select_get():
|
||||
selected.append(obj.name)
|
||||
|
||||
prev_container["settings"] = [
|
||||
cycles.samples,
|
||||
cycles.max_bounces,
|
||||
cycles.diffuse_bounces,
|
||||
cycles.glossy_bounces,
|
||||
cycles.transparent_max_bounces,
|
||||
cycles.transmission_bounces,
|
||||
cycles.volume_bounces,
|
||||
cycles.caustics_reflective,
|
||||
cycles.caustics_refractive,
|
||||
cycles.device,
|
||||
scene.render.engine,
|
||||
bpy.context.view_layer.objects.active,
|
||||
selected,
|
||||
[scene.render.resolution_x, scene.render.resolution_y]
|
||||
]
|
71
blender/arm/lightmapper/utility/octane/lightmap2.py
Normal file
71
blender/arm/lightmapper/utility/octane/lightmap2.py
Normal file
|
@ -0,0 +1,71 @@
|
|||
import bpy, os
|
||||
|
||||
def bake():
|
||||
|
||||
cam_name = "TLM-BakeCam-obj"
|
||||
|
||||
if cam_name in bpy.context.scene.objects:
|
||||
|
||||
print("Camera found...")
|
||||
|
||||
camera = bpy.context.scene.objects[cam_name]
|
||||
|
||||
camera.data.octane.baking_camera = True
|
||||
|
||||
for obj in bpy.context.scene.objects:
|
||||
bpy.ops.object.select_all(action='DESELECT')
|
||||
obj.select_set(False)
|
||||
|
||||
iterNum = 2
|
||||
currentIterNum = 1
|
||||
|
||||
for obj in bpy.context.scene.objects:
|
||||
if obj.type == "MESH":
|
||||
if obj.TLM_ObjectProperties.tlm_mesh_lightmap_use:
|
||||
iterNum = iterNum + 1
|
||||
|
||||
if iterNum > 1:
|
||||
iterNum = iterNum - 1
|
||||
|
||||
for obj in bpy.context.scene.objects:
|
||||
if obj.type == 'MESH' and obj.name in bpy.context.view_layer.objects:
|
||||
if obj.TLM_ObjectProperties.tlm_mesh_lightmap_use:
|
||||
|
||||
currentIterNum = currentIterNum + 1
|
||||
|
||||
scene = bpy.context.scene
|
||||
|
||||
print("Baking obj: " + obj.name)
|
||||
|
||||
print("Baking ID: " + str(currentIterNum) + " out of " + str(iterNum))
|
||||
|
||||
bpy.ops.object.select_all(action='DESELECT')
|
||||
|
||||
camera.data.octane.baking_group_id = currentIterNum
|
||||
|
||||
savedir = os.path.dirname(bpy.data.filepath)
|
||||
user_dir = scene.TLM_Engine3Properties.tlm_lightmap_savedir
|
||||
directory = os.path.join(savedir, user_dir)
|
||||
|
||||
image_settings = bpy.context.scene.render.image_settings
|
||||
image_settings.file_format = "HDR"
|
||||
image_settings.color_depth = '32'
|
||||
|
||||
filename = os.path.join(directory, "LM") + "_" + obj.name + ".hdr"
|
||||
bpy.context.scene.render.filepath = filename
|
||||
|
||||
resolution = int(obj.TLM_ObjectProperties.tlm_mesh_lightmap_resolution)
|
||||
|
||||
bpy.context.scene.render.resolution_x = resolution
|
||||
bpy.context.scene.render.resolution_y = resolution
|
||||
|
||||
bpy.ops.render.render(write_still=True)
|
||||
|
||||
else:
|
||||
|
||||
print("No baking camera found")
|
||||
|
||||
|
||||
|
||||
|
||||
print("Baking in Octane!")
|
|
@ -106,7 +106,7 @@ def postpack():
|
|||
rect = []
|
||||
|
||||
#For each object that targets the atlas
|
||||
for obj in bpy.data.objects:
|
||||
for obj in bpy.context.scene.objects:
|
||||
if obj.TLM_ObjectProperties.tlm_mesh_lightmap_use:
|
||||
if obj.TLM_ObjectProperties.tlm_postpack_object:
|
||||
if obj.TLM_ObjectProperties.tlm_postatlas_pointer == atlas.name:
|
||||
|
@ -156,7 +156,13 @@ def postpack():
|
|||
obj = bpy.data.objects[aob]
|
||||
|
||||
for idx, layer in enumerate(obj.data.uv_layers):
|
||||
if layer.name == "UVMap_Lightmap":
|
||||
|
||||
if not obj.TLM_ObjectProperties.tlm_use_default_channel:
|
||||
uv_channel = obj.TLM_ObjectProperties.tlm_uv_channel
|
||||
else:
|
||||
uv_channel = "UVMap_Lightmap"
|
||||
|
||||
if layer.name == uv_channel:
|
||||
obj.data.uv_layers.active_index = idx
|
||||
|
||||
print("UVLayer set to: " + str(obj.data.uv_layers.active_index))
|
||||
|
@ -194,7 +200,7 @@ def postpack():
|
|||
print("Written: " + str(os.path.join(lightmap_directory, atlas.name + end + formatEnc)))
|
||||
|
||||
#Change the material for each material, slot
|
||||
for obj in bpy.data.objects:
|
||||
for obj in bpy.context.scene.objects:
|
||||
if obj.TLM_ObjectProperties.tlm_mesh_lightmap_use:
|
||||
if obj.TLM_ObjectProperties.tlm_postpack_object:
|
||||
if obj.TLM_ObjectProperties.tlm_postatlas_pointer == atlas.name:
|
||||
|
@ -219,7 +225,7 @@ def postpack():
|
|||
existing_image.user_clear()
|
||||
|
||||
#Add dilation map here...
|
||||
for obj in bpy.data.objects:
|
||||
for obj in bpy.context.scene.objects:
|
||||
if obj.TLM_ObjectProperties.tlm_mesh_lightmap_use:
|
||||
if obj.TLM_ObjectProperties.tlm_postpack_object:
|
||||
if obj.TLM_ObjectProperties.tlm_postatlas_pointer == atlas.name:
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import bpy.ops as O
|
||||
import bpy, os, re, sys, importlib, struct, platform, subprocess, threading, string, bmesh
|
||||
import bpy, os, re, sys, importlib, struct, platform, subprocess, threading, string, bmesh, shutil, glob, uuid
|
||||
from io import StringIO
|
||||
from threading import Thread
|
||||
from queue import Queue, Empty
|
||||
|
@ -81,15 +81,8 @@ def save_image(image):
|
|||
|
||||
image.filepath_raw = savepath
|
||||
|
||||
# if "Normal" in image.name:
|
||||
# bpy.context.scene.render.image_settings.quality = 90
|
||||
# image.save_render( filepath = image.filepath_raw, scene = bpy.context.scene )
|
||||
# else:
|
||||
image.save()
|
||||
|
||||
|
||||
|
||||
|
||||
def get_file_size(filepath):
|
||||
size = "Unpack Files"
|
||||
try:
|
||||
|
@ -141,7 +134,7 @@ def check_is_org_material(self,material):
|
|||
|
||||
|
||||
def clean_empty_materials(self):
|
||||
for obj in bpy.data.objects:
|
||||
for obj in bpy.context.scene.objects:
|
||||
for slot in obj.material_slots:
|
||||
mat = slot.material
|
||||
if mat is None:
|
||||
|
@ -319,6 +312,11 @@ def lightmap_to_ao(material,lightmap_node):
|
|||
# https://github.com/mattedicksoncom/blender-xatlas/
|
||||
###########################################################
|
||||
|
||||
def gen_safe_name():
|
||||
genId = uuid.uuid4().hex
|
||||
# genId = "u_" + genId.replace("-","_")
|
||||
return "u_" + genId
|
||||
|
||||
def Unwrap_Lightmap_Group_Xatlas_2_headless_call(obj):
|
||||
|
||||
blender_xatlas = importlib.util.find_spec("blender_xatlas")
|
||||
|
@ -330,32 +328,54 @@ def Unwrap_Lightmap_Group_Xatlas_2_headless_call(obj):
|
|||
|
||||
packOptions = bpy.context.scene.pack_tool
|
||||
chartOptions = bpy.context.scene.chart_tool
|
||||
|
||||
sharedProperties = bpy.context.scene.shared_properties
|
||||
#sharedProperties.unwrapSelection
|
||||
|
||||
context = bpy.context
|
||||
|
||||
if obj.type == 'MESH':
|
||||
context.view_layer.objects.active = obj
|
||||
if obj.data.users > 1:
|
||||
obj.data = obj.data.copy() #make single user copy
|
||||
uv_layers = obj.data.uv_layers
|
||||
|
||||
#setup the lightmap uvs
|
||||
uvName = "UVMap_Lightmap"
|
||||
if sharedProperties.lightmapUVChoiceType == "NAME":
|
||||
uvName = sharedProperties.lightmapUVName
|
||||
elif sharedProperties.lightmapUVChoiceType == "INDEX":
|
||||
if sharedProperties.lightmapUVIndex < len(uv_layers):
|
||||
uvName = uv_layers[sharedProperties.lightmapUVIndex].name
|
||||
#save whatever mode the user was in
|
||||
startingMode = bpy.context.object.mode
|
||||
selected_objects = bpy.context.selected_objects
|
||||
|
||||
if not uvName in uv_layers:
|
||||
uvmap = uv_layers.new(name=uvName)
|
||||
uv_layers.active_index = len(uv_layers) - 1
|
||||
else:
|
||||
for i in range(0, len(uv_layers)):
|
||||
if uv_layers[i].name == uvName:
|
||||
uv_layers.active_index = i
|
||||
obj.select_set(True)
|
||||
#check something is actually selected
|
||||
#external function/operator will select them
|
||||
if len(selected_objects) == 0:
|
||||
print("Nothing Selected")
|
||||
self.report({"WARNING"}, "Nothing Selected, please select Something")
|
||||
return {'FINISHED'}
|
||||
|
||||
#store the names of objects to be lightmapped
|
||||
rename_dict = dict()
|
||||
safe_dict = dict()
|
||||
|
||||
#make sure all the objects have ligthmap uvs
|
||||
for obj in selected_objects:
|
||||
if obj.type == 'MESH':
|
||||
safe_name = gen_safe_name();
|
||||
rename_dict[obj.name] = (obj.name,safe_name)
|
||||
safe_dict[safe_name] = obj.name
|
||||
context.view_layer.objects.active = obj
|
||||
if obj.data.users > 1:
|
||||
obj.data = obj.data.copy() #make single user copy
|
||||
uv_layers = obj.data.uv_layers
|
||||
|
||||
#setup the lightmap uvs
|
||||
uvName = "UVMap_Lightmap"
|
||||
if sharedProperties.lightmapUVChoiceType == "NAME":
|
||||
uvName = sharedProperties.lightmapUVName
|
||||
elif sharedProperties.lightmapUVChoiceType == "INDEX":
|
||||
if sharedProperties.lightmapUVIndex < len(uv_layers):
|
||||
uvName = uv_layers[sharedProperties.lightmapUVIndex].name
|
||||
|
||||
if not uvName in uv_layers:
|
||||
uvmap = uv_layers.new(name=uvName)
|
||||
uv_layers.active_index = len(uv_layers) - 1
|
||||
else:
|
||||
for i in range(0, len(uv_layers)):
|
||||
if uv_layers[i].name == uvName:
|
||||
uv_layers.active_index = i
|
||||
obj.select_set(True)
|
||||
|
||||
#save all the current edges
|
||||
if sharedProperties.packOnly:
|
||||
|
@ -381,8 +401,11 @@ def Unwrap_Lightmap_Group_Xatlas_2_headless_call(obj):
|
|||
|
||||
bpy.ops.object.mode_set(mode='OBJECT')
|
||||
|
||||
#Create a fake obj export to a string
|
||||
#Will strip this down further later
|
||||
fakeFile = StringIO()
|
||||
blender_xatlas.export_obj_simple.save(
|
||||
rename_dict=rename_dict,
|
||||
context=bpy.context,
|
||||
filepath=fakeFile,
|
||||
mainUVChoiceType=sharedProperties.mainUVChoiceType,
|
||||
|
@ -393,20 +416,26 @@ def Unwrap_Lightmap_Group_Xatlas_2_headless_call(obj):
|
|||
use_mesh_modifiers=True,
|
||||
use_edges=True,
|
||||
use_smooth_groups=False,
|
||||
use_smooth_groups_bitflags=False,
|
||||
use_smooth_groups_bitflags=False,
|
||||
use_normals=True,
|
||||
use_uvs=True,
|
||||
use_materials=False,
|
||||
use_triangles=False,
|
||||
use_nurbs=False,
|
||||
use_vertex_groups=False,
|
||||
use_nurbs=False,
|
||||
use_vertex_groups=False,
|
||||
use_blen_objects=True,
|
||||
group_by_object=False,
|
||||
group_by_material=False,
|
||||
keep_vertex_order=False,
|
||||
)
|
||||
|
||||
file_path = os.path.dirname(os.path.abspath(blender_xatlas.__file__))
|
||||
#print just for reference
|
||||
# print(fakeFile.getvalue())
|
||||
|
||||
#get the path to xatlas
|
||||
#file_path = os.path.dirname(os.path.abspath(__file__))
|
||||
scriptsDir = bpy.utils.user_resource('SCRIPTS', "addons")
|
||||
file_path = os.path.join(scriptsDir, "blender_xatlas")
|
||||
if platform.system() == "Windows":
|
||||
xatlas_path = os.path.join(file_path, "xatlas", "xatlas-blender.exe")
|
||||
elif platform.system() == "Linux":
|
||||
|
@ -458,6 +487,8 @@ def Unwrap_Lightmap_Group_Xatlas_2_headless_call(obj):
|
|||
shell=True
|
||||
)
|
||||
|
||||
print(xatlas_path)
|
||||
|
||||
#shove the fake file in stdin
|
||||
stdin = xatlas_process.stdin
|
||||
value = bytes(fakeFile.getvalue() + "\n", 'UTF-8') #The \n is needed to end the input properly
|
||||
|
@ -482,17 +513,17 @@ def Unwrap_Lightmap_Group_Xatlas_2_headless_call(obj):
|
|||
obName: string = ""
|
||||
uvArray: List[float] = field(default_factory=list)
|
||||
faceArray: List[int] = field(default_factory=list)
|
||||
|
||||
|
||||
convertedObjects = []
|
||||
uvArrayComplete = []
|
||||
|
||||
|
||||
|
||||
#search through the out put for STARTOBJ
|
||||
#then start reading the objects
|
||||
obTest = None
|
||||
startRead = False
|
||||
for line in outObj.splitlines():
|
||||
|
||||
|
||||
line_split = line.split()
|
||||
|
||||
if not line_split:
|
||||
|
@ -504,14 +535,14 @@ def Unwrap_Lightmap_Group_Xatlas_2_headless_call(obj):
|
|||
print("Start reading the objects----------------------------------------")
|
||||
startRead = True
|
||||
# obTest = uvObject()
|
||||
|
||||
|
||||
if startRead:
|
||||
#if it's a new obj
|
||||
if line_start == 'o':
|
||||
#if there is already an object append it
|
||||
if obTest is not None:
|
||||
convertedObjects.append(obTest)
|
||||
|
||||
|
||||
obTest = uvObject() #create new uv object
|
||||
obTest.obName = line_split[1]
|
||||
|
||||
|
@ -536,9 +567,9 @@ def Unwrap_Lightmap_Group_Xatlas_2_headless_call(obj):
|
|||
|
||||
#append the final object
|
||||
convertedObjects.append(obTest)
|
||||
# print(convertedObjects)
|
||||
|
||||
|
||||
print(convertedObjects)
|
||||
|
||||
|
||||
#apply the output-------------------------------------------------------------
|
||||
#copy the uvs to the original objects
|
||||
# objIndex = 0
|
||||
|
@ -548,7 +579,7 @@ def Unwrap_Lightmap_Group_Xatlas_2_headless_call(obj):
|
|||
bpy.ops.object.select_all(action='DESELECT')
|
||||
|
||||
obTest = importObject
|
||||
|
||||
obTest.obName = safe_dict[obTest.obName] #probably shouldn't just replace it
|
||||
bpy.context.scene.objects[obTest.obName].select_set(True)
|
||||
context.view_layer.objects.active = bpy.context.scene.objects[obTest.obName]
|
||||
bpy.ops.object.mode_set(mode = 'OBJECT')
|
||||
|
@ -563,7 +594,7 @@ def Unwrap_Lightmap_Group_Xatlas_2_headless_call(obj):
|
|||
|
||||
nFaces = len(bm.faces)
|
||||
#need to ensure lookup table for some reason?
|
||||
if hasattr(bm.faces, "ensure_lookup_table"):
|
||||
if hasattr(bm.faces, "ensure_lookup_table"):
|
||||
bm.faces.ensure_lookup_table()
|
||||
|
||||
#loop through the faces
|
||||
|
@ -601,7 +632,7 @@ def Unwrap_Lightmap_Group_Xatlas_2_headless_call(obj):
|
|||
currentObject = bpy.context.scene.objects[edgeList['object']]
|
||||
bm = bmesh.new()
|
||||
bm.from_mesh(currentObject.data)
|
||||
if hasattr(bm.edges, "ensure_lookup_table"):
|
||||
if hasattr(bm.edges, "ensure_lookup_table"):
|
||||
bm.edges.ensure_lookup_table()
|
||||
|
||||
#assume that all the triangulated edges come after the original edges
|
||||
|
@ -617,6 +648,27 @@ def Unwrap_Lightmap_Group_Xatlas_2_headless_call(obj):
|
|||
bm.free()
|
||||
bpy.ops.object.mode_set(mode='EDIT')
|
||||
|
||||
#End setting the quads back again------------------------------------------------------------
|
||||
#End setting the quads back again-------------------------------------------------------------
|
||||
|
||||
print("Finished Xatlas----------------------------------------")
|
||||
#select the original objects that were selected
|
||||
for objectName in rename_dict:
|
||||
if objectName[0] in bpy.context.scene.objects:
|
||||
current_object = bpy.context.scene.objects[objectName[0]]
|
||||
current_object.select_set(True)
|
||||
context.view_layer.objects.active = current_object
|
||||
|
||||
bpy.ops.object.mode_set(mode=startingMode)
|
||||
|
||||
print("Finished Xatlas----------------------------------------")
|
||||
return {'FINISHED'}
|
||||
|
||||
def transfer_assets(copy, source, destination):
|
||||
for filename in glob.glob(os.path.join(source, '*.*')):
|
||||
shutil.copy(filename, destination)
|
||||
|
||||
def transfer_load():
|
||||
load_folder = bpy.path.abspath(os.path.join(os.path.dirname(bpy.data.filepath), bpy.context.scene.TLM_SceneProperties.tlm_load_folder))
|
||||
lightmap_folder = os.path.join(os.path.dirname(bpy.data.filepath), bpy.context.scene.TLM_EngineProperties.tlm_lightmap_savedir)
|
||||
print(load_folder)
|
||||
print(lightmap_folder)
|
||||
transfer_assets(True, load_folder, lightmap_folder)
|
|
@ -112,6 +112,7 @@ class ArmNodeAddInputButton(bpy.types.Operator):
|
|||
"""Add a new input socket to the node set by node_index."""
|
||||
bl_idname = 'arm.node_add_input'
|
||||
bl_label = 'Add Input'
|
||||
bl_options = {'UNDO', 'INTERNAL'}
|
||||
|
||||
node_index: StringProperty(name='Node Index', default='')
|
||||
socket_type: StringProperty(name='Socket Type', default='NodeSocketShader')
|
||||
|
@ -135,6 +136,7 @@ class ArmNodeAddInputValueButton(bpy.types.Operator):
|
|||
"""Add new input"""
|
||||
bl_idname = 'arm.node_add_input_value'
|
||||
bl_label = 'Add Input'
|
||||
bl_options = {'UNDO', 'INTERNAL'}
|
||||
node_index: StringProperty(name='Node Index', default='')
|
||||
socket_type: StringProperty(name='Socket Type', default='NodeSocketShader')
|
||||
|
||||
|
@ -148,6 +150,7 @@ class ArmNodeRemoveInputButton(bpy.types.Operator):
|
|||
"""Remove last input"""
|
||||
bl_idname = 'arm.node_remove_input'
|
||||
bl_label = 'Remove Input'
|
||||
bl_options = {'UNDO', 'INTERNAL'}
|
||||
node_index: StringProperty(name='Node Index', default='')
|
||||
|
||||
def execute(self, context):
|
||||
|
@ -163,6 +166,7 @@ class ArmNodeRemoveInputValueButton(bpy.types.Operator):
|
|||
"""Remove last input"""
|
||||
bl_idname = 'arm.node_remove_input_value'
|
||||
bl_label = 'Remove Input'
|
||||
bl_options = {'UNDO', 'INTERNAL'}
|
||||
node_index: StringProperty(name='Node Index', default='')
|
||||
|
||||
def execute(self, context):
|
||||
|
@ -178,6 +182,7 @@ class ArmNodeAddOutputButton(bpy.types.Operator):
|
|||
"""Add a new output socket to the node set by node_index"""
|
||||
bl_idname = 'arm.node_add_output'
|
||||
bl_label = 'Add Output'
|
||||
bl_options = {'UNDO', 'INTERNAL'}
|
||||
|
||||
node_index: StringProperty(name='Node Index', default='')
|
||||
socket_type: StringProperty(name='Socket Type', default='NodeSocketShader')
|
||||
|
@ -201,6 +206,7 @@ class ArmNodeRemoveOutputButton(bpy.types.Operator):
|
|||
"""Remove last output"""
|
||||
bl_idname = 'arm.node_remove_output'
|
||||
bl_label = 'Remove Output'
|
||||
bl_options = {'UNDO', 'INTERNAL'}
|
||||
node_index: StringProperty(name='Node Index', default='')
|
||||
|
||||
def execute(self, context):
|
||||
|
@ -216,6 +222,7 @@ class ArmNodeAddInputOutputButton(bpy.types.Operator):
|
|||
"""Add new input and output"""
|
||||
bl_idname = 'arm.node_add_input_output'
|
||||
bl_label = 'Add Input Output'
|
||||
bl_options = {'UNDO', 'INTERNAL'}
|
||||
|
||||
node_index: StringProperty(name='Node Index', default='')
|
||||
in_socket_type: StringProperty(name='In Socket Type', default='NodeSocketShader')
|
||||
|
@ -246,6 +253,7 @@ class ArmNodeRemoveInputOutputButton(bpy.types.Operator):
|
|||
"""Remove last input and output"""
|
||||
bl_idname = 'arm.node_remove_input_output'
|
||||
bl_label = 'Remove Input Output'
|
||||
bl_options = {'UNDO', 'INTERNAL'}
|
||||
node_index: StringProperty(name='Node Index', default='')
|
||||
|
||||
def execute(self, context):
|
||||
|
@ -265,7 +273,7 @@ class ArmNodeRemoveInputOutputButton(bpy.types.Operator):
|
|||
class ArmNodeSearch(bpy.types.Operator):
|
||||
bl_idname = "arm.node_search"
|
||||
bl_label = "Search..."
|
||||
bl_options = {"REGISTER"}
|
||||
bl_options = {"REGISTER", "INTERNAL"}
|
||||
bl_property = "item"
|
||||
|
||||
def get_search_items(self, context):
|
||||
|
|
|
@ -5,6 +5,7 @@ class NodeAddOutputButton(bpy.types.Operator):
|
|||
"""Add 4 States"""
|
||||
bl_idname = 'arm.add_output_4_parameters'
|
||||
bl_label = 'Add 4 States'
|
||||
bl_options = {'UNDO', 'INTERNAL'}
|
||||
node_index: StringProperty(name='Node Index', default='')
|
||||
socket_type: StringProperty(name='Socket Type', default='NodeSocketShader')
|
||||
name_format: StringProperty(name='Name Format', default='Output {0}')
|
||||
|
@ -31,6 +32,7 @@ class NodeRemoveOutputButton(bpy.types.Operator):
|
|||
"""Remove 4 last states"""
|
||||
bl_idname = 'arm.remove_output_4_parameters'
|
||||
bl_label = 'Remove 4 States'
|
||||
bl_options = {'UNDO', 'INTERNAL'}
|
||||
node_index: StringProperty(name='Node Index', default='')
|
||||
|
||||
def execute(self, context):
|
||||
|
|
|
@ -433,7 +433,7 @@ def compilation_server_done():
|
|||
def build_done():
|
||||
print('Finished in {:0.3f}s'.format(time.time() - profile_time))
|
||||
if log.num_warnings > 0:
|
||||
log.print_warn(f'{log.num_warnings} warnings occurred during compilation')
|
||||
log.print_warn(f'{log.num_warnings} warning{"s" if log.num_warnings > 1 else ""} occurred during compilation')
|
||||
if state.proc_build is None:
|
||||
return
|
||||
result = state.proc_build.poll()
|
||||
|
|
|
@ -17,17 +17,59 @@ shader_datas = []
|
|||
|
||||
|
||||
def build():
|
||||
"""Builds world shaders for all exported worlds."""
|
||||
global shader_datas
|
||||
|
||||
bpy.data.worlds['Arm'].world_defs = ''
|
||||
wrd = bpy.data.worlds['Arm']
|
||||
rpdat = arm.utils.get_rp()
|
||||
|
||||
mobile_mat = rpdat.arm_material_model == 'Mobile' or rpdat.arm_material_model == 'Solid'
|
||||
envpath = os.path.join(arm.utils.get_fp_build(), 'compiled', 'Assets', 'envmaps')
|
||||
|
||||
wrd.world_defs = ''
|
||||
worlds = []
|
||||
shader_datas = []
|
||||
|
||||
for scene in bpy.data.scenes:
|
||||
# Only export worlds from enabled scenes
|
||||
if scene.arm_export and scene.world is not None and scene.world not in worlds:
|
||||
worlds.append(scene.world)
|
||||
create_world_shaders(scene.world)
|
||||
with write_probes.setup_envmap_render():
|
||||
|
||||
for scene in bpy.data.scenes:
|
||||
world = scene.world
|
||||
|
||||
# Only export worlds from enabled scenes and only once per world
|
||||
if scene.arm_export and world is not None and world not in worlds:
|
||||
worlds.append(world)
|
||||
|
||||
world.arm_envtex_name = ''
|
||||
create_world_shaders(world)
|
||||
|
||||
if rpdat.arm_irradiance:
|
||||
# Plain background color
|
||||
if '_EnvCol' in world.world_defs:
|
||||
world_name = arm.utils.safestr(world.name)
|
||||
# Irradiance json file name
|
||||
world.arm_envtex_name = world_name
|
||||
world.arm_envtex_irr_name = world_name
|
||||
write_probes.write_color_irradiance(world_name, world.arm_envtex_color)
|
||||
|
||||
# Render world to envmap for (ir)radiance, if no
|
||||
# other probes are exported
|
||||
elif world.arm_envtex_name == '':
|
||||
write_probes.render_envmap(envpath, world)
|
||||
|
||||
filename = f'env_{arm.utils.safesrc(world.name)}'
|
||||
image_file = f'{filename}.jpg'
|
||||
image_filepath = os.path.join(envpath, image_file)
|
||||
|
||||
world.arm_envtex_name = image_file
|
||||
world.arm_envtex_irr_name = os.path.basename(image_filepath).rsplit('.', 1)[0]
|
||||
|
||||
write_radiance = rpdat.arm_radiance and not mobile_mat
|
||||
mip_count = write_probes.write_probes(image_filepath, True, world.arm_envtex_num_mips, write_radiance)
|
||||
world.arm_envtex_num_mips = mip_count
|
||||
|
||||
if write_radiance:
|
||||
# Set world def, everything else is handled by write_probes()
|
||||
wrd.world_defs += '_Rad'
|
||||
|
||||
|
||||
def create_world_shaders(world: bpy.types.World):
|
||||
|
@ -131,14 +173,7 @@ def build_node_tree(world: bpy.types.World, frag: Shader, vert: Shader, con: Sha
|
|||
col = world.color
|
||||
world.arm_envtex_color = [col[0], col[1], col[2], 1.0]
|
||||
world.arm_envtex_strength = 1.0
|
||||
|
||||
# Irradiance/Radiance: clear to color if no texture or sky is provided
|
||||
if rpdat.arm_irradiance or rpdat.arm_irradiance:
|
||||
if '_EnvSky' not in world.world_defs and '_EnvTex' not in world.world_defs and '_EnvImg' not in world.world_defs:
|
||||
# Irradiance json file name
|
||||
world.arm_envtex_name = world_name
|
||||
world.arm_envtex_irr_name = world_name
|
||||
write_probes.write_color_irradiance(world_name, world.arm_envtex_color)
|
||||
world.world_defs += '_EnvCol'
|
||||
|
||||
# Clouds enabled
|
||||
if rpdat.arm_clouds and world.arm_use_clouds:
|
||||
|
@ -279,7 +314,12 @@ def frag_write_clouds(world: bpy.types.World, frag: Shader):
|
|||
|
||||
func_cloud_radiance = 'float cloudRadiance(vec3 p, vec3 dir) {\n'
|
||||
if '_EnvSky' in world.world_defs:
|
||||
func_cloud_radiance += '\tvec3 sun_dir = hosekSunDirection;\n'
|
||||
# Nishita sky
|
||||
if 'vec3 sunDir' in frag.uniforms:
|
||||
func_cloud_radiance += '\tvec3 sun_dir = sunDir;\n'
|
||||
# Hosek
|
||||
else:
|
||||
func_cloud_radiance += '\tvec3 sun_dir = hosekSunDirection;\n'
|
||||
else:
|
||||
func_cloud_radiance += '\tvec3 sun_dir = vec3(0, 0, -1);\n'
|
||||
func_cloud_radiance += '''\tconst int steps = 8;
|
||||
|
@ -293,7 +333,7 @@ def frag_write_clouds(world: bpy.types.World, frag: Shader):
|
|||
}'''
|
||||
frag.add_function(func_cloud_radiance)
|
||||
|
||||
frag.add_function('''vec3 traceClouds(vec3 sky, vec3 dir) {
|
||||
func_trace_clouds = '''vec3 traceClouds(vec3 sky, vec3 dir) {
|
||||
\tconst float step_size = 0.5 / float(cloudsSteps);
|
||||
\tfloat T = 1.0;
|
||||
\tfloat C = 0.0;
|
||||
|
@ -312,6 +352,17 @@ def frag_write_clouds(world: bpy.types.World, frag: Shader):
|
|||
\t\t}
|
||||
\t\tuv += (dir.xy / dir.z) * step_size * cloudsUpper;
|
||||
\t}
|
||||
'''
|
||||
|
||||
\treturn vec3(C) + sky * T;
|
||||
}''')
|
||||
if world.arm_darken_clouds:
|
||||
func_trace_clouds += '\t// Darken clouds when the sun is low\n'
|
||||
|
||||
# Nishita sky
|
||||
if 'vec3 sunDir' in frag.uniforms:
|
||||
func_trace_clouds += '\tC *= smoothstep(-0.02, 0.25, sunDir.z);\n'
|
||||
# Hosek
|
||||
else:
|
||||
func_trace_clouds += '\tC *= smoothstep(0.04, 0.32, hosekSunDirection.z);\n'
|
||||
|
||||
func_trace_clouds += '\treturn vec3(C) + sky * T;\n}'
|
||||
frag.add_function(func_trace_clouds)
|
||||
|
|
|
@ -642,8 +642,47 @@ def to_vec1(v):
|
|||
return str(v)
|
||||
|
||||
|
||||
def to_vec2(v):
|
||||
return f'vec2({v[0]}, {v[1]})'
|
||||
|
||||
|
||||
def to_vec3(v):
|
||||
return 'vec3({0}, {1}, {2})'.format(v[0], v[1], v[2])
|
||||
return f'vec3({v[0]}, {v[1]}, {v[2]})'
|
||||
|
||||
|
||||
def cast_value(val: str, from_type: str, to_type: str) -> str:
|
||||
"""Casts a value that is already parsed in a glsl string to another
|
||||
value in a string.
|
||||
|
||||
vec2 types are not supported (not used in the node editor) and there
|
||||
is no cast towards int types. If casting from vec3 to vec4, the w
|
||||
coordinate/alpha channel is filled with a 1.
|
||||
|
||||
If this function is called with invalid parameters, a TypeError is
|
||||
raised.
|
||||
"""
|
||||
if from_type == to_type:
|
||||
return val
|
||||
|
||||
if from_type in ('int', 'float'):
|
||||
if to_type in ('int', 'float'):
|
||||
return val
|
||||
elif to_type in ('vec2', 'vec3', 'vec4'):
|
||||
return f'{to_type}({val})'
|
||||
|
||||
elif from_type == 'vec3':
|
||||
if to_type == 'float':
|
||||
return rgb_to_bw(val)
|
||||
elif to_type == 'vec4':
|
||||
return f'vec4({val}, 1.0)'
|
||||
|
||||
elif from_type == 'vec4':
|
||||
if to_type == 'float':
|
||||
return rgb_to_bw(val)
|
||||
elif to_type == 'vec3':
|
||||
return f'{val}.xyz'
|
||||
|
||||
raise TypeError("Invalid type cast in shader!")
|
||||
|
||||
|
||||
def rgb_to_bw(res_var: vec3str) -> floatstr:
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
import bpy
|
||||
from typing import Union
|
||||
|
||||
import bpy
|
||||
import mathutils
|
||||
|
||||
import arm.log as log
|
||||
import arm.material.cycles as c
|
||||
import arm.material.cycles_functions as c_functions
|
||||
|
@ -10,42 +12,81 @@ import arm.utils
|
|||
|
||||
|
||||
def parse_attribute(node: bpy.types.ShaderNodeAttribute, out_socket: bpy.types.NodeSocket, state: ParserState) -> Union[floatstr, vec3str]:
|
||||
# Color
|
||||
if out_socket == node.outputs[0]:
|
||||
# Vertex colors only for now
|
||||
state.con.add_elem('col', 'short4norm')
|
||||
return 'vcolor'
|
||||
out_type = 'float' if out_socket.type == 'VALUE' else 'vec3'
|
||||
|
||||
# Vector
|
||||
elif out_socket == node.outputs[1]:
|
||||
# UV maps only for now
|
||||
state.con.add_elem('tex', 'short2norm')
|
||||
if node.attribute_name == 'time':
|
||||
state.curshader.add_uniform('float time', link='_time')
|
||||
|
||||
if out_socket == node.outputs[3]:
|
||||
return '1.0'
|
||||
return c.cast_value('time', from_type='float', to_type=out_type)
|
||||
|
||||
# UV maps (higher priority) and vertex colors
|
||||
if node.attribute_type == 'GEOMETRY':
|
||||
|
||||
# Alpha output. Armory doesn't support vertex colors with alpha
|
||||
# values yet and UV maps don't have an alpha channel
|
||||
if out_socket == node.outputs[3]:
|
||||
return '1.0'
|
||||
|
||||
# UV maps
|
||||
mat = c.mat_get_material()
|
||||
mat_users = c.mat_get_material_users()
|
||||
|
||||
if mat_users is not None and mat in mat_users:
|
||||
mat_user = mat_users[mat][0]
|
||||
|
||||
# No UV layers for Curve
|
||||
# Curves don't have uv layers, so check that first
|
||||
if hasattr(mat_user.data, 'uv_layers'):
|
||||
lays = mat_user.data.uv_layers
|
||||
|
||||
# First UV map referenced
|
||||
if len(lays) > 0 and node.attribute_name == lays[0].name:
|
||||
state.con.add_elem('tex', 'short2norm')
|
||||
return c.cast_value('vec3(texCoord.x, 1.0 - texCoord.y, 0.0)', from_type='vec3', to_type=out_type)
|
||||
|
||||
# Second UV map referenced
|
||||
if len(lays) > 1 and node.attribute_name == lays[1].name:
|
||||
elif len(lays) > 1 and node.attribute_name == lays[1].name:
|
||||
state.con.add_elem('tex1', 'short2norm')
|
||||
return 'vec3(texCoord1.x, 1.0 - texCoord1.y, 0.0)'
|
||||
return c.cast_value('vec3(texCoord1.x, 1.0 - texCoord1.y, 0.0)', from_type='vec3', to_type=out_type)
|
||||
|
||||
return 'vec3(texCoord.x, 1.0 - texCoord.y, 0.0)'
|
||||
# Vertex colors
|
||||
# TODO: support multiple vertex color sets
|
||||
state.con.add_elem('col', 'short4norm')
|
||||
return c.cast_value('vcolor', from_type='vec3', to_type=out_type)
|
||||
|
||||
# Fac
|
||||
else:
|
||||
if node.attribute_name == 'time':
|
||||
state.curshader.add_uniform('float time', link='_time')
|
||||
return 'time'
|
||||
# Check object properties
|
||||
# see https://developer.blender.org/rB6fdcca8de6 for reference
|
||||
mat = c.mat_get_material()
|
||||
mat_users = c.mat_get_material_users()
|
||||
if mat_users is not None and mat in mat_users:
|
||||
# Use first material user for now...
|
||||
mat_user = mat_users[mat][0]
|
||||
|
||||
# Return 0.0 till drivers are implemented
|
||||
else:
|
||||
return '0.0'
|
||||
val = None
|
||||
# Custom properties first
|
||||
if node.attribute_name in mat_user:
|
||||
val = mat_user[node.attribute_name]
|
||||
# Blender properties
|
||||
elif hasattr(mat_user, node.attribute_name):
|
||||
val = getattr(mat_user, node.attribute_name)
|
||||
|
||||
if val is not None:
|
||||
if isinstance(val, float):
|
||||
return c.cast_value(str(val), from_type='float', to_type=out_type)
|
||||
elif isinstance(val, int):
|
||||
return c.cast_value(str(val), from_type='int', to_type=out_type)
|
||||
elif isinstance(val, mathutils.Vector) and len(val) <= 4:
|
||||
out = val.to_4d()
|
||||
|
||||
if out_socket == node.outputs[3]:
|
||||
return c.to_vec1(out[3])
|
||||
return c.cast_value(c.to_vec3(out), from_type='vec3', to_type=out_type)
|
||||
|
||||
# Default values, attribute name did not match
|
||||
if out_socket == node.outputs[3]:
|
||||
return '1.0'
|
||||
return c.cast_value('0.0', from_type='float', to_type=out_type)
|
||||
|
||||
|
||||
def parse_rgb(node: bpy.types.ShaderNodeRGB, out_socket: bpy.types.NodeSocket, state: ParserState) -> vec3str:
|
||||
|
|
|
@ -41,7 +41,7 @@ def parse_addshader(node: bpy.types.ShaderNodeAddShader, out_socket: NodeSocket,
|
|||
|
||||
def parse_bsdfprincipled(node: bpy.types.ShaderNodeBsdfPrincipled, out_socket: NodeSocket, state: ParserState) -> None:
|
||||
if state.parse_surface:
|
||||
c.write_normal(node.inputs[19])
|
||||
c.write_normal(node.inputs[20])
|
||||
state.out_basecol = c.parse_vector_input(node.inputs[0])
|
||||
# subsurface = c.parse_vector_input(node.inputs[1])
|
||||
# subsurface_radius = c.parse_vector_input(node.inputs[2])
|
||||
|
@ -62,11 +62,12 @@ def parse_bsdfprincipled(node: bpy.types.ShaderNodeBsdfPrincipled, out_socket: N
|
|||
if node.inputs[17].is_linked or node.inputs[17].default_value[0] != 0.0:
|
||||
state.out_emission = '({0}.x)'.format(c.parse_vector_input(node.inputs[17]))
|
||||
state.emission_found = True
|
||||
# clearcoar_normal = c.parse_vector_input(node.inputs[20])
|
||||
# tangent = c.parse_vector_input(node.inputs[21])
|
||||
# clearcoar_normal = c.parse_vector_input(node.inputs[21])
|
||||
# tangent = c.parse_vector_input(node.inputs[22])
|
||||
if state.parse_opacity:
|
||||
if len(node.inputs) > 20:
|
||||
state.out_opacity = c.parse_value_input(node.inputs[18])
|
||||
if len(node.inputs) > 21:
|
||||
state.out_opacity = c.parse_value_input(node.inputs[19])
|
||||
|
||||
|
||||
def parse_bsdfdiffuse(node: bpy.types.ShaderNodeBsdfDiffuse, out_socket: NodeSocket, state: ParserState) -> None:
|
||||
if state.parse_surface:
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import math
|
||||
import os
|
||||
from typing import Union
|
||||
|
||||
|
@ -293,13 +294,29 @@ def parse_tex_sky(node: bpy.types.ShaderNodeTexSky, out_socket: bpy.types.NodeSo
|
|||
# Pass through
|
||||
return c.to_vec3([0.0, 0.0, 0.0])
|
||||
|
||||
state.world.world_defs += '_EnvSky'
|
||||
|
||||
if node.sky_type == 'PREETHAM' or node.sky_type == 'HOSEK_WILKIE':
|
||||
if node.sky_type == 'PREETHAM':
|
||||
log.info('Info: Preetham sky model is not supported, using Hosek Wilkie sky model instead')
|
||||
|
||||
return parse_sky_hosekwilkie(node, state)
|
||||
|
||||
elif node.sky_type == 'NISHITA':
|
||||
return parse_sky_nishita(node, state)
|
||||
|
||||
else:
|
||||
log.error(f'Unsupported sky model: {node.sky_type}!')
|
||||
return c.to_vec3([0.0, 0.0, 0.0])
|
||||
|
||||
|
||||
def parse_sky_hosekwilkie(node: bpy.types.ShaderNodeTexSky, state: ParserState) -> vec3str:
|
||||
world = state.world
|
||||
curshader = state.curshader
|
||||
|
||||
# Match to cycles
|
||||
world.arm_envtex_strength *= 0.1
|
||||
|
||||
world.world_defs += '_EnvSky'
|
||||
assets.add_khafile_def('arm_hosek')
|
||||
curshader.add_uniform('vec3 A', link="_hosekA")
|
||||
curshader.add_uniform('vec3 B', link="_hosekB")
|
||||
|
@ -312,10 +329,10 @@ def parse_tex_sky(node: bpy.types.ShaderNodeTexSky, out_socket: bpy.types.NodeSo
|
|||
curshader.add_uniform('vec3 I', link="_hosekI")
|
||||
curshader.add_uniform('vec3 Z', link="_hosekZ")
|
||||
curshader.add_uniform('vec3 hosekSunDirection', link="_hosekSunDirection")
|
||||
curshader.add_function('''vec3 hosekWilkie(float cos_theta, float gamma, float cos_gamma) {
|
||||
curshader.add_function("""vec3 hosekWilkie(float cos_theta, float gamma, float cos_gamma) {
|
||||
\tvec3 chi = (1 + cos_gamma * cos_gamma) / pow(1 + H * H - 2 * cos_gamma * H, vec3(1.5));
|
||||
\treturn (1 + A * exp(B / (cos_theta + 0.01))) * (C + D * exp(E * gamma) + F * (cos_gamma * cos_gamma) + G * chi + I * sqrt(cos_theta));
|
||||
}''')
|
||||
}""")
|
||||
|
||||
world.arm_envtex_sun_direction = [node.sun_direction[0], node.sun_direction[1], node.sun_direction[2]]
|
||||
world.arm_envtex_turbidity = node.turbidity
|
||||
|
@ -353,6 +370,40 @@ def parse_tex_sky(node: bpy.types.ShaderNodeTexSky, out_socket: bpy.types.NodeSo
|
|||
return 'Z * hosekWilkie(cos_theta, gamma_val, cos_gamma) * envmapStrength;'
|
||||
|
||||
|
||||
def parse_sky_nishita(node: bpy.types.ShaderNodeTexSky, state: ParserState) -> vec3str:
|
||||
curshader = state.curshader
|
||||
curshader.add_include('std/sky.glsl')
|
||||
curshader.add_uniform('vec3 sunDir', link='_sunDirection')
|
||||
curshader.add_uniform('sampler2D nishitaLUT', link='_nishitaLUT', included=True,
|
||||
tex_addr_u='clamp', tex_addr_v='clamp')
|
||||
curshader.add_uniform('vec2 nishitaDensity', link='_nishitaDensity', included=True)
|
||||
|
||||
planet_radius = 6360e3 # Earth radius used in Blender
|
||||
ray_origin_z = planet_radius + node.altitude
|
||||
|
||||
state.world.arm_nishita_density = [node.air_density, node.dust_density, node.ozone_density]
|
||||
|
||||
sun = ''
|
||||
if node.sun_disc:
|
||||
# The sun size is calculated relative in terms of the distance
|
||||
# between the sun position and the sky dome normal at every
|
||||
# pixel (see sun_disk() in sky.glsl).
|
||||
#
|
||||
# An isosceles triangle is created with the camera at the
|
||||
# opposite side of the base with node.sun_size being the vertex
|
||||
# angle from which the base angle theta is calculated. Iron's
|
||||
# skydome geometry roughly resembles a unit sphere, so the leg
|
||||
# size is set to 1. The base size is the doubled normal-relative
|
||||
# target size.
|
||||
|
||||
# sun_size is already in radians despite being degrees in the UI
|
||||
theta = 0.5 * (math.pi - node.sun_size)
|
||||
size = math.cos(theta)
|
||||
sun = f'* sun_disk(n, sunDir, {size}, {node.sun_intensity})'
|
||||
|
||||
return f'nishita_atmosphere(n, vec3(0, 0, {ray_origin_z}), sunDir, {planet_radius}){sun}'
|
||||
|
||||
|
||||
def parse_tex_environment(node: bpy.types.ShaderNodeTexEnvironment, out_socket: bpy.types.NodeSocket, state: ParserState) -> vec3str:
|
||||
if state.context == ParserContext.OBJECT:
|
||||
log.warn('Environment Texture node is not supported for object node trees, using default value')
|
||||
|
|
|
@ -73,7 +73,8 @@ def is_transluc_type(node):
|
|||
node.type == 'BSDF_TRANSPARENT' or \
|
||||
node.type == 'BSDF_TRANSLUCENT' or \
|
||||
(node.type == 'GROUP' and node.node_tree.name.startswith('Armory PBR') and (node.inputs[1].is_linked or node.inputs[1].default_value != 1.0)) or \
|
||||
(node.type == 'BSDF_PRINCIPLED' and len(node.inputs) > 20 and (node.inputs[18].is_linked or node.inputs[18].default_value != 1.0)):
|
||||
(node.type == 'BSDF_PRINCIPLED' and len(node.inputs) > 20 and (node.inputs[18].is_linked or node.inputs[18].default_value != 1.0)) or \
|
||||
(node.type == 'BSDF_PRINCIPLED' and len(node.inputs) > 21 and (node.inputs[19].is_linked or node.inputs[19].default_value != 1.0)):
|
||||
return True
|
||||
return False
|
||||
|
||||
|
|
|
@ -119,16 +119,29 @@ class ShaderContext:
|
|||
c['link'] = link
|
||||
self.constants.append(c)
|
||||
|
||||
def add_texture_unit(self, ctype, name, link=None, is_image=None):
|
||||
def add_texture_unit(self, name, link=None, is_image=None,
|
||||
addr_u=None, addr_v=None,
|
||||
filter_min=None, filter_mag=None, mipmap_filter=None):
|
||||
for c in self.tunits:
|
||||
if c['name'] == name:
|
||||
return
|
||||
|
||||
c = { 'name': name }
|
||||
if link != None:
|
||||
c = {'name': name}
|
||||
if link is not None:
|
||||
c['link'] = link
|
||||
if is_image != None:
|
||||
if is_image is not None:
|
||||
c['is_image'] = is_image
|
||||
if addr_u is not None:
|
||||
c['addressing_u'] = addr_u
|
||||
if addr_v is not None:
|
||||
c['addressing_v'] = addr_v
|
||||
if filter_min is not None:
|
||||
c['filter_min'] = filter_min
|
||||
if filter_mag is not None:
|
||||
c['filter_mag'] = filter_mag
|
||||
if mipmap_filter is not None:
|
||||
c['mipmap_filter'] = mipmap_filter
|
||||
|
||||
self.tunits.append(c)
|
||||
|
||||
def make_vert(self, custom_name: str = None):
|
||||
|
@ -222,10 +235,10 @@ class Shader:
|
|||
if s not in self.outs:
|
||||
self.outs.append(s)
|
||||
|
||||
def add_uniform(self, s, link=None, included=False, top=False):
|
||||
# prevent duplicates
|
||||
if s in self.uniforms or s in self.uniforms_top:
|
||||
return
|
||||
def add_uniform(self, s, link=None, included=False, top=False,
|
||||
tex_addr_u=None, tex_addr_v=None,
|
||||
tex_filter_min=None, tex_filter_mag=None,
|
||||
tex_mipmap_filter=None):
|
||||
ar = s.split(' ')
|
||||
# layout(RGBA8) image3D voxels
|
||||
utype = ar[-2]
|
||||
|
@ -236,9 +249,15 @@ class Shader:
|
|||
# Add individual units - mySamplers[0], mySamplers[1]
|
||||
for i in range(int(uname[-2])):
|
||||
uname_array = uname[:-2] + str(i) + ']'
|
||||
self.context.add_texture_unit(utype, uname_array, link=link, is_image=is_image)
|
||||
self.context.add_texture_unit(
|
||||
uname_array, link, is_image,
|
||||
tex_addr_u, tex_addr_v,
|
||||
tex_filter_min, tex_filter_mag, tex_mipmap_filter)
|
||||
else:
|
||||
self.context.add_texture_unit(utype, uname, link=link, is_image=is_image)
|
||||
self.context.add_texture_unit(
|
||||
uname, link, is_image,
|
||||
tex_addr_u, tex_addr_v,
|
||||
tex_filter_min, tex_filter_mag, tex_mipmap_filter)
|
||||
else:
|
||||
# Prefer vec4[] for d3d to avoid padding
|
||||
if ar[0] == 'float' and '[' in ar[1]:
|
||||
|
@ -251,9 +270,8 @@ class Shader:
|
|||
if top:
|
||||
if not included and s not in self.uniforms_top:
|
||||
self.uniforms_top.append(s)
|
||||
else:
|
||||
if not included and s not in self.uniforms:
|
||||
self.uniforms.append(s)
|
||||
elif not included and s not in self.uniforms:
|
||||
self.uniforms.append(s)
|
||||
|
||||
def add_const(self, type_str: str, name: str, value_str: str, array_size: int = 0):
|
||||
"""
|
||||
|
|
|
@ -8,6 +8,7 @@ import arm.logicnode.arm_nodes as arm_nodes
|
|||
import arm.logicnode.replacement
|
||||
import arm.logicnode
|
||||
import arm.props_traits
|
||||
import arm.ui_icons as ui_icons
|
||||
import arm.utils
|
||||
|
||||
registered_nodes = []
|
||||
|
@ -58,13 +59,14 @@ class ARM_OT_AddNodeOverride(bpy.types.Operator):
|
|||
bl_idname = "arm.add_node_override"
|
||||
bl_label = "Add Node"
|
||||
bl_property = "type"
|
||||
bl_options = {'INTERNAL'}
|
||||
|
||||
type: StringProperty(name="NodeItem type")
|
||||
use_transform: BoolProperty(name="Use Transform")
|
||||
|
||||
def invoke(self, context, event):
|
||||
bpy.ops.node.add_node('INVOKE_DEFAULT', type=self.type, use_transform=self.use_transform)
|
||||
return {"FINISHED"}
|
||||
return {'FINISHED'}
|
||||
|
||||
@classmethod
|
||||
def description(cls, context, properties):
|
||||
|
@ -173,7 +175,7 @@ class ARM_PT_LogicNodePanel(bpy.types.Panel):
|
|||
layout.operator('arm.open_node_documentation', icon='HELP')
|
||||
column = layout.column(align=True)
|
||||
column.operator('arm.open_node_python_source', icon='FILE_SCRIPT')
|
||||
column.operator('arm.open_node_haxe_source', icon_value=arm.props_traits.icons_dict['haxe'].icon_id)
|
||||
column.operator('arm.open_node_haxe_source', icon_value=ui_icons.get_id("haxe"))
|
||||
|
||||
|
||||
class ArmOpenNodeHaxeSource(bpy.types.Operator):
|
||||
|
@ -261,7 +263,7 @@ class ARM_PT_Variables(bpy.types.Panel):
|
|||
setN.ntype = ID
|
||||
|
||||
class ARMAddVarNode(bpy.types.Operator):
|
||||
'''Add a linked node of that Variable'''
|
||||
"""Add a linked node of that Variable"""
|
||||
bl_idname = 'arm.add_var_node'
|
||||
bl_label = 'Add Get'
|
||||
bl_options = {'GRAB_CURSOR', 'BLOCKING'}
|
||||
|
@ -296,7 +298,7 @@ class ARMAddVarNode(bpy.types.Operator):
|
|||
return({'FINISHED'})
|
||||
|
||||
class ARMAddSetVarNode(bpy.types.Operator):
|
||||
'''Add a node to set this Variable'''
|
||||
"""Add a node to set this Variable"""
|
||||
bl_idname = 'arm.add_setvar_node'
|
||||
bl_label = 'Add Set'
|
||||
bl_options = {'GRAB_CURSOR', 'BLOCKING'}
|
||||
|
|
|
@ -178,7 +178,7 @@ def init_properties():
|
|||
('ErrorsOnly', 'Errors Only', 'Show only errors')],
|
||||
name="Compile Log Parameter", update=assets.invalidate_compiler_cache,
|
||||
default="Summary")
|
||||
bpy.types.World.arm_project_win_build_cpu = IntProperty(name="Count CPU", description="Specifies the maximum number of concurrent processes to use when building", default=1, min=1, max=multiprocessing.cpu_count())
|
||||
bpy.types.World.arm_project_win_build_cpu = IntProperty(name="CPU Count", description="Specifies the maximum number of concurrent processes to use when building", default=1, min=1, max=multiprocessing.cpu_count())
|
||||
bpy.types.World.arm_project_win_build_open = BoolProperty(name="Open Build Directory", description="Open the build directory after successfully assemble", default=False)
|
||||
|
||||
bpy.types.World.arm_project_icon = StringProperty(name="Icon (PNG)", description="Exported project icon, must be a PNG image", default="", subtype="FILE_PATH", update=assets.invalidate_compiler_cache)
|
||||
|
@ -212,11 +212,11 @@ def init_properties():
|
|||
bpy.types.World.arm_khafile = PointerProperty(name="Khafile", description="Source appended to khafile.js", update=assets.invalidate_compiler_cache, type=bpy.types.Text)
|
||||
bpy.types.World.arm_texture_quality = FloatProperty(name="Texture Quality", default=1.0, min=0.0, max=1.0, subtype='FACTOR', update=assets.invalidate_compiler_cache)
|
||||
bpy.types.World.arm_sound_quality = FloatProperty(name="Sound Quality", default=0.9, min=0.0, max=1.0, subtype='FACTOR', update=assets.invalidate_compiler_cache)
|
||||
bpy.types.World.arm_minimize = BoolProperty(name="Minimize Data", description="Export scene data in binary", default=True, update=assets.invalidate_compiled_data)
|
||||
bpy.types.World.arm_minimize = BoolProperty(name="Binary Scene Data", description="Export scene data in binary", default=True, update=assets.invalidate_compiled_data)
|
||||
bpy.types.World.arm_minify_js = BoolProperty(name="Minify JS", description="Minimize JavaScript output when publishing", default=True)
|
||||
bpy.types.World.arm_optimize_data = BoolProperty(name="Optimize Data", description="Export more efficient geometry and shader data, prolongs build times", default=True, update=assets.invalidate_compiled_data)
|
||||
bpy.types.World.arm_deinterleaved_buffers = BoolProperty(name="Deinterleaved Buffers", description="Use deinterleaved vertex buffers", default=False, update=assets.invalidate_compiler_cache)
|
||||
bpy.types.World.arm_export_tangents = BoolProperty(name="Export Tangents", description="Precompute tangents for normal mapping, otherwise computed in shader", default=True, update=assets.invalidate_compiled_data)
|
||||
bpy.types.World.arm_export_tangents = BoolProperty(name="Precompute Tangents", description="Precompute tangents for normal mapping, otherwise computed in shader", default=True, update=assets.invalidate_compiled_data)
|
||||
bpy.types.World.arm_batch_meshes = BoolProperty(name="Batch Meshes", description="Group meshes by materials to speed up rendering", default=False, update=assets.invalidate_compiler_cache)
|
||||
bpy.types.World.arm_batch_materials = BoolProperty(name="Batch Materials", description="Marge similar materials into single pipeline state", default=False, update=assets.invalidate_shader_cache)
|
||||
bpy.types.World.arm_stream_scene = BoolProperty(name="Stream Scene", description="Stream scene content", default=False, update=assets.invalidate_compiler_cache)
|
||||
|
@ -332,6 +332,7 @@ def init_properties():
|
|||
bpy.types.World.arm_envtex_sun_direction = FloatVectorProperty(name="Sun Direction", size=3, default=[0,0,0])
|
||||
bpy.types.World.arm_envtex_turbidity = FloatProperty(name="Turbidity", default=1.0)
|
||||
bpy.types.World.arm_envtex_ground_albedo = FloatProperty(name="Ground Albedo", default=0.0)
|
||||
bpy.types.World.arm_nishita_density = FloatVectorProperty(name="Nishita Density", size=3, default=[1, 1, 1])
|
||||
bpy.types.Material.arm_cast_shadow = BoolProperty(name="Cast Shadow", default=True)
|
||||
bpy.types.Material.arm_receive_shadow = BoolProperty(name="Receive Shadow", description="Requires forward render path", default=True)
|
||||
bpy.types.Material.arm_overlay = BoolProperty(name="Overlay", default=False)
|
||||
|
@ -398,7 +399,7 @@ def init_properties():
|
|||
('destination_color', 'Destination Color', 'Destination Color'),
|
||||
('inverse_source_color', 'Inverse Source Color', 'Inverse Source Color'),
|
||||
('inverse_destination_color', 'Inverse Destination Color', 'Inverse Destination Color')],
|
||||
name='Source', default='blend_one', description='Blending factor', update=assets.invalidate_shader_cache)
|
||||
name='Source (Alpha)', default='blend_one', description='Blending factor', update=assets.invalidate_shader_cache)
|
||||
bpy.types.Material.arm_blending_destination_alpha = EnumProperty(
|
||||
items=[('blend_one', 'One', 'One'),
|
||||
('blend_zero', 'Zero', 'Zero'),
|
||||
|
@ -410,14 +411,14 @@ def init_properties():
|
|||
('destination_color', 'Destination Color', 'Destination Color'),
|
||||
('inverse_source_color', 'Inverse Source Color', 'Inverse Source Color'),
|
||||
('inverse_destination_color', 'Inverse Destination Color', 'Inverse Destination Color')],
|
||||
name='Destination', default='blend_one', description='Blending factor', update=assets.invalidate_shader_cache)
|
||||
name='Destination (Alpha)', default='blend_one', description='Blending factor', update=assets.invalidate_shader_cache)
|
||||
bpy.types.Material.arm_blending_operation_alpha = EnumProperty(
|
||||
items=[('add', 'Add', 'Add'),
|
||||
('subtract', 'Subtract', 'Subtract'),
|
||||
('reverse_subtract', 'Reverse Subtract', 'Reverse Subtract'),
|
||||
('min', 'Min', 'Min'),
|
||||
('max', 'Max', 'Max')],
|
||||
name='Operation', default='add', description='Blending operation', update=assets.invalidate_shader_cache)
|
||||
name='Operation (Alpha)', default='add', description='Blending operation', update=assets.invalidate_shader_cache)
|
||||
# For scene
|
||||
bpy.types.Scene.arm_export = BoolProperty(name="Export", description="Export scene data", default=True)
|
||||
bpy.types.Scene.arm_terrain_textures = StringProperty(name="Textures", description="Set root folder for terrain assets", default="//Bundled/", subtype="DIR_PATH")
|
||||
|
@ -442,6 +443,10 @@ def init_properties():
|
|||
bpy.types.World.compo_defs = StringProperty(name="Compositor Shader Defs", default='')
|
||||
|
||||
bpy.types.World.arm_use_clouds = BoolProperty(name="Clouds", default=False, update=assets.invalidate_shader_cache)
|
||||
bpy.types.World.arm_darken_clouds = BoolProperty(
|
||||
name="Darken Clouds at Night",
|
||||
description="Darkens the clouds when the sun is low. This setting is for artistic purposes and is not physically correct",
|
||||
default=False, update=assets.invalidate_shader_cache)
|
||||
bpy.types.World.arm_clouds_lower = FloatProperty(name="Lower", default=1.0, min=0.1, max=10.0, update=assets.invalidate_shader_cache)
|
||||
bpy.types.World.arm_clouds_upper = FloatProperty(name="Upper", default=1.0, min=0.1, max=10.0, update=assets.invalidate_shader_cache)
|
||||
bpy.types.World.arm_clouds_wind = FloatVectorProperty(name="Wind", default=[1.0, 0.0], size=2, update=assets.invalidate_shader_cache)
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import bpy
|
||||
from bpy.props import *
|
||||
from bpy.types import Panel
|
||||
|
||||
|
||||
class ARM_PT_RbCollisionFilterMaskPanel(bpy.types.Panel):
|
||||
bl_label = "Collections Filter Mask"
|
||||
|
@ -18,7 +17,7 @@ class ARM_PT_RbCollisionFilterMaskPanel(bpy.types.Panel):
|
|||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
layout.use_property_split = True
|
||||
layout.use_property_split = False
|
||||
layout.use_property_decorate = False
|
||||
obj = context.object
|
||||
layout.prop(obj, 'arm_rb_collision_filter_mask', text="", expand=True)
|
||||
|
@ -30,8 +29,10 @@ class ARM_PT_RbCollisionFilterMaskPanel(bpy.types.Panel):
|
|||
row.alignment = 'RIGHT'
|
||||
row.label(text=f'Integer Mask Value: {str(int(col_mask, 2))}')
|
||||
|
||||
|
||||
def register():
|
||||
bpy.utils.register_class(ARM_PT_RbCollisionFilterMaskPanel)
|
||||
|
||||
|
||||
def unregister():
|
||||
bpy.utils.unregister_class(ARM_PT_RbCollisionFilterMaskPanel)
|
||||
|
|
|
@ -358,7 +358,7 @@ class ArmExporterSpecialsMenu(bpy.types.Menu):
|
|||
layout.operator("arm.exporter_gpuprofile")
|
||||
|
||||
class ArmoryExporterOpenFolderButton(bpy.types.Operator):
|
||||
'''Open published folder'''
|
||||
"""Open published folder"""
|
||||
bl_idname = 'arm.exporter_open_folder'
|
||||
bl_label = 'Open Folder'
|
||||
|
||||
|
|
|
@ -34,29 +34,29 @@ class ArmLodListItem(bpy.types.PropertyGroup):
|
|||
|
||||
class ARM_UL_LodList(bpy.types.UIList):
|
||||
def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index):
|
||||
# We could write some code to decide which icon to use here...
|
||||
custom_icon = 'OBJECT_DATAMODE'
|
||||
layout.use_property_split = False
|
||||
|
||||
# Make sure your code supports all 3 layout types
|
||||
if self.layout_type in {'DEFAULT', 'COMPACT'}:
|
||||
layout.prop(item, "enabled_prop")
|
||||
row = layout.row()
|
||||
row.separator(factor=0.1)
|
||||
row.prop(item, "enabled_prop")
|
||||
name = item.name
|
||||
if name == '':
|
||||
name = 'None'
|
||||
row = layout.row()
|
||||
row.label(text=name, icon=custom_icon)
|
||||
row.label(text=name, icon='OBJECT_DATAMODE')
|
||||
col = row.column()
|
||||
col.alignment = 'RIGHT'
|
||||
col.label(text="{:.2f}".format(item.screen_size_prop))
|
||||
|
||||
elif self.layout_type in {'GRID'}:
|
||||
layout.alignment = 'CENTER'
|
||||
layout.label(text="", icon = custom_icon)
|
||||
layout.label(text="", icon='OBJECT_DATAMODE')
|
||||
|
||||
class ArmLodListNewItem(bpy.types.Operator):
|
||||
# Add a new item to the list
|
||||
bl_idname = "arm_lodlist.new_item"
|
||||
bl_label = "Add a new item"
|
||||
bl_options = {'UNDO'}
|
||||
|
||||
def execute(self, context):
|
||||
mdata = bpy.context.object.data
|
||||
|
@ -69,10 +69,13 @@ class ArmLodListDeleteItem(bpy.types.Operator):
|
|||
# Delete the selected item from the list
|
||||
bl_idname = "arm_lodlist.delete_item"
|
||||
bl_label = "Deletes an item"
|
||||
bl_options = {'INTERNAL', 'UNDO'}
|
||||
|
||||
@classmethod
|
||||
def poll(self, context):
|
||||
def poll(cls, context):
|
||||
""" Enable if there's something in the list """
|
||||
if bpy.context.object is None:
|
||||
return False
|
||||
mdata = bpy.context.object.data
|
||||
return len(mdata.arm_lodlist) > 0
|
||||
|
||||
|
@ -98,6 +101,7 @@ class ArmLodListMoveItem(bpy.types.Operator):
|
|||
# Move an item in the list
|
||||
bl_idname = "arm_lodlist.move_item"
|
||||
bl_label = "Move an item in the list"
|
||||
bl_options = {'INTERNAL', 'UNDO'}
|
||||
direction: EnumProperty(
|
||||
items=(
|
||||
('UP', 'Up', ""),
|
||||
|
|
|
@ -1,30 +1,26 @@
|
|||
import shutil
|
||||
import bpy
|
||||
import os
|
||||
import json
|
||||
from bpy.types import Menu, Panel, UIList
|
||||
from bpy.props import *
|
||||
|
||||
class ArmTilesheetActionListItem(bpy.types.PropertyGroup):
|
||||
name: StringProperty(
|
||||
name="Name",
|
||||
description="A name for this item",
|
||||
default="Untitled")
|
||||
name="Name",
|
||||
description="A name for this item",
|
||||
default="Untitled")
|
||||
|
||||
start_prop: IntProperty(
|
||||
name="Start",
|
||||
description="A name for this item",
|
||||
default=0)
|
||||
name="Start",
|
||||
description="A name for this item",
|
||||
default=0)
|
||||
|
||||
end_prop: IntProperty(
|
||||
name="End",
|
||||
description="A name for this item",
|
||||
default=0)
|
||||
name="End",
|
||||
description="A name for this item",
|
||||
default=0)
|
||||
|
||||
loop_prop: BoolProperty(
|
||||
name="Loop",
|
||||
description="A name for this item",
|
||||
default=True)
|
||||
name="Loop",
|
||||
description="A name for this item",
|
||||
default=True)
|
||||
|
||||
class ARM_UL_TilesheetActionList(bpy.types.UIList):
|
||||
def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index):
|
||||
|
@ -52,14 +48,16 @@ class ArmTilesheetActionListNewItem(bpy.types.Operator):
|
|||
return{'FINISHED'}
|
||||
|
||||
class ArmTilesheetActionListDeleteItem(bpy.types.Operator):
|
||||
# Delete the selected item from the list
|
||||
"""Delete the selected item from the list"""
|
||||
bl_idname = "arm_tilesheetactionlist.delete_item"
|
||||
bl_label = "Deletes an item"
|
||||
|
||||
@classmethod
|
||||
def poll(self, context):
|
||||
""" Enable if there's something in the list """
|
||||
"""Enable if there's something in the list"""
|
||||
wrd = bpy.data.worlds['Arm']
|
||||
if len(wrd.arm_tilesheetlist) == 0:
|
||||
return False
|
||||
trait = wrd.arm_tilesheetlist[wrd.arm_tilesheetlist_index]
|
||||
return len(trait.arm_tilesheetactionlist) > 0
|
||||
|
||||
|
@ -78,18 +76,23 @@ class ArmTilesheetActionListDeleteItem(bpy.types.Operator):
|
|||
return{'FINISHED'}
|
||||
|
||||
class ArmTilesheetActionListMoveItem(bpy.types.Operator):
|
||||
# Move an item in the list
|
||||
"""Move an item in the list"""
|
||||
bl_idname = "arm_tilesheetactionlist.move_item"
|
||||
bl_label = "Move an item in the list"
|
||||
bl_options = {'INTERNAL'}
|
||||
|
||||
direction: EnumProperty(
|
||||
items=(
|
||||
('UP', 'Up', ""),
|
||||
('DOWN', 'Down', ""),))
|
||||
items=(
|
||||
('UP', 'Up', ""),
|
||||
('DOWN', 'Down', "")
|
||||
))
|
||||
|
||||
@classmethod
|
||||
def poll(self, context):
|
||||
""" Enable if there's something in the list. """
|
||||
"""Enable if there's something in the list"""
|
||||
wrd = bpy.data.worlds['Arm']
|
||||
if len(wrd.arm_tilesheetlist) == 0:
|
||||
return False
|
||||
trait = wrd.arm_tilesheetlist[wrd.arm_tilesheetlist_index]
|
||||
return len(trait.arm_tilesheetactionlist) > 0
|
||||
|
||||
|
@ -129,27 +132,27 @@ class ArmTilesheetActionListMoveItem(bpy.types.Operator):
|
|||
|
||||
class ArmTilesheetListItem(bpy.types.PropertyGroup):
|
||||
name: StringProperty(
|
||||
name="Name",
|
||||
description="A name for this item",
|
||||
default="Untitled")
|
||||
name="Name",
|
||||
description="A name for this item",
|
||||
default="Untitled")
|
||||
|
||||
tilesx_prop: IntProperty(
|
||||
name="Tiles X",
|
||||
description="A name for this item",
|
||||
default=0)
|
||||
name="Tiles X",
|
||||
description="A name for this item",
|
||||
default=0)
|
||||
|
||||
tilesy_prop: IntProperty(
|
||||
name="Tiles Y",
|
||||
description="A name for this item",
|
||||
default=0)
|
||||
name="Tiles Y",
|
||||
description="A name for this item",
|
||||
default=0)
|
||||
|
||||
framerate_prop: FloatProperty(
|
||||
name="Frame Rate",
|
||||
description="A name for this item",
|
||||
default=4.0)
|
||||
name="Frame Rate",
|
||||
description="A name for this item",
|
||||
default=4.0)
|
||||
|
||||
arm_tilesheetactionlist: CollectionProperty(type=ArmTilesheetActionListItem)
|
||||
arm_tilesheetactionlist_index: IntProperty(name="Index for my_list", default=0)
|
||||
arm_tilesheetactionlist_index: IntProperty(name="Index for arm_tilesheetactionlist", default=0)
|
||||
|
||||
class ARM_UL_TilesheetList(bpy.types.UIList):
|
||||
def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index):
|
||||
|
@ -162,10 +165,10 @@ class ARM_UL_TilesheetList(bpy.types.UIList):
|
|||
|
||||
elif self.layout_type in {'GRID'}:
|
||||
layout.alignment = 'CENTER'
|
||||
layout.label(text="", icon = custom_icon)
|
||||
layout.label(text="", icon=custom_icon)
|
||||
|
||||
class ArmTilesheetListNewItem(bpy.types.Operator):
|
||||
# Add a new item to the list
|
||||
"""Add a new item to the list"""
|
||||
bl_idname = "arm_tilesheetlist.new_item"
|
||||
bl_label = "Add a new item"
|
||||
|
||||
|
@ -176,7 +179,7 @@ class ArmTilesheetListNewItem(bpy.types.Operator):
|
|||
return{'FINISHED'}
|
||||
|
||||
class ArmTilesheetListDeleteItem(bpy.types.Operator):
|
||||
# Delete the selected item from the list
|
||||
"""Delete the selected item from the list"""
|
||||
bl_idname = "arm_tilesheetlist.delete_item"
|
||||
bl_label = "Deletes an item"
|
||||
|
||||
|
@ -200,13 +203,16 @@ class ArmTilesheetListDeleteItem(bpy.types.Operator):
|
|||
return{'FINISHED'}
|
||||
|
||||
class ArmTilesheetListMoveItem(bpy.types.Operator):
|
||||
# Move an item in the list
|
||||
"""Move an item in the list"""
|
||||
bl_idname = "arm_tilesheetlist.move_item"
|
||||
bl_label = "Move an item in the list"
|
||||
bl_options = {'INTERNAL'}
|
||||
|
||||
direction: EnumProperty(
|
||||
items=(
|
||||
('UP', 'Up', ""),
|
||||
('DOWN', 'Down', ""),))
|
||||
items=(
|
||||
('UP', 'Up', ""),
|
||||
('DOWN', 'Down', "")
|
||||
))
|
||||
|
||||
@classmethod
|
||||
def poll(self, context):
|
||||
|
@ -247,7 +253,6 @@ class ArmTilesheetListMoveItem(bpy.types.Operator):
|
|||
return{'FINISHED'}
|
||||
|
||||
def register():
|
||||
|
||||
bpy.utils.register_class(ArmTilesheetActionListItem)
|
||||
bpy.utils.register_class(ARM_UL_TilesheetActionList)
|
||||
bpy.utils.register_class(ArmTilesheetActionListNewItem)
|
||||
|
@ -261,7 +266,7 @@ def register():
|
|||
bpy.utils.register_class(ArmTilesheetListMoveItem)
|
||||
|
||||
bpy.types.World.arm_tilesheetlist = CollectionProperty(type=ArmTilesheetListItem)
|
||||
bpy.types.World.arm_tilesheetlist_index = IntProperty(name="Index for my_list", default=0)
|
||||
bpy.types.World.arm_tilesheetlist_index = IntProperty(name="Index for arm_tilesheetlist", default=0)
|
||||
|
||||
def unregister():
|
||||
bpy.utils.unregister_class(ArmTilesheetListItem)
|
||||
|
|
|
@ -2,6 +2,7 @@ import json
|
|||
import os
|
||||
import shutil
|
||||
import subprocess
|
||||
from typing import Union
|
||||
import webbrowser
|
||||
|
||||
from bpy.types import NodeTree
|
||||
|
@ -10,10 +11,26 @@ import bpy.utils.previews
|
|||
import arm.make as make
|
||||
from arm.props_traits_props import *
|
||||
import arm.proxy as proxy
|
||||
import arm.ui_icons as ui_icons
|
||||
import arm.utils
|
||||
import arm.write_data as write_data
|
||||
|
||||
icons_dict: bpy.utils.previews.ImagePreviewCollection
|
||||
ICON_HAXE = ui_icons.get_id('haxe')
|
||||
ICON_NODES = 'NODETREE'
|
||||
ICON_CANVAS = 'NODE_COMPOSITING'
|
||||
ICON_BUNDLED = ui_icons.get_id('bundle')
|
||||
ICON_WASM = ui_icons.get_id('wasm')
|
||||
|
||||
# Pay attention to the ID number parameter for backward compatibility!
|
||||
# This is important if the enum is reordered or the string identifier
|
||||
# is changed as the number is what's stored in the blend file
|
||||
PROP_TYPES_ENUM = [
|
||||
('Haxe Script', 'Haxe', 'Haxe script', ICON_HAXE, 0),
|
||||
('Logic Nodes', 'Nodes', 'Logic nodes (visual scripting)', ICON_NODES, 4),
|
||||
('UI Canvas', 'UI', 'User interface', ICON_CANVAS, 2),
|
||||
('Bundled Script', 'Bundled', 'Premade script with common functionality', ICON_BUNDLED, 3),
|
||||
('WebAssembly', 'Wasm', 'WebAssembly', ICON_WASM, 1)
|
||||
]
|
||||
|
||||
|
||||
def trigger_recompile(self, context):
|
||||
|
@ -61,78 +78,81 @@ def update_trait_group(self, context):
|
|||
pass
|
||||
|
||||
class ArmTraitListItem(bpy.types.PropertyGroup):
|
||||
def poll_node_trees(self, tree: NodeTree):
|
||||
"""Ensure that only logic node trees show up as node traits"""
|
||||
return tree.bl_idname == 'ArmLogicTreeType'
|
||||
|
||||
name: StringProperty(name="Name", description="A name for this item", default="")
|
||||
enabled_prop: BoolProperty(name="", description="A name for this item", default=True, update=trigger_recompile)
|
||||
is_object: BoolProperty(name="", default=True)
|
||||
fake_user: BoolProperty(name="Fake User", description="Export this trait even if it is deactivated", default=False)
|
||||
type_prop: EnumProperty(
|
||||
items = [('Haxe Script', 'Haxe', 'Haxe Script'),
|
||||
('WebAssembly', 'Wasm', 'WebAssembly'),
|
||||
('UI Canvas', 'UI', 'UI Canvas'),
|
||||
('Bundled Script', 'Bundled', 'Bundled Script'),
|
||||
('Logic Nodes', 'Nodes', 'Logic Nodes')
|
||||
],
|
||||
name = "Type")
|
||||
type_prop: EnumProperty(name="Type", items=PROP_TYPES_ENUM)
|
||||
class_name_prop: StringProperty(name="Class", description="A name for this item", default="", update=update_trait_group)
|
||||
canvas_name_prop: StringProperty(name="Canvas", description="A name for this item", default="", update=update_trait_group)
|
||||
webassembly_prop: StringProperty(name="Module", description="A name for this item", default="", update=update_trait_group)
|
||||
node_tree_prop: PointerProperty(type=NodeTree, update=update_trait_group)
|
||||
node_tree_prop: PointerProperty(type=NodeTree, update=update_trait_group, poll=poll_node_trees)
|
||||
arm_traitpropslist: CollectionProperty(type=ArmTraitPropListItem)
|
||||
arm_traitpropslist_index: IntProperty(name="Index for my_list", default=0)
|
||||
arm_traitpropswarnings: CollectionProperty(type=ArmTraitPropWarning)
|
||||
|
||||
class ARM_UL_TraitList(bpy.types.UIList):
|
||||
"""List of traits."""
|
||||
def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index):
|
||||
layout.use_property_split = False
|
||||
|
||||
custom_icon = "NONE"
|
||||
custom_icon_value = 0
|
||||
if item.type_prop == "Haxe Script":
|
||||
custom_icon_value = icons_dict["haxe"].icon_id
|
||||
custom_icon_value = ui_icons.get_id("haxe")
|
||||
elif item.type_prop == "WebAssembly":
|
||||
custom_icon_value = icons_dict["wasm"].icon_id
|
||||
custom_icon_value = ui_icons.get_id("wasm")
|
||||
elif item.type_prop == "UI Canvas":
|
||||
custom_icon = "OBJECT_DATAMODE"
|
||||
custom_icon = "NODE_COMPOSITING"
|
||||
elif item.type_prop == "Bundled Script":
|
||||
custom_icon_value = icons_dict["bundle"].icon_id
|
||||
custom_icon_value = ui_icons.get_id("bundle")
|
||||
elif item.type_prop == "Logic Nodes":
|
||||
custom_icon = 'NODETREE'
|
||||
|
||||
# Make sure your code supports all 3 layout types
|
||||
if self.layout_type in {'DEFAULT', 'COMPACT'}:
|
||||
layout.prop(item, "enabled_prop")
|
||||
row = layout.row()
|
||||
row.separator(factor=0.1)
|
||||
row.prop(item, "enabled_prop")
|
||||
# Display " " for props without a name to right-align the
|
||||
# fake_user button
|
||||
layout.label(text=item.name if item.name != "" else " ", icon=custom_icon, icon_value=custom_icon_value)
|
||||
row.label(text=item.name if item.name != "" else " ", icon=custom_icon, icon_value=custom_icon_value)
|
||||
|
||||
elif self.layout_type in {'GRID'}:
|
||||
layout.alignment = 'CENTER'
|
||||
layout.label(text="", icon=custom_icon, icon_value=custom_icon_value)
|
||||
|
||||
layout.prop(item, "fake_user", text="", icon="FAKE_USER_ON" if item.fake_user else "FAKE_USER_OFF")
|
||||
row = layout.row(align=True)
|
||||
row.prop(item, "fake_user", text="", icon="FAKE_USER_ON" if item.fake_user else "FAKE_USER_OFF")
|
||||
|
||||
class ArmTraitListNewItem(bpy.types.Operator):
|
||||
bl_idname = "arm_traitlist.new_item"
|
||||
bl_label = "New Trait Item"
|
||||
bl_label = "Add Trait"
|
||||
bl_description = "Add a new trait item to the list"
|
||||
|
||||
is_object: BoolProperty(name="Object Trait", description="Whether this is an object or scene trait", default=False)
|
||||
type_prop: EnumProperty(
|
||||
items = [('Haxe Script', 'Haxe', 'Haxe Script'),
|
||||
('WebAssembly', 'Wasm', 'WebAssembly'),
|
||||
('UI Canvas', 'UI', 'UI Canvas'),
|
||||
('Bundled Script', 'Bundled', 'Bundled Script'),
|
||||
('Logic Nodes', 'Nodes', 'Logic Nodes')
|
||||
],
|
||||
name = "Type")
|
||||
is_object: BoolProperty(name="Is Object Trait", description="Whether this trait belongs to an object or a scene", default=False)
|
||||
type_prop: EnumProperty(name="Type", items=PROP_TYPES_ENUM)
|
||||
|
||||
# Show more options when invoked from the operator search menu
|
||||
invoked_by_search: BoolProperty(name="", default=True)
|
||||
|
||||
def invoke(self, context, event):
|
||||
wm = context.window_manager
|
||||
return wm.invoke_props_dialog(self)
|
||||
return wm.invoke_props_dialog(self, width=400)
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
# Todo: show is_object property when called from operator search menu
|
||||
# layout.prop(self, "is_object")
|
||||
layout.prop(self, "type_prop", expand=True)
|
||||
|
||||
if self.invoked_by_search:
|
||||
row = layout.row()
|
||||
row.prop(self, "is_object")
|
||||
|
||||
row = layout.row()
|
||||
row.scale_y = 1.3
|
||||
row.prop(self, "type_prop", expand=True)
|
||||
|
||||
def execute(self, context):
|
||||
if self.is_object:
|
||||
|
@ -149,7 +169,7 @@ class ArmTraitListNewItem(bpy.types.Operator):
|
|||
class ArmTraitListDeleteItem(bpy.types.Operator):
|
||||
"""Delete the selected item from the list"""
|
||||
bl_idname = "arm_traitlist.delete_item"
|
||||
bl_label = "Deletes an item"
|
||||
bl_label = "Remove Trait"
|
||||
bl_options = {'INTERNAL'}
|
||||
|
||||
is_object: BoolProperty(name="", description="A name for this item", default=False)
|
||||
|
@ -582,8 +602,6 @@ class ArmNewCanvasDialog(bpy.types.Operator):
|
|||
self.canvas_name = self.canvas_name.replace(' ', '')
|
||||
write_data.write_canvasjson(self.canvas_name)
|
||||
arm.utils.fetch_script_names()
|
||||
# Todo: create new trait item when called from operator search
|
||||
# menu, then remove 'INTERNAL' from bl_options
|
||||
item = obj.arm_traitlist[obj.arm_traitlist_index]
|
||||
item.canvas_name_prop = self.canvas_name
|
||||
return {'FINISHED'}
|
||||
|
@ -635,11 +653,8 @@ class ARM_PT_TraitPanel(bpy.types.Panel):
|
|||
bl_context = "object"
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
layout.use_property_split = True
|
||||
layout.use_property_decorate = False
|
||||
obj = bpy.context.object
|
||||
draw_traits(layout, obj, is_object=True)
|
||||
draw_traits_panel(self.layout, obj, is_object=True)
|
||||
|
||||
class ARM_PT_SceneTraitPanel(bpy.types.Panel):
|
||||
bl_label = "Armory Scene Traits"
|
||||
|
@ -648,11 +663,8 @@ class ARM_PT_SceneTraitPanel(bpy.types.Panel):
|
|||
bl_context = "scene"
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
layout.use_property_split = True
|
||||
layout.use_property_decorate = False
|
||||
obj = bpy.context.scene
|
||||
draw_traits(layout, obj, is_object=False)
|
||||
draw_traits_panel(self.layout, obj, is_object=False)
|
||||
|
||||
class ARM_OT_CopyTraitsFromActive(bpy.types.Operator):
|
||||
bl_label = 'Copy Traits from Active Object'
|
||||
|
@ -728,21 +740,28 @@ class ARM_OT_CopyTraitsFromActive(bpy.types.Operator):
|
|||
|
||||
return {'INTERFACE'}
|
||||
|
||||
def draw_traits(layout, obj, is_object):
|
||||
rows = 2
|
||||
|
||||
def draw_traits_panel(layout: bpy.types.UILayout, obj: Union[bpy.types.Object, bpy.types.Scene],
|
||||
is_object: bool) -> None:
|
||||
layout.use_property_split = True
|
||||
layout.use_property_decorate = False
|
||||
|
||||
# Make the list bigger when there are a few traits
|
||||
num_rows = 2
|
||||
if len(obj.arm_traitlist) > 1:
|
||||
rows = 4
|
||||
num_rows = 4
|
||||
|
||||
row = layout.row()
|
||||
row.template_list("ARM_UL_TraitList", "The_List", obj, "arm_traitlist", obj, "arm_traitlist_index", rows=rows)
|
||||
row.template_list("ARM_UL_TraitList", "The_List", obj, "arm_traitlist", obj, "arm_traitlist_index", rows=num_rows)
|
||||
|
||||
col = row.column(align=True)
|
||||
op = col.operator("arm_traitlist.new_item", icon='ADD', text="")
|
||||
op.invoked_by_search = False
|
||||
op.is_object = is_object
|
||||
if is_object:
|
||||
op = col.operator("arm_traitlist.delete_item", icon='REMOVE', text="")#.all = False
|
||||
op = col.operator("arm_traitlist.delete_item", icon='REMOVE', text="")
|
||||
else:
|
||||
op = col.operator("arm_traitlist.delete_item_scene", icon='REMOVE', text="")#.all = False
|
||||
op = col.operator("arm_traitlist.delete_item_scene", icon='REMOVE', text="")
|
||||
op.is_object = is_object
|
||||
|
||||
if len(obj.arm_traitlist) > 1:
|
||||
|
@ -754,35 +773,29 @@ def draw_traits(layout, obj, is_object):
|
|||
op.direction = 'DOWN'
|
||||
op.is_object = is_object
|
||||
|
||||
# Draw trait specific content
|
||||
if obj.arm_traitlist_index >= 0 and len(obj.arm_traitlist) > 0:
|
||||
item = obj.arm_traitlist[obj.arm_traitlist_index]
|
||||
|
||||
row = layout.row(align=True)
|
||||
row.alignment = 'EXPAND'
|
||||
row.scale_y = 1.2
|
||||
|
||||
if item.type_prop == 'Haxe Script' or item.type_prop == 'Bundled Script':
|
||||
if item.type_prop == 'Haxe Script':
|
||||
row = layout.row(align=True)
|
||||
row.alignment = 'EXPAND'
|
||||
row.operator("arm.new_script", icon="FILE_NEW").is_object = is_object
|
||||
column = row.column(align=True)
|
||||
column.alignment = 'EXPAND'
|
||||
if item.class_name_prop == '':
|
||||
column.enabled = False
|
||||
op = column.operator("arm.edit_script", icon="FILE_SCRIPT")
|
||||
op.is_object = is_object
|
||||
op = row.operator("arm.new_script")
|
||||
op.is_object = is_object
|
||||
op = row.operator("arm.refresh_scripts", text="Refresh")
|
||||
else: # Bundled
|
||||
column.enabled = item.class_name_prop != ''
|
||||
column.operator("arm.edit_script", icon="FILE_SCRIPT").is_object = is_object
|
||||
|
||||
# Bundled scripts
|
||||
else:
|
||||
if item.class_name_prop == 'NavMesh':
|
||||
row = layout.row(align=True)
|
||||
row.alignment = 'EXPAND'
|
||||
op = layout.operator("arm.generate_navmesh")
|
||||
row = layout.row(align=True)
|
||||
row.alignment = 'EXPAND'
|
||||
column = row.column(align=True)
|
||||
column.alignment = 'EXPAND'
|
||||
if not item.class_name_prop == 'NavMesh':
|
||||
op = column.operator("arm.edit_bundled_script", icon="FILE_SCRIPT")
|
||||
op.is_object = is_object
|
||||
op = row.operator("arm.refresh_scripts", text="Refresh")
|
||||
row.operator("arm.generate_navmesh", icon="UV_VERTEXSEL")
|
||||
else:
|
||||
row.operator("arm.edit_bundled_script", icon="FILE_SCRIPT").is_object = is_object
|
||||
|
||||
row.operator("arm.refresh_scripts", text="Refresh", icon="FILE_REFRESH")
|
||||
|
||||
# Default props
|
||||
item.name = item.class_name_prop
|
||||
|
@ -797,86 +810,56 @@ def draw_traits(layout, obj, is_object):
|
|||
|
||||
elif item.type_prop == 'WebAssembly':
|
||||
item.name = item.webassembly_prop
|
||||
|
||||
row.operator("arm.new_wasm", icon="FILE_NEW")
|
||||
row.operator("arm.refresh_scripts", text="Refresh", icon="FILE_REFRESH")
|
||||
|
||||
row = layout.row()
|
||||
row.prop_search(item, "webassembly_prop", bpy.data.worlds['Arm'], "arm_wasm_list", text="Module")
|
||||
row = layout.row(align=True)
|
||||
row.alignment = 'EXPAND'
|
||||
column = row.column(align=True)
|
||||
column.alignment = 'EXPAND'
|
||||
if item.class_name_prop == '':
|
||||
column.enabled = False
|
||||
# op = column.operator("arm.edit_script", icon="FILE_SCRIPT")
|
||||
# op.is_object = is_object
|
||||
op = row.operator("arm.new_wasm")
|
||||
# op.is_object = is_object
|
||||
op = row.operator("arm.refresh_scripts", text="Refresh")
|
||||
|
||||
elif item.type_prop == 'UI Canvas':
|
||||
item.name = item.canvas_name_prop
|
||||
|
||||
row = layout.row(align=True)
|
||||
row.alignment = 'EXPAND'
|
||||
row.operator("arm.new_canvas", icon="FILE_NEW").is_object = is_object
|
||||
column = row.column(align=True)
|
||||
column.alignment = 'EXPAND'
|
||||
if item.canvas_name_prop == '':
|
||||
column.enabled = False
|
||||
op = column.operator("arm.edit_canvas", icon="FILE_SCRIPT")
|
||||
op.is_object = is_object
|
||||
op = row.operator("arm.new_canvas")
|
||||
op.is_object = is_object
|
||||
op = row.operator("arm.refresh_canvas_list", text="Refresh")
|
||||
column.enabled = item.canvas_name_prop != ''
|
||||
column.operator("arm.edit_canvas", icon="NODE_COMPOSITING").is_object = is_object
|
||||
row.operator("arm.refresh_canvas_list", text="Refresh", icon="FILE_REFRESH")
|
||||
|
||||
row = layout.row()
|
||||
row.prop_search(item, "canvas_name_prop", bpy.data.worlds['Arm'], "arm_canvas_list", text="Canvas")
|
||||
|
||||
elif item.type_prop == 'Logic Nodes':
|
||||
# Row for buttons
|
||||
row = layout.row(align=True)
|
||||
row.alignment = 'EXPAND'
|
||||
# New
|
||||
column = row.column(align=True)
|
||||
column.alignment = 'EXPAND'
|
||||
op = column.operator("arm.new_treenode", text="New Node Tree", icon="ADD")
|
||||
op.is_object = is_object
|
||||
# At least one check is active Logic Node Editor
|
||||
is_check_logic_node_editor = False
|
||||
context_screen = bpy.context.screen
|
||||
# Loop for all spaces
|
||||
if context_screen is not None:
|
||||
areas = context_screen.areas
|
||||
# Check if there is at least one active Logic Node Editor
|
||||
is_editor_active = False
|
||||
if bpy.context.screen is not None:
|
||||
areas = bpy.context.screen.areas
|
||||
for area in areas:
|
||||
for space in area.spaces:
|
||||
if space.type == 'NODE_EDITOR':
|
||||
if space.tree_type == 'ArmLogicTreeType' and space.node_tree is not None:
|
||||
is_check_logic_node_editor = True
|
||||
is_editor_active = True
|
||||
break
|
||||
if is_check_logic_node_editor:
|
||||
if is_editor_active:
|
||||
break
|
||||
# Edit
|
||||
column = row.column(align=True)
|
||||
column.alignment = 'EXPAND'
|
||||
if item.node_tree_prop is None:
|
||||
column.enabled = False
|
||||
else:
|
||||
column.enabled = is_check_logic_node_editor
|
||||
op = column.operator("arm.edit_treenode", text="Edit Node Tree", icon="NODETREE")
|
||||
op.is_object = is_object
|
||||
# Get from Node Tree Editor
|
||||
column = row.column(align=True)
|
||||
column.alignment = 'EXPAND'
|
||||
if item is None:
|
||||
column.enabled = False
|
||||
else:
|
||||
column.enabled = is_check_logic_node_editor
|
||||
op = column.operator("arm.get_treenode", text="From Node Editor", icon="IMPORT")
|
||||
op.is_object = is_object
|
||||
|
||||
# Row for search
|
||||
row.operator("arm.new_treenode", text="New Tree", icon="ADD").is_object = is_object
|
||||
|
||||
column = row.column(align=True)
|
||||
column.enabled = is_editor_active and item.node_tree_prop is not None
|
||||
column.operator("arm.edit_treenode", text="Edit Tree", icon="NODETREE").is_object = is_object
|
||||
|
||||
column = row.column(align=True)
|
||||
column.enabled = is_editor_active and item is not None
|
||||
column.operator("arm.get_treenode", text="From Editor", icon="IMPORT").is_object = is_object
|
||||
|
||||
row = layout.row()
|
||||
row.prop_search(item, "node_tree_prop", bpy.data, "node_groups", text="Tree")
|
||||
|
||||
# =====================
|
||||
# Draw trait properties
|
||||
if item.type_prop == 'Haxe Script' or item.type_prop == 'Bundled Script':
|
||||
# Props
|
||||
|
||||
if item.arm_traitpropslist:
|
||||
layout.label(text="Trait Properties:")
|
||||
if item.arm_traitpropswarnings:
|
||||
|
@ -887,13 +870,12 @@ def draw_traits(layout, obj, is_object):
|
|||
for warning in item.arm_traitpropswarnings:
|
||||
col.label(text=f'"{warning.propName}": {warning.warning}')
|
||||
|
||||
propsrow = layout.row()
|
||||
propsrows = max(len(item.arm_traitpropslist), 6)
|
||||
row = layout.row()
|
||||
row.template_list("ARM_UL_PropList", "The_List", item, "arm_traitpropslist", item, "arm_traitpropslist_index", rows=propsrows)
|
||||
|
||||
|
||||
def register():
|
||||
global icons_dict
|
||||
bpy.utils.register_class(ArmTraitListItem)
|
||||
bpy.utils.register_class(ARM_UL_TraitList)
|
||||
bpy.utils.register_class(ArmTraitListNewItem)
|
||||
|
@ -921,14 +903,8 @@ def register():
|
|||
bpy.types.Scene.arm_traitlist = CollectionProperty(type=ArmTraitListItem)
|
||||
bpy.types.Scene.arm_traitlist_index = IntProperty(name="Index for arm_traitlist", default=0)
|
||||
|
||||
icons_dict = bpy.utils.previews.new()
|
||||
icons_dir = os.path.join(os.path.dirname(__file__), "custom_icons")
|
||||
icons_dict.load("haxe", os.path.join(icons_dir, "haxe.png"), 'IMAGE')
|
||||
icons_dict.load("wasm", os.path.join(icons_dir, "wasm.png"), 'IMAGE')
|
||||
icons_dict.load("bundle", os.path.join(icons_dir, "bundle.png"), 'IMAGE')
|
||||
|
||||
def unregister():
|
||||
global icons_dict
|
||||
bpy.utils.unregister_class(ARM_OT_CopyTraitsFromActive)
|
||||
bpy.utils.unregister_class(ArmTraitListItem)
|
||||
bpy.utils.unregister_class(ARM_UL_TraitList)
|
||||
|
@ -950,4 +926,3 @@ def unregister():
|
|||
bpy.utils.unregister_class(ArmRefreshCanvasListButton)
|
||||
bpy.utils.unregister_class(ARM_PT_TraitPanel)
|
||||
bpy.utils.unregister_class(ARM_PT_SceneTraitPanel)
|
||||
bpy.utils.previews.remove(icons_dict)
|
||||
|
|
File diff suppressed because it is too large
Load diff
33
blender/arm/ui_icons.py
Normal file
33
blender/arm/ui_icons.py
Normal file
|
@ -0,0 +1,33 @@
|
|||
"""
|
||||
Blender user interface icon handling.
|
||||
"""
|
||||
import os.path
|
||||
from typing import Optional
|
||||
|
||||
import bpy.utils.previews
|
||||
|
||||
_icons_dict: Optional[bpy.utils.previews.ImagePreviewCollection] = None
|
||||
"""Dictionary of all loaded icons, or `None` if not loaded"""
|
||||
|
||||
_icons_dir = os.path.join(os.path.dirname(__file__), "custom_icons")
|
||||
"""Directory of the icon files"""
|
||||
|
||||
|
||||
def _load_icons() -> None:
|
||||
"""(Re)loads all icons"""
|
||||
global _icons_dict
|
||||
|
||||
if _icons_dict is not None:
|
||||
bpy.utils.previews.remove(_icons_dict)
|
||||
|
||||
_icons_dict = bpy.utils.previews.new()
|
||||
_icons_dict.load("bundle", os.path.join(_icons_dir, "bundle.png"), 'IMAGE')
|
||||
_icons_dict.load("haxe", os.path.join(_icons_dir, "haxe.png"), 'IMAGE')
|
||||
_icons_dict.load("wasm", os.path.join(_icons_dir, "wasm.png"), 'IMAGE')
|
||||
|
||||
|
||||
def get_id(identifier: str) -> int:
|
||||
"""Returns the icon ID from the given identifier"""
|
||||
if _icons_dict is None:
|
||||
_load_icons()
|
||||
return _icons_dict[identifier].icon_id
|
|
@ -946,7 +946,7 @@ def get_link_web_server():
|
|||
return '' if not hasattr(addon_prefs, 'link_web_server') else addon_prefs.link_web_server
|
||||
|
||||
def compare_version_blender_arm():
|
||||
return not (bpy.app.version[0] != 2 or bpy.app.version[1] != 83)
|
||||
return not (bpy.app.version[0] != 2 or bpy.app.version[1] != 93)
|
||||
|
||||
def type_name_to_type(name: str) -> bpy.types.bpy_struct:
|
||||
"""Return the Blender type given by its name, if registered."""
|
||||
|
|
|
@ -1,23 +1,87 @@
|
|||
import bpy
|
||||
from contextlib import contextmanager
|
||||
import math
|
||||
import multiprocessing
|
||||
import os
|
||||
import sys
|
||||
import subprocess
|
||||
import json
|
||||
import re
|
||||
import arm.utils
|
||||
import subprocess
|
||||
|
||||
import bpy
|
||||
|
||||
import arm.assets as assets
|
||||
import arm.log as log
|
||||
import arm.utils
|
||||
|
||||
|
||||
def add_irr_assets(output_file_irr):
|
||||
assets.add(output_file_irr + '.arm')
|
||||
|
||||
|
||||
def add_rad_assets(output_file_rad, rad_format, num_mips):
|
||||
assets.add(output_file_rad + '.' + rad_format)
|
||||
for i in range(0, num_mips):
|
||||
assets.add(output_file_rad + '_' + str(i) + '.' + rad_format)
|
||||
|
||||
# Generate probes from environment map
|
||||
def write_probes(image_filepath, disable_hdr, cached_num_mips, arm_radiance=True):
|
||||
|
||||
@contextmanager
|
||||
def setup_envmap_render():
|
||||
"""Creates a background scene for rendering environment textures.
|
||||
Use it as a context manager to automatically clean up on errors.
|
||||
"""
|
||||
rpdat = arm.utils.get_rp()
|
||||
radiance_size = int(rpdat.arm_radiance_size)
|
||||
|
||||
# Render worlds in a different scene so that there are no other
|
||||
# objects. The actual scene might be called differently if the name
|
||||
# is already taken
|
||||
scene = bpy.data.scenes.new("_arm_envmap_render")
|
||||
scene.render.engine = "CYCLES"
|
||||
scene.render.image_settings.file_format = "JPEG"
|
||||
scene.render.image_settings.quality = 100
|
||||
scene.render.resolution_x = radiance_size
|
||||
scene.render.resolution_y = radiance_size // 2
|
||||
|
||||
# Set GPU as rendering device if the user enabled it
|
||||
if bpy.context.preferences.addons["cycles"].preferences.compute_device_type == "CUDA":
|
||||
scene.cycles.device = "GPU"
|
||||
else:
|
||||
log.info('Armory: Using CPU for environment render (might be slow). Enable CUDA if possible.')
|
||||
|
||||
# One sample is enough for world background only
|
||||
scene.cycles.samples = 1
|
||||
|
||||
# Setup scene
|
||||
cam = bpy.data.cameras.new("_arm_cam_envmap_render")
|
||||
cam_obj = bpy.data.objects.new("_arm_cam_envmap_render", cam)
|
||||
scene.collection.objects.link(cam_obj)
|
||||
scene.camera = cam_obj
|
||||
|
||||
cam_obj.location = [0.0, 0.0, 0.0]
|
||||
cam.type = "PANO"
|
||||
cam.cycles.panorama_type = "EQUIRECTANGULAR"
|
||||
cam_obj.rotation_euler = [math.radians(90), 0, math.radians(-90)]
|
||||
|
||||
try:
|
||||
yield
|
||||
finally:
|
||||
bpy.data.objects.remove(cam_obj)
|
||||
bpy.data.cameras.remove(cam)
|
||||
bpy.data.scenes.remove(scene)
|
||||
|
||||
|
||||
def render_envmap(target_dir: str, world: bpy.types.World):
|
||||
"""Renders an environment texture for the given world into the
|
||||
target_dir. Use in combination with setup_envmap_render()."""
|
||||
scene = bpy.data.scenes["_arm_envmap_render"]
|
||||
scene.world = world
|
||||
|
||||
render_path = os.path.join(target_dir, f"env_{arm.utils.safesrc(world.name)}.jpg")
|
||||
scene.render.filepath = render_path
|
||||
|
||||
bpy.ops.render.render(write_still=True, scene=scene.name)
|
||||
|
||||
|
||||
def write_probes(image_filepath: str, disable_hdr: bool, cached_num_mips: int, arm_radiance=True) -> int:
|
||||
"""Generate probes from environment map and returns the mipmap count"""
|
||||
envpath = arm.utils.get_fp_build() + '/compiled/Assets/envmaps'
|
||||
|
||||
if not os.path.exists(envpath):
|
||||
|
@ -63,7 +127,7 @@ def write_probes(image_filepath, disable_hdr, cached_num_mips, arm_radiance=True
|
|||
scaled_file = output_file_rad + '.' + rad_format
|
||||
|
||||
if arm.utils.get_os() == 'win':
|
||||
output = subprocess.check_output([ \
|
||||
subprocess.check_output([
|
||||
kraffiti_path,
|
||||
'from=' + input_file,
|
||||
'to=' + scaled_file,
|
||||
|
@ -71,35 +135,35 @@ def write_probes(image_filepath, disable_hdr, cached_num_mips, arm_radiance=True
|
|||
'width=' + str(target_w),
|
||||
'height=' + str(target_h)])
|
||||
else:
|
||||
output = subprocess.check_output([ \
|
||||
kraffiti_path + \
|
||||
' from="' + input_file + '"' + \
|
||||
' to="' + scaled_file + '"' + \
|
||||
' format=' + rad_format + \
|
||||
' width=' + str(target_w) + \
|
||||
' height=' + str(target_h)], shell=True)
|
||||
subprocess.check_output([
|
||||
kraffiti_path
|
||||
+ ' from="' + input_file + '"'
|
||||
+ ' to="' + scaled_file + '"'
|
||||
+ ' format=' + rad_format
|
||||
+ ' width=' + str(target_w)
|
||||
+ ' height=' + str(target_h)], shell=True)
|
||||
|
||||
# Irradiance spherical harmonics
|
||||
if arm.utils.get_os() == 'win':
|
||||
subprocess.call([ \
|
||||
subprocess.call([
|
||||
cmft_path,
|
||||
'--input', scaled_file,
|
||||
'--filter', 'shcoeffs',
|
||||
'--outputNum', '1',
|
||||
'--output0', output_file_irr])
|
||||
else:
|
||||
subprocess.call([ \
|
||||
cmft_path + \
|
||||
' --input ' + '"' + scaled_file + '"' + \
|
||||
' --filter shcoeffs' + \
|
||||
' --outputNum 1' + \
|
||||
' --output0 ' + '"' + output_file_irr + '"'], shell=True)
|
||||
subprocess.call([
|
||||
cmft_path
|
||||
+ ' --input ' + '"' + scaled_file + '"'
|
||||
+ ' --filter shcoeffs'
|
||||
+ ' --outputNum 1'
|
||||
+ ' --output0 ' + '"' + output_file_irr + '"'], shell=True)
|
||||
|
||||
sh_to_json(output_file_irr)
|
||||
add_irr_assets(output_file_irr)
|
||||
|
||||
# Mip-mapped radiance
|
||||
if arm_radiance == False:
|
||||
if not arm_radiance:
|
||||
return cached_num_mips
|
||||
|
||||
# 4096 = 256 face
|
||||
|
@ -200,37 +264,37 @@ def write_probes(image_filepath, disable_hdr, cached_num_mips, arm_radiance=True
|
|||
if disable_hdr is True:
|
||||
for f in generated_files:
|
||||
if arm.utils.get_os() == 'win':
|
||||
subprocess.call([ \
|
||||
subprocess.call([
|
||||
kraffiti_path,
|
||||
'from=' + f + '.hdr',
|
||||
'to=' + f + '.jpg',
|
||||
'format=jpg'])
|
||||
else:
|
||||
subprocess.call([ \
|
||||
kraffiti_path + \
|
||||
' from="' + f + '.hdr"' + \
|
||||
' to="' + f + '.jpg"' + \
|
||||
' format=jpg'], shell=True)
|
||||
subprocess.call([
|
||||
kraffiti_path
|
||||
+ ' from="' + f + '.hdr"'
|
||||
+ ' to="' + f + '.jpg"'
|
||||
+ ' format=jpg'], shell=True)
|
||||
os.remove(f + '.hdr')
|
||||
|
||||
# Scale from (4x2 to 1x1>
|
||||
for i in range (0, 2):
|
||||
for i in range(0, 2):
|
||||
last = generated_files[-1]
|
||||
out = output_file_rad + '_' + str(mip_count + i)
|
||||
if arm.utils.get_os() == 'win':
|
||||
subprocess.call([ \
|
||||
subprocess.call([
|
||||
kraffiti_path,
|
||||
'from=' + last + '.' + rad_format,
|
||||
'to=' + out + '.' + rad_format,
|
||||
'scale=0.5',
|
||||
'format=' + rad_format], shell=True)
|
||||
else:
|
||||
subprocess.call([ \
|
||||
kraffiti_path + \
|
||||
' from=' + '"' + last + '.' + rad_format + '"' + \
|
||||
' to=' + '"' + out + '.' + rad_format + '"' + \
|
||||
' scale=0.5' + \
|
||||
' format=' + rad_format], shell=True)
|
||||
subprocess.call([
|
||||
kraffiti_path
|
||||
+ ' from=' + '"' + last + '.' + rad_format + '"'
|
||||
+ ' to=' + '"' + out + '.' + rad_format + '"'
|
||||
+ ' scale=0.5'
|
||||
+ ' format=' + rad_format], shell=True)
|
||||
generated_files.append(out)
|
||||
|
||||
mip_count += 2
|
||||
|
@ -239,6 +303,7 @@ def write_probes(image_filepath, disable_hdr, cached_num_mips, arm_radiance=True
|
|||
|
||||
return mip_count
|
||||
|
||||
|
||||
def sh_to_json(sh_file):
|
||||
"""Parse sh coefs produced by cmft into json array"""
|
||||
with open(sh_file + '.c') as f:
|
||||
|
@ -251,6 +316,8 @@ def sh_to_json(sh_file):
|
|||
parse_band_floats(irradiance_floats, band0_line)
|
||||
parse_band_floats(irradiance_floats, band1_line)
|
||||
parse_band_floats(irradiance_floats, band2_line)
|
||||
for i in range(0, len(irradiance_floats)):
|
||||
irradiance_floats[i] /= 2
|
||||
|
||||
sh_json = {'irradiance': irradiance_floats}
|
||||
ext = '.arm' if bpy.data.worlds['Arm'].arm_minimize else ''
|
||||
|
@ -259,15 +326,26 @@ def sh_to_json(sh_file):
|
|||
# Clean up .c
|
||||
os.remove(sh_file + '.c')
|
||||
|
||||
|
||||
def parse_band_floats(irradiance_floats, band_line):
|
||||
string_floats = re.findall(r'[-+]?\d*\.\d+|\d+', band_line)
|
||||
string_floats = string_floats[1:] # Remove 'Band 0/1/2' number
|
||||
string_floats = string_floats[1:] # Remove 'Band 0/1/2' number
|
||||
for s in string_floats:
|
||||
irradiance_floats.append(float(s))
|
||||
|
||||
|
||||
def write_sky_irradiance(base_name):
|
||||
# Hosek spherical harmonics
|
||||
irradiance_floats = [1.5519331988822218,2.3352207154503266,2.997277451988076,0.2673894962434794,0.4305630474135794,0.11331825259716752,-0.04453633521758638,-0.038753175134160295,-0.021302768541875794,0.00055858020486499,0.000371654770334503,0.000126606145406403,-0.000135708721978705,-0.000787399554583089,-0.001550090690860059,0.021947399048903773,0.05453650591711572,0.08783641266630278,0.17053593578630663,0.14734127083304463,0.07775404698816404,-2.6924363189795e-05,-7.9350169701934e-05,-7.559914435231e-05,0.27035455385870993,0.23122918445556914,0.12158817295211832]
|
||||
irradiance_floats = [
|
||||
1.5519331988822218, 2.3352207154503266, 2.997277451988076,
|
||||
0.2673894962434794, 0.4305630474135794, 0.11331825259716752,
|
||||
-0.04453633521758638, -0.038753175134160295, -0.021302768541875794,
|
||||
0.00055858020486499, 0.000371654770334503, 0.000126606145406403,
|
||||
-0.000135708721978705, -0.000787399554583089, -0.001550090690860059,
|
||||
0.021947399048903773, 0.05453650591711572, 0.08783641266630278,
|
||||
0.17053593578630663, 0.14734127083304463, 0.07775404698816404,
|
||||
-2.6924363189795e-05, -7.9350169701934e-05, -7.559914435231e-05,
|
||||
0.27035455385870993, 0.23122918445556914, 0.12158817295211832]
|
||||
for i in range(0, len(irradiance_floats)):
|
||||
irradiance_floats[i] /= 2
|
||||
|
||||
|
@ -282,6 +360,7 @@ def write_sky_irradiance(base_name):
|
|||
|
||||
assets.add(output_file + '.arm')
|
||||
|
||||
|
||||
def write_color_irradiance(base_name, col):
|
||||
"""Constant color irradiance"""
|
||||
# Adjust to Cycles
|
||||
|
|
Loading…
Reference in a new issue