Merge branch 'master' into blender2.9
# Conflicts: # blender/arm/material/shader.py # blender/arm/props_collision_filter_mask.py # blender/arm/props_ui.py
This commit is contained in:
commit
f64419dd06
31
Shaders/custom_mat_presets/custom_mat_deferred.frag.glsl
Normal file
31
Shaders/custom_mat_presets/custom_mat_deferred.frag.glsl
Normal file
|
@ -0,0 +1,31 @@
|
|||
#version 450
|
||||
|
||||
// Include functions for gbuffer operations (packFloat2() etc.)
|
||||
#include "../std/gbuffer.glsl"
|
||||
|
||||
// World-space normal from the vertex shader stage
|
||||
in vec3 wnormal;
|
||||
|
||||
// Gbuffer output. Deferred rendering uses the following layout:
|
||||
// [0]: normal x normal y roughness metallic/matID
|
||||
// [1]: base color r base color g base color b occlusion/specular
|
||||
out vec4 fragColor[2];
|
||||
|
||||
void main() {
|
||||
// Pack normals into 2 components to fit into the gbuffer
|
||||
vec3 n = normalize(wnormal);
|
||||
n /= (abs(n.x) + abs(n.y) + abs(n.z));
|
||||
n.xy = n.z >= 0.0 ? n.xy : octahedronWrap(n.xy);
|
||||
|
||||
// Define PBR material values
|
||||
vec3 basecol = vec3(1.0);
|
||||
float roughness = 0.0;
|
||||
float metallic = 0.0;
|
||||
float occlusion = 1.0;
|
||||
float specular = 1.0;
|
||||
uint materialId = 0;
|
||||
|
||||
// Store in gbuffer (see layout table above)
|
||||
fragColor[0] = vec4(n.xy, roughness, packFloatInt16(metallic, materialId));
|
||||
fragColor[1] = vec4(basecol.rgb, packFloat2(occlusion, specular));
|
||||
}
|
20
Shaders/custom_mat_presets/custom_mat_deferred.vert.glsl
Normal file
20
Shaders/custom_mat_presets/custom_mat_deferred.vert.glsl
Normal file
|
@ -0,0 +1,20 @@
|
|||
#version 450
|
||||
|
||||
// World to view projection matrix to correctly position the vertex on screen
|
||||
uniform mat4 WVP;
|
||||
// Matrix to transform normals from local into world space
|
||||
uniform mat3 N;
|
||||
|
||||
// Position and normal vectors of the current vertex in local space
|
||||
// Armory packs the vertex data to preserve memory, so nor.z values are
|
||||
// saved in pos.w
|
||||
in vec4 pos; // pos.xyz, nor.w
|
||||
in vec2 nor; // nor.xy
|
||||
|
||||
// Normal vector in world space
|
||||
out vec3 wnormal;
|
||||
|
||||
void main() {
|
||||
wnormal = normalize(N * vec3(nor.xy, pos.w));
|
||||
gl_Position = WVP * vec4(pos.xyz, 1.0);
|
||||
}
|
9
Shaders/custom_mat_presets/custom_mat_forward.frag.glsl
Normal file
9
Shaders/custom_mat_presets/custom_mat_forward.frag.glsl
Normal file
|
@ -0,0 +1,9 @@
|
|||
#version 450
|
||||
|
||||
// Color of each fragment on the screen
|
||||
out vec4 fragColor;
|
||||
|
||||
void main() {
|
||||
// Shadeless white color
|
||||
fragColor = vec4(1.0);
|
||||
}
|
11
Shaders/custom_mat_presets/custom_mat_forward.vert.glsl
Normal file
11
Shaders/custom_mat_presets/custom_mat_forward.vert.glsl
Normal file
|
@ -0,0 +1,11 @@
|
|||
#version 450
|
||||
|
||||
// World to view projection matrix to correctly position the vertex on screen
|
||||
uniform mat4 WVP;
|
||||
|
||||
// Position vector of the current vertex in local space
|
||||
in vec3 pos;
|
||||
|
||||
void main() {
|
||||
gl_Position = WVP * vec4(pos, 1.0);
|
||||
}
|
|
@ -2,7 +2,6 @@
|
|||
|
||||
#include "compiled.inc"
|
||||
#include "std/gbuffer.glsl"
|
||||
#include "std/light.glsl"
|
||||
#ifdef _Clusters
|
||||
#include "std/clusters.glsl"
|
||||
#endif
|
||||
|
@ -22,6 +21,7 @@
|
|||
uniform sampler2D gbufferD;
|
||||
uniform sampler2D gbuffer0;
|
||||
uniform sampler2D gbuffer1;
|
||||
uniform sampler2D gbuffer2;
|
||||
|
||||
#ifdef _VoxelAOvar
|
||||
uniform sampler3D voxels;
|
||||
|
@ -80,14 +80,11 @@ uniform mat4 invVP;
|
|||
#ifdef _ShadowMap
|
||||
#ifdef _SinglePoint
|
||||
//!uniform sampler2DShadow shadowMapSpot[1];
|
||||
//!uniform mat4 LWVPSpot0;
|
||||
//!uniform mat4 LWVPSpot[1];
|
||||
#endif
|
||||
#ifdef _Clusters
|
||||
//!uniform sampler2DShadow shadowMapSpot[4];
|
||||
//!uniform mat4 LWVPSpot0;
|
||||
//!uniform mat4 LWVPSpot1;
|
||||
//!uniform mat4 LWVPSpot2;
|
||||
//!uniform mat4 LWVPSpot3;
|
||||
//!uniform mat4 LWVPSpotArray[4];
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
@ -97,7 +94,7 @@ uniform vec3 eye;
|
|||
uniform vec3 eyeLook;
|
||||
|
||||
#ifdef _Clusters
|
||||
uniform vec4 lightsArray[maxLights * 2];
|
||||
uniform vec4 lightsArray[maxLights * 3];
|
||||
#ifdef _Spot
|
||||
uniform vec4 lightsArraySpot[maxLights];
|
||||
#endif
|
||||
|
@ -109,21 +106,36 @@ uniform vec2 cameraPlane;
|
|||
#ifdef _SinglePoint
|
||||
#ifdef _Spot
|
||||
//!uniform sampler2DShadow shadowMapSpot[1];
|
||||
//!uniform mat4 LWVPSpot0;
|
||||
//!uniform mat4 LWVPSpot[1];
|
||||
#else
|
||||
//!uniform samplerCubeShadow shadowMapPoint[1];
|
||||
//!uniform vec2 lightProj;
|
||||
#endif
|
||||
#endif
|
||||
#ifdef _Clusters
|
||||
//!uniform samplerCubeShadow shadowMapPoint[4];
|
||||
#ifdef _ShadowMapAtlas
|
||||
#ifdef _SingleAtlas
|
||||
uniform sampler2DShadow shadowMapAtlas;
|
||||
#endif
|
||||
#endif
|
||||
#ifdef _ShadowMapAtlas
|
||||
#ifndef _SingleAtlas
|
||||
//!uniform sampler2DShadow shadowMapAtlasPoint;
|
||||
#endif
|
||||
//!uniform vec4 pointLightDataArray[4];
|
||||
#else
|
||||
//!uniform samplerCubeShadow shadowMapPoint[4];
|
||||
#endif
|
||||
//!uniform vec2 lightProj;
|
||||
#ifdef _Spot
|
||||
//!uniform sampler2DShadow shadowMapSpot[4];
|
||||
//!uniform mat4 LWVPSpot0;
|
||||
//!uniform mat4 LWVPSpot1;
|
||||
//!uniform mat4 LWVPSpot2;
|
||||
//!uniform mat4 LWVPSpot3;
|
||||
#ifdef _ShadowMapAtlas
|
||||
#ifndef _SingleAtlas
|
||||
//!uniform sampler2DShadow shadowMapAtlasSpot;
|
||||
#endif
|
||||
#else
|
||||
//!uniform sampler2DShadow shadowMapSpot[4];
|
||||
#endif
|
||||
//!uniform mat4 LWVPSpotArray[4];
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
@ -132,7 +144,13 @@ uniform vec2 cameraPlane;
|
|||
uniform vec3 sunDir;
|
||||
uniform vec3 sunCol;
|
||||
#ifdef _ShadowMap
|
||||
#ifdef _ShadowMapAtlas
|
||||
#ifndef _SingleAtlas
|
||||
uniform sampler2DShadow shadowMapAtlasSun;
|
||||
#endif
|
||||
#else
|
||||
uniform sampler2DShadow shadowMap;
|
||||
#endif
|
||||
uniform float shadowsBias;
|
||||
#ifdef _CSM
|
||||
//!uniform vec4 casData[shadowmapCascades * 4 + 4];
|
||||
|
@ -159,6 +177,8 @@ uniform sampler2D texClouds;
|
|||
uniform float time;
|
||||
#endif
|
||||
|
||||
#include "std/light.glsl"
|
||||
|
||||
in vec2 texCoord;
|
||||
in vec3 viewRay;
|
||||
out vec4 fragColor;
|
||||
|
@ -186,6 +206,8 @@ void main() {
|
|||
vec3 v = normalize(eye - p);
|
||||
float dotNV = max(dot(n, v), 0.0);
|
||||
|
||||
vec4 g2 = textureLod(gbuffer2, texCoord, 0.0);
|
||||
|
||||
#ifdef _MicroShadowing
|
||||
occspec.x = mix(1.0, occspec.x, dotNV); // AO Fresnel
|
||||
#endif
|
||||
|
@ -196,7 +218,15 @@ void main() {
|
|||
|
||||
// Envmap
|
||||
#ifdef _Irr
|
||||
|
||||
vec3 envl = shIrradiance(n, shirr);
|
||||
|
||||
if (g2.b < 0.5) {
|
||||
envl = envl;
|
||||
} else {
|
||||
envl = vec3(1.0);
|
||||
}
|
||||
|
||||
#ifdef _EnvTex
|
||||
envl /= PI;
|
||||
#endif
|
||||
|
@ -289,10 +319,32 @@ void main() {
|
|||
|
||||
#ifdef _ShadowMap
|
||||
#ifdef _CSM
|
||||
svisibility = shadowTestCascade(shadowMap, eye, p + n * shadowsBias * 10, shadowsBias);
|
||||
svisibility = shadowTestCascade(
|
||||
#ifdef _ShadowMapAtlas
|
||||
#ifndef _SingleAtlas
|
||||
shadowMapAtlasSun
|
||||
#else
|
||||
shadowMapAtlas
|
||||
#endif
|
||||
#else
|
||||
shadowMap
|
||||
#endif
|
||||
, eye, p + n * shadowsBias * 10, shadowsBias
|
||||
);
|
||||
#else
|
||||
vec4 lPos = LWVP * vec4(p + n * shadowsBias * 100, 1.0);
|
||||
if (lPos.w > 0.0) svisibility = shadowTest(shadowMap, lPos.xyz / lPos.w, shadowsBias);
|
||||
vec4 lPos = LWVP * vec4(p + n * shadowsBias * 100, 1.0);
|
||||
if (lPos.w > 0.0) svisibility = shadowTest(
|
||||
#ifdef _ShadowMapAtlas
|
||||
#ifndef _SingleAtlas
|
||||
shadowMapAtlasSun
|
||||
#else
|
||||
shadowMapAtlas
|
||||
#endif
|
||||
#else
|
||||
shadowMap
|
||||
#endif
|
||||
, lPos.xyz / lPos.w, shadowsBias
|
||||
);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
@ -335,7 +387,18 @@ void main() {
|
|||
int casi, casindex;
|
||||
mat4 LWVP = getCascadeMat(distance(eye, p), casi, casindex);
|
||||
#endif
|
||||
fragColor.rgb += fragColor.rgb * SSSSTransmittance(LWVP, p, n, sunDir, lightPlane.y, shadowMap);
|
||||
fragColor.rgb += fragColor.rgb * SSSSTransmittance(
|
||||
LWVP, p, n, sunDir, lightPlane.y,
|
||||
#ifdef _ShadowMapAtlas
|
||||
#ifndef _SingleAtlas
|
||||
shadowMapAtlasSun
|
||||
#else
|
||||
shadowMapAtlas
|
||||
#endif
|
||||
#else
|
||||
shadowMap
|
||||
#endif
|
||||
);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -393,18 +456,19 @@ void main() {
|
|||
n,
|
||||
v,
|
||||
dotNV,
|
||||
lightsArray[li * 2].xyz, // lp
|
||||
lightsArray[li * 2 + 1].xyz, // lightCol
|
||||
lightsArray[li * 3].xyz, // lp
|
||||
lightsArray[li * 3 + 1].xyz, // lightCol
|
||||
albedo,
|
||||
roughness,
|
||||
occspec.y,
|
||||
f0
|
||||
#ifdef _ShadowMap
|
||||
, li, lightsArray[li * 2].w, true // bias
|
||||
// light index, shadow bias, cast_shadows
|
||||
, li, lightsArray[li * 3 + 2].x, lightsArray[li * 3 + 2].z != 0.0
|
||||
#endif
|
||||
#ifdef _Spot
|
||||
, lightsArray[li * 2 + 1].w != 0.0
|
||||
, lightsArray[li * 2 + 1].w // cutoff
|
||||
, lightsArray[li * 3 + 2].y != 0.0
|
||||
, lightsArray[li * 3 + 2].y // cutoff
|
||||
, lightsArraySpot[li].w // cutoff - exponent
|
||||
, lightsArraySpot[li].xyz // spotDir
|
||||
#endif
|
||||
|
|
|
@ -99,7 +99,7 @@
|
|||
},
|
||||
{
|
||||
"name": "LWVP",
|
||||
"link": "_biasLightWorldViewProjectionMatrix",
|
||||
"link": "_biasLightWorldViewProjectionMatrixSun",
|
||||
"ifndef": ["_CSM"],
|
||||
"ifdef": ["_Sun", "_ShadowMap"]
|
||||
},
|
||||
|
@ -199,43 +199,37 @@
|
|||
"ifdef": ["_SinglePoint", "_Spot"]
|
||||
},
|
||||
{
|
||||
"name": "LWVPSpot0",
|
||||
"name": "LWVPSpotArray",
|
||||
"link": "_biasLightWorldViewProjectionMatrixSpotArray",
|
||||
"ifdef": ["_Clusters", "_ShadowMap", "_Spot"]
|
||||
},
|
||||
{
|
||||
"name": "pointLightDataArray",
|
||||
"link": "_pointLightsAtlasArray",
|
||||
"ifdef": ["_Clusters", "_ShadowMap", "_ShadowMapAtlas"]
|
||||
},
|
||||
{
|
||||
"name": "LWVPSpot[0]",
|
||||
"link": "_biasLightWorldViewProjectionMatrixSpot0",
|
||||
"ifdef": ["_Spot", "_ShadowMap"]
|
||||
"ifndef": ["_ShadowMapAtlas"],
|
||||
"ifdef": ["_LTC", "_ShadowMap"]
|
||||
},
|
||||
{
|
||||
"name": "LWVPSpot1",
|
||||
"name": "LWVPSpot[1]",
|
||||
"link": "_biasLightWorldViewProjectionMatrixSpot1",
|
||||
"ifdef": ["_Spot", "_ShadowMap"]
|
||||
"ifndef": ["_ShadowMapAtlas"],
|
||||
"ifdef": ["_LTC", "_ShadowMap"]
|
||||
},
|
||||
{
|
||||
"name": "LWVPSpot2",
|
||||
"name": "LWVPSpot[2]",
|
||||
"link": "_biasLightWorldViewProjectionMatrixSpot2",
|
||||
"ifdef": ["_Spot", "_ShadowMap"]
|
||||
"ifndef": ["_ShadowMapAtlas"],
|
||||
"ifdef": ["_LTC", "_ShadowMap"]
|
||||
},
|
||||
{
|
||||
"name": "LWVPSpot3",
|
||||
"link": "_biasLightWorldViewProjectionMatrixSpot3",
|
||||
"ifdef": ["_Spot", "_ShadowMap"]
|
||||
},
|
||||
{
|
||||
"name": "LWVPSpot0",
|
||||
"link": "_biasLightWorldViewProjectionMatrixSpot0",
|
||||
"ifdef": ["_LTC", "_ShadowMap"]
|
||||
},
|
||||
{
|
||||
"name": "LWVPSpot1",
|
||||
"link": "_biasLightWorldViewProjectionMatrixSpot1",
|
||||
"ifdef": ["_LTC", "_ShadowMap"]
|
||||
},
|
||||
{
|
||||
"name": "LWVPSpot2",
|
||||
"link": "_biasLightWorldViewProjectionMatrixSpot2",
|
||||
"ifdef": ["_LTC", "_ShadowMap"]
|
||||
},
|
||||
{
|
||||
"name": "LWVPSpot3",
|
||||
"name": "LWVPSpot[3]",
|
||||
"link": "_biasLightWorldViewProjectionMatrixSpot3",
|
||||
"ifndef": ["_ShadowMapAtlas"],
|
||||
"ifdef": ["_LTC", "_ShadowMap"]
|
||||
}
|
||||
],
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
#include "compiled.inc"
|
||||
#include "std/gbuffer.glsl"
|
||||
#include "std/math.glsl"
|
||||
#include "std/light_mobile.glsl"
|
||||
#ifdef _Clusters
|
||||
#include "std/clusters.glsl"
|
||||
#endif
|
||||
|
@ -30,12 +29,15 @@ uniform int envmapNumMipmaps;
|
|||
uniform vec3 backgroundCol;
|
||||
#endif
|
||||
|
||||
#ifdef _SMSizeUniform
|
||||
//!uniform vec2 smSizeUniform;
|
||||
#endif
|
||||
uniform vec2 cameraProj;
|
||||
uniform vec3 eye;
|
||||
uniform vec3 eyeLook;
|
||||
|
||||
#ifdef _Clusters
|
||||
uniform vec4 lightsArray[maxLights * 2];
|
||||
uniform vec4 lightsArray[maxLights * 3];
|
||||
#ifdef _Spot
|
||||
uniform vec4 lightsArraySpot[maxLights];
|
||||
#endif
|
||||
|
@ -47,21 +49,36 @@ uniform vec2 cameraPlane;
|
|||
#ifdef _SinglePoint
|
||||
#ifdef _Spot
|
||||
//!uniform sampler2DShadow shadowMapSpot[1];
|
||||
//!uniform mat4 LWVPSpot0;
|
||||
//!uniform mat4 LWVPSpot[1];
|
||||
#else
|
||||
//!uniform samplerCubeShadow shadowMapPoint[1];
|
||||
//!uniform vec2 lightProj;
|
||||
#endif
|
||||
#endif
|
||||
#ifdef _Clusters
|
||||
//!uniform samplerCubeShadow shadowMapPoint[4];
|
||||
#ifdef _ShadowMapAtlas
|
||||
#ifdef _SingleAtlas
|
||||
uniform sampler2DShadow shadowMapAtlas;
|
||||
#endif
|
||||
#endif
|
||||
#ifdef _ShadowMapAtlas
|
||||
#ifndef _SingleAtlas
|
||||
//!uniform sampler2DShadow shadowMapAtlasPoint;
|
||||
#endif
|
||||
//!uniform vec4 pointLightDataArray[4];
|
||||
#else
|
||||
//!uniform samplerCubeShadow shadowMapPoint[4];
|
||||
#endif
|
||||
//!uniform vec2 lightProj;
|
||||
#ifdef _Spot
|
||||
//!uniform sampler2DShadow shadowMapSpot[4];
|
||||
//!uniform mat4 LWVPSpot0;
|
||||
//!uniform mat4 LWVPSpot1;
|
||||
//!uniform mat4 LWVPSpot2;
|
||||
//!uniform mat4 LWVPSpot3;
|
||||
#ifdef _ShadowMapAtlas
|
||||
#ifndef _SingleAtlas
|
||||
//!uniform sampler2DShadow shadowMapAtlasSpot;
|
||||
#endif
|
||||
#else
|
||||
//!uniform sampler2DShadow shadowMapSpot[4];
|
||||
#endif
|
||||
//!uniform mat4 LWVPSpotArray[4];
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
@ -70,7 +87,13 @@ uniform vec2 cameraPlane;
|
|||
uniform vec3 sunDir;
|
||||
uniform vec3 sunCol;
|
||||
#ifdef _ShadowMap
|
||||
#ifdef _ShadowMapAtlas
|
||||
#ifndef _SingleAtlas
|
||||
uniform sampler2DShadow shadowMapAtlasSun;
|
||||
#endif
|
||||
#else
|
||||
uniform sampler2DShadow shadowMap;
|
||||
#endif
|
||||
uniform float shadowsBias;
|
||||
#ifdef _CSM
|
||||
//!uniform vec4 casData[shadowmapCascades * 4 + 4];
|
||||
|
@ -90,6 +113,8 @@ uniform float pointBias;
|
|||
#endif
|
||||
#endif
|
||||
|
||||
#include "std/light_mobile.glsl"
|
||||
|
||||
in vec2 texCoord;
|
||||
in vec3 viewRay;
|
||||
out vec4 fragColor;
|
||||
|
@ -168,10 +193,32 @@ void main() {
|
|||
|
||||
#ifdef _ShadowMap
|
||||
#ifdef _CSM
|
||||
svisibility = shadowTestCascade(shadowMap, eye, p + n * shadowsBias * 10, shadowsBias, shadowmapSize * vec2(shadowmapCascades, 1.0));
|
||||
svisibility = shadowTestCascade(
|
||||
#ifdef _ShadowMapAtlas
|
||||
#ifndef _SingleAtlas
|
||||
shadowMapAtlasSun
|
||||
#else
|
||||
shadowMapAtlas
|
||||
#endif
|
||||
#else
|
||||
shadowMap
|
||||
#endif
|
||||
, eye, p + n * shadowsBias * 10, shadowsBias
|
||||
);
|
||||
#else
|
||||
vec4 lPos = LWVP * vec4(p + n * shadowsBias * 100, 1.0);
|
||||
if (lPos.w > 0.0) svisibility = shadowTest(shadowMap, lPos.xyz / lPos.w, shadowsBias, shadowmapSize);
|
||||
vec4 lPos = LWVP * vec4(p + n * shadowsBias * 100, 1.0);
|
||||
if (lPos.w > 0.0) svisibility = shadowTest(
|
||||
#ifdef _ShadowMapAtlas
|
||||
#ifndef _SingleAtlas
|
||||
shadowMapAtlasSun
|
||||
#else
|
||||
shadowMapAtlas
|
||||
#endif
|
||||
#else
|
||||
shadowMap
|
||||
#endif
|
||||
, lPos.xyz / lPos.w, shadowsBias
|
||||
);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
@ -211,18 +258,19 @@ void main() {
|
|||
n,
|
||||
v,
|
||||
dotNV,
|
||||
lightsArray[li * 2].xyz, // lp
|
||||
lightsArray[li * 2 + 1].xyz, // lightCol
|
||||
lightsArray[li * 3].xyz, // lp
|
||||
lightsArray[li * 3 + 1].xyz, // lightCol
|
||||
albedo,
|
||||
roughness,
|
||||
occspec.y,
|
||||
f0
|
||||
#ifdef _ShadowMap
|
||||
, li, lightsArray[li * 2].w, true // bias
|
||||
// light index, shadow bias, cast_shadows
|
||||
, li, lightsArray[li * 3 + 2].x, lightsArray[li * 3 + 2].z != 0.0
|
||||
#endif
|
||||
#ifdef _Spot
|
||||
, lightsArray[li * 2 + 1].w != 0.0
|
||||
, lightsArray[li * 2 + 1].w // cutoff
|
||||
, lightsArray[li * 3 + 2].y != 0.0
|
||||
, lightsArray[li * 3 + 2].y // cutoff
|
||||
, lightsArraySpot[li].w // cutoff - exponent
|
||||
, lightsArraySpot[li].xyz // spotDir
|
||||
#endif
|
||||
|
|
|
@ -88,7 +88,7 @@
|
|||
},
|
||||
{
|
||||
"name": "LWVP",
|
||||
"link": "_biasLightWorldViewProjectionMatrix",
|
||||
"link": "_biasLightWorldViewProjectionMatrixSun",
|
||||
"ifndef": ["_CSM"],
|
||||
"ifdef": ["_Sun", "_ShadowMap"]
|
||||
},
|
||||
|
@ -107,6 +107,11 @@
|
|||
"link": "_viewProjectionMatrix",
|
||||
"ifdef": ["_SSRS"]
|
||||
},
|
||||
{
|
||||
"name": "smSizeUniform",
|
||||
"link": "_shadowMapSize",
|
||||
"ifdef": ["_SMSizeUniform"]
|
||||
},
|
||||
{
|
||||
"name": "lightProj",
|
||||
"link": "_lightPlaneProj",
|
||||
|
@ -138,24 +143,38 @@
|
|||
"ifdef": ["_SinglePoint", "_Spot"]
|
||||
},
|
||||
{
|
||||
"name": "LWVPSpot0",
|
||||
"name": "LWVPSpotArray",
|
||||
"link": "_biasLightWorldViewProjectionMatrixSpotArray",
|
||||
"ifdef": ["_Clusters", "_ShadowMap", "_Spot"]
|
||||
},
|
||||
{
|
||||
"name": "pointLightDataArray",
|
||||
"link": "_pointLightsAtlasArray",
|
||||
"ifdef": ["_Clusters", "_ShadowMap", "_ShadowMapAtlas"]
|
||||
},
|
||||
{
|
||||
"name": "LWVPSpot[0]",
|
||||
"link": "_biasLightWorldViewProjectionMatrixSpot0",
|
||||
"ifdef": ["_Spot", "_ShadowMap"]
|
||||
"ifndef": ["_ShadowMapAtlas"],
|
||||
"ifdef": ["_LTC", "_ShadowMap"]
|
||||
},
|
||||
{
|
||||
"name": "LWVPSpot1",
|
||||
"name": "LWVPSpot[1]",
|
||||
"link": "_biasLightWorldViewProjectionMatrixSpot1",
|
||||
"ifdef": ["_Spot", "_ShadowMap"]
|
||||
"ifndef": ["_ShadowMapAtlas"],
|
||||
"ifdef": ["_LTC", "_ShadowMap"]
|
||||
},
|
||||
{
|
||||
"name": "LWVPSpot2",
|
||||
"name": "LWVPSpot[2]",
|
||||
"link": "_biasLightWorldViewProjectionMatrixSpot2",
|
||||
"ifdef": ["_Spot", "_ShadowMap"]
|
||||
"ifndef": ["_ShadowMapAtlas"],
|
||||
"ifdef": ["_LTC", "_ShadowMap"]
|
||||
},
|
||||
{
|
||||
"name": "LWVPSpot3",
|
||||
"name": "LWVPSpot[3]",
|
||||
"link": "_biasLightWorldViewProjectionMatrixSpot3",
|
||||
"ifdef": ["_Spot", "_ShadowMap"]
|
||||
"ifndef": ["_ShadowMapAtlas"],
|
||||
"ifdef": ["_LTC", "_ShadowMap"]
|
||||
}
|
||||
],
|
||||
"vertex_shader": "../include/pass_viewray.vert.glsl",
|
||||
|
|
|
@ -1,7 +1,4 @@
|
|||
|
||||
const int maxLights = 16;
|
||||
const int maxLightsCluster = 4; // Ensure fast loop unroll before going higher
|
||||
const float clusterNear = 3.0;
|
||||
const vec3 clusterSlices = vec3(16, 16, 16);
|
||||
|
||||
int getClusterI(vec2 tc, float viewz, vec2 cameraPlane) {
|
||||
|
@ -11,6 +8,10 @@ int getClusterI(vec2 tc, float viewz, vec2 cameraPlane) {
|
|||
float z = log(viewz - cnear + 1.0) / log(cameraPlane.y - cnear + 1.0);
|
||||
sliceZ = int(z * (clusterSlices.z - 1)) + 1;
|
||||
}
|
||||
// address gap between near plane and cluster near offset
|
||||
else if (viewz >= cameraPlane.x) {
|
||||
sliceZ = 1;
|
||||
}
|
||||
return int(tc.x * clusterSlices.x) +
|
||||
int(int(tc.y * clusterSlices.y) * clusterSlices.x) +
|
||||
int(sliceZ * clusterSlices.x * clusterSlices.y);
|
||||
|
|
|
@ -21,27 +21,41 @@
|
|||
#endif
|
||||
|
||||
#ifdef _ShadowMap
|
||||
#ifdef _SinglePoint
|
||||
#ifdef _Spot
|
||||
uniform sampler2DShadow shadowMapSpot[1];
|
||||
uniform mat4 LWVPSpot0;
|
||||
#else
|
||||
uniform samplerCubeShadow shadowMapPoint[1];
|
||||
uniform vec2 lightProj;
|
||||
#ifdef _SinglePoint
|
||||
#ifdef _Spot
|
||||
#ifndef _LTC
|
||||
uniform sampler2DShadow shadowMapSpot[1];
|
||||
uniform mat4 LWVPSpot[1];
|
||||
#endif
|
||||
#else
|
||||
uniform samplerCubeShadow shadowMapPoint[1];
|
||||
uniform vec2 lightProj;
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
#ifdef _Clusters
|
||||
uniform samplerCubeShadow shadowMapPoint[4];
|
||||
uniform vec2 lightProj;
|
||||
#ifdef _Spot
|
||||
uniform sampler2DShadow shadowMapSpot[4];
|
||||
uniform mat4 LWVPSpot0;
|
||||
uniform mat4 LWVPSpot1;
|
||||
uniform mat4 LWVPSpot2;
|
||||
uniform mat4 LWVPSpot3;
|
||||
#ifdef _Clusters
|
||||
#ifdef _SingleAtlas
|
||||
//!uniform sampler2DShadow shadowMapAtlas;
|
||||
#endif
|
||||
uniform vec2 lightProj;
|
||||
#ifdef _ShadowMapAtlas
|
||||
#ifndef _SingleAtlas
|
||||
uniform sampler2DShadow shadowMapAtlasPoint;
|
||||
#endif
|
||||
#else
|
||||
uniform samplerCubeShadow shadowMapPoint[4];
|
||||
#endif
|
||||
#ifdef _Spot
|
||||
#ifdef _ShadowMapAtlas
|
||||
#ifndef _SingleAtlas
|
||||
uniform sampler2DShadow shadowMapAtlasSpot;
|
||||
#endif
|
||||
#else
|
||||
uniform sampler2DShadow shadowMapSpot[maxLightsCluster];
|
||||
#endif
|
||||
uniform mat4 LWVPSpotArray[maxLightsCluster];
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef _LTC
|
||||
uniform vec3 lightArea0;
|
||||
|
@ -51,17 +65,14 @@ uniform vec3 lightArea3;
|
|||
uniform sampler2D sltcMat;
|
||||
uniform sampler2D sltcMag;
|
||||
#ifdef _ShadowMap
|
||||
#ifndef _Spot
|
||||
#ifndef _Spot
|
||||
#ifdef _SinglePoint
|
||||
uniform sampler2DShadow shadowMapSpot[1];
|
||||
uniform mat4 LWVPSpot0;
|
||||
uniform sampler2DShadow shadowMapSpot[1];
|
||||
uniform mat4 LWVPSpot[1];
|
||||
#endif
|
||||
#ifdef _Clusters
|
||||
uniform sampler2DShadow shadowMapSpot[4];
|
||||
uniform mat4 LWVPSpot0;
|
||||
uniform mat4 LWVPSpot1;
|
||||
uniform mat4 LWVPSpot2;
|
||||
uniform mat4 LWVPSpot3;
|
||||
uniform sampler2DShadow shadowMapSpot[maxLightsCluster];
|
||||
uniform mat4 LWVPSpotArray[maxLightsCluster];
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
@ -132,24 +143,24 @@ vec3 sampleLight(const vec3 p, const vec3 n, const vec3 v, const float dotNV, co
|
|||
#ifdef _ShadowMap
|
||||
if (receiveShadow) {
|
||||
#ifdef _SinglePoint
|
||||
vec4 lPos = LWVPSpot0 * vec4(p + n * bias * 10, 1.0);
|
||||
vec4 lPos = LWVPSpot[0] * vec4(p + n * bias * 10, 1.0);
|
||||
direct *= shadowTest(shadowMapSpot[0], lPos.xyz / lPos.w, bias);
|
||||
#endif
|
||||
#ifdef _Clusters
|
||||
if (index == 0) {
|
||||
vec4 lPos = LWVPSpot0 * vec4(p + n * bias * 10, 1.0);
|
||||
vec4 lPos = LWVPSpot[0] * vec4(p + n * bias * 10, 1.0);
|
||||
direct *= shadowTest(shadowMapSpot[0], lPos.xyz / lPos.w, bias);
|
||||
}
|
||||
else if (index == 1) {
|
||||
vec4 lPos = LWVPSpot1 * vec4(p + n * bias * 10, 1.0);
|
||||
vec4 lPos = LWVPSpot[1] * vec4(p + n * bias * 10, 1.0);
|
||||
direct *= shadowTest(shadowMapSpot[1], lPos.xyz / lPos.w, bias);
|
||||
}
|
||||
else if (index == 2) {
|
||||
vec4 lPos = LWVPSpot2 * vec4(p + n * bias * 10, 1.0);
|
||||
vec4 lPos = LWVPSpot[2] * vec4(p + n * bias * 10, 1.0);
|
||||
direct *= shadowTest(shadowMapSpot[2], lPos.xyz / lPos.w, bias);
|
||||
}
|
||||
else if (index == 3) {
|
||||
vec4 lPos = LWVPSpot3 * vec4(p + n * bias * 10, 1.0);
|
||||
vec4 lPos = LWVPSpot[3] * vec4(p + n * bias * 10, 1.0);
|
||||
direct *= shadowTest(shadowMapSpot[3], lPos.xyz / lPos.w, bias);
|
||||
}
|
||||
#endif
|
||||
|
@ -168,26 +179,26 @@ vec3 sampleLight(const vec3 p, const vec3 n, const vec3 v, const float dotNV, co
|
|||
#ifdef _ShadowMap
|
||||
if (receiveShadow) {
|
||||
#ifdef _SinglePoint
|
||||
vec4 lPos = LWVPSpot0 * vec4(p + n * bias * 10, 1.0);
|
||||
vec4 lPos = LWVPSpot[0] * vec4(p + n * bias * 10, 1.0);
|
||||
direct *= shadowTest(shadowMapSpot[0], lPos.xyz / lPos.w, bias);
|
||||
#endif
|
||||
#ifdef _Clusters
|
||||
if (index == 0) {
|
||||
vec4 lPos = LWVPSpot0 * vec4(p + n * bias * 10, 1.0);
|
||||
direct *= shadowTest(shadowMapSpot[0], lPos.xyz / lPos.w, bias);
|
||||
}
|
||||
else if (index == 1) {
|
||||
vec4 lPos = LWVPSpot1 * vec4(p + n * bias * 10, 1.0);
|
||||
direct *= shadowTest(shadowMapSpot[1], lPos.xyz / lPos.w, bias);
|
||||
}
|
||||
else if (index == 2) {
|
||||
vec4 lPos = LWVPSpot2 * vec4(p + n * bias * 10, 1.0);
|
||||
direct *= shadowTest(shadowMapSpot[2], lPos.xyz / lPos.w, bias);
|
||||
}
|
||||
else if (index == 3) {
|
||||
vec4 lPos = LWVPSpot3 * vec4(p + n * bias * 10, 1.0);
|
||||
direct *= shadowTest(shadowMapSpot[3], lPos.xyz / lPos.w, bias);
|
||||
}
|
||||
vec4 lPos = LWVPSpotArray[index] * vec4(p + n * bias * 10, 1.0);
|
||||
#ifdef _ShadowMapAtlas
|
||||
direct *= shadowTest(
|
||||
#ifndef _SingleAtlas
|
||||
shadowMapAtlasSpot
|
||||
#else
|
||||
shadowMapAtlas
|
||||
#endif
|
||||
, lPos.xyz / lPos.w, bias
|
||||
);
|
||||
#else
|
||||
if (index == 0) direct *= shadowTest(shadowMapSpot[0], lPos.xyz / lPos.w, bias);
|
||||
else if (index == 1) direct *= shadowTest(shadowMapSpot[1], lPos.xyz / lPos.w, bias);
|
||||
else if (index == 2) direct *= shadowTest(shadowMapSpot[2], lPos.xyz / lPos.w, bias);
|
||||
else if (index == 3) direct *= shadowTest(shadowMapSpot[3], lPos.xyz / lPos.w, bias);
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
@ -207,10 +218,21 @@ vec3 sampleLight(const vec3 p, const vec3 n, const vec3 v, const float dotNV, co
|
|||
#endif
|
||||
#endif
|
||||
#ifdef _Clusters
|
||||
if (index == 0) direct *= PCFCube(shadowMapPoint[0], ld, -l, bias, lightProj, n);
|
||||
else if (index == 1) direct *= PCFCube(shadowMapPoint[1], ld, -l, bias, lightProj, n);
|
||||
else if (index == 2) direct *= PCFCube(shadowMapPoint[2], ld, -l, bias, lightProj, n);
|
||||
else if (index == 3) direct *= PCFCube(shadowMapPoint[3], ld, -l, bias, lightProj, n);
|
||||
#ifdef _ShadowMapAtlas
|
||||
direct *= PCFFakeCube(
|
||||
#ifndef _SingleAtlas
|
||||
shadowMapAtlasPoint
|
||||
#else
|
||||
shadowMapAtlas
|
||||
#endif
|
||||
, ld, -l, bias, lightProj, n, index
|
||||
);
|
||||
#else
|
||||
if (index == 0) direct *= PCFCube(shadowMapPoint[0], ld, -l, bias, lightProj, n);
|
||||
else if (index == 1) direct *= PCFCube(shadowMapPoint[1], ld, -l, bias, lightProj, n);
|
||||
else if (index == 2) direct *= PCFCube(shadowMapPoint[2], ld, -l, bias, lightProj, n);
|
||||
else if (index == 3) direct *= PCFCube(shadowMapPoint[3], ld, -l, bias, lightProj, n);
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -8,27 +8,39 @@
|
|||
#endif
|
||||
|
||||
#ifdef _ShadowMap
|
||||
#ifdef _SinglePoint
|
||||
#ifdef _Spot
|
||||
uniform sampler2DShadow shadowMapSpot[1];
|
||||
uniform mat4 LWVPSpot0;
|
||||
#else
|
||||
uniform samplerCubeShadow shadowMapPoint[1];
|
||||
uniform vec2 lightProj;
|
||||
#ifdef _SinglePoint
|
||||
#ifdef _Spot
|
||||
uniform sampler2DShadow shadowMapSpot[1];
|
||||
uniform mat4 LWVPSpot[1];
|
||||
#else
|
||||
uniform samplerCubeShadow shadowMapPoint[1];
|
||||
uniform vec2 lightProj;
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
#ifdef _Clusters
|
||||
uniform samplerCubeShadow shadowMapPoint[4];
|
||||
uniform vec2 lightProj;
|
||||
#ifdef _Spot
|
||||
uniform sampler2DShadow shadowMapSpot[4];
|
||||
uniform mat4 LWVPSpot0;
|
||||
uniform mat4 LWVPSpot1;
|
||||
uniform mat4 LWVPSpot2;
|
||||
uniform mat4 LWVPSpot3;
|
||||
#ifdef _Clusters
|
||||
#ifdef _SingleAtlas
|
||||
//!uniform sampler2DShadow shadowMapAtlas;
|
||||
#endif
|
||||
uniform vec2 lightProj;
|
||||
#ifdef _ShadowMapAtlas
|
||||
#ifndef _SingleAtlas
|
||||
uniform sampler2DShadow shadowMapAtlasPoint;
|
||||
#endif
|
||||
#else
|
||||
uniform samplerCubeShadow shadowMapPoint[4];
|
||||
#endif
|
||||
#ifdef _Spot
|
||||
#ifdef _ShadowMapAtlas
|
||||
#ifndef _SingleAtlas
|
||||
uniform sampler2DShadow shadowMapAtlasSpot;
|
||||
#endif
|
||||
#else
|
||||
uniform sampler2DShadow shadowMapSpot[maxLightsCluster];
|
||||
#endif
|
||||
uniform mat4 LWVPSpotArray[maxLightsCluster];
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
||||
vec3 sampleLight(const vec3 p, const vec3 n, const vec3 v, const float dotNV, const vec3 lp, const vec3 lightCol,
|
||||
const vec3 albedo, const float rough, const float spec, const vec3 f0
|
||||
|
@ -62,26 +74,26 @@ vec3 sampleLight(const vec3 p, const vec3 n, const vec3 v, const float dotNV, co
|
|||
#ifdef _ShadowMap
|
||||
if (receiveShadow) {
|
||||
#ifdef _SinglePoint
|
||||
vec4 lPos = LWVPSpot0 * vec4(p + n * bias * 10, 1.0);
|
||||
vec4 lPos = LWVPSpot[0] * vec4(p + n * bias * 10, 1.0);
|
||||
direct *= shadowTest(shadowMapSpot[0], lPos.xyz / lPos.w, bias);
|
||||
#endif
|
||||
#ifdef _Clusters
|
||||
if (index == 0) {
|
||||
vec4 lPos = LWVPSpot0 * vec4(p + n * bias * 10, 1.0);
|
||||
direct *= shadowTest(shadowMapSpot[0], lPos.xyz / lPos.w, bias);
|
||||
}
|
||||
else if (index == 1) {
|
||||
vec4 lPos = LWVPSpot1 * vec4(p + n * bias * 10, 1.0);
|
||||
direct *= shadowTest(shadowMapSpot[1], lPos.xyz / lPos.w, bias);
|
||||
}
|
||||
else if (index == 2) {
|
||||
vec4 lPos = LWVPSpot2 * vec4(p + n * bias * 10, 1.0);
|
||||
direct *= shadowTest(shadowMapSpot[2], lPos.xyz / lPos.w, bias);
|
||||
}
|
||||
else if (index == 3) {
|
||||
vec4 lPos = LWVPSpot3 * vec4(p + n * bias * 10, 1.0);
|
||||
direct *= shadowTest(shadowMapSpot[3], lPos.xyz / lPos.w, bias);
|
||||
}
|
||||
vec4 lPos = LWVPSpotArray[index] * vec4(p + n * bias * 10, 1.0);
|
||||
#ifdef _ShadowMapAtlas
|
||||
direct *= shadowTest(
|
||||
#ifndef _SingleAtlas
|
||||
shadowMapAtlasSpot
|
||||
#else
|
||||
shadowMapAtlas
|
||||
#endif
|
||||
, lPos.xyz / lPos.w, bias
|
||||
);
|
||||
#else
|
||||
if (index == 0) direct *= shadowTest(shadowMapSpot[0], lPos.xyz / lPos.w, bias);
|
||||
else if (index == 1) direct *= shadowTest(shadowMapSpot[1], lPos.xyz / lPos.w, bias);
|
||||
else if (index == 2) direct *= shadowTest(shadowMapSpot[2], lPos.xyz / lPos.w, bias);
|
||||
else if (index == 3) direct *= shadowTest(shadowMapSpot[3], lPos.xyz / lPos.w, bias);
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
@ -90,20 +102,32 @@ vec3 sampleLight(const vec3 p, const vec3 n, const vec3 v, const float dotNV, co
|
|||
#endif
|
||||
|
||||
#ifdef _ShadowMap
|
||||
#ifndef _Spot
|
||||
|
||||
if (receiveShadow) {
|
||||
#ifdef _SinglePoint
|
||||
#ifndef _Spot
|
||||
direct *= PCFCube(shadowMapPoint[0], ld, -l, bias, lightProj, n);
|
||||
#endif
|
||||
#endif
|
||||
#ifdef _Clusters
|
||||
if (index == 0) direct *= PCFCube(shadowMapPoint[0], ld, -l, bias, lightProj, n);
|
||||
else if (index == 1) direct *= PCFCube(shadowMapPoint[1], ld, -l, bias, lightProj, n);
|
||||
else if (index == 2) direct *= PCFCube(shadowMapPoint[2], ld, -l, bias, lightProj, n);
|
||||
else if (index == 3) direct *= PCFCube(shadowMapPoint[3], ld, -l, bias, lightProj, n);
|
||||
#ifdef _ShadowMapAtlas
|
||||
direct *= PCFFakeCube(
|
||||
#ifndef _SingleAtlas
|
||||
shadowMapAtlasPoint
|
||||
#else
|
||||
shadowMapAtlas
|
||||
#endif
|
||||
, ld, -l, bias, lightProj, n, index
|
||||
);
|
||||
#else
|
||||
if (index == 0) direct *= PCFCube(shadowMapPoint[0], ld, -l, bias, lightProj, n);
|
||||
else if (index == 1) direct *= PCFCube(shadowMapPoint[1], ld, -l, bias, lightProj, n);
|
||||
else if (index == 2) direct *= PCFCube(shadowMapPoint[2], ld, -l, bias, lightProj, n);
|
||||
else if (index == 3) direct *= PCFCube(shadowMapPoint[3], ld, -l, bias, lightProj, n);
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
return direct;
|
||||
}
|
||||
|
|
|
@ -11,6 +11,50 @@ uniform vec4 casData[shadowmapCascades * 4 + 4];
|
|||
uniform vec2 smSizeUniform;
|
||||
#endif
|
||||
|
||||
#ifdef _ShadowMap
|
||||
#ifdef _Clusters
|
||||
#ifdef _ShadowMapAtlas
|
||||
uniform vec4 pointLightDataArray[maxLightsCluster * 6];
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef _ShadowMapAtlas
|
||||
// https://www.khronos.org/registry/OpenGL/specs/gl/glspec20.pdf // p:168
|
||||
// https://www.gamedev.net/forums/topic/687535-implementing-a-cube-map-lookup-function/5337472/
|
||||
vec2 sampleCube(vec3 dir, out int faceIndex) {
|
||||
vec3 dirAbs = abs(dir);
|
||||
float ma;
|
||||
vec2 uv;
|
||||
if(dirAbs.z >= dirAbs.x && dirAbs.z >= dirAbs.y) {
|
||||
faceIndex = dir.z < 0.0 ? 5 : 4;
|
||||
ma = 0.5 / dirAbs.z;
|
||||
uv = vec2(dir.z < 0.0 ? -dir.x : dir.x, -dir.y);
|
||||
}
|
||||
else if(dirAbs.y >= dirAbs.x) {
|
||||
faceIndex = dir.y < 0.0 ? 3 : 2;
|
||||
ma = 0.5 / dirAbs.y;
|
||||
uv = vec2(dir.x, dir.y < 0.0 ? -dir.z : dir.z);
|
||||
}
|
||||
else {
|
||||
faceIndex = dir.x < 0.0 ? 1 : 0;
|
||||
ma = 0.5 / dirAbs.x;
|
||||
uv = vec2(dir.x < 0.0 ? dir.z : -dir.z, -dir.y);
|
||||
}
|
||||
// downscale uv a little to hide seams
|
||||
// transform coordinates from clip space to texture space
|
||||
#ifndef _FlipY
|
||||
return uv * 0.9976 * ma + 0.5;
|
||||
#else
|
||||
#ifdef HLSL
|
||||
return uv * 0.9976 * ma + 0.5;
|
||||
#else
|
||||
return vec2(uv.x * ma, uv.y * -ma) * 0.9976 + 0.5;
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
float PCF(sampler2DShadow shadowMap, const vec2 uv, const float compare, const vec2 smSize) {
|
||||
float result = texture(shadowMap, vec3(uv + (vec2(-1.0, -1.0) / smSize), compare));
|
||||
result += texture(shadowMap, vec3(uv + (vec2(-1.0, 0.0) / smSize), compare));
|
||||
|
@ -50,6 +94,185 @@ float PCFCube(samplerCubeShadow shadowMapCube, const vec3 lp, vec3 ml, const flo
|
|||
return result / 9.0;
|
||||
}
|
||||
|
||||
#ifdef _ShadowMapAtlas
|
||||
// transform "out-of-bounds" coordinates to the correct face/coordinate system
|
||||
vec2 transformOffsetedUV(const int faceIndex, out int newFaceIndex, vec2 uv) {
|
||||
if (uv.x < 0.0) {
|
||||
if (faceIndex == 0) { // X+
|
||||
newFaceIndex = 4; // Z+
|
||||
}
|
||||
else if (faceIndex == 1) { // X-
|
||||
newFaceIndex = 5; // Z-
|
||||
}
|
||||
else if (faceIndex == 2) { // Y+
|
||||
newFaceIndex = 1; // X-
|
||||
}
|
||||
else if (faceIndex == 3) { // Y-
|
||||
newFaceIndex = 1; // X-
|
||||
}
|
||||
else if (faceIndex == 4) { // Z+
|
||||
newFaceIndex = 1; // X-
|
||||
}
|
||||
else { // Z-
|
||||
newFaceIndex = 0; // X+
|
||||
}
|
||||
uv = vec2(1.0 + uv.x, uv.y);
|
||||
}
|
||||
else if (uv.x > 1.0) {
|
||||
if (faceIndex == 0) { // X+
|
||||
newFaceIndex = 5; // Z-
|
||||
}
|
||||
else if (faceIndex == 1) { // X-
|
||||
newFaceIndex = 4; // Z+
|
||||
}
|
||||
else if (faceIndex == 2) { // Y+
|
||||
newFaceIndex = 0; // X+
|
||||
}
|
||||
else if (faceIndex == 3) { // Y-
|
||||
newFaceIndex = 0; // X+
|
||||
}
|
||||
else if (faceIndex == 4) { // Z+
|
||||
newFaceIndex = 0; // X+
|
||||
}
|
||||
else { // Z-
|
||||
newFaceIndex = 1; // X-
|
||||
}
|
||||
uv = vec2(1.0 - uv.x, uv.y);
|
||||
}
|
||||
else if (uv.y < 0.0) {
|
||||
if (faceIndex == 0) { // X+
|
||||
newFaceIndex = 2; // Y+
|
||||
}
|
||||
else if (faceIndex == 1) { // X-
|
||||
newFaceIndex = 2; // Y+
|
||||
}
|
||||
else if (faceIndex == 2) { // Y+
|
||||
newFaceIndex = 5; // Z-
|
||||
}
|
||||
else if (faceIndex == 3) { // Y-
|
||||
newFaceIndex = 4; // Z+
|
||||
}
|
||||
else if (faceIndex == 4) { // Z+
|
||||
newFaceIndex = 2; // Y+
|
||||
}
|
||||
else { // Z-
|
||||
newFaceIndex = 2; // Y+
|
||||
}
|
||||
uv = vec2(uv.x, 1.0 + uv.y);
|
||||
}
|
||||
else if (uv.y > 1.0) {
|
||||
if (faceIndex == 0) { // X+
|
||||
newFaceIndex = 3; // Y-
|
||||
}
|
||||
else if (faceIndex == 1) { // X-
|
||||
newFaceIndex = 3; // Y-
|
||||
}
|
||||
else if (faceIndex == 2) { // Y+
|
||||
newFaceIndex = 4; // Z+
|
||||
}
|
||||
else if (faceIndex == 3) { // Y-
|
||||
newFaceIndex = 5; // Z-
|
||||
}
|
||||
else if (faceIndex == 4) { // Z+
|
||||
newFaceIndex = 3; // Y-
|
||||
}
|
||||
else { // Z-
|
||||
newFaceIndex = 3; // Y-
|
||||
}
|
||||
uv = vec2(uv.x, 1.0 - uv.y);
|
||||
} else {
|
||||
newFaceIndex = faceIndex;
|
||||
}
|
||||
// cover corner cases too
|
||||
return uv;
|
||||
}
|
||||
|
||||
float PCFFakeCube(sampler2DShadow shadowMap, const vec3 lp, vec3 ml, const float bias, const vec2 lightProj, const vec3 n, const int index) {
|
||||
const vec2 smSize = smSizeUniform; // TODO: incorrect...
|
||||
const float compare = lpToDepth(lp, lightProj) - bias * 1.5;
|
||||
ml = ml + n * bias * 20;
|
||||
|
||||
int faceIndex = 0;
|
||||
const int lightIndex = index * 6;
|
||||
const vec2 uv = sampleCube(ml, faceIndex);
|
||||
|
||||
vec4 pointLightTile = pointLightDataArray[lightIndex + faceIndex]; // x: tile X offset, y: tile Y offset, z: tile size relative to atlas
|
||||
vec2 uvtiled = pointLightTile.z * uv + pointLightTile.xy;
|
||||
#ifdef _FlipY
|
||||
uvtiled.y = 1.0 - uvtiled.y; // invert Y coordinates for direct3d coordinate system
|
||||
#endif
|
||||
|
||||
float result = texture(shadowMap, vec3(uvtiled, compare));
|
||||
// soft shadowing
|
||||
int newFaceIndex = 0;
|
||||
uvtiled = transformOffsetedUV(faceIndex, newFaceIndex, vec2(uv + (vec2(-1.0, 0.0) / smSize)));
|
||||
pointLightTile = pointLightDataArray[lightIndex + newFaceIndex];
|
||||
uvtiled = pointLightTile.z * uvtiled + pointLightTile.xy;
|
||||
#ifdef _FlipY
|
||||
uvtiled.y = 1.0 - uvtiled.y; // invert Y coordinates for direct3d coordinate system
|
||||
#endif
|
||||
result += texture(shadowMap, vec3(pointLightTile.z * uvtiled + pointLightTile.xy, compare));
|
||||
|
||||
uvtiled = transformOffsetedUV(faceIndex, newFaceIndex, vec2(uv + (vec2(-1.0, 1.0) / smSize)));
|
||||
pointLightTile = pointLightDataArray[lightIndex + newFaceIndex];
|
||||
uvtiled = pointLightTile.z * uvtiled + pointLightTile.xy;
|
||||
#ifdef _FlipY
|
||||
uvtiled.y = 1.0 - uvtiled.y; // invert Y coordinates for direct3d coordinate system
|
||||
#endif
|
||||
result += texture(shadowMap, vec3(uvtiled, compare));
|
||||
|
||||
uvtiled = transformOffsetedUV(faceIndex, newFaceIndex, vec2(uv + (vec2(0.0, -1.0) / smSize)));
|
||||
pointLightTile = pointLightDataArray[lightIndex + newFaceIndex];
|
||||
uvtiled = pointLightTile.z * uvtiled + pointLightTile.xy;
|
||||
#ifdef _FlipY
|
||||
uvtiled.y = 1.0 - uvtiled.y; // invert Y coordinates for direct3d coordinate system
|
||||
#endif
|
||||
result += texture(shadowMap, vec3(uvtiled, compare));
|
||||
|
||||
uvtiled = transformOffsetedUV(faceIndex, newFaceIndex, vec2(uv + (vec2(-1.0, -1.0) / smSize)));
|
||||
pointLightTile = pointLightDataArray[lightIndex + newFaceIndex];
|
||||
uvtiled = pointLightTile.z * uvtiled + pointLightTile.xy;
|
||||
#ifdef _FlipY
|
||||
uvtiled.y = 1.0 - uvtiled.y; // invert Y coordinates for direct3d coordinate system
|
||||
#endif
|
||||
result += texture(shadowMap, vec3(uvtiled, compare));
|
||||
|
||||
uvtiled = transformOffsetedUV(faceIndex, newFaceIndex, vec2(uv + (vec2(0.0, 1.0) / smSize)));
|
||||
pointLightTile = pointLightDataArray[lightIndex + newFaceIndex];
|
||||
uvtiled = pointLightTile.z * uvtiled + pointLightTile.xy;
|
||||
#ifdef _FlipY
|
||||
uvtiled.y = 1.0 - uvtiled.y; // invert Y coordinates for direct3d coordinate system
|
||||
#endif
|
||||
result += texture(shadowMap, vec3(uvtiled, compare));
|
||||
|
||||
uvtiled = transformOffsetedUV(faceIndex, newFaceIndex, vec2(uv + (vec2(1.0, -1.0) / smSize)));
|
||||
pointLightTile = pointLightDataArray[lightIndex + newFaceIndex];
|
||||
uvtiled = pointLightTile.z * uvtiled + pointLightTile.xy;
|
||||
#ifdef _FlipY
|
||||
uvtiled.y = 1.0 - uvtiled.y; // invert Y coordinates for direct3d coordinate system
|
||||
#endif
|
||||
result += texture(shadowMap, vec3(uvtiled, compare));
|
||||
|
||||
uvtiled = transformOffsetedUV(faceIndex, newFaceIndex, vec2(uv + (vec2(1.0, 0.0) / smSize)));
|
||||
pointLightTile = pointLightDataArray[lightIndex + newFaceIndex];
|
||||
uvtiled = pointLightTile.z * uvtiled + pointLightTile.xy;
|
||||
#ifdef _FlipY
|
||||
uvtiled.y = 1.0 - uvtiled.y; // invert Y coordinates for direct3d coordinate system
|
||||
#endif
|
||||
result += texture(shadowMap, vec3(uvtiled, compare));
|
||||
|
||||
uvtiled = transformOffsetedUV(faceIndex, newFaceIndex, vec2(uv + (vec2(1.0, 1.0) / smSize)));
|
||||
pointLightTile = pointLightDataArray[lightIndex + newFaceIndex];
|
||||
uvtiled = pointLightTile.z * uvtiled + pointLightTile.xy;
|
||||
#ifdef _FlipY
|
||||
uvtiled.y = 1.0 - uvtiled.y; // invert Y coordinates for direct3d coordinate system
|
||||
#endif
|
||||
result += texture(shadowMap, vec3(uvtiled, compare));
|
||||
|
||||
return result / 9.0;
|
||||
}
|
||||
#endif
|
||||
|
||||
float shadowTest(sampler2DShadow shadowMap, const vec3 lPos, const float shadowsBias) {
|
||||
#ifdef _SMSizeUniform
|
||||
vec2 smSize = smSizeUniform;
|
||||
|
@ -95,7 +318,7 @@ mat4 getCascadeMat(const float d, out int casi, out int casIndex) {
|
|||
|
||||
float shadowTestCascade(sampler2DShadow shadowMap, const vec3 eye, const vec3 p, const float shadowsBias) {
|
||||
#ifdef _SMSizeUniform
|
||||
vec2 smSize = smSizeUniform * vec2(shadowmapCascades, 1.0);
|
||||
vec2 smSize = smSizeUniform;
|
||||
#else
|
||||
const vec2 smSize = shadowmapSize * vec2(shadowmapCascades, 1.0);
|
||||
#endif
|
||||
|
|
|
@ -12,7 +12,7 @@ uniform sampler2D gbufferD;
|
|||
uniform sampler2D snoise;
|
||||
|
||||
#ifdef _Clusters
|
||||
uniform vec4 lightsArray[maxLights * 2];
|
||||
uniform vec4 lightsArray[maxLights * 3];
|
||||
#ifdef _Spot
|
||||
uniform vec4 lightsArraySpot[maxLights];
|
||||
#endif
|
||||
|
@ -24,7 +24,7 @@ uniform vec2 cameraPlane;
|
|||
#ifdef _SinglePoint
|
||||
#ifdef _Spot
|
||||
uniform sampler2DShadow shadowMapSpot[1];
|
||||
uniform mat4 LWVPSpot0;
|
||||
uniform mat4 LWVPSpot[1];
|
||||
#else
|
||||
uniform samplerCubeShadow shadowMapPoint[1];
|
||||
uniform vec2 lightProj;
|
||||
|
@ -35,10 +35,7 @@ uniform vec2 cameraPlane;
|
|||
uniform vec2 lightProj;
|
||||
#ifdef _Spot
|
||||
uniform sampler2DShadow shadowMapSpot[4];
|
||||
uniform mat4 LWVPSpot0;
|
||||
uniform mat4 LWVPSpot1;
|
||||
uniform mat4 LWVPSpot2;
|
||||
uniform mat4 LWVPSpot3;
|
||||
uniform mat4 LWVPSpot[maxLightsCluster];
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
@ -103,7 +100,7 @@ void rayStep(inout vec3 curPos, inout float curOpticalDepth, inout float scatter
|
|||
|
||||
#ifdef _SinglePoint
|
||||
#ifdef _Spot
|
||||
vec4 lPos = LWVPSpot0 * vec4(curPos, 1.0);
|
||||
vec4 lPos = LWVPSpot[0] * vec4(curPos, 1.0);
|
||||
visibility = shadowTest(shadowMapSpot[0], lPos.xyz / lPos.w, pointBias);
|
||||
float spotEffect = dot(spotDir, normalize(pointPos - curPos)); // lightDir
|
||||
if (spotEffect < spotData.x) { // x - cutoff, y - cutoff - exponent
|
||||
|
|
|
@ -63,7 +63,7 @@
|
|||
},
|
||||
{
|
||||
"name": "LWVP",
|
||||
"link": "_biasLightWorldViewProjectionMatrix",
|
||||
"link": "_biasLightWorldViewProjectionMatrixSun",
|
||||
"ifndef": ["_CSM"],
|
||||
"ifdef": ["_Sun", "_ShadowMap"]
|
||||
},
|
||||
|
@ -108,23 +108,32 @@
|
|||
"ifdef": ["_SinglePoint", "_Spot"]
|
||||
},
|
||||
{
|
||||
"name": "LWVPSpot0",
|
||||
"name": "LWVPSpotArray",
|
||||
"link": "_biasLightWorldViewProjectionMatrixSpotArray",
|
||||
"ifdef": ["_Clusters", "_ShadowMap", "_Spot"]
|
||||
},
|
||||
{
|
||||
"name": "LWVPSpot[0]",
|
||||
"link": "_biasLightWorldViewProjectionMatrixSpot0",
|
||||
"ifndef": ["_ShadowMapAtlas"],
|
||||
"ifdef": ["_Spot", "_ShadowMap"]
|
||||
},
|
||||
{
|
||||
"name": "LWVPSpot1",
|
||||
"name": "LWVPSpot[1]",
|
||||
"link": "_biasLightWorldViewProjectionMatrixSpot1",
|
||||
"ifndef": ["_ShadowMapAtlas"],
|
||||
"ifdef": ["_Spot", "_ShadowMap"]
|
||||
},
|
||||
{
|
||||
"name": "LWVPSpot2",
|
||||
"name": "LWVPSpot[2]",
|
||||
"link": "_biasLightWorldViewProjectionMatrixSpot2",
|
||||
"ifndef": ["_ShadowMapAtlas"],
|
||||
"ifdef": ["_Spot", "_ShadowMap"]
|
||||
},
|
||||
{
|
||||
"name": "LWVPSpot3",
|
||||
"name": "LWVPSpot[3]",
|
||||
"link": "_biasLightWorldViewProjectionMatrixSpot3",
|
||||
"ifndef": ["_ShadowMapAtlas"],
|
||||
"ifdef": ["_Spot", "_ShadowMap"]
|
||||
}
|
||||
],
|
||||
|
|
|
@ -1,18 +1,18 @@
|
|||
package armory.logicnode;
|
||||
|
||||
import kha.FastFloat;
|
||||
|
||||
class ClampNode extends LogicNode {
|
||||
|
||||
public function new(tree: LogicTree) {
|
||||
super(tree);
|
||||
}
|
||||
|
||||
override function get(from: Int): Dynamic {
|
||||
var value: kha.FastFloat = inputs[0].get();
|
||||
var min: kha.FastFloat = inputs[1].get();
|
||||
var max: kha.FastFloat = inputs[2].get();
|
||||
override function get(from: Int): FastFloat {
|
||||
var value = inputs[0].get();
|
||||
var min = inputs[1].get();
|
||||
var max = inputs[2].get();
|
||||
|
||||
if (value == null || min == null || max == null) return null;
|
||||
|
||||
value <= min ? return min : value >= max ? return max : return value;
|
||||
return value < min ? min : value > max ? max : value;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,7 +15,7 @@ class PickLocationNode extends LogicNode {
|
|||
var object: Object = inputs[0].get();
|
||||
var coords: Vec4 = inputs[1].get();
|
||||
|
||||
if (object == null || coords == null) null;
|
||||
if (object == null || coords == null) return null;
|
||||
|
||||
#if arm_physics
|
||||
var physics = armory.trait.physics.PhysicsWorld.active;
|
||||
|
|
|
@ -12,12 +12,13 @@ class PickObjectNode extends LogicNode {
|
|||
|
||||
override function get(from: Int): Dynamic {
|
||||
var coords: Vec4 = inputs[0].get();
|
||||
var mask: Int = inputs[1].get();
|
||||
|
||||
if (coords == null) return null;
|
||||
|
||||
#if arm_physics
|
||||
var physics = armory.trait.physics.PhysicsWorld.active;
|
||||
var rb = physics.pickClosest(coords.x, coords.y);
|
||||
var rb = physics.pickClosest(coords.x, coords.y, mask);
|
||||
if (rb == null) return null;
|
||||
|
||||
if (from == 0) { // Object
|
||||
|
|
|
@ -5,8 +5,6 @@ import armory.system.Event;
|
|||
|
||||
class SendEventNode extends LogicNode {
|
||||
|
||||
var entries: Array<TEvent> = null;
|
||||
|
||||
public function new(tree: LogicTree) {
|
||||
super(tree);
|
||||
}
|
||||
|
@ -17,13 +15,9 @@ class SendEventNode extends LogicNode {
|
|||
|
||||
if (object == null) return;
|
||||
|
||||
var all = Event.get(name);
|
||||
if (all != null) {
|
||||
entries = [];
|
||||
for (e in all) if (e.mask == object.uid) entries.push(e);
|
||||
}
|
||||
var entries = Event.get(name);
|
||||
if (entries == null) return;
|
||||
for (e in entries) e.onEvent();
|
||||
for (e in entries) if (e.mask == object.uid) e.onEvent();
|
||||
|
||||
runOutput(0);
|
||||
}
|
||||
|
|
|
@ -4,8 +4,6 @@ import armory.system.Event;
|
|||
|
||||
class SendGlobalEventNode extends LogicNode {
|
||||
|
||||
var entries: Array<TEvent> = null;
|
||||
|
||||
public function new(tree: LogicTree) {
|
||||
super(tree);
|
||||
}
|
||||
|
@ -13,7 +11,7 @@ class SendGlobalEventNode extends LogicNode {
|
|||
override function run(from: Int) {
|
||||
var name: String = inputs[1].get();
|
||||
|
||||
entries = Event.get(name);
|
||||
var entries = Event.get(name);
|
||||
if (entries == null) return; // Event does not exist
|
||||
for (e in entries) e.onEvent();
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package armory.renderpath;
|
||||
|
||||
import iron.RenderPath;
|
||||
import iron.object.LightObject;
|
||||
|
||||
class Inc {
|
||||
|
||||
|
@ -39,6 +40,175 @@ class Inc {
|
|||
#end
|
||||
}
|
||||
|
||||
#if arm_shadowmap_atlas
|
||||
public static function updatePointLightAtlasData(): Void {
|
||||
var atlas = ShadowMapAtlas.shadowMapAtlases.get(ShadowMapAtlas.shadowMapAtlasName("point"));
|
||||
if (atlas != null) {
|
||||
if(LightObject.pointLightsData == null) {
|
||||
LightObject.pointLightsData = new kha.arrays.Float32Array(
|
||||
LightObject.maxLightsCluster * ShadowMapTile.tilesLightType("point") * 4 ); // max possible visible lights * 6 or 2 (faces) * 4 (xyzw)
|
||||
}
|
||||
|
||||
var n = iron.Scene.active.lights.length > LightObject.maxLightsCluster ? LightObject.maxLightsCluster : iron.Scene.active.lights.length;
|
||||
var i = 0;
|
||||
var j = 0;
|
||||
for (light in iron.Scene.active.lights) {
|
||||
if (i >= n)
|
||||
break;
|
||||
if (LightObject.discardLightCulled(light)) continue;
|
||||
if (light.data.raw.type == "point") {
|
||||
if (!light.data.raw.cast_shadow) {
|
||||
j += 4 * 6;
|
||||
continue;
|
||||
}
|
||||
for(k in 0...6) {
|
||||
LightObject.pointLightsData[j ] = light.tileOffsetX[k]; // posx
|
||||
LightObject.pointLightsData[j + 1] = light.tileOffsetY[k]; // posy
|
||||
LightObject.pointLightsData[j + 2] = light.tileScale[k]; // tile scale factor relative to atlas
|
||||
LightObject.pointLightsData[j + 3] = 0; // padding
|
||||
j += 4;
|
||||
}
|
||||
}
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static function bindShadowMapAtlas() {
|
||||
for (atlas in ShadowMapAtlas.shadowMapAtlases) {
|
||||
path.bindTarget(atlas.target, atlas.target);
|
||||
}
|
||||
}
|
||||
|
||||
static function getShadowMapAtlas(atlas:ShadowMapAtlas):String {
|
||||
inline function createDepthTarget(name: String, size: Int) {
|
||||
var t = new RenderTargetRaw();
|
||||
t.name = name;
|
||||
t.width = t.height = size;
|
||||
t.format = "DEPTH16";
|
||||
return path.createRenderTarget(t);
|
||||
}
|
||||
|
||||
var rt = path.renderTargets.get(atlas.target);
|
||||
// Create shadowmap atlas texture on the fly and replace existing on size change
|
||||
if (rt == null) {
|
||||
rt = createDepthTarget(atlas.target, atlas.sizew);
|
||||
}
|
||||
else if (atlas.updateRenderTarget) {
|
||||
atlas.updateRenderTarget = false;
|
||||
// Resize shadow map
|
||||
rt.unload();
|
||||
rt = createDepthTarget(atlas.target, atlas.sizew);
|
||||
}
|
||||
return atlas.target;
|
||||
}
|
||||
|
||||
public static function drawShadowMapAtlas() {
|
||||
#if rp_shadowmap
|
||||
#if rp_probes
|
||||
// Share shadow map with probe
|
||||
if (lastFrame == RenderPath.active.frame)
|
||||
return;
|
||||
lastFrame = RenderPath.active.frame;
|
||||
#end
|
||||
// add new lights to the atlases
|
||||
for (light in iron.Scene.active.lights) {
|
||||
if (!light.lightInAtlas && !light.culledLight && light.visible && light.shadowMapScale > 0.0
|
||||
&& light.data.raw.strength > 0.0 && light.data.raw.cast_shadow) {
|
||||
light.lightInAtlas = ShadowMapAtlas.addLight(light);
|
||||
}
|
||||
}
|
||||
// update point light data before rendering
|
||||
updatePointLightAtlasData();
|
||||
|
||||
for (atlas in ShadowMapAtlas.shadowMapAtlases) {
|
||||
var tilesToRemove = [];
|
||||
#if arm_shadowmap_atlas_lod
|
||||
var tilesToChangeSize = [];
|
||||
#end
|
||||
|
||||
var shadowmap = getShadowMapAtlas(atlas);
|
||||
path.setTargetStream(shadowmap);
|
||||
path.clearTarget(null, 1.0);
|
||||
|
||||
for (tile in atlas.activeTiles) {
|
||||
if (tile.light == null || !tile.light.visible || tile.light.culledLight
|
||||
|| !tile.light.data.raw.cast_shadow || tile.light.data.raw.strength == 0) {
|
||||
tile.unlockLight = true;
|
||||
tilesToRemove.push(tile);
|
||||
continue;
|
||||
}
|
||||
|
||||
#if arm_shadowmap_atlas_lod
|
||||
var newTileSize = atlas.getTileSize(tile.light.shadowMapScale);
|
||||
if (newTileSize != tile.size) {
|
||||
if (newTileSize == 0) {
|
||||
tile.unlockLight = true;
|
||||
tilesToRemove.push(tile);
|
||||
continue;
|
||||
}
|
||||
// queue for size change
|
||||
tile.newTileSize = newTileSize;
|
||||
tilesToChangeSize.push(tile);
|
||||
}
|
||||
#end
|
||||
// set the tile offset for this tile and every linked tile to this one
|
||||
var j = 0;
|
||||
tile.forEachTileLinked(function (lTile) {
|
||||
tile.light.tileOffsetX[j] = (lTile.coordsX == 0) ? 0.0 : lTile.coordsX / atlas.sizew;
|
||||
tile.light.tileOffsetY[j] = (lTile.coordsY == 0) ? 0.0 : lTile.coordsY / atlas.sizew;
|
||||
tile.light.tileScale[j] = lTile.size / atlas.sizew;
|
||||
j++;
|
||||
});
|
||||
// set shadowmap size for uniform
|
||||
tile.light.data.raw.shadowmap_size = atlas.sizew;
|
||||
|
||||
path.light = tile.light;
|
||||
|
||||
var face = 0;
|
||||
var faces = ShadowMapTile.tilesLightType(tile.light.data.raw.type);
|
||||
|
||||
tile.forEachTileLinked(function (lTile) {
|
||||
if (faces > 1) {
|
||||
#if arm_csm
|
||||
switch (tile.light.data.raw.type) {
|
||||
case "sun": tile.light.setCascade(iron.Scene.active.camera, face);
|
||||
case "point": path.currentFace = face;
|
||||
}
|
||||
#else
|
||||
path.currentFace = face;
|
||||
#end
|
||||
face++;
|
||||
}
|
||||
path.setCurrentViewportWithOffset(lTile.size, lTile.size, lTile.coordsX, lTile.coordsY);
|
||||
|
||||
path.drawMeshesStream("shadowmap");
|
||||
});
|
||||
|
||||
path.currentFace = -1;
|
||||
}
|
||||
path.endStream();
|
||||
|
||||
#if arm_shadowmap_atlas_lod
|
||||
for (tile in tilesToChangeSize) {
|
||||
tilesToRemove.push(tile);
|
||||
|
||||
var newTile = ShadowMapTile.assignTiles(tile.light, atlas, tile);
|
||||
if (newTile != null)
|
||||
atlas.activeTiles.push(newTile);
|
||||
}
|
||||
// update point light data after changing size of tiles to avoid render issues
|
||||
updatePointLightAtlasData();
|
||||
#end
|
||||
|
||||
for (tile in tilesToRemove) {
|
||||
atlas.activeTiles.remove(tile);
|
||||
tile.freeTile();
|
||||
}
|
||||
}
|
||||
#end // rp_shadowmap
|
||||
}
|
||||
#else
|
||||
public static function bindShadowMap() {
|
||||
for (l in iron.Scene.active.lights) {
|
||||
if (!l.visible || l.data.raw.type != "sun") continue;
|
||||
|
@ -56,10 +226,15 @@ class Inc {
|
|||
}
|
||||
}
|
||||
|
||||
static function shadowMapName(l: iron.object.LightObject): String {
|
||||
if (l.data.raw.type == "sun") return "shadowMap";
|
||||
if (l.data.raw.type == "point") return "shadowMapPoint[" + pointIndex + "]";
|
||||
else return "shadowMapSpot[" + spotIndex + "]";
|
||||
static function shadowMapName(light: LightObject): String {
|
||||
switch (light.data.raw.type) {
|
||||
case "sun":
|
||||
return "shadowMap";
|
||||
case "point":
|
||||
return "shadowMapPoint[" + pointIndex + "]";
|
||||
default:
|
||||
return "shadowMapSpot[" + spotIndex + "]";
|
||||
}
|
||||
}
|
||||
|
||||
static function getShadowMap(l: iron.object.LightObject): String {
|
||||
|
@ -130,6 +305,7 @@ class Inc {
|
|||
|
||||
#end // rp_shadowmap
|
||||
}
|
||||
#end
|
||||
|
||||
public static function applyConfig() {
|
||||
#if arm_config
|
||||
|
@ -203,7 +379,11 @@ class Inc {
|
|||
path.setTarget("accum", ["revealage"]);
|
||||
#if rp_shadowmap
|
||||
{
|
||||
#if arm_shadowmap_atlas
|
||||
bindShadowMapAtlas();
|
||||
#else
|
||||
bindShadowMap();
|
||||
#end
|
||||
}
|
||||
#end
|
||||
path.drawMeshes("translucent");
|
||||
|
@ -342,3 +522,490 @@ class Inc {
|
|||
#end
|
||||
}
|
||||
}
|
||||
|
||||
#if arm_shadowmap_atlas
|
||||
class ShadowMapAtlas {
|
||||
|
||||
public var target: String;
|
||||
public var baseTileSizeConst: Int;
|
||||
public var maxAtlasSizeConst: Int;
|
||||
|
||||
public var sizew: Int;
|
||||
public var sizeh: Int;
|
||||
|
||||
public var currTileOffset = 0;
|
||||
public var tiles: Array<ShadowMapTile> = [];
|
||||
public var activeTiles: Array<ShadowMapTile> = [];
|
||||
public var depth = 1;
|
||||
#if arm_shadowmap_atlas_lod
|
||||
static var tileSizes: Array<Int> = [];
|
||||
static var tileSizeFactor: Array<Float> = [];
|
||||
#end
|
||||
public var updateRenderTarget = false;
|
||||
public static var shadowMapAtlases:Map<String, ShadowMapAtlas> = new Map(); // map a shadowmap atlas to their light type
|
||||
|
||||
function new(light: LightObject) {
|
||||
|
||||
var maxTileSize = shadowMapAtlasSize(light);
|
||||
this.target = shadowMapAtlasName(light.data.raw.type);
|
||||
this.sizew = this.sizeh = this.baseTileSizeConst = maxTileSize;
|
||||
this.depth = getSubdivisions();
|
||||
this.maxAtlasSizeConst = getMaxAtlasSize(light.data.raw.type);
|
||||
|
||||
#if arm_shadowmap_atlas_lod
|
||||
if (tileSizes.length == 0)
|
||||
computeTileSizes(maxTileSize, depth);
|
||||
#end
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a light to an atlas. The atlas is decided based on the type of the light
|
||||
* @param light of type LightObject to be added to an yatlas
|
||||
* @return if the light was added succesfully
|
||||
*/
|
||||
public static function addLight(light: LightObject): Bool {
|
||||
// check if light can be added based on culling
|
||||
if (light.culledLight || light.shadowMapScale == 0.0)
|
||||
return false;
|
||||
|
||||
var atlasName = shadowMapAtlasName(light.data.raw.type);
|
||||
var atlas = shadowMapAtlases.get(atlasName);
|
||||
if (atlas == null) {
|
||||
// create a new atlas
|
||||
atlas = new ShadowMapAtlas(light);
|
||||
shadowMapAtlases.set(atlasName, atlas);
|
||||
}
|
||||
|
||||
// find a free tile for this light
|
||||
var mainTile = ShadowMapTile.assignTiles(light, atlas, null);
|
||||
if (mainTile == null)
|
||||
return false;
|
||||
// push main tile to active tiles
|
||||
atlas.activeTiles.push(mainTile);
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline function shadowMapAtlasSize(light:LightObject):Int {
|
||||
// TODO: this can break because we are changing shadowmap_size elsewhere.
|
||||
return light.data.raw.shadowmap_size;
|
||||
}
|
||||
|
||||
public function getTileSize(shadowMapScale: Float): Int {
|
||||
#if arm_shadowmap_atlas_lod
|
||||
// find the first scale factor that is smaller to the shadowmap scale, and then return the previous one.
|
||||
var i = 0;
|
||||
for (sizeFactor in tileSizeFactor) {
|
||||
if (sizeFactor < shadowMapScale) break;
|
||||
i++;
|
||||
}
|
||||
return tileSizes[i - 1];
|
||||
#else
|
||||
return this.baseTileSizeConst;
|
||||
#end
|
||||
}
|
||||
|
||||
#if arm_shadowmap_atlas_lod
|
||||
static function computeTileSizes(maxTileSize: Int, depth: Int): Void {
|
||||
// find the highest value based on the calculation done in the cluster code
|
||||
var base = LightObject.zToShadowMapScale(0, 16);
|
||||
var subdiv = base / depth;
|
||||
for(i in 0...depth){
|
||||
tileSizes.push(Std.int(maxTileSize / Math.pow(2, i)));
|
||||
tileSizeFactor.push(base);
|
||||
base -= subdiv;
|
||||
}
|
||||
tileSizes.push(0);
|
||||
tileSizeFactor.push(0.0);
|
||||
}
|
||||
#end
|
||||
|
||||
public inline function atlasLimitReached() {
|
||||
// asume square atlas
|
||||
return (currTileOffset + 1) * baseTileSizeConst > maxAtlasSizeConst;
|
||||
}
|
||||
|
||||
public static inline function shadowMapAtlasName(type: String): String {
|
||||
#if arm_shadowmap_atlas_single_map
|
||||
return "shadowMapAtlas";
|
||||
#else
|
||||
switch (type) {
|
||||
case "point":
|
||||
return "shadowMapAtlasPoint";
|
||||
case "sun":
|
||||
return "shadowMapAtlasSun";
|
||||
default:
|
||||
return "shadowMapAtlasSpot";
|
||||
}
|
||||
#end
|
||||
}
|
||||
|
||||
public static inline function getSubdivisions(): Int {
|
||||
#if (rp_shadowmap_atlas_lod_subdivisions == 2)
|
||||
return 2;
|
||||
#elseif (rp_shadowmap_atlas_lod_subdivisions == 3)
|
||||
return 3;
|
||||
#elseif (rp_shadowmap_atlas_lod_subdivisions == 4)
|
||||
return 4;
|
||||
#elseif (rp_shadowmap_atlas_lod_subdivisions == 5)
|
||||
return 5;
|
||||
#elseif (rp_shadowmap_atlas_lod_subdivisions == 6)
|
||||
return 6;
|
||||
#elseif (rp_shadowmap_atlas_lod_subdivisions == 7)
|
||||
return 7;
|
||||
#elseif (rp_shadowmap_atlas_lod_subdivisions == 8)
|
||||
return 8;
|
||||
#elseif (!arm_shadowmap_atlas_lod)
|
||||
return 1;
|
||||
#end
|
||||
}
|
||||
|
||||
public static inline function getMaxAtlasSize(type: String): Int {
|
||||
#if arm_shadowmap_atlas_single_map
|
||||
#if (rp_shadowmap_atlas_max_size == 1024)
|
||||
return 1024;
|
||||
#elseif (rp_shadowmap_atlas_max_size == 2048)
|
||||
return 2048;
|
||||
#elseif (rp_shadowmap_atlas_max_size == 4096)
|
||||
return 4096;
|
||||
#elseif (rp_shadowmap_atlas_max_size == 8192)
|
||||
return 8192;
|
||||
#elseif (rp_shadowmap_atlas_max_size == 16384)
|
||||
return 16384;
|
||||
#end
|
||||
#else
|
||||
switch (type) {
|
||||
case "point": {
|
||||
#if (rp_shadowmap_atlas_max_size_point == 1024)
|
||||
return 1024;
|
||||
#elseif (rp_shadowmap_atlas_max_size_point == 2048)
|
||||
return 2048;
|
||||
#elseif (rp_shadowmap_atlas_max_size_point == 4096)
|
||||
return 4096;
|
||||
#elseif (rp_shadowmap_atlas_max_size_point == 8192)
|
||||
return 8192;
|
||||
#elseif (rp_shadowmap_atlas_max_size_point == 16384)
|
||||
return 16384;
|
||||
#end
|
||||
}
|
||||
case "spot": {
|
||||
#if (rp_shadowmap_atlas_max_size_spot == 1024)
|
||||
return 1024;
|
||||
#elseif (rp_shadowmap_atlas_max_size_spot == 2048)
|
||||
return 2048;
|
||||
#elseif (rp_shadowmap_atlas_max_size_spot == 4096)
|
||||
return 4096;
|
||||
#elseif (rp_shadowmap_atlas_max_size_spot == 8192)
|
||||
return 8192;
|
||||
#elseif (rp_shadowmap_atlas_max_size_spot == 16384)
|
||||
return 16384;
|
||||
#end
|
||||
}
|
||||
case "sun": {
|
||||
#if (rp_shadowmap_atlas_max_size_sun == 1024)
|
||||
return 1024;
|
||||
#elseif (rp_shadowmap_atlas_max_size_sun == 2048)
|
||||
return 2048;
|
||||
#elseif (rp_shadowmap_atlas_max_size_sun == 4096)
|
||||
return 4096;
|
||||
#elseif (rp_shadowmap_atlas_max_size_sun == 8192)
|
||||
return 8192;
|
||||
#elseif (rp_shadowmap_atlas_max_size_sun == 16384)
|
||||
return 16384;
|
||||
#end
|
||||
}
|
||||
default: {
|
||||
#if (rp_shadowmap_atlas_max_size == 1024)
|
||||
return 1024;
|
||||
#elseif (rp_shadowmap_atlas_max_size == 2048)
|
||||
return 2048;
|
||||
#elseif (rp_shadowmap_atlas_max_size == 4096)
|
||||
return 4096;
|
||||
#elseif (rp_shadowmap_atlas_max_size == 8192)
|
||||
return 8192;
|
||||
#elseif (rp_shadowmap_atlas_max_size == 16384)
|
||||
return 16384;
|
||||
#end
|
||||
}
|
||||
|
||||
}
|
||||
#end
|
||||
}
|
||||
}
|
||||
|
||||
class ShadowMapTile {
|
||||
|
||||
public var light:Null<LightObject> = null;
|
||||
public var coordsX:Int;
|
||||
public var coordsY:Int;
|
||||
public var size:Int;
|
||||
public var tiles:Array<ShadowMapTile> = [];
|
||||
public var linkedTile:ShadowMapTile = null;
|
||||
|
||||
#if arm_shadowmap_atlas_lod
|
||||
public var parentTile: ShadowMapTile = null;
|
||||
public var activeSubTiles: Int = 0;
|
||||
public var newTileSize: Int = -1;
|
||||
|
||||
static var tilePattern = [[0, 0], [1, 0], [0, 1], [1, 1]];
|
||||
#end
|
||||
|
||||
function new(coordsX: Int, coordsY: Int, size: Int) {
|
||||
this.coordsX = coordsX;
|
||||
this.coordsY = coordsY;
|
||||
this.size = size;
|
||||
}
|
||||
|
||||
public static function assignTiles(light: LightObject, atlas: ShadowMapAtlas, oldTile: ShadowMapTile): ShadowMapTile {
|
||||
var tileSize = 0;
|
||||
|
||||
#if arm_shadowmap_atlas_lod
|
||||
if (oldTile != null && oldTile.newTileSize != -1) {
|
||||
// reuse tilesize instead of computing it again
|
||||
tileSize = oldTile.newTileSize;
|
||||
oldTile.newTileSize = -1;
|
||||
}
|
||||
else
|
||||
#end
|
||||
tileSize = atlas.getTileSize(light.shadowMapScale);
|
||||
|
||||
if (tileSize == 0)
|
||||
return null;
|
||||
|
||||
var tiles = [];
|
||||
tiles = findCreateTiles(light, oldTile, atlas, tilesLightType(light.data.raw.type), tileSize);
|
||||
|
||||
// lock new tiles with light
|
||||
for (tile in tiles)
|
||||
tile.lockTile(light);
|
||||
|
||||
return linkTiles(tiles);
|
||||
}
|
||||
|
||||
static inline function linkTiles(tiles: Array<ShadowMapTile>): ShadowMapTile {
|
||||
if (tiles.length > 1) {
|
||||
var linkedTile = tiles[0];
|
||||
for (i in 1...tiles.length) {
|
||||
linkedTile.linkedTile = tiles[i];
|
||||
linkedTile = tiles[i];
|
||||
}
|
||||
}
|
||||
return tiles[0];
|
||||
}
|
||||
|
||||
static inline function findCreateTiles(light: LightObject, oldTile: ShadowMapTile, atlas: ShadowMapAtlas, tilesPerLightType: Int, tileSize: Int): Array<ShadowMapTile> {
|
||||
var tilesFound: Array<ShadowMapTile> = [];
|
||||
|
||||
var updateAtlas = false;
|
||||
while (tilesFound.length < tilesPerLightType) {
|
||||
findTiles(light, oldTile, atlas.tiles, tileSize, tilesPerLightType, tilesFound);
|
||||
|
||||
if (tilesFound.length < tilesPerLightType) {
|
||||
tilesFound = []; // empty tilesFound
|
||||
// skip creating more tiles if limit has been reached
|
||||
if (atlas.atlasLimitReached())
|
||||
break;
|
||||
|
||||
createTiles(atlas.tiles, atlas.baseTileSizeConst, atlas.depth, atlas.currTileOffset, atlas.currTileOffset);
|
||||
atlas.currTileOffset++;
|
||||
// update texture to accomodate new size
|
||||
atlas.updateRenderTarget = true;
|
||||
atlas.sizew = atlas.sizeh = atlas.currTileOffset * atlas.baseTileSizeConst;
|
||||
}
|
||||
}
|
||||
return tilesFound;
|
||||
}
|
||||
|
||||
inline static function findTiles(light:LightObject, oldTile: ShadowMapTile,
|
||||
tiles: Array<ShadowMapTile>, size: Int, tilesCount: Int, tilesFound: Array<ShadowMapTile>): Void {
|
||||
#if arm_shadowmap_atlas_lod
|
||||
if (oldTile != null) {
|
||||
// reuse children tiles
|
||||
if (size < oldTile.size) {
|
||||
oldTile.forEachTileLinked(function(lTile) {
|
||||
var childTile = findFreeChildTile(lTile, size);
|
||||
tilesFound.push(childTile);
|
||||
});
|
||||
}
|
||||
// reuse parent tiles
|
||||
else {
|
||||
oldTile.forEachTileLinked(function(lTile) {
|
||||
// find out if parents tiles are not occupied
|
||||
var parentTile = findFreeParentTile(lTile, size);
|
||||
// if parent is free, add it to found tiles
|
||||
if (parentTile != null)
|
||||
tilesFound.push(parentTile);
|
||||
});
|
||||
if (tilesFound.length < tilesCount) {
|
||||
// find naively the rest of the tiles that couldn't be reused
|
||||
findTilesNaive(light, tiles, size, tilesCount, tilesFound);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
#end
|
||||
findTilesNaive(light, tiles, size, tilesCount, tilesFound);
|
||||
}
|
||||
|
||||
#if arm_shadowmap_atlas_lod
|
||||
static inline function findFreeChildTile(tile: ShadowMapTile, size: Int): ShadowMapTile {
|
||||
var childrenTile = tile;
|
||||
while (size < childrenTile.size) {
|
||||
childrenTile = childrenTile.tiles[0];
|
||||
}
|
||||
return childrenTile;
|
||||
}
|
||||
|
||||
static inline function findFreeParentTile(tile: ShadowMapTile, size: Int): ShadowMapTile {
|
||||
var parentTile = tile;
|
||||
while (size > parentTile.size) {
|
||||
parentTile = parentTile.parentTile;
|
||||
// stop if parent tile is occupied
|
||||
if (parentTile.activeSubTiles > 1) {
|
||||
parentTile = null;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return parentTile;
|
||||
}
|
||||
#end
|
||||
|
||||
static function findTilesNaive(light:LightObject, tiles: Array<ShadowMapTile>, size: Int, tilesCount: Int, tilesFound: Array<ShadowMapTile>): Void {
|
||||
for (tile in tiles) {
|
||||
if (tile.size == size) {
|
||||
if (tile.light == null #if arm_shadowmap_atlas_lod && tile.activeSubTiles == 0 #end) {
|
||||
tilesFound.push(tile);
|
||||
// stop after finding enough tiles
|
||||
if (tilesFound.length == tilesCount)
|
||||
return;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// skip over if end of the tree or tile is occupied
|
||||
if (tile.tiles.length == 0 || tile.light != null)
|
||||
continue;
|
||||
findTilesNaive(light, tile.tiles, size, tilesCount, tilesFound);
|
||||
// skip iterating over the rest of the tiles if found enough
|
||||
if (tilesFound.length == tilesCount)
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// create a basic tile and subdivide it if needed
|
||||
public static function createTiles(tiles:Array<ShadowMapTile>, size:Int, depth: Int, baseX:Int, baseY:Int) {
|
||||
var i = baseX;
|
||||
var j = 0;
|
||||
var lastTile = tiles.length;
|
||||
// assume occupied tiles start from 1 line before the base x
|
||||
var occupiedTiles = baseX - 1;
|
||||
|
||||
while (i >= 0) {
|
||||
if (i <= occupiedTiles) { // avoid overriding tiles
|
||||
j = baseY;
|
||||
}
|
||||
while (j <= baseY) {
|
||||
// create base tile of max-size
|
||||
tiles.push(new ShadowMapTile(size * i, size * j, size));
|
||||
#if arm_shadowmap_atlas_lod
|
||||
tiles[lastTile].tiles = subDivTile(tiles[lastTile], size, size * i, size * j, depth - 1);
|
||||
#end
|
||||
lastTile++;
|
||||
j++;
|
||||
}
|
||||
i--;
|
||||
}
|
||||
}
|
||||
|
||||
#if arm_shadowmap_atlas_lod
|
||||
static function subDivTile(parent: ShadowMapTile, size: Int, baseCoordsX: Int, baseCoordsY: Int, depth: Int): Array<ShadowMapTile> {
|
||||
var tileSize = Std.int(size / 2);
|
||||
|
||||
var tiles = [];
|
||||
|
||||
for (i in 0...4) {
|
||||
var coordsX = baseCoordsX + tilePattern[i][0] * tileSize;
|
||||
var coordsY = baseCoordsY + tilePattern[i][1] * tileSize;
|
||||
|
||||
var tile = new ShadowMapTile(coordsX, coordsY, tileSize);
|
||||
tile.parentTile = parent;
|
||||
|
||||
if (depth > 1)
|
||||
tile.tiles = subDivTile(tile, tileSize, coordsX, coordsY, depth - 1);
|
||||
tiles.push(tile);
|
||||
}
|
||||
|
||||
return tiles;
|
||||
}
|
||||
#end
|
||||
|
||||
public static inline function tilesLightType(type: String): Int {
|
||||
switch (type) {
|
||||
case "sun":
|
||||
return LightObject.cascadeCount;
|
||||
case "point":
|
||||
return 6;
|
||||
default:
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
inline function lockTile(light: LightObject): Void {
|
||||
if (this.light != null)
|
||||
return;
|
||||
this.light = light;
|
||||
#if arm_shadowmap_atlas_lod
|
||||
// update the count of used tiles for parents
|
||||
this.forEachParentTile(function (pTile) {
|
||||
pTile.activeSubTiles++;
|
||||
});
|
||||
#end
|
||||
}
|
||||
|
||||
public var unlockLight: Bool = false;
|
||||
|
||||
public function freeTile(): Void {
|
||||
// prevent duplicates
|
||||
if (light != null && unlockLight) {
|
||||
light.lightInAtlas = false;
|
||||
unlockLight = false;
|
||||
}
|
||||
|
||||
var linkedTile = this;
|
||||
var tempTile = this;
|
||||
while (linkedTile != null) {
|
||||
linkedTile.light = null;
|
||||
#if arm_shadowmap_atlas_lod
|
||||
// update the count of used tiles for parents
|
||||
linkedTile.forEachParentTile(function (pTile) {
|
||||
if (pTile.activeSubTiles > 0)
|
||||
pTile.activeSubTiles--;
|
||||
});
|
||||
#end
|
||||
|
||||
linkedTile = linkedTile.linkedTile;
|
||||
// unlink linked tiles
|
||||
tempTile.linkedTile = null;
|
||||
tempTile = linkedTile;
|
||||
}
|
||||
}
|
||||
|
||||
public inline function forEachTileLinked(action: ShadowMapTile->Void): Void {
|
||||
var linkedTile = this;
|
||||
while (linkedTile != null) {
|
||||
action(linkedTile);
|
||||
linkedTile = linkedTile.linkedTile;
|
||||
}
|
||||
}
|
||||
|
||||
#if arm_shadowmap_atlas_lod
|
||||
public inline function forEachParentTile(action: ShadowMapTile->Void): Void {
|
||||
var parentTile = this.parentTile;
|
||||
while (parentTile != null) {
|
||||
action(parentTile);
|
||||
parentTile = parentTile.parentTile;
|
||||
}
|
||||
}
|
||||
#end
|
||||
}
|
||||
#end
|
||||
|
|
|
@ -7,6 +7,8 @@ class RenderPathCreator {
|
|||
|
||||
public static var path: RenderPath;
|
||||
|
||||
public static var commands: Void->Void = function() {};
|
||||
|
||||
#if (rp_renderer == "Forward")
|
||||
public static var setTargetMeshes: Void->Void = RenderPathForward.setTargetMeshes;
|
||||
public static var drawMeshes: Void->Void = RenderPathForward.drawMeshes;
|
||||
|
@ -33,13 +35,22 @@ class RenderPathCreator {
|
|||
|
||||
#if (rp_renderer == "Forward")
|
||||
RenderPathForward.init(path);
|
||||
path.commands = RenderPathForward.commands;
|
||||
path.commands = function() {
|
||||
RenderPathForward.commands();
|
||||
commands();
|
||||
}
|
||||
#elseif (rp_renderer == "Deferred")
|
||||
RenderPathDeferred.init(path);
|
||||
path.commands = RenderPathDeferred.commands;
|
||||
path.commands = function() {
|
||||
RenderPathDeferred.commands();
|
||||
commands();
|
||||
}
|
||||
#elseif (rp_renderer == "Raytracer")
|
||||
RenderPathRaytracer.init(path);
|
||||
path.commands = RenderPathRaytracer.commands;
|
||||
path.commands = function() {
|
||||
RenderPathRaytracer.commands();
|
||||
commands();
|
||||
}
|
||||
#end
|
||||
return path;
|
||||
}
|
||||
|
|
|
@ -502,8 +502,13 @@ class RenderPathDeferred {
|
|||
#end
|
||||
|
||||
#if (rp_shadowmap)
|
||||
// atlasing is exclusive for now
|
||||
#if arm_shadowmap_atlas
|
||||
Inc.drawShadowMapAtlas();
|
||||
#else
|
||||
Inc.drawShadowMap();
|
||||
#end
|
||||
#end
|
||||
|
||||
// Voxels
|
||||
#if rp_voxelao
|
||||
|
@ -544,6 +549,13 @@ class RenderPathDeferred {
|
|||
path.bindTarget("_main", "gbufferD");
|
||||
path.bindTarget("gbuffer0", "gbuffer0");
|
||||
path.bindTarget("gbuffer1", "gbuffer1");
|
||||
|
||||
#if rp_gbuffer2
|
||||
{
|
||||
path.bindTarget("gbuffer2", "gbuffer2");
|
||||
}
|
||||
#end
|
||||
|
||||
#if (rp_ssgi != "Off")
|
||||
{
|
||||
if (armory.data.Config.raw.rp_ssgi != false) {
|
||||
|
@ -572,7 +584,11 @@ class RenderPathDeferred {
|
|||
|
||||
#if rp_shadowmap
|
||||
{
|
||||
#if arm_shadowmap_atlas
|
||||
Inc.bindShadowMapAtlas();
|
||||
#else
|
||||
Inc.bindShadowMap();
|
||||
#end
|
||||
}
|
||||
#end
|
||||
|
||||
|
@ -624,7 +640,11 @@ class RenderPathDeferred {
|
|||
{
|
||||
path.setTarget("singlea");
|
||||
path.bindTarget("_main", "gbufferD");
|
||||
#if arm_shadowmap_atlas
|
||||
Inc.bindShadowMapAtlas();
|
||||
#else
|
||||
Inc.bindShadowMap();
|
||||
#end
|
||||
path.drawShader("shader_datas/volumetric_light/volumetric_light");
|
||||
|
||||
path.setTarget("singleb");
|
||||
|
@ -652,6 +672,7 @@ class RenderPathDeferred {
|
|||
|
||||
#if rp_blending
|
||||
{
|
||||
path.setTarget("tex");
|
||||
path.drawMeshes("blend");
|
||||
}
|
||||
#end
|
||||
|
|
|
@ -300,7 +300,11 @@ class RenderPathForward {
|
|||
|
||||
#if rp_shadowmap
|
||||
{
|
||||
#if arm_shadowmap_atlas
|
||||
Inc.drawShadowMapAtlas();
|
||||
#else
|
||||
Inc.drawShadowMap();
|
||||
#end
|
||||
}
|
||||
#end
|
||||
|
||||
|
@ -352,7 +356,11 @@ class RenderPathForward {
|
|||
|
||||
#if rp_shadowmap
|
||||
{
|
||||
#if arm_shadowmap_atlas
|
||||
Inc.bindShadowMapAtlas();
|
||||
#else
|
||||
Inc.bindShadowMap();
|
||||
#end
|
||||
}
|
||||
#end
|
||||
|
||||
|
@ -466,7 +474,11 @@ class RenderPathForward {
|
|||
{
|
||||
path.setTarget("singlea");
|
||||
path.bindTarget("_main", "gbufferD");
|
||||
#if arm_shadowmap_atlas
|
||||
Inc.bindShadowMapAtlas();
|
||||
#else
|
||||
Inc.bindShadowMap();
|
||||
#end
|
||||
path.drawShader("shader_datas/volumetric_light/volumetric_light");
|
||||
|
||||
path.setTarget("singleb");
|
||||
|
|
74
Sources/armory/system/FSM.hx
Normal file
74
Sources/armory/system/FSM.hx
Normal file
|
@ -0,0 +1,74 @@
|
|||
package armory.system;
|
||||
|
||||
class FSM<T> {
|
||||
final transitions = new Array<Transition<T>>();
|
||||
final tempTransitions = new Array<Transition<T>>();
|
||||
var state: Null<State<T>>;
|
||||
var entered = false;
|
||||
|
||||
public function new() {}
|
||||
|
||||
public function bindTransition(canEnter: Void -> Bool, fromState: State<T>, toState: State<T>) {
|
||||
final transition = new Transition<T>(canEnter, fromState, toState);
|
||||
transitions.push(transition);
|
||||
syncTransitions();
|
||||
}
|
||||
|
||||
public function setInitState(state: State<T>) {
|
||||
this.state = state;
|
||||
syncTransitions();
|
||||
}
|
||||
|
||||
public function update() {
|
||||
if (!entered) {
|
||||
state.onEnter();
|
||||
entered = true;
|
||||
}
|
||||
|
||||
state.onUpdate();
|
||||
|
||||
for (transition in tempTransitions) {
|
||||
if (transition.canEnter()) {
|
||||
state.onExit();
|
||||
state = transition.toState;
|
||||
entered = false;
|
||||
syncTransitions();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function syncTransitions() {
|
||||
tempTransitions.resize(0);
|
||||
|
||||
for (transition in transitions) {
|
||||
if (transition.fromState == state) tempTransitions.push(transition);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class Transition<T> {
|
||||
public final canEnter: Void -> Bool;
|
||||
public final fromState: State<T>;
|
||||
public final toState: State<T>;
|
||||
|
||||
public function new(canEnter: Void -> Bool, fromState: State<T>, toState: State<T>) {
|
||||
this.canEnter = canEnter;
|
||||
this.fromState = fromState;
|
||||
this.toState = toState;
|
||||
}
|
||||
}
|
||||
|
||||
class State<T> {
|
||||
final owner: T;
|
||||
|
||||
public function new(owner: T) {
|
||||
this.owner = owner;
|
||||
}
|
||||
|
||||
public function onEnter() {}
|
||||
|
||||
public function onUpdate() {}
|
||||
|
||||
public function onExit() {}
|
||||
}
|
350
Sources/armory/system/InputMap.hx
Normal file
350
Sources/armory/system/InputMap.hx
Normal file
|
@ -0,0 +1,350 @@
|
|||
package armory.system;
|
||||
|
||||
import kha.FastFloat;
|
||||
import iron.system.Input;
|
||||
|
||||
class InputMap {
|
||||
var commands = new Map<String, Null<Array<InputCommand>>>();
|
||||
|
||||
public function new() {}
|
||||
|
||||
public function addKeyboard(config: String) {
|
||||
var command = new KeyboardCommand();
|
||||
return addCustomCommand(command, config);
|
||||
}
|
||||
|
||||
public function addGamepad(config: String) {
|
||||
var command = new GamepadCommand();
|
||||
return addCustomCommand(command, config);
|
||||
}
|
||||
|
||||
public function addCustomCommand(command: InputCommand, config: String) {
|
||||
if (commands[config] == null) commands[config] = new Array<InputCommand>();
|
||||
commands[config].push(command);
|
||||
return command;
|
||||
}
|
||||
}
|
||||
|
||||
class ActionMap extends InputMap {
|
||||
|
||||
public inline function started(config: String) {
|
||||
var started = false;
|
||||
|
||||
for (c in commands[config]) {
|
||||
if (c.started()) {
|
||||
started = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return started;
|
||||
}
|
||||
|
||||
public inline function released(config: String) {
|
||||
var released = false;
|
||||
|
||||
for (c in commands[config]) {
|
||||
if (c.released()) {
|
||||
released = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return released;
|
||||
}
|
||||
}
|
||||
|
||||
class AxisMap extends InputMap {
|
||||
var scale: FastFloat = 1.0;
|
||||
|
||||
public inline function getAxis(config: String) {
|
||||
var axis = 0.0;
|
||||
|
||||
for (c in commands[config]) {
|
||||
var tempAxis = c.getAxis();
|
||||
|
||||
if (tempAxis != 0.0 && tempAxis != axis) {
|
||||
axis += tempAxis;
|
||||
scale = c.getScale();
|
||||
}
|
||||
}
|
||||
|
||||
return axis;
|
||||
}
|
||||
|
||||
public inline function getScale() {
|
||||
return scale;
|
||||
}
|
||||
}
|
||||
|
||||
class InputCommand {
|
||||
var keys = new Array<String>();
|
||||
var modifiers = new Array<String>();
|
||||
var displacementKeys = new Array<String>();
|
||||
var displacementModifiers = new Array<String>();
|
||||
var deadzone: FastFloat = 0.0;
|
||||
var scale: FastFloat = 1.0;
|
||||
|
||||
public function new() {}
|
||||
|
||||
public function setKeys(keys: Array<String>) {
|
||||
return this.keys = keys;
|
||||
}
|
||||
|
||||
public function setMods(modifiers: Array<String>) {
|
||||
return this.modifiers = modifiers;
|
||||
}
|
||||
|
||||
public function setDisplacementKeys(keys: Array<String>) {
|
||||
return displacementKeys = keys;
|
||||
}
|
||||
|
||||
public function setDisplacementMods(modifiers: Array<String>) {
|
||||
return displacementModifiers = modifiers;
|
||||
}
|
||||
|
||||
public function setDeadzone(deadzone: FastFloat) {
|
||||
return this.deadzone = deadzone;
|
||||
}
|
||||
|
||||
public function setScale(scale: FastFloat) {
|
||||
return this.scale = scale;
|
||||
}
|
||||
|
||||
public function getScale() {
|
||||
return scale;
|
||||
}
|
||||
|
||||
public function started() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public function released() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public function getAxis(): FastFloat {
|
||||
return 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
class KeyboardCommand extends InputCommand {
|
||||
var keyboard = Input.getKeyboard();
|
||||
var mouse = Input.getMouse();
|
||||
|
||||
public inline override function started() {
|
||||
for (k in keys) {
|
||||
if (keyboard.started(k)) {
|
||||
for (m in modifiers) {
|
||||
if (!keyboard.down(m)) return false;
|
||||
}
|
||||
|
||||
for (m in displacementModifiers) {
|
||||
if (!mouse.down(m)) return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
for (k in displacementKeys) {
|
||||
if (mouse.started(k)) {
|
||||
for (m in modifiers) {
|
||||
if (!keyboard.down(m)) return false;
|
||||
}
|
||||
|
||||
for (m in displacementModifiers) {
|
||||
if (!mouse.down(m)) return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public inline override function released() {
|
||||
for (k in keys) {
|
||||
if (keyboard.released(k)) {
|
||||
for (m in modifiers) {
|
||||
if (!keyboard.down(m)) return false;
|
||||
}
|
||||
|
||||
for (m in displacementModifiers) {
|
||||
if (!mouse.down(m)) return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
for (k in displacementKeys) {
|
||||
if (mouse.released(k)) {
|
||||
for (m in modifiers) {
|
||||
if (!keyboard.down(m)) return false;
|
||||
}
|
||||
|
||||
for (m in displacementModifiers) {
|
||||
if (!mouse.down(m)) return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public inline override function getAxis() {
|
||||
var axis = 0.0;
|
||||
var movementX = mouse.movementX;
|
||||
var movementY = mouse.movementY;
|
||||
var wheelDelta = mouse.wheelDelta;
|
||||
|
||||
for (k in keys) {
|
||||
if (keyboard.down(k)) {
|
||||
axis++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (m in modifiers) {
|
||||
if (keyboard.down(m)) {
|
||||
axis --;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (k in displacementKeys) {
|
||||
switch (k) {
|
||||
case "moved x": if (movementX > deadzone) axis++;
|
||||
case "moved y": if (movementY > deadzone) axis--;
|
||||
case "wheel": if (wheelDelta < -deadzone) axis++;
|
||||
case "movement x": if (movementX > deadzone) return movementX - deadzone;
|
||||
case "movement y": if (movementY > deadzone) return movementY - deadzone;
|
||||
default: {
|
||||
if (mouse.down(k)) {
|
||||
axis ++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (m in displacementModifiers) {
|
||||
switch (m) {
|
||||
case "moved x": if (movementX < -deadzone) axis--;
|
||||
case "moved y": if (movementY < -deadzone) axis++;
|
||||
case "wheel": if (wheelDelta > deadzone) axis--;
|
||||
case "movement x": if (movementX < -deadzone) return movementX + deadzone;
|
||||
case "movement y": if (movementY < -deadzone) return movementY + deadzone;
|
||||
default: {
|
||||
if (mouse.down(m)) {
|
||||
axis --;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return axis > 1 ? 1 : axis < -1 ? -1 : axis;
|
||||
}
|
||||
}
|
||||
|
||||
class GamepadCommand extends InputCommand {
|
||||
var gamepad = Input.getGamepad(0);
|
||||
|
||||
public inline override function started() {
|
||||
for (k in keys) {
|
||||
if (gamepad.started(k)) {
|
||||
for (m in modifiers) {
|
||||
if (gamepad.down(m) < deadzone) return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public inline override function released() {
|
||||
for (k in keys) {
|
||||
if (gamepad.released(k)) {
|
||||
for (m in modifiers) {
|
||||
if (gamepad.down(m) < deadzone) return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public inline override function getAxis() {
|
||||
var axis = 0.0;
|
||||
var rsMovementX = gamepad.rightStick.movementX;
|
||||
var rsMovementY = gamepad.rightStick.movementY;
|
||||
var lsMovementX = gamepad.leftStick.movementX;
|
||||
var lsMovementY = gamepad.leftStick.movementY;
|
||||
var rtPressure = gamepad.down("r2") > 0.0 ? (gamepad.down("r2") - deadzone) / (1 - deadzone) : 0.0;
|
||||
var ltPressure = gamepad.down("l2") > 0.0 ? (gamepad.down("r2") - deadzone) / (1 - deadzone) : 0.0;
|
||||
|
||||
for (k in keys) {
|
||||
switch(k) {
|
||||
case "rtPressure": axis += rtPressure;
|
||||
case "ltPressure": axis += ltPressure;
|
||||
default: {
|
||||
if (gamepad.down(k) > deadzone) {
|
||||
axis++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (m in modifiers) {
|
||||
switch (m) {
|
||||
case "rtPressure": axis -= rtPressure;
|
||||
case "ltPressure": axis -= ltPressure;
|
||||
default: {
|
||||
if (gamepad.down(m) > deadzone) {
|
||||
axis--;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (k in displacementKeys) {
|
||||
switch(k) {
|
||||
case "rs moved x": if (rsMovementX > deadzone) axis++;
|
||||
case "rs moved y": if (rsMovementY > deadzone) axis++;
|
||||
case "ls moved x": if (lsMovementX > deadzone) axis++;
|
||||
case "ls moved y": if (lsMovementY > deadzone) axis++;
|
||||
case "rs movement x": if (rsMovementX > deadzone) return rsMovementX - deadzone;
|
||||
case "rs movement y": if (rsMovementY > deadzone) return rsMovementY - deadzone;
|
||||
case "ls movement x": if (lsMovementX > deadzone) return lsMovementX - deadzone;
|
||||
case "ls movement y": if (lsMovementY > deadzone) return lsMovementY - deadzone;
|
||||
}
|
||||
}
|
||||
|
||||
for (m in displacementModifiers) {
|
||||
switch (m) {
|
||||
case "rs moved x": if (rsMovementX < -deadzone) axis--;
|
||||
case "rs moved y": if (rsMovementY < -deadzone) axis--;
|
||||
case "ls moved x": if (lsMovementX < -deadzone) axis--;
|
||||
case "ls moved y": if (lsMovementY < -deadzone) axis--;
|
||||
case "rs movement x": if (rsMovementX < -deadzone) return rsMovementX + deadzone;
|
||||
case "rs movement y": if (rsMovementY < -deadzone) return rsMovementY + deadzone;
|
||||
case "ls movement x": if (lsMovementX < -deadzone) return lsMovementX + deadzone;
|
||||
case "ls movement y": if (lsMovementY < -deadzone) return lsMovementY + deadzone;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return axis > 1 ? 1 : axis < -1 ? -1 : axis;
|
||||
}
|
||||
}
|
|
@ -421,6 +421,9 @@ class DebugConsole extends Trait {
|
|||
var lightHandle = Id.handle();
|
||||
lightHandle.value = light.data.raw.strength / 10;
|
||||
light.data.raw.strength = ui.slider(lightHandle, "Strength", 0.0, 5.0, true) * 10;
|
||||
#if arm_shadowmap_atlas
|
||||
ui.text("status: " + (light.culledLight ? "culled" : "rendered"));
|
||||
#end
|
||||
}
|
||||
else if (Std.is(selectedObject, iron.object.CameraObject)) {
|
||||
selectedType = "(Camera)";
|
||||
|
|
|
@ -6,7 +6,6 @@ import iron.Trait;
|
|||
import iron.system.Time;
|
||||
import iron.math.Vec4;
|
||||
import iron.math.RayCaster;
|
||||
import iron.data.SceneFormat;
|
||||
|
||||
class Hit {
|
||||
|
||||
|
@ -285,7 +284,7 @@ class PhysicsWorld extends Trait {
|
|||
}
|
||||
|
||||
function updateContacts() {
|
||||
contacts = [];
|
||||
contacts.resize(0);
|
||||
|
||||
var disp: bullet.Bt.Dispatcher = dispatcher;
|
||||
var numManifolds = disp.getNumManifolds();
|
||||
|
@ -329,12 +328,12 @@ class PhysicsWorld extends Trait {
|
|||
}
|
||||
}
|
||||
|
||||
public function pickClosest(inputX: Float, inputY: Float): RigidBody {
|
||||
public function pickClosest(inputX: Float, inputY: Float, group: Int = 0x00000001, mask = 0xFFFFFFFF): RigidBody {
|
||||
var camera = iron.Scene.active.camera;
|
||||
var start = new Vec4();
|
||||
var end = new Vec4();
|
||||
RayCaster.getDirection(start, end, inputX, inputY, camera);
|
||||
var hit = rayCast(camera.transform.world.getLoc(), end);
|
||||
var hit = rayCast(camera.transform.world.getLoc(), end, group, mask);
|
||||
var rb = (hit != null) ? hit.rb : null;
|
||||
return rb;
|
||||
}
|
||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 245 B After Width: | Height: | Size: 6.3 KiB |
Binary file not shown.
Before Width: | Height: | Size: 5 KiB After Width: | Height: | Size: 1 KiB |
Binary file not shown.
Before Width: | Height: | Size: 4.9 KiB After Width: | Height: | Size: 725 B |
|
@ -32,6 +32,7 @@ import arm.material.mat_batch as mat_batch
|
|||
import arm.utils
|
||||
import arm.profiler
|
||||
|
||||
import arm.log as log
|
||||
|
||||
@unique
|
||||
class NodeType(Enum):
|
||||
|
@ -1157,7 +1158,16 @@ class ArmoryExporter:
|
|||
else:
|
||||
invscale_tex = 1 * 32767
|
||||
if has_tang:
|
||||
exportMesh.calc_tangents(uvmap=lay0.name)
|
||||
try:
|
||||
exportMesh.calc_tangents(uvmap=lay0.name)
|
||||
except Exception as e:
|
||||
if hasattr(e, 'message'):
|
||||
log.error(e.message)
|
||||
else:
|
||||
# Assume it was caused because of encountering n-gons
|
||||
log.error(f"""object {bobject.name} contains n-gons in its mesh, so it's impossible to compute tanget space for normal mapping.
|
||||
Make sure the mesh only has tris/quads.""")
|
||||
|
||||
tangdata = np.empty(num_verts * 3, dtype='<f4')
|
||||
if has_col:
|
||||
cdata = np.empty(num_verts * 3, dtype='<f4')
|
||||
|
@ -1950,8 +1960,9 @@ class ArmoryExporter:
|
|||
self.output['tilesheet_datas'].append(o)
|
||||
|
||||
def export_world(self):
|
||||
"""Exports the world of the scene."""
|
||||
"""Exports the world of the current scene."""
|
||||
world = self.scene.world
|
||||
|
||||
if world is not None:
|
||||
world_name = arm.utils.safestr(world.name)
|
||||
|
||||
|
@ -1962,6 +1973,9 @@ class ArmoryExporter:
|
|||
self.post_export_world(world, out_world)
|
||||
self.output['world_datas'].append(out_world)
|
||||
|
||||
elif arm.utils.get_rp().rp_background == 'World':
|
||||
log.warn(f'Scene "{self.scene.name}" is missing a world, some render targets will not be cleared')
|
||||
|
||||
def export_objects(self, scene):
|
||||
"""Exports all supported blender objects.
|
||||
|
||||
|
@ -2105,7 +2119,7 @@ class ArmoryExporter:
|
|||
self.output['camera_ref'] = self.scene.camera.name
|
||||
else:
|
||||
if self.scene.name == arm.utils.get_project_scene_name():
|
||||
log.warn('No camera found in active scene')
|
||||
log.warn(f'Scene "{self.scene.name}" is missing a camera')
|
||||
|
||||
self.output['material_datas'] = []
|
||||
|
||||
|
@ -2148,7 +2162,7 @@ class ArmoryExporter:
|
|||
|
||||
# No camera found
|
||||
if not self.camera_spawned:
|
||||
log.warn('No camera found in active scene layers')
|
||||
log.warn( f'Scene "{self.scene.name}" is missing a camera')
|
||||
|
||||
# No camera found, create a default one
|
||||
if (len(self.output['camera_datas']) == 0 or len(bpy.data.cameras) == 0) or not self.camera_spawned:
|
||||
|
@ -2182,7 +2196,7 @@ class ArmoryExporter:
|
|||
if self.scene.frame_current != current_frame:
|
||||
self.scene.frame_set(current_frame, subframe=current_subframe)
|
||||
|
||||
print('Scene exported in ' + str(time.time() - profile_time))
|
||||
print('Scene exported in {:0.3f}s'.format(time.time() - profile_time))
|
||||
|
||||
def create_default_camera(self, is_viewport_camera=False):
|
||||
"""Creates the default camera and adds a WalkNavigation trait to it."""
|
||||
|
@ -2304,6 +2318,10 @@ class ArmoryExporter:
|
|||
|
||||
return instanced_type, instanced_data
|
||||
|
||||
@staticmethod
|
||||
def rigid_body_static(rb):
|
||||
return (not rb.enabled and not rb.kinematic) or (rb.type == 'PASSIVE' and not rb.kinematic)
|
||||
|
||||
def post_export_object(self, bobject: bpy.types.Object, o, type):
|
||||
# Export traits
|
||||
self.export_traits(bobject, o)
|
||||
|
@ -2330,7 +2348,7 @@ class ArmoryExporter:
|
|||
elif rb.collision_shape == 'CAPSULE':
|
||||
shape = 6
|
||||
body_mass = rb.mass
|
||||
is_static = (not rb.enabled and not rb.kinematic) or (rb.type == 'PASSIVE' and not rb.kinematic)
|
||||
is_static = self.rigid_body_static(rb)
|
||||
if is_static:
|
||||
body_mass = 0
|
||||
x = {}
|
||||
|
|
|
@ -17,6 +17,8 @@ def register():
|
|||
|
||||
km = addon_keyconfig.keymaps.new(name='Window', space_type='EMPTY', region_type="WINDOW")
|
||||
km.keymap_items.new(props_ui.ArmoryPlayButton.bl_idname, type='F5', value='PRESS')
|
||||
km.keymap_items.new("tlm.build_lightmaps", type='F6', value='PRESS')
|
||||
km.keymap_items.new("tlm.clean_lightmaps", type='F7', value='PRESS')
|
||||
arm_keymaps.append(km)
|
||||
|
||||
def unregister():
|
||||
|
|
|
@ -29,12 +29,15 @@ else:
|
|||
|
||||
info_text = ''
|
||||
num_warnings = 0
|
||||
num_errors = 0
|
||||
|
||||
def clear(clear_warnings=False):
|
||||
global info_text, num_warnings
|
||||
def clear(clear_warnings=False, clear_errors=False):
|
||||
global info_text, num_warnings, num_errors
|
||||
info_text = ''
|
||||
if clear_warnings:
|
||||
num_warnings = 0
|
||||
if clear_errors:
|
||||
num_errors = 0
|
||||
|
||||
def format_text(text):
|
||||
return (text[:80] + '..') if len(text) > 80 else text # Limit str size
|
||||
|
@ -54,7 +57,7 @@ def info(text):
|
|||
info_text = format_text(text)
|
||||
|
||||
def print_warn(text):
|
||||
log('Warning: ' + text, WARN)
|
||||
log('WARNING: ' + text, WARN)
|
||||
|
||||
def warn(text):
|
||||
global num_warnings
|
||||
|
@ -62,4 +65,6 @@ def warn(text):
|
|||
print_warn(text)
|
||||
|
||||
def error(text):
|
||||
global num_errors
|
||||
num_errors += 1
|
||||
log('ERROR: ' + text, ERROR)
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
from arm.logicnode.arm_nodes import *
|
||||
|
||||
class OnVolumeTriggerNode(ArmLogicTreeNode):
|
||||
"""Activates the output when the given rigid body enter, overlap or leave the given trigger.
|
||||
"""Activates the output when the given object enters, overlaps or leaves the bounding box of the given trigger object. (Note: Works even if objects are not Rigid Bodies).
|
||||
|
||||
@input RB: this object is taken as the entering object
|
||||
@input Trigger: this object is used as the volume trigger
|
||||
|
|
|
@ -1,8 +1,19 @@
|
|||
from arm.logicnode.arm_nodes import *
|
||||
|
||||
class PickObjectNode(ArmLogicTreeNode):
|
||||
"""Pickes the rigid body in the given location using the screen
|
||||
coordinates (2D)."""
|
||||
"""Picks the rigid body in the given location using the screen
|
||||
coordinates (2D).
|
||||
|
||||
@seeNode Mask
|
||||
|
||||
@input Screen Coords: the location at which to pick, in screen
|
||||
coordinates
|
||||
@input Mask: a bit mask value to specify which
|
||||
objects are considered
|
||||
|
||||
@output RB: the object that was hit
|
||||
@output Hit: the hit position in world coordinates
|
||||
"""
|
||||
bl_idname = 'LNPickObjectNode'
|
||||
bl_label = 'Pick RB'
|
||||
arm_section = 'ray'
|
||||
|
@ -11,6 +22,7 @@ class PickObjectNode(ArmLogicTreeNode):
|
|||
def init(self, context):
|
||||
super(PickObjectNode, self).init(context)
|
||||
self.add_input('NodeSocketVector', 'Screen Coords')
|
||||
self.add_input('NodeSocketInt', 'Mask', default_value=1)
|
||||
|
||||
self.add_output('ArmNodeSocketObject', 'RB')
|
||||
self.add_output('NodeSocketVector', 'Hit')
|
||||
|
|
|
@ -9,6 +9,7 @@ import threading
|
|||
import webbrowser
|
||||
import shlex
|
||||
import errno
|
||||
import math
|
||||
|
||||
import bpy
|
||||
|
||||
|
@ -61,10 +62,9 @@ def remove_readonly(func, path, excinfo):
|
|||
def export_data(fp, sdk_path):
|
||||
wrd = bpy.data.worlds['Arm']
|
||||
|
||||
print('\n' + '_' * 10 + ' [Armory] Compiling ' + '_' * 10)
|
||||
print('Armory v{0} ({1})'.format(wrd.arm_version, wrd.arm_commit))
|
||||
if wrd.arm_verbose_output:
|
||||
print('\nArmory v{0} ({1})'.format(wrd.arm_version, wrd.arm_commit))
|
||||
print('OS: ' + arm.utils.get_os() + ', Target: ' + state.target + ', GAPI: ' + arm.utils.get_gapi() + ', Blender: ' + bpy.app.version_string)
|
||||
print(f'Blender: {bpy.app.version_string}, Target: {state.target}, GAPI: {arm.utils.get_gapi()}')
|
||||
|
||||
# Clean compiled variants if cache is disabled
|
||||
build_dir = arm.utils.get_fp_build()
|
||||
|
@ -157,10 +157,10 @@ def export_data(fp, sdk_path):
|
|||
cdefs = arm.utils.def_strings_to_array(wrd.compo_defs)
|
||||
|
||||
if wrd.arm_verbose_output:
|
||||
print('Exported modules:', modules)
|
||||
print('Shader flags:', defs)
|
||||
print('Compositor flags:', cdefs)
|
||||
print('Khafile flags:', assets.khafile_defs)
|
||||
print('Exported modules:', ', '.join(modules))
|
||||
print('Shader flags:', ' '.join(defs))
|
||||
print('Compositor flags:', ' '.join(cdefs))
|
||||
print('Khafile flags:', ' '.join(assets.khafile_defs))
|
||||
|
||||
# Render path is configurable at runtime
|
||||
has_config = wrd.arm_write_config or os.path.exists(arm.utils.get_fp() + '/Bundled/config.arm')
|
||||
|
@ -338,7 +338,7 @@ def build(target, is_play=False, is_publish=False, is_export=False):
|
|||
if arm.utils.get_save_on_build():
|
||||
bpy.ops.wm.save_mainfile()
|
||||
|
||||
log.clear(clear_warnings=True)
|
||||
log.clear(clear_warnings=True, clear_errors=True)
|
||||
|
||||
# Set camera in active scene
|
||||
active_scene = arm.utils.get_active_scene()
|
||||
|
@ -431,7 +431,7 @@ def compilation_server_done():
|
|||
log.error('Build failed, check console')
|
||||
|
||||
def build_done():
|
||||
print('Finished in ' + str(time.time() - profile_time))
|
||||
print('Finished in {:0.3f}s'.format(time.time() - profile_time))
|
||||
if log.num_warnings > 0:
|
||||
log.print_warn(f'{log.num_warnings} warning{"s" if log.num_warnings > 1 else ""} occurred during compilation')
|
||||
if state.proc_build is None:
|
||||
|
|
|
@ -1,8 +1,11 @@
|
|||
import os
|
||||
from typing import Optional, TextIO
|
||||
|
||||
import bpy
|
||||
import arm.utils
|
||||
import arm.log
|
||||
|
||||
from arm.exporter import ArmoryExporter
|
||||
import arm.log
|
||||
import arm.utils
|
||||
|
||||
parsed_nodes = []
|
||||
parsed_ids = dict() # Sharing node data
|
||||
|
@ -100,7 +103,9 @@ def build_node_tree(node_group):
|
|||
f.write('}')
|
||||
node_group.arm_cached = True
|
||||
|
||||
def build_node(node: bpy.types.Node, f):
|
||||
|
||||
def build_node(node: bpy.types.Node, f: TextIO) -> Optional[str]:
|
||||
"""Builds the given node and returns its name. f is an opened file object."""
|
||||
global parsed_nodes
|
||||
global parsed_ids
|
||||
|
||||
|
@ -126,7 +131,7 @@ def build_node(node: bpy.types.Node, f):
|
|||
parsed_nodes.append(name)
|
||||
|
||||
# Create node
|
||||
node_type = node.bl_idname[2:] # Discard 'LN'TimeNode prefix
|
||||
node_type = node.bl_idname[2:] # Discard 'LN' prefix
|
||||
f.write('\t\tvar ' + name + ' = new armory.logicnode.' + node_type + '(this);\n')
|
||||
|
||||
# Handle Function Nodes
|
||||
|
@ -167,26 +172,45 @@ def build_node(node: bpy.types.Node, f):
|
|||
|
||||
# Create inputs
|
||||
for inp in node.inputs:
|
||||
# Is linked - find node
|
||||
# True if the input is connected to a unlinked reroute
|
||||
# somewhere down the reroute line
|
||||
unconnected = False
|
||||
|
||||
# Is linked -> find the connected node
|
||||
if inp.is_linked:
|
||||
n = inp.links[0].from_node
|
||||
socket = inp.links[0].from_socket
|
||||
if (inp.bl_idname == 'ArmNodeSocketAction' and socket.bl_idname != 'ArmNodeSocketAction') or \
|
||||
(socket.bl_idname == 'ArmNodeSocketAction' and inp.bl_idname != 'ArmNodeSocketAction'):
|
||||
print('Armory Error: Sockets do not match in logic node tree "{0}" - node "{1}" - socket "{2}"'.format(group_name, node.name, inp.name))
|
||||
inp_name = build_node(n, f)
|
||||
for i in range(0, len(n.outputs)):
|
||||
if n.outputs[i] == socket:
|
||||
inp_from = i
|
||||
|
||||
# Follow reroutes first
|
||||
while n.type == "REROUTE":
|
||||
if len(n.inputs) == 0 or not n.inputs[0].is_linked:
|
||||
unconnected = True
|
||||
break
|
||||
# Not linked - create node with default values
|
||||
|
||||
socket = n.inputs[0].links[0].from_socket
|
||||
n = n.inputs[0].links[0].from_node
|
||||
|
||||
if not unconnected:
|
||||
if (inp.bl_idname == 'ArmNodeSocketAction' and socket.bl_idname != 'ArmNodeSocketAction') or \
|
||||
(socket.bl_idname == 'ArmNodeSocketAction' and inp.bl_idname != 'ArmNodeSocketAction'):
|
||||
arm.log.warn(f'Sockets do not match in logic node tree "{group_name}": node "{node.name}", socket "{inp.name}"')
|
||||
|
||||
inp_name = build_node(n, f)
|
||||
for i in range(0, len(n.outputs)):
|
||||
if n.outputs[i] == socket:
|
||||
inp_from = i
|
||||
break
|
||||
|
||||
# Not linked -> create node with default values
|
||||
else:
|
||||
inp_name = build_default_node(inp)
|
||||
inp_from = 0
|
||||
|
||||
# The input is linked to a reroute, but the reroute is unlinked
|
||||
if inp_name == None:
|
||||
if unconnected:
|
||||
inp_name = build_default_node(inp)
|
||||
inp_from = 0
|
||||
|
||||
# Add input
|
||||
f.write('\t\t' + name + '.addInput(' + inp_name + ', ' + str(inp_from) + ');\n')
|
||||
|
||||
|
|
|
@ -43,6 +43,22 @@ def add_world_defs():
|
|||
if rpdat.rp_shadowmap_cascades != '1':
|
||||
wrd.world_defs += '_CSM'
|
||||
assets.add_khafile_def('arm_csm')
|
||||
if rpdat.rp_shadowmap_atlas:
|
||||
assets.add_khafile_def('arm_shadowmap_atlas')
|
||||
wrd.world_defs += '_ShadowMapAtlas'
|
||||
if rpdat.rp_shadowmap_atlas_single_map:
|
||||
assets.add_khafile_def('arm_shadowmap_atlas_single_map')
|
||||
wrd.world_defs += '_SingleAtlas'
|
||||
assets.add_khafile_def('rp_shadowmap_atlas_max_size_point={0}'.format(int(rpdat.rp_shadowmap_atlas_max_size_point)))
|
||||
assets.add_khafile_def('rp_shadowmap_atlas_max_size_spot={0}'.format(int(rpdat.rp_shadowmap_atlas_max_size_spot)))
|
||||
assets.add_khafile_def('rp_shadowmap_atlas_max_size_sun={0}'.format(int(rpdat.rp_shadowmap_atlas_max_size_sun)))
|
||||
assets.add_khafile_def('rp_shadowmap_atlas_max_size={0}'.format(int(rpdat.rp_shadowmap_atlas_max_size)))
|
||||
|
||||
assets.add_khafile_def('rp_max_lights_cluster={0}'.format(int(rpdat.rp_max_lights_cluster)))
|
||||
assets.add_khafile_def('rp_max_lights={0}'.format(int(rpdat.rp_max_lights)))
|
||||
if rpdat.rp_shadowmap_atlas_lod:
|
||||
assets.add_khafile_def('arm_shadowmap_atlas_lod')
|
||||
assets.add_khafile_def('rp_shadowmap_atlas_lod_subdivisions={0}'.format(int(rpdat.rp_shadowmap_atlas_lod_subdivisions)))
|
||||
# SS
|
||||
if rpdat.rp_ssgi == 'RTGI' or rpdat.rp_ssgi == 'RTAO':
|
||||
if rpdat.rp_ssgi == 'RTGI':
|
||||
|
@ -89,9 +105,14 @@ def add_world_defs():
|
|||
wrd.world_defs += '_Spot'
|
||||
assets.add_khafile_def('arm_spot')
|
||||
|
||||
if point_lights == 1:
|
||||
wrd.world_defs += '_SinglePoint'
|
||||
elif point_lights > 1:
|
||||
if not rpdat.rp_shadowmap_atlas:
|
||||
if point_lights == 1:
|
||||
wrd.world_defs += '_SinglePoint'
|
||||
elif point_lights > 1:
|
||||
wrd.world_defs += '_Clusters'
|
||||
assets.add_khafile_def('arm_clusters')
|
||||
else:
|
||||
wrd.world_defs += '_SMSizeUniform'
|
||||
wrd.world_defs += '_Clusters'
|
||||
assets.add_khafile_def('arm_clusters')
|
||||
|
||||
|
@ -346,7 +367,21 @@ def build():
|
|||
assets.add_khafile_def('rp_chromatic_aberration')
|
||||
assets.add_shader_pass('chromatic_aberration_pass')
|
||||
|
||||
gbuffer2 = '_Veloc' in wrd.world_defs
|
||||
ignoreIrr = False
|
||||
|
||||
for obj in bpy.data.objects:
|
||||
if obj.type == "MESH":
|
||||
for slot in obj.material_slots:
|
||||
mat = slot.material
|
||||
|
||||
if mat: #Check if not NoneType
|
||||
|
||||
if mat.arm_ignore_irradiance:
|
||||
ignoreIrr = True
|
||||
|
||||
if ignoreIrr:
|
||||
wrd.world_defs += '_IgnoreIrr'
|
||||
gbuffer2 = '_Veloc' in wrd.world_defs or '_IgnoreIrr' in wrd.world_defs
|
||||
if gbuffer2:
|
||||
assets.add_khafile_def('rp_gbuffer2')
|
||||
wrd.world_defs += '_gbuffer2'
|
||||
|
|
|
@ -331,6 +331,7 @@ def parse_vector(node: bpy.types.Node, socket: bpy.types.NodeSocket) -> str:
|
|||
'COMBXYZ': nodes_converter.parse_combxyz,
|
||||
'VECT_MATH': nodes_converter.parse_vectormath,
|
||||
'DISPLACEMENT': nodes_vector.parse_displacement,
|
||||
'VECTOR_ROTATE': nodes_vector.parse_vectorrotate,
|
||||
}
|
||||
|
||||
if node.type in node_parser_funcs:
|
||||
|
@ -433,6 +434,7 @@ def parse_value(node, socket):
|
|||
'SEPRGB': nodes_converter.parse_seprgb,
|
||||
'SEPXYZ': nodes_converter.parse_sepxyz,
|
||||
'VECT_MATH': nodes_converter.parse_vectormath,
|
||||
'MAP_RANGE': nodes_converter.parse_maprange,
|
||||
}
|
||||
|
||||
if node.type in node_parser_funcs:
|
||||
|
|
|
@ -332,3 +332,186 @@ vec3 wrap(const vec3 value, const vec3 max, const vec3 min) {
|
|||
\t wrap(value.z, max.z, min.z));
|
||||
}
|
||||
"""
|
||||
|
||||
str_blackbody = """
|
||||
vec3 blackbody(const float temperature){
|
||||
|
||||
vec3 rgb = vec3(0.0, 0.0, 0.0);
|
||||
|
||||
vec3 r = vec3(0.0, 0.0, 0.0);
|
||||
vec3 g = vec3(0.0, 0.0, 0.0);
|
||||
vec3 b = vec3(0.0, 0.0, 0.0);
|
||||
|
||||
float t_inv = float(1.0 / temperature);
|
||||
|
||||
if (temperature >= 12000.0) {
|
||||
|
||||
rgb = vec3(0.826270103, 0.994478524, 1.56626022);
|
||||
|
||||
} else if(temperature < 965.0) {
|
||||
|
||||
rgb = vec3(4.70366907, 0.0, 0.0);
|
||||
|
||||
} else {
|
||||
|
||||
if (temperature >= 6365.0) {
|
||||
vec3 r = vec3(3.78765709e+03, 9.36026367e-06, 3.98995841e-01);
|
||||
vec3 g = vec3(-5.00279505e+02, -4.59745390e-06, 1.09090465e+00);
|
||||
vec4 b = vec4(6.72595954e-13, -2.73059993e-08, 4.24068546e-04, -7.52204323e-01);
|
||||
|
||||
rgb = vec3(r.r * t_inv + r.g * temperature + r.b, g.r * t_inv + g.g * temperature + g.b, ((b.r * temperature + b.g) * temperature + b.b) * temperature + b.a );
|
||||
|
||||
} else if (temperature >= 3315.0) {
|
||||
vec3 r = vec3(4.60124770e+03, 2.89727618e-05, 1.48001316e-01);
|
||||
vec3 g = vec3(-1.18134453e+03, -2.18913373e-05, 1.30656109e+00);
|
||||
vec4 b = vec4(-2.22463426e-13, -1.55078698e-08, 3.81675160e-04, -7.30646033e-01);
|
||||
|
||||
rgb = vec3(r.r * t_inv + r.g * temperature + r.b, g.r * t_inv + g.g * temperature + g.b, ((b.r * temperature + b.g) * temperature + b.b) * temperature + b.a );
|
||||
|
||||
} else if (temperature >= 1902.0) {
|
||||
vec3 r = vec3(4.66849800e+03, 2.85655028e-05, 1.29075375e-01);
|
||||
vec3 g = vec3(-1.42546105e+03, -4.01730887e-05, 1.44002695e+00);
|
||||
vec4 b = vec4(-2.02524603e-11, 1.79435860e-07, -2.60561875e-04, -1.41761141e-02);
|
||||
|
||||
rgb = vec3(r.r * t_inv + r.g * temperature + r.b, g.r * t_inv + g.g * temperature + g.b, ((b.r * temperature + b.g) * temperature + b.b) * temperature + b.a );
|
||||
|
||||
} else if (temperature >= 1449.0) {
|
||||
vec3 r = vec3(4.10671449e+03, -8.61949938e-05, 6.41423749e-01);
|
||||
vec3 g = vec3(-1.22075471e+03, 2.56245413e-05, 1.20753416e+00);
|
||||
vec4 b = vec4(0.0, 0.0, 0.0, 0.0);
|
||||
|
||||
rgb = vec3(r.r * t_inv + r.g * temperature + r.b, g.r * t_inv + g.g * temperature + g.b, ((b.r * temperature + b.g) * temperature + b.b) * temperature + b.a );
|
||||
|
||||
} else if (temperature >= 1167.0) {
|
||||
vec3 r = vec3(3.37763626e+03, -4.34581697e-04, 1.64843306e+00);
|
||||
vec3 g = vec3(-1.00402363e+03, 1.29189794e-04, 9.08181524e-01);
|
||||
vec4 b = vec4(0.0, 0.0, 0.0, 0.0);
|
||||
|
||||
rgb = vec3(r.r * t_inv + r.g * temperature + r.b, g.r * t_inv + g.g * temperature + g.b, ((b.r * temperature + b.g) * temperature + b.b) * temperature + b.a );
|
||||
|
||||
} else {
|
||||
vec3 r = vec3(2.52432244e+03, -1.06185848e-03, 3.11067539e+00);
|
||||
vec3 g = vec3(-7.50343014e+02, 3.15679613e-04, 4.73464526e-01);
|
||||
vec4 b = vec4(0.0, 0.0, 0.0, 0.0);
|
||||
|
||||
rgb = vec3(r.r * t_inv + r.g * temperature + r.b, g.r * t_inv + g.g * temperature + g.b, ((b.r * temperature + b.g) * temperature + b.b) * temperature + b.a );
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return rgb;
|
||||
|
||||
}
|
||||
"""
|
||||
|
||||
# Adapted from https://github.com/blender/blender/blob/594f47ecd2d5367ca936cf6fc6ec8168c2b360d0/source/blender/gpu/shaders/material/gpu_shader_material_map_range.glsl
|
||||
str_map_range_linear = """
|
||||
float map_range_linear(const float value, const float fromMin, const float fromMax, const float toMin, const float toMax) {
|
||||
if (fromMax != fromMin) {
|
||||
return float(toMin + ((value - fromMin) / (fromMax - fromMin)) * (toMax - toMin));
|
||||
}
|
||||
else {
|
||||
return float(0.0);
|
||||
}
|
||||
}
|
||||
"""
|
||||
|
||||
str_map_range_stepped = """
|
||||
float map_range_stepped(const float value, const float fromMin, const float fromMax, const float toMin, const float toMax, const float steps) {
|
||||
if (fromMax != fromMin) {
|
||||
float factor = (value - fromMin) / (fromMax - fromMin);
|
||||
factor = (steps > 0.0) ? floor(factor * (steps + 1.0)) / steps : 0.0;
|
||||
return float(toMin + factor * (toMax - toMin));
|
||||
}
|
||||
else {
|
||||
return float(0.0);
|
||||
}
|
||||
}
|
||||
"""
|
||||
|
||||
str_map_range_smoothstep = """
|
||||
float map_range_smoothstep(const float value, const float fromMin, const float fromMax, const float toMin, const float toMax)
|
||||
{
|
||||
if (fromMax != fromMin) {
|
||||
float factor = (fromMin > fromMax) ? 1.0 - smoothstep(fromMax, fromMin, value) :
|
||||
smoothstep(fromMin, fromMax, value);
|
||||
return float(toMin + factor * (toMax - toMin));
|
||||
}
|
||||
else {
|
||||
return float(0.0);
|
||||
}
|
||||
}
|
||||
"""
|
||||
|
||||
str_map_range_smootherstep = """
|
||||
float safe_divide(float a, float b)
|
||||
{
|
||||
return (b != 0.0) ? a / b : 0.0;
|
||||
}
|
||||
|
||||
float smootherstep(float edge0, float edge1, float x)
|
||||
{
|
||||
x = clamp(safe_divide((x - edge0), (edge1 - edge0)), 0.0, 1.0);
|
||||
return x * x * x * (x * (x * 6.0 - 15.0) + 10.0);
|
||||
}
|
||||
|
||||
float map_range_smootherstep(const float value, const float fromMin, const float fromMax, const float toMin, const float toMax) {
|
||||
if (fromMax != fromMin) {
|
||||
float factor = (fromMin > fromMax) ? 1.0 - smootherstep(fromMax, fromMin, value) :
|
||||
smootherstep(fromMin, fromMax, value);
|
||||
return float(toMin + factor * (toMax - toMin));
|
||||
}
|
||||
else {
|
||||
return float(0.0);
|
||||
}
|
||||
}
|
||||
"""
|
||||
|
||||
str_rotate_around_axis = """
|
||||
vec3 rotate_around_axis(const vec3 p, const vec3 axis, const float angle)
|
||||
{
|
||||
float costheta = cos(angle);
|
||||
float sintheta = sin(angle);
|
||||
vec3 r;
|
||||
|
||||
r.x = ((costheta + (1.0 - costheta) * axis.x * axis.x) * p.x) +
|
||||
(((1.0 - costheta) * axis.x * axis.y - axis.z * sintheta) * p.y) +
|
||||
(((1.0 - costheta) * axis.x * axis.z + axis.y * sintheta) * p.z);
|
||||
|
||||
r.y = (((1.0 - costheta) * axis.x * axis.y + axis.z * sintheta) * p.x) +
|
||||
((costheta + (1.0 - costheta) * axis.y * axis.y) * p.y) +
|
||||
(((1.0 - costheta) * axis.y * axis.z - axis.x * sintheta) * p.z);
|
||||
|
||||
r.z = (((1.0 - costheta) * axis.x * axis.z - axis.y * sintheta) * p.x) +
|
||||
(((1.0 - costheta) * axis.y * axis.z + axis.x * sintheta) * p.y) +
|
||||
((costheta + (1.0 - costheta) * axis.z * axis.z) * p.z);
|
||||
|
||||
return r;
|
||||
}
|
||||
"""
|
||||
|
||||
str_euler_to_mat3 = """
|
||||
mat3 euler_to_mat3(vec3 euler)
|
||||
{
|
||||
float cx = cos(euler.x);
|
||||
float cy = cos(euler.y);
|
||||
float cz = cos(euler.z);
|
||||
float sx = sin(euler.x);
|
||||
float sy = sin(euler.y);
|
||||
float sz = sin(euler.z);
|
||||
|
||||
mat3 mat;
|
||||
mat[0][0] = cy * cz;
|
||||
mat[0][1] = cy * sz;
|
||||
mat[0][2] = -sy;
|
||||
|
||||
mat[1][0] = sy * sx * cz - cx * sz;
|
||||
mat[1][1] = sy * sx * sz + cx * cz;
|
||||
mat[1][2] = cy * sx;
|
||||
|
||||
mat[2][0] = sy * cx * cz + sx * sz;
|
||||
mat[2][1] = sy * cx * sz - sx * cz;
|
||||
mat[2][2] = cy * cx;
|
||||
return mat;
|
||||
}
|
||||
"""
|
|
@ -8,73 +8,43 @@ import arm.material.cycles_functions as c_functions
|
|||
from arm.material.parser_state import ParserState
|
||||
from arm.material.shader import floatstr, vec3str
|
||||
|
||||
def parse_maprange(node: bpy.types.ShaderNodeMapRange, out_socket: bpy.types.NodeSocket, state: ParserState) -> floatstr:
|
||||
|
||||
interp = node.interpolation_type
|
||||
|
||||
value: str = c.parse_value_input(node.inputs[0]) if node.inputs[0].is_linked else c.to_vec1(node.inputs[0].default_value)
|
||||
fromMin = c.parse_value_input(node.inputs[1])
|
||||
fromMax = c.parse_value_input(node.inputs[2])
|
||||
toMin = c.parse_value_input(node.inputs[3])
|
||||
toMax = c.parse_value_input(node.inputs[4])
|
||||
|
||||
if interp == "LINEAR":
|
||||
|
||||
state.curshader.add_function(c_functions.str_map_range_linear)
|
||||
return f'map_range_linear({value}, {fromMin}, {fromMax}, {toMin}, {toMax})'
|
||||
|
||||
elif interp == "STEPPED":
|
||||
|
||||
steps = float(c.parse_value_input(node.inputs[5]))
|
||||
state.curshader.add_function(c_functions.str_map_range_stepped)
|
||||
return f'map_range_stepped({value}, {fromMin}, {fromMax}, {toMin}, {toMax}, {steps})'
|
||||
|
||||
elif interp == "SMOOTHSTEP":
|
||||
|
||||
state.curshader.add_function(c_functions.str_map_range_smoothstep)
|
||||
return f'map_range_smoothstep({value}, {fromMin}, {fromMax}, {toMin}, {toMax})'
|
||||
|
||||
elif interp == "SMOOTHERSTEP":
|
||||
|
||||
state.curshader.add_function(c_functions.str_map_range_smootherstep)
|
||||
return f'map_range_smootherstep({value}, {fromMin}, {fromMax}, {toMin}, {toMax})'
|
||||
|
||||
def parse_blackbody(node: bpy.types.ShaderNodeBlackbody, out_socket: bpy.types.NodeSocket, state: ParserState) -> vec3str:
|
||||
t = float(c.parse_value_input(node.inputs[0]))
|
||||
|
||||
rgb = [0, 0, 0]
|
||||
blackbody_table_r = [
|
||||
[2.52432244e+03, -1.06185848e-03, 3.11067539e+00],
|
||||
[3.37763626e+03, -4.34581697e-04, 1.64843306e+00],
|
||||
[4.10671449e+03, -8.61949938e-05, 6.41423749e-01],
|
||||
[4.66849800e+03, 2.85655028e-05, 1.29075375e-01],
|
||||
[4.60124770e+03, 2.89727618e-05, 1.48001316e-01],
|
||||
[3.78765709e+03, 9.36026367e-06, 3.98995841e-01]
|
||||
]
|
||||
blackbody_table_g = [
|
||||
[-7.50343014e+02, 3.15679613e-04, 4.73464526e-01],
|
||||
[-1.00402363e+03, 1.29189794e-04, 9.08181524e-01],
|
||||
[-1.22075471e+03, 2.56245413e-05, 1.20753416e+00],
|
||||
[-1.42546105e+03, -4.01730887e-05, 1.44002695e+00],
|
||||
[-1.18134453e+03, -2.18913373e-05, 1.30656109e+00],
|
||||
[-5.00279505e+02, -4.59745390e-06, 1.09090465e+00]
|
||||
]
|
||||
blackbody_table_b = [
|
||||
[0.0, 0.0, 0.0, 0.0],
|
||||
[0.0, 0.0, 0.0, 0.0],
|
||||
[0.0, 0.0, 0.0, 0.0],
|
||||
[-2.02524603e-11, 1.79435860e-07, -2.60561875e-04, -1.41761141e-02],
|
||||
[-2.22463426e-13, -1.55078698e-08, 3.81675160e-04, -7.30646033e-01],
|
||||
[6.72595954e-13, -2.73059993e-08, 4.24068546e-04, -7.52204323e-01]
|
||||
]
|
||||
|
||||
if t >= 12000:
|
||||
rgb[0] = 0.826270103
|
||||
rgb[1] = 0.994478524
|
||||
rgb[2] = 1.56626022
|
||||
|
||||
elif t < 965.0:
|
||||
rgb[0] = 4.70366907
|
||||
rgb[1] = 0.0
|
||||
rgb[2] = 0.0
|
||||
|
||||
else:
|
||||
if t >= 6365.0:
|
||||
i = 5
|
||||
elif t >= 3315.0:
|
||||
i = 4
|
||||
elif t >= 1902.0:
|
||||
i = 3
|
||||
elif t >= 1449.0:
|
||||
i = 2
|
||||
elif t >= 1167.0:
|
||||
i = 1
|
||||
else:
|
||||
i = 0
|
||||
|
||||
r = blackbody_table_r[i]
|
||||
g = blackbody_table_g[i]
|
||||
b = blackbody_table_b[i]
|
||||
|
||||
t_inv = 1.0 / t
|
||||
|
||||
rgb[0] = r[0] * t_inv + r[1] * t + r[2]
|
||||
rgb[1] = g[0] * t_inv + g[1] * t + g[2]
|
||||
rgb[2] = ((b[0] * t + b[1]) * t + b[2]) * t + b[3]
|
||||
|
||||
# Pass constant
|
||||
return c.to_vec3([rgb[0], rgb[1], rgb[2]])
|
||||
|
||||
t = c.parse_value_input(node.inputs[0])
|
||||
|
||||
state.curshader.add_function(c_functions.str_blackbody)
|
||||
return f'blackbody({t})'
|
||||
|
||||
def parse_clamp(node: bpy.types.ShaderNodeClamp, out_socket: bpy.types.NodeSocket, state: ParserState) -> floatstr:
|
||||
value = c.parse_value_input(node.inputs['Value'])
|
||||
|
@ -262,14 +232,21 @@ def parse_math(node: bpy.types.ShaderNodeMath, out_socket: bpy.types.NodeSocket,
|
|||
out_val = '({0} * {1})'.format(val1, val2)
|
||||
elif op == 'DIVIDE':
|
||||
out_val = '({0} / {1})'.format(val1, val2)
|
||||
elif op == 'MULTIPLY_ADD':
|
||||
val3 = c.parse_value_input(node.inputs[2])
|
||||
out_val = '({0} * {1} + {2})'.format(val1, val2, val3)
|
||||
elif op == 'POWER':
|
||||
out_val = 'pow({0}, {1})'.format(val1, val2)
|
||||
elif op == 'LOGARITHM':
|
||||
out_val = 'log({0})'.format(val1)
|
||||
elif op == 'SQRT':
|
||||
out_val = 'sqrt({0})'.format(val1)
|
||||
elif op == 'INVERSE_SQRT':
|
||||
out_val = 'inversesqrt({0})'.format(val1)
|
||||
elif op == 'ABSOLUTE':
|
||||
out_val = 'abs({0})'.format(val1)
|
||||
elif op == 'EXPONENT':
|
||||
out_val = 'exp({0})'.format(val1)
|
||||
elif op == 'MINIMUM':
|
||||
out_val = 'min({0}, {1})'.format(val1, val2)
|
||||
elif op == 'MAXIMUM':
|
||||
|
@ -278,6 +255,17 @@ def parse_math(node: bpy.types.ShaderNodeMath, out_socket: bpy.types.NodeSocket,
|
|||
out_val = 'float({0} < {1})'.format(val1, val2)
|
||||
elif op == 'GREATER_THAN':
|
||||
out_val = 'float({0} > {1})'.format(val1, val2)
|
||||
elif op == 'SIGN':
|
||||
out_val = 'sign({0})'.format(val1)
|
||||
elif op == 'COMPARE':
|
||||
val3 = c.parse_value_input(node.inputs[2])
|
||||
out_val = 'float((abs({0} - {1}) <= max({2}, 1e-5)) ? 1.0 : 0.0)'.format(val1, val2, val3)
|
||||
elif op == 'SMOOTH_MIN':
|
||||
val3 = c.parse_value_input(node.inputs[2])
|
||||
out_val = 'float(float({2} != 0.0 ? min({0},{1}) - (max({2} - abs({0} - {1}), 0.0) / {2}) * (max({2} - abs({0} - {1}), 0.0) / {2}) * (max({2} - abs({0} - {1}), 0.0) / {2}) * {2} * (1.0 / 6.0) : min({0}, {1})))'.format(val1, val2, val3)
|
||||
elif op == 'SMOOTH_MAX':
|
||||
val3 = c.parse_value_input(node.inputs[2])
|
||||
out_val = 'float(0-(float({2} != 0.0 ? min(-{0},-{1}) - (max({2} - abs(-{0} - (-{1})), 0.0) / {2}) * (max({2} - abs(-{0} - (-{1})), 0.0) / {2}) * (max({2} - abs(-{0} - (-{1})), 0.0) / {2}) * {2} * (1.0 / 6.0) : min(-{0}, (-{1})))))'.format(val1, val2, val3)
|
||||
elif op == 'ROUND':
|
||||
# out_val = 'round({0})'.format(val1)
|
||||
out_val = 'floor({0} + 0.5)'.format(val1)
|
||||
|
@ -285,11 +273,20 @@ def parse_math(node: bpy.types.ShaderNodeMath, out_socket: bpy.types.NodeSocket,
|
|||
out_val = 'floor({0})'.format(val1)
|
||||
elif op == 'CEIL':
|
||||
out_val = 'ceil({0})'.format(val1)
|
||||
elif op == 'TRUNC':
|
||||
out_val = 'trunc({0})'.format(val1)
|
||||
elif op == 'FRACT':
|
||||
out_val = 'fract({0})'.format(val1)
|
||||
elif op == 'MODULO':
|
||||
# out_val = 'float({0} % {1})'.format(val1, val2)
|
||||
out_val = 'mod({0}, {1})'.format(val1, val2)
|
||||
elif op == 'WRAP':
|
||||
val3 = c.parse_value_input(node.inputs[2])
|
||||
out_val = 'float((({1}-{2}) != 0.0) ? {0} - (({1}-{2}) * floor(({0} - {2}) / ({1}-{2}))) : {2})'.format(val1, val2, val3)
|
||||
elif op == 'SNAP':
|
||||
out_val = 'floor(({1} != 0.0) ? {0} / {1} : 0.0) * {1}'.format(val1, val2)
|
||||
elif op == 'PINGPONG':
|
||||
out_val = 'float(({1} != 0.0) ? abs(fract(({0} - {1}) / ({1} * 2.0)) * {1} * 2.0 - {1}) : 0.0)'.format(val1, val2)
|
||||
elif op == 'SINE':
|
||||
out_val = 'sin({0})'.format(val1)
|
||||
elif op == 'COSINE':
|
||||
|
@ -304,6 +301,16 @@ def parse_math(node: bpy.types.ShaderNodeMath, out_socket: bpy.types.NodeSocket,
|
|||
out_val = 'atan({0})'.format(val1)
|
||||
elif op == 'ARCTAN2':
|
||||
out_val = 'atan({0}, {1})'.format(val1, val2)
|
||||
elif op == 'SINH':
|
||||
out_val = 'sinh({0})'.format(val1)
|
||||
elif op == 'COSH':
|
||||
out_val = 'cosh({0})'.format(val1)
|
||||
elif op == 'TANH':
|
||||
out_val = 'tanh({0})'.format(val1)
|
||||
elif op == 'RADIANS':
|
||||
out_val = 'radians({0})'.format(val1)
|
||||
elif op == 'DEGREES':
|
||||
out_val = 'degrees({0})'.format(val1)
|
||||
|
||||
if node.use_clamp:
|
||||
return 'clamp({0}, 0.0, 1.0)'.format(out_val)
|
||||
|
|
|
@ -4,6 +4,7 @@ import bpy
|
|||
from mathutils import Euler, Vector
|
||||
|
||||
import arm.material.cycles as c
|
||||
import arm.material.cycles_functions as c_functions
|
||||
from arm.material.parser_state import ParserState
|
||||
from arm.material.shader import floatstr, vec3str
|
||||
|
||||
|
@ -141,3 +142,33 @@ def parse_displacement(node: bpy.types.ShaderNodeDisplacement, out_socket: bpy.t
|
|||
scale = c.parse_value_input(node.inputs[2])
|
||||
nor = c.parse_vector_input(node.inputs[3])
|
||||
return f'(vec3({height}) * {scale})'
|
||||
|
||||
def parse_vectorrotate(node: bpy.types.ShaderNodeVectorRotate, out_socket: bpy.types.NodeSocket, state: ParserState) -> vec3str:
|
||||
|
||||
type = node.rotation_type
|
||||
input_vector: bpy.types.NodeSocket = c.parse_vector_input(node.inputs[0])
|
||||
input_center: bpy.types.NodeSocket = c.parse_vector_input(node.inputs[1])
|
||||
input_axis: bpy.types.NodeSocket = c.parse_vector_input(node.inputs[2])
|
||||
input_angle: bpy.types.NodeSocket = c.parse_value_input(node.inputs[3])
|
||||
input_rotation: bpy.types.NodeSocket = c.parse_vector_input(node.inputs[4])
|
||||
|
||||
if node.invert:
|
||||
input_invert = "0"
|
||||
else:
|
||||
input_invert = "1"
|
||||
|
||||
state.curshader.add_function(c_functions.str_rotate_around_axis)
|
||||
|
||||
if type == 'AXIS_ANGLE':
|
||||
return f'vec3( (length({input_axis}) != 0.0) ? rotate_around_axis({input_vector} - {input_center}, normalize({input_axis}), {input_angle} * {input_invert}) + {input_center} : {input_vector} )'
|
||||
elif type == 'X_AXIS':
|
||||
return f'vec3( rotate_around_axis({input_vector} - {input_center}, vec3(1.0, 0.0, 0.0), {input_angle} * {input_invert}) + {input_center} )'
|
||||
elif type == 'Y_AXIS':
|
||||
return f'vec3( rotate_around_axis({input_vector} - {input_center}, vec3(0.0, 1.0, 0.0), {input_angle} * {input_invert}) + {input_center} )'
|
||||
elif type == 'Z_AXIS':
|
||||
return f'vec3( rotate_around_axis({input_vector} - {input_center}, vec3(0.0, 0.0, 1.0), {input_angle} * {input_invert}) + {input_center} )'
|
||||
elif type == 'EULER_XYZ':
|
||||
state.curshader.add_function(c_functions.str_euler_to_mat3)
|
||||
return f'vec3( mat3(({input_invert} < 0.0) ? transpose(euler_to_mat3({input_rotation})) : euler_to_mat3({input_rotation})) * ({input_vector} - {input_center}) + {input_center})'
|
||||
|
||||
return f'(vec3(1.0, 0.0, 0.0))'
|
||||
|
|
|
@ -1,16 +1,15 @@
|
|||
from typing import Dict, List
|
||||
|
||||
import bpy
|
||||
import arm.utils
|
||||
import arm.node_utils
|
||||
from bpy.types import Material
|
||||
from bpy.types import Object
|
||||
|
||||
import arm.material.cycles as cycles
|
||||
import arm.material.make_shader as make_shader
|
||||
import arm.material.mat_batch as mat_batch
|
||||
import arm.material.mat_state as mat_state
|
||||
import arm.material.cycles as cycles
|
||||
import arm.node_utils
|
||||
import arm.utils
|
||||
|
||||
def glsl_type(t): # Merge with cycles
|
||||
if t == 'RGB' or t == 'RGBA' or t == 'VECTOR':
|
||||
return 'vec3'
|
||||
else:
|
||||
return 'float'
|
||||
|
||||
def glsl_value(val):
|
||||
if str(type(val)) == "<class 'bpy_prop_array'>":
|
||||
|
@ -21,39 +20,27 @@ def glsl_value(val):
|
|||
else:
|
||||
return val
|
||||
|
||||
def parse(material, mat_data, mat_users, mat_armusers):
|
||||
|
||||
def parse(material: Material, mat_data, mat_users: Dict[Material, List[Object]], mat_armusers):
|
||||
wrd = bpy.data.worlds['Arm']
|
||||
rpdat = arm.utils.get_rp()
|
||||
|
||||
# No batch - shader data per material
|
||||
if material.arm_custom_material != '':
|
||||
rpasses = ['mesh']
|
||||
sd = {}
|
||||
sd['contexts'] = []
|
||||
con = {}
|
||||
con['vertex_elements'] = []
|
||||
elem = {}
|
||||
elem['name'] = 'pos'
|
||||
elem['data'] = 'short4norm'
|
||||
con['vertex_elements'].append(elem)
|
||||
elem = {}
|
||||
elem['name'] = 'nor'
|
||||
elem['data'] = 'short2norm'
|
||||
con['vertex_elements'].append(elem)
|
||||
elem = {}
|
||||
elem['name'] = 'tex'
|
||||
elem['data'] = 'short2norm'
|
||||
con['vertex_elements'].append(elem)
|
||||
elem = {}
|
||||
elem['name'] = 'tex1'
|
||||
elem['data'] = 'short2norm'
|
||||
con['vertex_elements'].append(elem)
|
||||
sd['contexts'].append(con)
|
||||
|
||||
con = {'vertex_elements': []}
|
||||
con['vertex_elements'].append({'name': 'pos', 'data': 'short4norm'})
|
||||
con['vertex_elements'].append({'name': 'nor', 'data': 'short2norm'})
|
||||
con['vertex_elements'].append({'name': 'tex', 'data': 'short2norm'})
|
||||
con['vertex_elements'].append({'name': 'tex1', 'data': 'short2norm'})
|
||||
|
||||
sd = {'contexts': [con]}
|
||||
shader_data_name = material.arm_custom_material
|
||||
bind_constants = {}
|
||||
bind_constants['mesh'] = []
|
||||
bind_textures = {}
|
||||
bind_textures['mesh'] = []
|
||||
bind_constants = {'mesh': []}
|
||||
bind_textures = {'mesh': []}
|
||||
|
||||
make_shader.make_instancing_and_skinning(material, mat_users)
|
||||
elif not wrd.arm_batch_materials or material.name.startswith('armdefault'):
|
||||
rpasses, shader_data, shader_data_name, bind_constants, bind_textures = make_shader.build(material, mat_users, mat_armusers)
|
||||
sd = shader_data.sd
|
||||
|
@ -63,39 +50,35 @@ def parse(material, mat_data, mat_users, mat_armusers):
|
|||
|
||||
# Material
|
||||
for rp in rpasses:
|
||||
|
||||
c = {}
|
||||
c['name'] = rp
|
||||
c['bind_constants'] = [] + bind_constants[rp]
|
||||
c['bind_textures'] = [] + bind_textures[rp]
|
||||
c = {
|
||||
'name': rp,
|
||||
'bind_constants': [] + bind_constants[rp],
|
||||
'bind_textures': [] + bind_textures[rp],
|
||||
}
|
||||
mat_data['contexts'].append(c)
|
||||
|
||||
if rp == 'mesh':
|
||||
const = {}
|
||||
const['name'] = 'receiveShadow'
|
||||
const['bool'] = material.arm_receive_shadow
|
||||
c['bind_constants'].append(const)
|
||||
c['bind_constants'].append({'name': 'receiveShadow', 'bool': material.arm_receive_shadow})
|
||||
|
||||
if material.arm_material_id != 0:
|
||||
const = {}
|
||||
const['name'] = 'materialID'
|
||||
const['int'] = material.arm_material_id
|
||||
c['bind_constants'].append(const)
|
||||
c['bind_constants'].append({'name': 'materialID', 'int': material.arm_material_id})
|
||||
|
||||
if material.arm_material_id == 2:
|
||||
wrd.world_defs += '_Hair'
|
||||
|
||||
elif rpdat.rp_sss_state == 'On':
|
||||
sss = False
|
||||
sss_node = arm.node_utils.get_node_by_type(material.node_tree, 'SUBSURFACE_SCATTERING')
|
||||
if sss_node != None and sss_node.outputs[0].is_linked: # Check linked node
|
||||
if sss_node is not None and sss_node.outputs[0].is_linked: # Check linked node
|
||||
sss = True
|
||||
sss_node = arm.node_utils.get_node_by_type(material.node_tree, 'BSDF_PRINCIPLED')
|
||||
if sss_node != None and sss_node.outputs[0].is_linked and (sss_node.inputs[1].is_linked or sss_node.inputs[1].default_value != 0.0):
|
||||
if sss_node is not None and sss_node.outputs[0].is_linked and (sss_node.inputs[1].is_linked or sss_node.inputs[1].default_value != 0.0):
|
||||
sss = True
|
||||
sss_node = arm.node_utils.get_node_armorypbr(material.node_tree)
|
||||
if sss_node != None and sss_node.outputs[0].is_linked and (sss_node.inputs[8].is_linked or sss_node.inputs[8].default_value != 0.0):
|
||||
if sss_node is not None and sss_node.outputs[0].is_linked and (sss_node.inputs[8].is_linked or sss_node.inputs[8].default_value != 0.0):
|
||||
sss = True
|
||||
const = {}
|
||||
const['name'] = 'materialID'
|
||||
|
||||
const = {'name': 'materialID'}
|
||||
if sss:
|
||||
const['int'] = 2
|
||||
else:
|
||||
|
@ -111,32 +94,26 @@ def parse(material, mat_data, mat_users, mat_armusers):
|
|||
if node.type == 'TEX_IMAGE':
|
||||
tex_name = arm.utils.safesrc(node.name)
|
||||
tex = cycles.make_texture(node, tex_name)
|
||||
if tex == None: # Empty texture
|
||||
tex = {}
|
||||
tex['name'] = tex_name
|
||||
tex['file'] = ''
|
||||
# Empty texture
|
||||
if tex is None:
|
||||
tex = {'name': tex_name, 'file': ''}
|
||||
c['bind_textures'].append(tex)
|
||||
|
||||
# Set marked inputs as uniforms
|
||||
for node in material.node_tree.nodes:
|
||||
for inp in node.inputs:
|
||||
if inp.is_uniform:
|
||||
uname = arm.utils.safesrc(inp.node.name) + arm.utils.safesrc(inp.name) # Merge with cycles
|
||||
const = {}
|
||||
const['name'] = uname
|
||||
const[glsl_type(inp.type)] = glsl_value(inp.default_value)
|
||||
c['bind_constants'].append(const)
|
||||
uname = arm.utils.safesrc(inp.node.name) + arm.utils.safesrc(inp.name) # Merge with cycles module
|
||||
c['bind_constants'].append({'name': uname, cycles.glsl_type(inp.type): glsl_value(inp.default_value)})
|
||||
|
||||
elif rp == 'translucent':
|
||||
const = {}
|
||||
const['name'] = 'receiveShadow'
|
||||
const['bool'] = material.arm_receive_shadow
|
||||
c['bind_constants'].append(const)
|
||||
c['bind_constants'].append({'name': 'receiveShadow', 'bool': material.arm_receive_shadow})
|
||||
|
||||
if wrd.arm_single_data_file:
|
||||
mat_data['shader'] = shader_data_name
|
||||
else:
|
||||
ext = '' if wrd.arm_minimize else '.json'
|
||||
# Make sure that custom materials are not expected to be in .arm format
|
||||
ext = '' if wrd.arm_minimize and material.arm_custom_material == "" else '.json'
|
||||
mat_data['shader'] = shader_data_name + ext + '/' + shader_data_name
|
||||
|
||||
return sd, rpasses
|
||||
|
|
|
@ -37,7 +37,6 @@ def write_norpos(con_mesh: shader.ShaderContext, vert: shader.Shader, declare=Fa
|
|||
prep = ''
|
||||
if declare:
|
||||
prep = 'vec3 '
|
||||
vert.write_pre = True
|
||||
is_bone = con_mesh.is_elem('bone')
|
||||
if is_bone:
|
||||
make_skin.skin_pos(vert)
|
||||
|
@ -48,4 +47,3 @@ def write_norpos(con_mesh: shader.ShaderContext, vert: shader.Shader, declare=Fa
|
|||
vert.write_attrib(prep + 'wnormal = normalize(N * vec3(nor.xy, pos.w));')
|
||||
if con_mesh.is_elem('ipos'):
|
||||
make_inst.inst_pos(con_mesh, vert)
|
||||
vert.write_pre = False
|
||||
|
|
|
@ -3,16 +3,25 @@ import bpy
|
|||
def write(vert, frag):
|
||||
wrd = bpy.data.worlds['Arm']
|
||||
is_shadows = '_ShadowMap' in wrd.world_defs
|
||||
is_shadows_atlas = '_ShadowMapAtlas' in wrd.world_defs
|
||||
is_single_atlas = '_SingleAtlas' in wrd.world_defs
|
||||
|
||||
frag.add_include('std/clusters.glsl')
|
||||
frag.add_include_front('std/clusters.glsl')
|
||||
frag.add_uniform('vec2 cameraProj', link='_cameraPlaneProj')
|
||||
frag.add_uniform('vec2 cameraPlane', link='_cameraPlane')
|
||||
frag.add_uniform('vec4 lightsArray[maxLights * 2]', link='_lightsArray')
|
||||
frag.add_uniform('vec4 lightsArray[maxLights * 3]', link='_lightsArray')
|
||||
frag.add_uniform('sampler2D clustersData', link='_clustersData')
|
||||
if is_shadows:
|
||||
frag.add_uniform('bool receiveShadow')
|
||||
frag.add_uniform('vec2 lightProj', link='_lightPlaneProj', included=True)
|
||||
frag.add_uniform('samplerCubeShadow shadowMapPoint[4]', included=True)
|
||||
if is_shadows_atlas:
|
||||
if not is_single_atlas:
|
||||
frag.add_uniform('sampler2DShadow shadowMapAtlasPoint', included=True)
|
||||
else:
|
||||
frag.add_uniform('sampler2DShadow shadowMapAtlas', top=True)
|
||||
frag.add_uniform('vec4 pointLightDataArray[maxLightsCluster]', link='_pointLightsAtlasArray', included=True)
|
||||
else:
|
||||
frag.add_uniform('samplerCubeShadow shadowMapPoint[4]', included=True)
|
||||
vert.add_out('vec4 wvpposition')
|
||||
vert.write('wvpposition = gl_Position;')
|
||||
# wvpposition.z / wvpposition.w
|
||||
|
@ -29,11 +38,15 @@ def write(vert, frag):
|
|||
frag.write('int numSpots = int(texelFetch(clustersData, ivec2(clusterI, 1 + maxLightsCluster), 0).r * 255);')
|
||||
frag.write('int numPoints = numLights - numSpots;')
|
||||
if is_shadows:
|
||||
frag.add_uniform('mat4 LWVPSpot0', link='_biasLightWorldViewProjectionMatrixSpot0', included=True)
|
||||
frag.add_uniform('mat4 LWVPSpot1', link='_biasLightWorldViewProjectionMatrixSpot1', included=True)
|
||||
frag.add_uniform('mat4 LWVPSpot2', link='_biasLightWorldViewProjectionMatrixSpot2', included=True)
|
||||
frag.add_uniform('mat4 LWVPSpot3', link='_biasLightWorldViewProjectionMatrixSpot3', included=True)
|
||||
frag.add_uniform('sampler2DShadow shadowMapSpot[4]', included=True)
|
||||
if is_shadows_atlas:
|
||||
if not is_single_atlas:
|
||||
frag.add_uniform('sampler2DShadow shadowMapAtlasSpot', included=True)
|
||||
else:
|
||||
frag.add_uniform('sampler2DShadow shadowMapAtlas', top=True)
|
||||
else:
|
||||
frag.add_uniform('sampler2DShadow shadowMapSpot[4]', included=True)
|
||||
# FIXME: type is actually mat4, but otherwise it will not be set as floats when writing the shaders' json files
|
||||
frag.add_uniform('vec4 LWVPSpotArray[maxLightsCluster]', link='_biasLightWorldViewProjectionMatrixSpotArray', included=True)
|
||||
|
||||
frag.write('for (int i = 0; i < min(numLights, maxLightsCluster); i++) {')
|
||||
frag.write('int li = int(texelFetch(clustersData, ivec2(clusterI, i + 1), 0).r * 255);')
|
||||
|
@ -43,19 +56,19 @@ def write(vert, frag):
|
|||
frag.write(' n,')
|
||||
frag.write(' vVec,')
|
||||
frag.write(' dotNV,')
|
||||
frag.write(' lightsArray[li * 2].xyz,') # lp
|
||||
frag.write(' lightsArray[li * 2 + 1].xyz,') # lightCol
|
||||
frag.write(' lightsArray[li * 3].xyz,') # lp
|
||||
frag.write(' lightsArray[li * 3 + 1].xyz,') # lightCol
|
||||
frag.write(' albedo,')
|
||||
frag.write(' roughness,')
|
||||
frag.write(' specular,')
|
||||
frag.write(' f0')
|
||||
if is_shadows:
|
||||
frag.write(' , li, lightsArray[li * 2].w, receiveShadow') # bias
|
||||
frag.write('\t, li, lightsArray[li * 3 + 2].x, lightsArray[li * 3 + 2].z != 0.0') # bias
|
||||
if '_Spot' in wrd.world_defs:
|
||||
frag.write(' , li > numPoints - 1')
|
||||
frag.write(' , lightsArray[li * 2 + 1].w') # cutoff
|
||||
frag.write(' , lightsArraySpot[li].w') # cutoff - exponent
|
||||
frag.write(' , lightsArraySpot[li].xyz') # spotDir
|
||||
frag.write('\t, lightsArray[li * 3 + 2].y != 0.0')
|
||||
frag.write('\t, lightsArray[li * 3 + 2].y') # cutoff
|
||||
frag.write('\t, lightsArraySpot[li].w') # cutoff - exponent
|
||||
frag.write('\t, lightsArraySpot[li].xyz') # spotDir
|
||||
if '_VoxelShadow' in wrd.world_defs and '_VoxelAOvar' in wrd.world_defs:
|
||||
frag.write(' , voxels, voxpos')
|
||||
frag.write(');')
|
||||
|
|
|
@ -125,6 +125,10 @@ def make_base(con_mesh, parse_opacity):
|
|||
if write_material_attribs_post != None:
|
||||
write_material_attribs_post(con_mesh, frag)
|
||||
|
||||
vert.add_out('vec3 wnormal')
|
||||
make_attrib.write_norpos(con_mesh, vert)
|
||||
frag.write_attrib('vec3 n = normalize(wnormal);')
|
||||
|
||||
if not is_displacement and not vattr_written:
|
||||
make_attrib.write_vertpos(vert)
|
||||
|
||||
|
@ -162,10 +166,6 @@ def make_base(con_mesh, parse_opacity):
|
|||
make_tess.interpolate(tese, 'vcolor', 3, declare_out=frag.contains('vcolor'))
|
||||
tese.write_pre = False
|
||||
|
||||
vert.add_out('vec3 wnormal')
|
||||
make_attrib.write_norpos(con_mesh, vert)
|
||||
frag.write_attrib('vec3 n = normalize(wnormal);')
|
||||
|
||||
if con_mesh.is_elem('tang'):
|
||||
if tese is not None:
|
||||
tese.add_out('mat3 TBN')
|
||||
|
@ -268,6 +268,9 @@ def make_deferred(con_mesh, rpasses):
|
|||
frag.write('vec2 posb = (prevwvpposition.xy / prevwvpposition.w) * 0.5 + 0.5;')
|
||||
frag.write('fragColor[2].rg = vec2(posa - posb);')
|
||||
|
||||
if mat_state.material.arm_ignore_irradiance:
|
||||
frag.write('fragColor[2].b = 1.0;')
|
||||
|
||||
return con_mesh
|
||||
|
||||
def make_raytracer(con_mesh):
|
||||
|
@ -354,6 +357,12 @@ def make_forward_mobile(con_mesh):
|
|||
return
|
||||
|
||||
is_shadows = '_ShadowMap' in wrd.world_defs
|
||||
is_shadows_atlas = '_ShadowMapAtlas' in wrd.world_defs
|
||||
shadowmap_sun = 'shadowMap'
|
||||
if is_shadows_atlas:
|
||||
is_single_atlas = '_SingleAtlas' in wrd.world_defs
|
||||
shadowmap_sun = 'shadowMapAtlasSun' if not is_single_atlas else 'shadowMapAtlas'
|
||||
frag.add_uniform('vec2 smSizeUniform', '_shadowMapSize', included=True)
|
||||
frag.write('vec3 direct = vec3(0.0);')
|
||||
|
||||
if '_Sun' in wrd.world_defs:
|
||||
|
@ -363,10 +372,10 @@ def make_forward_mobile(con_mesh):
|
|||
frag.write('float sdotNL = max(dot(n, sunDir), 0.0);')
|
||||
if is_shadows:
|
||||
vert.add_out('vec4 lightPosition')
|
||||
vert.add_uniform('mat4 LWVP', '_biasLightWorldViewProjectionMatrix')
|
||||
vert.add_uniform('mat4 LWVP', '_biasLightWorldViewProjectionMatrixSun')
|
||||
vert.write('lightPosition = LWVP * spos;')
|
||||
frag.add_uniform('bool receiveShadow')
|
||||
frag.add_uniform('sampler2DShadow shadowMap')
|
||||
frag.add_uniform(f'sampler2DShadow {shadowmap_sun}')
|
||||
frag.add_uniform('float shadowsBias', '_sunShadowsBias')
|
||||
|
||||
frag.write('if (receiveShadow) {')
|
||||
|
@ -374,14 +383,14 @@ def make_forward_mobile(con_mesh):
|
|||
frag.add_include('std/shadows.glsl')
|
||||
frag.add_uniform('vec4 casData[shadowmapCascades * 4 + 4]', '_cascadeData', included=True)
|
||||
frag.add_uniform('vec3 eye', '_cameraPosition')
|
||||
frag.write('svisibility = shadowTestCascade(shadowMap, eye, wposition + n * shadowsBias * 10, shadowsBias);')
|
||||
frag.write(f'svisibility = shadowTestCascade({shadowmap_sun}, eye, wposition + n * shadowsBias * 10, shadowsBias);')
|
||||
else:
|
||||
frag.write('if (lightPosition.w > 0.0) {')
|
||||
frag.write(' vec3 lPos = lightPosition.xyz / lightPosition.w;')
|
||||
if '_Legacy' in wrd.world_defs:
|
||||
frag.write(' svisibility = float(texture(shadowMap, vec2(lPos.xy)).r > lPos.z - shadowsBias);')
|
||||
frag.write(f' svisibility = float(texture({shadowmap_sun}, vec2(lPos.xy)).r > lPos.z - shadowsBias);')
|
||||
else:
|
||||
frag.write(' svisibility = texture(shadowMap, vec3(lPos.xy, lPos.z - shadowsBias)).r;')
|
||||
frag.write(f' svisibility = texture({shadowmap_sun}, vec3(lPos.xy, lPos.z - shadowsBias)).r;')
|
||||
frag.write('}')
|
||||
frag.write('}') # receiveShadow
|
||||
frag.write('direct += basecol * sdotNL * sunCol * svisibility;')
|
||||
|
@ -404,8 +413,8 @@ def make_forward_mobile(con_mesh):
|
|||
frag.write('if (receiveShadow) {')
|
||||
if '_Spot' in wrd.world_defs:
|
||||
vert.add_out('vec4 spotPosition')
|
||||
vert.add_uniform('mat4 LWVPSpot0', link='_biasLightWorldViewProjectionMatrixSpot0')
|
||||
vert.write('spotPosition = LWVPSpot0 * spos;')
|
||||
vert.add_uniform('mat4 LWVPSpotArray[1]', link='_biasLightWorldViewProjectionMatrixSpotArray')
|
||||
vert.write('spotPosition = LWVPSpotArray[0] * spos;')
|
||||
frag.add_uniform('sampler2DShadow shadowMapSpot[1]')
|
||||
frag.write('if (spotPosition.w > 0.0) {')
|
||||
frag.write(' vec3 lPos = spotPosition.xyz / spotPosition.w;')
|
||||
|
@ -532,13 +541,10 @@ def make_forward(con_mesh):
|
|||
frag.add_uniform('sampler2D sltcMag', '_ltcMag', included=True)
|
||||
if '_ShadowMap' in wrd.world_defs:
|
||||
if '_SinglePoint' in wrd.world_defs:
|
||||
frag.add_uniform('mat4 LWVPSpot0', link='_biasLightViewProjectionMatrixSpot0', included=True)
|
||||
frag.add_uniform('mat4 LWVPSpot[0]', link='_biasLightViewProjectionMatrixSpot0', included=True)
|
||||
frag.add_uniform('sampler2DShadow shadowMapSpot[1]', included=True)
|
||||
if '_Clusters' in wrd.world_defs:
|
||||
frag.add_uniform('mat4 LWVPSpot0', link='_biasLightWorldViewProjectionMatrixSpot0', included=True)
|
||||
frag.add_uniform('mat4 LWVPSpot1', link='_biasLightWorldViewProjectionMatrixSpot1', included=True)
|
||||
frag.add_uniform('mat4 LWVPSpot2', link='_biasLightWorldViewProjectionMatrixSpot2', included=True)
|
||||
frag.add_uniform('mat4 LWVPSpot3', link='_biasLightWorldViewProjectionMatrixSpot3', included=True)
|
||||
frag.add_uniform('mat4 LWVPSpotArray[4]', link='_biasLightWorldViewProjectionMatrixSpotArray', included=True)
|
||||
frag.add_uniform('sampler2DShadow shadowMapSpot[4]', included=True)
|
||||
|
||||
if not blend:
|
||||
|
@ -570,12 +576,14 @@ def make_forward_base(con_mesh, parse_opacity=False, transluc_pass=False):
|
|||
arm_discard = mat_state.material.arm_discard
|
||||
make_base(con_mesh, parse_opacity=(parse_opacity or arm_discard))
|
||||
|
||||
blend = mat_state.material.arm_blending
|
||||
|
||||
vert = con_mesh.vert
|
||||
frag = con_mesh.frag
|
||||
tese = con_mesh.tese
|
||||
|
||||
if parse_opacity or arm_discard:
|
||||
if arm_discard:
|
||||
if arm_discard or blend:
|
||||
opac = mat_state.material.arm_discard_opacity
|
||||
frag.write('if (opacity < {0}) discard;'.format(opac))
|
||||
elif transluc_pass:
|
||||
|
@ -584,7 +592,6 @@ def make_forward_base(con_mesh, parse_opacity=False, transluc_pass=False):
|
|||
opac = '0.9999' # 1.0 - eps
|
||||
frag.write('if (opacity < {0}) discard;'.format(opac))
|
||||
|
||||
blend = mat_state.material.arm_blending
|
||||
if blend:
|
||||
frag.add_out('vec4 fragColor[1]')
|
||||
if parse_opacity:
|
||||
|
@ -605,6 +612,12 @@ def make_forward_base(con_mesh, parse_opacity=False, transluc_pass=False):
|
|||
|
||||
frag.add_include('std/light.glsl')
|
||||
is_shadows = '_ShadowMap' in wrd.world_defs
|
||||
is_shadows_atlas = '_ShadowMapAtlas' in wrd.world_defs
|
||||
is_single_atlas = is_shadows_atlas and '_SingleAtlas' in wrd.world_defs
|
||||
shadowmap_sun = 'shadowMap'
|
||||
if is_shadows_atlas:
|
||||
shadowmap_sun = 'shadowMapAtlasSun' if not is_single_atlas else 'shadowMapAtlas'
|
||||
frag.add_uniform('vec2 smSizeUniform', '_shadowMapSize', included=True)
|
||||
|
||||
frag.write('vec3 albedo = surfaceAlbedo(basecol, metallic);')
|
||||
frag.write('vec3 f0 = surfaceF0(basecol, metallic);')
|
||||
|
@ -661,14 +674,14 @@ def make_forward_base(con_mesh, parse_opacity=False, transluc_pass=False):
|
|||
frag.write('float sdotVH = dot(vVec, sh);')
|
||||
if is_shadows:
|
||||
frag.add_uniform('bool receiveShadow')
|
||||
frag.add_uniform('sampler2DShadow shadowMap')
|
||||
frag.add_uniform(f'sampler2DShadow {shadowmap_sun}', top=True)
|
||||
frag.add_uniform('float shadowsBias', '_sunShadowsBias')
|
||||
frag.write('if (receiveShadow) {')
|
||||
if '_CSM' in wrd.world_defs:
|
||||
frag.add_include('std/shadows.glsl')
|
||||
frag.add_uniform('vec4 casData[shadowmapCascades * 4 + 4]', '_cascadeData', included=True)
|
||||
frag.add_uniform('vec3 eye', '_cameraPosition')
|
||||
frag.write('svisibility = shadowTestCascade(shadowMap, eye, wposition + n * shadowsBias * 10, shadowsBias);')
|
||||
frag.write(f'svisibility = shadowTestCascade({shadowmap_sun}, eye, wposition + n * shadowsBias * 10, shadowsBias);')
|
||||
else:
|
||||
if tese is not None:
|
||||
tese.add_out('vec4 lightPosition')
|
||||
|
@ -681,11 +694,11 @@ def make_forward_base(con_mesh, parse_opacity=False, transluc_pass=False):
|
|||
vert.write('lightPosition = LVP * vec4(wposition, 1.0);')
|
||||
else:
|
||||
vert.add_out('vec4 lightPosition')
|
||||
vert.add_uniform('mat4 LWVP', '_biasLightWorldViewProjectionMatrix')
|
||||
vert.add_uniform('mat4 LWVP', '_biasLightWorldViewProjectionMatrixSun')
|
||||
vert.write('lightPosition = LWVP * spos;')
|
||||
frag.write('vec3 lPos = lightPosition.xyz / lightPosition.w;')
|
||||
frag.write('const vec2 smSize = shadowmapSize;')
|
||||
frag.write('svisibility = PCF(shadowMap, lPos.xy, lPos.z - shadowsBias, smSize);')
|
||||
frag.write(f'svisibility = PCF({shadowmap_sun}, lPos.xy, lPos.z - shadowsBias, smSize);')
|
||||
frag.write('}') # receiveShadow
|
||||
if '_VoxelShadow' in wrd.world_defs and '_VoxelAOvar' in wrd.world_defs:
|
||||
frag.write('svisibility *= 1.0 - traceShadow(voxels, voxpos, sunDir);')
|
||||
|
@ -703,7 +716,7 @@ def make_forward_base(con_mesh, parse_opacity=False, transluc_pass=False):
|
|||
frag.add_uniform('float pointBias', link='_pointShadowsBias')
|
||||
if '_Spot' in wrd.world_defs:
|
||||
# Skip world matrix, already in world-space
|
||||
frag.add_uniform('mat4 LWVPSpot0', link='_biasLightViewProjectionMatrixSpot0', included=True)
|
||||
frag.add_uniform('mat4 LWVPSpot[1]', link='_biasLightViewProjectionMatrixSpotArray', included=True)
|
||||
frag.add_uniform('sampler2DShadow shadowMapSpot[1]', included=True)
|
||||
else:
|
||||
frag.add_uniform('vec2 lightProj', link='_lightPlaneProj', included=True)
|
||||
|
|
|
@ -1,31 +1,38 @@
|
|||
import os
|
||||
import bpy
|
||||
import subprocess
|
||||
import arm.utils
|
||||
import arm.assets as assets
|
||||
import arm.material.mat_utils as mat_utils
|
||||
import arm.material.mat_state as mat_state
|
||||
from arm.material.shader import ShaderData
|
||||
import arm.material.cycles as cycles
|
||||
import arm.material.make_mesh as make_mesh
|
||||
import arm.material.make_transluc as make_transluc
|
||||
import arm.material.make_overlay as make_overlay
|
||||
import arm.material.make_depth as make_depth
|
||||
import arm.material.make_decal as make_decal
|
||||
import arm.material.make_voxel as make_voxel
|
||||
from typing import Dict, List, Tuple
|
||||
|
||||
import bpy
|
||||
from bpy.types import Material
|
||||
from bpy.types import Object
|
||||
|
||||
import arm.api
|
||||
import arm.assets as assets
|
||||
import arm.exporter
|
||||
import arm.log as log
|
||||
import arm.material.cycles as cycles
|
||||
import arm.material.make_decal as make_decal
|
||||
import arm.material.make_depth as make_depth
|
||||
import arm.material.make_mesh as make_mesh
|
||||
import arm.material.make_overlay as make_overlay
|
||||
import arm.material.make_transluc as make_transluc
|
||||
import arm.material.make_voxel as make_voxel
|
||||
import arm.material.mat_state as mat_state
|
||||
import arm.material.mat_utils as mat_utils
|
||||
from arm.material.shader import Shader, ShaderContext, ShaderData
|
||||
import arm.utils
|
||||
|
||||
rpass_hook = None
|
||||
|
||||
def build(material, mat_users, mat_armusers):
|
||||
|
||||
def build(material: Material, mat_users: Dict[Material, List[Object]], mat_armusers) -> Tuple:
|
||||
mat_state.mat_users = mat_users
|
||||
mat_state.mat_armusers = mat_armusers
|
||||
mat_state.material = material
|
||||
mat_state.nodes = material.node_tree.nodes
|
||||
mat_state.data = ShaderData(material)
|
||||
mat_state.output_node = cycles.node_by_type(mat_state.nodes, 'OUTPUT_MATERIAL')
|
||||
if mat_state.output_node == None:
|
||||
if mat_state.output_node is None:
|
||||
# Place empty material output to keep compiler happy..
|
||||
mat_state.output_node = mat_state.nodes.new('ShaderNodeOutputMaterial')
|
||||
|
||||
|
@ -41,22 +48,7 @@ def build(material, mat_users, mat_armusers):
|
|||
if not os.path.exists(full_path):
|
||||
os.makedirs(full_path)
|
||||
|
||||
global_elems = []
|
||||
if mat_users != None and material in mat_users:
|
||||
for bo in mat_users[material]:
|
||||
# GPU Skinning
|
||||
if arm.utils.export_bone_data(bo):
|
||||
global_elems.append({'name': 'bone', 'data': 'short4norm'})
|
||||
global_elems.append({'name': 'weight', 'data': 'short4norm'})
|
||||
# Instancing
|
||||
if bo.arm_instanced != 'Off' or material.arm_particle_flag:
|
||||
global_elems.append({'name': 'ipos', 'data': 'float3'})
|
||||
if bo.arm_instanced == 'Loc + Rot' or bo.arm_instanced == 'Loc + Rot + Scale':
|
||||
global_elems.append({'name': 'irot', 'data': 'float3'})
|
||||
if bo.arm_instanced == 'Loc + Scale' or bo.arm_instanced == 'Loc + Rot + Scale':
|
||||
global_elems.append({'name': 'iscl', 'data': 'float3'})
|
||||
|
||||
mat_state.data.global_elems = global_elems
|
||||
make_instancing_and_skinning(material, mat_users)
|
||||
|
||||
bind_constants = dict()
|
||||
bind_textures = dict()
|
||||
|
@ -72,10 +64,10 @@ def build(material, mat_users, mat_armusers):
|
|||
|
||||
con = None
|
||||
|
||||
if rpdat.rp_driver != 'Armory' and arm.api.drivers[rpdat.rp_driver]['make_rpass'] != None:
|
||||
if rpdat.rp_driver != 'Armory' and arm.api.drivers[rpdat.rp_driver]['make_rpass'] is not None:
|
||||
con = arm.api.drivers[rpdat.rp_driver]['make_rpass'](rp)
|
||||
|
||||
if con != None:
|
||||
if con is not None:
|
||||
pass
|
||||
|
||||
elif rp == 'mesh':
|
||||
|
@ -99,7 +91,7 @@ def build(material, mat_users, mat_armusers):
|
|||
elif rp == 'voxel':
|
||||
con = make_voxel.make(rp)
|
||||
|
||||
elif rpass_hook != None:
|
||||
elif rpass_hook is not None:
|
||||
con = rpass_hook(rp)
|
||||
|
||||
write_shaders(rel_path, con, rp, matname)
|
||||
|
@ -107,7 +99,7 @@ def build(material, mat_users, mat_armusers):
|
|||
shader_data_name = matname + '_data'
|
||||
|
||||
if wrd.arm_single_data_file:
|
||||
if not 'shader_datas' in arm.exporter.current_output:
|
||||
if 'shader_datas' not in arm.exporter.current_output:
|
||||
arm.exporter.current_output['shader_datas'] = []
|
||||
arm.exporter.current_output['shader_datas'].append(mat_state.data.get()['shader_datas'][0])
|
||||
else:
|
||||
|
@ -117,7 +109,8 @@ def build(material, mat_users, mat_armusers):
|
|||
|
||||
return rpasses, mat_state.data, shader_data_name, bind_constants, bind_textures
|
||||
|
||||
def write_shaders(rel_path, con, rpass, matname):
|
||||
|
||||
def write_shaders(rel_path: str, con: ShaderContext, rpass: str, matname: str) -> None:
|
||||
keep_cache = mat_state.material.arm_cached
|
||||
write_shader(rel_path, con.vert, 'vert', rpass, matname, keep_cache=keep_cache)
|
||||
write_shader(rel_path, con.frag, 'frag', rpass, matname, keep_cache=keep_cache)
|
||||
|
@ -125,8 +118,9 @@ def write_shaders(rel_path, con, rpass, matname):
|
|||
write_shader(rel_path, con.tesc, 'tesc', rpass, matname, keep_cache=keep_cache)
|
||||
write_shader(rel_path, con.tese, 'tese', rpass, matname, keep_cache=keep_cache)
|
||||
|
||||
def write_shader(rel_path, shader, ext, rpass, matname, keep_cache=True):
|
||||
if shader == None or shader.is_linked:
|
||||
|
||||
def write_shader(rel_path: str, shader: Shader, ext: str, rpass: str, matname: str, keep_cache=True) -> None:
|
||||
if shader is None or shader.is_linked:
|
||||
return
|
||||
|
||||
# TODO: blend context
|
||||
|
@ -161,3 +155,43 @@ def write_shader(rel_path, shader, ext, rpass, matname, keep_cache=True):
|
|||
args.append('pos')
|
||||
proc = subprocess.call(args)
|
||||
os.chdir(cwd)
|
||||
|
||||
|
||||
def make_instancing_and_skinning(mat: Material, mat_users: Dict[Material, List[Object]]) -> None:
|
||||
"""Build material with instancing or skinning if enabled.
|
||||
If the material is a custom material, only validation checks for instancing are performed."""
|
||||
global_elems = []
|
||||
if mat_users is not None and mat in mat_users:
|
||||
# Whether there are both an instanced object and a not instanced object with this material
|
||||
instancing_usage = [False, False]
|
||||
|
||||
for bo in mat_users[mat]:
|
||||
if mat.arm_custom_material == '':
|
||||
# GPU Skinning
|
||||
if arm.utils.export_bone_data(bo):
|
||||
global_elems.append({'name': 'bone', 'data': 'short4norm'})
|
||||
global_elems.append({'name': 'weight', 'data': 'short4norm'})
|
||||
|
||||
# Instancing
|
||||
inst = bo.arm_instanced
|
||||
if inst != 'Off' or mat.arm_particle_flag:
|
||||
instancing_usage[0] = True
|
||||
|
||||
if mat.arm_custom_material == '':
|
||||
global_elems.append({'name': 'ipos', 'data': 'float3'})
|
||||
if 'Rot' in inst:
|
||||
global_elems.append({'name': 'irot', 'data': 'float3'})
|
||||
if 'Scale' in inst:
|
||||
global_elems.append({'name': 'iscl', 'data': 'float3'})
|
||||
|
||||
elif inst == 'Off':
|
||||
# Ignore children of instanced objects, they are instanced even when set to 'Off'
|
||||
instancing_usage[1] = bo.parent is None or bo.parent.arm_instanced == 'Off'
|
||||
|
||||
if instancing_usage[0] and instancing_usage[1]:
|
||||
# Display a warning for invalid instancing configurations
|
||||
# See https://github.com/armory3d/armory/issues/2072
|
||||
log.warn(f'Material "{mat.name}" has both instanced and not instanced objects, objects might flicker!')
|
||||
|
||||
if mat.arm_custom_material == '':
|
||||
mat_state.data.global_elems = global_elems
|
||||
|
|
|
@ -17,4 +17,4 @@ def skin_pos(vert):
|
|||
|
||||
def skin_nor(vert, prep):
|
||||
rpdat = arm.utils.get_rp()
|
||||
vert.write(prep + 'wnormal = normalize(N * (vec3(nor.xy, pos.w) + 2.0 * cross(skinA.xyz, cross(skinA.xyz, vec3(nor.xy, pos.w)) + skinA.w * vec3(nor.xy, pos.w))));')
|
||||
vert.write_attrib(prep + 'wnormal = normalize(N * (vec3(nor.xy, pos.w) + 2.0 * cross(skinA.xyz, cross(skinA.xyz, vec3(nor.xy, pos.w)) + skinA.w * vec3(nor.xy, pos.w))));')
|
||||
|
|
|
@ -192,6 +192,7 @@ class Shader:
|
|||
self.includes = []
|
||||
self.ins = []
|
||||
self.outs = []
|
||||
self.uniforms_top = []
|
||||
self.uniforms = []
|
||||
self.constants = []
|
||||
self.functions = {}
|
||||
|
@ -218,6 +219,14 @@ class Shader:
|
|||
if not self.has_include(s):
|
||||
self.includes.append(s)
|
||||
|
||||
def add_include_front(self, s):
|
||||
if not self.has_include(s):
|
||||
pos = 0
|
||||
# make sure compiled.inc is always on top
|
||||
if len(self.includes) > 0 and self.includes[0] == 'compiled.inc':
|
||||
pos = 1
|
||||
self.includes.insert(pos, s)
|
||||
|
||||
def add_in(self, s):
|
||||
if s not in self.ins:
|
||||
self.ins.append(s)
|
||||
|
@ -226,7 +235,7 @@ class Shader:
|
|||
if s not in self.outs:
|
||||
self.outs.append(s)
|
||||
|
||||
def add_uniform(self, s, link=None, included=False,
|
||||
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):
|
||||
|
@ -258,7 +267,10 @@ class Shader:
|
|||
ar[0] = 'floats'
|
||||
ar[1] = ar[1].split('[', 1)[0]
|
||||
self.context.add_constant(ar[0], ar[1], link=link)
|
||||
if not included and s not in self.uniforms:
|
||||
if top:
|
||||
if not included and s not in self.uniforms_top:
|
||||
self.uniforms_top.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):
|
||||
|
@ -397,6 +409,8 @@ class Shader:
|
|||
s += 'layout(triangle_strip) out;\n'
|
||||
s += 'layout(max_vertices=3) out;\n'
|
||||
|
||||
for a in self.uniforms_top:
|
||||
s += 'uniform ' + a + ';\n'
|
||||
for a in self.includes:
|
||||
s += '#include "' + a + '"\n'
|
||||
if self.geom_passthrough:
|
||||
|
|
|
@ -11,7 +11,7 @@ import arm.proxy
|
|||
import arm.utils
|
||||
|
||||
# Armory version
|
||||
arm_version = '2020.12'
|
||||
arm_version = '2021.4'
|
||||
arm_commit = '$Id$'
|
||||
|
||||
def get_project_html5_copy(self):
|
||||
|
@ -280,6 +280,12 @@ def init_properties():
|
|||
bpy.types.Object.arm_rb_trigger = BoolProperty(name="Trigger", description="Disable contact response", default=False)
|
||||
bpy.types.Object.arm_rb_deactivation_time = FloatProperty(name="Deactivation Time", description="Delay putting rigid body into sleep", default=0.0)
|
||||
bpy.types.Object.arm_rb_ccd = BoolProperty(name="Continuous Collision Detection", description="Improve collision for fast moving objects", default=False)
|
||||
bpy.types.Object.arm_rb_collision_filter_mask = bpy.props.BoolVectorProperty(
|
||||
name="Collision Collections Filter Mask",
|
||||
description="Collision collections rigid body interacts with",
|
||||
default=(True, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False),
|
||||
size=20,
|
||||
subtype='LAYER')
|
||||
bpy.types.Object.arm_animation_enabled = BoolProperty(name="Animation", description="Enable skinning & timeline animation", default=True)
|
||||
bpy.types.Object.arm_tilesheet = StringProperty(name="Tilesheet", description="Set tilesheet animation", default='')
|
||||
bpy.types.Object.arm_tilesheet_action = StringProperty(name="Tilesheet Action", description="Set startup action", default='')
|
||||
|
@ -332,6 +338,7 @@ def init_properties():
|
|||
bpy.types.Material.arm_overlay = BoolProperty(name="Overlay", default=False)
|
||||
bpy.types.Material.arm_decal = BoolProperty(name="Decal", default=False)
|
||||
bpy.types.Material.arm_two_sided = BoolProperty(name="Two-Sided", description="Flip normal when drawing back-face", default=False)
|
||||
bpy.types.Material.arm_ignore_irradiance = BoolProperty(name="Ignore Irradiance", description="Ignore irradiance for material", default=False)
|
||||
bpy.types.Material.arm_cull_mode = EnumProperty(
|
||||
items=[('none', 'Both', 'None'),
|
||||
('clockwise', 'Front', 'Clockwise'),
|
||||
|
|
|
@ -2,29 +2,29 @@ import bpy
|
|||
|
||||
|
||||
class ARM_PT_RbCollisionFilterMaskPanel(bpy.types.Panel):
|
||||
bl_label = "Armory Collision Filter Mask"
|
||||
bl_label = "Collections Filter Mask"
|
||||
bl_space_type = "PROPERTIES"
|
||||
bl_region_type = "WINDOW"
|
||||
bl_context = "physics"
|
||||
bl_parent_id = "ARM_PT_PhysicsPropsPanel"
|
||||
|
||||
@classmethod
|
||||
def poll(self, context):
|
||||
obj = context.object
|
||||
if obj is None:
|
||||
return False
|
||||
return obj.rigid_body is not None
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
layout.use_property_split = False
|
||||
layout.use_property_decorate = False
|
||||
obj = bpy.context.object
|
||||
if obj is None:
|
||||
return
|
||||
if obj.rigid_body is not None:
|
||||
layout.prop(obj, 'arm_rb_collision_filter_mask')
|
||||
obj = context.object
|
||||
layout.prop(obj, 'arm_rb_collision_filter_mask', text="", expand=True)
|
||||
|
||||
|
||||
def register():
|
||||
bpy.utils.register_class(ARM_PT_RbCollisionFilterMaskPanel)
|
||||
bpy.types.Object.arm_rb_collision_filter_mask = bpy.props.BoolVectorProperty(
|
||||
name="Collision Filter Mask",
|
||||
default=[True] + [False] * 19,
|
||||
size=20,
|
||||
subtype='LAYER')
|
||||
|
||||
|
||||
def unregister():
|
||||
|
|
|
@ -18,11 +18,11 @@ class ArmPropertyListItem(bpy.types.PropertyGroup):
|
|||
|
||||
class ARM_UL_PropertyList(bpy.types.UIList):
|
||||
def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index):
|
||||
layout.use_property_split = False
|
||||
# Make sure your code supports all 3 layout types
|
||||
if self.layout_type in {'DEFAULT', 'COMPACT'}:
|
||||
row = layout.row()
|
||||
row.prop(item, "name_prop", text="", emboss=False, icon="OBJECT_DATAMODE")
|
||||
row.prop(item, item.type_prop + "_prop", text="", emboss=(item.type_prop == 'boolean'))
|
||||
layout.prop(item, "name_prop", text="", emboss=False, icon="OBJECT_DATAMODE")
|
||||
layout.prop(item, item.type_prop + "_prop", text="", emboss=(item.type_prop == 'boolean'))
|
||||
elif self.layout_type in {'GRID'}:
|
||||
layout.alignment = 'CENTER'
|
||||
layout.label(text="", icon="OBJECT_DATAMODE")
|
||||
|
@ -155,4 +155,4 @@ def unregister():
|
|||
bpy.utils.unregister_class(ARM_UL_PropertyList)
|
||||
bpy.utils.unregister_class(ArmPropertyListNewItem)
|
||||
bpy.utils.unregister_class(ArmPropertyListDeleteItem)
|
||||
bpy.utils.unregister_class(ArmPropertyListMoveItem)
|
||||
bpy.utils.unregister_class(ArmPropertyListMoveItem)
|
||||
|
|
|
@ -242,6 +242,62 @@ class ArmRPListItem(bpy.types.PropertyGroup):
|
|||
rp_autoexposure: BoolProperty(name="Auto Exposure", description="Adjust exposure based on luminance", default=False, update=update_renderpath)
|
||||
rp_compositornodes: BoolProperty(name="Compositor", description="Draw compositor nodes", default=True, update=update_renderpath)
|
||||
rp_shadows: BoolProperty(name="Shadows", description="Enable shadow casting", default=True, update=update_renderpath)
|
||||
rp_max_lights: EnumProperty(
|
||||
items=[('4', '4', '4'),
|
||||
('8', '8', '8'),
|
||||
('16', '16', '16'),
|
||||
('24', '24', '24'),
|
||||
('32', '32', '32'),
|
||||
('64', '64', '64'),],
|
||||
name="Max Lights", description="Max number of lights that can be visible in the screen", default='16')
|
||||
rp_max_lights_cluster: EnumProperty(
|
||||
items=[('4', '4', '4'),
|
||||
('8', '8', '8'),
|
||||
('16', '16', '16'),
|
||||
('24', '24', '24'),
|
||||
('32', '32', '32'),
|
||||
('64', '64', '64'),],
|
||||
name="Max Lights Shadows", description="Max number of rendered shadow maps that can be visible in the screen. Always equal or lower than Max Lights", default='16')
|
||||
rp_shadowmap_atlas: BoolProperty(name="Shadow Map Atlasing", description="Group shadow maps of lights of the same type in the same texture", default=False, update=update_renderpath)
|
||||
rp_shadowmap_atlas_single_map: BoolProperty(name="Shadow Map Atlas single map", description="Use a single texture for all different light types.", default=False, update=update_renderpath)
|
||||
rp_shadowmap_atlas_lod: BoolProperty(name="Shadow Map Atlas LOD (Experimental)", description="When enabled, the size of the shadow map will be determined on runtime based on the distance of the light to the camera", default=False, update=update_renderpath)
|
||||
rp_shadowmap_atlas_lod_subdivisions: EnumProperty(
|
||||
items=[('2', '2', '2'),
|
||||
('3', '3', '3'),
|
||||
('4', '4', '4'),
|
||||
('5', '5', '5'),
|
||||
('6', '6', '6'),
|
||||
('7', '7', '7'),
|
||||
('8', '8', '8'),],
|
||||
name="LOD Subdivisions", description="Number of subdivisions of the default tile size for LOD", default='2', update=update_renderpath)
|
||||
rp_shadowmap_atlas_max_size_point: EnumProperty(
|
||||
items=[('1024', '1024', '1024'),
|
||||
('2048', '2048', '2048'),
|
||||
('4096', '4096', '4096'),
|
||||
('8192', '8192', '8192'),
|
||||
('16384', '16384', '16384')],
|
||||
name="Max Atlas Texture Size Points", description="Sets the limit of the size of the texture.", default='8192', update=update_renderpath)
|
||||
rp_shadowmap_atlas_max_size_spot: EnumProperty(
|
||||
items=[('1024', '1024', '1024'),
|
||||
('2048', '2048', '2048'),
|
||||
('4096', '4096', '4096'),
|
||||
('8192', '8192', '8192'),
|
||||
('16384', '16384', '16384')],
|
||||
name="Max Atlas Texture Size Spots", description="Sets the limit of the size of the texture.", default='8192', update=update_renderpath)
|
||||
rp_shadowmap_atlas_max_size_sun: EnumProperty(
|
||||
items=[('1024', '1024', '1024'),
|
||||
('2048', '2048', '2048'),
|
||||
('4096', '4096', '4096'),
|
||||
('8192', '8192', '8192'),
|
||||
('16384', '16384', '16384')],
|
||||
name="Max Atlas Texture Size Sun", description="Sets the limit of the size of the texture.", default='8192', update=update_renderpath)
|
||||
rp_shadowmap_atlas_max_size: EnumProperty(
|
||||
items=[('1024', '1024', '1024'),
|
||||
('2048', '2048', '2048'),
|
||||
('4096', '4096', '4096'),
|
||||
('8192', '8192', '8192'),
|
||||
('16384', '16384', '16384')],
|
||||
name="Max Atlas Texture Size", description="Sets the limit of the size of the texture.", default='8192', update=update_renderpath)
|
||||
rp_shadowmap_cube: EnumProperty(
|
||||
items=[('256', '256', '256'),
|
||||
('512', '512', '512'),
|
||||
|
|
|
@ -328,20 +328,24 @@ class ArmEditBundledScriptButton(bpy.types.Operator):
|
|||
obj = bpy.context.scene
|
||||
sdk_path = arm.utils.get_sdk_path()
|
||||
project_path = arm.utils.get_fp()
|
||||
pkg = arm.utils.safestr(bpy.data.worlds['Arm'].arm_project_package)
|
||||
item = obj.arm_traitlist[obj.arm_traitlist_index]
|
||||
source_hx_path = os.path.join(sdk_path , 'armory', 'Sources', 'armory', 'trait', item.class_name_prop + '.hx')
|
||||
target_hx_path = os.path.join(project_path, 'Sources', pkg, item.class_name_prop + '.hx')
|
||||
|
||||
pkg = arm.utils.safestr(bpy.data.worlds['Arm'].arm_project_package)
|
||||
source_hx_path = os.path.join(sdk_path, 'armory', 'Sources', 'armory', 'trait', item.class_name_prop + '.hx')
|
||||
target_dir = os.path.join(project_path, 'Sources', pkg)
|
||||
target_hx_path = os.path.join(target_dir, item.class_name_prop + '.hx')
|
||||
|
||||
if not os.path.isfile(target_hx_path):
|
||||
if not os.path.exists(target_dir):
|
||||
os.makedirs(target_dir)
|
||||
|
||||
# Rewrite package and copy
|
||||
sf = open(source_hx_path, encoding="utf-8")
|
||||
sf.readline()
|
||||
tf = open(target_hx_path, 'w', encoding="utf-8")
|
||||
tf.write('package ' + pkg + ';\n')
|
||||
shutil.copyfileobj(sf, tf)
|
||||
sf.close()
|
||||
tf.close()
|
||||
with open(source_hx_path, encoding="utf-8") as sf:
|
||||
sf.readline()
|
||||
with open(target_hx_path, 'w', encoding="utf-8") as tf:
|
||||
tf.write('package ' + pkg + ';\n')
|
||||
shutil.copyfileobj(sf, tf)
|
||||
|
||||
arm.utils.fetch_script_names()
|
||||
|
||||
# From bundled to script
|
||||
|
@ -861,9 +865,10 @@ def draw_traits_panel(layout: bpy.types.UILayout, obj: Union[bpy.types.Object, b
|
|||
if item.arm_traitpropswarnings:
|
||||
box = layout.box()
|
||||
box.label(text=f"Warnings ({len(item.arm_traitpropswarnings)}):", icon="ERROR")
|
||||
col = box.column(align=True)
|
||||
|
||||
for warning in item.arm_traitpropswarnings:
|
||||
box.label(text=warning.warning)
|
||||
col.label(text=f'"{warning.propName}": {warning.warning}')
|
||||
|
||||
propsrows = max(len(item.arm_traitpropslist), 6)
|
||||
row = layout.row()
|
||||
|
|
|
@ -32,6 +32,7 @@ def filter_objects(item, b_object):
|
|||
|
||||
|
||||
class ArmTraitPropWarning(bpy.types.PropertyGroup):
|
||||
propName: StringProperty(name="Property Name")
|
||||
warning: StringProperty(name="Warning")
|
||||
|
||||
|
||||
|
|
|
@ -1,11 +1,13 @@
|
|||
import json
|
||||
import os
|
||||
import time
|
||||
import shutil
|
||||
|
||||
import bpy
|
||||
from bpy.props import *
|
||||
|
||||
import arm.api
|
||||
import arm.assets as assets
|
||||
from arm.exporter import ArmoryExporter
|
||||
import arm.log as log
|
||||
import arm.logicnode.replacement
|
||||
import arm.make as make
|
||||
|
@ -191,7 +193,19 @@ class ARM_PT_PhysicsPropsPanel(bpy.types.Panel):
|
|||
if obj == None:
|
||||
return
|
||||
|
||||
if obj.rigid_body != None:
|
||||
rb = obj.rigid_body
|
||||
if rb is not None:
|
||||
col = layout.column()
|
||||
row = col.row()
|
||||
row.alignment = 'RIGHT'
|
||||
|
||||
rb_type = 'Dynamic'
|
||||
if ArmoryExporter.rigid_body_static(rb):
|
||||
rb_type = 'Static'
|
||||
if rb.kinematic:
|
||||
rb_type = 'Kinematic'
|
||||
row.label(text=(f'Rigid Body Export Type: {rb_type}'), icon='AUTO')
|
||||
|
||||
layout.prop(obj, 'arm_rb_linear_factor')
|
||||
layout.prop(obj, 'arm_rb_angular_factor')
|
||||
layout.prop(obj, 'arm_rb_trigger')
|
||||
|
@ -299,6 +313,146 @@ class InvalidateMaterialCacheButton(bpy.types.Operator):
|
|||
context.material.signature = ''
|
||||
return{'FINISHED'}
|
||||
|
||||
|
||||
class ARM_OT_NewCustomMaterial(bpy.types.Operator):
|
||||
bl_idname = "arm.new_custom_material"
|
||||
bl_label = "New Custom Material"
|
||||
bl_description = "Add a new custom material. This will create all the necessary files and folders"
|
||||
|
||||
def poll_mat_name(self, context):
|
||||
project_dir = arm.utils.get_fp()
|
||||
shader_dir_dst = os.path.join(project_dir, 'Shaders')
|
||||
mat_name = arm.utils.safestr(self.mat_name)
|
||||
|
||||
self.mat_exists = os.path.isdir(os.path.join(project_dir, 'Bundled', mat_name))
|
||||
|
||||
vert_exists = os.path.isfile(os.path.join(shader_dir_dst, f'{mat_name}.vert.glsl'))
|
||||
frag_exists = os.path.isfile(os.path.join(shader_dir_dst, f'{mat_name}.frag.glsl'))
|
||||
self.shader_exists = vert_exists or frag_exists
|
||||
|
||||
mat_name: StringProperty(
|
||||
name='Material Name', description='The name of the new material',
|
||||
default='MyCustomMaterial',
|
||||
update=poll_mat_name)
|
||||
mode: EnumProperty(
|
||||
name='Target RP', description='Choose for which render path mode the new material is created',
|
||||
default='deferred',
|
||||
items=[('deferred', 'Deferred', 'Create the material for a deferred render path'),
|
||||
('forward', 'Forward', 'Create the material for a forward render path')])
|
||||
mat_exists: BoolProperty(
|
||||
name='Material Already Exists',
|
||||
default=False,
|
||||
options={'HIDDEN', 'SKIP_SAVE'})
|
||||
shader_exists: BoolProperty(
|
||||
name='Shaders Already Exist',
|
||||
default=False,
|
||||
options={'HIDDEN', 'SKIP_SAVE'})
|
||||
|
||||
def invoke(self, context, event):
|
||||
if not bpy.data.is_saved:
|
||||
self.report({'INFO'}, "Please save your file first")
|
||||
return {"CANCELLED"}
|
||||
|
||||
# Try to set deferred/forward based on the selected render path
|
||||
try:
|
||||
self.mode = 'forward' if arm.utils.get_rp().rp_renderer == 'Forward' else 'deferred'
|
||||
except IndexError:
|
||||
# No render path, use default (deferred)
|
||||
pass
|
||||
|
||||
self.poll_mat_name(context)
|
||||
wm = context.window_manager
|
||||
return wm.invoke_props_dialog(self, width=300)
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
layout.use_property_split = True
|
||||
layout.use_property_decorate = False
|
||||
|
||||
layout.prop(self, 'mat_name')
|
||||
layout.prop(self, 'mode', expand=True)
|
||||
|
||||
if self.mat_exists:
|
||||
box = layout.box()
|
||||
box.alert = True
|
||||
col = box.column(align=True)
|
||||
col.label(text='A custom material with that name already exists,', icon='ERROR')
|
||||
col.label(text='clicking on \'OK\' will override the material!', icon='BLANK1')
|
||||
|
||||
if self.shader_exists:
|
||||
box = layout.box()
|
||||
box.alert = True
|
||||
col = box.column(align=True)
|
||||
col.label(text='Shader file(s) with that name already exists,', icon='ERROR')
|
||||
col.label(text='clicking on \'OK\' will override the shader(s)!', icon='BLANK1')
|
||||
|
||||
def execute(self, context):
|
||||
if self.mat_name == '':
|
||||
return {'CANCELLED'}
|
||||
|
||||
project_dir = arm.utils.get_fp()
|
||||
shader_dir_src = os.path.join(arm.utils.get_sdk_path(), 'armory', 'Shaders', 'custom_mat_presets')
|
||||
shader_dir_dst = os.path.join(project_dir, 'Shaders')
|
||||
mat_name = arm.utils.safestr(self.mat_name)
|
||||
mat_dir = os.path.join(project_dir, 'Bundled', mat_name)
|
||||
|
||||
os.makedirs(mat_dir, exist_ok=True)
|
||||
os.makedirs(shader_dir_dst, exist_ok=True)
|
||||
|
||||
# Shader data
|
||||
if self.mode == 'forward':
|
||||
col_attachments = ['RGBA64']
|
||||
constants = [{'link': '_worldViewProjectionMatrix', 'name': 'WVP', 'type': 'mat4'}]
|
||||
vertex_elems = [{'name': 'pos', 'data': 'short4norm'}]
|
||||
else:
|
||||
col_attachments = ['RGBA64', 'RGBA64']
|
||||
constants = [
|
||||
{'link': '_worldViewProjectionMatrix', 'name': 'WVP', 'type': 'mat4'},
|
||||
{'link': '_normalMatrix', 'name': 'N', 'type': 'mat3'}
|
||||
]
|
||||
vertex_elems = [
|
||||
{'name': 'pos', 'data': 'short4norm'},
|
||||
{'name': 'nor', 'data': 'short2norm'}
|
||||
]
|
||||
|
||||
con = {
|
||||
'color_attachments': col_attachments,
|
||||
'compare_mode': 'less',
|
||||
'constants': constants,
|
||||
'cull_mode': 'clockwise',
|
||||
'depth_write': True,
|
||||
'fragment_shader': f'{mat_name}.frag',
|
||||
'name': 'mesh',
|
||||
'texture_units': [],
|
||||
'vertex_shader': f'{mat_name}.vert',
|
||||
'vertex_elements': vertex_elems
|
||||
}
|
||||
data = {
|
||||
'shader_datas': [{
|
||||
'contexts': [con],
|
||||
'name': f'{mat_name}'
|
||||
}]
|
||||
}
|
||||
|
||||
# Save shader data file
|
||||
with open(os.path.join(mat_dir, f'{mat_name}.json'), 'w') as datafile:
|
||||
json.dump(data, datafile, indent=4, sort_keys=True)
|
||||
|
||||
# Copy preset shaders to project
|
||||
if self.mode == 'forward':
|
||||
shutil.copy(os.path.join(shader_dir_src, 'custom_mat_forward.frag.glsl'), os.path.join(shader_dir_dst, f'{mat_name}.frag.glsl'))
|
||||
shutil.copy(os.path.join(shader_dir_src, 'custom_mat_forward.vert.glsl'), os.path.join(shader_dir_dst, f'{mat_name}.vert.glsl'))
|
||||
else:
|
||||
shutil.copy(os.path.join(shader_dir_src, 'custom_mat_deferred.frag.glsl'), os.path.join(shader_dir_dst, f'{mat_name}.frag.glsl'))
|
||||
shutil.copy(os.path.join(shader_dir_src, 'custom_mat_deferred.vert.glsl'), os.path.join(shader_dir_dst, f'{mat_name}.vert.glsl'))
|
||||
|
||||
# True if called from the material properties tab, else it was called from the search menu
|
||||
if hasattr(context, 'material') and context.material is not None:
|
||||
context.material.arm_custom_material = mat_name
|
||||
|
||||
return{'FINISHED'}
|
||||
|
||||
|
||||
class ARM_PT_MaterialPropsPanel(bpy.types.Panel):
|
||||
bl_label = "Armory Props"
|
||||
bl_space_type = "PROPERTIES"
|
||||
|
@ -318,6 +472,7 @@ class ARM_PT_MaterialPropsPanel(bpy.types.Panel):
|
|||
wrd = bpy.data.worlds['Arm']
|
||||
columnb.enabled = len(wrd.arm_rplist) > 0 and arm.utils.get_rp().rp_renderer == 'Forward'
|
||||
columnb.prop(mat, 'arm_receive_shadow')
|
||||
layout.prop(mat, 'arm_ignore_irradiance')
|
||||
layout.prop(mat, 'arm_two_sided')
|
||||
columnb = layout.column()
|
||||
columnb.enabled = not mat.arm_two_sided
|
||||
|
@ -330,7 +485,9 @@ class ARM_PT_MaterialPropsPanel(bpy.types.Panel):
|
|||
columnb.enabled = mat.arm_discard
|
||||
columnb.prop(mat, 'arm_discard_opacity')
|
||||
columnb.prop(mat, 'arm_discard_opacity_shadows')
|
||||
layout.prop(mat, 'arm_custom_material')
|
||||
row = layout.row(align=True)
|
||||
row.prop(mat, 'arm_custom_material')
|
||||
row.operator('arm.new_custom_material', text='', icon='ADD')
|
||||
layout.prop(mat, 'arm_skip_context')
|
||||
layout.prop(mat, 'arm_particle_fade')
|
||||
layout.prop(mat, 'arm_billboard')
|
||||
|
@ -436,7 +593,20 @@ class ARM_PT_ArmoryPlayerPanel(bpy.types.Panel):
|
|||
box.alert = True
|
||||
|
||||
col = box.column(align=True)
|
||||
col.label(text=f'{log.num_warnings} warning{"s" if log.num_warnings > 1 else ""} occurred during compilation!', icon='ERROR')
|
||||
warnings = 'warnings' if log.num_warnings > 1 else 'warning'
|
||||
col.label(text=f'{log.num_warnings} {warnings} occurred during compilation!', icon='ERROR')
|
||||
# Blank icon to achieve the same indentation as the line before
|
||||
# prevent showing "open console" twice:
|
||||
if log.num_errors == 0:
|
||||
col.label(text='Please open the console to get more information.', icon='BLANK1')
|
||||
|
||||
if log.num_errors > 0:
|
||||
box = layout.box()
|
||||
box.alert = True
|
||||
# Less spacing between lines
|
||||
col = box.column(align=True)
|
||||
errors = 'errors' if log.num_errors > 1 else 'error'
|
||||
col.label(text=f'{log.num_errors} {errors} occurred during compilation!', icon='CANCEL')
|
||||
# Blank icon to achieve the same indentation as the line before
|
||||
col.label(text='Please open the console to get more information.', icon='BLANK1')
|
||||
|
||||
|
@ -1158,6 +1328,13 @@ class ARM_PT_RenderPathShadowsPanel(bpy.types.Panel):
|
|||
rpdat = wrd.arm_rplist[wrd.arm_rplist_index]
|
||||
self.layout.prop(rpdat, "rp_shadows", text="")
|
||||
|
||||
def compute_subdivs(self, max, subdivs):
|
||||
l = [max]
|
||||
for i in range(subdivs - 1):
|
||||
l.append(int(max / 2))
|
||||
max = max / 2
|
||||
return l
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
layout.use_property_split = True
|
||||
|
@ -1177,6 +1354,55 @@ class ARM_PT_RenderPathShadowsPanel(bpy.types.Panel):
|
|||
col2.prop(rpdat, 'arm_shadowmap_split')
|
||||
col.prop(rpdat, 'arm_shadowmap_bounds')
|
||||
col.prop(rpdat, 'arm_pcfsize')
|
||||
layout.separator()
|
||||
|
||||
layout.prop(rpdat, 'rp_shadowmap_atlas')
|
||||
colatlas = layout.column()
|
||||
colatlas.enabled = rpdat.rp_shadowmap_atlas
|
||||
colatlas.prop(rpdat, 'rp_max_lights')
|
||||
colatlas.prop(rpdat, 'rp_max_lights_cluster')
|
||||
colatlas.prop(rpdat, 'rp_shadowmap_atlas_lod')
|
||||
|
||||
colatlas_lod = colatlas.column()
|
||||
colatlas_lod.enabled = rpdat.rp_shadowmap_atlas_lod
|
||||
colatlas_lod.prop(rpdat, 'rp_shadowmap_atlas_lod_subdivisions')
|
||||
|
||||
colatlas_lod_info = colatlas_lod.row()
|
||||
colatlas_lod_info.alignment = 'RIGHT'
|
||||
subdivs_list = self.compute_subdivs(int(rpdat.rp_shadowmap_cascade), int(rpdat.rp_shadowmap_atlas_lod_subdivisions))
|
||||
subdiv_text = "Subdivisions: " + ', '.join(map(str, subdivs_list))
|
||||
colatlas_lod_info.label(text=subdiv_text, icon="IMAGE_ZDEPTH")
|
||||
|
||||
colatlas.prop(rpdat, 'rp_shadowmap_atlas_single_map')
|
||||
# show size for single texture
|
||||
if rpdat.rp_shadowmap_atlas_single_map:
|
||||
colatlas_single = colatlas.column()
|
||||
colatlas_single.prop(rpdat, 'rp_shadowmap_atlas_max_size')
|
||||
if int(rpdat.rp_shadowmap_cascade) >= int(rpdat.rp_shadowmap_atlas_max_size):
|
||||
print(rpdat.rp_shadowmap_atlas_max_size)
|
||||
colatlas_warning = colatlas_single.row()
|
||||
colatlas_warning.alignment = 'RIGHT'
|
||||
colatlas_warning.label(text=f'Warning: {rpdat.rp_shadowmap_atlas_max_size} is too small for the shadowmap size: {rpdat.rp_shadowmap_cascade}', icon="ERROR")
|
||||
else:
|
||||
# show size for all types
|
||||
colatlas_mixed = colatlas.column()
|
||||
colatlas_mixed.prop(rpdat, 'rp_shadowmap_atlas_max_size_spot')
|
||||
if int(rpdat.rp_shadowmap_cascade) > int(rpdat.rp_shadowmap_atlas_max_size_spot):
|
||||
colatlas_warning = colatlas_mixed.row()
|
||||
colatlas_warning.alignment = 'RIGHT'
|
||||
colatlas_warning.label(text=f'Warning: {rpdat.rp_shadowmap_atlas_max_size_spot} is too small for the shadowmap size: {rpdat.rp_shadowmap_cascade}', icon="ERROR")
|
||||
|
||||
colatlas_mixed.prop(rpdat, 'rp_shadowmap_atlas_max_size_point')
|
||||
if int(rpdat.rp_shadowmap_cascade) >= int(rpdat.rp_shadowmap_atlas_max_size_point):
|
||||
colatlas_warning = colatlas_mixed.row()
|
||||
colatlas_warning.alignment = 'RIGHT'
|
||||
colatlas_warning.label(text=f'Warning: {rpdat.rp_shadowmap_atlas_max_size_point} is too small for the shadowmap size: {rpdat.rp_shadowmap_cube}', icon="ERROR")
|
||||
|
||||
colatlas_mixed.prop(rpdat, 'rp_shadowmap_atlas_max_size_sun')
|
||||
if int(rpdat.rp_shadowmap_cascade) >= int(rpdat.rp_shadowmap_atlas_max_size_sun):
|
||||
colatlas_warning = colatlas_mixed.row()
|
||||
colatlas_warning.alignment = 'RIGHT'
|
||||
colatlas_warning.label(text=f'Warning: {rpdat.rp_shadowmap_atlas_max_size_sun} is too small for the shadowmap size: {rpdat.rp_shadowmap_cascade}', icon="ERROR")
|
||||
|
||||
class ARM_PT_RenderPathVoxelsPanel(bpy.types.Panel):
|
||||
bl_label = "Voxel AO"
|
||||
|
@ -2210,6 +2436,7 @@ def register():
|
|||
bpy.utils.register_class(ARM_PT_WorldPropsPanel)
|
||||
bpy.utils.register_class(InvalidateCacheButton)
|
||||
bpy.utils.register_class(InvalidateMaterialCacheButton)
|
||||
bpy.utils.register_class(ARM_OT_NewCustomMaterial)
|
||||
bpy.utils.register_class(ARM_PT_MaterialPropsPanel)
|
||||
bpy.utils.register_class(ARM_PT_MaterialBlendingPropsPanel)
|
||||
bpy.utils.register_class(ARM_PT_MaterialDriverPropsPanel)
|
||||
|
@ -2297,6 +2524,7 @@ def unregister():
|
|||
bpy.utils.unregister_class(ARM_PT_ScenePropsPanel)
|
||||
bpy.utils.unregister_class(InvalidateCacheButton)
|
||||
bpy.utils.unregister_class(InvalidateMaterialCacheButton)
|
||||
bpy.utils.unregister_class(ARM_OT_NewCustomMaterial)
|
||||
bpy.utils.unregister_class(ARM_PT_MaterialDriverPropsPanel)
|
||||
bpy.utils.unregister_class(ARM_PT_MaterialBlendingPropsPanel)
|
||||
bpy.utils.unregister_class(ARM_PT_MaterialPropsPanel)
|
||||
|
@ -2349,4 +2577,4 @@ def unregister():
|
|||
bpy.utils.unregister_class(scene.TLM_PT_Filtering)
|
||||
bpy.utils.unregister_class(scene.TLM_PT_Encoding)
|
||||
bpy.utils.unregister_class(scene.TLM_PT_Utility)
|
||||
bpy.utils.unregister_class(scene.TLM_PT_Additional)
|
||||
bpy.utils.unregister_class(scene.TLM_PT_Additional)
|
||||
|
|
|
@ -4,7 +4,7 @@ import os
|
|||
import platform
|
||||
import re
|
||||
import subprocess
|
||||
from typing import Any
|
||||
from typing import Any, Dict, List, Optional, Tuple
|
||||
import webbrowser
|
||||
import shlex
|
||||
import locale
|
||||
|
@ -156,11 +156,11 @@ def get_sdk_path():
|
|||
addon_prefs = get_arm_preferences()
|
||||
p = bundled_sdk_path()
|
||||
if use_local_sdk:
|
||||
return get_fp() + '/armsdk/'
|
||||
return os.path.normpath(get_fp() + '/armsdk/')
|
||||
elif os.path.exists(p) and addon_prefs.sdk_bundled:
|
||||
return p
|
||||
return os.path.normpath(p)
|
||||
else:
|
||||
return addon_prefs.sdk_path
|
||||
return os.path.normpath(addon_prefs.sdk_path)
|
||||
|
||||
def get_last_commit():
|
||||
p = get_sdk_path() + 'armory/.git/refs/heads/master'
|
||||
|
@ -292,116 +292,97 @@ def fetch_bundled_script_names():
|
|||
for file in glob.glob('*.hx'):
|
||||
wrd.arm_bundled_scripts_list.add().name = file.rsplit('.', 1)[0]
|
||||
|
||||
|
||||
script_props = {}
|
||||
script_props_defaults = {}
|
||||
script_warnings = {}
|
||||
def fetch_script_props(file):
|
||||
with open(file, encoding="utf-8") as f:
|
||||
name = file.rsplit('.', 1)[0]
|
||||
if 'Sources' in name:
|
||||
name = name[name.index('Sources') + 8:]
|
||||
if '/' in name:
|
||||
name = name.replace('/', '.')
|
||||
if '\\' in file:
|
||||
name = name.replace('\\', '.')
|
||||
script_warnings: Dict[str, List[Tuple[str, str]]] = {} # Script name -> List of (identifier, warning message)
|
||||
|
||||
script_props[name] = []
|
||||
script_props_defaults[name] = []
|
||||
script_warnings[name] = []
|
||||
# See https://regex101.com/r/bbrCzN/8
|
||||
RX_MODIFIERS = r'(?P<modifiers>(?:public\s+|private\s+|static\s+|inline\s+|final\s+)*)?' # Optional modifiers
|
||||
RX_IDENTIFIER = r'(?P<identifier>[_$a-z]+[_a-z0-9]*)' # Variable name, follow Haxe rules
|
||||
RX_TYPE = r'(?::\s+(?P<type>[_a-z]+[\._a-z0-9]*))?' # Optional type annotation
|
||||
RX_VALUE = r'(?:\s*=\s*(?P<value>(?:\".*\")|(?:[^;]+)|))?' # Optional default value
|
||||
|
||||
lines = f.read().splitlines()
|
||||
PROP_REGEX_RAW = fr'@prop\s+{RX_MODIFIERS}(?P<attr_type>var|final)\s+{RX_IDENTIFIER}{RX_TYPE}{RX_VALUE};'
|
||||
PROP_REGEX = re.compile(PROP_REGEX_RAW, re.IGNORECASE)
|
||||
def fetch_script_props(filename: str):
|
||||
"""Parses @prop declarations from the given Haxe script."""
|
||||
with open(filename, 'r', encoding='utf-8') as sourcefile:
|
||||
source = sourcefile.read()
|
||||
|
||||
# Read next line
|
||||
read_prop = False
|
||||
for lineno, line in enumerate(lines):
|
||||
# enumerate() starts with 0
|
||||
lineno += 1
|
||||
if source == '':
|
||||
return
|
||||
|
||||
if not read_prop:
|
||||
read_prop = line.lstrip().startswith('@prop')
|
||||
name = filename.rsplit('.', 1)[0]
|
||||
|
||||
# Convert the name into a package path relative to the "Sources" dir
|
||||
if 'Sources' in name:
|
||||
name = name[name.index('Sources') + 8:]
|
||||
if '/' in name:
|
||||
name = name.replace('/', '.')
|
||||
if '\\' in filename:
|
||||
name = name.replace('\\', '.')
|
||||
|
||||
script_props[name] = []
|
||||
script_props_defaults[name] = []
|
||||
script_warnings[name] = []
|
||||
|
||||
for match in re.finditer(PROP_REGEX, source):
|
||||
|
||||
p_modifiers: Optional[str] = match.group('modifiers')
|
||||
p_identifier: str = match.group('identifier')
|
||||
p_type: Optional[str] = match.group('type')
|
||||
p_default_val: Optional[str] = match.group('value')
|
||||
|
||||
if p_modifiers is not None:
|
||||
if 'static' in p_modifiers:
|
||||
script_warnings[name].append((p_identifier, '`static` modifier might cause unwanted behaviour!'))
|
||||
if 'inline' in p_modifiers:
|
||||
script_warnings[name].append((p_identifier, '`inline` modifier is not supported!'))
|
||||
continue
|
||||
if 'final' in p_modifiers or match.group('attr_type') == 'final':
|
||||
script_warnings[name].append((p_identifier, '`final` properties are not supported!'))
|
||||
continue
|
||||
|
||||
if read_prop:
|
||||
if 'var ' in line:
|
||||
# Line of code
|
||||
code_ref = line.split('var ')[1].split(';')[0]
|
||||
else:
|
||||
script_warnings[name].append(f"Line {lineno - 1}: Unused @prop")
|
||||
read_prop = line.lstrip().startswith('@prop')
|
||||
continue
|
||||
# Property type is annotated
|
||||
if p_type is not None:
|
||||
if p_type.startswith("iron.object."):
|
||||
p_type = p_type[12:]
|
||||
elif p_type.startswith("iron.math."):
|
||||
p_type = p_type[10:]
|
||||
|
||||
valid_prop = False
|
||||
type_default_val = get_type_default_value(p_type)
|
||||
if type_default_val is None:
|
||||
script_warnings[name].append((p_identifier, f'unsupported type `{p_type}`!'))
|
||||
continue
|
||||
|
||||
# Declaration = Assignment;
|
||||
var_sides = code_ref.split('=')
|
||||
# DeclarationName: DeclarationType
|
||||
decl_sides = var_sides[0].split(':')
|
||||
# Default value exists
|
||||
if p_default_val is not None:
|
||||
# Remove string quotes
|
||||
p_default_val = p_default_val.replace('\'', '').replace('"', '')
|
||||
else:
|
||||
p_default_val = type_default_val
|
||||
|
||||
prop_name = decl_sides[0].strip()
|
||||
# Default value is given instead, try to infer the properties type from it
|
||||
elif p_default_val is not None:
|
||||
p_type = get_prop_type_from_value(p_default_val)
|
||||
|
||||
if 'static ' in line:
|
||||
# Static properties can be overwritten multiple times
|
||||
# from multiple property lists
|
||||
script_warnings[name].append(f"Line {lineno} (\"{prop_name}\"): Static properties may result in undefined behaviours!")
|
||||
# Type is not recognized
|
||||
if p_type is None:
|
||||
script_warnings[name].append((p_identifier, 'could not infer property type from given value!'))
|
||||
continue
|
||||
if p_type == "String":
|
||||
p_default_val = p_default_val.replace('\'', '').replace('"', '')
|
||||
|
||||
# If the prop type is annotated in the code
|
||||
# (= declaration has two parts)
|
||||
if len(decl_sides) > 1:
|
||||
prop_type = decl_sides[1].strip()
|
||||
if prop_type.startswith("iron.object."):
|
||||
prop_type = prop_type[12:]
|
||||
elif prop_type.startswith("iron.math."):
|
||||
prop_type = prop_type[10:]
|
||||
else:
|
||||
script_warnings[name].append((p_identifier, 'missing type or default value!'))
|
||||
continue
|
||||
|
||||
# Default value exists
|
||||
if len(var_sides) > 1 and var_sides[1].strip() != "":
|
||||
# Type is not supported
|
||||
if get_type_default_value(prop_type) is None:
|
||||
script_warnings[name].append(f"Line {lineno} (\"{prop_name}\"): Type {prop_type} is not supported!")
|
||||
read_prop = False
|
||||
continue
|
||||
# Register prop
|
||||
prop = (p_identifier, p_type)
|
||||
script_props[name].append(prop)
|
||||
script_props_defaults[name].append(p_default_val)
|
||||
|
||||
prop_value = var_sides[1].replace('\'', '').replace('"', '').strip()
|
||||
|
||||
else:
|
||||
prop_value = get_type_default_value(prop_type)
|
||||
|
||||
# Type is not supported
|
||||
if prop_value is None:
|
||||
script_warnings[name].append(f"Line {lineno} (\"{prop_name}\"): Type {prop_type} is not supported!")
|
||||
read_prop = False
|
||||
continue
|
||||
|
||||
valid_prop = True
|
||||
|
||||
# Default value exists
|
||||
elif len(var_sides) > 1 and var_sides[1].strip() != "":
|
||||
prop_value = var_sides[1].strip()
|
||||
prop_type = get_prop_type_from_value(prop_value)
|
||||
|
||||
# Type is not recognized
|
||||
if prop_type is None:
|
||||
script_warnings[name].append(f"Line {lineno} (\"{prop_name}\"): Property type not recognized!")
|
||||
read_prop = False
|
||||
continue
|
||||
if prop_type == "String":
|
||||
prop_value = prop_value.replace('\'', '').replace('"', '')
|
||||
|
||||
valid_prop = True
|
||||
|
||||
else:
|
||||
script_warnings[name].append(f"Line {lineno} (\"{prop_name}\"): Not a valid property!")
|
||||
read_prop = False
|
||||
continue
|
||||
|
||||
prop = (prop_name, prop_type)
|
||||
|
||||
# Register prop
|
||||
if valid_prop:
|
||||
script_props[name].append(prop)
|
||||
script_props_defaults[name].append(prop_value)
|
||||
|
||||
read_prop = False
|
||||
|
||||
def get_prop_type_from_value(value: str):
|
||||
"""
|
||||
|
@ -555,7 +536,8 @@ def fetch_prop(o):
|
|||
item.arm_traitpropswarnings.clear()
|
||||
for warning in warnings:
|
||||
entry = item.arm_traitpropswarnings.add()
|
||||
entry.warning = warning
|
||||
entry.propName = warning[0]
|
||||
entry.warning = warning[1]
|
||||
|
||||
def fetch_bundled_trait_props():
|
||||
# Bundled script props
|
||||
|
@ -594,7 +576,7 @@ def safesrc(s):
|
|||
def safestr(s: str) -> str:
|
||||
"""Outputs a string where special characters have been replaced with
|
||||
'_', which can be safely used in file and path names."""
|
||||
for c in r'[]/\;,><&*:%=+@!#^()|?^':
|
||||
for c in r'''[]/\;,><&*:%=+@!#^()|?^'"''':
|
||||
s = s.replace(c, '_')
|
||||
return ''.join([i if ord(i) < 128 else '_' for i in s])
|
||||
|
||||
|
@ -1096,4 +1078,4 @@ def register(local_sdk=False):
|
|||
use_local_sdk = local_sdk
|
||||
|
||||
def unregister():
|
||||
pass
|
||||
pass
|
||||
|
|
|
@ -254,6 +254,9 @@ project.addSources('Sources');
|
|||
assets.add_khafile_def('arm_debug')
|
||||
khafile.write(add_shaders(sdk_path + "/armory/Shaders/debug_draw/**", rel_path=do_relpath_sdk))
|
||||
|
||||
if not is_publish and state.target == 'html5':
|
||||
khafile.write("project.addParameter('--debug');\n")
|
||||
|
||||
if wrd.arm_verbose_output:
|
||||
khafile.write("project.addParameter('--times');\n")
|
||||
|
||||
|
@ -517,6 +520,7 @@ def write_indexhtml(w, h, is_publish):
|
|||
add_compiledglsl = ''
|
||||
def write_compiledglsl(defs, make_variants):
|
||||
rpdat = arm.utils.get_rp()
|
||||
wrd = bpy.data.worlds['Arm']
|
||||
shadowmap_size = arm.utils.get_cascade_size(rpdat) if rpdat.rp_shadows else 0
|
||||
with open(arm.utils.build_dir() + '/compiled/Shaders/compiled.inc', 'w') as f:
|
||||
f.write(
|
||||
|
@ -533,6 +537,9 @@ def write_compiledglsl(defs, make_variants):
|
|||
#endif
|
||||
""")
|
||||
|
||||
if state.target == 'html5' or arm.utils.get_gapi() == 'direct3d11':
|
||||
f.write("#define _FlipY\n")
|
||||
|
||||
f.write("""const float PI = 3.1415926535;
|
||||
const float PI2 = PI * 2.0;
|
||||
const vec2 shadowmapSize = vec2(""" + str(shadowmap_size) + """, """ + str(shadowmap_size) + """);
|
||||
|
@ -689,6 +696,22 @@ const float voxelgiAperture = """ + str(round(rpdat.arm_voxelgi_aperture * 100)
|
|||
if rpdat.arm_skin == 'On':
|
||||
f.write(
|
||||
"""const int skinMaxBones = """ + str(rpdat.arm_skin_max_bones) + """;
|
||||
""")
|
||||
|
||||
if '_Clusters' in wrd.world_defs:
|
||||
max_lights = "4"
|
||||
max_lights_clusters = "4"
|
||||
if rpdat.rp_shadowmap_atlas:
|
||||
max_lights = str(rpdat.rp_max_lights)
|
||||
max_lights_clusters = str(rpdat.rp_max_lights_cluster)
|
||||
# prevent max lights cluster being higher than max lights
|
||||
if (int(max_lights_clusters) > int(max_lights)):
|
||||
max_lights_clusters = max_lights
|
||||
|
||||
f.write(
|
||||
"""const int maxLights = """ + max_lights + """;
|
||||
const int maxLightsCluster = """ + max_lights_clusters + """;
|
||||
const float clusterNear = 3.0;
|
||||
""")
|
||||
|
||||
f.write(add_compiledglsl + '\n') # External defined constants
|
||||
|
|
Loading…
Reference in a new issue