forked from LeenkxTeam/LNXSDK
1830 lines
55 KiB
Haxe
1830 lines
55 KiB
Haxe
package leenkx.renderpath;
|
|
|
|
import iron.RenderPath;
|
|
import iron.object.LightObject;
|
|
|
|
import leenkx.math.Helper;
|
|
import kha.arrays.Float32Array;
|
|
|
|
class Inc {
|
|
static var path: RenderPath;
|
|
public static var superSample = 1.0;
|
|
|
|
static var pointIndex = 0;
|
|
static var spotIndex = 0;
|
|
static var lastFrame = -1;
|
|
|
|
#if ((rp_voxels != 'Off') && lnx_config)
|
|
static var voxelsCreated = false;
|
|
#end
|
|
|
|
#if (rp_voxels != "Off")
|
|
static var voxel_sh0:kha.compute.Shader = null;
|
|
static var voxel_sh1:kha.compute.Shader = null;
|
|
static var voxel_ta0:kha.compute.TextureUnit;
|
|
static var voxel_tb0:kha.compute.TextureUnit;
|
|
static var voxel_ca0:kha.compute.ConstantLocation;
|
|
static var voxel_cb0:kha.compute.ConstantLocation;
|
|
static var voxel_ta1:kha.compute.TextureUnit;
|
|
static var voxel_tb1:kha.compute.TextureUnit;
|
|
static var voxel_tc1:kha.compute.TextureUnit;
|
|
static var m = iron.math.Mat4.identity();
|
|
static var voxel_ca1:kha.compute.ConstantLocation;
|
|
static var voxel_cb1:kha.compute.ConstantLocation;
|
|
#if (rp_voxels == "Voxel GI")
|
|
static var voxel_td1:kha.compute.TextureUnit;
|
|
static var voxel_te1:kha.compute.TextureUnit;
|
|
static var voxel_cc1:kha.compute.ConstantLocation;
|
|
#else
|
|
#if lnx_voxelgi_shadows
|
|
static var voxel_te1:kha.compute.TextureUnit;
|
|
#end
|
|
#end
|
|
#if (lnx_voxelgi_shadows || rp_voxels == "Voxel GI")
|
|
static var voxel_sh2:kha.compute.Shader = null;
|
|
static var voxel_ta2:kha.compute.TextureUnit;
|
|
static var voxel_tb2:kha.compute.TextureUnit;
|
|
static var voxel_ca2:kha.compute.ConstantLocation;
|
|
static var voxel_cb2:kha.compute.ConstantLocation;
|
|
static var voxel_cc2:kha.compute.ConstantLocation;
|
|
#end
|
|
static var voxel_sh3:kha.compute.Shader = null;
|
|
static var voxel_ta3:kha.compute.TextureUnit;
|
|
static var voxel_tb3:kha.compute.TextureUnit;
|
|
static var voxel_tc3:kha.compute.TextureUnit;
|
|
static var voxel_td3:kha.compute.TextureUnit;
|
|
static var voxel_te3:kha.compute.TextureUnit;
|
|
static var voxel_tf3:kha.compute.TextureUnit;
|
|
#if lnx_brdf
|
|
static var voxel_tg3:kha.compute.TextureUnit;
|
|
#end
|
|
#if lnx_radiance
|
|
static var voxel_th3:kha.compute.TextureUnit;
|
|
#end
|
|
static var voxel_ca3:kha.compute.ConstantLocation;
|
|
static var voxel_cb3:kha.compute.ConstantLocation;
|
|
static var voxel_cc3:kha.compute.ConstantLocation;
|
|
static var voxel_cd3:kha.compute.ConstantLocation;
|
|
static var voxel_ce3:kha.compute.ConstantLocation;
|
|
#if lnx_irradiance
|
|
static var voxel_cf3:kha.compute.ConstantLocation;
|
|
#end
|
|
#if lnx_radiance
|
|
static var voxel_cg3:kha.compute.ConstantLocation;
|
|
#end
|
|
#if lnx_envcol
|
|
static var voxel_ch3:kha.compute.ConstantLocation;
|
|
#end
|
|
#if (rp_voxels == "Voxel GI")
|
|
static var voxel_sh4:kha.compute.Shader = null;
|
|
static var voxel_ta4:kha.compute.TextureUnit;
|
|
static var voxel_tb4:kha.compute.TextureUnit;
|
|
static var voxel_tc4:kha.compute.TextureUnit;
|
|
static var voxel_td4:kha.compute.TextureUnit;
|
|
static var voxel_te4:kha.compute.TextureUnit;
|
|
static var voxel_tf4:kha.compute.TextureUnit;
|
|
static var voxel_ca4:kha.compute.ConstantLocation;
|
|
static var voxel_cb4:kha.compute.ConstantLocation;
|
|
static var voxel_cc4:kha.compute.ConstantLocation;
|
|
static var voxel_cd4:kha.compute.ConstantLocation;
|
|
#end
|
|
#end //rp_voxels
|
|
|
|
public static function init(_path: RenderPath) {
|
|
path = _path;
|
|
|
|
#if lnx_config
|
|
var config = leenkx.data.Config.raw;
|
|
for (l in iron.Scene.active.lights) {
|
|
l.data.raw.shadowmap_size = l.data.raw.type == "sun" ?
|
|
config.rp_shadowmap_cascade :
|
|
config.rp_shadowmap_cube;
|
|
}
|
|
superSample = config.rp_supersample;
|
|
#else
|
|
|
|
#if (rp_supersampling == 1.5)
|
|
superSample = 1.5;
|
|
#elseif (rp_supersampling == 2)
|
|
superSample = 2.0;
|
|
#elseif (rp_supersampling == 4)
|
|
superSample = 4.0;
|
|
#end
|
|
|
|
#end
|
|
}
|
|
|
|
#if lnx_shadowmap_atlas
|
|
public static function updatePointLightAtlasData(transparent: Bool): Void {
|
|
var atlas = transparent ? ShadowMapAtlas.shadowMapAtlasesTransparent.get(ShadowMapAtlas.shadowMapAtlasName("point", true)) : ShadowMapAtlas.shadowMapAtlases.get(ShadowMapAtlas.shadowMapAtlasName("point", false));
|
|
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);
|
|
}
|
|
#if rp_shadowmap_transparent
|
|
for (atlas in ShadowMapAtlas.shadowMapAtlasesTransparent) {
|
|
path.bindTarget(atlas.target, atlas.target);
|
|
}
|
|
#end
|
|
}
|
|
|
|
static function getShadowMapAtlas(atlas:ShadowMapAtlas, transparent: Bool):String {
|
|
inline function createDepthTarget(name: String, size: Int) {
|
|
var t = new RenderTargetRaw();
|
|
t.name = name;
|
|
t.width = t.height = size;
|
|
t.format = transparent ? "RGBA64" : "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
|
|
|
|
#if lnx_debug
|
|
beginShadowsLogicProfile();
|
|
// reset data on rejected lights
|
|
for (atlas in ShadowMapAtlas.shadowMapAtlases) {
|
|
atlas.rejectedLights = [];
|
|
}
|
|
#if rp_shadowmap_transparent
|
|
for (atlas in ShadowMapAtlas.shadowMapAtlasesTransparent) {
|
|
atlas.rejectedLights = [];
|
|
}
|
|
#end
|
|
#end
|
|
|
|
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) {
|
|
ShadowMapAtlas.addLight(light, false);
|
|
}
|
|
#if rp_shadowmap_transparent
|
|
if (!light.lightInAtlasTransparent && !light.culledLight && light.visible && light.shadowMapScale > 0.0
|
|
&& light.data.raw.strength > 0.0 && light.data.raw.cast_shadow) {
|
|
ShadowMapAtlas.addLight(light, true);
|
|
}
|
|
#end
|
|
}
|
|
// update point light data before rendering
|
|
updatePointLightAtlasData(false);
|
|
#if rp_shadowmap_transparent
|
|
updatePointLightAtlasData(true);
|
|
#end
|
|
|
|
for (atlas in ShadowMapAtlas.shadowMapAtlases) {
|
|
var tilesToRemove = [];
|
|
#if lnx_shadowmap_atlas_lod
|
|
var tilesToChangeSize = [];
|
|
#end
|
|
|
|
var shadowmap = getShadowMapAtlas(atlas, false);
|
|
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 lnx_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 / atlas.sizew;
|
|
tile.light.tileOffsetY[j] = 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);
|
|
|
|
#if lnx_debug
|
|
beginShadowsRenderProfile();
|
|
#end
|
|
tile.forEachTileLinked(function (lTile) {
|
|
if (faces > 1) {
|
|
#if lnx_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");
|
|
});
|
|
#if lnx_debug
|
|
endShadowsRenderProfile();
|
|
#end
|
|
|
|
path.currentFace = -1;
|
|
}
|
|
path.endStream();
|
|
}
|
|
|
|
#if rp_shadowmap_transparent
|
|
for (atlas in ShadowMapAtlas.shadowMapAtlasesTransparent) {
|
|
var tilesToRemove = [];
|
|
#if lnx_shadowmap_atlas_lod
|
|
var tilesToChangeSize = [];
|
|
#end
|
|
|
|
var shadowmap = getShadowMapAtlas(atlas, true);
|
|
path.setTargetStream(shadowmap);
|
|
|
|
path.clearTarget(0xffffff, 0.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 lnx_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 / atlas.sizew;
|
|
tile.light.tileOffsetY[j] = 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);
|
|
|
|
#if lnx_debug
|
|
beginShadowsRenderProfile();
|
|
#end
|
|
tile.forEachTileLinked(function (lTile) {
|
|
if (faces > 1) {
|
|
#if lnx_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_transparent");
|
|
});
|
|
#if lnx_debug
|
|
endShadowsRenderProfile();
|
|
#end
|
|
|
|
path.currentFace = -1;
|
|
}
|
|
path.endStream();
|
|
|
|
#if lnx_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(false);
|
|
// update point light data after changing size of tiles to avoid render issues
|
|
updatePointLightAtlasData(true);
|
|
#end
|
|
|
|
for (tile in tilesToRemove) {
|
|
atlas.activeTiles.remove(tile);
|
|
tile.freeTile();
|
|
}
|
|
}
|
|
#end
|
|
#end
|
|
}
|
|
#else
|
|
public static function bindShadowMap() {
|
|
for (l in iron.Scene.active.lights) {
|
|
if (!l.visible || l.data.raw.type != "sun") continue;
|
|
var n = "shadowMap";
|
|
path.bindTarget(n, n);
|
|
var n = "shadowMapTransparent";
|
|
path.bindTarget(n, n);
|
|
break;
|
|
}
|
|
for (i in 0...pointIndex) {
|
|
var n = "shadowMapPoint[" + i + "]";
|
|
path.bindTarget(n, n);
|
|
var n = "shadowMapPointTransparent[" + i + "]";
|
|
path.bindTarget(n, n);
|
|
}
|
|
for (i in 0...spotIndex) {
|
|
var n = "shadowMapSpot[" + i + "]";
|
|
path.bindTarget(n, n);
|
|
var n = "shadowMapSpotTransparent[" + i + "]";
|
|
path.bindTarget(n, n);
|
|
}
|
|
}
|
|
|
|
static function shadowMapName(light: LightObject, transparent: Bool): String {
|
|
switch (light.data.raw.type) {
|
|
case "sun":
|
|
return transparent ? "shadowMapTransparent" : "shadowMap";
|
|
case "point":
|
|
return transparent ? "shadowMapPointTransparent[" + pointIndex + "]" : "shadowMapPoint[" + pointIndex + "]";
|
|
default:
|
|
return transparent ? "shadowMapSpotTransparent[" + spotIndex + "]" : "shadowMapSpot[" + spotIndex + "]";
|
|
}
|
|
}
|
|
|
|
static function getShadowMap(l: iron.object.LightObject, transparent: Bool): String {
|
|
var target = shadowMapName(l, transparent);
|
|
var rt = path.renderTargets.get(target);
|
|
// Create shadowmap on the fly
|
|
if (rt == null) {
|
|
if (path.light.data.raw.shadowmap_cube) {
|
|
// Cubemap size
|
|
var size = path.light.data.raw.shadowmap_size;
|
|
var t = new RenderTargetRaw();
|
|
t.name = target;
|
|
t.width = size;
|
|
t.height = size;
|
|
t.format = transparent ? "RGBA64" : "DEPTH16";
|
|
t.is_cubemap = true;
|
|
rt = path.createRenderTarget(t);
|
|
}
|
|
else { // Non-cube sm
|
|
var sizew = path.light.data.raw.shadowmap_size;
|
|
var sizeh = sizew;
|
|
#if lnx_csm // Cascades - atlas on x axis
|
|
if (l.data.raw.type == "sun") {
|
|
sizew = sizew * iron.object.LightObject.cascadeCount;
|
|
}
|
|
#end
|
|
var t = new RenderTargetRaw();
|
|
t.name = target;
|
|
t.width = sizew;
|
|
t.height = sizeh;
|
|
t.format = transparent ? "RGBA64" : "DEPTH16";
|
|
rt = path.createRenderTarget(t);
|
|
}
|
|
}
|
|
return target;
|
|
}
|
|
|
|
public static function drawShadowMap() {
|
|
#if (rp_shadowmap)
|
|
|
|
#if rp_probes
|
|
// Share shadow map with probe
|
|
if (lastFrame == RenderPath.active.frame) return;
|
|
lastFrame = RenderPath.active.frame;
|
|
#end
|
|
|
|
pointIndex = 0;
|
|
spotIndex = 0;
|
|
for (l in iron.Scene.active.lights) {
|
|
if (!l.visible) continue;
|
|
|
|
path.light = l;
|
|
var shadowmap = Inc.getShadowMap(l, false);
|
|
var faces = l.data.raw.shadowmap_cube ? 6 : 1;
|
|
for (i in 0...faces) {
|
|
if (faces > 1) path.currentFace = i;
|
|
path.setTarget(shadowmap);
|
|
path.clearTarget(null, 1.0);
|
|
if (l.data.raw.cast_shadow) {
|
|
path.drawMeshes("shadowmap");
|
|
}
|
|
}
|
|
path.currentFace = -1;
|
|
|
|
if (l.data.raw.type == "point") pointIndex++;
|
|
else if (l.data.raw.type == "spot" || l.data.raw.type == "area") spotIndex++;
|
|
}
|
|
|
|
#if rp_shadowmap_transparent
|
|
pointIndex = 0;
|
|
spotIndex = 0;
|
|
for (l in iron.Scene.active.lights) {
|
|
if (!l.visible) continue;
|
|
|
|
path.light = l;
|
|
var shadowmap_transparent = Inc.getShadowMap(l, true);
|
|
var faces = l.data.raw.shadowmap_cube ? 6 : 1;
|
|
for (i in 0...faces) {
|
|
if (faces > 1) path.currentFace = i;
|
|
path.setTarget(shadowmap_transparent);
|
|
path.clearTarget(0xffffff, 0.0);
|
|
if (l.data.raw.cast_shadow) {
|
|
path.drawMeshes("shadowmap_transparent");
|
|
}
|
|
}
|
|
path.currentFace = -1;
|
|
|
|
if (l.data.raw.type == "point") pointIndex++;
|
|
else if (l.data.raw.type == "spot" || l.data.raw.type == "area") spotIndex++;
|
|
}
|
|
#end
|
|
#end // rp_shadowmap
|
|
}
|
|
#end
|
|
|
|
public static function applyConfig() {
|
|
#if lnx_config
|
|
var config = leenkx.data.Config.raw;
|
|
// Resize shadow map
|
|
var l = path.light;
|
|
if (l.data.raw.type == "sun" && l.data.raw.shadowmap_size != config.rp_shadowmap_cascade) {
|
|
l.data.raw.shadowmap_size = config.rp_shadowmap_cascade;
|
|
var rt = path.renderTargets.get("shadowMap");
|
|
if (rt != null) {
|
|
rt.unload();
|
|
path.renderTargets.remove("shadowMap");
|
|
}
|
|
}
|
|
else if (l.data.raw.shadowmap_size != config.rp_shadowmap_cube) {
|
|
l.data.raw.shadowmap_size = config.rp_shadowmap_cube;
|
|
var rt = path.renderTargets.get("shadowMapCube");
|
|
if (rt != null) {
|
|
rt.unload();
|
|
path.renderTargets.remove("shadowMapCube");
|
|
}
|
|
}
|
|
if (superSample != config.rp_supersample) {
|
|
superSample = config.rp_supersample;
|
|
for (rt in path.renderTargets) {
|
|
if (rt.raw.width == 0 && rt.raw.scale != null) {
|
|
rt.raw.scale = getSuperSampling();
|
|
}
|
|
}
|
|
path.resize();
|
|
}
|
|
// Init voxels
|
|
#if (rp_voxels != 'Off')
|
|
if (!voxelsCreated) initGI();
|
|
#end
|
|
#end // lnx_config
|
|
}
|
|
|
|
#if (rp_translucency)
|
|
public static function initTranslucency() {
|
|
path.createDepthBuffer("main", "DEPTH24");
|
|
|
|
var t = new RenderTargetRaw();
|
|
t.name = "accum";
|
|
t.width = 0;
|
|
t.height = 0;
|
|
t.displayp = getDisplayp();
|
|
t.format = "RGBA64";
|
|
t.scale = getSuperSampling();
|
|
t.depth_buffer = "main";
|
|
path.createRenderTarget(t);
|
|
|
|
var t = new RenderTargetRaw();
|
|
t.name = "revealage";
|
|
t.width = 0;
|
|
t.height = 0;
|
|
t.displayp = getDisplayp();
|
|
t.format = "R16";
|
|
t.scale = getSuperSampling();
|
|
t.depth_buffer = "main";
|
|
path.createRenderTarget(t);
|
|
|
|
path.loadShader("shader_datas/translucent_resolve/translucent_resolve");
|
|
}
|
|
|
|
public static function drawTranslucency(target: String) {
|
|
path.setTarget("accum");
|
|
path.clearTarget(0xff000000);
|
|
path.setTarget("revealage");
|
|
path.clearTarget(0xffffffff);
|
|
path.setTarget("accum", ["revealage"]);
|
|
|
|
#if rp_shadowmap
|
|
{
|
|
#if lnx_shadowmap_atlas
|
|
bindShadowMapAtlas();
|
|
#else
|
|
bindShadowMap();
|
|
#end
|
|
}
|
|
#end
|
|
|
|
#if (rp_voxels != "Off")
|
|
path.bindTarget("voxelsOut", "voxels");
|
|
#if (rp_voxels == "Voxel GI" || lnx_voxelgi_shadows)
|
|
path.bindTarget("voxelsSDF", "voxelsSDF");
|
|
#end
|
|
#end
|
|
|
|
#if rp_ssrs
|
|
path.bindTarget("_main", "gbufferD");
|
|
#end
|
|
|
|
path.drawMeshes("translucent");
|
|
|
|
#if rp_render_to_texture
|
|
{
|
|
path.setTarget(target);
|
|
}
|
|
#else
|
|
{
|
|
path.setTarget("");
|
|
}
|
|
#end
|
|
|
|
path.bindTarget("accum", "gbuffer0");
|
|
path.bindTarget("revealage", "gbuffer1");
|
|
path.drawShader("shader_datas/translucent_resolve/translucent_resolve");
|
|
}
|
|
#end
|
|
|
|
#if rp_bloom
|
|
public static inline function drawBloom(srcRTName: String, downsampler: Downsampler, upsampler: Upsampler) {
|
|
if (leenkx.data.Config.raw.rp_bloom != false) {
|
|
// This can result in little jumps in the perceived bloom radius
|
|
// when resizing the window because numMips might change, but
|
|
// all implementations using this approach have the same problem
|
|
// (including Eevee)
|
|
final minDim = Math.min(path.currentW, path.currentH);
|
|
final logMinDim = Math.max(1.0, Helper.log2(minDim) + (Main.bloomRadius - 8.0));
|
|
final numMips = Std.int(logMinDim);
|
|
|
|
// Sample scale for upsampling, 0.5 to use half-texel steps,
|
|
// use fraction of logMinDim to make the visual jumps
|
|
// described above less visible
|
|
Postprocess.bloom_uniforms[3] = 0.5 + logMinDim - numMips;
|
|
|
|
downsampler.dispatch(srcRTName, numMips);
|
|
upsampler.dispatch(srcRTName, numMips);
|
|
}
|
|
}
|
|
#end
|
|
|
|
#if (rp_voxels != 'Off')
|
|
public static function initGI(tname = "voxels") {
|
|
var t = new RenderTargetRaw();
|
|
t.name = tname;
|
|
|
|
#if lnx_config
|
|
var config = leenkx.data.Config.raw;
|
|
if (config.rp_voxels != true || voxelsCreated) return;
|
|
voxelsCreated = true;
|
|
#end
|
|
|
|
var res = iron.RenderPath.getVoxelRes();
|
|
var resZ = iron.RenderPath.getVoxelResZ();
|
|
|
|
if (t.name == "voxels_diffuse" || t.name == "voxels_specular" || t.name == "voxels_ao") {
|
|
t.width = 0;
|
|
t.height = 0;
|
|
t.displayp = getDisplayp();
|
|
t.format = "RGBA32";
|
|
}
|
|
else {
|
|
if (t.name == "voxelsSDF" || t.name == "voxelsSDFtmp") {
|
|
t.format = "R16";
|
|
t.width = res;
|
|
t.height = res * Main.voxelgiClipmapCount;
|
|
t.depth = res;
|
|
}
|
|
else {
|
|
#if (rp_voxels == "Voxel AO")
|
|
{
|
|
if (t.name == "voxelsOut" || t.name == "voxelsOutB") {
|
|
t.format = "R16";
|
|
t.width = res * (6 + 16);
|
|
t.height = res * Main.voxelgiClipmapCount;
|
|
t.depth = res;
|
|
}
|
|
else {
|
|
t.format = "R32UI";
|
|
t.width = res * 6;
|
|
t.height = res;
|
|
t.depth = res * 2;
|
|
}
|
|
}
|
|
#else
|
|
{
|
|
if (t.name == "voxelsOut" || t.name == "voxelsOutB") {
|
|
t.format = "RGBA64";
|
|
t.width = res * (6 + 16);
|
|
t.height = res * Main.voxelgiClipmapCount;
|
|
t.depth = res;
|
|
}
|
|
else {
|
|
t.format = "R32UI";
|
|
t.width = res * 6;
|
|
t.height = res;
|
|
t.depth = res * 16;
|
|
}
|
|
}
|
|
#end
|
|
}
|
|
}
|
|
t.is_image = true;
|
|
t.mipmaps = true;
|
|
path.createRenderTarget(t);
|
|
}
|
|
#end
|
|
|
|
public static inline function getCubeSize(): Int {
|
|
#if (rp_shadowmap_cube == 256)
|
|
return 256;
|
|
#elseif (rp_shadowmap_cube == 512)
|
|
return 512;
|
|
#elseif (rp_shadowmap_cube == 1024)
|
|
return 1024;
|
|
#elseif (rp_shadowmap_cube == 2048)
|
|
return 2048;
|
|
#elseif (rp_shadowmap_cube == 4096)
|
|
return 4096;
|
|
#else
|
|
return 0;
|
|
#end
|
|
}
|
|
|
|
public static inline function getCascadeSize(): Int {
|
|
#if (rp_shadowmap_cascade == 256)
|
|
return 256;
|
|
#elseif (rp_shadowmap_cascade == 512)
|
|
return 512;
|
|
#elseif (rp_shadowmap_cascade == 1024)
|
|
return 1024;
|
|
#elseif (rp_shadowmap_cascade == 2048)
|
|
return 2048;
|
|
#elseif (rp_shadowmap_cascade == 4096)
|
|
return 4096;
|
|
#elseif (rp_shadowmap_cascade == 8192)
|
|
return 8192;
|
|
#elseif (rp_shadowmap_cascade == 16384)
|
|
return 16384;
|
|
#else
|
|
return 0;
|
|
#end
|
|
}
|
|
|
|
public static inline function getSuperSampling(): Float {
|
|
return superSample;
|
|
}
|
|
|
|
public static inline function getHdrFormat(): String {
|
|
#if rp_hdr
|
|
return "RGBA64";
|
|
#else
|
|
return "RGBA32";
|
|
#end
|
|
}
|
|
|
|
public static inline function getDisplayp(): Null<Int> {
|
|
#if rp_resolution_filter // Custom resolution set
|
|
return Main.resolutionSize;
|
|
#else
|
|
return null;
|
|
#end
|
|
}
|
|
|
|
#if lnx_debug
|
|
public static var shadowsLogicTime = 0.0;
|
|
public static var shadowsRenderTime = 0.0;
|
|
static var startShadowsLogicTime = 0.0;
|
|
static var startShadowsRenderTime = 0.0;
|
|
static var callBackSetup = false;
|
|
static function setupEndFrameCallback() {
|
|
if (!callBackSetup) {
|
|
callBackSetup = true;
|
|
iron.App.endFrameCallbacks.push(endFrame);
|
|
}
|
|
}
|
|
static function beginShadowsLogicProfile() { setupEndFrameCallback(); startShadowsLogicTime = kha.Scheduler.realTime(); }
|
|
static function beginShadowsRenderProfile() { startShadowsRenderTime = kha.Scheduler.realTime(); }
|
|
static function endShadowsLogicProfile() { shadowsLogicTime += kha.Scheduler.realTime() - startShadowsLogicTime - shadowsRenderTime; }
|
|
static function endShadowsRenderProfile() { shadowsRenderTime += kha.Scheduler.realTime() - startShadowsRenderTime; }
|
|
public static function endFrame() { shadowsLogicTime = 0; shadowsRenderTime = 0; }
|
|
#end
|
|
|
|
#if (rp_voxels != "Off")
|
|
public static function computeVoxelsBegin() {
|
|
if (voxel_sh0 == null)
|
|
{
|
|
voxel_sh0 = path.getComputeShader("voxel_offsetprev");
|
|
|
|
voxel_ta0 = voxel_sh0.getTextureUnit("voxelsB");
|
|
voxel_tb0 = voxel_sh0.getTextureUnit("voxelsOut");
|
|
|
|
voxel_ca0 = voxel_sh0.getConstantLocation("clipmaps");
|
|
voxel_cb0 = voxel_sh0.getConstantLocation("clipmapLevel");
|
|
}
|
|
if (voxel_sh1 == null)
|
|
{
|
|
voxel_sh1 = path.getComputeShader("voxel_temporal");
|
|
voxel_ta1 = voxel_sh1.getTextureUnit("voxels");
|
|
voxel_tb1 = voxel_sh1.getTextureUnit("voxelsB");
|
|
voxel_tc1 = voxel_sh1.getTextureUnit("voxelsOut");
|
|
|
|
voxel_ca1 = voxel_sh1.getConstantLocation("clipmaps");
|
|
voxel_cb1 = voxel_sh1.getConstantLocation("clipmapLevel");
|
|
voxel_cc1 = voxel_sh1.getConstantLocation("envmapStrength");
|
|
|
|
#if (rp_voxels == "Voxel GI")
|
|
voxel_td1 = voxel_sh1.getTextureUnit("voxelsSampler");
|
|
voxel_te1 = voxel_sh1.getTextureUnit("SDF");
|
|
voxel_cc1 = voxel_sh1.getConstantLocation("envmapStrength");
|
|
#else
|
|
#if lnx_voxelgi_shadows
|
|
voxel_te1 = voxel_sh1.getTextureUnit("SDF");
|
|
#end
|
|
#end
|
|
}
|
|
#if (lnx_voxelgi_shadows || rp_voxels == "Voxel GI")
|
|
if (voxel_sh2 == null)
|
|
{
|
|
voxel_sh2 = path.getComputeShader("voxel_sdf_jumpflood");
|
|
voxel_ta2 = voxel_sh2.getTextureUnit("voxelsSDF");
|
|
voxel_tb2 = voxel_sh2.getTextureUnit("voxelsSDFtmp");
|
|
|
|
voxel_ca2 = voxel_sh2.getConstantLocation("clipmaps");
|
|
voxel_cb2 = voxel_sh2.getConstantLocation("clipmapLevel");
|
|
voxel_cc2 = voxel_sh2.getConstantLocation("jump_size");
|
|
}
|
|
#end
|
|
if (voxel_sh3 == null)
|
|
{
|
|
#if (rp_voxels == "Voxel AO")
|
|
voxel_sh3 = path.getComputeShader("voxel_resolve_ao");
|
|
#else
|
|
voxel_sh3 = path.getComputeShader("voxel_resolve_diffuse");
|
|
#end
|
|
voxel_ta3 = voxel_sh3.getTextureUnit("voxels");
|
|
voxel_tb3 = voxel_sh3.getTextureUnit("gbufferD");
|
|
voxel_tc3 = voxel_sh3.getTextureUnit("gbuffer0");
|
|
#if (rp_voxels == "Voxel AO")
|
|
voxel_td3 = voxel_sh3.getTextureUnit("voxels_ao");
|
|
#else
|
|
voxel_td3 = voxel_sh3.getTextureUnit("voxels_diffuse");
|
|
#end
|
|
voxel_te3 = voxel_sh3.getTextureUnit("gbuffer1");
|
|
voxel_tf3 = voxel_sh3.getTextureUnit("gbuffer2");
|
|
#if lnx_brdf
|
|
voxel_tg3 = voxel_sh3.getTextureUnit("senvmapBrdf");
|
|
#end
|
|
#if lnx_radiance
|
|
voxel_th3 = voxel_sh3.getTextureUnit("senvmapRadiance");
|
|
#end
|
|
voxel_ca3 = voxel_sh3.getConstantLocation("clipmaps");
|
|
voxel_cb3 = voxel_sh3.getConstantLocation("InvVP");
|
|
voxel_cc3 = voxel_sh3.getConstantLocation("eye");
|
|
voxel_cd3 = voxel_sh3.getConstantLocation("postprocess_resolution");
|
|
voxel_ce3 = voxel_sh3.getConstantLocation("envmapStrength");
|
|
#if lnx_irradiance
|
|
voxel_cf3 = voxel_sh3.getConstantLocation("shirr");
|
|
#end
|
|
#if lnx_radiance
|
|
voxel_cg3 = voxel_sh3.getConstantLocation("envmapNumMipmaps");
|
|
#end
|
|
#if lnx_envcol
|
|
voxel_ch3 = voxel_sh3.getConstantLocation("backgroundCol");
|
|
#end
|
|
}
|
|
#if (rp_voxels == "Voxel GI")
|
|
if (voxel_sh4 == null)
|
|
{
|
|
voxel_sh4 = path.getComputeShader("voxel_resolve_specular");
|
|
voxel_ta4 = voxel_sh4.getTextureUnit("voxels");
|
|
voxel_tb4 = voxel_sh4.getTextureUnit("gbufferD");
|
|
voxel_tc4 = voxel_sh4.getTextureUnit("gbuffer0");
|
|
voxel_td4 = voxel_sh4.getTextureUnit("voxelsSDF");
|
|
voxel_te4 = voxel_sh4.getTextureUnit("voxels_specular");
|
|
voxel_tf4 = voxel_sh4.getTextureUnit("sveloc");
|
|
voxel_ca4 = voxel_sh4.getConstantLocation("clipmaps");
|
|
voxel_cb4 = voxel_sh4.getConstantLocation("InvVP");
|
|
voxel_cc4 = voxel_sh4.getConstantLocation("eye");
|
|
voxel_cd4 = voxel_sh4.getConstantLocation("postprocess_resolution");
|
|
}
|
|
#end
|
|
}
|
|
|
|
public static function computeVoxelsOffsetPrev() {
|
|
var rts = path.renderTargets;
|
|
var res = iron.RenderPath.getVoxelRes();
|
|
var clipmaps = iron.RenderPath.clipmaps;
|
|
var clipmap = clipmaps[iron.RenderPath.clipmapLevel];
|
|
|
|
kha.compute.Compute.setShader(voxel_sh0);
|
|
|
|
kha.compute.Compute.setTexture(voxel_ta0, rts.get("voxelsOut").image, kha.compute.Access.Read);
|
|
kha.compute.Compute.setTexture(voxel_tb0, rts.get("voxelsOutB").image, kha.compute.Access.Write);
|
|
|
|
var fa:Float32Array = new Float32Array(Main.voxelgiClipmapCount * 10);
|
|
for (i in 0...Main.voxelgiClipmapCount) {
|
|
fa[i * 10] = clipmaps[i].voxelSize;
|
|
fa[i * 10 + 1] = clipmaps[i].extents.x;
|
|
fa[i * 10 + 2] = clipmaps[i].extents.y;
|
|
fa[i * 10 + 3] = clipmaps[i].extents.z;
|
|
fa[i * 10 + 4] = clipmaps[i].center.x;
|
|
fa[i * 10 + 5] = clipmaps[i].center.y;
|
|
fa[i * 10 + 6] = clipmaps[i].center.z;
|
|
fa[i * 10 + 7] = clipmaps[i].offset_prev.x;
|
|
fa[i * 10 + 8] = clipmaps[i].offset_prev.y;
|
|
fa[i * 10 + 9] = clipmaps[i].offset_prev.z;
|
|
}
|
|
|
|
kha.compute.Compute.setFloats(voxel_ca0, fa);
|
|
|
|
kha.compute.Compute.setInt(voxel_cb0, iron.RenderPath.clipmapLevel);
|
|
|
|
kha.compute.Compute.compute(Std.int(res / 8), Std.int(res / 8), Std.int(res / 8));
|
|
}
|
|
|
|
public static function computeVoxelsTemporal() {
|
|
var rts = path.renderTargets;
|
|
var res = iron.RenderPath.getVoxelRes();
|
|
var camera = iron.Scene.active.camera;
|
|
var clipmaps = iron.RenderPath.clipmaps;
|
|
var clipmap = clipmaps[iron.RenderPath.clipmapLevel];
|
|
|
|
kha.compute.Compute.setShader(voxel_sh1);
|
|
|
|
kha.compute.Compute.setTexture(voxel_ta1, rts.get("voxels").image, kha.compute.Access.Read);
|
|
kha.compute.Compute.setTexture(voxel_tb1, rts.get("voxelsOutB").image, kha.compute.Access.Read);
|
|
kha.compute.Compute.setTexture(voxel_tc1, rts.get("voxelsOut").image, kha.compute.Access.Write);
|
|
#if (rp_voxels == "Voxel GI")
|
|
kha.compute.Compute.setSampledTexture(voxel_td1, rts.get("voxelsOutB").image);
|
|
kha.compute.Compute.setTexture(voxel_te1, rts.get("voxelsSDF").image, kha.compute.Access.Write);
|
|
kha.compute.Compute.setFloat(voxel_cc1, iron.Scene.active.world == null ? 0.0 : iron.Scene.active.world.probe.raw.strength);
|
|
#else
|
|
#if lnx_voxelgi_shadows
|
|
kha.compute.Compute.setTexture(voxel_te1, rts.get("voxelsSDF").image, kha.compute.Access.Write);
|
|
#end
|
|
#end
|
|
|
|
var fa:Float32Array = new Float32Array(Main.voxelgiClipmapCount * 10);
|
|
for (i in 0...Main.voxelgiClipmapCount) {
|
|
fa[i * 10] = clipmaps[i].voxelSize;
|
|
fa[i * 10 + 1] = clipmaps[i].extents.x;
|
|
fa[i * 10 + 2] = clipmaps[i].extents.y;
|
|
fa[i * 10 + 3] = clipmaps[i].extents.z;
|
|
fa[i * 10 + 4] = clipmaps[i].center.x;
|
|
fa[i * 10 + 5] = clipmaps[i].center.y;
|
|
fa[i * 10 + 6] = clipmaps[i].center.z;
|
|
fa[i * 10 + 7] = clipmaps[i].offset_prev.x;
|
|
fa[i * 10 + 8] = clipmaps[i].offset_prev.y;
|
|
fa[i * 10 + 9] = clipmaps[i].offset_prev.z;
|
|
}
|
|
|
|
kha.compute.Compute.setFloats(voxel_ca1, fa);
|
|
|
|
kha.compute.Compute.setInt(voxel_cb1, iron.RenderPath.clipmapLevel);
|
|
|
|
kha.compute.Compute.setFloat(voxel_cc1, iron.Scene.active.world == null ? 0.0 : iron.Scene.active.world.probe.raw.strength);
|
|
|
|
kha.compute.Compute.compute(Std.int(res / 8), Std.int(res / 8), Std.int(res / 8));
|
|
}
|
|
|
|
#if (lnx_voxelgi_shadows || (rp_voxels == "Voxel GI"))
|
|
public static function computeVoxelsSDF() {
|
|
var rts = path.renderTargets;
|
|
var res = iron.RenderPath.getVoxelRes();
|
|
var clipmaps = iron.RenderPath.clipmaps;
|
|
var clipmap = clipmaps[iron.RenderPath.clipmapLevel];
|
|
|
|
var read_sdf = "voxelsSDF";
|
|
var write_sdf = "voxelsSDFtmp";
|
|
|
|
var passcount = Std.int(Math.ceil(Math.log(res) / Math.log(2.0)));
|
|
|
|
for (i in 0...passcount) {
|
|
kha.compute.Compute.setShader(voxel_sh2);
|
|
|
|
kha.compute.Compute.setTexture(voxel_ta2, rts.get(read_sdf).image, kha.compute.Access.Read);
|
|
kha.compute.Compute.setTexture(voxel_tb2, rts.get(write_sdf).image, kha.compute.Access.Write);
|
|
|
|
var fa:Float32Array = new Float32Array(Main.voxelgiClipmapCount * 10);
|
|
for (i in 0...Main.voxelgiClipmapCount) {
|
|
fa[i * 10] = clipmaps[i].voxelSize;
|
|
fa[i * 10 + 1] = clipmaps[i].extents.x;
|
|
fa[i * 10 + 2] = clipmaps[i].extents.y;
|
|
fa[i * 10 + 3] = clipmaps[i].extents.z;
|
|
fa[i * 10 + 4] = clipmaps[i].center.x;
|
|
fa[i * 10 + 5] = clipmaps[i].center.y;
|
|
fa[i * 10 + 6] = clipmaps[i].center.z;
|
|
fa[i * 10 + 7] = clipmaps[i].offset_prev.x;
|
|
fa[i * 10 + 8] = clipmaps[i].offset_prev.y;
|
|
fa[i * 10 + 9] = clipmaps[i].offset_prev.z;
|
|
}
|
|
|
|
kha.compute.Compute.setFloats(voxel_ca2, fa);
|
|
|
|
kha.compute.Compute.setInt(voxel_cb2, iron.RenderPath.clipmapLevel);
|
|
|
|
var jump_size = Math.pow(2.0, passcount - i - 1);
|
|
kha.compute.Compute.setFloat(voxel_cc2, jump_size);
|
|
|
|
kha.compute.Compute.compute(Std.int(res / 8), Std.int(res / 8), Std.int(res / 8));
|
|
|
|
if (i < passcount - 1)
|
|
{
|
|
read_sdf = read_sdf == "voxelsSDF" ? "voxelsSDFtmp" : "voxelsSDF";
|
|
write_sdf = write_sdf == "voxelsSDF" ? "voxelsSDFtmp" : "voxelsSDF";
|
|
}
|
|
}
|
|
}
|
|
#end
|
|
|
|
#if (rp_voxels == "Voxel AO")
|
|
public static function resolveAO() {
|
|
var rts = path.renderTargets;
|
|
var res = iron.RenderPath.getVoxelRes();
|
|
var camera = iron.Scene.active.camera;
|
|
var clipmaps = iron.RenderPath.clipmaps;
|
|
var clipmap = clipmaps[iron.RenderPath.clipmapLevel];
|
|
|
|
kha.compute.Compute.setShader(voxel_sh3);
|
|
|
|
kha.compute.Compute.setSampledTexture(voxel_ta3, rts.get("voxelsOut").image);
|
|
kha.compute.Compute.setSampledTexture(voxel_tb3, rts.get("half").image);
|
|
kha.compute.Compute.setSampledTexture(voxel_tc3, rts.get("gbuffer0").image);
|
|
kha.compute.Compute.setTexture(voxel_td3, rts.get("voxels_ao").image, kha.compute.Access.Write);
|
|
|
|
kha.compute.Compute.setSampledTexture(voxel_te3, rts.get("gbuffer1").image);
|
|
#if rp_gbuffer2
|
|
kha.compute.Compute.setSampledTexture(voxel_tf3, rts.get("gbuffer2").image);
|
|
#end
|
|
#if lnx_brdf
|
|
kha.compute.Compute.setSampledTexture(voxel_tg3, iron.Scene.active.embedded.get("brdf.png"));
|
|
#end
|
|
#if lnx_radiance
|
|
kha.compute.Compute.setSampledTexture(voxel_th3, iron.Scene.active.world.probe.radiance);
|
|
#end
|
|
|
|
var fa:Float32Array = new Float32Array(Main.voxelgiClipmapCount * 10);
|
|
for (i in 0...Main.voxelgiClipmapCount) {
|
|
fa[i * 10] = clipmaps[i].voxelSize;
|
|
fa[i * 10 + 1] = clipmaps[i].extents.x;
|
|
fa[i * 10 + 2] = clipmaps[i].extents.y;
|
|
fa[i * 10 + 3] = clipmaps[i].extents.z;
|
|
fa[i * 10 + 4] = clipmaps[i].center.x;
|
|
fa[i * 10 + 5] = clipmaps[i].center.y;
|
|
fa[i * 10 + 6] = clipmaps[i].center.z;
|
|
fa[i * 10 + 7] = clipmaps[i].offset_prev.x;
|
|
fa[i * 10 + 8] = clipmaps[i].offset_prev.y;
|
|
fa[i * 10 + 9] = clipmaps[i].offset_prev.z;
|
|
}
|
|
|
|
kha.compute.Compute.setFloats(voxel_ca3, fa);
|
|
|
|
#if lnx_centerworld
|
|
m.setFrom(vmat(camera.V));
|
|
#else
|
|
m.setFrom(camera.V);
|
|
#end
|
|
m.multmat(camera.P);
|
|
m.getInverse(m);
|
|
|
|
kha.compute.Compute.setMatrix(voxel_cb3, m.self);
|
|
|
|
kha.compute.Compute.setFloat3(voxel_cc3, camera.transform.worldx(), camera.transform.worldy(), camera.transform.worldz());
|
|
|
|
var width = iron.App.w();
|
|
var height = iron.App.h();
|
|
var dp = getDisplayp();
|
|
if (dp != null) { // 1080p/..
|
|
if (width > height) {
|
|
width = Std.int(width * (dp / height) * Inc.getSuperSampling());
|
|
height = Std.int(dp * Inc.getSuperSampling());
|
|
}
|
|
else {
|
|
height = Std.int(height * (dp / width) * Inc.getSuperSampling());
|
|
width = Std.int(dp * Inc.getSuperSampling());
|
|
}
|
|
}
|
|
kha.compute.Compute.setFloat2(voxel_cd3, width, height);
|
|
|
|
kha.compute.Compute.setFloat(voxel_ce3, iron.Scene.active.world == null ? 0.0 : iron.Scene.active.world.probe.raw.strength);
|
|
#if lnx_irradiance
|
|
var irradiance = iron.Scene.active.world == null ?
|
|
iron.data.WorldData.getEmptyIrradiance() :
|
|
iron.Scene.active.world.probe.irradiance;
|
|
kha.compute.Compute.setFloats(voxel_cf3, irradiance);
|
|
#end
|
|
#if lnx_radiance
|
|
kha.compute.Compute.setFloat(voxel_cg3, iron.Scene.active.world != null ? iron.Scene.active.world.probe.raw.radiance_mipmaps + 1 - 2 : 1);
|
|
#end
|
|
|
|
#if lnx_envcol
|
|
var x: kha.FastFloat = 0.0;
|
|
var y: kha.FastFloat = 0.0;
|
|
var z: kha.FastFloat = 0.0;
|
|
|
|
if (camera.data.raw.clear_color != null) {
|
|
x = camera.data.raw.clear_color[0];
|
|
y = camera.data.raw.clear_color[1];
|
|
z = camera.data.raw.clear_color[2];
|
|
}
|
|
|
|
kha.compute.Compute.setFloat3(voxel_ch3, x, y, z);
|
|
#end
|
|
|
|
kha.compute.Compute.compute(Std.int((width + 7) / 8), Std.int((height + 7) / 8), 1);
|
|
}
|
|
#else
|
|
public static function resolveDiffuse() {
|
|
var rts = path.renderTargets;
|
|
var res = iron.RenderPath.getVoxelRes();
|
|
var camera = iron.Scene.active.camera;
|
|
var clipmaps = iron.RenderPath.clipmaps;
|
|
var clipmap = clipmaps[iron.RenderPath.clipmapLevel];
|
|
|
|
kha.compute.Compute.setShader(voxel_sh3);
|
|
|
|
kha.compute.Compute.setSampledTexture(voxel_ta3, rts.get("voxelsOut").image);
|
|
kha.compute.Compute.setSampledTexture(voxel_tb3, rts.get("half").image);
|
|
kha.compute.Compute.setSampledTexture(voxel_tc3, rts.get("gbuffer0").image);
|
|
kha.compute.Compute.setTexture(voxel_td3, rts.get("voxels_diffuse").image, kha.compute.Access.Write);
|
|
kha.compute.Compute.setSampledTexture(voxel_te3, rts.get("gbuffer1").image);
|
|
#if rp_gbuffer2
|
|
kha.compute.Compute.setSampledTexture(voxel_tf3, rts.get("gbuffer2").image);
|
|
#end
|
|
#if lnx_brdf
|
|
kha.compute.Compute.setSampledTexture(voxel_tg3, iron.Scene.active.embedded.get("brdf.png"));
|
|
#end
|
|
#if lnx_radiance
|
|
kha.compute.Compute.setSampledTexture(voxel_th3, iron.Scene.active.world.probe.radiance);
|
|
#end
|
|
|
|
var fa:Float32Array = new Float32Array(Main.voxelgiClipmapCount * 10);
|
|
for (i in 0...Main.voxelgiClipmapCount) {
|
|
fa[i * 10] = clipmaps[i].voxelSize;
|
|
fa[i * 10 + 1] = clipmaps[i].extents.x;
|
|
fa[i * 10 + 2] = clipmaps[i].extents.y;
|
|
fa[i * 10 + 3] = clipmaps[i].extents.z;
|
|
fa[i * 10 + 4] = clipmaps[i].center.x;
|
|
fa[i * 10 + 5] = clipmaps[i].center.y;
|
|
fa[i * 10 + 6] = clipmaps[i].center.z;
|
|
fa[i * 10 + 7] = clipmaps[i].offset_prev.x;
|
|
fa[i * 10 + 8] = clipmaps[i].offset_prev.y;
|
|
fa[i * 10 + 9] = clipmaps[i].offset_prev.z;
|
|
}
|
|
|
|
kha.compute.Compute.setFloats(voxel_ca3, fa);
|
|
|
|
#if lnx_centerworld
|
|
m.setFrom(vmat(camera.V));
|
|
#else
|
|
m.setFrom(camera.V);
|
|
#end
|
|
m.multmat(camera.P);
|
|
m.getInverse(m);
|
|
|
|
kha.compute.Compute.setMatrix(voxel_cb3, m.self);
|
|
|
|
kha.compute.Compute.setFloat3(voxel_cc3, camera.transform.worldx(), camera.transform.worldy(), camera.transform.worldz());
|
|
|
|
var width = iron.App.w();
|
|
var height = iron.App.h();
|
|
var dp = getDisplayp();
|
|
if (dp != null) { // 1080p/..
|
|
if (width > height) {
|
|
width = Std.int(width * (dp / height) * Inc.getSuperSampling());
|
|
height = Std.int(dp * Inc.getSuperSampling());
|
|
}
|
|
else {
|
|
height = Std.int(height * (dp / width) * Inc.getSuperSampling());
|
|
width = Std.int(dp * Inc.getSuperSampling());
|
|
}
|
|
}
|
|
kha.compute.Compute.setFloat2(voxel_cd3, width, height);
|
|
|
|
kha.compute.Compute.setFloat(voxel_ce3, iron.Scene.active.world == null ? 0.0 : iron.Scene.active.world.probe.raw.strength);
|
|
#if lnx_irradiance
|
|
var irradiance = iron.Scene.active.world == null ?
|
|
iron.data.WorldData.getEmptyIrradiance() :
|
|
iron.Scene.active.world.probe.irradiance;
|
|
kha.compute.Compute.setFloats(voxel_cf3, irradiance);
|
|
#end
|
|
#if lnx_radiance
|
|
kha.compute.Compute.setFloat(voxel_cg3, iron.Scene.active.world != null ? iron.Scene.active.world.probe.raw.radiance_mipmaps + 1 - 2 : 1);
|
|
#end
|
|
|
|
#if lnx_envcol
|
|
var x: kha.FastFloat = 0.0;
|
|
var y: kha.FastFloat = 0.0;
|
|
var z: kha.FastFloat = 0.0;
|
|
|
|
if (camera.data.raw.clear_color != null) {
|
|
x = camera.data.raw.clear_color[0];
|
|
y = camera.data.raw.clear_color[1];
|
|
z = camera.data.raw.clear_color[2];
|
|
}
|
|
|
|
kha.compute.Compute.setFloat3(voxel_ch3, x, y, z);
|
|
#end
|
|
|
|
kha.compute.Compute.compute(Std.int((width + 7) / 8), Std.int((height + 7) / 8), 1);
|
|
}
|
|
|
|
public static function resolveSpecular() {
|
|
var rts = path.renderTargets;
|
|
var res = iron.RenderPath.getVoxelRes();
|
|
var camera = iron.Scene.active.camera;
|
|
var clipmaps = iron.RenderPath.clipmaps;
|
|
var clipmap = clipmaps[iron.RenderPath.clipmapLevel];
|
|
|
|
kha.compute.Compute.setShader(voxel_sh4);
|
|
|
|
kha.compute.Compute.setSampledTexture(voxel_ta4, rts.get("voxelsOut").image);
|
|
kha.compute.Compute.setSampledTexture(voxel_tb4, rts.get("half").image);
|
|
kha.compute.Compute.setSampledTexture(voxel_tc4, rts.get("gbuffer0").image);
|
|
kha.compute.Compute.setSampledTexture(voxel_td4, rts.get("voxelsSDF").image);
|
|
kha.compute.Compute.setTexture(voxel_te4, rts.get("voxels_specular").image, kha.compute.Access.Write);
|
|
#if rp_gbuffer2
|
|
kha.compute.Compute.setSampledTexture(voxel_tf4, rts.get("gbuffer2").image);
|
|
#end
|
|
|
|
var fa:Float32Array = new Float32Array(Main.voxelgiClipmapCount * 10);
|
|
for (i in 0...Main.voxelgiClipmapCount) {
|
|
fa[i * 10] = clipmaps[i].voxelSize;
|
|
fa[i * 10 + 1] = clipmaps[i].extents.x;
|
|
fa[i * 10 + 2] = clipmaps[i].extents.y;
|
|
fa[i * 10 + 3] = clipmaps[i].extents.z;
|
|
fa[i * 10 + 4] = clipmaps[i].center.x;
|
|
fa[i * 10 + 5] = clipmaps[i].center.y;
|
|
fa[i * 10 + 6] = clipmaps[i].center.z;
|
|
fa[i * 10 + 7] = clipmaps[i].offset_prev.x;
|
|
fa[i * 10 + 8] = clipmaps[i].offset_prev.y;
|
|
fa[i * 10 + 9] = clipmaps[i].offset_prev.z;
|
|
}
|
|
|
|
kha.compute.Compute.setFloats(voxel_ca4, fa);
|
|
|
|
#if lnx_centerworld
|
|
m.setFrom(vmat(camera.V));
|
|
#else
|
|
m.setFrom(camera.V);
|
|
#end
|
|
m.multmat(camera.P);
|
|
m.getInverse(m);
|
|
|
|
kha.compute.Compute.setMatrix(voxel_cb4, m.self);
|
|
|
|
kha.compute.Compute.setFloat3(voxel_cc4, camera.transform.worldx(), camera.transform.worldy(), camera.transform.worldz());
|
|
|
|
var width = iron.App.w();
|
|
var height = iron.App.h();
|
|
var dp = getDisplayp();
|
|
if (dp != null) { // 1080p/..
|
|
if (width > height) {
|
|
width = Std.int(width * (dp / height) * Inc.getSuperSampling());
|
|
height = Std.int(dp * Inc.getSuperSampling());
|
|
}
|
|
else {
|
|
height = Std.int(height * (dp / width) * Inc.getSuperSampling());
|
|
width = Std.int(dp * Inc.getSuperSampling());
|
|
}
|
|
}
|
|
kha.compute.Compute.setFloat2(voxel_cd4, width, height);
|
|
|
|
kha.compute.Compute.compute(Std.int((width + 7) / 8), Std.int((height + 7) / 8), 1);
|
|
}
|
|
#end // GI
|
|
#end // Voxels
|
|
}
|
|
|
|
#if lnx_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 lnx_shadowmap_atlas_lod
|
|
var tileSizes: Array<Int> = [];
|
|
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
|
|
public static var shadowMapAtlasesTransparent:Map<String, ShadowMapAtlas> = new Map(); // map a shadowmap atlas to their light type
|
|
|
|
#if lnx_debug
|
|
public var lightType: String;
|
|
public var rejectedLights: Array<LightObject> = [];
|
|
#end
|
|
|
|
function new(light: LightObject, transparent: Bool) {
|
|
|
|
var maxTileSize = shadowMapAtlasSize(light);
|
|
this.target = shadowMapAtlasName(light.data.raw.type, transparent);
|
|
this.sizew = this.sizeh = this.baseTileSizeConst = maxTileSize;
|
|
this.depth = getSubdivisions();
|
|
this.maxAtlasSizeConst = getMaxAtlasSize(light.data.raw.type);
|
|
|
|
#if lnx_shadowmap_atlas_lod
|
|
computeTileSizes(maxTileSize, depth);
|
|
#end
|
|
|
|
#if lnx_debug
|
|
#if lnx_shadowmap_atlas_single_map
|
|
this.lightType = "any";
|
|
#else
|
|
this.lightType = light.data.raw.type;
|
|
#end
|
|
#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, transparent: Bool) {
|
|
var atlasName = shadowMapAtlasName(light.data.raw.type, transparent);
|
|
var atlas = transparent ? shadowMapAtlasesTransparent.get(atlasName) : shadowMapAtlases.get(atlasName);
|
|
if (atlas == null) {
|
|
// create a new atlas
|
|
atlas = new ShadowMapAtlas(light, transparent);
|
|
if (transparent)
|
|
shadowMapAtlasesTransparent.set(atlasName, atlas);
|
|
else
|
|
shadowMapAtlases.set(atlasName, atlas);
|
|
}
|
|
|
|
// find a free tile for this light
|
|
var mainTile = ShadowMapTile.assignTiles(light, atlas, null);
|
|
if (mainTile == null) {
|
|
#if lnx_debug
|
|
if (!atlas.rejectedLights.contains(light))
|
|
atlas.rejectedLights.push(light);
|
|
#end
|
|
return;
|
|
}
|
|
|
|
atlas.activeTiles.push(mainTile);
|
|
// notify the tile on light remove
|
|
light.tileNotifyOnRemove = mainTile.notifyOnLightRemove;
|
|
// notify atlas when this tile is freed
|
|
mainTile.notifyOnFree = atlas.freeActiveTile;
|
|
// "lock" light to make sure it's not eligible to be added again
|
|
if (transparent)
|
|
light.lightInAtlasTransparent = true;
|
|
else
|
|
light.lightInAtlas = 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 lnx_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 lnx_shadowmap_atlas_lod
|
|
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){
|
|
this.tileSizes.push(Std.int(maxTileSize / Math.pow(2, i)));
|
|
this.tileSizeFactor.push(base);
|
|
base -= subdiv;
|
|
}
|
|
this.tileSizes.push(0);
|
|
this.tileSizeFactor.push(0.0);
|
|
}
|
|
#end
|
|
|
|
public inline function atlasLimitReached() {
|
|
// asume square atlas
|
|
return (currTileOffset + 1) * baseTileSizeConst > maxAtlasSizeConst;
|
|
}
|
|
|
|
public static inline function shadowMapAtlasName(type: String, transparent: Bool): String {
|
|
#if lnx_shadowmap_atlas_single_map
|
|
return "shadowMapAtlas";
|
|
#else
|
|
switch (type) {
|
|
case "point":
|
|
return transparent ? "shadowMapAtlasPointTransparent" : "shadowMapAtlasPoint";
|
|
case "sun":
|
|
return transparent ? "shadowMapAtlasSunTransparent" : "shadowMapAtlasSun";
|
|
default:
|
|
return transparent ? "shadowMapAtlasSpotTransparent" : "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 (!lnx_shadowmap_atlas_lod)
|
|
return 1;
|
|
#end
|
|
}
|
|
|
|
public static inline function getMaxAtlasSize(type: String): Int {
|
|
#if lnx_shadowmap_atlas_single_map
|
|
#if (rp_shadowmap_atlas_max_size == 512)
|
|
return 512;
|
|
#elseif (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;
|
|
#elseif (rp_shadowmap_atlas_max_size == 32768)
|
|
return 32768;
|
|
#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;
|
|
#elseif (rp_shadowmap_atlas_max_size_point == 32768)
|
|
return 32768;
|
|
#end
|
|
}
|
|
case "spot": {
|
|
#if (rp_shadowmap_atlas_max_size_spot == 512)
|
|
return 512;
|
|
#elseif (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;
|
|
#elseif (rp_shadowmap_atlas_max_size_spot == 32768)
|
|
return 32768;
|
|
#end
|
|
}
|
|
case "sun": {
|
|
#if (rp_shadowmap_atlas_max_size_sun == 512)
|
|
return 512;
|
|
#elseif (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;
|
|
#elseif (rp_shadowmap_atlas_max_size_sun == 32768)
|
|
return 32768;
|
|
#end
|
|
}
|
|
default: {
|
|
#if (rp_shadowmap_atlas_max_size == 512)
|
|
return 512;
|
|
#elseif (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;
|
|
#elseif (rp_shadowmap_atlas_max_size == 32768)
|
|
return 32768;
|
|
#end
|
|
}
|
|
}
|
|
#end
|
|
}
|
|
|
|
function freeActiveTile(tile: ShadowMapTile) {
|
|
activeTiles.remove(tile);
|
|
}
|
|
}
|
|
|
|
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 lnx_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 lnx_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> = [];
|
|
|
|
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 lnx_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 lnx_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 lnx_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 lnx_shadowmap_atlas_lod
|
|
tiles[lastTile].tiles = subDivTile(tiles[lastTile], size, size * i, size * j, depth - 1);
|
|
#end
|
|
lastTile++;
|
|
j++;
|
|
}
|
|
i--;
|
|
}
|
|
}
|
|
|
|
#if lnx_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;
|
|
}
|
|
}
|
|
|
|
public function notifyOnLightRemove() {
|
|
unlockLight = true;
|
|
freeTile();
|
|
}
|
|
|
|
inline function lockTile(light: LightObject): Void {
|
|
if (this.light != null)
|
|
return;
|
|
this.light = light;
|
|
#if lnx_shadowmap_atlas_lod
|
|
// update the count of used tiles for parents
|
|
this.forEachParentTile(function (pTile) {
|
|
pTile.activeSubTiles++;
|
|
});
|
|
#end
|
|
}
|
|
|
|
public var unlockLight: Bool = false;
|
|
public var notifyOnFree: ShadowMapTile -> Void;
|
|
|
|
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 lnx_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;
|
|
}
|
|
// notify atlas that this tile has been freed
|
|
if (notifyOnFree != null) {
|
|
notifyOnFree(this);
|
|
notifyOnFree = null;
|
|
}
|
|
}
|
|
|
|
public inline function forEachTileLinked(action: ShadowMapTile->Void): Void {
|
|
var linkedTile = this;
|
|
while (linkedTile != null) {
|
|
action(linkedTile);
|
|
linkedTile = linkedTile.linkedTile;
|
|
}
|
|
}
|
|
|
|
#if lnx_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
|