forked from LeenkxTeam/LNXSDK
Merge pull request 'main' (#135) from Onek8/LNXSDK:main into main
Reviewed-on: LeenkxTeam/LNXSDK#135
This commit is contained in:
17
leenkx/Shaders/add_pass/add_pass.json
Normal file
17
leenkx/Shaders/add_pass/add_pass.json
Normal file
@ -0,0 +1,17 @@
|
||||
{
|
||||
"contexts": [
|
||||
{
|
||||
"name": "add_pass",
|
||||
"depth_write": false,
|
||||
"compare_mode": "always",
|
||||
"cull_mode": "none",
|
||||
"blend_source": "source_alpha",
|
||||
"blend_destination": "inverse_source_alpha",
|
||||
"blend_operation": "add",
|
||||
"links": [],
|
||||
"texture_params": [],
|
||||
"vertex_shader": "../include/pass.vert.glsl",
|
||||
"fragment_shader": "../include/pass_copy.frag.glsl"
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -11,6 +11,7 @@
|
||||
#endif
|
||||
|
||||
uniform sampler2D tex;
|
||||
|
||||
#ifdef _CDepth
|
||||
uniform sampler2D gbufferD;
|
||||
#endif
|
||||
@ -67,6 +68,7 @@ uniform vec3 PPComp14;
|
||||
uniform vec4 PPComp15;
|
||||
uniform vec4 PPComp16;
|
||||
uniform vec4 PPComp18;
|
||||
uniform vec4 PPComp19;
|
||||
#endif
|
||||
|
||||
// #ifdef _CPos
|
||||
@ -230,6 +232,45 @@ vec3 lensflare(vec2 uv, vec2 pos) {
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef _CDistort
|
||||
float distortHash(vec2 p) {
|
||||
return fract(sin(dot(p, vec2(127.1, 311.7))) * 43758.5453123);
|
||||
}
|
||||
|
||||
float distortValueNoise(vec2 p) {
|
||||
vec2 i = floor(p);
|
||||
vec2 f = fract(p);
|
||||
vec2 u = f * f * (3.0 - 2.0 * f);
|
||||
|
||||
float a = distortHash(i);
|
||||
float b = distortHash(i + vec2(1.0, 0.0));
|
||||
float c = distortHash(i + vec2(0.0, 1.0));
|
||||
float d = distortHash(i + vec2(1.0, 1.0));
|
||||
|
||||
return mix(mix(a, b, u.x), mix(c, d, u.x), u.y);
|
||||
}
|
||||
|
||||
vec2 distortSmoothNoise(vec2 p) {
|
||||
return vec2(
|
||||
distortValueNoise(p),
|
||||
distortValueNoise(p + vec2(5.2, 1.3))
|
||||
);
|
||||
}
|
||||
|
||||
vec2 distortUV(vec2 uv, vec2 nUV, float t, float strength) {
|
||||
float intensity = 0.01 * strength;
|
||||
float scale = 4.0;
|
||||
float speed = 0.25;
|
||||
|
||||
nUV.x += t * speed;
|
||||
nUV.y += t * speed;
|
||||
vec2 noise = distortSmoothNoise(nUV * scale);
|
||||
|
||||
uv += (-1.0 + noise * 2.0) * intensity;
|
||||
return uv;
|
||||
}
|
||||
#endif
|
||||
|
||||
void main() {
|
||||
vec2 texCo = texCoord;
|
||||
#ifdef _DynRes
|
||||
@ -252,22 +293,25 @@ void main() {
|
||||
|
||||
#ifdef _CFishEye
|
||||
#ifdef _CPostprocess
|
||||
const float fishEyeStrength = -(PPComp2.y);
|
||||
float fishEyeStrength = PPComp2.y;
|
||||
#else
|
||||
const float fishEyeStrength = -0.01;
|
||||
float fishEyeStrength = compoFisheyeStrength;
|
||||
#endif
|
||||
const vec2 m = vec2(0.5, 0.5);
|
||||
vec2 d = texCo - m;
|
||||
float r = sqrt(dot(d, d));
|
||||
float power = (2.0 * PI / (2.0 * sqrt(dot(m, m)))) * fishEyeStrength;
|
||||
float bind;
|
||||
if (power > 0.0) { bind = sqrt(dot(m, m)); }
|
||||
else { bind = m.x; }
|
||||
if (power > 0.0) {
|
||||
texCo = m + normalize(d) * tan(r * power) * bind / tan(bind * power);
|
||||
}
|
||||
else {
|
||||
texCo = m + normalize(d) * atan(r * -power * 10.0) * bind / atan(-power * bind * 10.0);
|
||||
|
||||
if (abs(fishEyeStrength) > 0.0001) {
|
||||
const vec2 m = vec2(0.5, 0.5);
|
||||
vec2 d = texCo - m;
|
||||
float r = sqrt(dot(d, d));
|
||||
float power = - (2.0 * PI / (2.0 * sqrt(dot(m, m)))) * fishEyeStrength;
|
||||
float bind;
|
||||
if (power > 0.0) { bind = sqrt(dot(m, m)); }
|
||||
else { bind = m.x; }
|
||||
if (power > 0.0) {
|
||||
texCo = m + normalize(d) * tan(r * power) * bind / tan(bind * power);
|
||||
}
|
||||
else {
|
||||
texCo = m + normalize(d) * atan(r * -power * 10.0) * bind / atan(-power * bind * 10.0);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -277,9 +321,28 @@ void main() {
|
||||
#else
|
||||
float strengthDistort = compoDistortStrength;
|
||||
#endif
|
||||
float uX = time * strengthDistort;
|
||||
texCo.y = texCo.y + (sin(texCo.x*4.0+uX*2.0)*0.01);
|
||||
texCo.x = texCo.x + (cos(texCo.y*4.0+uX*2.0)*0.01);
|
||||
|
||||
vec2 nUV = texCo;
|
||||
|
||||
texCo = distortUV(texCo, nUV, time, strengthDistort);
|
||||
texCo = distortUV(texCo, vec2(nUV.x + 0.1, nUV.y + 0.1), time, strengthDistort);
|
||||
texCo = distortUV(texCo, vec2(nUV.x + 0.2, nUV.y + 0.2), time, strengthDistort);
|
||||
texCo = distortUV(texCo, vec2(nUV.x + 0.3, nUV.y + 0.3), time, strengthDistort);
|
||||
texCo = distortUV(texCo, vec2(nUV.x + 0.4, nUV.y + 0.4), time, strengthDistort);
|
||||
texCo = distortUV(texCo, vec2(nUV.x + 0.5, nUV.y + 0.5), time, strengthDistort);
|
||||
texCo = distortUV(texCo, vec2(nUV.x + 0.6, nUV.y + 0.6), time, strengthDistort);
|
||||
texCo = distortUV(texCo, vec2(nUV.x + 0.7, nUV.y + 0.7), time, strengthDistort);
|
||||
texCo = distortUV(texCo, vec2(nUV.x + 0.8, nUV.y + 0.8), time, strengthDistort);
|
||||
texCo = distortUV(texCo, vec2(nUV.x + 0.9, nUV.y + 0.9), time, strengthDistort);
|
||||
texCo = distortUV(texCo, vec2(nUV.x + 0.15, nUV.y + 0.15), time, strengthDistort);
|
||||
texCo = distortUV(texCo, vec2(nUV.x + 0.25, nUV.y + 0.25), time, strengthDistort);
|
||||
texCo = distortUV(texCo, vec2(nUV.x + 0.35, nUV.y + 0.35), time, strengthDistort);
|
||||
texCo = distortUV(texCo, vec2(nUV.x + 0.45, nUV.y + 0.45), time, strengthDistort);
|
||||
texCo = distortUV(texCo, vec2(nUV.x + 0.55, nUV.y + 0.55), time, strengthDistort);
|
||||
texCo = distortUV(texCo, vec2(nUV.x + 0.65, nUV.y + 0.65), time, strengthDistort);
|
||||
texCo = distortUV(texCo, vec2(nUV.x + 0.75, nUV.y + 0.75), time, strengthDistort);
|
||||
texCo = distortUV(texCo, vec2(nUV.x + 0.85, nUV.y + 0.85), time, strengthDistort);
|
||||
texCo = distortUV(texCo, vec2(nUV.x + 0.95, nUV.y + 0.95), time, strengthDistort);
|
||||
#endif
|
||||
|
||||
#ifdef _CDepth
|
||||
@ -343,6 +406,7 @@ void main() {
|
||||
float compoDistance = PPComp3.x;
|
||||
float compoLength = PPComp3.y;
|
||||
float compoStop = PPComp3.z;
|
||||
vec2 focus = vec2(PPComp19.x, PPComp19.y);
|
||||
|
||||
if (PPComp2.z == 1){
|
||||
compoAutoFocus = true;
|
||||
@ -350,9 +414,9 @@ void main() {
|
||||
compoAutoFocus = false;
|
||||
}
|
||||
|
||||
fragColor.rgb = dof(texCo, depth, tex, gbufferD, texStep, cameraProj, compoAutoFocus, compoDistance, compoLength, compoStop);
|
||||
fragColor.rgb = dof(texCo, depth, tex, gbufferD, texStep, cameraProj, compoAutoFocus, compoDistance, compoLength, compoStop, focus, PPComp19.z);
|
||||
#else
|
||||
fragColor.rgb = dof(texCo, depth, tex, gbufferD, texStep, cameraProj, true, compoDOFDistance, compoDOFLength, compoDOFFstop);
|
||||
fragColor.rgb = dof(texCo, depth, tex, gbufferD, texStep, cameraProj, true, compoDOFDistance, compoDOFLength, compoDOFFstop, vec2(0.5, 0.5), 1.0);
|
||||
#endif
|
||||
#else
|
||||
fragColor = textureLod(tex, texCo, 0.0);
|
||||
@ -382,8 +446,10 @@ void main() {
|
||||
vec3 col4 = textureLod(tex, texCo + vec2(texStep.x, texStep.y) * SharpenSize, 0.0).rgb;
|
||||
vec3 colavg = (col1 + col2 + col3 + col4) * 0.25;
|
||||
|
||||
float edgeMagnitude = length(fragColor.rgb - colavg);
|
||||
fragColor.rgb = mix(fragColor.rgb, SharpenColor, min(edgeMagnitude * strengthSharpen * 2.0, 1.0));
|
||||
float edgeMagnitude = length(fragColor.rgb - colavg);
|
||||
float luma = dot(fragColor.rgb, vec3(0.299, 0.587, 0.114));
|
||||
float sharpenMask = 1.0 - smoothstep(0.5, 0.8, luma);
|
||||
fragColor.rgb = mix(fragColor.rgb, SharpenColor, min(edgeMagnitude * strengthSharpen * 2.0, 1.0) * sharpenMask);
|
||||
#endif
|
||||
|
||||
#ifdef _CFog
|
||||
|
||||
@ -245,6 +245,11 @@
|
||||
"name": "PPComp18",
|
||||
"link": "_PPComp18",
|
||||
"ifdef": ["_CPostprocess"]
|
||||
},
|
||||
{
|
||||
"name": "PPComp19",
|
||||
"link": "_PPComp19",
|
||||
"ifdef": ["_CPostprocess"]
|
||||
}
|
||||
],
|
||||
"texture_params": [],
|
||||
|
||||
@ -14,6 +14,6 @@ void main() {
|
||||
#endif
|
||||
|
||||
#ifdef _EmissionShaded
|
||||
fragColor[GBUF_IDX_EMISSION] = vec4(0.0);
|
||||
fragColor[GBUF_IDX_EMISSION] = vec4(color, 1.0);
|
||||
#endif
|
||||
}
|
||||
|
||||
8
leenkx/Shaders/render_draw/render_line.frag.glsl
Normal file
8
leenkx/Shaders/render_draw/render_line.frag.glsl
Normal file
@ -0,0 +1,8 @@
|
||||
#version 450
|
||||
|
||||
in vec4 color;
|
||||
out vec4 fragColor;
|
||||
|
||||
void main() {
|
||||
fragColor = vec4(color);
|
||||
}
|
||||
12
leenkx/Shaders/render_draw/render_line.vert.glsl
Normal file
12
leenkx/Shaders/render_draw/render_line.vert.glsl
Normal file
@ -0,0 +1,12 @@
|
||||
#version 450
|
||||
|
||||
in vec3 pos;
|
||||
in vec4 col;
|
||||
|
||||
uniform mat4 ViewProjection;
|
||||
out vec4 color;
|
||||
|
||||
void main() {
|
||||
color = col;
|
||||
gl_Position = ViewProjection * vec4(pos, 1.0);
|
||||
}
|
||||
15
leenkx/Shaders/render_draw/render_line_deferred.frag.glsl
Normal file
15
leenkx/Shaders/render_draw/render_line_deferred.frag.glsl
Normal file
@ -0,0 +1,15 @@
|
||||
#version 450
|
||||
|
||||
#include "compiled.inc"
|
||||
|
||||
in vec4 color;
|
||||
out vec4 fragColor[GBUF_SIZE];
|
||||
|
||||
void main() {
|
||||
fragColor[GBUF_IDX_0] = vec4(1.0, 1.0, 0.0, 1.0);
|
||||
fragColor[GBUF_IDX_1] = vec4(color);
|
||||
|
||||
#ifdef _EmissionShaded
|
||||
fragColor[GBUF_IDX_EMISSION] = vec4(color);
|
||||
#endif
|
||||
}
|
||||
@ -10,9 +10,9 @@
|
||||
|
||||
const int samples = 8; // Samples on the first ring
|
||||
const int rings = 6; // Ring count
|
||||
const vec2 focus = vec2(0.5, 0.5);
|
||||
//const vec2 focus = vec2(0.5, 0.5);
|
||||
const float coc = 0.03; // Circle of confusion size in mm (35mm film = 0.03mm)
|
||||
const float maxblur = 1.0;
|
||||
//const float maxblur = 1.0;
|
||||
const float threshold = 0.5; // Highlight threshold
|
||||
const float gain = 2.0; // Highlight gain
|
||||
const float bias = 0.5; // Bokeh edge bias
|
||||
@ -41,7 +41,9 @@ vec3 dof(
|
||||
const bool autoFocus,
|
||||
const float DOFDistance,
|
||||
const float DOFLength,
|
||||
const float DOFFStop) {
|
||||
const float DOFFStop,
|
||||
const vec2 focus,
|
||||
const float maxblur) {
|
||||
|
||||
float depth = linearize(gdepth, cameraProj);
|
||||
float fDepth = 0.0;
|
||||
@ -85,7 +87,6 @@ vec3 dof(
|
||||
float pw = (cos(float(j) * step) * float(i));
|
||||
float ph = (sin(float(j) * step) * float(i));
|
||||
float p = 1.0;
|
||||
// if (pentagon) p = penta(vec2(pw, ph));
|
||||
blurredCol += color(texCoord + vec2(pw * w, ph * h), blur, tex, texStep) * mix(1.0, (float(i)) / (float(rings)), bias) * p;
|
||||
s += 1.0 * mix(1.0, (float(i)) / (float(rings)), bias) * p;
|
||||
}
|
||||
|
||||
@ -28,15 +28,14 @@
|
||||
|
||||
#ifdef _ShadowMap
|
||||
#ifdef _SinglePoint
|
||||
#ifdef _Spot
|
||||
#ifndef _LTC
|
||||
uniform sampler2DShadow shadowMapSpot[1];
|
||||
#ifdef _ShadowMapTransparent
|
||||
uniform sampler2D shadowMapSpotTransparent[1];
|
||||
#endif
|
||||
uniform mat4 LWVPSpotArray[1];
|
||||
#if defined(_Spot) || defined(_LTC)
|
||||
uniform sampler2DShadow shadowMapSpot[1];
|
||||
#ifdef _ShadowMapTransparent
|
||||
uniform sampler2D shadowMapSpotTransparent[1];
|
||||
#endif
|
||||
#else
|
||||
uniform mat4 LWVPSpotArray[1];
|
||||
#endif
|
||||
#ifndef _Spot
|
||||
uniform samplerCubeShadow shadowMapPoint[1];
|
||||
#ifdef _ShadowMapTransparent
|
||||
uniform samplerCube shadowMapPointTransparent[1];
|
||||
@ -45,41 +44,35 @@
|
||||
#endif
|
||||
#endif
|
||||
#ifdef _Clusters
|
||||
#ifdef _SingleAtlas
|
||||
//!uniform sampler2DShadow shadowMapAtlas;
|
||||
#ifdef _ShadowMapTransparent
|
||||
//!uniform sampler2D shadowMapAtlasTransparent;
|
||||
#endif
|
||||
#endif
|
||||
#ifndef _SinglePoint
|
||||
uniform vec2 lightProj;
|
||||
#endif
|
||||
#ifdef _ShadowMapAtlas
|
||||
#ifndef _SingleAtlas
|
||||
uniform sampler2DShadow shadowMapAtlasPoint;
|
||||
#ifdef _ShadowMapTransparent
|
||||
uniform sampler2D shadowMapAtlasPointTransparent;
|
||||
#endif
|
||||
#endif
|
||||
#else
|
||||
uniform samplerCubeShadow shadowMapPoint[4];
|
||||
#ifdef _ShadowMapTransparent
|
||||
uniform samplerCube shadowMapPointTransparent[4];
|
||||
#endif
|
||||
#endif
|
||||
#ifdef _Spot
|
||||
#ifdef _ShadowMapAtlas
|
||||
#ifndef _SingleAtlas
|
||||
uniform sampler2DShadow shadowMapAtlasSpot;
|
||||
uniform sampler2DShadow shadowMapAtlasPoint;
|
||||
#ifdef _ShadowMapTransparent
|
||||
uniform sampler2D shadowMapAtlasSpotTransparent;
|
||||
uniform sampler2D shadowMapAtlasPointTransparent;
|
||||
#endif
|
||||
#endif
|
||||
#else
|
||||
uniform samplerCubeShadow shadowMapPoint[4];
|
||||
#ifdef _ShadowMapTransparent
|
||||
uniform samplerCube shadowMapPointTransparent[4];
|
||||
#endif
|
||||
#endif
|
||||
#if defined(_Spot) || defined(_LTC)
|
||||
#ifdef _ShadowMapAtlas
|
||||
#ifndef _SingleAtlas
|
||||
uniform sampler2DShadow shadowMapAtlasSpot;
|
||||
#ifdef _ShadowMapTransparent
|
||||
uniform sampler2D shadowMapAtlasSpotTransparent;
|
||||
#endif
|
||||
#endif
|
||||
#else
|
||||
uniform sampler2DShadow shadowMapSpot[4];
|
||||
#ifdef _ShadowMapTransparent
|
||||
uniform sampler2D shadowMapSpotTransparent[4];
|
||||
#endif
|
||||
uniform sampler2DShadow shadowMapSpot[4];
|
||||
#ifdef _ShadowMapTransparent
|
||||
uniform sampler2D shadowMapSpotTransparent[4];
|
||||
#endif
|
||||
#endif
|
||||
uniform mat4 LWVPSpotArray[maxLightsCluster];
|
||||
#endif
|
||||
@ -93,15 +86,6 @@ uniform vec3 lightArea2;
|
||||
uniform vec3 lightArea3;
|
||||
uniform sampler2D sltcMat;
|
||||
uniform sampler2D sltcMag;
|
||||
#ifdef _ShadowMap
|
||||
#ifdef _SinglePoint
|
||||
uniform sampler2DShadow shadowMapSpot[1];
|
||||
#ifdef _ShadowMapTransparent
|
||||
uniform sampler2D shadowMapSpotTransparent[1];
|
||||
#endif
|
||||
uniform mat4 LWVPSpotArray[1];
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
||||
vec3 sampleLightCore(const vec3 p, const vec3 n, const vec3 v, const float dotNV, const vec3 lp, const vec3 lightCol,
|
||||
@ -143,37 +127,41 @@ vec3 sampleLightCore(const vec3 p, const vec3 n, const vec3 v, const float dotNV
|
||||
#ifdef _VoxelPass
|
||||
vec3 direct = vec3(dotNL);
|
||||
#else
|
||||
#ifdef _LTC
|
||||
float theta = acos(dotNV);
|
||||
vec2 tuv = vec2(rough, theta / (0.5 * PI));
|
||||
tuv = tuv * LUT_SCALE + LUT_BIAS;
|
||||
vec4 t = textureLod(sltcMat, tuv, 0.0);
|
||||
mat3 invM = mat3(
|
||||
vec3(1.0, 0.0, t.y),
|
||||
vec3(0.0, t.z, 0.0),
|
||||
vec3(t.w, 0.0, t.x));
|
||||
float ltcspec = ltcEvaluate(n, v, dotNV, p, invM, lightArea0, lightArea1, lightArea2, lightArea3);
|
||||
ltcspec *= textureLod(sltcMag, tuv, 0.0).a;
|
||||
float ltcdiff = ltcEvaluate(n, v, dotNV, p, mat3(1.0), lightArea0, lightArea1, lightArea2, lightArea3);
|
||||
vec3 direct = albedo * ltcdiff + ltcspec * spec * 0.05;
|
||||
#else
|
||||
vec3 standard;
|
||||
#ifdef _Anisotropy
|
||||
vec3 direct;
|
||||
if (abs(anisotropy) > 0.001 && dot(tangent, tangent) > 0.001) {
|
||||
vec3 bitangent = normalize(cross(n, tangent));
|
||||
direct = lambertDiffuseBRDF(albedo, dotNL) +
|
||||
standard = lambertDiffuseBRDF(albedo, dotNL) +
|
||||
anisotropicBRDF(f0, rough, anisotropy, anisoRot,
|
||||
tangent, bitangent, n, l, v, dotNL, dotNV) * spec;
|
||||
} else {
|
||||
direct = lambertDiffuseBRDF(albedo, dotNL) +
|
||||
standard = lambertDiffuseBRDF(albedo, dotNL) +
|
||||
specularBRDF(f0, rough, dotNL, dotNH, dotNV, dotVH) * spec;
|
||||
}
|
||||
#else
|
||||
vec3 direct = lambertDiffuseBRDF(albedo, dotNL) +
|
||||
standard = lambertDiffuseBRDF(albedo, dotNL) +
|
||||
specularBRDF(f0, rough, dotNL, dotNH, dotNV, dotVH) * spec;
|
||||
#endif
|
||||
|
||||
#ifdef _Spot
|
||||
if (isSpot) {
|
||||
standard *= spotlightMask(l, spotDir, right, scale, spotSize, spotBlend);
|
||||
}
|
||||
#endif
|
||||
|
||||
vec3 area = vec3(0.0);
|
||||
#ifdef _LTC
|
||||
float theta = acos(dotNV);
|
||||
vec2 tuv = vec2(rough, theta / (0.5 * PI)) * LUT_SCALE + LUT_BIAS;
|
||||
vec4 t = textureLod(sltcMat, tuv, 0.0);
|
||||
mat3 invM = mat3(vec3(1.0, 0.0, t.y), vec3(0.0, t.z, 0.0), vec3(t.w, 0.0, t.x));
|
||||
float ltcspec = ltcEvaluate(n, v, dotNV, p, invM, lightArea0, lightArea1, lightArea2, lightArea3);
|
||||
ltcspec *= textureLod(sltcMag, tuv, 0.0).a;
|
||||
float ltcdiff = ltcEvaluate(n, v, dotNV, p, mat3(1.0), lightArea0, lightArea1, lightArea2, lightArea3);
|
||||
area = albedo * ltcdiff + ltcspec * spec * 0.05;
|
||||
#endif
|
||||
vec3 direct = standard + area;
|
||||
|
||||
// before attenuate/shadow so everything is properly shadowed in one pass
|
||||
#ifdef _ExtBRDF
|
||||
float layerWeight;
|
||||
@ -195,26 +183,31 @@ vec3 sampleLightCore(const vec3 p, const vec3 n, const vec3 v, const float dotNV
|
||||
direct *= attenuate(dist);
|
||||
direct *= min(lightCol, vec3(100.0));
|
||||
|
||||
#ifdef _LTC
|
||||
#ifdef _LightIES
|
||||
direct *= iesAttenuation(-l);
|
||||
#endif
|
||||
|
||||
vec3 visibility = vec3(1.0);
|
||||
#ifdef _ShadowMap
|
||||
if (receiveShadow) {
|
||||
if (receiveShadow) {
|
||||
#if defined(_Spot) || defined(_LTC)
|
||||
#ifdef _SinglePoint
|
||||
vec4 lPos = LWVPSpotArray[0] * vec4(p + n * bias * 10, 1.0);
|
||||
direct *= shadowTest(shadowMapSpot[0],
|
||||
#ifdef _ShadowMapTransparent
|
||||
shadowMapSpotTransparent[0],
|
||||
#endif
|
||||
lPos.xyz / lPos.w, bias
|
||||
#ifdef _ShadowMapTransparent
|
||||
, transparent
|
||||
#endif
|
||||
);
|
||||
vec4 lPos = LWVPSpotArray[0] * vec4(p + n * bias * 10.0, 1.0);
|
||||
visibility = shadowTest(shadowMapSpot[0],
|
||||
#ifdef _ShadowMapTransparent
|
||||
shadowMapSpotTransparent[0],
|
||||
#endif
|
||||
lPos.xyz / lPos.w, bias
|
||||
#ifdef _ShadowMapTransparent
|
||||
, transparent
|
||||
#endif
|
||||
);
|
||||
#endif
|
||||
#ifdef _Clusters
|
||||
vec4 lPos = LWVPSpotArray[index] * vec4(p + n * bias * 10, 1.0);
|
||||
#ifdef _ShadowMapAtlas
|
||||
tileBounds = tileBoundsSpotArray[index];
|
||||
direct *= shadowTest(
|
||||
vec4 lPos = LWVPSpotArray[index] * vec4(p + n * bias * 10.0, 1.0);
|
||||
#ifdef _ShadowMapAtlas
|
||||
tileBounds = tileBoundsSpotArray[index];
|
||||
visibility = shadowTest(
|
||||
#ifdef _ShadowMapTransparent
|
||||
#ifndef _SingleAtlas
|
||||
shadowMapAtlasSpot, shadowMapAtlasSpotTransparent
|
||||
@ -233,147 +226,48 @@ vec3 sampleLightCore(const vec3 p, const vec3 n, const vec3 v, const float dotNV
|
||||
, transparent
|
||||
#endif
|
||||
);
|
||||
#else
|
||||
if (index == 0) direct *= shadowTest(shadowMapSpot[0],
|
||||
#ifdef _ShadowMapTransparent
|
||||
shadowMapSpotTransparent[0],
|
||||
#endif
|
||||
lPos.xyz / lPos.w, bias
|
||||
#ifdef _ShadowMapTransparent
|
||||
, transparent
|
||||
#endif
|
||||
);
|
||||
else if (index == 1) direct *= shadowTest(shadowMapSpot[1],
|
||||
#ifdef _ShadowMapTransparent
|
||||
shadowMapSpotTransparent[1],
|
||||
#endif
|
||||
lPos.xyz / lPos.w, bias
|
||||
#ifdef _ShadowMapTransparent
|
||||
, transparent
|
||||
#endif
|
||||
);
|
||||
else if (index == 2) direct *= shadowTest(shadowMapSpot[2],
|
||||
#ifdef _ShadowMapTransparent
|
||||
shadowMapSpotTransparent[2],
|
||||
#endif
|
||||
lPos.xyz / lPos.w, bias
|
||||
#ifdef _ShadowMapTransparent
|
||||
, transparent
|
||||
#endif
|
||||
);
|
||||
else if (index == 3) direct *= shadowTest(shadowMapSpot[3],
|
||||
#ifdef _ShadowMapTransparent
|
||||
shadowMapSpotTransparent[3],
|
||||
#endif
|
||||
lPos.xyz / lPos.w, bias
|
||||
#ifdef _ShadowMapTransparent
|
||||
, transparent
|
||||
#endif
|
||||
);
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
l_out = l;
|
||||
return direct;
|
||||
#endif
|
||||
|
||||
#ifdef _Spot
|
||||
if (isSpot) {
|
||||
direct *= spotlightMask(l, spotDir, right, scale, spotSize, spotBlend);
|
||||
|
||||
#ifdef _ShadowMap
|
||||
if (receiveShadow) {
|
||||
#ifdef _SinglePoint
|
||||
vec4 lPos = LWVPSpotArray[0] * vec4(p + n * bias * 10, 1.0);
|
||||
direct *= shadowTest(shadowMapSpot[0],
|
||||
#ifdef _ShadowMapTransparent
|
||||
shadowMapSpotTransparent[0],
|
||||
#endif
|
||||
lPos.xyz / lPos.w, bias
|
||||
#ifdef _ShadowMapTransparent
|
||||
, transparent
|
||||
#endif
|
||||
);
|
||||
#endif
|
||||
#ifdef _Clusters
|
||||
vec4 lPos = LWVPSpotArray[index] * vec4(p + n * bias * 10, 1.0);
|
||||
#ifdef _ShadowMapAtlas
|
||||
tileBounds = tileBoundsSpotArray[index];
|
||||
direct *= shadowTest(
|
||||
#else
|
||||
if (index == 0) visibility = shadowTest(shadowMapSpot[0],
|
||||
#ifdef _ShadowMapTransparent
|
||||
#ifndef _SingleAtlas
|
||||
shadowMapAtlasSpot, shadowMapAtlasSpotTransparent
|
||||
#else
|
||||
shadowMapAtlas, shadowMapAtlasTransparent
|
||||
shadowMapSpotTransparent[0],
|
||||
#endif
|
||||
#else
|
||||
#ifndef _SingleAtlas
|
||||
shadowMapAtlasSpot
|
||||
#else
|
||||
shadowMapAtlas
|
||||
#endif
|
||||
#endif
|
||||
, lPos.xyz / lPos.w, bias
|
||||
lPos.xyz / lPos.w, bias
|
||||
#ifdef _ShadowMapTransparent
|
||||
, transparent
|
||||
#endif
|
||||
);
|
||||
else if (index == 1) visibility = shadowTest(shadowMapSpot[1],
|
||||
#ifdef _ShadowMapTransparent
|
||||
shadowMapSpotTransparent[1],
|
||||
#endif
|
||||
lPos.xyz / lPos.w, bias
|
||||
#ifdef _ShadowMapTransparent
|
||||
, transparent
|
||||
#endif
|
||||
);
|
||||
else if (index == 2) visibility = shadowTest(shadowMapSpot[2],
|
||||
#ifdef _ShadowMapTransparent
|
||||
shadowMapSpotTransparent[2],
|
||||
#endif
|
||||
lPos.xyz / lPos.w, bias
|
||||
#ifdef _ShadowMapTransparent
|
||||
, transparent
|
||||
#endif
|
||||
);
|
||||
else if (index == 3) visibility = shadowTest(shadowMapSpot[3],
|
||||
#ifdef _ShadowMapTransparent
|
||||
shadowMapSpotTransparent[3],
|
||||
#endif
|
||||
lPos.xyz / lPos.w, bias
|
||||
#ifdef _ShadowMapTransparent
|
||||
, transparent
|
||||
#endif
|
||||
);
|
||||
#else
|
||||
if (index == 0) direct *= shadowTest(shadowMapSpot[0],
|
||||
#ifdef _ShadowMapTransparent
|
||||
shadowMapSpotTransparent[0],
|
||||
#endif
|
||||
lPos.xyz / lPos.w, bias
|
||||
#ifdef _ShadowMapTransparent
|
||||
, transparent
|
||||
#endif
|
||||
);
|
||||
else if (index == 1) direct *= shadowTest(shadowMapSpot[1],
|
||||
#ifdef _ShadowMapTransparent
|
||||
shadowMapSpotTransparent[1],
|
||||
#endif
|
||||
lPos.xyz / lPos.w, bias
|
||||
#ifdef _ShadowMapTransparent
|
||||
, transparent
|
||||
#endif
|
||||
);
|
||||
else if (index == 2) direct *= shadowTest(shadowMapSpot[2],
|
||||
#ifdef _ShadowMapTransparent
|
||||
shadowMapSpotTransparent[2],
|
||||
#endif
|
||||
lPos.xyz / lPos.w, bias
|
||||
#ifdef _ShadowMapTransparent
|
||||
, transparent
|
||||
#endif
|
||||
);
|
||||
else if (index == 3) direct *= shadowTest(shadowMapSpot[3],
|
||||
#ifdef _ShadowMapTransparent
|
||||
shadowMapSpotTransparent[3],
|
||||
#endif
|
||||
lPos.xyz / lPos.w, bias
|
||||
#ifdef _ShadowMapTransparent
|
||||
, transparent
|
||||
#endif
|
||||
);
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
l_out = l;
|
||||
return direct;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef _LightIES
|
||||
direct *= iesAttenuation(-l);
|
||||
#endif
|
||||
|
||||
#ifdef _ShadowMap
|
||||
if (receiveShadow) {
|
||||
#endif
|
||||
#else
|
||||
#ifdef _SinglePoint
|
||||
#ifndef _Spot
|
||||
direct *= PCFCube(shadowMapPoint[0],
|
||||
visibility = PCFCube(shadowMapPoint[0],
|
||||
#ifdef _ShadowMapTransparent
|
||||
shadowMapPointTransparent[0],
|
||||
#endif
|
||||
@ -383,10 +277,9 @@ vec3 sampleLightCore(const vec3 p, const vec3 n, const vec3 v, const float dotNV
|
||||
#endif
|
||||
);
|
||||
#endif
|
||||
#endif
|
||||
#ifdef _Clusters
|
||||
#ifdef _ShadowMapAtlas
|
||||
direct *= PCFFakeCube(
|
||||
visibility = PCFFakeCube(
|
||||
#ifdef _ShadowMapTransparent
|
||||
#ifndef _SingleAtlas
|
||||
shadowMapAtlasPoint, shadowMapAtlasPointTransparent
|
||||
@ -406,49 +299,50 @@ vec3 sampleLightCore(const vec3 p, const vec3 n, const vec3 v, const float dotNV
|
||||
#endif
|
||||
);
|
||||
#else
|
||||
if (index == 0) direct *= PCFCube(shadowMapPoint[0],
|
||||
#ifdef _ShadowMapTransparent
|
||||
shadowMapPointTransparent[0],
|
||||
#endif
|
||||
ld, -l, bias, lightProj, n
|
||||
#ifdef _ShadowMapTransparent
|
||||
, transparent
|
||||
#endif
|
||||
);
|
||||
else if (index == 1) direct *= PCFCube(shadowMapPoint[1],
|
||||
#ifdef _ShadowMapTransparent
|
||||
shadowMapPointTransparent[1],
|
||||
#endif
|
||||
ld, -l, bias, lightProj, n
|
||||
#ifdef _ShadowMapTransparent
|
||||
, transparent
|
||||
#endif
|
||||
);
|
||||
else if (index == 2) direct *= PCFCube(shadowMapPoint[2],
|
||||
#ifdef _ShadowMapTransparent
|
||||
shadowMapPointTransparent[2],
|
||||
#endif
|
||||
ld, -l, bias, lightProj, n
|
||||
#ifdef _ShadowMapTransparent
|
||||
, transparent
|
||||
#endif
|
||||
);
|
||||
else if (index == 3) direct *= PCFCube(shadowMapPoint[3],
|
||||
#ifdef _ShadowMapTransparent
|
||||
shadowMapPointTransparent[3],
|
||||
#endif
|
||||
ld, -l, bias, lightProj, n
|
||||
#ifdef _ShadowMapTransparent
|
||||
, transparent
|
||||
#endif
|
||||
);
|
||||
if (index == 0) visibility = PCFCube(shadowMapPoint[0],
|
||||
#ifdef _ShadowMapTransparent
|
||||
shadowMapPointTransparent[0],
|
||||
#endif
|
||||
ld, -l, bias, lightProj, n
|
||||
#ifdef _ShadowMapTransparent
|
||||
, transparent
|
||||
#endif
|
||||
);
|
||||
else if (index == 1) visibility = PCFCube(shadowMapPoint[1],
|
||||
#ifdef _ShadowMapTransparent
|
||||
shadowMapPointTransparent[1],
|
||||
#endif
|
||||
ld, -l, bias, lightProj, n
|
||||
#ifdef _ShadowMapTransparent
|
||||
, transparent
|
||||
#endif
|
||||
);
|
||||
else if (index == 2) visibility = PCFCube(shadowMapPoint[2],
|
||||
#ifdef _ShadowMapTransparent
|
||||
shadowMapPointTransparent[2],
|
||||
#endif
|
||||
ld, -l, bias, lightProj, n
|
||||
#ifdef _ShadowMapTransparent
|
||||
, transparent
|
||||
#endif
|
||||
);
|
||||
else if (index == 3) visibility = PCFCube(shadowMapPoint[3],
|
||||
#ifdef _ShadowMapTransparent
|
||||
shadowMapPointTransparent[3],
|
||||
#endif
|
||||
ld, -l, bias, lightProj, n
|
||||
#ifdef _ShadowMapTransparent
|
||||
, transparent
|
||||
#endif
|
||||
);
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
l_out = l;
|
||||
return direct;
|
||||
return direct * visibility;
|
||||
}
|
||||
|
||||
vec3 sampleLight(const vec3 p, const vec3 n, const vec3 v, const float dotNV, const vec3 lp, const vec3 lightCol,
|
||||
|
||||
@ -1,41 +1,81 @@
|
||||
/*
|
||||
https://github.com/JonasFolletete/glsl-triplanar-mapping
|
||||
vec4 boxProjection(sampler2D image, vec3 normal, vec3 coord, float blend) {
|
||||
vec3 n = normalize(normal);
|
||||
vec3 N = abs(n);
|
||||
vec4 color1, color2, color3;
|
||||
|
||||
MIT License
|
||||
vec2 uv = coord.yz;
|
||||
if (n.x < 0.0) {
|
||||
uv.x = 1.0 - uv.x;
|
||||
}
|
||||
color1 = texture(image, uv);
|
||||
|
||||
Copyright (c) 2018 Jonas Folletête
|
||||
uv = coord.xz;
|
||||
if (n.y > 0.0) {
|
||||
uv.x = 1.0 - uv.x;
|
||||
}
|
||||
color2 = texture(image, uv);
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
uv = vec2(coord.y, 1.0 - coord.x);
|
||||
if (n.z > 0.0) {
|
||||
uv.x = 1.0 - uv.x;
|
||||
}
|
||||
color3 = texture(image, uv);
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
N /= max(dot(N, vec3(1.0)), 1e-8);
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
float limit = 0.5 + 0.5 * clamp(blend, 0.0, 1.0);
|
||||
vec3 weight;
|
||||
weight = N.xyz / (N.xyx + N.yzz);
|
||||
weight = clamp((weight - 0.5 * (1.0 - clamp(blend, 0.0, 1.0))) / max(1e-8, clamp(blend, 0.0, 1.0)), 0.0, 1.0);
|
||||
|
||||
vec3 blendNormal(vec3 normal) {
|
||||
vec3 blending = abs(normal);
|
||||
blending = normalize(max(blending, 0.00001));
|
||||
blending /= vec3(blending.x + blending.y + blending.z);
|
||||
return blending;
|
||||
if (N.z < (1.0 - limit) * (N.y + N.x)) {
|
||||
weight.z = 0.0;
|
||||
weight.y = 1.0 - weight.x;
|
||||
}
|
||||
else if (N.x < (1.0 - limit) * (N.y + N.z)) {
|
||||
weight.x = 0.0;
|
||||
weight.z = 1.0 - weight.y;
|
||||
}
|
||||
else if (N.y < (1.0 - limit) * (N.x + N.z)) {
|
||||
weight.y = 0.0;
|
||||
weight.x = 1.0 - weight.z;
|
||||
}
|
||||
else {
|
||||
weight = ((2.0 - limit) * N + (limit - 1.0)) / max(1e-8, clamp(blend, 0.0, 1.0));
|
||||
}
|
||||
|
||||
return color1 * weight.x + color2 * weight.y + color3 * weight.z;
|
||||
}
|
||||
|
||||
vec3 triplanarMapping (sampler2D ImageTexture, vec3 normal, vec3 position) {
|
||||
vec3 normalBlend = blendNormal(normal);
|
||||
vec3 xColor = texture(ImageTexture, position.yz).rgb;
|
||||
vec3 yColor = texture(ImageTexture, position.xz).rgb;
|
||||
vec3 zColor = texture(ImageTexture, position.xy).rgb;
|
||||
|
||||
return (xColor * normalBlend.x + yColor * normalBlend.y + zColor * normalBlend.z);
|
||||
vec2 sphericalMapping(vec3 coord) {
|
||||
vec3 vin = coord * 2.0 - vec3(1.0);
|
||||
float len = length(vin);
|
||||
float v, u;
|
||||
if (len > 0.0) {
|
||||
if (vin.x == 0.0 && vin.y == 0.0) {
|
||||
u = 0.0;
|
||||
}
|
||||
else {
|
||||
u = (1.0 - atan(vin.x, vin.y) / PI) * 0.5;
|
||||
}
|
||||
v = acos(clamp(vin.z / len, -1.0, 1.0)) / PI;
|
||||
}
|
||||
else {
|
||||
v = u = 0.0;
|
||||
}
|
||||
return vec2(u, v);
|
||||
}
|
||||
|
||||
vec2 tubeMapping(vec3 coord) {
|
||||
vec3 vin = coord * 2.0 - vec3(1.0);
|
||||
float u, v;
|
||||
v = - (vin.z + 1.0) * 0.5;
|
||||
float len = sqrt(vin.x * vin.x + vin.y * vin.y);
|
||||
if (len > 0.0) {
|
||||
u = (1.0 - (atan(vin.x / len, vin.y / len) / PI)) * 0.5;
|
||||
}
|
||||
else {
|
||||
v = u = 0.0;
|
||||
}
|
||||
return vec2(u, v);
|
||||
}
|
||||
|
||||
@ -19,6 +19,14 @@ vec2 envMapEquirect(const vec3 normal) {
|
||||
return vec2(theta / PI2, phi / PI);
|
||||
}
|
||||
|
||||
vec2 envMapMirror(const vec3 co) {
|
||||
vec3 nco = normalize(co);
|
||||
nco.y -= 1.0;
|
||||
float div = 2.0 * sqrt(max(-0.5 * nco.y, 0.0));
|
||||
nco /= max(1e-8, div);
|
||||
return 0.5 * nco.xz + 0.5;
|
||||
}
|
||||
|
||||
float rand(const vec2 co) { // Unreliable
|
||||
return fract(sin(dot(co.xy, vec2(12.9898, 78.233))) * 43758.5453);
|
||||
}
|
||||
|
||||
@ -4,6 +4,36 @@ uniform vec2 morphScaleOffset;
|
||||
uniform vec2 morphDataDim;
|
||||
uniform vec4 morphWeights[8];
|
||||
|
||||
void getMorphedVertex(vec2 uvCoord, inout vec3 A, vec4 imorph) {
|
||||
vec3 morph = texture(morphDataPos, uvCoord).rgb * morphScaleOffset.x + morphScaleOffset.y;
|
||||
A += imorph.x * morph;
|
||||
|
||||
morph = texture(morphDataPos, vec2(uvCoord.x, uvCoord.y - morphDataDim.y)).rgb * morphScaleOffset.x + morphScaleOffset.y;
|
||||
A += imorph.y * morph;
|
||||
|
||||
morph = texture(morphDataPos, vec2(uvCoord.x, uvCoord.y - 2.0 * morphDataDim.y)).rgb * morphScaleOffset.x + morphScaleOffset.y;
|
||||
A += imorph.z * morph;
|
||||
|
||||
morph = texture(morphDataPos, vec2(uvCoord.x, uvCoord.y - 3.0 * morphDataDim.y)).rgb * morphScaleOffset.x + morphScaleOffset.y;
|
||||
A += imorph.w * morph;
|
||||
}
|
||||
|
||||
void getMorphedNormal(vec2 uvCoord, vec3 oldNor, inout vec3 morphNor, vec4 imorph) {
|
||||
vec3 norm = oldNor + imorph.x * (texture(morphDataNor, uvCoord).rgb * 2.0 - 1.0);
|
||||
morphNor += norm;
|
||||
|
||||
norm = oldNor + imorph.y * (texture(morphDataNor, vec2(uvCoord.x, uvCoord.y - morphDataDim.y)).rgb * 2.0 - 1.0);
|
||||
morphNor += norm;
|
||||
|
||||
norm = oldNor + imorph.z * (texture(morphDataNor, vec2(uvCoord.x, uvCoord.y - 2.0 * morphDataDim.y)).rgb * 2.0 - 1.0);
|
||||
morphNor += norm;
|
||||
|
||||
norm = oldNor + imorph.w * (texture(morphDataNor, vec2(uvCoord.x, uvCoord.y - 3.0 * morphDataDim.y)).rgb * 2.0 - 1.0);
|
||||
morphNor += norm;
|
||||
|
||||
morphNor = normalize(morphNor);
|
||||
}
|
||||
|
||||
void getMorphedVertex(vec2 uvCoord, inout vec3 A){
|
||||
vec3 totalDelta = vec3(0.0);
|
||||
for(int i = 0; i<8; i++ )
|
||||
|
||||
@ -237,8 +237,24 @@ class App {
|
||||
traitRenders.remove(f);
|
||||
}
|
||||
|
||||
public static function notifyOnRender2D(f: kha.graphics2.Graphics->Void) {
|
||||
traitRenders2D.push(f);
|
||||
public static function notifyOnRender2D(f: kha.graphics2.Graphics->Void, index: Int = -1) {
|
||||
if (index < 0 || index >= traitRenders2D.length) {
|
||||
traitRenders2D.push(f);
|
||||
} else {
|
||||
traitRenders2D.insert(index, f);
|
||||
}
|
||||
}
|
||||
|
||||
public static function moveRender2D(f: kha.graphics2.Graphics->Void, newIndex: Int) {
|
||||
var oldIndex = traitRenders2D.indexOf(f);
|
||||
if (oldIndex != -1) {
|
||||
traitRenders2D.splice(oldIndex, 1);
|
||||
if (newIndex >= traitRenders2D.length) {
|
||||
traitRenders2D.push(f);
|
||||
} else {
|
||||
traitRenders2D.insert(newIndex, f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static function removeRender2D(f: kha.graphics2.Graphics->Void) {
|
||||
|
||||
@ -531,13 +531,11 @@ class RenderPath {
|
||||
|
||||
if (!drawn) submitDraw(context);
|
||||
|
||||
#if lnx_debug
|
||||
// Callbacks to specific context
|
||||
if (contextEvents != null) {
|
||||
var ar = contextEvents.get(context);
|
||||
if (ar != null) for (i in 0...ar.length) ar[i](currentG, i, ar.length);
|
||||
}
|
||||
#end
|
||||
|
||||
end();
|
||||
}
|
||||
@ -597,7 +595,6 @@ class RenderPath {
|
||||
}
|
||||
}
|
||||
|
||||
#if lnx_debug
|
||||
static var contextEvents: Map<String, Array<Graphics->Int->Int->Void>> = null;
|
||||
public static function notifyOnContext(name: String, onContext: Graphics->Int->Int->Void) {
|
||||
if (contextEvents == null) contextEvents = new Map();
|
||||
@ -608,7 +605,13 @@ class RenderPath {
|
||||
}
|
||||
ar.push(onContext);
|
||||
}
|
||||
#end
|
||||
|
||||
public static function removeNotifyOnContext(name: String, onContext: Graphics->Int->Int->Void) {
|
||||
if (contextEvents != null) {
|
||||
var ar = contextEvents.get(name);
|
||||
if (ar != null) ar.remove(onContext);
|
||||
}
|
||||
}
|
||||
|
||||
#if rp_decals
|
||||
public function drawDecals(context: String) {
|
||||
|
||||
@ -14,6 +14,7 @@ import iron.object.SpeakerObject;
|
||||
import iron.object.DecalObject;
|
||||
import iron.object.ProbeObject;
|
||||
import iron.object.Tilesheet;
|
||||
import iron.object.CurveObject;
|
||||
import iron.data.CameraData;
|
||||
import iron.data.MeshData;
|
||||
import iron.data.LightData;
|
||||
@ -64,6 +65,7 @@ class Scene {
|
||||
#end
|
||||
public var empties: Array<Object>;
|
||||
public var animations: Array<Animation>;
|
||||
public var tilesheets: Array<Tilesheet>;
|
||||
#if lnx_skin
|
||||
public var armatures: Array<Armature>;
|
||||
#end
|
||||
@ -110,6 +112,7 @@ class Scene {
|
||||
#end
|
||||
empties = [];
|
||||
animations = [];
|
||||
tilesheets = [];
|
||||
#if lnx_skin
|
||||
armatures = [];
|
||||
#end
|
||||
@ -135,6 +138,14 @@ class Scene {
|
||||
|
||||
// Startup scene
|
||||
active.addScene(format.name, null, function(sceneObject: Object) {
|
||||
|
||||
if (format.properties != null) {
|
||||
sceneObject.properties = new Map();
|
||||
for (p in format.properties) {
|
||||
sceneObject.properties.set(p.name, cleanValue(p.value));
|
||||
}
|
||||
}
|
||||
|
||||
// Create traits bottom-up (children first, then parents)
|
||||
createTraitsBottomUp(sceneObject);
|
||||
|
||||
@ -342,6 +353,7 @@ class Scene {
|
||||
if (terrainStream != null) terrainStream.update(active.camera);
|
||||
#end
|
||||
for (anim in animations) anim.update(Time.delta);
|
||||
for (tilesheet in tilesheets) tilesheet.update();
|
||||
for (e in empties) if (e != null && e.parent != null) e.transform.update();
|
||||
}
|
||||
|
||||
@ -441,6 +453,11 @@ class Scene {
|
||||
return g;
|
||||
}
|
||||
|
||||
public function removeFromGroups(object: Object) {
|
||||
if (groups == null) return;
|
||||
for (name in groups.keys()) getGroup(name).remove(object);
|
||||
}
|
||||
|
||||
public function addMeshObject(data: MeshData, materials: Vector<MaterialData>, parent: Object = null): MeshObject {
|
||||
var object = new MeshObject(data, materials);
|
||||
parent != null ? object.setParent(parent) : object.setParent(root);
|
||||
@ -450,6 +467,12 @@ class Scene {
|
||||
return object;
|
||||
}
|
||||
|
||||
public function addCurveObject(data: TCurveData, parent: Object = null): CurveObject {
|
||||
var object = new CurveObject(data);
|
||||
parent != null ? object.setParent(parent) : object.setParent(root);
|
||||
return object;
|
||||
}
|
||||
|
||||
public function addLightObject(data: LightData, parent: Object = null): LightObject {
|
||||
var object = new LightObject(data);
|
||||
parent != null ? object.setParent(parent) : object.setParent(root);
|
||||
@ -709,6 +732,10 @@ class Scene {
|
||||
else done(ro);
|
||||
});
|
||||
}
|
||||
else if (o.type == "curve_object") {
|
||||
var object = addCurveObject(Data.getCurveRawByName(format.curve_datas, o.data_ref), parent);
|
||||
returnObject(object, o, done);
|
||||
}
|
||||
else done(null);
|
||||
}
|
||||
|
||||
@ -889,11 +916,13 @@ class Scene {
|
||||
}
|
||||
else { #end // lnx_skin
|
||||
#if lnx_stream
|
||||
streamMeshObject(
|
||||
if ((o.particle_refs == null || o.particle_refs.length == 0) && o.is_particle == null && parent != null)
|
||||
streamMeshObject(object_file, data_ref, sceneName, null, materials, parent, parentObject, o, done);
|
||||
else
|
||||
returnMeshObject(object_file, data_ref, sceneName, null, materials, parent, parentObject, o, done);
|
||||
#else
|
||||
returnMeshObject(
|
||||
returnMeshObject(object_file, data_ref, sceneName, null, materials, parent, parentObject, o, done);
|
||||
#end
|
||||
object_file, data_ref, sceneName, null, materials, parent, parentObject, o, done);
|
||||
#if lnx_skin
|
||||
}
|
||||
#end
|
||||
@ -968,20 +997,24 @@ class Scene {
|
||||
#end
|
||||
if (o.properties != null) {
|
||||
object.properties = new Map();
|
||||
for (p in o.properties) object.properties.set(p.name, p.value);
|
||||
for (p in o.properties) {
|
||||
object.properties.set(p.name, cleanValue(p.value));
|
||||
}
|
||||
}
|
||||
|
||||
if (o.vertex_groups != null) {
|
||||
object.vertex_groups = new Map();
|
||||
for (p in o.vertex_groups){
|
||||
var verts = [];
|
||||
for(i in 0...Std.int(p.value.length/3)){
|
||||
var x = Std.parseFloat(p.value[i*3]);
|
||||
var y = Std.parseFloat(p.value[i*3+1]);
|
||||
var z = Std.parseFloat(p.value[i*3+2]);
|
||||
verts.push(new iron.math.Vec4(x, y, z, 1));
|
||||
cast(object, MeshObject).vertexGroups = new Map();
|
||||
for (p in o.vertex_groups) {
|
||||
var verts:Array<iron.math.Vec4> = [];
|
||||
|
||||
var data:kha.arrays.Float32Array = cast p.value;
|
||||
|
||||
if (data != null) {
|
||||
for (i in 0...Std.int(data.length / 3)) {
|
||||
verts.push(new iron.math.Vec4(data[i * 3], data[i * 3 + 1], data[i * 3 + 2], 1.0));
|
||||
}
|
||||
}
|
||||
object.vertex_groups.set(p.name, verts);
|
||||
cast(object, MeshObject).vertexGroups.set(p.name, verts);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1145,4 +1178,16 @@ class Scene {
|
||||
public function notifyOnRemove(f: Void->Void) {
|
||||
traitRemoves.push(f);
|
||||
}
|
||||
|
||||
static function cleanValue(val: Dynamic): Dynamic {
|
||||
if (val == null) return null;
|
||||
if (untyped val.buffer != null) {
|
||||
var data: kha.arrays.Float32Array = cast val;
|
||||
return [for (i in 0...data.length) data[i]];
|
||||
}
|
||||
if (Std.isOfType(val, Array)) {
|
||||
return [for (item in (cast val: Array<Dynamic>)) cleanValue(item)];
|
||||
}
|
||||
return val;
|
||||
}
|
||||
}
|
||||
|
||||
@ -125,10 +125,10 @@ class Trait {
|
||||
/**
|
||||
Add 2D render handler.
|
||||
**/
|
||||
public function notifyOnRender2D(f: kha.graphics2.Graphics->Void) {
|
||||
public function notifyOnRender2D(f: kha.graphics2.Graphics->Void, index: Int = -1) {
|
||||
if (_render2D == null) _render2D = [];
|
||||
_render2D.push(f);
|
||||
App.notifyOnRender2D(f);
|
||||
App.notifyOnRender2D(f, index);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -311,7 +311,7 @@ class Data {
|
||||
|
||||
loadingSceneRaws.set(file, [done]);
|
||||
|
||||
// If no extension specified, set to .arm
|
||||
// If no extension specified, set to .lnx
|
||||
var compressed = file.endsWith(".lz4");
|
||||
var isJson = file.endsWith(".json");
|
||||
var ext = (compressed || isJson || file.endsWith(".lnx")) ? "" : ".lnx";
|
||||
@ -405,6 +405,13 @@ class Data {
|
||||
}
|
||||
#end
|
||||
|
||||
public static function getCurveRawByName(datas: Array<TCurveData>, name: String): TCurveData {
|
||||
if (datas == null || datas.length == 0) return null;
|
||||
if (name == "") return datas[0];
|
||||
for (dat in datas) if (dat.name == name) return dat;
|
||||
return null;
|
||||
}
|
||||
|
||||
// Raw assets
|
||||
public static function getBlob(file: String, done: kha.Blob->Void) {
|
||||
var cached = cachedBlobs.get(file); // Is already cached
|
||||
|
||||
@ -40,6 +40,8 @@ class Geometry {
|
||||
public var instancedVB: VertexBuffer = null;
|
||||
public var instanced = false;
|
||||
public var instanceCount = 0;
|
||||
public var instanceElements: Array<{name: String, data: String}> = [];
|
||||
public var instanceStride: Int = 0;
|
||||
|
||||
public var positions: TVertexArray;
|
||||
public var normals: TVertexArray;
|
||||
@ -130,11 +132,39 @@ class Geometry {
|
||||
structure.add("iscl", kha.graphics4.VertexData.Float3);
|
||||
}
|
||||
|
||||
if (instanceElements != null && instanceElements.length > 0) {
|
||||
for (elem in instanceElements) {
|
||||
if (StringTools.startsWith(elem.name, "i") && elem.name != "ipos" && elem.name != "irot" && elem.name != "iscl") {
|
||||
var vdata = VertexData.Float1;
|
||||
var dataStr: String = Reflect.field(elem, "data");
|
||||
switch (dataStr) {
|
||||
case "float1": vdata = VertexData.Float1;
|
||||
case "float2": vdata = VertexData.Float2;
|
||||
case "float3": vdata = VertexData.Float3;
|
||||
case "float4": vdata = VertexData.Float4;
|
||||
}
|
||||
structure.add(elem.name, vdata);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.instanceStride = Std.int(structure.byteSize() / 4);
|
||||
instanceCount = Std.int(data.length / Std.int(structure.byteSize() / 4));
|
||||
instancedVB = new VertexBuffer(instanceCount, structure, usage, 1);
|
||||
var vertices = instancedVB.lock();
|
||||
for (i in 0...Std.int(vertices.byteLength / 4)) vertices.setFloat32(i * 4, data[i]);
|
||||
instancedVB.unlock();
|
||||
|
||||
}
|
||||
|
||||
public function updateInstanced(data: Float32Array) {
|
||||
if (instancedVB == null) return;
|
||||
|
||||
var vertices = instancedVB.lock();
|
||||
for (i in 0...Std.int(vertices.byteLength / 4)) {
|
||||
vertices.setFloat32(i * 4, data[i]);
|
||||
}
|
||||
instancedVB.unlock();
|
||||
}
|
||||
|
||||
public function copyVertices(vertices: ByteArray, offset = 0, fakeUVs = false) {
|
||||
|
||||
@ -40,6 +40,8 @@ typedef TSceneFormat = {
|
||||
@:optional public var irradiance: Float32Array; // Blob with spherical harmonics, bands 0,1,2
|
||||
@:optional public var terrain_datas: Array<TTerrainData>;
|
||||
@:optional public var terrain_ref: String;
|
||||
@:optional public var properties: Array<TProperty>;
|
||||
@:optional public var curve_datas: Array<TCurveData>;
|
||||
}
|
||||
|
||||
#if js
|
||||
@ -432,6 +434,7 @@ typedef TParticleData = {
|
||||
// Velocity
|
||||
public var object_align_factor: Float32Array;
|
||||
public var factor_random: FastFloat;
|
||||
public var normal_factor: FastFloat;
|
||||
// Rotation
|
||||
public var use_rotations: Bool;
|
||||
public var rotation_mode: Int; // 0 - None, 1 - Normal, 2 - Normal-Tangent, 3 - Velocity/Hair, 4 - Global X, 5 - Global Y, 6 - Global Z, 7 - Object X, 8 - Object Y, 9 - Object Z
|
||||
@ -525,7 +528,7 @@ typedef TVertex_groups = {
|
||||
@:structInit class TVertex_groups {
|
||||
#end
|
||||
public var name: String;
|
||||
public var value: Dynamic;
|
||||
public var value: Float32Array;
|
||||
}
|
||||
|
||||
#if js
|
||||
@ -564,6 +567,21 @@ typedef TConstraint = {
|
||||
@:optional public var invert_z: Null<Bool>;
|
||||
@:optional public var use_offset: Null<Bool>;
|
||||
@:optional public var influence: Null<FastFloat>;
|
||||
@:optional public var use_min_x: Null<Bool>;
|
||||
@:optional public var use_max_x: Null<Bool>;
|
||||
@:optional public var use_min_y: Null<Bool>;
|
||||
@:optional public var use_max_y: Null<Bool>;
|
||||
@:optional public var use_min_z: Null<Bool>;
|
||||
@:optional public var use_max_z: Null<Bool>;
|
||||
@:optional public var use_limit_x: Null<Bool>;
|
||||
@:optional public var use_limit_y: Null<Bool>;
|
||||
@:optional public var use_limit_z: Null<Bool>;
|
||||
@:optional public var min_x: Null<FastFloat>;
|
||||
@:optional public var max_x: Null<FastFloat>;
|
||||
@:optional public var min_y: Null<FastFloat>;
|
||||
@:optional public var max_y: Null<FastFloat>;
|
||||
@:optional public var min_z: Null<FastFloat>;
|
||||
@:optional public var max_z: Null<FastFloat>;
|
||||
}
|
||||
|
||||
#if js
|
||||
@ -622,3 +640,48 @@ typedef TTrack = {
|
||||
public var values: Float32Array; // sampled - full matrix transforms, non-sampled - values
|
||||
@:optional public var ref_values: Array<Array<String>>; // ref values
|
||||
}
|
||||
|
||||
#if js
|
||||
typedef TBezierPoint = {
|
||||
#else
|
||||
@:structInit class TBezierPoint {
|
||||
#end
|
||||
public var co: Float32Array;
|
||||
public var handle_left: Float32Array;
|
||||
public var handle_right: Float32Array;
|
||||
}
|
||||
|
||||
#if js
|
||||
typedef TSpline = {
|
||||
#else
|
||||
@:structInit class TSpline {
|
||||
#end
|
||||
public var closed: Bool;
|
||||
public var resolution: Int;
|
||||
public var points: Array<TBezierPoint>;
|
||||
public var material_index: Int;
|
||||
}
|
||||
|
||||
#if js
|
||||
typedef TShapeKey = {
|
||||
#else
|
||||
@:structInit class TShapeKey {
|
||||
#end
|
||||
public var name: String;
|
||||
public var value: Float;
|
||||
public var points: Array<TBezierPoint>;
|
||||
}
|
||||
|
||||
#if js
|
||||
typedef TCurveData = {
|
||||
#else
|
||||
@:structInit class TCurveData {
|
||||
#end
|
||||
public var name: String;
|
||||
public var object: String;
|
||||
public var splines: Array<TSpline>;
|
||||
public var strength: Float;
|
||||
public var color: Float32Array;
|
||||
@:optional public var material_refs: Array<String>;
|
||||
@:optional public var shape_keys: Array<TShapeKey>;
|
||||
}
|
||||
|
||||
@ -79,6 +79,8 @@ class ShaderContext {
|
||||
|
||||
var structure: VertexStructure;
|
||||
var instancingType = 0;
|
||||
var instanceElements: Array<{name: String, data: String}> = [];
|
||||
var instanceStride: Int = 0;
|
||||
|
||||
public function new(raw: TShaderContext, done: ShaderContext->Void, overrideContext: TShaderOverride = null) {
|
||||
this.raw = raw;
|
||||
@ -108,6 +110,11 @@ class ShaderContext {
|
||||
if (instancingType == 3 || instancingType == 4) {
|
||||
instStruct.add("iscl", VertexData.Float3);
|
||||
}
|
||||
|
||||
for (e in instanceElements)
|
||||
instStruct.add(e.name, parseData(e.data));
|
||||
this.instanceStride = Std.int(instStruct.byteSize() / 4);
|
||||
|
||||
instStruct.instanced = true;
|
||||
pipeState.inputLayout = [structure, instStruct];
|
||||
}
|
||||
@ -268,10 +275,12 @@ class ShaderContext {
|
||||
if (Reflect.field(elem, "name") == "ipos") { ipos = true; continue; }
|
||||
if (Reflect.field(elem, "name") == "irot") { irot = true; continue; }
|
||||
if (Reflect.field(elem, "name") == "iscl") { iscl = true; continue; }
|
||||
if (Reflect.field(elem, "name").startsWith("i")) { instanceElements.push(elem); continue; }
|
||||
#else
|
||||
if (elem.name == "ipos") { ipos = true; continue; }
|
||||
if (elem.name == "irot") { irot = true; continue; }
|
||||
if (elem.name == "iscl") { iscl = true; continue; }
|
||||
if (elem.name.startsWith("i")) { instanceElements.push(elem); continue; }
|
||||
#end
|
||||
structure.add(elem.name, parseData(elem.data));
|
||||
}
|
||||
|
||||
357
leenkx/Sources/iron/format/gif/Data.hx
Normal file
357
leenkx/Sources/iron/format/gif/Data.hx
Normal file
@ -0,0 +1,357 @@
|
||||
package iron.format.gif;
|
||||
|
||||
import haxe.io.Bytes;
|
||||
|
||||
/**
|
||||
* Gif data.
|
||||
*/
|
||||
typedef Data =
|
||||
{
|
||||
/**
|
||||
* Gif version. There is only 2 Gif version exists. 87a and 89a.
|
||||
* 87a have less features and does not support any extensions.
|
||||
* Unknown version is adviced to be interpreted as newest (89a) official version.
|
||||
*/
|
||||
var version:Version;
|
||||
/**
|
||||
* Information about logical screen of Gif that provides basic information about Gif.
|
||||
*/
|
||||
var logicalScreenDescriptor:LogicalScreenDescriptor;
|
||||
/**
|
||||
* Global color table used for Gif. Present only if Logical Screen Descriptor contained global color table flag.
|
||||
* Note that this color table not always present since frames can contain local color tables that overrides global color table.
|
||||
*/
|
||||
@:optional var globalColorTable:Null<ColorTable>;
|
||||
/**
|
||||
* List of Gif data blocks.
|
||||
*/
|
||||
var blocks:List<Block>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gif data block. Custom blocks are not supported.
|
||||
*/
|
||||
enum Block
|
||||
{
|
||||
/**
|
||||
* Gif frame block.
|
||||
* Note that this block does not contain link to graphic control extension of Frame even if it is present. GraphicControl extension Block commonly present right before frame Block.
|
||||
*/
|
||||
BFrame(frame:Frame);
|
||||
/**
|
||||
* Additional extension block. This Block does not supported in 87a Gif specification version.
|
||||
*/
|
||||
BExtension(extension:Extension);
|
||||
/**
|
||||
* End of File block. Represents end of Gif data.
|
||||
*/
|
||||
BEOF;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extension block contains additional data about Gif image. This block does not supported by 87a version.
|
||||
*/
|
||||
enum Extension
|
||||
{
|
||||
/**
|
||||
* Graphic Control extension gives additional control over next frame, like frame delay, disposal method, alpha channel and other information.
|
||||
*/
|
||||
EGraphicControl(gce:GraphicControlExtension);
|
||||
/**
|
||||
* Commentary extension. Not show up as any visual, just a text in file.
|
||||
*/
|
||||
EComment(text:String);
|
||||
/**
|
||||
* Text extension. Must work as text rendering on the image, but ignored by all major Gif decoders.
|
||||
*/
|
||||
EText(pte:PlainTextExtension);
|
||||
/**
|
||||
* Application extension allow to insert additional application data into Gif. Mostly used app extension is NETSCAPE2.0 looping extension, used to set up amount of loops in frame.
|
||||
*/
|
||||
EApplicationExtension(ext:ApplicationExtension);
|
||||
|
||||
/**
|
||||
* Unknown extension.
|
||||
*/
|
||||
EUnknown(id:Int, data:Bytes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Application extension. Mostly used only for one reason - setting up loops count. There is exist other app extensions but they are really rare.
|
||||
*/
|
||||
enum ApplicationExtension
|
||||
{
|
||||
/**
|
||||
* NETSCAPE2.0 looping extension. Contains only amount of animation repeats.
|
||||
* Note that there is two NETSCAPE2.0 app extensions for Gif format and the type of extension is stored in first byte of data. Looping extension have ID 1.
|
||||
*/
|
||||
AENetscapeLooping(loops:Int);
|
||||
/**
|
||||
* Unknown or unsupported app extension.
|
||||
*/
|
||||
AEUnknown(name:String, version:String, data:Bytes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Typical color table for Gif image.
|
||||
* Can contain 2, 4, 8, 16, 32, 64, 128 or 256 colors.
|
||||
* Data stored in RGB format. Information about alpha channel provided by Graohic Control Extension.
|
||||
*/
|
||||
typedef ColorTable = Bytes;
|
||||
|
||||
/**
|
||||
* Single frame of the image.
|
||||
* Actually it's a merge of 3 consequent blocks:
|
||||
* 1. Image Descriptor.
|
||||
* Contains frame informations like position, size, existing of local color table and interlaced flag.
|
||||
* 2. [Local color table].
|
||||
* Only present if Image Descriptor contains local color table flag. Overrides global color table.
|
||||
* 3. Pixel data blocks.
|
||||
* LZW compressed pixel data.
|
||||
*/
|
||||
typedef Frame =
|
||||
{
|
||||
/**
|
||||
* X position of image on the Logical Screen
|
||||
*/
|
||||
var x:Int;
|
||||
|
||||
/**
|
||||
* Y position of image on the Logical Screen
|
||||
*/
|
||||
var y:Int;
|
||||
|
||||
/**
|
||||
* Width of image in pixels
|
||||
*/
|
||||
var width:Int;
|
||||
|
||||
/**
|
||||
* Height of image in pixels
|
||||
*/
|
||||
var height:Int;
|
||||
|
||||
/**
|
||||
* Is this image uses local color table?
|
||||
*/
|
||||
var localColorTable:Bool;
|
||||
|
||||
/**
|
||||
* Is this image written in interlace mode?
|
||||
* Note: The pixel data already deinterlaced and this flag presented only for information purpose (and for Writer when there is one).
|
||||
*/
|
||||
var interlaced:Bool;
|
||||
|
||||
/**
|
||||
* Is local color table sorted in order of decreasing priority?
|
||||
*/
|
||||
var sorted:Bool;
|
||||
|
||||
/**
|
||||
* Size of local color table
|
||||
*/
|
||||
var localColorTableSize:Int;
|
||||
|
||||
/**
|
||||
* Pixel data of frame. Stored as Indexed colors, 1 byte per pixel.
|
||||
*/
|
||||
var pixels:Bytes;
|
||||
|
||||
/**
|
||||
* Local color table used by frame. Stored as 3-byte RGB colors. If value is null, must be used global color table.
|
||||
*/
|
||||
var colorTable:ColorTable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Graphic Control Extension block, used for setting up disposal method, transparency, delay and user input.
|
||||
*/
|
||||
typedef GraphicControlExtension =
|
||||
{
|
||||
/**
|
||||
* Disposal method of frame.
|
||||
*/
|
||||
var disposalMethod:DisposalMethod;
|
||||
/**
|
||||
* Is image must wait for user input, before dispose?
|
||||
* This flag may be used by user-defined program but absolutely ignored by any Gif players.
|
||||
*/
|
||||
var userInput:Bool;
|
||||
/**
|
||||
* Is image have transparency?
|
||||
*/
|
||||
var hasTransparentColor:Bool;
|
||||
/**
|
||||
* Delay, before next image appears. Delay is in centiseconds (1 centisecond = 1/100 seconds).
|
||||
* Note: Some players (like FastStone) cut fraction of elapsed time when progressing to next frame which results in small timing error.
|
||||
* Recommended to use `time -= delay` instead of `time = 0`.
|
||||
*/
|
||||
var delay:Int;
|
||||
/**
|
||||
* Index in color table that used as transparent.
|
||||
*/
|
||||
var transparentIndex:Int;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extension for rendering text on Gif logical screen. It does not supported by major Gif decoders.
|
||||
* Font and text size decision is left to decoder. (recommended to decide based on grid/cell size)
|
||||
* Text must be rendered with one character at cell.
|
||||
* It's recommended to replace any characters less than 0x20 and greater than 0xf7 to be rendered as Space (0x20)
|
||||
*/
|
||||
typedef PlainTextExtension =
|
||||
{
|
||||
/**
|
||||
* X position of text grid on Logical Screen.
|
||||
*/
|
||||
var textGridX:Int;
|
||||
/**
|
||||
* Y position of text grid on Logical Screen.
|
||||
*/
|
||||
var textGridY:Int;
|
||||
/**
|
||||
* Width of text grid in pixels.
|
||||
*/
|
||||
var textGridWidth:Int;
|
||||
/**
|
||||
* Height of text grid in pixels.
|
||||
*/
|
||||
var textGridHeight:Int;
|
||||
/**
|
||||
* Width of character cell in text grid.
|
||||
*/
|
||||
var charCellWidth:Int;
|
||||
/**
|
||||
* Height of character cell in text grid.
|
||||
*/
|
||||
var charCellHeight:Int;
|
||||
/**
|
||||
* Foreground/character color index.
|
||||
*/
|
||||
var textForegroundColorIndex:Int;
|
||||
/**
|
||||
* Background color index.
|
||||
*/
|
||||
var textBackgroundColorIndex:Int;
|
||||
/**
|
||||
* Text to render.
|
||||
*/
|
||||
var text:String;
|
||||
}
|
||||
|
||||
/**
|
||||
* Logical screen descriptor of GIF file.
|
||||
* Contains very basic information about Gif.
|
||||
*/
|
||||
typedef LogicalScreenDescriptor =
|
||||
{
|
||||
/**
|
||||
* Width of GIF image in pixels
|
||||
*/
|
||||
var width:Int;
|
||||
|
||||
/**
|
||||
* Height of GIF image in pixels
|
||||
*/
|
||||
var height:Int;
|
||||
|
||||
/**
|
||||
* Is this file uses global color table?
|
||||
*/
|
||||
var hasGlobalColorTable:Bool;
|
||||
|
||||
/**
|
||||
* Specification:
|
||||
* Number of bits per primary color available
|
||||
to the original image, minus 1. This value represents the size of
|
||||
the entire palette from which the colors in the graphic were
|
||||
selected, not the number of colors actually used in the graphic.
|
||||
For example, if the value in this field is 3, then the palette of
|
||||
the original image had 4 bits per primary color available to create
|
||||
the image. This value should be set to indicate the richness of
|
||||
the original palette, even if not every color from the whole
|
||||
palette is available on the source machine.
|
||||
*/
|
||||
var colorResolution:Int;
|
||||
|
||||
/**
|
||||
* Specification:
|
||||
* Indicates whether the Global Color Table is sorted.
|
||||
If the flag is set, the Global Color Table is sorted, in order of
|
||||
decreasing importance. Typically, the order would be decreasing
|
||||
frequency, with most frequent color first. This assists a decoder,
|
||||
with fewer available colors, in choosing the best subset of colors;
|
||||
the decoder may use an initial segment of the table to render the
|
||||
graphic.
|
||||
*/
|
||||
var sorted:Bool;
|
||||
|
||||
/**
|
||||
* Size of global color table.
|
||||
*/
|
||||
var globalColorTableSize:Int;
|
||||
|
||||
/**
|
||||
* Background color index in global color table
|
||||
*/
|
||||
var backgroundColorIndex:Int;
|
||||
|
||||
/**
|
||||
* Factor used to compute an approximation of the aspect ratio of the pixel in the original image.
|
||||
*/
|
||||
var pixelAspectRatio:Float;
|
||||
}
|
||||
|
||||
/**
|
||||
* Version of Gif file.
|
||||
* The only 2 official versions is GIF87a and GIF89a.
|
||||
*/
|
||||
enum Version
|
||||
{
|
||||
/**
|
||||
* First version of Gif file format from May 1987.
|
||||
*
|
||||
* Note: The checking of unsupported blocks disabled by default to save some time. To enable supported blocks check set `yagp_strict_version_check` debug variable.
|
||||
*/
|
||||
GIF87a;
|
||||
/**
|
||||
* Second and actual version of Gif file format from July 1989.
|
||||
*/
|
||||
GIF89a;
|
||||
/**
|
||||
* Unknown version of Gif file.
|
||||
*/
|
||||
Unknown(version:String);
|
||||
}
|
||||
|
||||
/**
|
||||
* Disposal method of GIF frame.
|
||||
*/
|
||||
enum DisposalMethod
|
||||
{
|
||||
/**
|
||||
* The disposal method is unspecified. Action on demand of viewer.
|
||||
*
|
||||
* Mostly interpreted as NO_ACTION.
|
||||
*/
|
||||
UNSPECIFIED;
|
||||
/**
|
||||
* No action required.
|
||||
*/
|
||||
NO_ACTION;
|
||||
/**
|
||||
* Fill frame rectangle with background color.
|
||||
*
|
||||
* Usage note:
|
||||
* Most renderers clears to transparency instead of filling background color, when frame's transparent color index not equals to background color index.
|
||||
*/
|
||||
FILL_BACKGROUND;
|
||||
/**
|
||||
* Render previous state of gif as it before rendering disposing frame.
|
||||
*/
|
||||
RENDER_PREVIOUS;
|
||||
/**
|
||||
* Reserved disposal methods.
|
||||
*/
|
||||
UNDEFINED(index:Int);
|
||||
}
|
||||
359
leenkx/Sources/iron/format/gif/GifEncoder.hx
Normal file
359
leenkx/Sources/iron/format/gif/GifEncoder.hx
Normal file
@ -0,0 +1,359 @@
|
||||
package iron.format.gif;
|
||||
|
||||
/*
|
||||
* No copyright asserted on the source code of this class. May be used
|
||||
* for any purpose.
|
||||
*
|
||||
* Original code by Kevin Weiner, FM Software.
|
||||
* Adapted by Thomas Hourdel (https://github.com/Chman/Moments)
|
||||
* Ported to Haxe by Tilman Schmidt and Sven Bergstr├╢m
|
||||
*/
|
||||
|
||||
import haxe.io.UInt8Array;
|
||||
import haxe.io.BytesOutput;
|
||||
|
||||
@:enum abstract GifRepeat(Int)
|
||||
from Int to Int {
|
||||
var None = 0;
|
||||
var Infinite = -1;
|
||||
}
|
||||
|
||||
@:enum abstract GifQuality(Int)
|
||||
from Int to Int {
|
||||
var Best = 1;
|
||||
var VeryHigh = 10;
|
||||
var QuiteHigh = 20;
|
||||
var High = 35;
|
||||
var Mid = 50;
|
||||
var Low = 65;
|
||||
var QuiteLow = 80;
|
||||
var VeryLow = 90;
|
||||
var Worst = 100;
|
||||
}
|
||||
|
||||
class GifEncoder {
|
||||
|
||||
var width: Int;
|
||||
var height: Int;
|
||||
var framerate: Float = 24; // used if frame.delay < 0
|
||||
var repeat: Int = -1; // -1: infinite, 0: none, >0: repeat count
|
||||
|
||||
var colorDepth: Int = 8; // Number of bit planes
|
||||
var paletteSize: Int = 7; // Color table size (bits-1)
|
||||
var sampleInterval: Int = 10; // Default sample interval for quantizer
|
||||
|
||||
//caches
|
||||
var pixels: UInt8Array;
|
||||
var indexedPixels: UInt8Array; // Converted frame indexed to palette
|
||||
var colorTab: UInt8Array; // RGB palette
|
||||
var usedEntry: Array<Bool>; // Active palette entries
|
||||
//
|
||||
var nq: NeuQuant;
|
||||
var lzwEncoder: LzwEncoder;
|
||||
//internal
|
||||
var started: Bool = false;
|
||||
var first_frame: Bool = true;
|
||||
|
||||
//:todo: error handling could be better - but throw inside of another thread on cpp is too quiet
|
||||
|
||||
/** Allows a custom print handler for error messages.
|
||||
Defaults to Sys.println on sys targets, and trace otherwise. */
|
||||
public var print: Dynamic->Void;
|
||||
|
||||
// Public API
|
||||
|
||||
/** Construct a gif encoder with options:
|
||||
|
||||
frame width/height:
|
||||
Default is 0, required
|
||||
|
||||
framerate:
|
||||
This is used if an added frame has a delay that is negative.
|
||||
|
||||
repeat:
|
||||
Default is 0 (no repeat); -1 means play indefinitely.
|
||||
Use GifRepeat for clarity
|
||||
|
||||
quality:
|
||||
Sets quality of color quantization (conversion of images to
|
||||
the maximum 256 colors allowed by the GIF specification). Lower values (minimum = 1)
|
||||
produce better colors, but slow processing significantly. Higher values will speed
|
||||
up the quantization pass at the cost of lower image quality (maximum = 100). */
|
||||
public function new(
|
||||
_frame_width:Int,
|
||||
_frame_height:Int,
|
||||
_framerate:Float,
|
||||
_repeat:Int = GifRepeat.Infinite,
|
||||
_quality:Int = 10
|
||||
) {
|
||||
|
||||
#if sys
|
||||
print = Sys.println;
|
||||
#else
|
||||
print = function(v) { trace(v); }
|
||||
#end
|
||||
|
||||
width = _frame_width;
|
||||
height = _frame_height;
|
||||
framerate = _framerate;
|
||||
repeat = _repeat;
|
||||
|
||||
sampleInterval = Std.int(clamp(_quality, 1, 100));
|
||||
usedEntry = [for (i in 0...256) false];
|
||||
|
||||
pixels = new UInt8Array(width * height * 3);
|
||||
indexedPixels = new UInt8Array(width * height);
|
||||
|
||||
nq = new NeuQuant();
|
||||
lzwEncoder = new LzwEncoder();
|
||||
|
||||
} //new
|
||||
|
||||
public function start(output:BytesOutput) : Void {
|
||||
|
||||
if(output == null) {
|
||||
print("gif: start() output must not be null.");
|
||||
return;
|
||||
}
|
||||
|
||||
output.writeString("GIF89a");
|
||||
|
||||
write_LSD(output);
|
||||
|
||||
started = true;
|
||||
|
||||
} //start
|
||||
|
||||
public function add(output:BytesOutput, frame:GifFrame) : Void {
|
||||
|
||||
if(output == null) {
|
||||
print("gif: add() output must not be null.");
|
||||
return;
|
||||
}
|
||||
|
||||
if(!started) {
|
||||
print("gif: add() requires start to be called before adding frames.");
|
||||
return;
|
||||
}
|
||||
|
||||
var pixels = get_pixels(frame);
|
||||
analyze(pixels);
|
||||
|
||||
if(first_frame) {
|
||||
|
||||
write_palette(output);
|
||||
|
||||
if(repeat != GifRepeat.None) {
|
||||
write_NetscapeExt(output);
|
||||
}
|
||||
|
||||
first_frame = false;
|
||||
|
||||
} //first_frame
|
||||
|
||||
var delay = if(frame.delay < 0) {
|
||||
1.0/framerate;
|
||||
} else {
|
||||
frame.delay;
|
||||
}
|
||||
|
||||
write_GraphicControlExt(output, delay);
|
||||
write_image_desc(output, first_frame);
|
||||
|
||||
if(!first_frame) {
|
||||
write_palette(output);
|
||||
}
|
||||
|
||||
write_pixels(output);
|
||||
|
||||
} //add
|
||||
|
||||
public function commit(output:BytesOutput) : Void {
|
||||
|
||||
if(output == null) {
|
||||
print("gif: commit() output must be not null.");
|
||||
return;
|
||||
}
|
||||
|
||||
if(!started) {
|
||||
print("gif: commit() called without start() being called first.");
|
||||
return;
|
||||
}
|
||||
|
||||
output.writeByte(0x3b); // Gif trailer
|
||||
output.flush();
|
||||
output.close();
|
||||
|
||||
started = false;
|
||||
first_frame = true;
|
||||
|
||||
} //commit
|
||||
|
||||
//helpers
|
||||
|
||||
function get_pixels(frame:GifFrame):UInt8Array {
|
||||
|
||||
//if not flipped we can use the data as is
|
||||
if (!frame.flippedY) return frame.data;
|
||||
|
||||
//otherwise flip it, and return the cached array
|
||||
var stride = width * 3;
|
||||
for(y in 0...height) {
|
||||
var begin = (height - 1 - y) * stride;
|
||||
pixels.view.buffer.blit(y * stride, frame.data.view.buffer, begin, stride);
|
||||
}
|
||||
|
||||
return pixels;
|
||||
|
||||
} //get_pixels
|
||||
|
||||
function analyze(pixels:UInt8Array) {
|
||||
|
||||
// Create reduced palette
|
||||
nq.reset(pixels, pixels.length, sampleInterval);
|
||||
colorTab = nq.process();
|
||||
|
||||
// Map image pixels to new palette
|
||||
var k:Int = 0;
|
||||
for (i in 0...(width * height)) {
|
||||
var r = pixels[k++] & 0xff;
|
||||
var g = pixels[k++] & 0xff;
|
||||
var b = pixels[k++] & 0xff;
|
||||
var index = nq.map(r, g,b);
|
||||
usedEntry[index] = true;
|
||||
indexedPixels[i] = index;
|
||||
}
|
||||
|
||||
} //analyze
|
||||
|
||||
//writers
|
||||
//
|
||||
|
||||
/** Writes Logical Screen Descriptor. */
|
||||
function write_LSD(output:BytesOutput) {
|
||||
//
|
||||
|
||||
// Logical screen size
|
||||
output.writeInt16(width);
|
||||
output.writeInt16(height);
|
||||
|
||||
// Packed fields
|
||||
output.writeByte(0x80 | // 1 : global color table flag = 1 (gct used)
|
||||
0x70 | // 2-4 : color resolution = 7
|
||||
0x00 | // 5 : gct sort flag = 0
|
||||
paletteSize); // 6-8 : gct size
|
||||
|
||||
output.writeByte(0); // Background color index
|
||||
output.writeByte(0); // Pixel aspect ratio - assume 1:1
|
||||
|
||||
} //write_LSD
|
||||
|
||||
/** Writes Netscape application extension to define repeat count. */
|
||||
function write_NetscapeExt(output:BytesOutput):Void {
|
||||
|
||||
var repeats = repeat;
|
||||
if(repeats == GifRepeat.Infinite || repeats < 0) repeats = 0;
|
||||
if(repeats == GifRepeat.None) repeats = -1;
|
||||
|
||||
output.writeByte(0x21); // Extension introducer
|
||||
output.writeByte(0xff); // App extension label
|
||||
output.writeByte(11); // Block size
|
||||
output.writeString("NETSCAPE" + "2.0"); // App id + auth code
|
||||
output.writeByte(3); // Sub-block size
|
||||
output.writeByte(1); // Loop sub-block id
|
||||
output.writeInt16(repeats); // Loop count (extra iterations, 0=repeat forever)
|
||||
output.writeByte(0); // Block terminator
|
||||
|
||||
} //write_NetscapeExt
|
||||
|
||||
/** Write color table. */
|
||||
function write_palette(output:BytesOutput):Void {
|
||||
|
||||
output.write(colorTab.view.buffer);
|
||||
|
||||
var n:Int = (3 * 256) - colorTab.length;
|
||||
|
||||
for (i in 0...n) {
|
||||
output.writeByte(0);
|
||||
}
|
||||
|
||||
} //write_palette
|
||||
|
||||
/** Encodes and writes pixel data. */
|
||||
function write_pixels(output:BytesOutput):Void {
|
||||
|
||||
lzwEncoder.reset(indexedPixels, colorDepth);
|
||||
lzwEncoder.encode(output);
|
||||
|
||||
} //write_pixels
|
||||
|
||||
/** Writes Image Descriptor. */
|
||||
function write_image_desc(output:BytesOutput, first:Bool):Void {
|
||||
|
||||
output.writeByte(0x2c); // Image separator
|
||||
output.writeInt16(0); // Image position x = 0
|
||||
output.writeInt16(0); // Image position y = 0
|
||||
output.writeInt16(width); // Image width
|
||||
output.writeInt16(height); // Image height
|
||||
|
||||
//Write LCT, or GCT
|
||||
|
||||
if(first) {
|
||||
|
||||
output.writeByte(0); // No LCT - GCT is used for first (or only) frame
|
||||
|
||||
} else {
|
||||
|
||||
output.writeByte(0x80 | // 1 local color table 1=yes
|
||||
0 | // 2 interlace - 0=no
|
||||
0 | // 3 sorted - 0=no
|
||||
0 | // 4-5 reserved
|
||||
paletteSize); // 6-8 size of color table
|
||||
|
||||
} //else
|
||||
|
||||
} //write_image_desc
|
||||
|
||||
/** Writes Graphic Control Extension. Delay is in seconds, floored and converted to 1/100 of a second */
|
||||
function write_GraphicControlExt(output:BytesOutput, delay:Float):Void {
|
||||
|
||||
output.writeByte(0x21); // Extension introducer
|
||||
output.writeByte(0xf9); // GCE label
|
||||
output.writeByte(4); // data block size
|
||||
|
||||
// Packed fields
|
||||
output.writeByte(0 | // 1:3 reserved
|
||||
0 | // 4:6 disposal
|
||||
0 | // 7 user input - 0 = none
|
||||
0 ); // 8 transparency flag
|
||||
|
||||
//convert to 1/100 sec
|
||||
var delay_val = Math.floor(delay * 100);
|
||||
|
||||
output.writeInt16(delay_val); // Delay x 1/100 sec
|
||||
output.writeByte(0); // Transparent color index
|
||||
output.writeByte(0); // Block terminator
|
||||
|
||||
} //write_GraphicControlExt
|
||||
|
||||
/** Clamp a value between a and b and return the clamped version */
|
||||
static inline public function clamp(value:Float, a:Float, b:Float):Float
|
||||
{
|
||||
return ( value < a ) ? a : ( ( value > b ) ? b : value );
|
||||
}
|
||||
|
||||
} //GifEncoder
|
||||
|
||||
|
||||
typedef GifFrame = {
|
||||
|
||||
/** Delay of the frame in seconds. This value gets floored
|
||||
when encoded due to gif format requirements. If this value is negative,
|
||||
the default encoder frame rate will be used. */
|
||||
var delay: Float;
|
||||
/** Whether or not this frame should be flipped on the Y axis */
|
||||
var flippedY: Bool;
|
||||
/** Pixels data in unsigned bytes, rgb format */
|
||||
var data: UInt8Array;
|
||||
|
||||
}
|
||||
350
leenkx/Sources/iron/format/gif/LzwEncoder.hx
Normal file
350
leenkx/Sources/iron/format/gif/LzwEncoder.hx
Normal file
@ -0,0 +1,350 @@
|
||||
package iron.format.gif;
|
||||
|
||||
/*
|
||||
* No copyright asserted on the source code of this class. May be used
|
||||
* for any purpose, however, refer to the Unisys LZW patent for restrictions
|
||||
* on use of the associated LZWEncoder class :
|
||||
*
|
||||
* The Unisys patent expired on 20 June 2003 in the USA, in Europe it expired
|
||||
* on 18 June 2004, in Japan the patent expired on 20 June 2004 and in Canada
|
||||
* it expired on 7 July 2004. The U.S. IBM patent expired 11 August 2006, The
|
||||
* Software Freedom Law Center says that after 1 October 2006, there will be
|
||||
* no significant patent claims interfering with employment of the GIF format.
|
||||
*
|
||||
* Original code by Kevin Weiner, FM Software.
|
||||
* Adapted from Jef Poskanzer's Java port by way of J. M. G. Elliott.
|
||||
* Ported to Haxe by Tilman Schmidt and Sven Bergstr├╢m
|
||||
*
|
||||
*/
|
||||
|
||||
import haxe.io.Int32Array;
|
||||
import haxe.io.UInt8Array;
|
||||
|
||||
class LzwEncoder {
|
||||
static var EOF(default, never):Int = -1;
|
||||
|
||||
var pixAry:UInt8Array;
|
||||
var initCodeSize:Int;
|
||||
var curPixel:Int;
|
||||
|
||||
// GIFCOMPR.C - GIF Image compression routines
|
||||
//
|
||||
// Lempel-Ziv compression based on 'compress'. GIF modifications by
|
||||
// David Rowley (mgardi@watdcsu.waterloo.edu)
|
||||
|
||||
// General DEFINEs
|
||||
|
||||
static var BITS(default, never):Int = 12;
|
||||
|
||||
static var HSIZE(default, never):Int = 5003; // 80% occupancy
|
||||
|
||||
// GIF Image compression - modified 'compress'
|
||||
//
|
||||
// Based on: compress.c - File compression ala IEEE Computer, June 1984.
|
||||
//
|
||||
// By Authors: Spencer W. Thomas (decvax!harpo!utah-cs!utah-gr!thomas)
|
||||
// Jim McKie (decvax!mcvax!jim)
|
||||
// Steve Davies (decvax!vax135!petsd!peora!srd)
|
||||
// Ken Turkowski (decvax!decwrl!turtlevax!ken)
|
||||
// James A. Woods (decvax!ihnp4!ames!jaw)
|
||||
// Joe Orost (decvax!vax135!petsd!joe)
|
||||
|
||||
var n_bits:Int; // number of bits/code
|
||||
var maxbits:Int = BITS; // user settable max # bits/code
|
||||
var maxcode:Int; // maximum code, given n_bits
|
||||
var maxmaxcode:Int = 1 << BITS; // should NEVER generate this code
|
||||
|
||||
var htab:Int32Array;
|
||||
var codetab:Int32Array;
|
||||
|
||||
var hsize:Int = HSIZE; // for dynamic table sizing
|
||||
|
||||
var free_ent:Int = 0; // first unused entry
|
||||
|
||||
// block compression parameters -- after all codes are used up,
|
||||
// and compression rate changes, start over.
|
||||
var clear_flg:Bool = false;
|
||||
|
||||
// Algorithm: use open addressing double hashing (no chaining) on the
|
||||
// prefix code / next character combination. We do a variant of Knuth's
|
||||
// algorithm D (vol. 3, sec. 6.4) along with G. Knott's relatively-prime
|
||||
// secondary probe. Here, the modular division first probe is gives way
|
||||
// to a faster exclusive-or manipulation. Also do block compression with
|
||||
// an adaptive reset, whereby the code table is cleared when the compression
|
||||
// ratio decreases, but after the table fills. The variable-length output
|
||||
// codes are re-sized at this point, and a special CLEAR code is generated
|
||||
// for the decompressor. Late addition: construct the table according to
|
||||
// file size for noticeable speed improvement on small files. Please direct
|
||||
// questions about this implementation to ames!jaw.
|
||||
|
||||
var g_init_bits:Int;
|
||||
|
||||
var ClearCode:Int;
|
||||
var EOFCode:Int;
|
||||
|
||||
// output
|
||||
//
|
||||
// output the given code.
|
||||
// Inputs:
|
||||
// code: A n_bits-bit integer. If == -1, then EOF. This assumes
|
||||
// that n_bits =< wordsize - 1.
|
||||
// outputs:
|
||||
// outputs code to the file.
|
||||
// Assumptions:
|
||||
// Chars are 8 bits long.
|
||||
// Algorithm:
|
||||
// Maintain a BITS character long buffer (so that 8 codes will
|
||||
// fit in it exactly). Use the VAX insv instruction to insert each
|
||||
// code in turn. When the buffer fills up empty it and start over.
|
||||
|
||||
var cur_accum:Int = 0;
|
||||
var cur_bits:Int = 0;
|
||||
|
||||
var masks:Array<Int> =
|
||||
[
|
||||
0x0000,
|
||||
0x0001,
|
||||
0x0003,
|
||||
0x0007,
|
||||
0x000F,
|
||||
0x001F,
|
||||
0x003F,
|
||||
0x007F,
|
||||
0x00FF,
|
||||
0x01FF,
|
||||
0x03FF,
|
||||
0x07FF,
|
||||
0x0FFF,
|
||||
0x1FFF,
|
||||
0x3FFF,
|
||||
0x7FFF,
|
||||
0xFFFF ];
|
||||
|
||||
// Number of characters so far in this 'packet'
|
||||
var a_count:Int;
|
||||
|
||||
// Define the storage for the packet accumulator
|
||||
var accum:UInt8Array;
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
public function new()
|
||||
{
|
||||
htab = new Int32Array(HSIZE);
|
||||
codetab = new Int32Array(HSIZE);
|
||||
accum = new UInt8Array(256);
|
||||
}
|
||||
|
||||
//Reset the encoder to new pixel data and default values
|
||||
public function reset(pixels:UInt8Array, color_depth:Int) { //width and height used to be passed in though they were never used
|
||||
pixAry = pixels;
|
||||
initCodeSize = Std.int(Math.max(2, color_depth));
|
||||
|
||||
maxbits = BITS;
|
||||
maxmaxcode = 1 << BITS;
|
||||
hsize = HSIZE;
|
||||
free_ent = 0;
|
||||
clear_flg = false;
|
||||
cur_accum = 0;
|
||||
cur_bits = 0;
|
||||
}
|
||||
|
||||
// add a character to the end of the current packet, and if it is 254
|
||||
// characters, flush the packet to disk.
|
||||
function add(c:UInt, out:haxe.io.Output):Void
|
||||
{
|
||||
accum[a_count++] = c;
|
||||
if (a_count >= 254)
|
||||
flush(out);
|
||||
}
|
||||
|
||||
// Clear out the hash table
|
||||
|
||||
// table clear for block compress
|
||||
function clearTable(out:haxe.io.Output):Void
|
||||
{
|
||||
resetCodeTable(hsize);
|
||||
free_ent = ClearCode + 2;
|
||||
clear_flg = true;
|
||||
|
||||
output(ClearCode, out);
|
||||
}
|
||||
|
||||
// reset code table
|
||||
function resetCodeTable(hsize:Int):Void
|
||||
{
|
||||
for (i in 0...hsize)
|
||||
htab[i] = -1;
|
||||
}
|
||||
|
||||
function compress(init_bits:Int, out:haxe.io.Output):Void
|
||||
{
|
||||
var fcode:Int;
|
||||
var i:Int /* = 0 */;
|
||||
var c:Int;
|
||||
var ent:Int;
|
||||
var disp:Int;
|
||||
var hsize_reg:Int;
|
||||
var hshift:Int;
|
||||
|
||||
// Set up the globals: g_init_bits - initial number of bits
|
||||
g_init_bits = init_bits;
|
||||
|
||||
// Set up the necessary values
|
||||
clear_flg = false;
|
||||
n_bits = g_init_bits;
|
||||
maxcode = maxCode(n_bits);
|
||||
|
||||
ClearCode = 1 << (init_bits - 1);
|
||||
EOFCode = ClearCode + 1;
|
||||
free_ent = ClearCode + 2;
|
||||
|
||||
a_count = 0; // clear packet
|
||||
|
||||
ent = nextPixel();
|
||||
|
||||
hshift = 0;
|
||||
fcode = hsize;
|
||||
while (fcode < 65536) {
|
||||
++hshift;
|
||||
fcode *= 2;
|
||||
}
|
||||
|
||||
hshift = 8 - hshift; // set hash code range bound
|
||||
|
||||
hsize_reg = hsize;
|
||||
resetCodeTable(hsize_reg); // clear hash table
|
||||
|
||||
output(ClearCode, out);
|
||||
|
||||
while ((c = nextPixel()) != EOF)
|
||||
{
|
||||
fcode = (c << maxbits) + ent;
|
||||
i = (c << hshift) ^ ent; // xor hashing
|
||||
|
||||
if (htab[i] == fcode)
|
||||
{
|
||||
ent = codetab[i];
|
||||
continue;
|
||||
}
|
||||
else if (htab[i] >= 0) // non-empty slot
|
||||
{
|
||||
disp = hsize_reg - i; // secondary hash (after G. Knott)
|
||||
if (i == 0)
|
||||
disp = 1;
|
||||
do
|
||||
{
|
||||
if ((i -= disp) < 0)
|
||||
i += hsize_reg;
|
||||
|
||||
if (htab[i] == fcode)
|
||||
{
|
||||
ent = codetab[i];
|
||||
break;
|
||||
}
|
||||
} while (htab[i] >= 0);
|
||||
if (htab[i] == fcode) continue;
|
||||
}
|
||||
output(ent, out);
|
||||
ent = c;
|
||||
if (free_ent < maxmaxcode)
|
||||
{
|
||||
codetab[i] = free_ent++; // code -> hashtable
|
||||
htab[i] = fcode;
|
||||
}
|
||||
else
|
||||
clearTable(out);
|
||||
}
|
||||
// Put out the final code.
|
||||
output(ent, out);
|
||||
output(EOFCode, out);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
public function encode(os:haxe.io.Output):Void
|
||||
{
|
||||
os.writeByte( initCodeSize ); // write "initial code size" byte
|
||||
curPixel = 0;
|
||||
compress(initCodeSize + 1, os); // compress and write the pixel data
|
||||
os.writeByte(0); // write block terminator
|
||||
}
|
||||
|
||||
// flush the packet to disk, and reset the accumulator
|
||||
function flush(out:haxe.io.Output):Void
|
||||
{
|
||||
if (a_count > 0)
|
||||
{
|
||||
out.writeByte(a_count);
|
||||
out.writeBytes(accum.view.buffer, 0, a_count);
|
||||
a_count = 0;
|
||||
}
|
||||
}
|
||||
|
||||
inline function maxCode(n_bits:Int):Int
|
||||
{
|
||||
return (1 << n_bits) - 1;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// Return the next pixel from the image
|
||||
//----------------------------------------------------------------------------
|
||||
function nextPixel():Int
|
||||
{
|
||||
if (curPixel == pixAry.length)
|
||||
return EOF;
|
||||
|
||||
curPixel++;
|
||||
return pixAry[curPixel - 1] & 0xff;
|
||||
}
|
||||
|
||||
function output(code:Int, out:haxe.io.Output):Void
|
||||
{
|
||||
cur_accum &= masks[cur_bits];
|
||||
|
||||
if (cur_bits > 0)
|
||||
cur_accum |= (code << cur_bits);
|
||||
else
|
||||
cur_accum = code;
|
||||
|
||||
cur_bits += n_bits;
|
||||
|
||||
while (cur_bits >= 8)
|
||||
{
|
||||
add(cur_accum & 0xff, out);
|
||||
cur_accum >>= 8;
|
||||
cur_bits -= 8;
|
||||
}
|
||||
|
||||
// If the next entry is going to be too big for the code size,
|
||||
// then increase it, if possible.
|
||||
if (free_ent > maxcode || clear_flg)
|
||||
{
|
||||
if (clear_flg)
|
||||
{
|
||||
maxcode = maxCode(n_bits = g_init_bits);
|
||||
clear_flg = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
++n_bits;
|
||||
if (n_bits == maxbits)
|
||||
maxcode = maxmaxcode;
|
||||
else
|
||||
maxcode = maxCode(n_bits);
|
||||
}
|
||||
}
|
||||
|
||||
if (code == EOFCode)
|
||||
{
|
||||
// At EOF, write the rest of the buffer.
|
||||
while (cur_bits > 0)
|
||||
{
|
||||
add(cur_accum & 0xff, out);
|
||||
cur_accum >>= 8;
|
||||
cur_bits -= 8;
|
||||
}
|
||||
|
||||
flush(out);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
541
leenkx/Sources/iron/format/gif/NeuQuant.hx
Normal file
541
leenkx/Sources/iron/format/gif/NeuQuant.hx
Normal file
@ -0,0 +1,541 @@
|
||||
package iron.format.gif;
|
||||
|
||||
/*
|
||||
* Copyright (c) 1994 Anthony Dekker
|
||||
* Ported to Java by Kevin Weiner, FM Software
|
||||
* Ported to Haxe by Tilman Schmidt and Sven Bergstr├╢m
|
||||
*
|
||||
* NEUQUANT Neural-Net quantization algorithm by Anthony Dekker, 1994.
|
||||
* See "Kohonen neural networks for optimal colour quantization"
|
||||
* in "Network: Computation in Neural Systems" Vol. 5 (1994) pp 351-367.
|
||||
* for a discussion of the algorithm.
|
||||
*
|
||||
* Any party obtaining a copy of these files from the author, directly or
|
||||
* indirectly, is granted, free of charge, a full and unrestricted irrevocable,
|
||||
* world-wide, paid up, royalty-free, nonexclusive right and license to deal
|
||||
* in this software and documentation files (the "Software"), including without
|
||||
* limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons who receive
|
||||
* copies from any such party to do so, with the only requirement being
|
||||
* that this copyright notice remain intact.
|
||||
*
|
||||
*/
|
||||
|
||||
import haxe.io.Int32Array;
|
||||
import haxe.io.UInt8Array;
|
||||
|
||||
class NeuQuant {
|
||||
|
||||
inline static var netsize : Int = 256; // Number of colours used
|
||||
|
||||
// Four primes near 500 - assume no image has a length so large that it is divisible by all four primes
|
||||
inline static var prime1 : Int = 499;
|
||||
inline static var prime2 : Int = 491;
|
||||
inline static var prime3 : Int = 487;
|
||||
inline static var prime4 : Int = 503;
|
||||
|
||||
inline static var minpicturebytes : Int = (3 * prime4); // Minimum size for input image
|
||||
|
||||
// Network Definitions
|
||||
inline static var netbiasshift : Int = 4; // Bias for colour values
|
||||
inline static var ncycles : Int = 100; // No. of learning cycles
|
||||
|
||||
// Defs for freq and bias
|
||||
inline static var intbiasshift : Int = 16; // Bias for fractions
|
||||
inline static var intbias : Int = (1 << intbiasshift);
|
||||
inline static var gammashift : Int = 10; // Gamma = 1024
|
||||
inline static var gamma : Int = (1 << gammashift);
|
||||
inline static var betashift : Int = 10;
|
||||
inline static var beta : Int = (intbias >> betashift); // Beta = 1/1024
|
||||
inline static var betagamma : Int = (intbias << (gammashift - betashift));
|
||||
|
||||
// Defs for decreasing radius factor
|
||||
inline static var initrad : Int = (netsize >> 3); // For 256 cols, radius starts
|
||||
inline static var radiusbiasshift : Int = 6; // At 32.0 biased by 6 bits
|
||||
inline static var radiusbias : Int = (1 << radiusbiasshift);
|
||||
inline static var initradius : Int = (initrad * radiusbias); // And decreases by a
|
||||
inline static var radiusdec : Int = 30; // Factor of 1/30 each cycle
|
||||
|
||||
// Defs for decreasing alpha factor
|
||||
inline static var alphabiasshift : Int = 10; /* alpha starts at 1.0 */
|
||||
inline static var initalpha : Int = (1 << alphabiasshift);
|
||||
|
||||
// Radbias and alpharadbias used for radpower calculation
|
||||
inline static var radbiasshift : Int = 8;
|
||||
inline static var radbias : Int = (1 << radbiasshift);
|
||||
inline static var alpharadbshift : Int = (alphabiasshift + radbiasshift);
|
||||
inline static var alpharadbias : Int = (1 << alpharadbshift);
|
||||
|
||||
var alphadec:Int; // Biased by 10 bits
|
||||
|
||||
// Types and Global Variables
|
||||
|
||||
var thepicture: UInt8Array; // The input image itself
|
||||
var lengthcount: Int; // Lengthcount = H*W*3
|
||||
var samplefac: Int; // Sampling factor 1..30
|
||||
var network: Int32Array; // The network itself - [netsize][4]
|
||||
var netindex: Int32Array; // For network lookup - really 256
|
||||
var bias: Int32Array; // Bias array for learning
|
||||
var freq: Int32Array; // Frequency array for learning
|
||||
var radpower: Int32Array; // Radpower for precomputation
|
||||
var colormap_map: UInt8Array; // Cached color map array
|
||||
var colormap_index: Int32Array; // Cached color map index
|
||||
|
||||
public function new()
|
||||
{
|
||||
netindex = new Int32Array(256);
|
||||
bias = new Int32Array(netsize);
|
||||
freq = new Int32Array(netsize);
|
||||
radpower = new Int32Array(initrad);
|
||||
network = new Int32Array(netsize * 4);
|
||||
colormap_map = new UInt8Array(3 * netsize);
|
||||
colormap_index = new Int32Array(netsize);
|
||||
}
|
||||
|
||||
// Reset network in range (0,0,0) to (255,255,255) and set parameters
|
||||
public function reset(thepic:UInt8Array, len:Int, sample:Int):Void {
|
||||
thepicture = thepic;
|
||||
lengthcount = len;
|
||||
samplefac = sample;
|
||||
|
||||
for (i in 0...netsize) {
|
||||
network[i*4 + 0] = network[i*4 + 1] = network[i*4 + 2] = Std.int((i << (netbiasshift + 8)) / netsize);
|
||||
freq[i] = Std.int(intbias / netsize); // 1 / netsize
|
||||
bias[i] = 0; // allocated to zero?
|
||||
}
|
||||
}
|
||||
|
||||
public function colormap():UInt8Array
|
||||
{
|
||||
for(i in 0...netsize) {
|
||||
colormap_index[network[i * 4 + 3]] = i;
|
||||
}
|
||||
|
||||
var k:Int = 0;
|
||||
for (i in 0...netsize)
|
||||
{
|
||||
var j = colormap_index[i];
|
||||
colormap_map[k++] = network[j * 4];
|
||||
colormap_map[k++] = network[j * 4 + 1];
|
||||
colormap_map[k++] = network[j * 4 + 2];
|
||||
}
|
||||
|
||||
return colormap_map;
|
||||
}
|
||||
|
||||
// Insertion sort of network and building of netindex[0..255] (to do after unbias)
|
||||
public function inxbuild():Void
|
||||
{
|
||||
var i:Int;
|
||||
var j:Int;
|
||||
var smallpos:Int;
|
||||
var smallval:Int;
|
||||
var previouscol:Int;
|
||||
var startpos:Int;
|
||||
|
||||
previouscol = 0;
|
||||
startpos = 0;
|
||||
|
||||
for (i in 0...netsize)
|
||||
{
|
||||
smallpos = i;
|
||||
smallval = network[i*4 + 1]; // Index on g
|
||||
|
||||
// Find smallest in i..netsize-1
|
||||
for (j in (i + 1)...netsize)
|
||||
{
|
||||
if (network[j*4 + 1] < smallval)
|
||||
{
|
||||
smallpos = j;
|
||||
smallval = network[j*4 + 1]; // Index on g
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Swap p (i) and q (smallpos) entries
|
||||
if (i != smallpos)
|
||||
{
|
||||
j = network[smallpos*4 + 0];
|
||||
network[smallpos*4 + 0] = network[i*4 + 0];
|
||||
network[i*4 + 0] = j;
|
||||
j = network[smallpos*4 + 1];
|
||||
network[smallpos*4 + 1] = network[i*4 + 1];
|
||||
network[i*4 + 1] = j;
|
||||
j = network[smallpos*4 + 2];
|
||||
network[smallpos*4 + 2] = network[i*4 + 2];
|
||||
network[i*4 + 2] = j;
|
||||
j = network[smallpos*4 + 3];
|
||||
network[smallpos*4 + 3] = network[i*4 + 3];
|
||||
network[i*4 + 3] = j;
|
||||
}
|
||||
|
||||
// Smallval entry is now in position i
|
||||
if (smallval != previouscol)
|
||||
{
|
||||
netindex[previouscol] = (startpos + i) >> 1;
|
||||
|
||||
for (j in (previouscol + 1)...smallval)
|
||||
netindex[j] = i;
|
||||
|
||||
previouscol = smallval;
|
||||
startpos = i;
|
||||
}
|
||||
}
|
||||
|
||||
var maxnetpos = netsize - 1;
|
||||
|
||||
netindex[previouscol] = (startpos + maxnetpos) >> 1;
|
||||
|
||||
for (j in (previouscol + 1)...256)
|
||||
netindex[j] = maxnetpos;
|
||||
}
|
||||
|
||||
// Main learning Loop
|
||||
public function learn():Void
|
||||
{
|
||||
var i:Int;
|
||||
var j:Int;
|
||||
var b:Int;
|
||||
var g:Int;
|
||||
var r:Int;
|
||||
var radius:Int;
|
||||
var rad:Int;
|
||||
var alpha:Int;
|
||||
var step:Int;
|
||||
var delta:Int;
|
||||
var samplepixels:Int;
|
||||
|
||||
var p:UInt8Array;
|
||||
var pix:Int;
|
||||
var lim:Int;
|
||||
|
||||
if (lengthcount < minpicturebytes)
|
||||
samplefac = 1;
|
||||
|
||||
alphadec = 30 + Std.int((samplefac - 1) / 3);
|
||||
p = thepicture;
|
||||
pix = 0;
|
||||
lim = lengthcount;
|
||||
samplepixels = Std.int(lengthcount / (3 * samplefac));
|
||||
delta = Std.int(samplepixels / ncycles);
|
||||
alpha = initalpha;
|
||||
radius = initradius;
|
||||
|
||||
rad = radius >> radiusbiasshift;
|
||||
|
||||
if (rad <= 1)
|
||||
rad = 0;
|
||||
|
||||
for (i in 0...rad)
|
||||
radpower[i] = Std.int(alpha * (((rad * rad - i * i) * radbias) / (rad * rad)));
|
||||
|
||||
if (lengthcount < minpicturebytes)
|
||||
{
|
||||
step = 3;
|
||||
}
|
||||
else if ((lengthcount % prime1) != 0)
|
||||
{
|
||||
step = 3 * prime1;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((lengthcount % prime2) != 0)
|
||||
{
|
||||
step = 3 * prime2;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((lengthcount % prime3) != 0)
|
||||
step = 3 * prime3;
|
||||
else
|
||||
step = 3 * prime4;
|
||||
}
|
||||
}
|
||||
|
||||
i = 0;
|
||||
while (i < samplepixels)
|
||||
{
|
||||
b = (p[pix + 0] & 0xff) << netbiasshift;
|
||||
g = (p[pix + 1] & 0xff) << netbiasshift;
|
||||
r = (p[pix + 2] & 0xff) << netbiasshift;
|
||||
j = contest(b, g, r);
|
||||
|
||||
altersingle(alpha, j, b, g, r);
|
||||
|
||||
if (rad != 0)
|
||||
alterneigh(rad, j, b, g, r); // Alter neighbours
|
||||
|
||||
pix += step;
|
||||
|
||||
if (pix >= lim)
|
||||
pix -= lengthcount;
|
||||
|
||||
i++;
|
||||
|
||||
if (delta == 0)
|
||||
delta = 1;
|
||||
|
||||
if (i % delta == 0)
|
||||
{
|
||||
alpha -= Std.int(alpha / alphadec);
|
||||
radius -= Std.int(radius / radiusdec);
|
||||
rad = radius >> radiusbiasshift;
|
||||
|
||||
if (rad <= 1)
|
||||
rad = 0;
|
||||
|
||||
for (j in 0...rad)
|
||||
radpower[j] = Std.int(alpha * (((rad * rad - j * j) * radbias) / (rad * rad)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Search for BGR values 0..255 (after net is unbiased) and return colour index
|
||||
public function map(b:Int, g:Int, r:Int):Int
|
||||
{
|
||||
var i:Int;
|
||||
var j:Int;
|
||||
var dist:Int;
|
||||
var a:Int;
|
||||
var bestd:Int;
|
||||
var best:Int;
|
||||
|
||||
bestd = 1000; // Biggest possible dist is 256*3
|
||||
best = -1;
|
||||
i = netindex[g]; // Index on g
|
||||
j = i - 1; // Start at netindex[g] and work outwards
|
||||
|
||||
while ((i < netsize) || (j >= 0))
|
||||
{
|
||||
if (i < netsize)
|
||||
{
|
||||
dist = network[i*4 + 1] - g; // Inx key
|
||||
|
||||
if (dist >= bestd)
|
||||
{
|
||||
i = netsize; // Stop iter
|
||||
}
|
||||
else
|
||||
{
|
||||
if (dist < 0)
|
||||
dist = -dist;
|
||||
|
||||
a = network[i*4 + 0] - b;
|
||||
|
||||
if (a < 0)
|
||||
a = -a;
|
||||
|
||||
dist += a;
|
||||
|
||||
if (dist < bestd)
|
||||
{
|
||||
a = network[i*4 + 2] - r;
|
||||
|
||||
if (a < 0)
|
||||
a = -a;
|
||||
|
||||
dist += a;
|
||||
|
||||
if (dist < bestd)
|
||||
{
|
||||
bestd = dist;
|
||||
best = network[i*4 + 3];
|
||||
}
|
||||
}
|
||||
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
if (j >= 0)
|
||||
{
|
||||
dist = g - network[j*4 + 1]; // Inx key - reverse dif
|
||||
|
||||
if (dist >= bestd)
|
||||
{
|
||||
j = -1; // Stop iter
|
||||
}
|
||||
else
|
||||
{
|
||||
if (dist < 0)
|
||||
dist = -dist;
|
||||
|
||||
a = network[j*4 + 0] - b;
|
||||
|
||||
if (a < 0)
|
||||
a = -a;
|
||||
|
||||
dist += a;
|
||||
|
||||
if (dist < bestd)
|
||||
{
|
||||
a = network[j*4 + 2] - r;
|
||||
|
||||
if (a < 0)
|
||||
a = -a;
|
||||
|
||||
dist += a;
|
||||
|
||||
if (dist < bestd)
|
||||
{
|
||||
bestd = dist;
|
||||
best = network[j*4 + 3];
|
||||
}
|
||||
}
|
||||
|
||||
j--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return best;
|
||||
}
|
||||
|
||||
public function process():UInt8Array
|
||||
{
|
||||
learn();
|
||||
unbiasnet();
|
||||
inxbuild();
|
||||
return colormap();
|
||||
}
|
||||
|
||||
// Unbias network to give byte values 0..255 and record position i to prepare for sort
|
||||
public function unbiasnet():Void
|
||||
{
|
||||
for (i in 0...netsize)
|
||||
{
|
||||
network[i*4] >>= netbiasshift;
|
||||
network[i*4 + 1] >>= netbiasshift;
|
||||
network[i*4 + 2] >>= netbiasshift;
|
||||
network[i*4 + 3] = i; // Record colour no
|
||||
}
|
||||
}
|
||||
|
||||
// Move adjacent neurons by precomputed alpha*(1-((i-j)^2/[r]^2)) in radpower[|i-j|]
|
||||
function alterneigh(rad:Int, i:Int, b:Int, g:Int, r:Int):Void
|
||||
{
|
||||
var j:Int;
|
||||
var k:Int;
|
||||
var lo:Int;
|
||||
var hi:Int;
|
||||
var a:Int;
|
||||
var m:Int;
|
||||
|
||||
lo = i - rad;
|
||||
|
||||
if (lo < -1)
|
||||
lo = -1;
|
||||
|
||||
hi = i + rad;
|
||||
|
||||
if (hi > netsize)
|
||||
hi = netsize;
|
||||
|
||||
j = i + 1;
|
||||
k = i - 1;
|
||||
m = 1;
|
||||
|
||||
while ((j < hi) || (k > lo))
|
||||
{
|
||||
a = radpower[m++];
|
||||
|
||||
if (j < hi)
|
||||
{
|
||||
network[j * 4 + 0] -= Std.int((a * (network[j * 4 + 0] - b)) / alpharadbias);
|
||||
network[j * 4 + 1] -= Std.int((a * (network[j * 4 + 1] - g)) / alpharadbias);
|
||||
network[j * 4 + 2] -= Std.int((a * (network[j * 4 + 2] - r)) / alpharadbias);
|
||||
j++;
|
||||
}
|
||||
|
||||
if (k > lo)
|
||||
{
|
||||
network[k * 4 + 0] -= Std.int((a * (network[k * 4 + 0] - b)) / alpharadbias);
|
||||
network[k * 4 + 1] -= Std.int((a * (network[k * 4 + 1] - g)) / alpharadbias);
|
||||
network[k * 4 + 2] -= Std.int((a * (network[k * 4 + 2] - r)) / alpharadbias);
|
||||
k--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Move neuron i towards biased (b,g,r) by factor alpha
|
||||
function altersingle(alpha:Int, i:Int, b:Int, g:Int, r:Int):Void
|
||||
{
|
||||
/* Alter hit neuron */
|
||||
network[i*4 + 0] -= Std.int((alpha * (network[i*4 + 0] - b)) / initalpha);
|
||||
network[i*4 + 1] -= Std.int((alpha * (network[i*4 + 1] - g)) / initalpha);
|
||||
network[i*4 + 2] -= Std.int((alpha * (network[i*4 + 2] - r)) / initalpha);
|
||||
}
|
||||
|
||||
inline function make_abs(value:Int) : Int {
|
||||
var tmp = value >> 31;
|
||||
value ^= tmp;
|
||||
value += tmp & 1;
|
||||
return value;
|
||||
}
|
||||
|
||||
// Search for biased BGR values
|
||||
static inline var bestd_init = ~(1 << 31);
|
||||
function contest(b:Int, g:Int, r:Int):Int
|
||||
{
|
||||
// Finds closest neuron (min dist) and updates freq
|
||||
// Finds best neuron (min dist-bias) and returns position
|
||||
// For frequently chosen neurons, freq[i] is high and bias[i] is negative
|
||||
// bias[i] = gamma*((1/netsize)-freq[i])
|
||||
|
||||
var i:Int;
|
||||
var dist:Int;
|
||||
var a:Int;
|
||||
var biasdist:Int;
|
||||
var betafreq:Int;
|
||||
var bestpos:Int;
|
||||
var bestbiaspos:Int;
|
||||
var bestd:Int;
|
||||
var bestbiasd:Int;
|
||||
|
||||
bestd = bestd_init;
|
||||
bestbiasd = bestd;
|
||||
bestpos = -1;
|
||||
bestbiaspos = bestpos;
|
||||
|
||||
for (i in 0...netsize)
|
||||
{
|
||||
var i_n = i * 4;
|
||||
var b_i = i_n + 0;
|
||||
var g_i = i_n + 1;
|
||||
var r_i = i_n + 2;
|
||||
|
||||
var b_a = network[b_i];
|
||||
var g_a = network[g_i];
|
||||
var r_a = network[r_i];
|
||||
|
||||
b_a = make_abs(b_a - b);
|
||||
g_a = make_abs(g_a - g);
|
||||
r_a = make_abs(r_a - r);
|
||||
|
||||
dist = b_a + g_a + r_a;
|
||||
|
||||
if (dist < bestd)
|
||||
{
|
||||
bestd = dist;
|
||||
bestpos = i;
|
||||
}
|
||||
|
||||
biasdist = dist - ((bias[i]) >> (intbiasshift - netbiasshift));
|
||||
|
||||
if (biasdist < bestbiasd)
|
||||
{
|
||||
bestbiasd = biasdist;
|
||||
bestbiaspos = i;
|
||||
}
|
||||
|
||||
betafreq = (freq[i] >> betashift);
|
||||
freq[i] -= betafreq;
|
||||
bias[i] += (betafreq << gammashift);
|
||||
}
|
||||
|
||||
freq[bestpos] += beta;
|
||||
bias[bestpos] -= betagamma;
|
||||
return bestbiaspos;
|
||||
}
|
||||
|
||||
}
|
||||
346
leenkx/Sources/iron/format/gif/Reader.hx
Normal file
346
leenkx/Sources/iron/format/gif/Reader.hx
Normal file
@ -0,0 +1,346 @@
|
||||
package iron.format.gif;
|
||||
import iron.format.gif.Data;
|
||||
import haxe.io.Bytes;
|
||||
import haxe.io.BytesOutput;
|
||||
import haxe.io.Input;
|
||||
|
||||
/**
|
||||
* ...
|
||||
* @author Yanrishatum
|
||||
*/
|
||||
class Reader
|
||||
{
|
||||
|
||||
private var i:Input;
|
||||
|
||||
public function new(i:Input)
|
||||
{
|
||||
this.i = i;
|
||||
i.bigEndian = false;
|
||||
}
|
||||
|
||||
public function read():Data
|
||||
{
|
||||
for (b in [71, 73, 70])
|
||||
{
|
||||
if (i.readByte() != b) throw "Invalid header";
|
||||
}
|
||||
|
||||
var gifVer:String = i.readString(3);
|
||||
var version:Version = Version.GIF89a;
|
||||
switch(gifVer)
|
||||
{
|
||||
case "87a": version = Version.GIF87a;
|
||||
case "89a": version = Version.GIF89a;
|
||||
default: version = Version.Unknown(gifVer);
|
||||
}
|
||||
|
||||
// Logical screen descriptor.
|
||||
var width:Int = i.readUInt16();
|
||||
var height:Int = i.readUInt16();
|
||||
var packedField:Int = i.readByte();
|
||||
var bgIndex:Int = i.readByte();
|
||||
var pixelAspectRatio:Float = i.readByte();
|
||||
if (pixelAspectRatio != 0) pixelAspectRatio = (pixelAspectRatio + 15) / 64;
|
||||
else pixelAspectRatio = 1;
|
||||
|
||||
var lsd:LogicalScreenDescriptor =
|
||||
{
|
||||
width: width,
|
||||
height: height,
|
||||
hasGlobalColorTable: (packedField & 128) == 128,
|
||||
colorResolution: (packedField & 112) >>> 4,
|
||||
sorted: (packedField & 8) == 8,
|
||||
globalColorTableSize: 2 << (packedField & 7),
|
||||
backgroundColorIndex: bgIndex,
|
||||
pixelAspectRatio: pixelAspectRatio
|
||||
}
|
||||
|
||||
var gct:ColorTable = null;
|
||||
if (lsd.hasGlobalColorTable) gct = readColorTable(lsd.globalColorTableSize);
|
||||
|
||||
var blocks:List<Block> = new List();
|
||||
|
||||
while (true)
|
||||
{
|
||||
var b:Block = readBlock();
|
||||
blocks.add(b);
|
||||
if (b == Block.BEOF) break;
|
||||
}
|
||||
|
||||
return
|
||||
{
|
||||
version: version,
|
||||
logicalScreenDescriptor: lsd,
|
||||
globalColorTable: gct,
|
||||
blocks: blocks
|
||||
}
|
||||
}
|
||||
|
||||
private function readBlock():Block
|
||||
{
|
||||
var blockID:Int = i.readByte();
|
||||
switch(blockID)
|
||||
{
|
||||
case 0x2C:
|
||||
// Image
|
||||
return readImage();
|
||||
case 0x21:
|
||||
// Extension
|
||||
return readExtension();
|
||||
case 0x3B:
|
||||
return Block.BEOF;
|
||||
}
|
||||
// The behaviour of taking unknown block ID is unspecified.
|
||||
return Block.BEOF;
|
||||
}
|
||||
|
||||
private function readImage():Block
|
||||
{
|
||||
var x:Int = i.readUInt16();
|
||||
var y:Int = i.readUInt16();
|
||||
var width:Int = i.readUInt16();
|
||||
var height:Int = i.readUInt16();
|
||||
var packed:Int = i.readByte();
|
||||
var localColorTable:Bool = (packed & 128) == 128;
|
||||
var interlaced:Bool = (packed & 64) == 64;
|
||||
var sorted:Bool = (packed & 32) == 32;
|
||||
var localColorTableSize:Int = 2 << (packed & 7);
|
||||
|
||||
var lct:ColorTable = null;
|
||||
if (localColorTable) lct = readColorTable(localColorTableSize);
|
||||
|
||||
return Block.BFrame(
|
||||
{
|
||||
x: x,
|
||||
y: y,
|
||||
width: width,
|
||||
height: height,
|
||||
localColorTable: localColorTable,
|
||||
interlaced:interlaced,
|
||||
sorted:sorted,
|
||||
localColorTableSize:localColorTableSize,
|
||||
pixels:readPixels(width, height, interlaced),
|
||||
colorTable:lct
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
private function readPixels(width:Int, height:Int, interlaced:Bool):Bytes
|
||||
{
|
||||
var input:Input = this.i;
|
||||
|
||||
var pixelsCount:Int = width * height;
|
||||
var pixels:Bytes = Bytes.alloc(pixelsCount);
|
||||
|
||||
var minCodeSize:Int = input.readByte();
|
||||
|
||||
var blockSize:Int = input.readByte() - 1;
|
||||
var bits:Int = input.readByte();
|
||||
var bitsCount:Int = 8;
|
||||
|
||||
var clearCode:Int = 1 << minCodeSize;
|
||||
var eoiCode:Int = clearCode + 1;
|
||||
|
||||
var codeSize:Int = minCodeSize + 1;
|
||||
var codeSizeLimit:Int = 1 << codeSize;
|
||||
var codeMask = codeSizeLimit - 1;
|
||||
|
||||
|
||||
var baseDict:Array<Array<Int>> = new Array();
|
||||
for (i in 0...clearCode) baseDict[i] = [i];
|
||||
|
||||
var dict:Array<Array<Int>> = new Array();
|
||||
var dictLen:Int = clearCode + 2;
|
||||
var newRecord:Array<Int>;
|
||||
|
||||
var i:Int = 0;
|
||||
var code:Int = 0;
|
||||
var last:Int;
|
||||
|
||||
while (i < pixelsCount)
|
||||
{
|
||||
last = code;
|
||||
while (bitsCount < codeSize)
|
||||
{
|
||||
if (blockSize == 0) break;
|
||||
bits |= input.readByte() << bitsCount;
|
||||
bitsCount += 8;
|
||||
blockSize--;
|
||||
if (blockSize == 0) blockSize = input.readByte();
|
||||
}
|
||||
code = bits & codeMask;
|
||||
bits >>= codeSize;
|
||||
bitsCount -= codeSize;
|
||||
|
||||
if (code == clearCode)
|
||||
{
|
||||
dict = baseDict.copy();
|
||||
dictLen = clearCode + 2;
|
||||
codeSize = minCodeSize + 1;
|
||||
codeSizeLimit = (1 << codeSize);
|
||||
codeMask = codeSizeLimit - 1;
|
||||
continue;
|
||||
}
|
||||
if (code == eoiCode) break;
|
||||
|
||||
if (code < dictLen)
|
||||
{
|
||||
if (last != clearCode)
|
||||
{
|
||||
newRecord = dict[last].copy();
|
||||
newRecord.push(dict[code][0]);
|
||||
dict[dictLen++] = newRecord;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (code != dictLen) throw 'Invalid LZW code. Excepted: $dictLen, got: $code';
|
||||
newRecord = dict[last].copy();
|
||||
newRecord.push(newRecord[0]);
|
||||
dict[dictLen++] = newRecord;
|
||||
}
|
||||
|
||||
newRecord = dict[code];
|
||||
for (item in newRecord) pixels.set(i++, item);
|
||||
|
||||
if (dictLen == codeSizeLimit && codeSize < 12)
|
||||
{
|
||||
codeSize++;
|
||||
codeSizeLimit = (1 << codeSize);
|
||||
codeMask = codeSizeLimit - 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Just in case
|
||||
while (blockSize > 0)
|
||||
{
|
||||
input.readByte();
|
||||
blockSize--;
|
||||
if (blockSize == 0) blockSize = input.readByte();
|
||||
}
|
||||
|
||||
while (i < pixelsCount) pixels.set(i++, 0);
|
||||
if (interlaced)
|
||||
{
|
||||
var buffer:Bytes = Bytes.alloc(pixelsCount);
|
||||
var offset:Int = deinterlace(pixels, buffer, 8, 0, 0 , width, height); // Every 8 line with start at 0
|
||||
offset = deinterlace(pixels, buffer, 8, 4, offset, width, height); // Every 8 line with start at 4
|
||||
offset = deinterlace(pixels, buffer, 4, 2, offset, width, height); // Every 4 line with start at 2
|
||||
deinterlace(pixels, buffer, 2, 1, offset, width, height); // Every 2 line with start at 1
|
||||
pixels = buffer;
|
||||
}
|
||||
return pixels;
|
||||
}
|
||||
|
||||
private function deinterlace(input:Bytes, output:Bytes, step:Int, y:Int, offset:Int, width:Int, height:Int):Int
|
||||
{
|
||||
while (y < height)
|
||||
{
|
||||
output.blit(y * width, input, offset, width);
|
||||
offset += width;
|
||||
y += step;
|
||||
}
|
||||
return offset;
|
||||
}
|
||||
|
||||
private function readExtension():Block
|
||||
{
|
||||
var subId:Int = i.readByte();
|
||||
|
||||
switch(subId)
|
||||
{
|
||||
case 0xF9:
|
||||
// Graphics Control Extension
|
||||
if (i.readByte() != 4) throw "Incorrect Graphic Control Extension block size!";
|
||||
var packed:Int = i.readByte();
|
||||
var disposalMethod:DisposalMethod = switch ( (packed & 28) >> 2)
|
||||
{
|
||||
case 0: DisposalMethod.UNSPECIFIED;
|
||||
case 1: DisposalMethod.NO_ACTION;
|
||||
case 2: DisposalMethod.FILL_BACKGROUND;
|
||||
case 3: DisposalMethod.RENDER_PREVIOUS;
|
||||
default: DisposalMethod.UNDEFINED((packed & 28) >> 2);
|
||||
};
|
||||
var b:Block = Block.BExtension(Extension.EGraphicControl(
|
||||
{
|
||||
disposalMethod:disposalMethod,
|
||||
userInput: (packed & 2) == 2,
|
||||
hasTransparentColor: (packed & 1) == 1,
|
||||
delay: i.readUInt16(),
|
||||
transparentIndex: i.readByte()
|
||||
}));
|
||||
i.readByte(); // Terminator
|
||||
return b;
|
||||
case 0x01:
|
||||
// Text block
|
||||
// Exists only on paper, nobody ever used it.
|
||||
if (i.readByte() != 12) throw "Incorrect size of Plain Text Extension introducer block.";
|
||||
return Block.BExtension(Extension.EText(
|
||||
{
|
||||
textGridX: i.readUInt16(),
|
||||
textGridY: i.readUInt16(),
|
||||
textGridWidth: i.readUInt16(),
|
||||
textGridHeight: i.readUInt16(),
|
||||
charCellWidth: i.readByte(),
|
||||
charCellHeight: i.readByte(),
|
||||
textForegroundColorIndex: i.readByte(),
|
||||
textBackgroundColorIndex: i.readByte(),
|
||||
text: readBlocks().toString()
|
||||
}));
|
||||
case 0xFE:
|
||||
// Commentary
|
||||
return Block.BExtension(Extension.EComment(readBlocks().toString()));
|
||||
case 0xFF:
|
||||
// Application extension
|
||||
return readApplicationExtension();
|
||||
default:
|
||||
return Block.BExtension(Extension.EUnknown(subId, readBlocks()));
|
||||
}
|
||||
}
|
||||
|
||||
private function readApplicationExtension():Block
|
||||
{
|
||||
if (i.readByte() != 11) throw "Incorrect size of Application Extension introducer block.";
|
||||
var name:String = i.readString(8);
|
||||
var version:String = i.readString(3);
|
||||
var data:Bytes = readBlocks();
|
||||
if (name == "NETSCAPE" && version == "2.0" && data.get(0) == 1)
|
||||
{
|
||||
return Block.BExtension(Extension.EApplicationExtension(ApplicationExtension.AENetscapeLooping(data.get(1) | (data.get(2) << 8))));
|
||||
}
|
||||
return Block.BExtension(Extension.EApplicationExtension(ApplicationExtension.AEUnknown(name, version, data)));
|
||||
}
|
||||
|
||||
private inline function readBlocks():Bytes
|
||||
{
|
||||
var buffer:BytesOutput = new BytesOutput();
|
||||
var bytes:Bytes = Bytes.alloc(255);
|
||||
var len:Int = i.readByte();
|
||||
while (len != 0)
|
||||
{
|
||||
i.readBytes(bytes, 0, len);
|
||||
buffer.writeBytes(bytes, 0, len);
|
||||
len = i.readByte();
|
||||
}
|
||||
buffer.flush();
|
||||
bytes = buffer.getBytes();
|
||||
buffer.close();
|
||||
return bytes;
|
||||
}
|
||||
|
||||
private function readColorTable(size:Int):ColorTable
|
||||
{
|
||||
size *= 3;
|
||||
var output:ColorTable = ColorTable.alloc(size);
|
||||
var c:Int = 0;
|
||||
while (c < size)
|
||||
{
|
||||
output.set(c , i.readByte()); // R
|
||||
output.set(c + 1, i.readByte()); // G
|
||||
output.set(c + 2, i.readByte()); // B
|
||||
c += 3;
|
||||
}
|
||||
return output;
|
||||
}
|
||||
}
|
||||
390
leenkx/Sources/iron/format/gif/Tools.hx
Normal file
390
leenkx/Sources/iron/format/gif/Tools.hx
Normal file
@ -0,0 +1,390 @@
|
||||
package iron.format.gif;
|
||||
|
||||
import iron.format.gif.Data;
|
||||
import haxe.io.Bytes;
|
||||
import haxe.io.BytesData;
|
||||
|
||||
/**
|
||||
* Tools for gif data.
|
||||
* @author Yanrishatum
|
||||
*/
|
||||
class Tools
|
||||
{
|
||||
/**
|
||||
* Returns amount of frames in Gif data.
|
||||
*/
|
||||
public static function framesCount(data:Data):Int
|
||||
{
|
||||
var frames:Int = 0;
|
||||
for (block in data.blocks)
|
||||
{
|
||||
switch(block)
|
||||
{
|
||||
case Block.BFrame(_):
|
||||
frames++;
|
||||
default :
|
||||
}
|
||||
}
|
||||
return frames;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns frame at given index.
|
||||
* @param data Gif data.
|
||||
* @param frameIndex Index of frame.
|
||||
* @return Frame at given index or null, if there is no frame at that index.
|
||||
*/
|
||||
public static function frame(data:Data, frameIndex:Int):Frame
|
||||
{
|
||||
var counter:Int = 0;
|
||||
for (block in data.blocks)
|
||||
{
|
||||
switch (block)
|
||||
{
|
||||
case Block.BFrame(frame):
|
||||
if (counter == frameIndex) return frame;
|
||||
counter++;
|
||||
default :
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns Graphic Control extension for frame at given index.
|
||||
* @param data Gif data.
|
||||
* @param frameIndex Index of frame.
|
||||
* @return GCE extension if it is exists for given frame, null otherwise.
|
||||
*/
|
||||
public static function graphicControl(data:Data, frameIndex:Int):GraphicControlExtension
|
||||
{
|
||||
var counter:Int = 0;
|
||||
var gce:GraphicControlExtension = null;
|
||||
for (block in data.blocks)
|
||||
{
|
||||
switch (block)
|
||||
{
|
||||
case Block.BFrame(frame):
|
||||
if (counter == frameIndex) return gce;
|
||||
gce = null;
|
||||
counter++;
|
||||
case Block.BExtension(Extension.EGraphicControl(g)):
|
||||
gce = g;
|
||||
default :
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
//==========================================================
|
||||
// Extracting.
|
||||
//==========================================================
|
||||
|
||||
/**
|
||||
* Extracts frame pixel data in Blue-Green-Red-Alpha pixel format.
|
||||
* This function extracts only exact frame and does put previous frame pixel data into resulting Bytes. Note that frame size may not equal to Gif logical screen size.
|
||||
* @param data Gif data.
|
||||
* @param frameIndex Frame index.
|
||||
* @return BGRA pixel data with dimensions equals to specified Frame size. If frame does not present in Gif data returns null.
|
||||
*/
|
||||
public static function extractBGRA(data:Data, frameIndex:Int):Bytes
|
||||
{
|
||||
var gce:GraphicControlExtension = null;
|
||||
var frameCaret:Int = 0;
|
||||
for (block in data.blocks)
|
||||
{
|
||||
switch (block)
|
||||
{
|
||||
case Block.BExtension(ext):
|
||||
switch(ext)
|
||||
{
|
||||
case Extension.EGraphicControl(g):
|
||||
gce = g;
|
||||
default:
|
||||
}
|
||||
case Block.BFrame(frame):
|
||||
if (frameCaret == frameIndex)
|
||||
{
|
||||
var bytes:Bytes = Bytes.alloc(frame.width * frame.height * 4);
|
||||
var ct:Bytes = frame.localColorTable ? frame.colorTable : data.globalColorTable;
|
||||
if (ct == null) throw "Frame does not have a color table!";
|
||||
var transparentIndex:Int = gce != null && gce.hasTransparentColor ? gce.transparentIndex * 3 : -1;
|
||||
var writeCaret:Int = 0;
|
||||
for (i in 0...frame.pixels.length)
|
||||
{
|
||||
var index:Int = frame.pixels.get(i) * 3;
|
||||
bytes.set(writeCaret , ct.get(index + 2)); // B
|
||||
bytes.set(writeCaret + 1, ct.get(index + 1)); // G
|
||||
bytes.set(writeCaret + 2, ct.get(index )); // R
|
||||
if (transparentIndex == index) bytes.set(writeCaret + 3, 0); // A = 0
|
||||
else bytes.set(writeCaret + 3, 0xFF); // A = FF
|
||||
|
||||
writeCaret += 4;
|
||||
}
|
||||
return bytes;
|
||||
}
|
||||
frameCaret++;
|
||||
gce = null;
|
||||
default:
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts frame pixel data in Red-Green-Blue-Alpha pixel format.
|
||||
* This function extracts only exact frame and does put previous frame pixel data into resulting Bytes. Note that frame size may not equal to Gif logical screen size.
|
||||
* @param data Gif data.
|
||||
* @param frameIndex Frame index.
|
||||
* @return RGBA pixel data with dimensions equals to specified Frame size. If frame does not present in Gif data returns null.
|
||||
*/
|
||||
public static function extractRGBA(data:Data, frameIndex:Int):Bytes
|
||||
{
|
||||
var gce:GraphicControlExtension = null;
|
||||
var frameCaret:Int = 0;
|
||||
for (block in data.blocks)
|
||||
{
|
||||
switch (block)
|
||||
{
|
||||
case Block.BExtension(ext):
|
||||
switch(ext)
|
||||
{
|
||||
case Extension.EGraphicControl(g):
|
||||
gce = g;
|
||||
default:
|
||||
}
|
||||
case Block.BFrame(frame):
|
||||
if (frameCaret == frameIndex)
|
||||
{
|
||||
var bytes:Bytes = Bytes.alloc(frame.width * frame.height * 4);
|
||||
var ct:Bytes = frame.localColorTable ? frame.colorTable : data.globalColorTable;
|
||||
if (ct == null) throw "Frame does not have a color table!";
|
||||
var transparentIndex:Int = gce != null && gce.hasTransparentColor ? gce.transparentIndex * 3 : -1;
|
||||
var writeCaret:Int = 0;
|
||||
for (i in 0...frame.pixels.length)
|
||||
{
|
||||
var index:Int = frame.pixels.get(i) * 3;
|
||||
bytes.set(writeCaret , ct.get(index )); // R
|
||||
bytes.set(writeCaret + 1, ct.get(index + 1)); // G
|
||||
bytes.set(writeCaret + 2, ct.get(index + 2)); // B
|
||||
if (transparentIndex == index) bytes.set(writeCaret + 3, 0); // A = 0
|
||||
else bytes.set(writeCaret + 3, 0xFF); // A = FF
|
||||
|
||||
writeCaret += 4;
|
||||
}
|
||||
return bytes;
|
||||
}
|
||||
frameCaret++;
|
||||
gce = null;
|
||||
default:
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts full Gif pixel data to specified frame in Blue-Green-Red-Alpha pixel format.
|
||||
* This functions returns full representation of frame including rendering of all other frames before.
|
||||
* @param data Gif data.
|
||||
* @param frameIndex Frame index.
|
||||
* @return BGRA pixel data with dimensions equals to Gif logical screen with full pixel data of Gif image at specified frame.
|
||||
*/
|
||||
public static function extractFullBGRA(data:Data, frameIndex:Int):Bytes
|
||||
{
|
||||
var gce:GraphicControlExtension = null;
|
||||
var frameCaret:Int = 0;
|
||||
|
||||
var bytes:Bytes = Bytes.alloc(data.logicalScreenDescriptor.width* data.logicalScreenDescriptor.height * 4);
|
||||
|
||||
for (block in data.blocks)
|
||||
{
|
||||
switch (block)
|
||||
{
|
||||
case Block.BExtension(ext):
|
||||
switch(ext)
|
||||
{
|
||||
case Extension.EGraphicControl(g):
|
||||
gce = g;
|
||||
default:
|
||||
}
|
||||
case Block.BFrame(frame):
|
||||
var ct:Bytes = frame.localColorTable ? frame.colorTable : data.globalColorTable;
|
||||
if (ct == null) throw "Frame does not have a color table!";
|
||||
var transparentIndex:Int = gce != null && gce.hasTransparentColor ? gce.transparentIndex * 3 : -1;
|
||||
var pixels:Bytes = frame.pixels;
|
||||
var x:Int = 0;
|
||||
var writeCaret:Int = (frame.y * data.logicalScreenDescriptor.width + frame.x) * 4;
|
||||
var lineSkip:Int = (data.logicalScreenDescriptor.width - frame.width) * 4 + 4;
|
||||
|
||||
var disposalMethod:DisposalMethod = frameCaret != frameIndex && gce != null ? gce.disposalMethod : DisposalMethod.NO_ACTION;
|
||||
|
||||
switch (disposalMethod)
|
||||
{
|
||||
case DisposalMethod.RENDER_PREVIOUS:
|
||||
// Do not render frame at all
|
||||
case DisposalMethod.FILL_BACKGROUND:
|
||||
for (i in 0...pixels.length)
|
||||
{
|
||||
bytes.set(writeCaret , 0); // B
|
||||
bytes.set(writeCaret + 1, 0); // G
|
||||
bytes.set(writeCaret + 2, 0); // R
|
||||
bytes.set(writeCaret + 3, 0); // A
|
||||
|
||||
if (++x == frame.width)
|
||||
{
|
||||
x = 0;
|
||||
writeCaret += lineSkip;
|
||||
}
|
||||
else writeCaret += 4;
|
||||
}
|
||||
default:
|
||||
for (i in 0...pixels.length)
|
||||
{
|
||||
var index:Int = pixels.get(i) * 3;
|
||||
if (transparentIndex != index) // Render only if pixel non-transparent
|
||||
{
|
||||
bytes.set(writeCaret , ct.get(index + 2)); // B
|
||||
bytes.set(writeCaret + 1, ct.get(index + 1)); // G
|
||||
bytes.set(writeCaret + 2, ct.get(index )); // R
|
||||
bytes.set(writeCaret + 3, 0xFF); // A
|
||||
}
|
||||
|
||||
if (++x == frame.width)
|
||||
{
|
||||
x = 0;
|
||||
writeCaret += lineSkip;
|
||||
}
|
||||
else writeCaret += 4;
|
||||
}
|
||||
}
|
||||
|
||||
if (frameCaret == frameIndex) return bytes;
|
||||
frameCaret++;
|
||||
gce = null;
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
return bytes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts full Gif pixel data to specified frame in Red-Green-Blue-Alpha pixel format.
|
||||
* This functions returns full representation of frame including rendering of all other frames before.
|
||||
* @param data Gif data.
|
||||
* @param frameIndex Frame index.
|
||||
* @return RGBA pixel data with dimensions equals to Gif logical screen with full pixel data of Gif image at specified frame.
|
||||
*/
|
||||
public static function extractFullRGBA(data:Data, frameIndex:Int):Bytes
|
||||
{
|
||||
var gce:GraphicControlExtension = null;
|
||||
var frameCaret:Int = 0;
|
||||
|
||||
var bytes:Bytes = Bytes.alloc(data.logicalScreenDescriptor.width* data.logicalScreenDescriptor.height * 4);
|
||||
|
||||
for (block in data.blocks)
|
||||
{
|
||||
switch (block)
|
||||
{
|
||||
case Block.BExtension(ext):
|
||||
switch(ext)
|
||||
{
|
||||
case Extension.EGraphicControl(g):
|
||||
gce = g;
|
||||
default:
|
||||
}
|
||||
case Block.BFrame(frame):
|
||||
var ct:Bytes = frame.localColorTable ? frame.colorTable : data.globalColorTable;
|
||||
if (ct == null) throw "Frame does not have a color table!";
|
||||
var transparentIndex:Int = gce != null && gce.hasTransparentColor ? gce.transparentIndex * 3 : -1;
|
||||
var pixels:Bytes = frame.pixels;
|
||||
var x:Int = 0;
|
||||
var writeCaret:Int = (frame.y * data.logicalScreenDescriptor.width + frame.x) * 4;
|
||||
var lineSkip:Int = (data.logicalScreenDescriptor.width - frame.width) * 4 + 4;
|
||||
|
||||
var disposalMethod:DisposalMethod = frameCaret != frameIndex && gce != null ? gce.disposalMethod : DisposalMethod.NO_ACTION;
|
||||
|
||||
switch (disposalMethod)
|
||||
{
|
||||
case DisposalMethod.RENDER_PREVIOUS:
|
||||
// Do not render frame at all
|
||||
case DisposalMethod.FILL_BACKGROUND:
|
||||
for (i in 0...pixels.length)
|
||||
{
|
||||
bytes.set(writeCaret , 0); // R
|
||||
bytes.set(writeCaret + 1, 0); // G
|
||||
bytes.set(writeCaret + 2, 0); // B
|
||||
bytes.set(writeCaret + 3, 0); // A
|
||||
|
||||
if (++x == frame.width)
|
||||
{
|
||||
x = 0;
|
||||
writeCaret += lineSkip;
|
||||
}
|
||||
else writeCaret += 4;
|
||||
}
|
||||
default:
|
||||
for (i in 0...pixels.length)
|
||||
{
|
||||
var index:Int = pixels.get(i) * 3;
|
||||
if (transparentIndex != index) // Render only if pixel non-transparent
|
||||
{
|
||||
bytes.set(writeCaret , ct.get(index )); // R
|
||||
bytes.set(writeCaret + 1, ct.get(index + 1)); // G
|
||||
bytes.set(writeCaret + 2, ct.get(index + 2)); // B
|
||||
bytes.set(writeCaret + 3, 0xFF); // A
|
||||
}
|
||||
|
||||
if (++x == frame.width)
|
||||
{
|
||||
x = 0;
|
||||
writeCaret += lineSkip;
|
||||
}
|
||||
else writeCaret += 4;
|
||||
}
|
||||
}
|
||||
|
||||
if (frameCaret == frameIndex) return bytes;
|
||||
frameCaret++;
|
||||
gce = null;
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
return bytes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns amount of animation repeats stored in Gif data.
|
||||
* This is link to Netscape Looping application extension. If this extension does not present amount of loops equals to 1.
|
||||
* @param data Gif data.
|
||||
* @return Amount of animation repeats. Zero equals to infinite amount of repeats.
|
||||
*/
|
||||
public static function loopCount(data:Data):Int
|
||||
{
|
||||
for (block in data.blocks)
|
||||
{
|
||||
switch(block)
|
||||
{
|
||||
case Block.BExtension(Extension.EApplicationExtension(ApplicationExtension.AENetscapeLooping(loops))): return loops;
|
||||
default :
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
//==========================================================
|
||||
// In-Dev writer tools.
|
||||
//==========================================================
|
||||
|
||||
//public static function buildFrameFromTrueColor(pixels:Bytes, width:Int, height:Int):Void
|
||||
//{
|
||||
//
|
||||
//}
|
||||
|
||||
private static var LN2:Float = Math.log(2);
|
||||
@:noCompletion public static inline function log2(val:Float):Float
|
||||
{
|
||||
return Math.log(val) / LN2;
|
||||
}
|
||||
}
|
||||
525
leenkx/Sources/iron/format/gif/Writer.hx
Normal file
525
leenkx/Sources/iron/format/gif/Writer.hx
Normal file
@ -0,0 +1,525 @@
|
||||
package format.gif;
|
||||
import format.gif.Data;
|
||||
import haxe.ds.Vector;
|
||||
import haxe.io.Bytes;
|
||||
import haxe.io.Output;
|
||||
import haxe.io.UInt8Array;
|
||||
|
||||
/**
|
||||
* ...
|
||||
* @author Yanrishatum
|
||||
*/
|
||||
class Writer
|
||||
{
|
||||
|
||||
private var o:Output;
|
||||
private var lzw:LZWEncoder;
|
||||
private var gctSize:Int;
|
||||
|
||||
public function new(o:Output)
|
||||
{
|
||||
this.o = o;
|
||||
this.lzw = new LZWEncoder();
|
||||
o.bigEndian = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Write entire Data at once.
|
||||
* @param data Input gif file data
|
||||
*/
|
||||
public function write(data:Data):Void
|
||||
{
|
||||
// Header
|
||||
writeHeader(data.version);
|
||||
|
||||
// Logical screen descriptor.
|
||||
writeLogicalScreenDescriptor(data.logicalScreenDescriptor, data.globalColorTable);
|
||||
|
||||
for (block in data.blocks)
|
||||
{
|
||||
switch (block)
|
||||
{
|
||||
case Block.BEOF:
|
||||
writeEOF();
|
||||
return;
|
||||
case Block.BExtension(ext):
|
||||
switch (ext)
|
||||
{
|
||||
case Extension.EUnknown(id, bytes):
|
||||
writeUnknownExtension(id, bytes);
|
||||
case Extension.EComment(text):
|
||||
writeComment(text);
|
||||
case Extension.EText(textExt):
|
||||
writeText(textExt);
|
||||
case Extension.EGraphicControl(gce):
|
||||
writeGraphicControl(gce);
|
||||
case Extension.EApplicationExtension(appExt):
|
||||
writeAppExtension(appExt);
|
||||
}
|
||||
case Block.BFrame(frame):
|
||||
writeFrame(frame);
|
||||
}
|
||||
}
|
||||
writeEOF(); // If we doesn't encountered EOF block - write it.
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes header of Gif file. Must be first.
|
||||
* @param version
|
||||
*/
|
||||
public function writeHeader(version:Version):Void
|
||||
{
|
||||
o.writeString("GIF");
|
||||
switch(version)
|
||||
{
|
||||
case Version.GIF87a: o.writeString("87a");
|
||||
case Version.GIF89a: o.writeString("89a");
|
||||
case Version.Unknown(v):
|
||||
if (v.length == 3) o.writeString(v);
|
||||
else if (v.length > 3) o.writeString(v.substr(0, 3));
|
||||
else
|
||||
{
|
||||
while (v.length < 3) v += "-";
|
||||
o.writeString(v);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes Logical Screen Descriptor block. Must go right after header.
|
||||
* @param lsd Logical Screen Descriptor object.
|
||||
* @param globalColorTable Global color table. Required only if LSD contains hasGlobalColorTable flag.
|
||||
* Color table must be a RGB-aligned Bytes with 3 bytes per color.
|
||||
*/
|
||||
public function writeLogicalScreenDescriptor(lsd:LogicalScreenDescriptor, globalColorTable:Bytes = null):Void
|
||||
{
|
||||
o.writeUInt16(lsd.width);
|
||||
o.writeUInt16(lsd.height);
|
||||
|
||||
var packed:Int = 0;
|
||||
if (lsd.hasGlobalColorTable) packed |= 128;
|
||||
packed |= (lsd.colorResolution << 4) & 112;
|
||||
if (lsd.sorted) packed |= 8;
|
||||
packed |= Math.round(Tools.log2(lsd.globalColorTableSize) - 1) & 7;
|
||||
o.writeByte(packed);
|
||||
|
||||
o.writeByte(lsd.backgroundColorIndex);
|
||||
if (lsd.pixelAspectRatio == 1) o.writeByte(0);
|
||||
else o.writeByte(Std.int(lsd.pixelAspectRatio) * 64 - 15);
|
||||
|
||||
if (lsd.hasGlobalColorTable)
|
||||
{
|
||||
if (globalColorTable != null)
|
||||
{
|
||||
o.writeBytes(globalColorTable, 0, globalColorTable.length);
|
||||
gctSize = lsd.globalColorTableSize;
|
||||
}
|
||||
else throw "hasGlobalColorTable flag present, but there is no global color table!";
|
||||
}
|
||||
}
|
||||
|
||||
public function writeComment(text:String):Void
|
||||
{
|
||||
o.writeByte(0x21);
|
||||
o.writeByte(0xFE);
|
||||
writeStringBlocks(text);
|
||||
}
|
||||
|
||||
public function writeText(textExt:PlainTextExtension):Void
|
||||
{
|
||||
o.writeByte(0x21);
|
||||
o.writeByte(0x01);
|
||||
o.writeByte(12);
|
||||
o.writeUInt16(textExt.textGridX);
|
||||
o.writeUInt16(textExt.textGridY);
|
||||
o.writeUInt16(textExt.textGridWidth);
|
||||
o.writeUInt16(textExt.textGridHeight);
|
||||
o.writeByte(textExt.charCellWidth);
|
||||
o.writeByte(textExt.charCellHeight);
|
||||
o.writeByte(textExt.textForegroundColorIndex);
|
||||
o.writeByte(textExt.textForegroundColorIndex);
|
||||
writeStringBlocks(textExt.text);
|
||||
}
|
||||
|
||||
public function writeGraphicControl(gce:GraphicControlExtension):Void
|
||||
{
|
||||
o.writeByte(0x21);
|
||||
o.writeByte(0xF9);
|
||||
o.writeByte(4);
|
||||
var packed:Int = 0;
|
||||
|
||||
switch (gce.disposalMethod)
|
||||
{
|
||||
case DisposalMethod.UNSPECIFIED: // 0
|
||||
case DisposalMethod.NO_ACTION: packed |= 4;
|
||||
case DisposalMethod.FILL_BACKGROUND: packed |= 8;
|
||||
case DisposalMethod.RENDER_PREVIOUS: packed |= 12;
|
||||
case DisposalMethod.UNDEFINED(idx): packed |= (idx & 7) << 2;
|
||||
}
|
||||
if (gce.userInput) packed |= 2;
|
||||
if (gce.hasTransparentColor) packed |= 1;
|
||||
|
||||
o.writeByte(packed);
|
||||
o.writeUInt16(gce.delay);
|
||||
o.writeByte(gce.transparentIndex);
|
||||
o.writeByte(0); // Terminator
|
||||
}
|
||||
|
||||
public function writeAppExtension(appExt:ApplicationExtension):Void
|
||||
{
|
||||
o.writeByte(0x21);
|
||||
o.writeByte(0xFF);
|
||||
o.writeByte(11);
|
||||
switch (appExt)
|
||||
{
|
||||
case ApplicationExtension.AENetscapeLooping(loops):
|
||||
o.writeString("NETSCAPE2.0");
|
||||
o.writeByte(3);
|
||||
o.writeByte(1); // Looping
|
||||
o.writeUInt16(loops);
|
||||
o.writeByte(0);
|
||||
case ApplicationExtension.AEUnknown(name, version, bytes):
|
||||
o.writeString(name);
|
||||
o.writeString(version);
|
||||
writeBlocks(bytes);
|
||||
}
|
||||
}
|
||||
|
||||
public function writeUnknownExtension(id:Int, bytes:Bytes):Void
|
||||
{
|
||||
o.writeByte(0x21);
|
||||
o.writeByte(id);
|
||||
writeBlocks(bytes);
|
||||
}
|
||||
|
||||
public function writeFrame(frame:Frame):Void
|
||||
{
|
||||
|
||||
o.writeByte(0x2C);
|
||||
o.writeUInt16(frame.x);
|
||||
o.writeUInt16(frame.y);
|
||||
o.writeUInt16(frame.width);
|
||||
o.writeUInt16(frame.height);
|
||||
|
||||
var packed:Int = 0;
|
||||
if (frame.localColorTable) packed |= 128;
|
||||
if (frame.interlaced) packed |= 64;
|
||||
if (frame.sorted) packed |= 32;
|
||||
packed |= Math.round(Tools.log2(frame.localColorTableSize) - 1) & 7;
|
||||
o.writeByte(packed);
|
||||
if (frame.localColorTable)
|
||||
{
|
||||
if (frame.colorTable != null) o.writeBytes(frame.colorTable, 0, frame.colorTable.length);
|
||||
else throw "localColorTable flag is set, but there is no local color table!";
|
||||
}
|
||||
|
||||
lzw.encode(frame.width, frame.height, frame.pixels, frame.localColorTable ? frame.localColorTableSize : gctSize, o, frame.interlaced);
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes EndOfFile block.
|
||||
*/
|
||||
public function writeEOF():Void
|
||||
{
|
||||
o.writeByte(0x3B);
|
||||
}
|
||||
|
||||
private function writeStringBlocks(text:String):Void
|
||||
{
|
||||
var len:Int;
|
||||
var caret:Int = 0;
|
||||
while (caret < text.length)
|
||||
{
|
||||
len = text.length - caret;
|
||||
if (len > 0xFF) len = 0xFF;
|
||||
o.writeByte(len);
|
||||
for (i in 0...len) o.writeByte(text.charCodeAt(i + caret));
|
||||
caret += len;
|
||||
}
|
||||
o.writeByte(0);
|
||||
}
|
||||
|
||||
private function writeBlocks(bytes:Bytes):Void
|
||||
{
|
||||
var len:Int;
|
||||
var caret:Int = 0;
|
||||
while (caret < bytes.length)
|
||||
{
|
||||
len = bytes.length - caret;
|
||||
if (len > 0xFF) len = 0xFF;
|
||||
o.writeByte(len);
|
||||
o.writeBytes(bytes, caret, len);
|
||||
caret += 0xFF;
|
||||
}
|
||||
o.writeByte(0); // Terminator
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
class LZWEncoder
|
||||
{
|
||||
private var EOF:Int = -1;
|
||||
private static inline var BITS:Int = 12;
|
||||
private static inline var HSIZE:Int = 5003;
|
||||
private var masks:Array<Int> = [0x0000, 0x0001, 0x0003, 0x0007, 0x000F, 0x001F,
|
||||
0x003F, 0x007F, 0x00FF, 0x01FF, 0x03FF, 0x07FF,
|
||||
0x0FFF, 0x1FFF, 0x3FFF, 0x7FFF, 0xFFFF];
|
||||
|
||||
private var out:Output;
|
||||
private var bits:Int;
|
||||
private var bitsCount:Int;
|
||||
|
||||
private var minCodeSize:Int;
|
||||
private var codeSize:Int;
|
||||
private var codeSizeLimit:Int;
|
||||
private var clearFlag:Bool;
|
||||
|
||||
private var clearCode:Int;
|
||||
private var eofCode:Int;
|
||||
|
||||
// Dict
|
||||
private var htab:Vector<Int>;
|
||||
private var codetab:Vector<Int>;
|
||||
private var freeEnt:Int;
|
||||
|
||||
// Block buffer
|
||||
private var blockBuffer:Bytes;
|
||||
private var blockBufferCaret:Int;
|
||||
|
||||
// Input data
|
||||
private var pixels:Bytes;
|
||||
private var width:Int;
|
||||
private var height:Int;
|
||||
private var remaining:Int;
|
||||
|
||||
// Non-interlaced
|
||||
private var pixelsCaret:Int;
|
||||
// Interlaced
|
||||
private var interlaced:Bool;
|
||||
private var pixelsX:Int;
|
||||
private var pixelsY:Int;
|
||||
private var interlacingStage:Int;
|
||||
private var interlacingStep:Int;
|
||||
|
||||
public function new()
|
||||
{
|
||||
blockBuffer = Bytes.alloc(256);
|
||||
}
|
||||
|
||||
public function encode(width:Int, height:Int, pixels:Bytes, colorsCount:Int, out:Output, interlaced:Bool):Void
|
||||
{
|
||||
minCodeSize = Math.round(Tools.log2(colorsCount));
|
||||
|
||||
this.pixels = pixels;
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
this.out = out;
|
||||
|
||||
htab = new Vector(HSIZE);
|
||||
codetab = new Vector(HSIZE);
|
||||
|
||||
blockBufferCaret = 0;
|
||||
bits = 0;
|
||||
bitsCount = 0;
|
||||
|
||||
clearCode = 1 << minCodeSize;
|
||||
eofCode = clearCode + 1;
|
||||
freeEnt = clearCode + 2;
|
||||
|
||||
out.writeByte(minCodeSize);
|
||||
remaining = width * height;
|
||||
|
||||
this.interlaced = interlaced;
|
||||
if (interlaced)
|
||||
{
|
||||
pixelsX = 0;
|
||||
pixelsY = 0;
|
||||
interlacingStage = 0;
|
||||
interlacingStep = 8;
|
||||
}
|
||||
else pixelsCaret = 0;
|
||||
|
||||
compress();
|
||||
out.writeByte(0);
|
||||
}
|
||||
|
||||
private function char_out(c:Int):Void
|
||||
{
|
||||
blockBuffer.set(blockBufferCaret++, c);
|
||||
if (blockBufferCaret >= 254) flush_char();
|
||||
}
|
||||
|
||||
private function cl_block():Void
|
||||
{
|
||||
cl_hash(HSIZE);
|
||||
freeEnt = clearCode + 2;
|
||||
clearFlag = true;
|
||||
output(clearCode);
|
||||
}
|
||||
|
||||
private function cl_hash(hsize:Int):Void
|
||||
{
|
||||
for (i in 0...hsize) htab[i] = -1;
|
||||
}
|
||||
|
||||
private function compress():Void
|
||||
{
|
||||
var disp:Int;
|
||||
var i:Int;
|
||||
|
||||
clearFlag = false;
|
||||
codeSize = minCodeSize + 1;
|
||||
codeSizeLimit = MAXCODE(codeSize);
|
||||
|
||||
var ent:Int = nextPixel();
|
||||
|
||||
var hshift:Int = 0;
|
||||
var fcode:Int = HSIZE;
|
||||
while (fcode < 65536)
|
||||
{
|
||||
++hshift;
|
||||
fcode *= 2;
|
||||
}
|
||||
hshift = 8 - hshift;
|
||||
var hsize_reg:Int = HSIZE;
|
||||
cl_hash(hsize_reg);
|
||||
|
||||
output(clearCode);
|
||||
|
||||
var c:Int;
|
||||
while ((c = nextPixel()) != EOF)
|
||||
{
|
||||
fcode = (c << BITS) + ent;
|
||||
i = (c << hshift) ^ ent;
|
||||
if (htab[i] == fcode)
|
||||
{
|
||||
ent = codetab[i];
|
||||
continue;
|
||||
}
|
||||
else if (htab[i] >= 0)
|
||||
{
|
||||
disp = hsize_reg - i;
|
||||
if (i == 0) disp = 1;
|
||||
var skip:Bool = false;
|
||||
do
|
||||
{
|
||||
if ((i -= disp) < 0) i += hsize_reg;
|
||||
if (htab[i] == fcode)
|
||||
{
|
||||
ent = codetab[i];
|
||||
skip = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
while (htab[i] >= 0);
|
||||
if (skip) continue;
|
||||
}
|
||||
|
||||
output(ent);
|
||||
ent = c;
|
||||
if (freeEnt < (1 << BITS))
|
||||
{
|
||||
codetab[i] = freeEnt++;
|
||||
htab[i] = fcode;
|
||||
}
|
||||
else
|
||||
{
|
||||
cl_block();
|
||||
}
|
||||
}
|
||||
|
||||
output(ent);
|
||||
output(eofCode);
|
||||
}
|
||||
|
||||
private function flush_char():Void
|
||||
{
|
||||
if (blockBufferCaret > 0)
|
||||
{
|
||||
out.writeByte(blockBufferCaret);
|
||||
out.writeBytes(blockBuffer, 0, blockBufferCaret);
|
||||
blockBufferCaret = 0;
|
||||
}
|
||||
}
|
||||
|
||||
private inline function MAXCODE(n_bits:Int):Int
|
||||
{
|
||||
return (1 << n_bits) - 1;
|
||||
}
|
||||
|
||||
private function nextPixel():Int
|
||||
{
|
||||
if (remaining == 0) return EOF;
|
||||
remaining--;
|
||||
if (interlaced)
|
||||
{
|
||||
if (++pixelsX == width)
|
||||
{
|
||||
pixelsX = 0;
|
||||
pixelsY += interlacingStep;
|
||||
if (pixelsY >= height)
|
||||
{
|
||||
switch (interlacingStage)
|
||||
{
|
||||
// first: Every 8 line with start at 0
|
||||
case 0: pixelsY = 4; // Every 8 line with start at 4
|
||||
case 1: pixelsY = 2; interlacingStep = 4; // Every 4 line with start at 2
|
||||
case 2: pixelsY = 1; interlacingStep = 2; // Every 2 line with start at 1
|
||||
default: return -1; // EOF
|
||||
}
|
||||
interlacingStage++;
|
||||
}
|
||||
}
|
||||
return pixels.get(pixelsY * width + pixelsX);
|
||||
}
|
||||
else
|
||||
{
|
||||
return pixels.get(pixelsCaret++);
|
||||
}
|
||||
}
|
||||
|
||||
private function output(code:Int):Void
|
||||
{
|
||||
bits &= masks[bitsCount];
|
||||
|
||||
if (bitsCount > 0) bits |= (code << bitsCount);
|
||||
else bits = code;
|
||||
|
||||
bitsCount += codeSize;
|
||||
|
||||
while (bitsCount >= 8)
|
||||
{
|
||||
char_out(bits & 0xFF);
|
||||
bits >>= 8;
|
||||
bitsCount -= 8;
|
||||
}
|
||||
|
||||
if (freeEnt > codeSizeLimit || clearFlag)
|
||||
{
|
||||
if (clearFlag)
|
||||
{
|
||||
codeSizeLimit = MAXCODE(codeSize = minCodeSize + 1);
|
||||
clearFlag = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
codeSize++;
|
||||
if (codeSize == BITS) codeSizeLimit = 1 << BITS;
|
||||
else codeSizeLimit = MAXCODE(codeSize);
|
||||
}
|
||||
}
|
||||
|
||||
if (code == eofCode)
|
||||
{
|
||||
while (bitsCount > 0)
|
||||
{
|
||||
char_out(bits & 0xFF);
|
||||
bits >>= 8;
|
||||
bitsCount -= 8;
|
||||
}
|
||||
flush_char();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
37
leenkx/Sources/iron/format/jpg/Data.hx
Normal file
37
leenkx/Sources/iron/format/jpg/Data.hx
Normal file
@ -0,0 +1,37 @@
|
||||
/*
|
||||
* format - Haxe File Formats
|
||||
*
|
||||
* JPG File Format
|
||||
* Copyright (C) 2007-2009 Trevor McCauley, Baluta Cristian (hx port) & Robert Sköld (format conversion)
|
||||
*
|
||||
* Copyright (c) 2009, The Haxe Project Contributors
|
||||
* All rights reserved.
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* - Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* - Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE HAXE PROJECT CONTRIBUTORS "AS IS" AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE HAXE PROJECT CONTRIBUTORS BE LIABLE FOR
|
||||
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
|
||||
* DAMAGE.
|
||||
*/
|
||||
package iron.format.jpg;
|
||||
|
||||
typedef Data = {
|
||||
var width : Int;
|
||||
var height : Int;
|
||||
var quality : Float;
|
||||
var pixels : haxe.io.Bytes;
|
||||
}
|
||||
657
leenkx/Sources/iron/format/jpg/Writer.hx
Normal file
657
leenkx/Sources/iron/format/jpg/Writer.hx
Normal file
@ -0,0 +1,657 @@
|
||||
package iron.format.jpg;
|
||||
|
||||
class Writer {
|
||||
var ZigZag: Array<Int>;
|
||||
|
||||
// Static table initialization
|
||||
function initZigZag() {
|
||||
ZigZag = [
|
||||
0, 1, 5, 6,14,15,27,28,
|
||||
2, 4, 7,13,16,26,29,42,
|
||||
3, 8,12,17,25,30,41,43,
|
||||
9,11,18,24,31,40,44,53,
|
||||
10,19,23,32,39,45,52,54,
|
||||
20,22,33,38,46,51,55,60,
|
||||
21,34,37,47,50,56,59,61,
|
||||
35,36,48,49,57,58,62,63
|
||||
];
|
||||
}
|
||||
|
||||
var YTable: Array<Int>;
|
||||
var UVTable: Array<Int>;
|
||||
var fdtbl_Y: Array<Float>;
|
||||
var fdtbl_UV: Array<Float>;
|
||||
|
||||
function initQuantTables(sf: Int) {
|
||||
var YQT: Array<Int> = [
|
||||
16, 11, 10, 16, 24, 40, 51, 61,
|
||||
12, 12, 14, 19, 26, 58, 60, 55,
|
||||
14, 13, 16, 24, 40, 57, 69, 56,
|
||||
14, 17, 22, 29, 51, 87, 80, 62,
|
||||
18, 22, 37, 56, 68,109,103, 77,
|
||||
24, 35, 55, 64, 81,104,113, 92,
|
||||
49, 64, 78, 87,103,121,120,101,
|
||||
72, 92, 95, 98,112,100,103, 99
|
||||
];
|
||||
for (i in 0...64) {
|
||||
var t: Int = Math.floor( (YQT[i] * sf + 50) / 100 );
|
||||
if( t < 1 ) t = 1;
|
||||
else if( t > 255 ) t = 255;
|
||||
YTable[ ZigZag[i] ] = t;
|
||||
}
|
||||
var UVQT: Array<Int> = [
|
||||
17, 18, 24, 47, 99, 99, 99, 99,
|
||||
18, 21, 26, 66, 99, 99, 99, 99,
|
||||
24, 26, 56, 99, 99, 99, 99, 99,
|
||||
47, 66, 99, 99, 99, 99, 99, 99,
|
||||
99, 99, 99, 99, 99, 99, 99, 99,
|
||||
99, 99, 99, 99, 99, 99, 99, 99,
|
||||
99, 99, 99, 99, 99, 99, 99, 99,
|
||||
99, 99, 99, 99, 99, 99, 99, 99
|
||||
];
|
||||
for( j in 0...64 ) {
|
||||
var u: Int = Math.floor( (UVQT[j] * sf + 50) / 100 );
|
||||
if( u < 1 ) u = 1;
|
||||
else if( u > 255 ) u = 255;
|
||||
UVTable[ ZigZag[j] ] = u;
|
||||
}
|
||||
var aasf: Array<Float> = [
|
||||
1.0, 1.387039845, 1.306562965, 1.175875602,
|
||||
1.0, 0.785694958, 0.541196100, 0.275899379
|
||||
];
|
||||
var k = 0;
|
||||
for( row in 0...8 ) {
|
||||
for( col in 0...8 ) {
|
||||
fdtbl_Y[k] = (1.0 / (YTable [ZigZag[k]] * aasf[row] * aasf[col] * 8.0));
|
||||
fdtbl_UV[k] = (1.0 / (UVTable[ZigZag[k]] * aasf[row] * aasf[col] * 8.0));
|
||||
k++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var std_dc_luminance_nrcodes: Array<Int>;
|
||||
var std_dc_luminance_values: haxe.io.Bytes;
|
||||
var std_ac_luminance_nrcodes: Array<Int>;
|
||||
var std_ac_luminance_values: haxe.io.Bytes;
|
||||
|
||||
function initLuminance() {
|
||||
std_dc_luminance_nrcodes = [0,0,1,5,1,1,1,1,1,1,0,0,0,0,0,0,0];
|
||||
std_dc_luminance_values = strIntsToBytes( '0,1,2,3,4,5,6,7,8,9,10,11' );
|
||||
std_ac_luminance_nrcodes = [0,0,2,1,3,3,2,4,3,5,5,4,4,0,0,1,0x7d];
|
||||
std_ac_luminance_values = strIntsToBytes(
|
||||
'0x01,0x02,0x03,0x00,0x04,0x11,0x05,0x12,' +
|
||||
'0x21,0x31,0x41,0x06,0x13,0x51,0x61,0x07,' +
|
||||
'0x22,0x71,0x14,0x32,0x81,0x91,0xa1,0x08,' +
|
||||
'0x23,0x42,0xb1,0xc1,0x15,0x52,0xd1,0xf0,' +
|
||||
'0x24,0x33,0x62,0x72,0x82,0x09,0x0a,0x16,' +
|
||||
'0x17,0x18,0x19,0x1a,0x25,0x26,0x27,0x28,' +
|
||||
'0x29,0x2a,0x34,0x35,0x36,0x37,0x38,0x39,' +
|
||||
'0x3a,0x43,0x44,0x45,0x46,0x47,0x48,0x49,' +
|
||||
'0x4a,0x53,0x54,0x55,0x56,0x57,0x58,0x59,' +
|
||||
'0x5a,0x63,0x64,0x65,0x66,0x67,0x68,0x69,' +
|
||||
'0x6a,0x73,0x74,0x75,0x76,0x77,0x78,0x79,' +
|
||||
'0x7a,0x83,0x84,0x85,0x86,0x87,0x88,0x89,' +
|
||||
'0x8a,0x92,0x93,0x94,0x95,0x96,0x97,0x98,' +
|
||||
'0x99,0x9a,0xa2,0xa3,0xa4,0xa5,0xa6,0xa7,' +
|
||||
'0xa8,0xa9,0xaa,0xb2,0xb3,0xb4,0xb5,0xb6,' +
|
||||
'0xb7,0xb8,0xb9,0xba,0xc2,0xc3,0xc4,0xc5,' +
|
||||
'0xc6,0xc7,0xc8,0xc9,0xca,0xd2,0xd3,0xd4,' +
|
||||
'0xd5,0xd6,0xd7,0xd8,0xd9,0xda,0xe1,0xe2,' +
|
||||
'0xe3,0xe4,0xe5,0xe6,0xe7,0xe8,0xe9,0xea,' +
|
||||
'0xf1,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7,0xf8,' +
|
||||
'0xf9,0xfa'
|
||||
);
|
||||
}
|
||||
|
||||
function strIntsToBytes( s: String ) {
|
||||
var len = s.length;
|
||||
var b = new haxe.io.BytesBuffer();
|
||||
var val = 0;
|
||||
var i = 0;
|
||||
for( j in 0...len ) {
|
||||
if( s.charAt( j ) == ',' ) {
|
||||
val = Std.parseInt( s.substr(i, j - i) );
|
||||
b.addByte( val );
|
||||
i = j + 1;
|
||||
}
|
||||
}
|
||||
if( i < len ) {
|
||||
val = Std.parseInt( s.substr(i) );
|
||||
b.addByte( val );
|
||||
}
|
||||
return b.getBytes();
|
||||
}
|
||||
|
||||
var std_dc_chrominance_nrcodes: Array<Int>;
|
||||
var std_dc_chrominance_values: haxe.io.Bytes;
|
||||
var std_ac_chrominance_nrcodes: Array<Int>;
|
||||
var std_ac_chrominance_values: haxe.io.Bytes;
|
||||
|
||||
function initChrominance() {
|
||||
std_dc_chrominance_nrcodes = [0,0,3,1,1,1,1,1,1,1,1,1,0,0,0,0,0];
|
||||
std_dc_chrominance_values = strIntsToBytes( '0,1,2,3,4,5,6,7,8,9,10,11' );
|
||||
std_ac_chrominance_nrcodes = [0,0,2,1,2,4,4,3,4,7,5,4,4,0,1,2,0x77];
|
||||
std_ac_chrominance_values = strIntsToBytes(
|
||||
'0x00,0x01,0x02,0x03,0x11,0x04,0x05,0x21,' +
|
||||
'0x31,0x06,0x12,0x41,0x51,0x07,0x61,0x71,' +
|
||||
'0x13,0x22,0x32,0x81,0x08,0x14,0x42,0x91,' +
|
||||
'0xa1,0xb1,0xc1,0x09,0x23,0x33,0x52,0xf0,' +
|
||||
'0x15,0x62,0x72,0xd1,0x0a,0x16,0x24,0x34,' +
|
||||
'0xe1,0x25,0xf1,0x17,0x18,0x19,0x1a,0x26,' +
|
||||
'0x27,0x28,0x29,0x2a,0x35,0x36,0x37,0x38,' +
|
||||
'0x39,0x3a,0x43,0x44,0x45,0x46,0x47,0x48,' +
|
||||
'0x49,0x4a,0x53,0x54,0x55,0x56,0x57,0x58,' +
|
||||
'0x59,0x5a,0x63,0x64,0x65,0x66,0x67,0x68,' +
|
||||
'0x69,0x6a,0x73,0x74,0x75,0x76,0x77,0x78,' +
|
||||
'0x79,0x7a,0x82,0x83,0x84,0x85,0x86,0x87,' +
|
||||
'0x88,0x89,0x8a,0x92,0x93,0x94,0x95,0x96,' +
|
||||
'0x97,0x98,0x99,0x9a,0xa2,0xa3,0xa4,0xa5,' +
|
||||
'0xa6,0xa7,0xa8,0xa9,0xaa,0xb2,0xb3,0xb4,' +
|
||||
'0xb5,0xb6,0xb7,0xb8,0xb9,0xba,0xc2,0xc3,' +
|
||||
'0xc4,0xc5,0xc6,0xc7,0xc8,0xc9,0xca,0xd2,' +
|
||||
'0xd3,0xd4,0xd5,0xd6,0xd7,0xd8,0xd9,0xda,' +
|
||||
'0xe2,0xe3,0xe4,0xe5,0xe6,0xe7,0xe8,0xe9,' +
|
||||
'0xea,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7,0xf8,' +
|
||||
'0xf9,0xfa'
|
||||
);
|
||||
}
|
||||
|
||||
var YDC_HT: Map<Int,BitString>;
|
||||
var UVDC_HT: Map<Int,BitString>;
|
||||
var YAC_HT: Map<Int,BitString>;
|
||||
var UVAC_HT: Map<Int,BitString>;
|
||||
|
||||
// Función para crear la tabla Huffman (Helper)
|
||||
function computeHuffmanTbl(nrcodes: Array<Int>, std_table: haxe.io.Bytes): Map<Int,BitString> {
|
||||
var codevalue = 0;
|
||||
var pos_in_table = 0;
|
||||
var HT: Map<Int,BitString> = new Map();
|
||||
for( k in 1...17 ) {
|
||||
var end = nrcodes[k];
|
||||
for( j in 0...end ) {
|
||||
var idx: Int = std_table.get( pos_in_table );
|
||||
HT.set( idx, new BitString( k, codevalue ) );
|
||||
pos_in_table++;
|
||||
codevalue++;
|
||||
}
|
||||
codevalue *= 2;
|
||||
}
|
||||
return HT;
|
||||
}
|
||||
|
||||
function initHuffmanTbl() {
|
||||
YDC_HT = computeHuffmanTbl(std_dc_luminance_nrcodes, std_dc_luminance_values);
|
||||
UVDC_HT = computeHuffmanTbl(std_dc_chrominance_nrcodes, std_dc_chrominance_values);
|
||||
|
||||
YAC_HT = computeHuffmanTbl(std_ac_luminance_nrcodes, std_ac_luminance_values);
|
||||
UVAC_HT = computeHuffmanTbl(std_ac_chrominance_nrcodes, std_ac_chrominance_values);
|
||||
|
||||
// CORRECCIÓN DE TABLAS: Asegurar la existencia de EOB (0x00) y ZRL (0xF0)
|
||||
// Esto es necesario para evitar fallos si el 'computeHuffmanTbl' no incluye estos valores por algún motivo.
|
||||
if (YAC_HT.get(0x00) == null) YAC_HT.set(0x00, new BitString(4, 0x00));
|
||||
if (UVAC_HT.get(0x00) == null) UVAC_HT.set(0x00, new BitString(4, 0x00));
|
||||
if (YAC_HT.get(0xF0) == null) YAC_HT.set(0xF0, new BitString(11, 0x1E));
|
||||
if (UVAC_HT.get(0xF0) == null) UVAC_HT.set(0xF0, new BitString(11, 0x1E));
|
||||
}
|
||||
|
||||
var bitcode: Map<Int,BitString>;
|
||||
var category: Map<Int,Int>;
|
||||
|
||||
function initCategoryNumber() {
|
||||
var nrlower = 1;
|
||||
var nrupper = 2;
|
||||
var idx: Int;
|
||||
for (cat in 1...16) {
|
||||
//Positive numbers
|
||||
for( nr in nrlower...nrupper ) {
|
||||
idx = 32767 + nr;
|
||||
category.set( idx, cat );
|
||||
bitcode.set( idx, new BitString( cat, nr ) );
|
||||
}
|
||||
//Negative numbers
|
||||
var nrneg: Int = -(nrupper - 1);
|
||||
while( nrneg <= -nrlower ) {
|
||||
idx = 32767 + nrneg;
|
||||
category.set( idx, cat );
|
||||
bitcode.set( idx, new BitString( cat, nrupper - 1 + nrneg ) );
|
||||
nrneg++;
|
||||
}
|
||||
nrlower <<= 1;
|
||||
nrupper <<= 1;
|
||||
}
|
||||
}
|
||||
|
||||
// IO functions
|
||||
var byteout: haxe.io.Output;
|
||||
var bytenew: Int;
|
||||
var bytepos: Int;
|
||||
|
||||
function writeBits(bs: BitString) {
|
||||
// Se confía en que bs no es nulo gracias al clamping y las correcciones de tablas.
|
||||
var value: Int = bs.val;
|
||||
var posval: Int = bs.len - 1;
|
||||
while( posval >= 0 ) {
|
||||
if( (value & (1 << posval)) != 0 ) {
|
||||
bytenew |= (1 << bytepos);
|
||||
}
|
||||
posval--;
|
||||
bytepos--;
|
||||
if( bytepos < 0 ) {
|
||||
if( bytenew == 0xFF ) {
|
||||
b(0xFF);
|
||||
b(0);
|
||||
}
|
||||
else {
|
||||
b(bytenew);
|
||||
}
|
||||
bytepos = 7;
|
||||
bytenew = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function writeWord( val: Int ) {
|
||||
b( (val >> 8) & 0xFF );
|
||||
b( val & 0xFF );
|
||||
}
|
||||
|
||||
// DCT & quantization core
|
||||
|
||||
function fDCTQuant(data: Array<Float>, fdtbl: Array<Float>): Array<Float> {
|
||||
/* Pass 1: process rows. */
|
||||
var dataOff = 0;
|
||||
for (i in 0...8) {
|
||||
var tmp0: Float = data[dataOff + 0] + data[dataOff + 7];
|
||||
var tmp7: Float = data[dataOff + 0] - data[dataOff + 7];
|
||||
var tmp1: Float = data[dataOff + 1] + data[dataOff + 6];
|
||||
var tmp6: Float = data[dataOff + 1] - data[dataOff + 6];
|
||||
var tmp2: Float = data[dataOff + 2] + data[dataOff + 5];
|
||||
var tmp5: Float = data[dataOff + 2] - data[dataOff + 5];
|
||||
var tmp3: Float = data[dataOff + 3] + data[dataOff + 4];
|
||||
var tmp4: Float = data[dataOff + 3] - data[dataOff + 4];
|
||||
|
||||
/* Even part */
|
||||
var tmp10: Float = tmp0 + tmp3; /* phase 2 */
|
||||
var tmp13: Float = tmp0 - tmp3;
|
||||
var tmp11: Float = tmp1 + tmp2;
|
||||
var tmp12: Float = tmp1 - tmp2;
|
||||
|
||||
data[dataOff + 0] = tmp10 + tmp11; /* phase 3 */
|
||||
data[dataOff + 4] = tmp10 - tmp11;
|
||||
|
||||
var z1: Float = (tmp12 + tmp13) * 0.707106781; /* c4 */
|
||||
data[dataOff + 2] = tmp13 + z1; /* phase 5 */
|
||||
data[dataOff + 6] = tmp13 - z1;
|
||||
|
||||
/* Odd part */
|
||||
tmp10 = tmp4 + tmp5; /* phase 2 */
|
||||
tmp11 = tmp5 + tmp6;
|
||||
tmp12 = tmp6 + tmp7;
|
||||
|
||||
/* The rotator is modified from fig 4-8 to avoid extra negations. */
|
||||
var z5: Float = (tmp10 - tmp12) * 0.382683433; /* c6 */
|
||||
var z2: Float = 0.541196100 * tmp10 + z5; /* c2-c6 */
|
||||
var z4: Float = 1.306562965 * tmp12 + z5; /* c2+c6 */
|
||||
var z3: Float = tmp11 * 0.707106781; /* c4 */
|
||||
|
||||
var z11: Float = tmp7 + z3; /* phase 5 */
|
||||
var z13: Float = tmp7 - z3;
|
||||
|
||||
data[dataOff + 5] = z13 + z2; /* phase 6 */
|
||||
data[dataOff + 3] = z13 - z2;
|
||||
data[dataOff + 1] = z11 + z4;
|
||||
data[dataOff + 7] = z11 - z4;
|
||||
|
||||
dataOff += 8; /* advance pointer to next row */
|
||||
}
|
||||
|
||||
/* Pass 2: process columns. */
|
||||
dataOff = 0;
|
||||
for (j in 0...8) {
|
||||
var tmp0p2: Float = data[dataOff+ 0] + data[dataOff+56];
|
||||
var tmp7p2: Float = data[dataOff+ 0] - data[dataOff+56];
|
||||
var tmp1p2: Float = data[dataOff+ 8] + data[dataOff+48];
|
||||
var tmp6p2: Float = data[dataOff+ 8] - data[dataOff+48];
|
||||
var tmp2p2: Float = data[dataOff+16] + data[dataOff+40];
|
||||
var tmp5p2: Float = data[dataOff+16] - data[dataOff+40];
|
||||
var tmp3p2: Float = data[dataOff+24] + data[dataOff+32];
|
||||
var tmp4p2: Float = data[dataOff+24] - data[dataOff+32];
|
||||
|
||||
/* Even part */
|
||||
var tmp10p2: Float = tmp0p2 + tmp3p2; /* phase 2 */
|
||||
var tmp13p2: Float = tmp0p2 - tmp3p2;
|
||||
var tmp11p2: Float = tmp1p2 + tmp2p2;
|
||||
var tmp12p2: Float = tmp1p2 - tmp2p2;
|
||||
|
||||
data[dataOff+ 0] = tmp10p2 + tmp11p2; /* phase 3 */
|
||||
data[dataOff+32] = tmp10p2 - tmp11p2;
|
||||
|
||||
var z1p2: Float = (tmp12p2 + tmp13p2) * 0.707106781; /* c4 */
|
||||
data[dataOff+16] = tmp13p2 + z1p2; /* phase 5 */
|
||||
data[dataOff+48] = tmp13p2 - z1p2;
|
||||
|
||||
/* Odd part */
|
||||
tmp10p2 = tmp4p2 + tmp5p2; /* phase 2 */
|
||||
tmp11p2 = tmp5p2 + tmp6p2;
|
||||
tmp12p2 = tmp6p2 + tmp7p2;
|
||||
|
||||
/* The rotator is modified from fig 4-8 to avoid extra negations. */
|
||||
var z5p2: Float = (tmp10p2 - tmp12p2) * 0.382683433; /* c6 */
|
||||
var z2p2: Float = 0.541196100 * tmp10p2 + z5p2; /* c2-c6 */
|
||||
var z4p2: Float = 1.306562965 * tmp12p2 + z5p2; /* c2+c6 */
|
||||
var z3p2: Float= tmp11p2 * 0.707106781; /* c4 */
|
||||
|
||||
var z11p2: Float = tmp7p2 + z3p2; /* phase 5 */
|
||||
var z13p2: Float = tmp7p2 - z3p2;
|
||||
|
||||
data[dataOff+40] = z13p2 + z2p2; /* phase 6 */
|
||||
data[dataOff+24] = z13p2 - z2p2;
|
||||
data[dataOff+ 8] = z11p2 + z4p2;
|
||||
data[dataOff+56] = z11p2 - z4p2;
|
||||
|
||||
dataOff++; /* advance pointer to next column */
|
||||
}
|
||||
|
||||
// Quantize/descale the coefficients
|
||||
for (k in 0...64) {
|
||||
// Apply the quantization and scaling factor & Round to nearest integer
|
||||
data[k] = Math.round(data[k] * fdtbl[k]);
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
// Chunk writing
|
||||
|
||||
inline function b(v) {
|
||||
byteout.writeByte(v);
|
||||
}
|
||||
|
||||
function writeAPP0() {
|
||||
b(0xFF); b(0xE0); //<- marker 0xFFE0
|
||||
b(0); b(16); //<- length
|
||||
b("J".code); // J
|
||||
b("F".code);
|
||||
b("I".code);
|
||||
b("F".code);
|
||||
b(0);
|
||||
b(1); // versionhi
|
||||
b(1); // versionlo
|
||||
b(0); // xyunits
|
||||
b(0); b(1); // xdensity
|
||||
b(0); b(1); // ydensity
|
||||
b(0); // thumbnwidth
|
||||
b(0); // thumbnheight
|
||||
}
|
||||
function writeDQT() {
|
||||
b(0xFF); b(0xDB); //<- marker 0xFFDB
|
||||
b(0); b(132); //<- length
|
||||
b(0);
|
||||
for( j in 0...64 )
|
||||
b(YTable[j]);
|
||||
b(1);
|
||||
for( j in 0...64 )
|
||||
b(UVTable[j]);
|
||||
}
|
||||
function writeSOF0(width: Int, height: Int) {
|
||||
b(0xFF); b(0xC0); //<- marker 0xFFC0
|
||||
b(0); b(17); //<- length, truecolor YUV JPG
|
||||
b(8); // precision
|
||||
b( (height>>8) & 0xFF );
|
||||
b( height & 0xFF );
|
||||
b( (width>>8) & 0xFF );
|
||||
b( width & 0xFF );
|
||||
b(3); // nrofcomponents
|
||||
b(1); // IdY
|
||||
b(0x11); // HVY
|
||||
b(0); // QTY
|
||||
b(2); // IdU
|
||||
b(0x11); // HVU
|
||||
b(1); // QTU
|
||||
b(3); // IdV
|
||||
b(0x11); // HVV
|
||||
b(1); // QTV
|
||||
}
|
||||
|
||||
function writeDHT() {
|
||||
b(0xFF); b(0xC4); //<- marker 0xFFC4
|
||||
b(0x01); b(0xA2); //<- length
|
||||
b(0); // HTYDCinfo
|
||||
for( j in 1...17 )
|
||||
b(std_dc_luminance_nrcodes[j]);
|
||||
byteout.write(std_dc_luminance_values);
|
||||
|
||||
b(0x10); // HTYACinfo
|
||||
for( j in 1...17 )
|
||||
b(std_ac_luminance_nrcodes[j]);
|
||||
byteout.write(std_ac_luminance_values);
|
||||
|
||||
b(1); // HTUDCinfo
|
||||
for( j in 1...17 )
|
||||
b(std_dc_chrominance_nrcodes[j]);
|
||||
byteout.write(std_dc_chrominance_values);
|
||||
|
||||
b(0x11); // HTUACinfo
|
||||
for( j in 1...17 )
|
||||
b(std_ac_chrominance_nrcodes[j]);
|
||||
byteout.write(std_ac_chrominance_values);
|
||||
}
|
||||
|
||||
function writeSOS() {
|
||||
b(0xFF); b(0xDA); //<- marker 0xFFDA
|
||||
b(0); b(12); //<- length
|
||||
b(3); // nrofcomponents
|
||||
b(1); // IdY
|
||||
b(0); // HTY
|
||||
b(2); // IdU
|
||||
b(0x11); // HTU
|
||||
b(3); // IdV
|
||||
b(0x11); // HTV
|
||||
b(0); // Ss
|
||||
b(0x3F); // Se
|
||||
b(0); // Bf
|
||||
}
|
||||
|
||||
// Core processing
|
||||
var DU: Array<Float>;
|
||||
|
||||
function processDU(CDU: Array<Float>, fdtbl: Array<Float>, DC: Float, HTDC: Map<Int,BitString>, HTAC: Map<Int,BitString>): Float {
|
||||
var EOB: BitString = HTAC.get( 0x00 );
|
||||
var M16zeroes: BitString = HTAC.get( 0xF0 );
|
||||
|
||||
var DU_DCT: Array<Float> = fDCTQuant(CDU, fdtbl);
|
||||
//ZigZag reorder
|
||||
for (i in 0...64) {
|
||||
DU[ ZigZag[i] ] = DU_DCT[i];
|
||||
}
|
||||
var idx: Int;
|
||||
var Diff = Std.int( DU[0] - DC );
|
||||
DC = DU[0];
|
||||
|
||||
// CORRECCIÓN DE RANGO: Clamping de la diferencia DC (previene accesos a `category` fuera de rango)
|
||||
if (Diff > 16383) Diff = 16383;
|
||||
if (Diff < -16383) Diff = -16383;
|
||||
|
||||
//Encode DC
|
||||
if( Diff == 0 ) {
|
||||
writeBits( HTDC.get(0) );
|
||||
} else {
|
||||
idx = 32767 + Diff;
|
||||
writeBits(HTDC.get( category.get( idx ) ));
|
||||
writeBits( bitcode.get( idx ) );
|
||||
}
|
||||
|
||||
//Encode ACs
|
||||
var end0pos = 63;
|
||||
while( (end0pos > 0) && ( DU[end0pos] == 0.0 ) ) end0pos--;
|
||||
|
||||
//end0pos = first element in reverse order !=0
|
||||
if ( end0pos == 0 ) {
|
||||
writeBits(EOB);
|
||||
return DC;
|
||||
}
|
||||
var i = 1;
|
||||
while ( i <= end0pos ) {
|
||||
var startpos = i;
|
||||
while( ( DU[i] == 0.0 ) && ( i <= end0pos ) ) i++;
|
||||
|
||||
// Chequeo de seguridad si 'i' saltó más allá
|
||||
if (i > end0pos) break;
|
||||
|
||||
var nrzeroes: Int = i - startpos;
|
||||
if ( nrzeroes >= 16 ) {
|
||||
for( nrmarker in 0...(nrzeroes >> 4) ) writeBits(M16zeroes);
|
||||
nrzeroes &= 0xF;
|
||||
}
|
||||
|
||||
// CORRECCIÓN DE RANGO: Clamping del coeficiente AC
|
||||
var du_val = Std.int( DU[i] );
|
||||
if (du_val > 16383) du_val = 16383;
|
||||
if (du_val < -16383) du_val = -16383;
|
||||
|
||||
// LÓGICA DE SALTO: Si el clamping forzó el valor a 0, saltamos.
|
||||
if (du_val == 0) {
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
|
||||
idx = 32767 + du_val;
|
||||
|
||||
var cat = category.get( idx );
|
||||
// Si 'cat' es nulo, significa que el valor de 'du_val' está fuera del rango -16383..16383, lo cual el clamping debería haber prevenido.
|
||||
var index_ac = nrzeroes * 16 + cat;
|
||||
|
||||
writeBits( HTAC.get( index_ac ) );
|
||||
writeBits( bitcode.get( idx ) );
|
||||
i++;
|
||||
}
|
||||
if( end0pos != 63 ) writeBits(EOB);
|
||||
return DC;
|
||||
}
|
||||
|
||||
var YDU: Array<Float>;
|
||||
var UDU: Array<Float>;
|
||||
var VDU: Array<Float>;
|
||||
|
||||
function RGB2YUV(img: haxe.io.Bytes, width : Int, xpos: Int, ypos: Int) {
|
||||
var pos = 0;
|
||||
for( y in 0...8 ) {
|
||||
var offset = ((y + ypos) * width + xpos) << 2;
|
||||
for( x in 0...8 ) {
|
||||
offset++; // skip alpha
|
||||
var R = img.get(offset++);
|
||||
var G = img.get(offset++);
|
||||
var B = img.get(offset++);
|
||||
YDU[pos] = ((( 0.29900) * R + ( 0.58700) * G + ( 0.11400) * B)) -128;
|
||||
UDU[pos] = (((-0.16874) * R + (-0.33126) * G + ( 0.50000) * B));
|
||||
VDU[pos] = ((( 0.50000) * R + (-0.41869) * G + (-0.08131) * B));
|
||||
pos++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function new( out : haxe.io.Output ) {
|
||||
//begin : lines added to initialize variables
|
||||
YTable = new Array<Int>();
|
||||
UVTable = new Array<Int>();
|
||||
fdtbl_Y = new Array<Float>();
|
||||
fdtbl_UV = new Array<Float>();
|
||||
for (i in 0...64) {
|
||||
YTable.push(0); UVTable.push(0);
|
||||
fdtbl_Y.push(0.0); fdtbl_UV.push(0.0);
|
||||
}
|
||||
|
||||
bitcode = new Map();
|
||||
category = new Map();
|
||||
byteout = out;
|
||||
bytenew = 0;
|
||||
bytepos = 7;
|
||||
|
||||
YDC_HT = new Map();
|
||||
UVDC_HT = new Map();
|
||||
YAC_HT = new Map();
|
||||
UVAC_HT = new Map();
|
||||
|
||||
YDU = new Array<Float>();
|
||||
UDU = new Array<Float>();
|
||||
VDU = new Array<Float>();
|
||||
DU = new Array<Float>();
|
||||
for (i in 0...64) {
|
||||
YDU.push(0.0); UDU.push(0.0); VDU.push(0.0); DU.push(0.0);
|
||||
}
|
||||
initZigZag();
|
||||
initLuminance();
|
||||
initChrominance();
|
||||
//end : lines added to initialize variables
|
||||
|
||||
// Create tables
|
||||
initHuffmanTbl();
|
||||
initCategoryNumber();
|
||||
}
|
||||
|
||||
public function write( image : Data ) {
|
||||
// init quality table
|
||||
var quality = image.quality;
|
||||
if( quality <= 0 ) quality = 1;
|
||||
if( quality > 100 ) quality = 100;
|
||||
var sf =
|
||||
if( quality < 50 ) Std.int( 5000 / quality )
|
||||
else Std.int( 200 - quality * 2 );
|
||||
initQuantTables(sf);
|
||||
|
||||
// Initialize bit writer
|
||||
bytenew = 0;
|
||||
bytepos = 7;
|
||||
|
||||
var width = image.width;
|
||||
var height = image.height;
|
||||
// Add JPEG headers
|
||||
writeWord(0xFFD8); // SOI
|
||||
writeAPP0();
|
||||
writeDQT();
|
||||
writeSOF0( width, height );
|
||||
writeDHT();
|
||||
writeSOS();
|
||||
|
||||
// Encode 8x8 macroblocks
|
||||
var DCY = 0.0;
|
||||
var DCU = 0.0;
|
||||
var DCV = 0.0;
|
||||
bytenew = 0;
|
||||
bytepos = 7;
|
||||
var ypos = 0;
|
||||
while( ypos < height ) {
|
||||
var xpos = 0;
|
||||
while( xpos < width ) {
|
||||
|
||||
// CORRECCIÓN CRÍTICA DE ESTADO: Limpieza de arreglos para evitar arrastre de valores (lo que el 'trace' estaba enmascarando)
|
||||
// Se asegura que los buffers sean cero antes de llenarlos con RGB2YUV si no se llenan completamente.
|
||||
for (k in 0...64) { YDU[k] = 0.0; UDU[k] = 0.0; VDU[k] = 0.0; }
|
||||
|
||||
RGB2YUV(image.pixels, width, xpos, ypos);
|
||||
DCY = processDU(YDU, fdtbl_Y, DCY, YDC_HT, YAC_HT);
|
||||
DCU = processDU(UDU, fdtbl_UV, DCU, UVDC_HT, UVAC_HT);
|
||||
DCV = processDU(VDU, fdtbl_UV, DCV, UVDC_HT, UVAC_HT);
|
||||
xpos += 8;
|
||||
}
|
||||
ypos += 8;
|
||||
}
|
||||
|
||||
// Do the bit alignment of the EOI marker
|
||||
if( bytepos >= 0 ) {
|
||||
var fillbits = new BitString( bytepos + 1, ( 1 << (bytepos + 1) ) - 1 );
|
||||
writeBits(fillbits);
|
||||
}
|
||||
|
||||
writeWord(0xFFD9); //EOI
|
||||
}
|
||||
}
|
||||
|
||||
private class BitString {
|
||||
public var len: Int;
|
||||
public var val: Int;
|
||||
|
||||
public function new( l: Int, v: Int ) {
|
||||
len = l;
|
||||
val = v;
|
||||
}
|
||||
}
|
||||
651
leenkx/Sources/iron/format/jpg/WriterOriginal.hx
Normal file
651
leenkx/Sources/iron/format/jpg/WriterOriginal.hx
Normal file
@ -0,0 +1,651 @@
|
||||
/*
|
||||
* format - Haxe File Formats
|
||||
*
|
||||
* JPG File Format
|
||||
* Copyright (C) 2007-2009 Thibault Imbert, AS3-to-Haxe by Michel Oster
|
||||
*
|
||||
* Copyright (c) 2009, The Haxe Project Contributors
|
||||
* All rights reserved.
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* - Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* - Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE HAXE PROJECT CONTRIBUTORS "AS IS" AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE HAXE PROJECT CONTRIBUTORS BE LIABLE FOR
|
||||
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
|
||||
* DAMAGE.
|
||||
*/
|
||||
package iron.format.jpg;
|
||||
|
||||
class Writer {
|
||||
var ZigZag: Array<Int>;
|
||||
|
||||
// Static table initialization
|
||||
function initZigZag() {
|
||||
ZigZag = [
|
||||
0, 1, 5, 6,14,15,27,28,
|
||||
2, 4, 7,13,16,26,29,42,
|
||||
3, 8,12,17,25,30,41,43,
|
||||
9,11,18,24,31,40,44,53,
|
||||
10,19,23,32,39,45,52,54,
|
||||
20,22,33,38,46,51,55,60,
|
||||
21,34,37,47,50,56,59,61,
|
||||
35,36,48,49,57,58,62,63
|
||||
];
|
||||
}
|
||||
|
||||
var YTable: Array<Int>; // = new Array(64);
|
||||
var UVTable: Array<Int>; // = new Array(64);
|
||||
var fdtbl_Y: Array<Float>; // = new Array(64);
|
||||
var fdtbl_UV: Array<Float>; // = new Array(64);
|
||||
|
||||
function initQuantTables(sf: Int) {
|
||||
var YQT: Array<Int> = [
|
||||
16, 11, 10, 16, 24, 40, 51, 61,
|
||||
12, 12, 14, 19, 26, 58, 60, 55,
|
||||
14, 13, 16, 24, 40, 57, 69, 56,
|
||||
14, 17, 22, 29, 51, 87, 80, 62,
|
||||
18, 22, 37, 56, 68,109,103, 77,
|
||||
24, 35, 55, 64, 81,104,113, 92,
|
||||
49, 64, 78, 87,103,121,120,101,
|
||||
72, 92, 95, 98,112,100,103, 99
|
||||
];
|
||||
for (i in 0...64) {
|
||||
var t: Int = Math.floor( (YQT[i] * sf + 50) / 100 );
|
||||
if( t < 1 ) t = 1;
|
||||
else if( t > 255 ) t = 255;
|
||||
YTable[ ZigZag[i] ] = t;
|
||||
}
|
||||
var UVQT: Array<Int> = [
|
||||
17, 18, 24, 47, 99, 99, 99, 99,
|
||||
18, 21, 26, 66, 99, 99, 99, 99,
|
||||
24, 26, 56, 99, 99, 99, 99, 99,
|
||||
47, 66, 99, 99, 99, 99, 99, 99,
|
||||
99, 99, 99, 99, 99, 99, 99, 99,
|
||||
99, 99, 99, 99, 99, 99, 99, 99,
|
||||
99, 99, 99, 99, 99, 99, 99, 99,
|
||||
99, 99, 99, 99, 99, 99, 99, 99
|
||||
];
|
||||
for( j in 0...64 ) {
|
||||
var u: Int = Math.floor( (UVQT[j] * sf + 50) / 100 );
|
||||
if( u < 1 ) u = 1;
|
||||
else if( u > 255 ) u = 255;
|
||||
UVTable[ ZigZag[j] ] = u;
|
||||
}
|
||||
var aasf: Array<Float> = [
|
||||
1.0, 1.387039845, 1.306562965, 1.175875602,
|
||||
1.0, 0.785694958, 0.541196100, 0.275899379
|
||||
];
|
||||
var k = 0;
|
||||
for( row in 0...8 ) {
|
||||
for( col in 0...8 ) {
|
||||
fdtbl_Y[k] = (1.0 / (YTable [ZigZag[k]] * aasf[row] * aasf[col] * 8.0));
|
||||
fdtbl_UV[k] = (1.0 / (UVTable[ZigZag[k]] * aasf[row] * aasf[col] * 8.0));
|
||||
k++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var std_dc_luminance_nrcodes: Array<Int>;
|
||||
var std_dc_luminance_values: haxe.io.Bytes;
|
||||
var std_ac_luminance_nrcodes: Array<Int>;
|
||||
var std_ac_luminance_values: haxe.io.Bytes;
|
||||
|
||||
function initLuminance() {
|
||||
std_dc_luminance_nrcodes = [0,0,1,5,1,1,1,1,1,1,0,0,0,0,0,0,0];
|
||||
std_dc_luminance_values = strIntsToBytes( '0,1,2,3,4,5,6,7,8,9,10,11' );
|
||||
std_ac_luminance_nrcodes = [0,0,2,1,3,3,2,4,3,5,5,4,4,0,0,1,0x7d];
|
||||
std_ac_luminance_values = strIntsToBytes(
|
||||
'0x01,0x02,0x03,0x00,0x04,0x11,0x05,0x12,' +
|
||||
'0x21,0x31,0x41,0x06,0x13,0x51,0x61,0x07,' +
|
||||
'0x22,0x71,0x14,0x32,0x81,0x91,0xa1,0x08,' +
|
||||
'0x23,0x42,0xb1,0xc1,0x15,0x52,0xd1,0xf0,' +
|
||||
'0x24,0x33,0x62,0x72,0x82,0x09,0x0a,0x16,' +
|
||||
'0x17,0x18,0x19,0x1a,0x25,0x26,0x27,0x28,' +
|
||||
'0x29,0x2a,0x34,0x35,0x36,0x37,0x38,0x39,' +
|
||||
'0x3a,0x43,0x44,0x45,0x46,0x47,0x48,0x49,' +
|
||||
'0x4a,0x53,0x54,0x55,0x56,0x57,0x58,0x59,' +
|
||||
'0x5a,0x63,0x64,0x65,0x66,0x67,0x68,0x69,' +
|
||||
'0x6a,0x73,0x74,0x75,0x76,0x77,0x78,0x79,' +
|
||||
'0x7a,0x83,0x84,0x85,0x86,0x87,0x88,0x89,' +
|
||||
'0x8a,0x92,0x93,0x94,0x95,0x96,0x97,0x98,' +
|
||||
'0x99,0x9a,0xa2,0xa3,0xa4,0xa5,0xa6,0xa7,' +
|
||||
'0xa8,0xa9,0xaa,0xb2,0xb3,0xb4,0xb5,0xb6,' +
|
||||
'0xb7,0xb8,0xb9,0xba,0xc2,0xc3,0xc4,0xc5,' +
|
||||
'0xc6,0xc7,0xc8,0xc9,0xca,0xd2,0xd3,0xd4,' +
|
||||
'0xd5,0xd6,0xd7,0xd8,0xd9,0xda,0xe1,0xe2,' +
|
||||
'0xe3,0xe4,0xe5,0xe6,0xe7,0xe8,0xe9,0xea,' +
|
||||
'0xf1,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7,0xf8,' +
|
||||
'0xf9,0xfa'
|
||||
);
|
||||
}
|
||||
|
||||
function strIntsToBytes( s: String ) {
|
||||
var len = s.length;
|
||||
var b = new haxe.io.BytesBuffer();
|
||||
var val = 0;
|
||||
var i = 0;
|
||||
for( j in 0...len ) {
|
||||
if( s.charAt( j ) == ',' ) {
|
||||
val = Std.parseInt( s.substr(i, j - i) );
|
||||
b.addByte( val );
|
||||
i = j + 1;
|
||||
}
|
||||
}
|
||||
if( i < len ) {
|
||||
val = Std.parseInt( s.substr(i) );
|
||||
b.addByte( val );
|
||||
}
|
||||
return b.getBytes();
|
||||
}
|
||||
|
||||
var std_dc_chrominance_nrcodes: Array<Int>;
|
||||
var std_dc_chrominance_values: haxe.io.Bytes;
|
||||
var std_ac_chrominance_nrcodes: Array<Int>;
|
||||
var std_ac_chrominance_values: haxe.io.Bytes;
|
||||
|
||||
function initChrominance() {
|
||||
std_dc_chrominance_nrcodes = [0,0,3,1,1,1,1,1,1,1,1,1,0,0,0,0,0];
|
||||
std_dc_chrominance_values = strIntsToBytes( '0,1,2,3,4,5,6,7,8,9,10,11' );
|
||||
std_ac_chrominance_nrcodes = [0,0,2,1,2,4,4,3,4,7,5,4,4,0,1,2,0x77];
|
||||
std_ac_chrominance_values = strIntsToBytes(
|
||||
'0x00,0x01,0x02,0x03,0x11,0x04,0x05,0x21,' +
|
||||
'0x31,0x06,0x12,0x41,0x51,0x07,0x61,0x71,' +
|
||||
'0x13,0x22,0x32,0x81,0x08,0x14,0x42,0x91,' +
|
||||
'0xa1,0xb1,0xc1,0x09,0x23,0x33,0x52,0xf0,' +
|
||||
'0x15,0x62,0x72,0xd1,0x0a,0x16,0x24,0x34,' +
|
||||
'0xe1,0x25,0xf1,0x17,0x18,0x19,0x1a,0x26,' +
|
||||
'0x27,0x28,0x29,0x2a,0x35,0x36,0x37,0x38,' +
|
||||
'0x39,0x3a,0x43,0x44,0x45,0x46,0x47,0x48,' +
|
||||
'0x49,0x4a,0x53,0x54,0x55,0x56,0x57,0x58,' +
|
||||
'0x59,0x5a,0x63,0x64,0x65,0x66,0x67,0x68,' +
|
||||
'0x69,0x6a,0x73,0x74,0x75,0x76,0x77,0x78,' +
|
||||
'0x79,0x7a,0x82,0x83,0x84,0x85,0x86,0x87,' +
|
||||
'0x88,0x89,0x8a,0x92,0x93,0x94,0x95,0x96,' +
|
||||
'0x97,0x98,0x99,0x9a,0xa2,0xa3,0xa4,0xa5,' +
|
||||
'0xa6,0xa7,0xa8,0xa9,0xaa,0xb2,0xb3,0xb4,' +
|
||||
'0xb5,0xb6,0xb7,0xb8,0xb9,0xba,0xc2,0xc3,' +
|
||||
'0xc4,0xc5,0xc6,0xc7,0xc8,0xc9,0xca,0xd2,' +
|
||||
'0xd3,0xd4,0xd5,0xd6,0xd7,0xd8,0xd9,0xda,' +
|
||||
'0xe2,0xe3,0xe4,0xe5,0xe6,0xe7,0xe8,0xe9,' +
|
||||
'0xea,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7,0xf8,' +
|
||||
'0xf9,0xfa'
|
||||
);
|
||||
}
|
||||
|
||||
var YDC_HT: Map<Int,BitString>;
|
||||
var UVDC_HT: Map<Int,BitString>;
|
||||
var YAC_HT: Map<Int,BitString>;
|
||||
var UVAC_HT: Map<Int,BitString>;
|
||||
|
||||
function initHuffmanTbl() {
|
||||
YDC_HT = computeHuffmanTbl(std_dc_luminance_nrcodes, std_dc_luminance_values);
|
||||
UVDC_HT = computeHuffmanTbl(std_dc_chrominance_nrcodes, std_dc_chrominance_values);
|
||||
YAC_HT = computeHuffmanTbl(std_ac_luminance_nrcodes, std_ac_luminance_values);
|
||||
UVAC_HT = computeHuffmanTbl(std_ac_chrominance_nrcodes, std_ac_chrominance_values);
|
||||
}
|
||||
|
||||
function computeHuffmanTbl(nrcodes: Array<Int>, std_table: haxe.io.Bytes): Map<Int,BitString> {
|
||||
var codevalue = 0;
|
||||
var pos_in_table = 0;
|
||||
var HT: Map<Int,BitString> = new Map();
|
||||
for( k in 1...17 ) {
|
||||
var end = nrcodes[k];
|
||||
for( j in 0...end ) {
|
||||
var idx: Int = std_table.get( pos_in_table );
|
||||
HT.set( idx, new BitString( k, codevalue ) );
|
||||
pos_in_table++;
|
||||
codevalue++;
|
||||
}
|
||||
codevalue *= 2;
|
||||
}
|
||||
return HT;
|
||||
}
|
||||
|
||||
var bitcode: Map<Int,BitString>;
|
||||
var category: Map<Int,Int>;
|
||||
|
||||
function initCategoryNumber() {
|
||||
var nrlower = 1;
|
||||
var nrupper = 2;
|
||||
var idx: Int;
|
||||
for (cat in 1...16) {
|
||||
//Positive numbers
|
||||
for( nr in nrlower...nrupper ) {
|
||||
idx = 32767 + nr;
|
||||
category.set( idx, cat );
|
||||
bitcode.set( idx, new BitString( cat, nr ) );
|
||||
}
|
||||
//Negative numbers
|
||||
var nrneg: Int = -(nrupper - 1);
|
||||
while( nrneg <= -nrlower ) {
|
||||
idx = 32767 + nrneg;
|
||||
category.set( idx, cat );
|
||||
bitcode.set( idx, new BitString( cat, nrupper - 1 + nrneg ) );
|
||||
nrneg++;
|
||||
}
|
||||
nrlower <<= 1;
|
||||
nrupper <<= 1;
|
||||
}
|
||||
}
|
||||
|
||||
// IO functions
|
||||
var byteout: haxe.io.Output;
|
||||
var bytenew: Int;
|
||||
var bytepos: Int;
|
||||
|
||||
function writeBits(bs: BitString) {
|
||||
var value: Int = bs.val;
|
||||
var posval: Int = bs.len - 1;
|
||||
while( posval >= 0 ) {
|
||||
//if (value & uint(1 << posval) ) {
|
||||
if( (value & (1 << posval)) != 0 ) { //<- CORRECT ?
|
||||
//bytenew |= uint(1 << bytepos);
|
||||
bytenew |= (1 << bytepos);
|
||||
}
|
||||
posval--;
|
||||
bytepos--;
|
||||
if( bytepos < 0 ) {
|
||||
if( bytenew == 0xFF ) {
|
||||
b(0xFF);
|
||||
b(0);
|
||||
}
|
||||
else {
|
||||
b(bytenew);
|
||||
}
|
||||
bytepos = 7;
|
||||
bytenew = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function writeWord( val: Int ) {
|
||||
b( (val >> 8) & 0xFF );
|
||||
b( val & 0xFF );
|
||||
}
|
||||
|
||||
// DCT & quantization core
|
||||
|
||||
function fDCTQuant(data: Array<Float>, fdtbl: Array<Float>): Array<Float> {
|
||||
/* Pass 1: process rows. */
|
||||
var dataOff = 0;
|
||||
for (i in 0...8) {
|
||||
var tmp0: Float = data[dataOff + 0] + data[dataOff + 7];
|
||||
var tmp7: Float = data[dataOff + 0] - data[dataOff + 7];
|
||||
var tmp1: Float = data[dataOff + 1] + data[dataOff + 6];
|
||||
var tmp6: Float = data[dataOff + 1] - data[dataOff + 6];
|
||||
var tmp2: Float = data[dataOff + 2] + data[dataOff + 5];
|
||||
var tmp5: Float = data[dataOff + 2] - data[dataOff + 5];
|
||||
var tmp3: Float = data[dataOff + 3] + data[dataOff + 4];
|
||||
var tmp4: Float = data[dataOff + 3] - data[dataOff + 4];
|
||||
|
||||
/* Even part */
|
||||
var tmp10: Float = tmp0 + tmp3; /* phase 2 */
|
||||
var tmp13: Float = tmp0 - tmp3;
|
||||
var tmp11: Float = tmp1 + tmp2;
|
||||
var tmp12: Float = tmp1 - tmp2;
|
||||
|
||||
data[dataOff + 0] = tmp10 + tmp11; /* phase 3 */
|
||||
data[dataOff + 4] = tmp10 - tmp11;
|
||||
|
||||
var z1: Float = (tmp12 + tmp13) * 0.707106781; /* c4 */
|
||||
data[dataOff + 2] = tmp13 + z1; /* phase 5 */
|
||||
data[dataOff + 6] = tmp13 - z1;
|
||||
|
||||
/* Odd part */
|
||||
tmp10 = tmp4 + tmp5; /* phase 2 */
|
||||
tmp11 = tmp5 + tmp6;
|
||||
tmp12 = tmp6 + tmp7;
|
||||
|
||||
/* The rotator is modified from fig 4-8 to avoid extra negations. */
|
||||
var z5: Float = (tmp10 - tmp12) * 0.382683433; /* c6 */
|
||||
var z2: Float = 0.541196100 * tmp10 + z5; /* c2-c6 */
|
||||
var z4: Float = 1.306562965 * tmp12 + z5; /* c2+c6 */
|
||||
var z3: Float = tmp11 * 0.707106781; /* c4 */
|
||||
|
||||
var z11: Float = tmp7 + z3; /* phase 5 */
|
||||
var z13: Float = tmp7 - z3;
|
||||
|
||||
data[dataOff + 5] = z13 + z2; /* phase 6 */
|
||||
data[dataOff + 3] = z13 - z2;
|
||||
data[dataOff + 1] = z11 + z4;
|
||||
data[dataOff + 7] = z11 - z4;
|
||||
|
||||
dataOff += 8; /* advance pointer to next row */
|
||||
}
|
||||
|
||||
/* Pass 2: process columns. */
|
||||
dataOff = 0;
|
||||
for (j in 0...8) {
|
||||
var tmp0p2: Float = data[dataOff+ 0] + data[dataOff+56];
|
||||
var tmp7p2: Float = data[dataOff+ 0] - data[dataOff+56];
|
||||
var tmp1p2: Float = data[dataOff+ 8] + data[dataOff+48];
|
||||
var tmp6p2: Float = data[dataOff+ 8] - data[dataOff+48];
|
||||
var tmp2p2: Float = data[dataOff+16] + data[dataOff+40];
|
||||
var tmp5p2: Float = data[dataOff+16] - data[dataOff+40];
|
||||
var tmp3p2: Float = data[dataOff+24] + data[dataOff+32];
|
||||
var tmp4p2: Float = data[dataOff+24] - data[dataOff+32];
|
||||
|
||||
/* Even part */
|
||||
var tmp10p2: Float = tmp0p2 + tmp3p2; /* phase 2 */
|
||||
var tmp13p2: Float = tmp0p2 - tmp3p2;
|
||||
var tmp11p2: Float = tmp1p2 + tmp2p2;
|
||||
var tmp12p2: Float = tmp1p2 - tmp2p2;
|
||||
|
||||
data[dataOff+ 0] = tmp10p2 + tmp11p2; /* phase 3 */
|
||||
data[dataOff+32] = tmp10p2 - tmp11p2;
|
||||
|
||||
var z1p2: Float = (tmp12p2 + tmp13p2) * 0.707106781; /* c4 */
|
||||
data[dataOff+16] = tmp13p2 + z1p2; /* phase 5 */
|
||||
data[dataOff+48] = tmp13p2 - z1p2;
|
||||
|
||||
/* Odd part */
|
||||
tmp10p2 = tmp4p2 + tmp5p2; /* phase 2 */
|
||||
tmp11p2 = tmp5p2 + tmp6p2;
|
||||
tmp12p2 = tmp6p2 + tmp7p2;
|
||||
|
||||
/* The rotator is modified from fig 4-8 to avoid extra negations. */
|
||||
var z5p2: Float = (tmp10p2 - tmp12p2) * 0.382683433; /* c6 */
|
||||
var z2p2: Float = 0.541196100 * tmp10p2 + z5p2; /* c2-c6 */
|
||||
var z4p2: Float = 1.306562965 * tmp12p2 + z5p2; /* c2+c6 */
|
||||
var z3p2: Float= tmp11p2 * 0.707106781; /* c4 */
|
||||
|
||||
var z11p2: Float = tmp7p2 + z3p2; /* phase 5 */
|
||||
var z13p2: Float = tmp7p2 - z3p2;
|
||||
|
||||
data[dataOff+40] = z13p2 + z2p2; /* phase 6 */
|
||||
data[dataOff+24] = z13p2 - z2p2;
|
||||
data[dataOff+ 8] = z11p2 + z4p2;
|
||||
data[dataOff+56] = z11p2 - z4p2;
|
||||
|
||||
dataOff++; /* advance pointer to next column */
|
||||
}
|
||||
|
||||
// Quantize/descale the coefficients
|
||||
for (k in 0...64) {
|
||||
// Apply the quantization and scaling factor & Round to nearest integer
|
||||
data[k] = Math.round(data[k] * fdtbl[k]);
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
// Chunk writing
|
||||
|
||||
inline function b(v) {
|
||||
byteout.writeByte(v);
|
||||
}
|
||||
|
||||
function writeAPP0() {
|
||||
b(0xFF); b(0xE0); //<- marker 0xFFE0
|
||||
b(0); b(16); //<- length
|
||||
b("J".code); // J
|
||||
b("F".code);
|
||||
b("I".code);
|
||||
b("F".code);
|
||||
b(0);
|
||||
b(1); // versionhi
|
||||
b(1); // versionlo
|
||||
b(0); // xyunits
|
||||
b(0); b(1); // xdensity
|
||||
b(0); b(1); // ydensity
|
||||
b(0); // thumbnwidth
|
||||
b(0); // thumbnheight
|
||||
}
|
||||
function writeDQT() {
|
||||
b(0xFF); b(0xDB); //<- marker 0xFFDB
|
||||
b(0); b(132); //<- length
|
||||
b(0);
|
||||
for( j in 0...64 )
|
||||
b(YTable[j]);
|
||||
b(1);
|
||||
for( j in 0...64 )
|
||||
b(UVTable[j]);
|
||||
}
|
||||
function writeSOF0(width: Int, height: Int) {
|
||||
b(0xFF); b(0xC0); //<- marker 0xFFC0
|
||||
b(0); b(17); //<- length, truecolor YUV JPG
|
||||
b(8); // precision
|
||||
b( (height>>8) & 0xFF );
|
||||
b( height & 0xFF );
|
||||
b( (width>>8) & 0xFF );
|
||||
b( width & 0xFF );
|
||||
b(3); // nrofcomponents
|
||||
b(1); // IdY
|
||||
b(0x11); // HVY
|
||||
b(0); // QTY
|
||||
b(2); // IdU
|
||||
b(0x11); // HVU
|
||||
b(1); // QTU
|
||||
b(3); // IdV
|
||||
b(0x11); // HVV
|
||||
b(1); // QTV
|
||||
}
|
||||
|
||||
function writeDHT() {
|
||||
b(0xFF); b(0xC4); //<- marker 0xFFC4
|
||||
b(0x01); b(0xA2); //<- length
|
||||
b(0); // HTYDCinfo
|
||||
for( j in 1...17 )
|
||||
b(std_dc_luminance_nrcodes[j]);
|
||||
byteout.write(std_dc_luminance_values);
|
||||
|
||||
b(0x10); // HTYACinfo
|
||||
for( j in 1...17 )
|
||||
b(std_ac_luminance_nrcodes[j]);
|
||||
byteout.write(std_ac_luminance_values);
|
||||
|
||||
b(1); // HTUDCinfo
|
||||
for( j in 1...17 )
|
||||
b(std_dc_chrominance_nrcodes[j]);
|
||||
byteout.write(std_dc_chrominance_values);
|
||||
|
||||
b(0x11); // HTUACinfo
|
||||
for( j in 1...17 )
|
||||
b(std_ac_chrominance_nrcodes[j]);
|
||||
byteout.write(std_ac_chrominance_values);
|
||||
}
|
||||
|
||||
function writeSOS() {
|
||||
b(0xFF); b(0xDA); //<- marker 0xFFDA
|
||||
b(0); b(12); //<- length
|
||||
b(3); // nrofcomponents
|
||||
b(1); // IdY
|
||||
b(0); // HTY
|
||||
b(2); // IdU
|
||||
b(0x11); // HTU
|
||||
b(3); // IdV
|
||||
b(0x11); // HTV
|
||||
b(0); // Ss
|
||||
b(0x3F); // Se
|
||||
b(0); // Bf
|
||||
}
|
||||
|
||||
// Core processing
|
||||
var DU: Array<Float>; //<- initialized in function new JPEGEncoder()
|
||||
|
||||
function processDU(CDU: Array<Float>, fdtbl: Array<Float>, DC: Float, HTDC: Map<Int,BitString>, HTAC: Map<Int,BitString>): Float {
|
||||
var EOB: BitString = HTAC.get( 0x00 );
|
||||
var M16zeroes: BitString = HTAC.get( 0xF0 );
|
||||
|
||||
var DU_DCT: Array<Float> = fDCTQuant(CDU, fdtbl);
|
||||
//ZigZag reorder
|
||||
for (i in 0...64) {
|
||||
DU[ ZigZag[i] ] = DU_DCT[i];
|
||||
}
|
||||
var idx: Int;
|
||||
var Diff = Std.int( DU[0] - DC );
|
||||
DC = DU[0];
|
||||
//Encode DC
|
||||
if( Diff == 0 ) {
|
||||
writeBits( HTDC.get(0) ); // Diff might be 0
|
||||
} else {
|
||||
idx = 32767 + Diff;
|
||||
writeBits(HTDC.get( category.get( idx ) ));
|
||||
writeBits( bitcode.get( idx ) );
|
||||
}
|
||||
|
||||
//Encode ACs
|
||||
var end0pos = 63;
|
||||
//for (; (end0pos>0)&&(DU[end0pos]==0); end0pos--) { };
|
||||
while( (end0pos > 0) && ( DU[end0pos] == 0.0 ) ) end0pos--;
|
||||
|
||||
//end0pos = first element in reverse order !=0
|
||||
if ( end0pos == 0 ) {
|
||||
writeBits(EOB);
|
||||
return DC;
|
||||
}
|
||||
var i = 1;
|
||||
while ( i <= end0pos ) {
|
||||
var startpos = i;
|
||||
//for (; (DU[i]==0) && (i<=end0pos); i++) { }; <- it's a 'while' loop
|
||||
while( ( DU[i] == 0.0 ) && ( i <= end0pos ) ) i++;
|
||||
|
||||
var nrzeroes: Int = i - startpos;
|
||||
if ( nrzeroes >= 16 ) {
|
||||
//for (var nrmarker: Int=1; nrmarker <= nrzeroes/16; nrmarker++) {
|
||||
for( nrmarker in 0...(nrzeroes >> 4) ) writeBits(M16zeroes);
|
||||
nrzeroes &= 0xF;
|
||||
}
|
||||
idx = 32767 + Std.int( DU[i] ); //<- line added
|
||||
writeBits( HTAC.get( nrzeroes * 16 + category.get( idx ) ) );
|
||||
writeBits( bitcode.get( idx ) );
|
||||
i++;
|
||||
}
|
||||
if( end0pos != 63 ) writeBits(EOB);
|
||||
return DC;
|
||||
}
|
||||
|
||||
var YDU: Array<Float>;
|
||||
var UDU: Array<Float>;
|
||||
var VDU: Array<Float>;
|
||||
|
||||
function RGB2YUV(img: haxe.io.Bytes, width : Int, xpos: Int, ypos: Int) {
|
||||
var pos = 0;
|
||||
for( y in 0...8 ) {
|
||||
var offset = ((y + ypos) * width + xpos) << 2;
|
||||
for( x in 0...8 ) {
|
||||
offset++; // skip alpha
|
||||
var R = img.get(offset++);
|
||||
var G = img.get(offset++);
|
||||
var B = img.get(offset++);
|
||||
YDU[pos] = ((( 0.29900) * R + ( 0.58700) * G + ( 0.11400) * B)) -128;
|
||||
UDU[pos] = (((-0.16874) * R + (-0.33126) * G + ( 0.50000) * B));
|
||||
VDU[pos] = ((( 0.50000) * R + (-0.41869) * G + (-0.08131) * B));
|
||||
pos++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function new( out : haxe.io.Output ) {
|
||||
//begin : lines added to initialize variables
|
||||
YTable = new Array<Int>();
|
||||
UVTable = new Array<Int>();
|
||||
fdtbl_Y = new Array<Float>();
|
||||
fdtbl_UV = new Array<Float>();
|
||||
for (i in 0...64) {
|
||||
YTable.push(0); UVTable.push(0);
|
||||
fdtbl_Y.push(0.0); fdtbl_UV.push(0.0);
|
||||
}
|
||||
|
||||
bitcode = new Map(); //<- 65535 elements <BitString>
|
||||
category = new Map(); //<- 65535 elements <Int>
|
||||
byteout = out;
|
||||
bytenew = 0;
|
||||
bytepos = 7;
|
||||
|
||||
YDC_HT = new Map();
|
||||
UVDC_HT = new Map();
|
||||
YAC_HT = new Map();
|
||||
UVAC_HT = new Map();
|
||||
|
||||
YDU = new Array<Float>(); //<- 64 elements
|
||||
UDU = new Array<Float>();
|
||||
VDU = new Array<Float>();
|
||||
DU = new Array<Float>();
|
||||
for (i in 0...64) {
|
||||
YDU.push(0.0); UDU.push(0.0); VDU.push(0.0); DU.push(0.0);
|
||||
}
|
||||
initZigZag();
|
||||
initLuminance();
|
||||
initChrominance();
|
||||
//end : lines added to initialize variables
|
||||
|
||||
// Create tables
|
||||
initHuffmanTbl();
|
||||
initCategoryNumber();
|
||||
}
|
||||
|
||||
public function write( image : Data ) {
|
||||
// init quality table
|
||||
var quality = image.quality;
|
||||
if( quality <= 0 ) quality = 1;
|
||||
if( quality > 100 ) quality = 100;
|
||||
var sf =
|
||||
if( quality < 50 ) Std.int( 5000 / quality )
|
||||
else Std.int( 200 - quality * 2 );
|
||||
initQuantTables(sf);
|
||||
|
||||
// Initialize bit writer
|
||||
bytenew = 0;
|
||||
bytepos = 7;
|
||||
|
||||
var width = image.width;
|
||||
var height = image.height;
|
||||
// Add JPEG headers
|
||||
writeWord(0xFFD8); // SOI
|
||||
writeAPP0();
|
||||
writeDQT();
|
||||
writeSOF0( width, height );
|
||||
writeDHT();
|
||||
writeSOS();
|
||||
|
||||
// Encode 8x8 macroblocks
|
||||
var DCY = 0.0;
|
||||
var DCU = 0.0;
|
||||
var DCV = 0.0;
|
||||
bytenew = 0;
|
||||
bytepos = 7;
|
||||
var ypos = 0;
|
||||
while( ypos < height ) {
|
||||
var xpos = 0;
|
||||
while( xpos < width ) {
|
||||
RGB2YUV(image.pixels, width, xpos, ypos);
|
||||
DCY = processDU(YDU, fdtbl_Y, DCY, YDC_HT, YAC_HT);
|
||||
DCU = processDU(UDU, fdtbl_UV, DCU, UVDC_HT, UVAC_HT);
|
||||
DCV = processDU(VDU, fdtbl_UV, DCV, UVDC_HT, UVAC_HT);
|
||||
xpos += 8;
|
||||
}
|
||||
ypos += 8;
|
||||
}
|
||||
|
||||
// Do the bit alignment of the EOI marker
|
||||
if( bytepos >= 0 ) {
|
||||
var fillbits = new BitString( bytepos + 1, ( 1 << (bytepos + 1) ) - 1 );
|
||||
writeBits(fillbits);
|
||||
}
|
||||
|
||||
writeWord(0xFFD9); //EOI
|
||||
}
|
||||
}
|
||||
|
||||
private class BitString {
|
||||
public var len: Int;
|
||||
public var val: Int;
|
||||
|
||||
public function new( l: Int, v: Int ) {
|
||||
len = l;
|
||||
val = v;
|
||||
}
|
||||
}
|
||||
56
leenkx/Sources/iron/format/wav/Data.hx
Normal file
56
leenkx/Sources/iron/format/wav/Data.hx
Normal file
@ -0,0 +1,56 @@
|
||||
/*
|
||||
* format - Haxe File Formats
|
||||
*
|
||||
* WAVE File Format
|
||||
* Copyright (C) 2009 Robin Palotai
|
||||
*
|
||||
* Copyright (c) 2009, The Haxe Project Contributors
|
||||
* All rights reserved.
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* - Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* - Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE HAXE PROJECT CONTRIBUTORS "AS IS" AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE HAXE PROJECT CONTRIBUTORS BE LIABLE FOR
|
||||
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
|
||||
* DAMAGE.
|
||||
*/
|
||||
package iron.format.wav;
|
||||
|
||||
typedef WAVE = {
|
||||
header : WAVEHeader,
|
||||
data : haxe.io.Bytes,
|
||||
cuePoints : Array<CuePoint>
|
||||
}
|
||||
|
||||
typedef WAVEHeader = {
|
||||
format : WAVEFormat,
|
||||
channels : Int,
|
||||
samplingRate : Int,
|
||||
byteRate : Int, // samplingRate * channels * bitsPerSample / 8
|
||||
blockAlign : Int, // channels * bitsPerSample / 8
|
||||
bitsPerSample : Int
|
||||
}
|
||||
|
||||
typedef CuePoint = {
|
||||
id : Int,
|
||||
sampleOffset : Int
|
||||
}
|
||||
|
||||
enum WAVEFormat {
|
||||
WF_PCM;
|
||||
}
|
||||
|
||||
|
||||
155
leenkx/Sources/iron/format/wav/Reader.hx
Normal file
155
leenkx/Sources/iron/format/wav/Reader.hx
Normal file
@ -0,0 +1,155 @@
|
||||
/*
|
||||
* format - Haxe File Formats
|
||||
*
|
||||
* WAVE File Format
|
||||
* Copyright (C) 2009 Robin Palotai
|
||||
*
|
||||
* Copyright (c) 2009, The Haxe Project Contributors
|
||||
* All rights reserved.
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* - Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* - Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE HAXE PROJECT CONTRIBUTORS "AS IS" AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE HAXE PROJECT CONTRIBUTORS BE LIABLE FOR
|
||||
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
|
||||
* DAMAGE.
|
||||
*/
|
||||
package iron.format.wav;
|
||||
import iron.format.wav.Data;
|
||||
|
||||
class Reader {
|
||||
|
||||
var i : haxe.io.Input;
|
||||
var version : Int;
|
||||
|
||||
public function new(i) {
|
||||
this.i = i;
|
||||
i.bigEndian = false;
|
||||
}
|
||||
|
||||
inline function readInt() {
|
||||
#if haxe3
|
||||
return i.readInt32();
|
||||
#else
|
||||
return i.readUInt30();
|
||||
#end
|
||||
}
|
||||
|
||||
public function read() : WAVE {
|
||||
|
||||
if (i.readString(4) != "RIFF")
|
||||
throw "RIFF header expected";
|
||||
|
||||
var len = readInt();
|
||||
|
||||
if (i.readString(4) != "WAVE")
|
||||
throw "WAVE signature not found";
|
||||
|
||||
var fmt = i.readString(4);
|
||||
while(fmt != "fmt ") {
|
||||
switch( fmt ) {
|
||||
case "JUNK": //protool
|
||||
var junkLen = i.readInt32();
|
||||
i.read(junkLen);
|
||||
fmt = i.readString(4);
|
||||
case "bext":
|
||||
var bextLen = i.readInt32();
|
||||
i.read(bextLen);
|
||||
fmt = i.readString(4);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ( fmt != "fmt " )
|
||||
throw "unsupported wave chunk "+fmt;
|
||||
|
||||
var fmtlen = readInt();
|
||||
var format = switch (i.readUInt16()) {
|
||||
case 1,3: WF_PCM;
|
||||
default: throw "only PCM (uncompressed) WAV files are supported";
|
||||
}
|
||||
var channels = i.readUInt16();
|
||||
var samplingRate = readInt();
|
||||
var byteRate = readInt();
|
||||
var blockAlign = i.readUInt16();
|
||||
var bitsPerSample = i.readUInt16();
|
||||
|
||||
if (fmtlen > 16)
|
||||
i.read(fmtlen - 16);
|
||||
|
||||
var nextChunk = i.readString (4);
|
||||
while (nextChunk != "data") {
|
||||
// read past other subchunks
|
||||
i.read(readInt());
|
||||
nextChunk = i.readString (4);
|
||||
}
|
||||
|
||||
// data
|
||||
if (nextChunk != "data")
|
||||
throw "expected data subchunk";
|
||||
|
||||
var datalen = readInt();
|
||||
|
||||
var data : haxe.io.Bytes;
|
||||
try {
|
||||
data = i.read(datalen);
|
||||
} catch (e : haxe.io.Eof) {
|
||||
throw "Invalid chunk data length";
|
||||
}
|
||||
|
||||
var cuePoints = new Array<CuePoint>();
|
||||
try {
|
||||
|
||||
while (true) {
|
||||
var nextChunk = i.readString (4);
|
||||
switch (nextChunk) {
|
||||
case "cue ":
|
||||
readInt();
|
||||
var nbCuePoints = readInt();
|
||||
|
||||
for (_ in 0...nbCuePoints) {
|
||||
var cueId = readInt();
|
||||
readInt();
|
||||
i.readString(4);
|
||||
readInt();
|
||||
readInt();
|
||||
var cueSampleOffset = readInt();
|
||||
cuePoints.push({ id : cueId, sampleOffset: cueSampleOffset });
|
||||
}
|
||||
default:
|
||||
var n = readInt();
|
||||
if( n < 0 ) break;
|
||||
i.read(n);
|
||||
}
|
||||
}
|
||||
|
||||
} catch (e : haxe.io.Eof) { }
|
||||
|
||||
return {
|
||||
header: {
|
||||
format: format,
|
||||
channels: channels,
|
||||
samplingRate: samplingRate,
|
||||
byteRate: byteRate,
|
||||
blockAlign: blockAlign,
|
||||
bitsPerSample: bitsPerSample
|
||||
},
|
||||
data: data,
|
||||
cuePoints: cuePoints
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
72
leenkx/Sources/iron/format/wav/Writer.hx
Normal file
72
leenkx/Sources/iron/format/wav/Writer.hx
Normal file
@ -0,0 +1,72 @@
|
||||
/*
|
||||
* format - Haxe File Formats
|
||||
*
|
||||
* WAVE File Format
|
||||
* Copyright (C) 2009 Robin Palotai
|
||||
*
|
||||
* Copyright (c) 2009, The Haxe Project Contributors
|
||||
* All rights reserved.
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* - Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* - Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE HAXE PROJECT CONTRIBUTORS "AS IS" AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE HAXE PROJECT CONTRIBUTORS BE LIABLE FOR
|
||||
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
|
||||
* DAMAGE.
|
||||
*/
|
||||
|
||||
package iron.format.wav;
|
||||
import iron.format.wav.Data;
|
||||
|
||||
class Writer {
|
||||
|
||||
var o : haxe.io.Output;
|
||||
|
||||
public function new(output : haxe.io.Output) {
|
||||
o = output;
|
||||
o.bigEndian = false;
|
||||
}
|
||||
|
||||
public function write(wav : WAVE) {
|
||||
var hdr = wav.header;
|
||||
|
||||
o.writeString("RIFF");
|
||||
writeInt(36 + wav.data.length);
|
||||
o.writeString("WAVE");
|
||||
|
||||
o.writeString("fmt ");
|
||||
writeInt(16);
|
||||
o.writeUInt16(1);
|
||||
o.writeUInt16(hdr.channels);
|
||||
writeInt(hdr.samplingRate);
|
||||
writeInt(hdr.byteRate);
|
||||
o.writeUInt16(hdr.blockAlign);
|
||||
o.writeUInt16(hdr.bitsPerSample);
|
||||
|
||||
o.writeString("data");
|
||||
writeInt(wav.data.length);
|
||||
o.write(wav.data);
|
||||
}
|
||||
|
||||
inline function writeInt( v : Int ) {
|
||||
#if haxe3
|
||||
o.writeInt32(v);
|
||||
#else
|
||||
o.writeUInt30(v);
|
||||
#end
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,31 +1,119 @@
|
||||
package iron.object;
|
||||
|
||||
import iron.data.SceneFormat;
|
||||
import iron.math.Vec4;
|
||||
import iron.math.Quat;
|
||||
|
||||
class Constraint {
|
||||
var raw: TConstraint;
|
||||
var target: Transform = null;
|
||||
|
||||
public function new(constr: TConstraint) {
|
||||
raw = constr;
|
||||
public function new(constraint: TConstraint) {
|
||||
raw = constraint;
|
||||
}
|
||||
|
||||
public function apply(transform: Transform) {
|
||||
if (target == null && raw.target != null) target = Scene.active.getChild(raw.target).transform;
|
||||
|
||||
if (target == null && raw.type != "LIMIT_LOCATION" && raw.type != "LIMIT_ROTATION" && raw.type != "LIMIT_SCALE") return;
|
||||
|
||||
if (raw.type == "COPY_LOCATION") {
|
||||
if (raw.use_x) {
|
||||
transform.world._30 = target.loc.x;
|
||||
if (raw.use_offset) transform.world._30 += transform.loc.x;
|
||||
if (raw.use_offset) {
|
||||
if (raw.use_x) transform.world._30 += target.world._30;
|
||||
if (raw.use_y) transform.world._31 += target.world._31;
|
||||
if (raw.use_z) transform.world._32 += target.world._32;
|
||||
}
|
||||
if (raw.use_y) {
|
||||
transform.world._31 = target.loc.y;
|
||||
if (raw.use_offset) transform.world._31 += transform.loc.y;
|
||||
else {
|
||||
if (raw.use_x) transform.world._30 = target.world._30;
|
||||
if (raw.use_y) transform.world._31 = target.world._31;
|
||||
if (raw.use_z) transform.world._32 = target.world._32;
|
||||
}
|
||||
if (raw.use_z) {
|
||||
transform.world._32 = target.loc.z;
|
||||
if (raw.use_offset) transform.world._32 += transform.loc.z;
|
||||
}
|
||||
|
||||
else if (raw.type == "COPY_ROTATION") {
|
||||
var tq = target.rot;
|
||||
var mq = transform.rot;
|
||||
if (raw.use_offset) {
|
||||
mq.mult(tq);
|
||||
}
|
||||
else {
|
||||
if (raw.use_x) mq.x = tq.x;
|
||||
if (raw.use_y) mq.y = tq.y;
|
||||
if (raw.use_z) mq.z = tq.z;
|
||||
mq.w = tq.w;
|
||||
}
|
||||
var loc = new Vec4(transform.world._30, transform.world._31, transform.world._32);
|
||||
var scale = transform.scale;
|
||||
transform.world.compose(loc, mq, scale);
|
||||
}
|
||||
|
||||
else if (raw.type == "COPY_SCALE") {
|
||||
var ts = target.scale;
|
||||
if (raw.use_offset) {
|
||||
if (raw.use_x) transform.scale.x *= ts.x;
|
||||
if (raw.use_y) transform.scale.y *= ts.y;
|
||||
if (raw.use_z) transform.scale.z *= ts.z;
|
||||
}
|
||||
else {
|
||||
if (raw.use_x) transform.scale.x = ts.x;
|
||||
if (raw.use_y) transform.scale.y = ts.y;
|
||||
if (raw.use_z) transform.scale.z = ts.z;
|
||||
}
|
||||
var loc = new Vec4(transform.world._30, transform.world._31, transform.world._32);
|
||||
transform.world.compose(loc, transform.rot, transform.scale);
|
||||
}
|
||||
|
||||
else if (raw.type == "COPY_TRANSFORMS") {
|
||||
transform.world.setFrom(target.world);
|
||||
}
|
||||
|
||||
else if (raw.type == "LIMIT_LOCATION") {
|
||||
if (raw.use_min_x && transform.world._30 < raw.min_x) transform.world._30 = raw.min_x;
|
||||
if (raw.use_max_x && transform.world._30 > raw.max_x) transform.world._30 = raw.max_x;
|
||||
|
||||
if (raw.use_min_y && transform.world._31 < raw.min_y) transform.world._31 = raw.min_y;
|
||||
if (raw.use_max_y && transform.world._31 > raw.max_y) transform.world._31 = raw.max_y;
|
||||
|
||||
if (raw.use_min_z && transform.world._32 < raw.min_z) transform.world._32 = raw.min_z;
|
||||
if (raw.use_max_z && transform.world._32 > raw.max_z) transform.world._32 = raw.max_z;
|
||||
}
|
||||
|
||||
else if (raw.type == "LIMIT_ROTATION") {
|
||||
var euler = transform.rot.getEuler();
|
||||
var changed = false;
|
||||
|
||||
if (raw.use_limit_x) {
|
||||
if (euler.x < raw.min_x) { euler.x = raw.min_x; changed = true; }
|
||||
if (euler.x > raw.max_x) { euler.x = raw.max_x; changed = true; }
|
||||
}
|
||||
if (raw.use_limit_y) {
|
||||
if (euler.y < raw.min_y) { euler.y = raw.min_y; changed = true; }
|
||||
if (euler.y > raw.max_y) { euler.y = raw.max_y; changed = true; }
|
||||
}
|
||||
if (raw.use_limit_z) {
|
||||
if (euler.z < raw.min_z) { euler.z = raw.min_z; changed = true; }
|
||||
if (euler.z > raw.max_z) { euler.z = raw.max_z; changed = true; }
|
||||
}
|
||||
|
||||
if (changed) {
|
||||
transform.rot.fromEuler(euler.x, euler.y, euler.z);
|
||||
var loc = new Vec4(transform.world._30, transform.world._31, transform.world._32);
|
||||
transform.world.compose(loc, transform.rot, transform.scale);
|
||||
}
|
||||
}
|
||||
|
||||
else if (raw.type == "LIMIT_SCALE") {
|
||||
if (raw.use_min_x && transform.scale.x < raw.min_x) transform.scale.x = raw.min_x;
|
||||
if (raw.use_max_x && transform.scale.x > raw.max_x) transform.scale.x = raw.max_x;
|
||||
|
||||
if (raw.use_min_y && transform.scale.y < raw.min_y) transform.scale.y = raw.min_y;
|
||||
if (raw.use_max_y && transform.scale.y > raw.max_y) transform.scale.y = raw.max_y;
|
||||
|
||||
if (raw.use_min_z && transform.scale.z < raw.min_z) transform.scale.z = raw.min_z;
|
||||
if (raw.use_max_z && transform.scale.z > raw.max_z) transform.scale.z = raw.max_z;
|
||||
|
||||
var loc = new Vec4(transform.world._30, transform.world._31, transform.world._32);
|
||||
transform.world.compose(loc, transform.rot, transform.scale);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
1338
leenkx/Sources/iron/object/CurveObject.hx
Normal file
1338
leenkx/Sources/iron/object/CurveObject.hx
Normal file
File diff suppressed because it is too large
Load Diff
@ -11,6 +11,12 @@ import iron.object.CameraObject;
|
||||
class LightObject extends Object {
|
||||
|
||||
public var data: LightData;
|
||||
public var color: Vec4;
|
||||
public var strength: Float;
|
||||
#if lnx_spot
|
||||
public var size: Float;
|
||||
public var blend: Float;
|
||||
#end
|
||||
|
||||
#if rp_shadowmap
|
||||
#if lnx_shadowmap_atlas
|
||||
@ -79,6 +85,15 @@ class LightObject extends Object {
|
||||
super();
|
||||
|
||||
this.data = data;
|
||||
this.color = new Vec4(data.raw.color[0], data.raw.color[1], data.raw.color[2]);
|
||||
this.strength = data.raw.strength;
|
||||
|
||||
#if lnx_spot
|
||||
if (data.raw.type == "spot"){
|
||||
this.size = data.raw.spot_size;
|
||||
this.blend = data.raw.spot_blend;
|
||||
}
|
||||
#end
|
||||
|
||||
var type = data.raw.type;
|
||||
var fov = data.raw.fov;
|
||||
@ -374,7 +389,7 @@ class LightObject extends Object {
|
||||
// Centralize discarding conditions when iterating over lights
|
||||
// Important to avoid issues later with "misaligned" data in uniforms (lightsArray, clusterData, LWVPSpotArray)
|
||||
public inline static function discardLight(light: LightObject) {
|
||||
return !light.visible || light.data.raw.strength == 0.0 || light.data.raw.type == "sun";
|
||||
return !light.visible || light.strength == 0.0 || light.data.raw.type == "sun";
|
||||
}
|
||||
// Discarding conditions but with culling included
|
||||
public inline static function discardLightCulled(light: LightObject) {
|
||||
@ -457,7 +472,7 @@ class LightObject extends Object {
|
||||
lpos.set(l.transform.worldx(), l.transform.worldy(), l.transform.worldz());
|
||||
lpos.applymat4(camera.V);
|
||||
lpos.z *= -1.0;
|
||||
var radius = getRadius(l.data.raw.strength);
|
||||
var radius = getRadius(l.strength);
|
||||
var minX = 0;
|
||||
var minY = 0;
|
||||
var minZ = 0;
|
||||
@ -552,10 +567,10 @@ class LightObject extends Object {
|
||||
lightsArray[i * 12 + 3] = 0.0; // padding or spot scale x
|
||||
|
||||
// light color
|
||||
var f = l.data.raw.strength;
|
||||
lightsArray[i * 12 + 4] = l.data.raw.color[0] * f;
|
||||
lightsArray[i * 12 + 5] = l.data.raw.color[1] * f;
|
||||
lightsArray[i * 12 + 6] = l.data.raw.color[2] * f;
|
||||
var f = l.strength;
|
||||
lightsArray[i * 12 + 4] = l.color.x * f;
|
||||
lightsArray[i * 12 + 5] = l.color.y * f;
|
||||
lightsArray[i * 12 + 6] = l.color.z * f;
|
||||
lightsArray[i * 12 + 7] = 0.0; // padding or spot scale y
|
||||
|
||||
// other data
|
||||
@ -566,13 +581,13 @@ class LightObject extends Object {
|
||||
|
||||
#if lnx_spot
|
||||
if (l.data.raw.type == "spot") {
|
||||
lightsArray[i * 12 + 9] = l.data.raw.spot_size;
|
||||
lightsArray[i * 12 + 9] = l.size;
|
||||
|
||||
var dir = l.look().normalize();
|
||||
lightsArraySpot[i * 8 ] = dir.x;
|
||||
lightsArraySpot[i * 8 + 1] = dir.y;
|
||||
lightsArraySpot[i * 8 + 2] = dir.z;
|
||||
lightsArraySpot[i * 8 + 3] = l.data.raw.spot_blend;
|
||||
lightsArraySpot[i * 8 + 3] = l.blend;
|
||||
|
||||
// Premultiply scale with z component
|
||||
var scale = l.transform.scale;
|
||||
|
||||
@ -28,13 +28,15 @@ class MeshObject extends Object {
|
||||
public var cameraList: Array<String> = null;
|
||||
public var screenSize = 0.0;
|
||||
public var frustumCulling = true;
|
||||
public var tilesheet: Tilesheet = null;
|
||||
public var activeTilesheet: Tilesheet = null;
|
||||
public var tilesheets: Array<Tilesheet> = null;
|
||||
public var skip_context: String = null; // Do not draw this context
|
||||
public var force_context: String = null; // Draw only this context
|
||||
static var lastPipeline: PipelineState = null;
|
||||
#if lnx_morph_target
|
||||
public var morphTarget: MorphTarget = null;
|
||||
#end
|
||||
public var vertexGroups: Map<String, Array<Vec4>> = null;
|
||||
|
||||
#if lnx_veloc
|
||||
public var prevMatrix = Mat4.identity();
|
||||
@ -50,6 +52,10 @@ class MeshObject extends Object {
|
||||
|
||||
public function setData(data: MeshData) {
|
||||
this.data = data;
|
||||
|
||||
if (this.materials != null && this.materials.length > 0)
|
||||
data.geom.instanceElements = @:privateAccess this.materials[0].shader.contexts[0].instanceElements;
|
||||
|
||||
data.refcount++;
|
||||
|
||||
#if (!lnx_batch)
|
||||
@ -87,7 +93,8 @@ class MeshObject extends Object {
|
||||
particleSystems = null;
|
||||
}
|
||||
#end
|
||||
if (tilesheet != null) tilesheet.remove();
|
||||
if (activeTilesheet != null) activeTilesheet.remove();
|
||||
if (tilesheets != null) { for (ts in tilesheets) { ts.remove(); } tilesheets = null; }
|
||||
if (Scene.active != null) Scene.active.meshes.remove(this);
|
||||
#if (rp_renderer == "Deferred")
|
||||
if (Scene.active != null) Scene.active.markMaterialParamsDirty();
|
||||
@ -126,12 +133,34 @@ class MeshObject extends Object {
|
||||
#end
|
||||
|
||||
public function setupTilesheet(tilesheetData: iron.data.SceneFormat.TTilesheetData) {
|
||||
tilesheet = new Tilesheet(tilesheetData, this);
|
||||
activeTilesheet = new Tilesheet(tilesheetData, this);
|
||||
if (tilesheets == null) tilesheets = new Array<Tilesheet>();
|
||||
tilesheets.push(activeTilesheet);
|
||||
}
|
||||
|
||||
public function setActiveTilesheet(tilesheetData: iron.data.SceneFormat.TTilesheetData, tilesheetActionRef: String = null) {
|
||||
var set = false;
|
||||
if (tilesheets != null) {
|
||||
for (ts in tilesheets) {
|
||||
if (ts.raw == tilesheetData) {
|
||||
if (activeTilesheet != null) activeTilesheet.pause();
|
||||
activeTilesheet = ts;
|
||||
if (tilesheetActionRef != null) activeTilesheet.play(tilesheetActionRef);
|
||||
set = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!set) {
|
||||
if (activeTilesheet != null) activeTilesheet.pause();
|
||||
setupTilesheet(tilesheetData);
|
||||
if (tilesheetActionRef != null) activeTilesheet.play(tilesheetActionRef);
|
||||
}
|
||||
}
|
||||
|
||||
public function setTilesheetAction(actionRef: String) {
|
||||
if (tilesheet != null) {
|
||||
tilesheet.play(actionRef);
|
||||
if (activeTilesheet != null) {
|
||||
activeTilesheet.play(actionRef);
|
||||
}
|
||||
}
|
||||
|
||||
@ -164,6 +193,7 @@ class MeshObject extends Object {
|
||||
}
|
||||
|
||||
function cullMesh(context: String, camera: CameraObject, light: LightObject): Bool {
|
||||
var isShadow = context == "shadowmap";
|
||||
if (camera == null) return false;
|
||||
|
||||
if (camera.data.raw.frustum_culling && frustumCulling) {
|
||||
@ -172,11 +202,12 @@ class MeshObject extends Object {
|
||||
var radiusScale = data.isSkinned ? 2.0 : 1.0;
|
||||
#if lnx_gpu_particles
|
||||
// particleSystems for update, particleOwner for render
|
||||
if (particleSystems != null || particleOwner != null) radiusScale *= 1000;
|
||||
if (particleSystems != null && particleSystems.length > 0) return setCulled(isShadow, false);
|
||||
#end
|
||||
/*
|
||||
if (context == "voxel") radiusScale *= 100;
|
||||
if (data.geom.instanced) radiusScale *= 100;
|
||||
var isShadow = context == "shadowmap";
|
||||
if (data.geom.instanced) radiusScale *= 100;*/
|
||||
if (data.geom.instanced) return setCulled(isShadow, false);
|
||||
var frustumPlanes = isShadow ? light.frustumPlanes : camera.frustumPlanes;
|
||||
|
||||
if (isShadow && light.data.raw.type != "sun") { // Non-sun light bounds intersect camera frustum
|
||||
@ -191,8 +222,9 @@ class MeshObject extends Object {
|
||||
}
|
||||
}
|
||||
|
||||
culled = false;
|
||||
return culled;
|
||||
//culled = false;
|
||||
//return culled;
|
||||
return setCulled(isShadow, false);
|
||||
}
|
||||
|
||||
function skipContext(context: String, mat: MaterialData): Bool {
|
||||
@ -227,11 +259,6 @@ class MeshObject extends Object {
|
||||
if (cullMesh(context, Scene.active.camera, RenderPath.active.light)) return;
|
||||
var meshContext = raw != null ? context == "mesh" : false;
|
||||
|
||||
// Update tilesheet
|
||||
if (tilesheet != null && meshContext) {
|
||||
tilesheet.update();
|
||||
}
|
||||
|
||||
if (cameraList != null && cameraList.indexOf(Scene.active.camera.name) < 0) return;
|
||||
|
||||
#if lnx_gpu_particles
|
||||
|
||||
@ -61,6 +61,25 @@ class MorphTarget {
|
||||
public inline function setMorphValueDirect(index: Int, value: Float) {
|
||||
morphWeights.set(index, value);
|
||||
}
|
||||
|
||||
public function getMorphValue(name: String): Float {
|
||||
var i = morphMap.get(name);
|
||||
return (i != null) ? morphWeights.get(i) : 0.0;
|
||||
}
|
||||
|
||||
public function hasMorph(name: String): Bool {
|
||||
return morphMap.exists(name);
|
||||
}
|
||||
|
||||
public function resetWeights() {
|
||||
for (i in 0...morphWeights.length) {
|
||||
morphWeights.set(i, 0.0);
|
||||
}
|
||||
}
|
||||
|
||||
public function getMorphNames(): Array<String> {
|
||||
return [for (key in morphMap.keys()) key];
|
||||
}
|
||||
}
|
||||
|
||||
#end
|
||||
|
||||
@ -27,7 +27,6 @@ class Object {
|
||||
public var culled = false; // Object was culled last frame
|
||||
public var culledMesh = false;
|
||||
public var culledShadow = false;
|
||||
public var vertex_groups: Map<String, Array<Vec4>> = null;
|
||||
public var properties: Map<String, Dynamic> = null;
|
||||
var isEmpty = false;
|
||||
|
||||
@ -95,6 +94,7 @@ class Object {
|
||||
Removes the game object from the scene.
|
||||
**/
|
||||
public function remove() {
|
||||
Scene.active.removeFromGroups(this);
|
||||
if (isEmpty && Scene.active != null) Scene.active.empties.remove(this);
|
||||
if (animation != null) animation.remove();
|
||||
while (children.length > 0) children[0].remove();
|
||||
@ -111,16 +111,12 @@ class Object {
|
||||
@return Object or null
|
||||
**/
|
||||
public function getChild(name: String): Object {
|
||||
if (this.name == name) return this;
|
||||
else if (this.filename != "") {
|
||||
if (this.name == name + "_" + this.filename) return this;
|
||||
}
|
||||
|
||||
for (c in children) {
|
||||
if (c.name == name) return c;
|
||||
if (c.filename != "" && c.name == name + "_" + c.filename) return c;
|
||||
var r = c.getChild(name);
|
||||
if (r != null) return r;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -145,12 +141,10 @@ class Object {
|
||||
}
|
||||
|
||||
public function getChildOfType<T: Object>(type: Class<T>): T {
|
||||
if (Std.isOfType(this, type)) return cast this;
|
||||
else {
|
||||
for (c in children) {
|
||||
var r = c.getChildOfType(type);
|
||||
if (r != null) return r;
|
||||
}
|
||||
for (c in children) {
|
||||
if (Std.isOfType(c, type)) return cast c;
|
||||
var r = c.getChildOfType(type);
|
||||
if (r != null) return r;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -8,6 +8,7 @@ import iron.data.SceneFormat;
|
||||
import iron.math.Quat;
|
||||
import iron.math.Vec3;
|
||||
import iron.math.Vec4;
|
||||
import iron.object.CurveObject;
|
||||
import iron.object.MeshObject;
|
||||
import iron.object.Object;
|
||||
import iron.system.Time;
|
||||
@ -19,6 +20,10 @@ import kha.arrays.Uint32Array;
|
||||
class ParticleSystemCPU {
|
||||
public var data: ParticleData;
|
||||
public var speed: FastFloat = 1.0; // Not used yet. Added to go in hand with `ParticleSystemGPU`
|
||||
public var curveGuides: Array<CurveObject> = [];
|
||||
public var curveGuideStrength: FastFloat = 1.0;
|
||||
public var curveGuideSpeed: FastFloat = 1.0;
|
||||
var paused: Bool = false;
|
||||
var r: TParticleData;
|
||||
|
||||
// Format
|
||||
@ -37,6 +42,7 @@ class ParticleSystemCPU {
|
||||
// Velocity
|
||||
var velocity: Vec3 = new Vec3(0.0, 0.0, 1.0); // object_align_factor: Float32Array
|
||||
var velocityRandom: FastFloat = 0.0; // factor_random
|
||||
var normalFactor: FastFloat = 0.0;
|
||||
|
||||
// Rotation
|
||||
var rotation: Bool = false; // use_rotations
|
||||
@ -114,11 +120,12 @@ class ParticleSystemCPU {
|
||||
scale = r.particle_size;
|
||||
scaleRandom = r.size_random;
|
||||
|
||||
velocity = new Vec3(r.object_align_factor[0], r.object_align_factor[1], r.object_align_factor[2]).mult(frameRate / baseFrameRate).mult(1 / scale);
|
||||
velocity = new Vec3(r.object_align_factor[0], r.object_align_factor[1], r.object_align_factor[2]).mult(frameRate / baseFrameRate);
|
||||
velocityRandom = r.factor_random * (frameRate / baseFrameRate);
|
||||
normalFactor = r.normal_factor * (frameRate / baseFrameRate);
|
||||
|
||||
if (Scene.active.raw.gravity != null) {
|
||||
gravity = new Vec3(Scene.active.raw.gravity[0], Scene.active.raw.gravity[1], Scene.active.raw.gravity[2]).mult(frameRate / baseFrameRate).mult(1 / scale);
|
||||
gravity = new Vec3(Scene.active.raw.gravity[0], Scene.active.raw.gravity[1], Scene.active.raw.gravity[2]).mult(frameRate / baseFrameRate);
|
||||
}
|
||||
gravityFactor = r.weight_gravity * (frameRate / baseFrameRate);
|
||||
textureFactor = r.weight_texture;
|
||||
@ -139,39 +146,36 @@ class ParticleSystemCPU {
|
||||
scaleElementsCount = getRampElementsLength();
|
||||
scaleRampSizeFactor = getRampSizeFactor();
|
||||
|
||||
switch (type) {
|
||||
case 0: // Emission
|
||||
loopAnim = {
|
||||
tick: function () {
|
||||
spawnTime += Time.delta * Time.scale;
|
||||
var expected: Int = Math.floor(spawnTime / spawnRate);
|
||||
while (spawnedParticles < expected && spawnedParticles < count) {
|
||||
spawnParticle();
|
||||
spawnedParticles++;
|
||||
}
|
||||
updateParticles();
|
||||
},
|
||||
target: null,
|
||||
props: null,
|
||||
duration: loop ? lifetimeSeconds : lifetimeSeconds * 2,
|
||||
done: function () {
|
||||
if (loop) start();
|
||||
}
|
||||
}
|
||||
|
||||
Scene.active.notifyOnInit(function () {
|
||||
if (autoStart) start();
|
||||
});
|
||||
case 1: // Hair
|
||||
Scene.active.notifyOnInit(function () {
|
||||
for (i in 0...count) spawnParticle();
|
||||
});
|
||||
default:
|
||||
}
|
||||
|
||||
Scene.active.notifyOnInit(function () {
|
||||
for (i in 0...count) addToPool();
|
||||
|
||||
switch (type) {
|
||||
case 0: // Emission
|
||||
loopAnim = {
|
||||
tick: function () {
|
||||
if (paused) return;
|
||||
spawnTime += Time.delta;
|
||||
var expected: Int = Math.floor(spawnTime / spawnRate);
|
||||
while (spawnedParticles < expected && spawnedParticles < count) {
|
||||
spawnParticle();
|
||||
spawnedParticles++;
|
||||
}
|
||||
updateParticles();
|
||||
},
|
||||
target: null,
|
||||
props: null,
|
||||
duration: loop ? lifetimeSeconds : lifetimeSeconds * 2,
|
||||
done: function () {
|
||||
if (loop) start();
|
||||
}
|
||||
}
|
||||
if (autoStart) start();
|
||||
case 1: // Hair
|
||||
for (i in 0...count) spawnParticle();
|
||||
default:
|
||||
}
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
@ -182,14 +186,12 @@ class ParticleSystemCPU {
|
||||
Tween.to(loopAnim);
|
||||
}
|
||||
|
||||
// TODO
|
||||
public function pause() {
|
||||
|
||||
paused = true;
|
||||
}
|
||||
|
||||
// TODO
|
||||
public function resume() {
|
||||
|
||||
paused = false;
|
||||
}
|
||||
|
||||
public function stop() {
|
||||
@ -246,6 +248,8 @@ class ParticleSystemCPU {
|
||||
var scalePos: FastFloat = owner.data.scalePos;
|
||||
var scalePosParticle: FastFloat = cast(o, MeshObject).data.scalePos;
|
||||
|
||||
var normDir: Vec3 = new Vec3();
|
||||
|
||||
// TODO: add all properties from Blender's UI
|
||||
switch (emitFrom) {
|
||||
case 0: // Vertices
|
||||
@ -253,6 +257,8 @@ class ParticleSystemCPU {
|
||||
var i: Int = Std.int(Math.random() * (pa.values.length / pa.size));
|
||||
var loc: Vec4 = new Vec4(pa.values[i * pa.size] * normFactor, pa.values[i * pa.size + 1] * normFactor, pa.values[i * pa.size + 2] * normFactor, 1);
|
||||
|
||||
if (normalFactor != 0.0) normDir = new Vec3(loc.x, loc.y, loc.z).normalize();
|
||||
|
||||
if (!localCoords) {
|
||||
loc.applyQuat(objectRot);
|
||||
loc.add(objectPos);
|
||||
@ -274,6 +280,9 @@ class ParticleSystemCPU {
|
||||
var pos: Vec3 = randomPointInTriangle(v0, v1, v2);
|
||||
|
||||
var loc: Vec4 = new Vec4(pos.x, pos.y, pos.z, 1).mult(normFactor);
|
||||
|
||||
if (normalFactor != 0.0) normDir = new Vec3(loc.x, loc.y, loc.z).normalize();
|
||||
|
||||
if (!localCoords) {
|
||||
loc.applyQuat(objectRot);
|
||||
loc.add(objectPos);
|
||||
@ -285,6 +294,8 @@ class ParticleSystemCPU {
|
||||
scaleFactorVolume.mult(0.5);
|
||||
var loc: Vec4 = new Vec4((Math.random() * 2.0 - 1.0) * scaleFactorVolume.x, (Math.random() * 2.0 - 1.0) * scaleFactorVolume.y, (Math.random() * 2.0 - 1.0) * scaleFactorVolume.z, 1);
|
||||
|
||||
if (normalFactor != 0.0) normDir = new Vec3(loc.x, loc.y, loc.z).normalize();
|
||||
|
||||
if (!localCoords) {
|
||||
loc.applyQuat(objectRot);
|
||||
loc.add(objectPos);
|
||||
@ -310,7 +321,9 @@ class ParticleSystemCPU {
|
||||
var randomZ: FastFloat = (Math.random() * 2 / (scale * particleScale) - 1 / (scale * particleScale)) * velocityRandom;
|
||||
var g: Vec3 = new Vec3();
|
||||
|
||||
var rotatedVelocity: Vec4 = new Vec4(velocity.x + randomX, velocity.y + randomY, velocity.z + randomZ, 1);
|
||||
if (normalFactor != 0.0) normDir = normDir.mult(normalFactor);
|
||||
|
||||
var rotatedVelocity: Vec4 = new Vec4(velocity.x + randomX + normDir.x, velocity.y + randomY + normDir.y, velocity.z + randomZ + normDir.z, 1);
|
||||
if (!localCoords) rotatedVelocity.applyQuat(objectRot);
|
||||
|
||||
if (rotation) {
|
||||
@ -371,7 +384,7 @@ class ParticleSystemCPU {
|
||||
|
||||
function updateParticles() {
|
||||
for (particle => physics in particlePhysics) {
|
||||
physics.age += Time.delta * Time.scale;
|
||||
physics.age += Time.delta;
|
||||
|
||||
if (physics.age >= physics.lifetime) {
|
||||
particlePhysics.remove(particle);
|
||||
@ -379,14 +392,63 @@ class ParticleSystemCPU {
|
||||
continue;
|
||||
}
|
||||
|
||||
physics.velocity.x += physics.gravity.x * Time.delta * Time.scale;
|
||||
physics.velocity.y += physics.gravity.y * Time.delta * Time.scale;
|
||||
physics.velocity.z += physics.gravity.z * Time.delta * Time.scale;
|
||||
physics.velocity.x += physics.gravity.x * Time.delta;
|
||||
physics.velocity.y += physics.gravity.y * Time.delta;
|
||||
physics.velocity.z += physics.gravity.z * Time.delta;
|
||||
|
||||
if (curveGuides != null && curveGuides.length > 0) {
|
||||
var curveVelX: FastFloat = 0.0;
|
||||
var curveVelY: FastFloat = 0.0;
|
||||
var curveVelZ: FastFloat = 0.0;
|
||||
var validCurves: Int = 0;
|
||||
|
||||
for (curve in curveGuides) {
|
||||
if (curve != null && curve.data != null && curve.data.splines != null && curve.splinesLength > 0) {
|
||||
var t = physics.age / physics.lifetime;
|
||||
|
||||
var tangent = curve.getTangent(t, 0);
|
||||
tangent.w = 0.0;
|
||||
tangent.applymat4(curve.transform.world);
|
||||
tangent.normalize();
|
||||
|
||||
var curveLen = curve.getLength(0);
|
||||
var speed = (curveLen / physics.lifetime) * curveGuideSpeed;
|
||||
|
||||
var tgtX = tangent.x * speed;
|
||||
var tgtY = tangent.y * speed;
|
||||
var tgtZ = tangent.z * speed;
|
||||
|
||||
if (localCoords) {
|
||||
var targetVel = new Vec4(tgtX, tgtY, tgtZ, 0.0);
|
||||
var invOwnerRot = new Quat(-owner.transform.rot.x, -owner.transform.rot.y, -owner.transform.rot.z, owner.transform.rot.w);
|
||||
targetVel.applyQuat(invOwnerRot);
|
||||
tgtX = targetVel.x;
|
||||
tgtY = targetVel.y;
|
||||
tgtZ = targetVel.z;
|
||||
}
|
||||
|
||||
curveVelX += tgtX;
|
||||
curveVelY += tgtY;
|
||||
curveVelZ += tgtZ;
|
||||
validCurves++;
|
||||
}
|
||||
}
|
||||
|
||||
if (validCurves > 0) {
|
||||
curveVelX /= validCurves;
|
||||
curveVelY /= validCurves;
|
||||
curveVelZ /= validCurves;
|
||||
|
||||
physics.velocity.x += (curveVelX - physics.velocity.x) * curveGuideStrength;
|
||||
physics.velocity.y += (curveVelY - physics.velocity.y) * curveGuideStrength;
|
||||
physics.velocity.z += (curveVelZ - physics.velocity.z) * curveGuideStrength;
|
||||
}
|
||||
}
|
||||
|
||||
particle.transform.translate(
|
||||
physics.velocity.x * Time.delta * Time.scale,
|
||||
physics.velocity.y * Time.delta * Time.scale,
|
||||
physics.velocity.z * Time.delta * Time.scale
|
||||
physics.velocity.x * Time.delta,
|
||||
physics.velocity.y * Time.delta,
|
||||
physics.velocity.z * Time.delta
|
||||
);
|
||||
|
||||
if (rotation && dynamicRotation && orientationAxis == 3) setVelocityHair(particle, physics.velocity, randQuat, phaseQuat);
|
||||
|
||||
@ -141,10 +141,10 @@ class ParticleSystemGPU {
|
||||
dimx = object.transform.dim.x;
|
||||
dimy = object.transform.dim.y;
|
||||
|
||||
if (object.tilesheet != null) {
|
||||
tilesx = object.tilesheet.getTilesX();
|
||||
tilesy = object.tilesheet.getTilesY();
|
||||
tilesFramerate = object.tilesheet.action.framerate;
|
||||
if (object.activeTilesheet != null) {
|
||||
tilesx = object.activeTilesheet.getTilesX();
|
||||
tilesy = object.activeTilesheet.getTilesY();
|
||||
tilesFramerate = object.activeTilesheet.action.framerate;
|
||||
}
|
||||
|
||||
// Animate
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
package iron.object;
|
||||
|
||||
import kha.FastFloat;
|
||||
import kha.Sound;
|
||||
import kha.audio1.AudioChannel;
|
||||
import iron.data.Data;
|
||||
import iron.data.SceneFormat;
|
||||
@ -13,9 +14,10 @@ class SpeakerObject extends Object {
|
||||
|
||||
public var data: TSpeakerData;
|
||||
public var paused(default, null) = false;
|
||||
public var sound(default, null): kha.Sound = null;
|
||||
public var sound(default, null): Sound = null;
|
||||
public var channels(default, null): Array<AudioChannel> = [];
|
||||
public var volume(default, null) : FastFloat;
|
||||
public var sampleRate(default, null) : Int;
|
||||
|
||||
public function new(data: TSpeakerData) {
|
||||
super();
|
||||
@ -26,28 +28,32 @@ class SpeakerObject extends Object {
|
||||
|
||||
if (data.sound == "") return;
|
||||
|
||||
Data.getSound(data.sound, function(sound: kha.Sound) {
|
||||
this.sound = sound;
|
||||
Data.getSound(data.sound, function(sound: Sound) {
|
||||
this.sound = cloneSound(sound);
|
||||
App.notifyOnInit(init);
|
||||
});
|
||||
}
|
||||
|
||||
function init() {
|
||||
sampleRate = sound.sampleRate;
|
||||
if (data.pitch != 1.0)
|
||||
sound.sampleRate = Std.int(sampleRate * data.pitch);
|
||||
if (visible && data.play_on_start) play();
|
||||
}
|
||||
|
||||
public function play() {
|
||||
if (sound == null || data.muted) return;
|
||||
public function play(): AudioChannel {
|
||||
if (sound == null || data.muted) return null;
|
||||
if (paused) {
|
||||
for (c in channels) c.play();
|
||||
paused = false;
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
var channel = Audio.play(sound, data.loop, data.stream);
|
||||
if (channel != null) {
|
||||
channels.push(channel);
|
||||
if (data.attenuation > 0 && channels.length == 1) App.notifyOnUpdate(update);
|
||||
}
|
||||
return channel;
|
||||
}
|
||||
|
||||
public function pause() {
|
||||
@ -65,14 +71,24 @@ class SpeakerObject extends Object {
|
||||
|
||||
data.sound = sound;
|
||||
|
||||
Data.getSound(sound, function(sound: kha.Sound) {
|
||||
this.sound = sound;
|
||||
Data.getSound(sound, function(sound: Sound) {
|
||||
this.sound = cloneSound(sound);
|
||||
});
|
||||
|
||||
sampleRate = this.sound.sampleRate;
|
||||
if (data.pitch != 1.0)
|
||||
this.sound.sampleRate = Std.int(sampleRate * data.pitch);
|
||||
}
|
||||
|
||||
public function setVolume(volume: FastFloat) {
|
||||
data.volume = volume;
|
||||
}
|
||||
|
||||
public function setPosition(position: Float) {
|
||||
for (c in channels)
|
||||
if (position < c.length) c.position = position;
|
||||
}
|
||||
|
||||
function update() {
|
||||
if (paused) return;
|
||||
for (c in channels) if (c.finished) channels.remove(c);
|
||||
@ -103,6 +119,17 @@ class SpeakerObject extends Object {
|
||||
super.remove();
|
||||
}
|
||||
|
||||
function cloneSound(sound: Sound): Sound {
|
||||
if (sound == null) return null;
|
||||
var s = Type.createEmptyInstance(Sound);
|
||||
s.compressedData = sound.compressedData;
|
||||
s.uncompressedData = sound.uncompressedData;
|
||||
s.sampleRate = sound.sampleRate;
|
||||
s.length = sound.length;
|
||||
s.channels = sound.channels;
|
||||
return s;
|
||||
}
|
||||
|
||||
#end
|
||||
|
||||
}
|
||||
|
||||
@ -1,11 +1,12 @@
|
||||
package iron.object;
|
||||
|
||||
import iron.App;
|
||||
import iron.Scene;
|
||||
import iron.data.SceneFormat;
|
||||
import iron.system.Time;
|
||||
import haxe.ds.Map;
|
||||
|
||||
|
||||
@:allow(iron.Scene)
|
||||
class Tilesheet {
|
||||
|
||||
public var tileX: Float = 0.0;
|
||||
@ -14,7 +15,8 @@ class Tilesheet {
|
||||
public var flipY: Bool = false;
|
||||
public var paused: Bool = false;
|
||||
public var frame: Int = 0;
|
||||
public var actions: Array<TTilesheetAction>;
|
||||
public var raw: TTilesheetData = null;
|
||||
public var actions: Array<TTilesheetAction> = null;
|
||||
public var action: TTilesheetAction = null;
|
||||
|
||||
public var ready: Bool = false;
|
||||
@ -31,8 +33,11 @@ class Tilesheet {
|
||||
|
||||
public function new(tilesheetData: TTilesheetData, ownerObject: MeshObject = null) {
|
||||
owner = ownerObject;
|
||||
raw = tilesheetData;
|
||||
actions = tilesheetData.actions;
|
||||
|
||||
Scene.active.tilesheets.push(this);
|
||||
|
||||
pendingAction = tilesheetData.start_action;
|
||||
if ((pendingAction == null || pendingAction == "") && actions.length > 0) {
|
||||
pendingAction = actions[0].name;
|
||||
@ -262,8 +267,10 @@ class Tilesheet {
|
||||
}
|
||||
|
||||
public function remove() {
|
||||
Scene.active.tilesheets.remove(this);
|
||||
ready = false;
|
||||
action = null;
|
||||
raw = null;
|
||||
actions = null;
|
||||
owner = null;
|
||||
currentMesh = null;
|
||||
|
||||
@ -106,6 +106,7 @@ class Transform {
|
||||
Rebuild the matrices, if needed.
|
||||
**/
|
||||
public function update() {
|
||||
if (object.constraints != null && object.constraints.length > 0) dirty = true;
|
||||
if (dirty) buildMatrix();
|
||||
}
|
||||
|
||||
@ -150,12 +151,19 @@ class Transform {
|
||||
if (boneParent != null) local.multmats(boneParent, local);
|
||||
|
||||
if (object.parent != null && !localOnly) {
|
||||
world.multmats3x4(local, object.parent.transform.world);
|
||||
// Swap multiplication order for linked objects to keep local transform intact
|
||||
var swapMult: Bool = object.raw != null && object.parent.raw != null && object.parent.raw.group_ref != null && object.parent.raw.group_ref != "";
|
||||
var a: Mat4 = swapMult ? object.parent.transform.world : local;
|
||||
var b: Mat4 = swapMult ? local : object.parent.transform.world;
|
||||
world.multmats3x4(a, b);
|
||||
}
|
||||
else {
|
||||
world.setFrom(local);
|
||||
}
|
||||
|
||||
// Constraints
|
||||
if (object.constraints != null) for (c in object.constraints) c.apply(this);
|
||||
|
||||
worldUnpack.setFrom(world);
|
||||
if (scaleWorld != 1.0) {
|
||||
worldUnpack._00 *= scaleWorld;
|
||||
@ -172,9 +180,6 @@ class Transform {
|
||||
worldUnpack._23 *= scaleWorld;
|
||||
}
|
||||
|
||||
// Constraints
|
||||
if (object.constraints != null) for (c in object.constraints) c.apply(this);
|
||||
|
||||
computeDim();
|
||||
|
||||
// Update children
|
||||
@ -265,7 +270,7 @@ class Transform {
|
||||
}
|
||||
|
||||
function computeRadius() {
|
||||
radius = Math.sqrt(dim.x * dim.x + dim.y * dim.y + dim.z * dim.z);
|
||||
radius = 0.5 * Math.sqrt(dim.x * dim.x + dim.y * dim.y + dim.z * dim.z);
|
||||
}
|
||||
|
||||
function computeDim() {
|
||||
|
||||
@ -428,12 +428,10 @@ class Uniforms {
|
||||
var v: Vec4 = null;
|
||||
helpVec.set(0, 0, 0, 0);
|
||||
switch (c.link) {
|
||||
#if lnx_debug
|
||||
case "_input": {
|
||||
helpVec.set(Input.getMouse().x / iron.App.w(), Input.getMouse().y / iron.App.h(), Input.getMouse().down() ? 1.0 : 0.0, 0.0);
|
||||
v = helpVec;
|
||||
}
|
||||
#end
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
@ -690,7 +688,7 @@ class Uniforms {
|
||||
}
|
||||
case "_hosekSunDirection": {
|
||||
var w = Scene.active.world;
|
||||
if (w != null) {
|
||||
if (w != null && w.raw.sun_direction != null) {
|
||||
// Clamp Z for night cycle
|
||||
helpVec.set(w.raw.sun_direction[0],
|
||||
w.raw.sun_direction[1],
|
||||
@ -956,6 +954,22 @@ class Uniforms {
|
||||
}
|
||||
|
||||
static function setObjectConstant(g: Graphics, object: Object, location: ConstantLocation, c: TShaderConstant) {
|
||||
#if lnx_spot
|
||||
if (c.name == "LWVPSpot") {
|
||||
var light = getSpot(0);
|
||||
if (light != null) {
|
||||
if (object == null) helpMat.setIdentity();
|
||||
else helpMat.setFrom(object.transform.worldUnpack);
|
||||
|
||||
helpMat.multmat(light.VP);
|
||||
helpMat.multmat(biasMat);
|
||||
|
||||
g.setMatrix(location, helpMat.self);
|
||||
return;
|
||||
}
|
||||
}
|
||||
#end
|
||||
|
||||
if (c.link == null) return;
|
||||
|
||||
var camera = Scene.active.camera;
|
||||
@ -1263,17 +1277,17 @@ class Uniforms {
|
||||
var vy: Null<kha.FastFloat> = null;
|
||||
switch (c.link) {
|
||||
case "_tilesheetOffset": {
|
||||
var ts = cast(object, MeshObject).tilesheet;
|
||||
var ts = cast(object, MeshObject).activeTilesheet;
|
||||
vx = ts.tileX;
|
||||
vy = ts.tileY;
|
||||
}
|
||||
case "_tilesheetFlip": {
|
||||
var ts = cast(object, MeshObject).tilesheet;
|
||||
var ts = cast(object, MeshObject).activeTilesheet;
|
||||
vx = ts.flipX ? 1.0 : 0.0;
|
||||
vy = ts.flipY ? 1.0 : 0.0;
|
||||
}
|
||||
case "_tilesheetTiles": {
|
||||
var ts = cast(object, MeshObject).tilesheet;
|
||||
var ts = cast(object, MeshObject).activeTilesheet;
|
||||
vx = ts.getTilesX();
|
||||
vy = ts.getTilesY();
|
||||
}
|
||||
|
||||
12
leenkx/Sources/leenkx/logicnode/ActiveSceneObjectNode.hx
Normal file
12
leenkx/Sources/leenkx/logicnode/ActiveSceneObjectNode.hx
Normal file
@ -0,0 +1,12 @@
|
||||
package leenkx.logicnode;
|
||||
|
||||
class ActiveSceneObjectNode extends LogicNode {
|
||||
|
||||
public function new(tree: LogicTree) {
|
||||
super(tree);
|
||||
}
|
||||
|
||||
override function get(from: Int): Dynamic {
|
||||
return iron.Scene.active.root.getChild(iron.Scene.active.raw.name);
|
||||
}
|
||||
}
|
||||
@ -3,6 +3,8 @@ package leenkx.logicnode;
|
||||
import iron.data.SceneFormat.TSceneFormat;
|
||||
import iron.data.Data;
|
||||
import iron.object.Object;
|
||||
import iron.object.MeshObject;
|
||||
import iron.Scene;
|
||||
|
||||
class AddParticleToObjectNode extends LogicNode {
|
||||
|
||||
@ -22,22 +24,22 @@ class AddParticleToObjectNode extends LogicNode {
|
||||
|
||||
if (objFrom == null || objTo == null) return;
|
||||
|
||||
var mobjFrom = cast(objFrom, iron.object.MeshObject);
|
||||
var mobjFrom = cast(objFrom, MeshObject);
|
||||
|
||||
var psys = mobjFrom.particleSystems != null ? mobjFrom.particleSystems[slot] :
|
||||
mobjFrom.particleOwner != null && mobjFrom.particleOwner.particleSystems != null ? mobjFrom.particleOwner.particleSystems[slot] : null;
|
||||
|
||||
if (psys == null) return;
|
||||
|
||||
var mobjTo = cast(objTo, iron.object.MeshObject);
|
||||
|
||||
mobjTo.setupParticleSystem(iron.Scene.active.raw.name, {name: 'LnxPS', seed: 0, particle: @:privateAccess psys.r.name});
|
||||
var mobjTo = cast(objTo, MeshObject);
|
||||
|
||||
mobjTo.setupParticleSystem(Scene.active.raw.name, {name: 'LnxPS', seed: 0, particle: @:privateAccess psys.r.name});
|
||||
|
||||
mobjTo.render_emitter = inputs[4].get();
|
||||
|
||||
iron.Scene.active.spawnObject(psys.data.raw.instance_object, null, function(o: Object) {
|
||||
Scene.active.spawnObject(psys.data.raw.instance_object, null, function(o: Object) {
|
||||
if (o != null) {
|
||||
var c: iron.object.MeshObject = cast o;
|
||||
var c: MeshObject = cast o;
|
||||
if (mobjTo.particleChildren == null) mobjTo.particleChildren = [];
|
||||
mobjTo.particleChildren.push(c);
|
||||
c.particleOwner = mobjTo;
|
||||
@ -55,7 +57,7 @@ class AddParticleToObjectNode extends LogicNode {
|
||||
var slot: Int = inputs[3].get();
|
||||
|
||||
var mobjTo: Object = inputs[4].get();
|
||||
var mobjTo = cast(mobjTo, iron.object.MeshObject);
|
||||
var mobjTo = cast(mobjTo, MeshObject);
|
||||
|
||||
#if lnx_json
|
||||
sceneName += ".json";
|
||||
@ -65,28 +67,34 @@ class AddParticleToObjectNode extends LogicNode {
|
||||
|
||||
Data.getSceneRaw(sceneName, (rawScene: TSceneFormat) -> {
|
||||
|
||||
for (obj in rawScene.objects) {
|
||||
if (obj.name == objectName) {
|
||||
mobjTo.setupParticleSystem(sceneName, obj.particle_refs[slot]);
|
||||
mobjTo.render_emitter = inputs[5].get();
|
||||
for (obj in rawScene.objects) {
|
||||
if (obj.name == objectName) {
|
||||
mobjTo.setupParticleSystem(sceneName, obj.particle_refs[slot]);
|
||||
mobjTo.render_emitter = inputs[5].get();
|
||||
|
||||
iron.Scene.active.spawnObject(rawScene.particle_datas[slot].instance_object, null, function(o: Object) {
|
||||
if (o != null) {
|
||||
var c: iron.object.MeshObject = cast o;
|
||||
if (mobjTo.particleChildren == null) mobjTo.particleChildren = [];
|
||||
mobjTo.particleChildren.push(c);
|
||||
c.particleOwner = mobjTo;
|
||||
c.particleIndex = mobjTo.particleChildren.length - 1;
|
||||
}
|
||||
}, true, rawScene);
|
||||
for (i => ps in rawScene.particle_datas)
|
||||
if (obj.particle_refs[slot].particle == ps.name){
|
||||
slot = i;
|
||||
break;
|
||||
}
|
||||
|
||||
var oslot: Int = mobjTo.particleSystems.length-1;
|
||||
var opsys = mobjTo.particleSystems[oslot];
|
||||
@:privateAccess opsys.setupGeomGpu(mobjTo.particleChildren[oslot]);
|
||||
Scene.active.spawnObject(rawScene.particle_datas[slot].instance_object, null, function(o: Object) {
|
||||
if (o != null) {
|
||||
var c: MeshObject = cast o;
|
||||
if (mobjTo.particleChildren == null) mobjTo.particleChildren = [];
|
||||
mobjTo.particleChildren.push(c);
|
||||
c.particleOwner = mobjTo;
|
||||
c.particleIndex = mobjTo.particleChildren.length - 1;
|
||||
}
|
||||
}, true, rawScene);
|
||||
|
||||
break;
|
||||
var oslot: Int = mobjTo.particleSystems.length-1;
|
||||
var opsys = mobjTo.particleSystems[oslot];
|
||||
@:privateAccess opsys.setupGeomGpu(mobjTo.particleChildren[oslot]);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
|
||||
@ -111,7 +111,9 @@ class AddPhysicsConstraintNode extends LogicNode {
|
||||
}
|
||||
}
|
||||
}
|
||||
pivotObject.addTrait(con);
|
||||
con.name = property0;
|
||||
|
||||
pivotObject.addTrait(con);
|
||||
}
|
||||
#end
|
||||
runOutput(0);
|
||||
|
||||
46
leenkx/Sources/leenkx/logicnode/AddPhysicsHookNode.hx
Normal file
46
leenkx/Sources/leenkx/logicnode/AddPhysicsHookNode.hx
Normal file
@ -0,0 +1,46 @@
|
||||
package leenkx.logicnode;
|
||||
|
||||
import iron.object.Object;
|
||||
#if lnx_bullet
|
||||
import leenkx.trait.physics.bullet.PhysicsHook;
|
||||
#end
|
||||
|
||||
class AddPhysicsHookNode extends LogicNode {
|
||||
|
||||
public function new(tree: LogicTree) {
|
||||
super(tree);
|
||||
}
|
||||
|
||||
override function run(from: Int) {
|
||||
var obj: Object = inputs[1].get();
|
||||
var target: Object = inputs[2].get();
|
||||
var inputVerts: Dynamic = inputs[3].get();
|
||||
|
||||
var flattenedVerts: Array<Float> = [];
|
||||
|
||||
if (Std.isOfType(inputVerts, Array)) {
|
||||
var vArray: Array<Dynamic> = cast inputVerts;
|
||||
for (v in vArray) {
|
||||
if (v.x != null) {
|
||||
flattenedVerts.push(v.x);
|
||||
flattenedVerts.push(v.y);
|
||||
flattenedVerts.push(v.z);
|
||||
}
|
||||
else if (v[0] != null) {
|
||||
flattenedVerts.push(v[0]);
|
||||
flattenedVerts.push(v[1]);
|
||||
flattenedVerts.push(v[2]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if lnx_bullet
|
||||
var hook = obj.getTrait(PhysicsHook);
|
||||
if (hook == null) {
|
||||
hook = new PhysicsHook(target.name, flattenedVerts);
|
||||
obj.addTrait(hook);
|
||||
}
|
||||
#end
|
||||
runOutput(0);
|
||||
}
|
||||
}
|
||||
@ -20,7 +20,6 @@ class AddRigidBodyNode extends LogicNode {
|
||||
|
||||
override function run(from: Int) {
|
||||
object = inputs[1].get();
|
||||
if (object == null) return;
|
||||
|
||||
#if lnx_physics
|
||||
|
||||
@ -70,6 +69,7 @@ class AddRigidBodyNode extends LogicNode {
|
||||
case "Cylinder": shape = Cylinder;
|
||||
case "Convex Hull": shape = ConvexHull;
|
||||
case "Mesh": shape = Mesh;
|
||||
case "Compound Parent": shape = Compound;
|
||||
}
|
||||
|
||||
rb = new RigidBody(shape, mass, friction, bounciness, group, mask);
|
||||
@ -77,6 +77,48 @@ class AddRigidBodyNode extends LogicNode {
|
||||
rb.staticObj = !active;
|
||||
rb.isTriggerObject(trigger);
|
||||
|
||||
if (property0 == "Compound Parent") {
|
||||
var compoundChildren = [];
|
||||
for (child in object.children) {
|
||||
var childRb: RigidBody = child.getTrait(RigidBody);
|
||||
if (childRb != null) {
|
||||
var childShape = 0;
|
||||
switch (@:privateAccess childRb.shape) {
|
||||
case Box: childShape = 0;
|
||||
case Sphere: childShape = 1;
|
||||
case ConvexHull: childShape = 2;
|
||||
case Mesh: childShape = 3;
|
||||
case Cone: childShape = 4;
|
||||
case Cylinder: childShape = 5;
|
||||
case Capsule: childShape = 6;
|
||||
default: childShape = 0;
|
||||
}
|
||||
childRb.remove();
|
||||
var m = object.transform.world.clone();
|
||||
m.getInverse(object.transform.world);
|
||||
m.multmat(child.transform.world);
|
||||
var loc = new iron.math.Vec4();
|
||||
var rot = new iron.math.Quat();
|
||||
var scl = new iron.math.Vec4();
|
||||
m.decompose(loc, rot, scl);
|
||||
compoundChildren.push({
|
||||
shape: childShape,
|
||||
posX: loc.x,
|
||||
posY: loc.y,
|
||||
posZ: loc.z,
|
||||
rotX: rot.x,
|
||||
rotY: rot.y,
|
||||
rotZ: rot.z,
|
||||
rotW: rot.w,
|
||||
dimX: child.transform.dim.x,
|
||||
dimY: child.transform.dim.y,
|
||||
dimZ: child.transform.dim.z
|
||||
});
|
||||
}
|
||||
}
|
||||
@:privateAccess rb.compoundChildren = compoundChildren;
|
||||
}
|
||||
|
||||
if (property1) {
|
||||
rb.linearDamping = linDamp;
|
||||
rb.angularDamping = angDamp;
|
||||
|
||||
38
leenkx/Sources/leenkx/logicnode/AddSoftBodyNode.hx
Normal file
38
leenkx/Sources/leenkx/logicnode/AddSoftBodyNode.hx
Normal file
@ -0,0 +1,38 @@
|
||||
package leenkx.logicnode;
|
||||
|
||||
import iron.object.Object;
|
||||
#if lnx_physics_soft
|
||||
import leenkx.trait.physics.bullet.SoftBody;
|
||||
#end
|
||||
|
||||
class AddSoftBodyNode extends LogicNode {
|
||||
|
||||
public function new(tree: LogicTree) {
|
||||
super(tree);
|
||||
}
|
||||
|
||||
override function run(from: Int) {
|
||||
var obj: Object = inputs[1].get();
|
||||
if (obj == null) return;
|
||||
|
||||
var shape: Int = inputs[2].get(); // 0: Cloth, 1: Volume
|
||||
var bend: Float = inputs[3].get();
|
||||
var mass: Float = inputs[4].get();
|
||||
var margin: Float = inputs[5].get();
|
||||
var friction: Float = inputs[6].get();
|
||||
var damping: Float = inputs[7].get();
|
||||
var pressure: Float = inputs[8].get();
|
||||
var lStiff: Float = inputs[9].get();
|
||||
var aStiff: Float = inputs[10].get();
|
||||
|
||||
#if lnx_physics_soft
|
||||
var sb: SoftBody = obj.getTrait(SoftBody);
|
||||
if (sb == null) {
|
||||
sb = new SoftBody(shape, bend, mass, margin, friction, damping, lStiff, aStiff, pressure);
|
||||
obj.addTrait(sb);
|
||||
}
|
||||
#end
|
||||
|
||||
runOutput(0);
|
||||
}
|
||||
}
|
||||
@ -4,25 +4,38 @@ import iron.object.Object;
|
||||
|
||||
class AddTraitNode extends LogicNode {
|
||||
|
||||
public var property0: String;
|
||||
var trait: Dynamic;
|
||||
|
||||
public function new(tree: LogicTree) {
|
||||
super(tree);
|
||||
}
|
||||
|
||||
override function run(from: Int) {
|
||||
var object: Object = inputs[1].get();
|
||||
var traitName: String = inputs[2].get();
|
||||
|
||||
assert(Error, object != null, "Object should not be null");
|
||||
assert(Error, traitName != null, "Trait name should not be null");
|
||||
|
||||
var cname = Type.resolveClass(Main.projectPackage + "." + traitName);
|
||||
if (cname == null) cname = Type.resolveClass(Main.projectPackage + ".node." + traitName);
|
||||
assert(Error, cname != null, 'No trait with the name "$traitName" found, make sure that the trait is exported!');
|
||||
assert(Warning, object.getTrait(cname) == null, 'Object already has the trait "$traitName" applied');
|
||||
if (property0 == 'TraitName'){
|
||||
var traitName: String = inputs[2].get();
|
||||
|
||||
assert(Error, traitName != null, "Trait name should not be null");
|
||||
|
||||
var cname = Type.resolveClass(Main.projectPackage + "." + traitName);
|
||||
if (cname == null) cname = Type.resolveClass(Main.projectPackage + ".node." + traitName);
|
||||
assert(Error, cname != null, 'No trait with the name "$traitName" found, make sure that the trait is exported!');
|
||||
assert(Warning, object.getTrait(cname) == null, 'Object already has the trait "$traitName" applied');
|
||||
|
||||
trait = Type.createInstance(cname, []);
|
||||
|
||||
} else
|
||||
trait = inputs[2].get();
|
||||
|
||||
var trait = Type.createInstance(cname, []);
|
||||
object.addTrait(trait);
|
||||
|
||||
runOutput(0);
|
||||
}
|
||||
|
||||
override function get(from: Int): Dynamic {
|
||||
return trait;
|
||||
}
|
||||
}
|
||||
|
||||
23
leenkx/Sources/leenkx/logicnode/AlongCurveNode.hx
Normal file
23
leenkx/Sources/leenkx/logicnode/AlongCurveNode.hx
Normal file
@ -0,0 +1,23 @@
|
||||
package leenkx.logicnode;
|
||||
|
||||
import iron.object.Object;
|
||||
import iron.object.CurveObject;
|
||||
|
||||
class AlongCurveNode extends LogicNode {
|
||||
|
||||
public function new(tree: LogicTree) {
|
||||
super(tree);
|
||||
}
|
||||
|
||||
override function run(from: Int) {
|
||||
var object: Object = inputs[1].get();
|
||||
var curve: CurveObject = inputs[2].get();
|
||||
var splineIdx: Int = inputs[3].get();
|
||||
var forwardAxis: String = inputs[4].get();
|
||||
var position: Float = inputs[5].get();
|
||||
|
||||
curve.follow(object, position, splineIdx, forwardAxis);
|
||||
|
||||
runOutput(0);
|
||||
}
|
||||
}
|
||||
35
leenkx/Sources/leenkx/logicnode/AnimationNode.hx
Normal file
35
leenkx/Sources/leenkx/logicnode/AnimationNode.hx
Normal file
@ -0,0 +1,35 @@
|
||||
package leenkx.logicnode;
|
||||
|
||||
import iron.object.Object;
|
||||
import iron.object.Animation;
|
||||
import iron.object.ObjectAnimation;
|
||||
|
||||
class AnimationNode extends LogicNode {
|
||||
|
||||
public function new(tree: LogicTree) {
|
||||
super(tree);
|
||||
}
|
||||
|
||||
override function get(from: Int): Dynamic {
|
||||
var object: Object = inputs[0].get();
|
||||
|
||||
if (object == null)
|
||||
return from == 0 ? null : 0;
|
||||
|
||||
var animation: Animation = object.animation;
|
||||
if (animation == null) animation = object.getParentArmature(object.name);
|
||||
|
||||
var actions: Array<String> = [];
|
||||
|
||||
if (animation.isSkinned)
|
||||
for(a in animation.armature.actions)
|
||||
actions.push(a.name);
|
||||
else
|
||||
for (a in cast(animation, ObjectAnimation).oactions)
|
||||
if (a != null)
|
||||
actions.push(a.objects[0].name);
|
||||
|
||||
return from == 0 ? actions : actions.length;
|
||||
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,29 @@
|
||||
package leenkx.logicnode;
|
||||
|
||||
#if lnx_navigation
|
||||
import leenkx.trait.navigation.Navigation;
|
||||
import leenkx.trait.NavMesh;
|
||||
#end
|
||||
|
||||
import iron.object.Object;
|
||||
import iron.math.Vec4;
|
||||
|
||||
class AroundNavigableLocationNode extends LogicNode {
|
||||
|
||||
public function new(tree: LogicTree) {
|
||||
super(tree);
|
||||
|
||||
}
|
||||
|
||||
override function get(from: Int): Dynamic {
|
||||
#if lnx_navigation
|
||||
var activeNavMesh: NavMesh = Navigation.active.navMeshes.get(inputs[0].get());
|
||||
var position: Vec4 = inputs[1].get();
|
||||
var radius: Float = inputs[2].get();
|
||||
|
||||
assert(Error, activeNavMesh != null, "No Navigation Mesh Present");
|
||||
return activeNavMesh.getRandomPointAround(position, radius);
|
||||
#end
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@ -12,7 +12,7 @@ class ArrayAddNode extends LogicNode {
|
||||
override function run(from: Int) {
|
||||
ar = inputs[1].get();
|
||||
|
||||
if (ar == null) return;
|
||||
assert(Error, ar != null, 'Array should not be null');
|
||||
|
||||
// "Modify Original" == `false` -> Copy the input array
|
||||
if (!inputs[2].get()) {
|
||||
|
||||
@ -2,7 +2,7 @@ package leenkx.logicnode;
|
||||
|
||||
class ArrayGetNextNode extends LogicNode {
|
||||
|
||||
var i = 0;
|
||||
var i = -1;
|
||||
|
||||
public function new(tree: LogicTree) {
|
||||
super(tree);
|
||||
@ -13,12 +13,12 @@ class ArrayGetNextNode extends LogicNode {
|
||||
|
||||
if (ar == null) return null;
|
||||
|
||||
var value = ar[i];
|
||||
|
||||
if (i < ar.length - 1)
|
||||
i++;
|
||||
else
|
||||
i = 0;
|
||||
|
||||
var value = ar[i];
|
||||
|
||||
return value;
|
||||
|
||||
|
||||
@ -2,7 +2,7 @@ package leenkx.logicnode;
|
||||
|
||||
class ArrayGetPreviousNextNode extends LogicNode {
|
||||
|
||||
var i = 0;
|
||||
var i = -1;
|
||||
|
||||
public function new(tree: LogicTree) {
|
||||
super(tree);
|
||||
|
||||
@ -13,7 +13,7 @@ class ArrayInsertNode extends LogicNode {
|
||||
var index: Int = inputs[2].get();
|
||||
var value: Dynamic = inputs[3].get();
|
||||
|
||||
if (ar == null || value == null) return;
|
||||
assert(Error, ar != null && value != null, 'Array or Value should not be null');
|
||||
|
||||
ar.insert(index, value);
|
||||
|
||||
|
||||
@ -11,7 +11,7 @@ class ArrayLoopNode extends LogicNode {
|
||||
|
||||
override function run(from: Int) {
|
||||
var ar: Array<Dynamic> = inputs[1].get();
|
||||
if (ar == null) return;
|
||||
assert(Error, ar != null, 'Array should not be null');
|
||||
|
||||
index = -1;
|
||||
for (val in ar) {
|
||||
|
||||
@ -10,7 +10,7 @@ class ArrayRemoveNode extends LogicNode {
|
||||
|
||||
override function run(from: Int) {
|
||||
var ar: Array<Dynamic> = inputs[1].get();
|
||||
if (ar == null) return;
|
||||
assert(Error, ar != null, 'Array should not be null');
|
||||
|
||||
var i: Int = inputs[2].get();
|
||||
if (i < 0) i = ar.length + i;
|
||||
|
||||
@ -10,7 +10,7 @@ class ArrayRemoveValueNode extends LogicNode {
|
||||
|
||||
override function run(from: Int) {
|
||||
var ar: Array<Dynamic> = inputs[1].get();
|
||||
if (ar == null) return;
|
||||
assert(Error, ar != null, 'Array should not be null');
|
||||
|
||||
var val: Dynamic = inputs[2].get();
|
||||
|
||||
|
||||
@ -8,7 +8,7 @@ class ArrayResizeNode extends LogicNode {
|
||||
|
||||
override function run(from: Int) {
|
||||
var ar: Array<Dynamic> = inputs[1].get();
|
||||
if (ar == null) return;
|
||||
assert(Error, ar != null, 'Array should not be null');
|
||||
|
||||
var len = inputs[2].get();
|
||||
|
||||
|
||||
@ -8,7 +8,7 @@ class ArraySetNode extends LogicNode {
|
||||
|
||||
override function run(from: Int) {
|
||||
var ar: Array<Dynamic> = inputs[1].get();
|
||||
if (ar == null) return;
|
||||
assert(Error, ar != null, 'Array should not be null');
|
||||
|
||||
var i: Int = inputs[2].get();
|
||||
var value: Dynamic = inputs[3].get();
|
||||
|
||||
@ -10,7 +10,7 @@ class ArraySpliceNode extends LogicNode {
|
||||
|
||||
override function run(from: Int) {
|
||||
var ar: Array<Dynamic> = inputs[1].get();
|
||||
if (ar == null) return;
|
||||
assert(Error, ar != null, 'Array should not be null');
|
||||
|
||||
var i = inputs[2].get();
|
||||
var len = inputs[3].get();
|
||||
|
||||
62
leenkx/Sources/leenkx/logicnode/AsyncArrayLoopNode.hx
Normal file
62
leenkx/Sources/leenkx/logicnode/AsyncArrayLoopNode.hx
Normal file
@ -0,0 +1,62 @@
|
||||
package leenkx.logicnode;
|
||||
|
||||
class AsyncArrayLoopNode extends LogicNode {
|
||||
|
||||
var array:Array<Dynamic>;
|
||||
var index:Int = 0;
|
||||
var running:Bool = false;
|
||||
var itemsPerFrame:Int = 1;
|
||||
|
||||
public function new(tree:LogicTree) {
|
||||
super(tree);
|
||||
}
|
||||
|
||||
override function run(from: Int) {
|
||||
array = inputs[1].get();
|
||||
itemsPerFrame = inputs[2].get();
|
||||
index = 0;
|
||||
if (array == null || array.length == 0) {
|
||||
runOutput(3);
|
||||
return;
|
||||
}
|
||||
running = true;
|
||||
tree.notifyOnUpdate(update);
|
||||
}
|
||||
|
||||
function update() {
|
||||
if (!running) return;
|
||||
|
||||
var processed = 0;
|
||||
while (processed < itemsPerFrame && index < array.length) {
|
||||
index++;
|
||||
processed++;
|
||||
runOutput(0);
|
||||
|
||||
if (tree.loopBreak) {
|
||||
tree.loopBreak = false;
|
||||
running = false;
|
||||
tree.removeUpdate(update);
|
||||
runOutput(2);
|
||||
return;
|
||||
}
|
||||
|
||||
if (tree.loopContinue) {
|
||||
tree.loopContinue = false;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (index >= array.length) {
|
||||
running = false;
|
||||
tree.removeUpdate(update);
|
||||
runOutput(3);
|
||||
}
|
||||
}
|
||||
|
||||
override function get(from: Int): Dynamic {
|
||||
if (from == 1)
|
||||
return array[index - 1];
|
||||
return index - 1;
|
||||
|
||||
}
|
||||
}
|
||||
63
leenkx/Sources/leenkx/logicnode/AsyncLoopNode.hx
Normal file
63
leenkx/Sources/leenkx/logicnode/AsyncLoopNode.hx
Normal file
@ -0,0 +1,63 @@
|
||||
package leenkx.logicnode;
|
||||
|
||||
class AsyncLoopNode extends LogicNode {
|
||||
|
||||
var from:Int;
|
||||
var to:Int;
|
||||
var index:Int;
|
||||
var running:Bool = false;
|
||||
var itemsPerFrame:Int = 1;
|
||||
|
||||
public function new(tree:LogicTree) {
|
||||
super(tree);
|
||||
}
|
||||
|
||||
override function run(from: Int) {
|
||||
this.from = inputs[1].get();
|
||||
this.to = inputs[2].get();
|
||||
this.itemsPerFrame = inputs[3].get();
|
||||
index = this.from;
|
||||
|
||||
if (this.from >= this.to) {
|
||||
runOutput(2);
|
||||
return;
|
||||
}
|
||||
|
||||
running = true;
|
||||
tree.notifyOnUpdate(update);
|
||||
}
|
||||
|
||||
function update() {
|
||||
if (!running) return;
|
||||
|
||||
var processed = 0;
|
||||
while (processed < itemsPerFrame && index < to) {
|
||||
runOutput(0);
|
||||
index++;
|
||||
processed++;
|
||||
|
||||
if (tree.loopBreak) {
|
||||
tree.loopBreak = false;
|
||||
running = false;
|
||||
tree.removeUpdate(update);
|
||||
runOutput(2);
|
||||
return;
|
||||
}
|
||||
|
||||
if (tree.loopContinue) {
|
||||
tree.loopContinue = false;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (index >= to) {
|
||||
running = false;
|
||||
tree.removeUpdate(update);
|
||||
runOutput(2);
|
||||
}
|
||||
}
|
||||
|
||||
override function get(from: Int): Dynamic {
|
||||
return index - 1;
|
||||
}
|
||||
}
|
||||
@ -1,7 +1,5 @@
|
||||
package leenkx.logicnode;
|
||||
|
||||
import iron.object.Object;
|
||||
|
||||
class CallFunctionNode extends LogicNode {
|
||||
|
||||
var result: Dynamic;
|
||||
@ -11,8 +9,8 @@ class CallFunctionNode extends LogicNode {
|
||||
}
|
||||
|
||||
override function run(from: Int) {
|
||||
var object: Dynamic = inputs[1].get();
|
||||
if (object == null) return;
|
||||
var trait: Dynamic = inputs[1].get();
|
||||
if (trait == null){ runOutput(0); return; }
|
||||
|
||||
var funName: String = inputs[2].get();
|
||||
var args: Array<Dynamic> = [];
|
||||
@ -21,9 +19,9 @@ class CallFunctionNode extends LogicNode {
|
||||
args.push(inputs[i].get());
|
||||
}
|
||||
|
||||
var func = Reflect.field(object, funName);
|
||||
var func = Reflect.field(trait, funName);
|
||||
if (func != null) {
|
||||
result = Reflect.callMethod(object, func, args);
|
||||
result = Reflect.callMethod(trait, func, args);
|
||||
}
|
||||
|
||||
runOutput(0);
|
||||
|
||||
@ -14,14 +14,16 @@ class CameraGetNode extends LogicNode {
|
||||
case 3: leenkx.renderpath.Postprocess.camera_uniforms[3];//Camera: Exposure Compensation
|
||||
case 4: leenkx.renderpath.Postprocess.camera_uniforms[4];//Fisheye Distortion
|
||||
case 5: leenkx.renderpath.Postprocess.camera_uniforms[5];//DoF AutoFocus §§ If true, it ignores the DoF Distance setting
|
||||
case 6: leenkx.renderpath.Postprocess.camera_uniforms[6];//DoF Distance
|
||||
case 7: leenkx.renderpath.Postprocess.camera_uniforms[7];//DoF Focal Length mm
|
||||
case 8: leenkx.renderpath.Postprocess.camera_uniforms[8];//DoF F-Stop
|
||||
case 9: leenkx.renderpath.Postprocess.camera_uniforms[9];//Tonemapping Method
|
||||
case 10: leenkx.renderpath.Postprocess.camera_uniforms[10];//Distort
|
||||
case 11: leenkx.renderpath.Postprocess.camera_uniforms[11];//Film Grain
|
||||
case 12: leenkx.renderpath.Postprocess.camera_uniforms[12];//Sharpen
|
||||
case 13: leenkx.renderpath.Postprocess.camera_uniforms[13];//Vignette
|
||||
case 6: new iron.math.Vec4(leenkx.renderpath.Postprocess.auto_focus[0], leenkx.renderpath.Postprocess.auto_focus[1], 0, 1); //Auto Focus Value
|
||||
case 7: leenkx.renderpath.Postprocess.auto_focus[2]; //max blur
|
||||
case 8: leenkx.renderpath.Postprocess.camera_uniforms[6];//DoF Distance
|
||||
case 9: leenkx.renderpath.Postprocess.camera_uniforms[7];//DoF Focal Length mm
|
||||
case 10: leenkx.renderpath.Postprocess.camera_uniforms[8];//DoF F-Stop
|
||||
case 11: leenkx.renderpath.Postprocess.camera_uniforms[9];//Tonemapping Method
|
||||
case 12: leenkx.renderpath.Postprocess.camera_uniforms[10];//Distort
|
||||
case 13: leenkx.renderpath.Postprocess.camera_uniforms[11];//Film Grain
|
||||
case 14: leenkx.renderpath.Postprocess.camera_uniforms[12];//Sharpen
|
||||
case 15: leenkx.renderpath.Postprocess.camera_uniforms[13];//Vignette
|
||||
default: 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
@ -23,6 +23,11 @@ class CameraSetNode extends LogicNode {
|
||||
leenkx.renderpath.Postprocess.camera_uniforms[4] = inputs[1].get();//Fisheye Distortion
|
||||
case 'Auto Focus':
|
||||
leenkx.renderpath.Postprocess.camera_uniforms[5] = inputs[1].get();//DoF AutoFocus §§ If true, it ignores the DoF Distance setting
|
||||
case 'Auto Focus Value':
|
||||
leenkx.renderpath.Postprocess.auto_focus[0] = inputs[1].get().x;
|
||||
leenkx.renderpath.Postprocess.auto_focus[1] = inputs[1].get().y;
|
||||
case 'DoF Max Blur':
|
||||
leenkx.renderpath.Postprocess.auto_focus[2] = inputs[1].get(); // DoF Max Blur
|
||||
case 'DoF Distance':
|
||||
leenkx.renderpath.Postprocess.camera_uniforms[6] = inputs[1].get();//DoF Distance
|
||||
case 'DoF Length':
|
||||
|
||||
@ -14,6 +14,7 @@ class CanvasSetSliderNode extends LogicNode {
|
||||
}
|
||||
|
||||
#if lnx_ui
|
||||
/*
|
||||
function update() {
|
||||
if (!canvas.ready) return;
|
||||
|
||||
@ -26,7 +27,7 @@ class CanvasSetSliderNode extends LogicNode {
|
||||
catch (e: Dynamic) {}
|
||||
|
||||
runOutput(0);
|
||||
}
|
||||
}*/
|
||||
|
||||
override function run(from: Int) {
|
||||
element = inputs[1].get();
|
||||
@ -34,9 +35,15 @@ class CanvasSetSliderNode extends LogicNode {
|
||||
|
||||
canvas = CanvasScript.getActiveCanvas();
|
||||
|
||||
canvas.notifyOnReady(() -> {
|
||||
canvas.getHandle(element).value = value;
|
||||
runOutput(0);
|
||||
|
||||
});
|
||||
|
||||
// Ensure canvas is ready
|
||||
tree.notifyOnUpdate(update);
|
||||
update();
|
||||
//tree.notifyOnUpdate(update);
|
||||
//update();
|
||||
}
|
||||
#end
|
||||
}
|
||||
|
||||
@ -4,148 +4,15 @@ import iron.math.Vec4;
|
||||
|
||||
class ColorMixNode extends LogicNode {
|
||||
|
||||
var SIZE:Int = 38;
|
||||
var GAMMA:Float = 2.4;
|
||||
var EPSILON:Float = 0.00000001;
|
||||
|
||||
var SPD_C:Array<Float> = [0.96853629, 0.96855103, 0.96859338, 0.96877345, 0.96942204, 0.97143709, 0.97541862, 0.98074186, 0.98580992, 0.98971194, 0.99238027, 0.99409844, 0.995172, 0.99576545, 0.99593552, 0.99564041, 0.99464769, 0.99229579, 0.98638762, 0.96829712, 0.89228016, 0.53740239, 0.15360445, 0.05705719, 0.03126539, 0.02205445, 0.01802271, 0.0161346, 0.01520947, 0.01475977, 0.01454263, 0.01444459, 0.01439897, 0.0143762, 0.01436343, 0.01435687, 0.0143537, 0.01435408];
|
||||
var SPD_M:Array<Float> = [0.51567122, 0.5401552, 0.62645502, 0.75595012, 0.92826996, 0.97223624, 0.98616174, 0.98955255, 0.98676237, 0.97312575, 0.91944277, 0.32564851, 0.13820628, 0.05015143, 0.02912336, 0.02421691, 0.02660696, 0.03407586, 0.04835936, 0.0001172, 0.00008554, 0.85267882, 0.93188793, 0.94810268, 0.94200977, 0.91478045, 0.87065445, 0.78827548, 0.65738359, 0.59909403, 0.56817268, 0.54031997, 0.52110241, 0.51041094, 0.50526577, 0.5025508, 0.50126452, 0.50083021];
|
||||
var SPD_Y:Array<Float> = [0.02055257, 0.02059936, 0.02062723, 0.02073387, 0.02114202, 0.02233154, 0.02556857, 0.03330189, 0.05185294, 0.10087639, 0.24000413, 0.53589066, 0.79874659, 0.91186529, 0.95399623, 0.97137099, 0.97939505, 0.98345207, 0.98553736, 0.98648905, 0.98674535, 0.98657555, 0.98611877, 0.98559942, 0.98507063, 0.98460039, 0.98425301, 0.98403909, 0.98388535, 0.98376116, 0.98368246, 0.98365023, 0.98361309, 0.98357259, 0.98353856, 0.98351247, 0.98350101, 0.98350852];
|
||||
var SPD_R:Array<Float> = [0.03147571, 0.03146636, 0.03140624, 0.03119611, 0.03053888, 0.02856855, 0.02459485, 0.0192952, 0.01423112, 0.01033111, 0.00765876, 0.00593693, 0.00485616, 0.00426186, 0.00409039, 0.00438375, 0.00537525, 0.00772962, 0.0136612, 0.03181352, 0.10791525, 0.46249516, 0.84604333, 0.94275572, 0.96860996, 0.97783966, 0.98187757, 0.98377315, 0.98470202, 0.98515481, 0.98537114, 0.98546685, 0.98550011, 0.98551031, 0.98550741, 0.98551323, 0.98551563, 0.98551547];
|
||||
var SPD_G:Array<Float> = [0.49108579, 0.46944057, 0.4016578, 0.2449042, 0.0682688, 0.02732883, 0.013606, 0.01000187, 0.01284127, 0.02636635, 0.07058713, 0.70421692, 0.85473994, 0.95081565, 0.9717037, 0.97651888, 0.97429245, 0.97012917, 0.9425863, 0.99989207, 0.99989891, 0.13823139, 0.06968113, 0.05628787, 0.06111561, 0.08987709, 0.13656016, 0.22169624, 0.32176956, 0.36157329, 0.4836192, 0.46488579, 0.47440306, 0.4857699, 0.49267971, 0.49625685, 0.49807754, 0.49889859];
|
||||
var SPD_B:Array<Float> = [0.97901834, 0.97901649, 0.97901118, 0.97892146, 0.97858555, 0.97743705, 0.97428075, 0.96663223, 0.94822893, 0.89937713, 0.76070164, 0.4642044, 0.20123039, 0.08808402, 0.04592894, 0.02860373, 0.02060067, 0.01656701, 0.01451549, 0.01357964, 0.01331243, 0.01347661, 0.01387181, 0.01435472, 0.01479836, 0.0151525, 0.01540513, 0.01557233, 0.0156571, 0.01571025, 0.01571916, 0.01572133, 0.01572502, 0.01571717, 0.01571905, 0.01571059, 0.01569728, 0.0157002];
|
||||
var CIE_CMF_X:Array<Float> = [0.00006469, 0.00021941, 0.00112057, 0.00376661, 0.01188055, 0.02328644, 0.03455942, 0.03722379, 0.03241838, 0.02123321, 0.01049099, 0.00329584, 0.00050704, 0.00094867, 0.00627372, 0.01686462, 0.02868965, 0.04267481, 0.05625475, 0.0694704, 0.08305315, 0.0861261, 0.09046614, 0.08500387, 0.07090667, 0.05062889, 0.03547396, 0.02146821, 0.01251646, 0.00680458, 0.00346457, 0.00149761, 0.0007697, 0.00040737, 0.00016901, 0.00009522, 0.00004903, 0.00002];
|
||||
var CIE_CMF_Y:Array<Float> = [0.00000184, 0.00000621, 0.00003101, 0.00010475, 0.00035364, 0.00095147, 0.00228226, 0.00420733, 0.0066888, 0.0098884, 0.01524945, 0.02141831, 0.03342293, 0.05131001, 0.07040208, 0.08783871, 0.09424905, 0.09795667, 0.09415219, 0.08678102, 0.07885653, 0.0635267, 0.05374142, 0.04264606, 0.03161735, 0.02088521, 0.01386011, 0.00810264, 0.0046301, 0.00249138, 0.0012593, 0.00054165, 0.00027795, 0.00014711, 0.00006103, 0.00003439, 0.00001771, 0.00000722];
|
||||
var CIE_CMF_Z:Array<Float> = [0.00030502, 0.00103681, 0.00531314, 0.01795439, 0.05707758, 0.11365162, 0.17335873, 0.19620658, 0.18608237, 0.13995048, 0.08917453, 0.04789621, 0.02814563, 0.01613766, 0.0077591, 0.00429615, 0.00200551, 0.00086147, 0.00036904, 0.00019143, 0.00014956, 0.00009231, 0.00006813, 0.00002883, 0.00001577, 0.00000394, 0.00000158, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0];
|
||||
var XYZ_RGB:Array<Array<Float>> = [[3.24306333, -1.53837619, -0.49893282], [-0.96896309, 1.87542451, 0.04154303], [0.05568392, -0.20417438, 1.05799454]];
|
||||
|
||||
public function linearToConcentration(l1:Float, l2:Float, t:Float):Float {
|
||||
var t1 = l1 * Math.pow(1 - t, 2);
|
||||
var t2 = l2 * Math.pow(t, 2);
|
||||
return t2 / (t1 + t2);
|
||||
}
|
||||
|
||||
public function spectralMix(color1:Array<Int>, color2:Array<Int>, t:Float):Array<Int> {
|
||||
var lrgb1 = srgbToLinear(color1);
|
||||
var lrgb2 = srgbToLinear(color2);
|
||||
|
||||
var R1 = linearToReflectance(lrgb1);
|
||||
var R2 = linearToReflectance(lrgb2);
|
||||
|
||||
var l1 = dotProduct(R1, CIE_CMF_Y);
|
||||
var l2 = dotProduct(R2, CIE_CMF_Y);
|
||||
|
||||
t = linearToConcentration(l1, l2, t);
|
||||
|
||||
var R:Array<Float> = new Array<Float>();
|
||||
for (i in 0...SIZE) {
|
||||
var KS = (1 - t) * Math.pow((1 - R1[i]), 2) / (2 * R1[i]) + t * Math.pow((1 - R2[i]), 2) / (2 * R2[i]);
|
||||
var KM = 1 + KS - Math.sqrt(KS * KS + 2 * KS);
|
||||
R.push(KM);
|
||||
}
|
||||
|
||||
var xyz = reflectanceToXYZ(R);
|
||||
return xyzToSrgb(xyz);
|
||||
}
|
||||
|
||||
public function uncompand(x:Float):Float {
|
||||
return x < 0.04045 ? x / 12.92 : Math.pow((x + 0.055) / 1.055, GAMMA);
|
||||
}
|
||||
|
||||
public function compand(x:Float):Float {
|
||||
return x < 0.0031308 ? x * 12.92 : 1.055 * Math.pow(x, 1.0 / GAMMA) - 0.055;
|
||||
}
|
||||
|
||||
public function srgbToLinear(srgb:Array<Int>):Array<Float> {
|
||||
var r = uncompand(srgb[0] / 255);
|
||||
var g = uncompand(srgb[1] / 255);
|
||||
var b = uncompand(srgb[2] / 255);
|
||||
return [r, g, b];
|
||||
}
|
||||
|
||||
public function linearToSrgb(lrgb:Array<Float>):Array<Int> {
|
||||
var r = compand(lrgb[0]);
|
||||
var g = compand(lrgb[1]);
|
||||
var b = compand(lrgb[2]);
|
||||
return [Math.round(clamp(r, 0, 1) * 255), Math.round(clamp(g, 0, 1) * 255), Math.round(clamp(b, 0, 1) * 255)];
|
||||
}
|
||||
|
||||
public function reflectanceToXYZ(R:Array<Float>):Array<Float> {
|
||||
var x = dotProduct(R, CIE_CMF_X);
|
||||
var y = dotProduct(R, CIE_CMF_Y);
|
||||
var z = dotProduct(R, CIE_CMF_Z);
|
||||
return [x, y, z];
|
||||
}
|
||||
|
||||
public function xyzToSrgb(xyz:Array<Float>):Array<Int> {
|
||||
var r = dotProduct(XYZ_RGB[0], xyz);
|
||||
var g = dotProduct(XYZ_RGB[1], xyz);
|
||||
var b = dotProduct(XYZ_RGB[2], xyz);
|
||||
return linearToSrgb([r, g, b]);
|
||||
}
|
||||
|
||||
public function spectralUpsampling(lrgb:Array<Float>):Array<Float> {
|
||||
var w = Math.min(Math.min(lrgb[0], lrgb[1]), lrgb[2]);
|
||||
|
||||
var lrgbNew = [lrgb[0] - w, lrgb[1] - w, lrgb[2] - w];
|
||||
|
||||
var c = Math.min(lrgbNew[1], lrgbNew[2]);
|
||||
var m = Math.min(lrgbNew[0], lrgbNew[2]);
|
||||
var y = Math.min(lrgbNew[0], lrgbNew[1]);
|
||||
var r = Math.max(0, Math.min(lrgbNew[0] - lrgbNew[2], lrgbNew[0] - lrgbNew[1]));
|
||||
var g = Math.max(0, Math.min(lrgbNew[1] - lrgbNew[2], lrgbNew[1] - lrgbNew[0]));
|
||||
var b = Math.max(0, Math.min(lrgbNew[2] - lrgbNew[1], lrgbNew[2] - lrgbNew[0]));
|
||||
|
||||
return [w, c, m, y, r, g, b];
|
||||
}
|
||||
|
||||
public function linearToReflectance(lrgb:Array<Float>):Array<Float> {
|
||||
var weights = spectralUpsampling(lrgb);
|
||||
|
||||
var R:Array<Float> = new Array<Float>();
|
||||
for (i in 0...SIZE) {
|
||||
R[i] = Math.max(EPSILON,
|
||||
weights[0]
|
||||
+ weights[1] * SPD_C[i]
|
||||
+ weights[2] * SPD_M[i]
|
||||
+ weights[3] * SPD_Y[i]
|
||||
+ weights[4] * SPD_R[i]
|
||||
+ weights[5] * SPD_G[i]
|
||||
+ weights[6] * SPD_B[i]
|
||||
);
|
||||
}
|
||||
|
||||
return R;
|
||||
}
|
||||
|
||||
public function dotProduct(a:Array<Float>, b:Array<Float>):Float {
|
||||
var sum:Float = 0;
|
||||
for (i in 0...a.length) {
|
||||
sum += a[i] * b[i];
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
public function clamp(value:Float, minValue:Float, maxValue:Float):Float {
|
||||
return Math.min(Math.max(value, minValue), maxValue);
|
||||
}
|
||||
|
||||
public function new(tree: LogicTree) {
|
||||
super(tree);
|
||||
}
|
||||
|
||||
override function get(from: Int): Dynamic {
|
||||
var Color1:Vec4 = inputs[0].get();
|
||||
var Color2:Vec4 = inputs[1].get();
|
||||
var mix:Float = inputs[2].get();
|
||||
|
||||
mix = Math.min(Math.max(mix, 0), 1);
|
||||
|
||||
var col1 = [Std.int(Color1.x*255), Std.int(Color1.y*255), Std.int(Color1.z*255)];
|
||||
var col2 = [Std.int(Color2.x*255), Std.int(Color2.y*255), Std.int(Color2.z*255)];
|
||||
|
||||
var ColorMix = spectralMix(col1, col2, mix);
|
||||
|
||||
return new Vec4(ColorMix[0]/255, ColorMix[1]/255, ColorMix[2]/255, (Color1.w+Color2.w)/2);
|
||||
var c1: Vec4 = inputs[0].get();
|
||||
var c2: Vec4 = inputs[1].get();
|
||||
var factor: Float = inputs[2].get();
|
||||
|
||||
return leenkx.trait.internal.Spectral.mix([c1, c2], [1.0 - factor, factor]);
|
||||
}
|
||||
}
|
||||
|
||||
26
leenkx/Sources/leenkx/logicnode/ColorsMixNode.hx
Normal file
26
leenkx/Sources/leenkx/logicnode/ColorsMixNode.hx
Normal file
@ -0,0 +1,26 @@
|
||||
package leenkx.logicnode;
|
||||
|
||||
import iron.math.Vec4;
|
||||
import leenkx.trait.internal.Spectral;
|
||||
|
||||
class ColorsMixNode extends LogicNode {
|
||||
|
||||
var result = new Vec4();
|
||||
|
||||
public function new(tree: LogicTree) {
|
||||
super(tree);
|
||||
}
|
||||
|
||||
override function get(from: Int): Dynamic {
|
||||
var colors: Array<Vec4> = inputs[0].get();
|
||||
var factors: Array<Float> = inputs[1].get();
|
||||
|
||||
if (colors == null || factors == null || colors.length == 0 || factors.length == 0) {
|
||||
return result;
|
||||
}
|
||||
|
||||
result.setFrom(Spectral.mix(colors, factors));
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
73
leenkx/Sources/leenkx/logicnode/ConvexBreakNode.hx
Normal file
73
leenkx/Sources/leenkx/logicnode/ConvexBreakNode.hx
Normal file
@ -0,0 +1,73 @@
|
||||
package leenkx.logicnode;
|
||||
|
||||
import iron.object.Object;
|
||||
import iron.object.MeshObject;
|
||||
import leenkx.object.BreakerExtension;
|
||||
import iron.math.Vec4;
|
||||
|
||||
class ConvexBreakNode extends LogicNode {
|
||||
|
||||
public var property0: String;
|
||||
|
||||
var objects: Array<Object>;
|
||||
|
||||
public function new(tree: LogicTree) {
|
||||
super(tree);
|
||||
}
|
||||
|
||||
override function run(from: Int) {
|
||||
objects = [];
|
||||
var object: Object = inputs[1].get();
|
||||
|
||||
var breaker: ConvexBreaker = new ConvexBreaker(0.1);
|
||||
@:privateAccess breaker.scaleUV = inputs[3].get();
|
||||
@:privateAccess breaker.flatShading = inputs[4].get();
|
||||
|
||||
breaker.initBreakableObject(cast object, 0, 0, new Vec4(), new Vec4(), true);
|
||||
|
||||
var debris: Array<MeshObject> = [];
|
||||
|
||||
if (property0 == 'Plane')
|
||||
debris = breaker.subdivideByPlane(cast object, inputs[3].get(), inputs[2].get());
|
||||
else
|
||||
debris = breaker.subdivideByImpact(cast object, inputs[2].get(), inputs[3].get(), 1, 1);
|
||||
|
||||
for (o in debris) {
|
||||
var obj: Object = cast o;
|
||||
obj.name = o.data.raw.name;
|
||||
|
||||
var dims = new kha.arrays.Float32Array(3);
|
||||
dims[0] = o.data.geom.aabb.x;
|
||||
dims[1] = o.data.geom.aabb.y;
|
||||
dims[2] = o.data.geom.aabb.z;
|
||||
|
||||
obj.raw = cast {
|
||||
type: "mesh_object",
|
||||
name: obj.name,
|
||||
data_ref: obj.name,
|
||||
dimensions: dims
|
||||
};
|
||||
|
||||
obj.addTrait(new leenkx.trait.internal.UniformsManager());
|
||||
|
||||
var ud = breaker.userDataMap.get(cast o);
|
||||
if (ud == null) continue;
|
||||
objects.push(obj);
|
||||
}
|
||||
|
||||
if (objects.length > 1){
|
||||
for (obj in objects)
|
||||
obj.setParent(iron.Scene.active.root);
|
||||
object.remove();
|
||||
runOutput(1);
|
||||
}
|
||||
else
|
||||
runOutput(2);
|
||||
|
||||
runOutput(0);
|
||||
}
|
||||
|
||||
override function get(from: Int): Dynamic {
|
||||
return objects;
|
||||
}
|
||||
}
|
||||
@ -1,7 +1,6 @@
|
||||
package leenkx.logicnode;
|
||||
import iron.object.Object;
|
||||
import iron.math.Vec4;
|
||||
import leenkx.system.Event;
|
||||
|
||||
|
||||
class CreateMapNode extends LogicNode {
|
||||
|
||||
@ -2,6 +2,8 @@ package leenkx.logicnode;
|
||||
|
||||
#if lnx_navigation
|
||||
import leenkx.trait.navigation.Navigation;
|
||||
import leenkx.trait.NavMesh;
|
||||
import leenkx.trait.NavCrowd;
|
||||
#end
|
||||
|
||||
import iron.object.Object;
|
||||
@ -9,24 +11,30 @@ import iron.math.Vec4;
|
||||
|
||||
class CrowdGoToLocationNode extends LogicNode {
|
||||
|
||||
var object: Object;
|
||||
var location: Vec4;
|
||||
|
||||
public function new(tree: LogicTree) {
|
||||
super(tree);
|
||||
}
|
||||
|
||||
override function run(from: Int) {
|
||||
object = inputs[1].get();
|
||||
location = inputs[2].get();
|
||||
|
||||
var navMeshId: String = inputs[1].get();
|
||||
var object: Object = inputs[2].get();
|
||||
var location: Vec4 = inputs[3].get();
|
||||
var maxSpeed: Float = inputs[4].get();
|
||||
var maxAcceleration: Float = inputs[5].get();
|
||||
var turnSpeed: Float = inputs[6].get();
|
||||
|
||||
assert(Error, object != null, "The object input not be null");
|
||||
assert(Error, location != null, "The location to navigate to must not be null");
|
||||
|
||||
#if lnx_navigation
|
||||
assert(Error, Navigation.active.navMeshes.length > 0, "No Navigation Mesh Present");
|
||||
var crowdAgent: leenkx.trait.NavCrowd = object.getTrait(leenkx.trait.NavCrowd);
|
||||
var activeNavMesh: NavMesh = Navigation.active.navMeshes.get(navMeshId);
|
||||
assert(Error, activeNavMesh != null, "No Navigation Mesh Present");
|
||||
|
||||
var crowdAgent: NavCrowd = object.getTrait(NavCrowd);
|
||||
assert(Error, crowdAgent != null, "Object does not have a NavCrowd trait");
|
||||
crowdAgent.crowdAgentSetMaxSpeed(maxSpeed);
|
||||
crowdAgent.crowdAgentSetMaxAcceleration(maxAcceleration);
|
||||
crowdAgent.turnSpeed = turnSpeed;
|
||||
crowdAgent.crowdAgentGoto(location);
|
||||
#end
|
||||
runOutput(0);
|
||||
|
||||
33
leenkx/Sources/leenkx/logicnode/CrowdSetLocationNode.hx
Normal file
33
leenkx/Sources/leenkx/logicnode/CrowdSetLocationNode.hx
Normal file
@ -0,0 +1,33 @@
|
||||
package leenkx.logicnode;
|
||||
|
||||
#if lnx_navigation
|
||||
import leenkx.trait.navigation.Navigation;
|
||||
import leenkx.trait.NavMesh;
|
||||
import leenkx.trait.NavCrowd;
|
||||
#end
|
||||
|
||||
import iron.object.Object;
|
||||
import iron.math.Vec4;
|
||||
|
||||
class CrowdSetLocationNode extends LogicNode {
|
||||
|
||||
public function new(tree: LogicTree) {
|
||||
super(tree);
|
||||
}
|
||||
|
||||
override function run(from: Int) {
|
||||
var navMeshId: String = inputs[1].get();
|
||||
var object: Object = inputs[2].get();
|
||||
var location: Vec4 = inputs[3].get();
|
||||
|
||||
#if lnx_navigation
|
||||
var activeNavMesh: NavMesh = Navigation.active.navMeshes.get(navMeshId);
|
||||
assert(Error, activeNavMesh != null, "No Navigation Mesh Present");
|
||||
|
||||
var crowdAgent: NavCrowd = object.getTrait(NavCrowd);
|
||||
assert(Error, crowdAgent != null, "Object does not have a NavCrowd trait");
|
||||
crowdAgent.crowdAgentTeleport(location);
|
||||
#end
|
||||
runOutput(0);
|
||||
}
|
||||
}
|
||||
33
leenkx/Sources/leenkx/logicnode/CurveGuideNode.hx
Normal file
33
leenkx/Sources/leenkx/logicnode/CurveGuideNode.hx
Normal file
@ -0,0 +1,33 @@
|
||||
package leenkx.logicnode;
|
||||
|
||||
import iron.object.Object;
|
||||
import iron.object.MeshObject;
|
||||
|
||||
class CurveGuideNode extends LogicNode {
|
||||
|
||||
public function new(tree: LogicTree) {
|
||||
super(tree);
|
||||
}
|
||||
|
||||
override function run(from: Int) {
|
||||
#if lnx_cpu_particles
|
||||
var object: Object = inputs[1].get();
|
||||
var slot: Int = inputs[2].get();
|
||||
|
||||
if (object == null){ runOutput(0); return; }
|
||||
|
||||
var mo: MeshObject = cast object;
|
||||
|
||||
var psys = mo.particleSystems != null ? mo.particleSystems[slot] : null;
|
||||
|
||||
if (psys == null){ runOutput(0); return; }
|
||||
|
||||
psys.curveGuides = inputs[3].get();
|
||||
psys.curveGuideStrength = inputs[4].get();
|
||||
psys.curveGuideSpeed = inputs[5].get();
|
||||
|
||||
#end
|
||||
|
||||
runOutput(0);
|
||||
}
|
||||
}
|
||||
27
leenkx/Sources/leenkx/logicnode/DeformCurveNode.hx
Normal file
27
leenkx/Sources/leenkx/logicnode/DeformCurveNode.hx
Normal file
@ -0,0 +1,27 @@
|
||||
package leenkx.logicnode;
|
||||
|
||||
import iron.object.CurveObject;
|
||||
import iron.object.MeshObject;
|
||||
import iron.data.MeshData;
|
||||
|
||||
class DeformCurveNode extends LogicNode {
|
||||
|
||||
public function new(tree: LogicTree) {
|
||||
super(tree);
|
||||
}
|
||||
|
||||
override function run(from: Int) {
|
||||
var mo: MeshObject = inputs[1].get();
|
||||
var curve: CurveObject = inputs[2].get();
|
||||
|
||||
var mData: MeshData = curve.generateDeformedMesh(mo.data, inputs[3].get(), inputs[4].get(), 1, inputs[5].get(), inputs[6].get());
|
||||
|
||||
if (mData != null){
|
||||
mo.setData(mData);
|
||||
mo.transform.scale.set(1, 1, 1);
|
||||
mo.transform.buildMatrix();
|
||||
}
|
||||
|
||||
runOutput(0);
|
||||
}
|
||||
}
|
||||
@ -8,11 +8,9 @@ import iron.object.CameraObject;
|
||||
import leenkx.renderpath.RenderPathCreator;
|
||||
|
||||
class DrawCameraNode extends LogicNode {
|
||||
static inline var numStaticInputs = 2;
|
||||
|
||||
var cameras: Array<CameraObject>;
|
||||
var renderTargets: Array<kha.Image>;
|
||||
var positions: Array<Vec2>;
|
||||
var camera: CameraObject;
|
||||
var renderTarget: kha.Image;
|
||||
var position: Vec2 = new Vec2();
|
||||
|
||||
public function new(tree: LogicTree) {
|
||||
super(tree);
|
||||
@ -21,39 +19,18 @@ class DrawCameraNode extends LogicNode {
|
||||
override function run(from: Int) {
|
||||
switch (from) {
|
||||
case 0: // Start
|
||||
if (cameras == null) {
|
||||
final numDynamicInputs = inputs.length - numStaticInputs;
|
||||
final numCams = Std.int(numDynamicInputs / 5);
|
||||
camera = inputs[2].get();
|
||||
position.set(
|
||||
inputs[3].get(),
|
||||
inputs[4].get()
|
||||
);
|
||||
|
||||
// Preallocate
|
||||
cameras = [];
|
||||
cameras.resize(numCams);
|
||||
|
||||
positions = [];
|
||||
positions.resize(numCams);
|
||||
for (i in 0...positions.length) {
|
||||
positions[i] = new Vec2();
|
||||
}
|
||||
|
||||
renderTargets = [];
|
||||
renderTargets.resize(numCams);
|
||||
}
|
||||
|
||||
for (i in 0...cameras.length) {
|
||||
cameras[i] = inputs[numStaticInputs + i * 5].get();
|
||||
positions[i].set(
|
||||
inputs[numStaticInputs + i * 5 + 1].get(),
|
||||
inputs[numStaticInputs + i * 5 + 2].get()
|
||||
);
|
||||
|
||||
// TODO: implement proper rendertarget cache/pool
|
||||
renderTargets[i] = kha.Image.createRenderTarget(
|
||||
inputs[numStaticInputs + i * 5 + 3].get(), // w
|
||||
inputs[numStaticInputs + i * 5 + 4].get(), // h
|
||||
kha.graphics4.TextureFormat.RGBA32,
|
||||
kha.graphics4.DepthStencilFormat.NoDepthAndStencil
|
||||
);
|
||||
}
|
||||
renderTarget = kha.Image.createRenderTarget(
|
||||
inputs[5].get(), // w
|
||||
inputs[6].get(), // h
|
||||
kha.graphics4.TextureFormat.RGBA32,
|
||||
kha.graphics4.DepthStencilFormat.NoDepthAndStencil
|
||||
);
|
||||
|
||||
tree.notifyOnRender(render);
|
||||
tree.notifyOnRender2D(render2D);
|
||||
@ -67,47 +44,45 @@ class DrawCameraNode extends LogicNode {
|
||||
}
|
||||
|
||||
function render(g:kha.graphics4.Graphics) {
|
||||
if (inputs[7].get()) return;
|
||||
final rpPaused = RenderPath.active.paused;
|
||||
RenderPath.active.paused = false;
|
||||
|
||||
final sceneCam = iron.Scene.active.camera;
|
||||
|
||||
for (i in 0...cameras.length) {
|
||||
final cam = cameras[i];
|
||||
final cam = camera;
|
||||
|
||||
final oldRT = cam.renderTarget;
|
||||
cam.renderTarget = renderTargets[i];
|
||||
final oldRT = cam.renderTarget;
|
||||
cam.renderTarget = renderTarget;
|
||||
|
||||
iron.Scene.active.camera = cam;
|
||||
cam.renderFrame(g);
|
||||
iron.Scene.active.camera = cam;
|
||||
cam.renderFrame(g);
|
||||
|
||||
cam.renderTarget = oldRT;
|
||||
}
|
||||
cam.renderTarget = oldRT;
|
||||
|
||||
iron.Scene.active.camera = sceneCam;
|
||||
RenderPath.active.paused = rpPaused;
|
||||
}
|
||||
|
||||
function render2D(g: kha.graphics2.Graphics) {
|
||||
for(i in 0...cameras.length) {
|
||||
final rt = renderTargets[i];
|
||||
if (inputs[7].get()) return;
|
||||
final rt = renderTarget;
|
||||
|
||||
positions[i].set(
|
||||
inputs[numStaticInputs + i * 5 + 1].get(),
|
||||
inputs[numStaticInputs + i * 5 + 2].get()
|
||||
);
|
||||
position.set(
|
||||
inputs[3].get(),
|
||||
inputs[4].get()
|
||||
);
|
||||
|
||||
final posX = positions[i].x;
|
||||
final posY = positions[i].y;
|
||||
final posX = position.x;
|
||||
final posY = position.y;
|
||||
|
||||
g.color = 0xff000000;
|
||||
g.fillRect(posX, posY, rt.width, rt.height);
|
||||
g.color = 0xffffffff;
|
||||
|
||||
if (kha.Image.renderTargetsInvertedY())
|
||||
g.drawScaledImage(rt, posX, posY+rt.height, rt.width, -rt.height);
|
||||
else
|
||||
g.drawScaledImage(rt, posX, posY, rt.width, rt.height);
|
||||
}
|
||||
g.color = 0xff000000;
|
||||
g.fillRect(posX, posY, rt.width, rt.height);
|
||||
g.color = 0xffffffff;
|
||||
|
||||
if (kha.Image.renderTargetsInvertedY())
|
||||
g.drawScaledImage(rt, posX, posY+rt.height, rt.width, -rt.height);
|
||||
else
|
||||
g.drawScaledImage(rt, posX, posY, rt.width, rt.height);
|
||||
}
|
||||
}
|
||||
|
||||
129
leenkx/Sources/leenkx/logicnode/DrawGifNode.hx
Normal file
129
leenkx/Sources/leenkx/logicnode/DrawGifNode.hx
Normal file
@ -0,0 +1,129 @@
|
||||
package leenkx.logicnode;
|
||||
|
||||
import iron.math.Vec4;
|
||||
import kha.Image;
|
||||
import kha.Color;
|
||||
import leenkx.renderpath.RenderToTexture;
|
||||
|
||||
import iron.format.gif.Reader;
|
||||
import iron.format.gif.Data;
|
||||
import iron.format.gif.Tools;
|
||||
|
||||
import haxe.io.Bytes;
|
||||
import haxe.io.BytesInput;
|
||||
|
||||
import leenkx.system.Event;
|
||||
|
||||
class DrawGifNode extends LogicNode {
|
||||
var data:Data = null;
|
||||
var frames:Int = 0;
|
||||
var img: Array<Image> = [];
|
||||
var lastImgName = "";
|
||||
var duration = 0.0;
|
||||
var index = 0;
|
||||
var i = 0;
|
||||
|
||||
public function new(tree: LogicTree) {
|
||||
super(tree);
|
||||
|
||||
Event.add('load', function() {
|
||||
var extractedBytes:Bytes = Tools.extractFullRGBA(data, i);
|
||||
img.push(kha.Image.fromBytes(extractedBytes, data.logicalScreenDescriptor.width, data.logicalScreenDescriptor.height, kha.graphics4.TextureFormat.RGBA32));
|
||||
});
|
||||
|
||||
tree.notifyOnRemove(onRemove);
|
||||
|
||||
}
|
||||
|
||||
function onRemove() {
|
||||
Event.remove('load');
|
||||
}
|
||||
|
||||
override function run(from: Int) {
|
||||
if (from == 0){
|
||||
|
||||
if (data == null){
|
||||
runOutput(0);
|
||||
return;
|
||||
}
|
||||
|
||||
RenderToTexture.ensure2DContext("DrawGifNode");
|
||||
|
||||
final colorVec: Vec4 = inputs[3].get();
|
||||
final anchorH: Int = inputs[4].get();
|
||||
final anchorV: Int = inputs[5].get();
|
||||
final x: Float = inputs[6].get();
|
||||
final y: Float = inputs[7].get();
|
||||
final width: Float = inputs[8].get();
|
||||
final height: Float = inputs[9].get();
|
||||
final angle: Float = inputs[10].get();
|
||||
|
||||
var sindex: Int = inputs[11].get();
|
||||
var eindex: Int = inputs[12].get();
|
||||
final fdur: Float = inputs[13].get();
|
||||
final loop: Bool = inputs[14].get();
|
||||
|
||||
final drawx = x - 0.5 * width * anchorH;
|
||||
final drawy = y - 0.5 * height * anchorV;
|
||||
|
||||
if(eindex == -1 || eindex > frames)
|
||||
eindex = frames;
|
||||
|
||||
if(sindex < 0 || sindex > frames)
|
||||
sindex = 0;
|
||||
|
||||
if (index < sindex || index > eindex)
|
||||
index = sindex;
|
||||
|
||||
duration += iron.system.Time.delta;
|
||||
if (duration >= fdur){
|
||||
if (index < eindex)
|
||||
index += 1;
|
||||
else
|
||||
if (loop) index = sindex;
|
||||
duration = 0;
|
||||
if (i < eindex){
|
||||
++i;
|
||||
Event.send('load');
|
||||
}
|
||||
}
|
||||
|
||||
if (img.length > (index - sindex)){
|
||||
RenderToTexture.g.rotate(angle, x, y);
|
||||
RenderToTexture.g.color = Color.fromFloats(colorVec.x, colorVec.y, colorVec.z, colorVec.w);
|
||||
RenderToTexture.g.drawScaledImage(img[index-sindex], drawx, drawy, width, height);
|
||||
RenderToTexture.g.rotate(-angle, x, y);
|
||||
}
|
||||
|
||||
runOutput(0);
|
||||
}
|
||||
else{
|
||||
final imgName: String = inputs[2].get();
|
||||
if (imgName != lastImgName) {
|
||||
lastImgName = imgName;
|
||||
img = [];
|
||||
i = inputs[11].get();
|
||||
index = i;
|
||||
iron.data.Data.getBlob(imgName, (blob: kha.Blob) -> {
|
||||
var bytes: Bytes = blob.toBytes();
|
||||
var input: BytesInput = new BytesInput(bytes);
|
||||
data = new Reader(input).read();
|
||||
frames = Tools.framesCount(data);
|
||||
if (i > frames) return;
|
||||
var extractedBytes:Bytes = Tools.extractFullRGBA(data, i);
|
||||
img.push(kha.Image.fromBytes(extractedBytes, data.logicalScreenDescriptor.width, data.logicalScreenDescriptor.height, kha.graphics4.TextureFormat.RGBA32));
|
||||
++i;
|
||||
Event.send('load');
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
override function get(from: Int): Dynamic {
|
||||
if (from == 1)
|
||||
return frames;
|
||||
else
|
||||
return index;
|
||||
}
|
||||
}
|
||||
@ -1,12 +1,15 @@
|
||||
package leenkx.logicnode;
|
||||
|
||||
import iron.math.Vec4;
|
||||
import iron.RenderPath;
|
||||
import kha.Image;
|
||||
import kha.Color;
|
||||
import leenkx.renderpath.RenderToTexture;
|
||||
|
||||
class DrawImageRenderNode extends LogicNode {
|
||||
var img: Image;
|
||||
var img2D: Image;
|
||||
var initialized: Bool = false;
|
||||
|
||||
public function new(tree: LogicTree) {
|
||||
super(tree);
|
||||
@ -14,8 +17,21 @@ class DrawImageRenderNode extends LogicNode {
|
||||
|
||||
override function run(from: Int) {
|
||||
|
||||
if (from == 1)
|
||||
if (from == 1){
|
||||
if (inputs[16].get()){
|
||||
if (initialized) return;
|
||||
initialized = true;
|
||||
}
|
||||
|
||||
img = kha.Image.createRenderTarget(iron.App.w(), iron.App.h(),
|
||||
kha.graphics4.TextureFormat.RGBA32,
|
||||
kha.graphics4.DepthStencilFormat.NoDepthAndStencil);
|
||||
if (inputs[15].get() || kha.Image.renderTargetsInvertedY())
|
||||
img2D = kha.Image.createRenderTarget(iron.App.w(), iron.App.h(),
|
||||
kha.graphics4.TextureFormat.RGBA32,
|
||||
kha.graphics4.DepthStencilFormat.NoDepthAndStencil);
|
||||
tree.notifyOnRender(render);
|
||||
}
|
||||
else {
|
||||
|
||||
RenderToTexture.ensure2DContext("DrawImageRenderNode");
|
||||
@ -38,12 +54,14 @@ class DrawImageRenderNode extends LogicNode {
|
||||
|
||||
RenderToTexture.g.rotate(angle, x, y);
|
||||
|
||||
if (img != null){
|
||||
RenderToTexture.g.color = 0xff000000;
|
||||
RenderToTexture.g.fillRect(drawx, drawy, width, height);
|
||||
RenderToTexture.g.color = RenderToTexture.g.color = Color.fromFloats(colorVec.x, colorVec.y, colorVec.z, colorVec.w);
|
||||
RenderToTexture.g.color = 0xff000000;
|
||||
RenderToTexture.g.fillRect(drawx, drawy, width, height);
|
||||
RenderToTexture.g.color = RenderToTexture.g.color = Color.fromFloats(colorVec.x, colorVec.y, colorVec.z, colorVec.w);
|
||||
|
||||
if ((inputs[15].get() || kha.Image.renderTargetsInvertedY()) && img2D != null)
|
||||
RenderToTexture.g.drawScaledSubImage(img2D, sx, sy, swidth, sheight, drawx, drawy, width, height);
|
||||
else if(img != null)
|
||||
RenderToTexture.g.drawScaledSubImage(img, sx, sy, swidth, sheight, drawx, drawy, width, height);
|
||||
}
|
||||
|
||||
RenderToTexture.g.rotate(-angle, x, y);
|
||||
|
||||
@ -54,13 +72,11 @@ class DrawImageRenderNode extends LogicNode {
|
||||
}
|
||||
|
||||
function render(g: kha.graphics4.Graphics) {
|
||||
final rpPaused = RenderPath.active.paused;
|
||||
RenderPath.active.paused = false;
|
||||
|
||||
var camera = inputs[2].get();
|
||||
|
||||
img = kha.Image.createRenderTarget(iron.App.w(), iron.App.h(),
|
||||
kha.graphics4.TextureFormat.RGBA32,
|
||||
kha.graphics4.DepthStencilFormat.NoDepthAndStencil);
|
||||
|
||||
final sceneCam = iron.Scene.active.camera;
|
||||
final oldRT = camera.renderTarget;
|
||||
|
||||
@ -69,38 +85,30 @@ class DrawImageRenderNode extends LogicNode {
|
||||
|
||||
camera.renderFrame(g);
|
||||
|
||||
img = camera.renderTarget;
|
||||
|
||||
if (inputs[15].get() || kha.Image.renderTargetsInvertedY()) {
|
||||
|
||||
img = kha.Image.createRenderTarget(iron.App.w(), iron.App.h(),
|
||||
kha.graphics4.TextureFormat.RGBA32,
|
||||
kha.graphics4.DepthStencilFormat.NoDepthAndStencil);
|
||||
img2D.g2.begin(true, Color.Transparent);
|
||||
img2D.g2.color = Color.White;
|
||||
|
||||
img.g2.begin(true, Color.Transparent);
|
||||
if (kha.Image.renderTargetsInvertedY())
|
||||
img2D.g2.drawScaledImage(camera.renderTarget, 0, iron.App.h(), iron.App.w(), -iron.App.h());
|
||||
else
|
||||
img2D.g2.drawImage(camera.renderTarget, 0, 0);
|
||||
|
||||
img.g2.color = Color.White;
|
||||
|
||||
if (kha.Image.renderTargetsInvertedY()) {
|
||||
img.g2.drawScaledImage(camera.renderTarget, 0, iron.App.h(), iron.App.w(), -iron.App.h());
|
||||
} else {
|
||||
img.g2.drawImage(camera.renderTarget, 0, 0);
|
||||
}
|
||||
|
||||
if (inputs[15].get()) {
|
||||
for (f in @:privateAccess iron.App.traitRenders2D) {
|
||||
f(img.g2);
|
||||
}
|
||||
}
|
||||
|
||||
img.g2.end();
|
||||
if (inputs[15].get())
|
||||
for (f in @:privateAccess iron.App.traitRenders2D)
|
||||
f(img2D.g2);
|
||||
|
||||
img2D.g2.end();
|
||||
}
|
||||
|
||||
camera.renderTarget = oldRT;
|
||||
iron.Scene.active.camera = sceneCam;
|
||||
|
||||
tree.removeRender(render);
|
||||
RenderPath.active.paused = rpPaused;
|
||||
|
||||
if (!inputs[16].get())
|
||||
tree.removeRender(render);
|
||||
|
||||
}
|
||||
|
||||
|
||||
@ -16,8 +16,8 @@ class DrawRoundedRectNode extends LogicNode {
|
||||
super(tree);
|
||||
}
|
||||
|
||||
#if lnx_ui
|
||||
override function run(from: Int) {
|
||||
#if lnx_ui
|
||||
RenderToTexture.ensure2DContext("DrawPolygonNode");
|
||||
|
||||
final anchorH: Int = inputs[4].get();
|
||||
@ -61,8 +61,6 @@ class DrawRoundedRectNode extends LogicNode {
|
||||
} else {
|
||||
RenderToTexture.g.drawPolygon(0, 0, vertices, inputs[3].get());
|
||||
}
|
||||
#end
|
||||
|
||||
RenderToTexture.g.rotate(-angle, x, y);
|
||||
runOutput(0);
|
||||
}
|
||||
@ -115,4 +113,5 @@ class DrawRoundedRectNode extends LogicNode {
|
||||
|
||||
return vertices;
|
||||
}
|
||||
#end
|
||||
}
|
||||
|
||||
@ -3,9 +3,13 @@ package leenkx.logicnode;
|
||||
import kha.Font;
|
||||
import kha.Color;
|
||||
import leenkx.renderpath.RenderToTexture;
|
||||
import kha.graphics2.VerTextAlignment;
|
||||
import kha.graphics2.HorTextAlignment;
|
||||
|
||||
#if lnx_ui
|
||||
import leenkx.ui.Canvas;
|
||||
|
||||
using zui.GraphicsExtension;
|
||||
#end
|
||||
|
||||
class DrawStringNode extends LogicNode {
|
||||
@ -13,13 +17,20 @@ class DrawStringNode extends LogicNode {
|
||||
var lastFontName = "";
|
||||
var string:String;
|
||||
|
||||
public var property1: String;
|
||||
public var property2: String;
|
||||
|
||||
public function new(tree: LogicTree) {
|
||||
super(tree);
|
||||
}
|
||||
|
||||
#if lnx_ui
|
||||
override function run(from: Int) {
|
||||
RenderToTexture.ensure2DContext("DrawStringNode");
|
||||
|
||||
var horA = TextLeft;
|
||||
var verA = TextTop;
|
||||
|
||||
string = Std.string(inputs[1].get());
|
||||
var angle: Float = inputs[7].get();
|
||||
|
||||
@ -45,6 +56,18 @@ class DrawStringNode extends LogicNode {
|
||||
return;
|
||||
}
|
||||
|
||||
switch(property1){
|
||||
case 'TextLeft': horA = TextLeft;
|
||||
case 'TextCenter': horA = TextCenter;
|
||||
case 'TextRight': horA = TextRight;
|
||||
}
|
||||
|
||||
switch(property2){
|
||||
case 'TextTop': verA = TextTop;
|
||||
case 'TextMiddle': verA = TextMiddle;
|
||||
case 'TextBottom': verA = TextBottom;
|
||||
}
|
||||
|
||||
RenderToTexture.g.rotate(angle, inputs[5].get(), inputs[6].get());
|
||||
|
||||
final colorVec = inputs[4].get();
|
||||
@ -53,7 +76,7 @@ class DrawStringNode extends LogicNode {
|
||||
RenderToTexture.g.fontSize = inputs[3].get();
|
||||
RenderToTexture.g.font = font;
|
||||
|
||||
RenderToTexture.g.drawString(string, inputs[5].get(), inputs[6].get());
|
||||
RenderToTexture.g.drawAlignedString(string, inputs[5].get(), inputs[6].get(), horA, verA);
|
||||
|
||||
RenderToTexture.g.rotate(-angle, inputs[5].get(), inputs[6].get());
|
||||
|
||||
@ -65,4 +88,5 @@ class DrawStringNode extends LogicNode {
|
||||
return from == 1 ? RenderToTexture.g.font.width(RenderToTexture.g.fontSize, string) : RenderToTexture.g.font.height(RenderToTexture.g.fontSize);
|
||||
|
||||
}
|
||||
#end
|
||||
}
|
||||
|
||||
47
leenkx/Sources/leenkx/logicnode/FollowCurveNode.hx
Normal file
47
leenkx/Sources/leenkx/logicnode/FollowCurveNode.hx
Normal file
@ -0,0 +1,47 @@
|
||||
package leenkx.logicnode;
|
||||
|
||||
import iron.object.Object;
|
||||
import iron.object.CurveObject;
|
||||
import iron.system.Time;
|
||||
|
||||
class FollowCurveNode extends LogicNode {
|
||||
|
||||
var progress: Float = -1;
|
||||
|
||||
public function new(tree: LogicTree) {
|
||||
super(tree);
|
||||
}
|
||||
|
||||
override function run(from: Int) {
|
||||
var cycle: Bool = false;
|
||||
var object: Object = inputs[1].get();
|
||||
var curve: CurveObject = inputs[2].get();
|
||||
var splineIdx: Int = inputs[3].get();
|
||||
|
||||
if (progress == -1) progress = inputs[8].get();
|
||||
|
||||
var len = curve.getLength(splineIdx);
|
||||
var speed = inputs[5].get();
|
||||
|
||||
var currentDist = progress * len;
|
||||
currentDist += (speed * Time.delta * (inputs[6].get() ? 1.0 : -1.0));
|
||||
|
||||
if (inputs[7].get()){
|
||||
if (currentDist > len){ currentDist -= len; cycle = true; }
|
||||
else if (currentDist < 0){ currentDist += len; cycle = true; }
|
||||
}
|
||||
|
||||
progress = (len > 0) ? currentDist / len : 0.0;
|
||||
|
||||
if (cycle)
|
||||
runOutput(1);
|
||||
else{
|
||||
curve.follow(object, progress, splineIdx, inputs[4].get());
|
||||
runOutput(0);
|
||||
}
|
||||
}
|
||||
|
||||
override function get(from: Int): Dynamic {
|
||||
return progress;
|
||||
}
|
||||
}
|
||||
57
leenkx/Sources/leenkx/logicnode/FormatNumberNode.hx
Normal file
57
leenkx/Sources/leenkx/logicnode/FormatNumberNode.hx
Normal file
@ -0,0 +1,57 @@
|
||||
package leenkx.logicnode;
|
||||
|
||||
class FormatNumberNode extends LogicNode {
|
||||
|
||||
public function new(tree: LogicTree) {
|
||||
super(tree);
|
||||
}
|
||||
|
||||
override function get(from: Int): Dynamic {
|
||||
var number: Float = inputs[0].get();
|
||||
var thousandsSeparator: String = inputs[1].get();
|
||||
var decimalSeparator: String = inputs[2].get();
|
||||
var includeSymbol: Bool = inputs[3].get();
|
||||
|
||||
var s = Std.string(number);
|
||||
var isNegative = s.charAt(0) == "-";
|
||||
if (isNegative) s = s.substring(1);
|
||||
|
||||
var parts = s.split(".");
|
||||
var integerPart = parts[0];
|
||||
var decimalPart = (parts.length > 1) ? parts[1] : "";
|
||||
|
||||
var len = integerPart.length;
|
||||
var formattedInteger = "";
|
||||
|
||||
if (len <= 3) {
|
||||
formattedInteger = integerPart;
|
||||
} else {
|
||||
var firstGroupLen = len % 3;
|
||||
if (firstGroupLen == 0) firstGroupLen = 3;
|
||||
|
||||
formattedInteger += integerPart.substring(0, firstGroupLen);
|
||||
|
||||
var i = firstGroupLen;
|
||||
while (i < len) {
|
||||
formattedInteger += thousandsSeparator + integerPart.substring(i, i + 3);
|
||||
i += 3;
|
||||
}
|
||||
}
|
||||
|
||||
var finalNumber = formattedInteger;
|
||||
if (decimalPart != "") {
|
||||
finalNumber += decimalSeparator + decimalPart;
|
||||
}
|
||||
|
||||
var result = "";
|
||||
var sign = isNegative ? "-" : "";
|
||||
|
||||
if (includeSymbol) {
|
||||
result = sign + "$" + finalNumber;
|
||||
} else {
|
||||
result = sign + finalNumber;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
32
leenkx/Sources/leenkx/logicnode/FormatTimeNode.hx
Normal file
32
leenkx/Sources/leenkx/logicnode/FormatTimeNode.hx
Normal file
@ -0,0 +1,32 @@
|
||||
package leenkx.logicnode;
|
||||
|
||||
class FormatTimeNode extends LogicNode {
|
||||
|
||||
public function new(tree: LogicTree) {
|
||||
super(tree);
|
||||
}
|
||||
|
||||
override function get(from: Int): Dynamic {
|
||||
var time: Float = inputs[0].get();
|
||||
var format: String = inputs[1].get();
|
||||
|
||||
var totalSeconds = Math.floor(time);
|
||||
var milliseconds = Math.floor((time - totalSeconds) * 1000);
|
||||
var centiseconds = Math.floor(milliseconds / 10);
|
||||
|
||||
var h = Math.floor(totalSeconds / 3600);
|
||||
var m = Math.floor((totalSeconds % 3600) / 60);
|
||||
var s = totalSeconds % 60;
|
||||
|
||||
var result = format;
|
||||
result = StringTools.replace(result, "HH", (h < 10 ? "0" : "") + h);
|
||||
result = StringTools.replace(result, "MM", (m < 10 ? "0" : "") + m);
|
||||
result = StringTools.replace(result, "SS", (s < 10 ? "0" : "") + s);
|
||||
result = StringTools.replace(result, "MS", (centiseconds < 10 ? "0" : "") + centiseconds);
|
||||
result = StringTools.replace(result, "H", "" + h);
|
||||
result = StringTools.replace(result, "M", "" + m);
|
||||
result = StringTools.replace(result, "S", "" + s);
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@ -2,24 +2,32 @@ package leenkx.logicnode;
|
||||
|
||||
import iron.object.Object;
|
||||
|
||||
#if lnx_navigation
|
||||
import leenkx.trait.navigation.Navigation;
|
||||
import leenkx.trait.NavAgent;
|
||||
#end
|
||||
|
||||
class GetAgentDataNode extends LogicNode {
|
||||
|
||||
public function new(tree: LogicTree) {
|
||||
super(tree);
|
||||
}
|
||||
|
||||
override function get(from: Int): Float {
|
||||
override function get(from: Int): Dynamic {
|
||||
var object: Object = inputs[0].get();
|
||||
|
||||
assert(Error, object != null, "The object to naviagte should not be null");
|
||||
|
||||
#if lnx_navigation
|
||||
var agent: leenkx.trait.NavAgent = object.getTrait(leenkx.trait.NavAgent);
|
||||
assert(Error, agent != null, "The object does not have NavAgent Trait");
|
||||
if(from == 0) return agent.speed;
|
||||
else return agent.turnDuration;
|
||||
#else
|
||||
return null;
|
||||
#end
|
||||
#if lnx_navigation
|
||||
var agent: NavAgent = object.getTrait(NavAgent);
|
||||
if (agent == null) return null;
|
||||
return switch(from){
|
||||
case 0: agent.navMeshId;
|
||||
case 1: agent.speed;
|
||||
case 2: agent.turnDuration;
|
||||
case 3: @:privateAccess agent.path;
|
||||
default: null;
|
||||
}
|
||||
#else
|
||||
return null;
|
||||
#end
|
||||
}
|
||||
}
|
||||
27
leenkx/Sources/leenkx/logicnode/GetAreaLightDataNode.hx
Normal file
27
leenkx/Sources/leenkx/logicnode/GetAreaLightDataNode.hx
Normal file
@ -0,0 +1,27 @@
|
||||
package leenkx.logicnode;
|
||||
|
||||
import iron.object.LightObject;
|
||||
|
||||
class GetAreaLightDataNode extends LogicNode {
|
||||
|
||||
public function new(tree: LogicTree) {
|
||||
super(tree);
|
||||
}
|
||||
|
||||
override function get(from: Int): Dynamic {
|
||||
var light: LightObject = inputs[0].get();
|
||||
|
||||
if (light == null) return null;
|
||||
|
||||
#if lnx_ltc
|
||||
if (light.data.raw.type == "area")
|
||||
if (from == 0)
|
||||
return light.data.raw.size;
|
||||
else
|
||||
return light.data.raw.size_y;
|
||||
#end
|
||||
|
||||
return null;
|
||||
|
||||
}
|
||||
}
|
||||
12
leenkx/Sources/leenkx/logicnode/GetAssetsNode.hx
Normal file
12
leenkx/Sources/leenkx/logicnode/GetAssetsNode.hx
Normal file
@ -0,0 +1,12 @@
|
||||
package leenkx.logicnode;
|
||||
|
||||
class GetAssetsNode extends LogicNode {
|
||||
|
||||
public function new(tree: LogicTree) {
|
||||
super(tree);
|
||||
}
|
||||
|
||||
override function get(from: Int): Dynamic {
|
||||
return leenkx.system.Starter.assets;
|
||||
}
|
||||
}
|
||||
@ -1,10 +1,18 @@
|
||||
package leenkx.logicnode;
|
||||
|
||||
import iron.object.Object;
|
||||
import iron.object.MeshObject;
|
||||
import iron.object.CameraObject;
|
||||
import iron.object.LightObject;
|
||||
import iron.object.SpeakerObject;
|
||||
import iron.object.DecalObject;
|
||||
import iron.object.ProbeObject;
|
||||
import iron.object.CurveObject;
|
||||
|
||||
class GetChildNode extends LogicNode {
|
||||
|
||||
public var property0: String;
|
||||
public var property1: String;
|
||||
|
||||
public function new(tree: LogicTree) {
|
||||
super(tree);
|
||||
@ -12,22 +20,31 @@ class GetChildNode extends LogicNode {
|
||||
|
||||
override function get(from: Int): Dynamic {
|
||||
var object: Object = inputs[0].get();
|
||||
var childName: String = inputs[1].get();
|
||||
if (object == null) return null;
|
||||
|
||||
if (object == null || childName == null) return null;
|
||||
if (property0 != "By Type") {
|
||||
var childName: String = inputs[1].get();
|
||||
if (childName == null) return null;
|
||||
|
||||
switch (property0) {
|
||||
case "By Name":
|
||||
return object.getChild(childName);
|
||||
case "Contains":
|
||||
return contains(object, childName);
|
||||
case "Starts With":
|
||||
return startsWith(object, childName);
|
||||
case "Ends With":
|
||||
return endsWith(object, childName);
|
||||
return switch (property0) {
|
||||
case "By Name": object.getChild(childName);
|
||||
case "Contains": contains(object, childName);
|
||||
case "Starts With": startsWith(object, childName);
|
||||
case "Ends With": endsWith(object, childName);
|
||||
default: null;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
return switch (property1) {
|
||||
case "MeshObject": object.getChildOfType(MeshObject);
|
||||
case "CameraObject": object.getChildOfType(CameraObject);
|
||||
case "LightObject": object.getChildOfType(LightObject);
|
||||
case "SpeakerObject": object.getChildOfType(SpeakerObject);
|
||||
case "DecalObject": object.getChildOfType(DecalObject);
|
||||
case "ProbeObject": object.getChildOfType(ProbeObject);
|
||||
case "CurveObject": object.getChildOfType(CurveObject);
|
||||
default: null;
|
||||
}
|
||||
}
|
||||
|
||||
function contains(o: Object, name: String): Object {
|
||||
|
||||
@ -13,6 +13,6 @@ class GetChildrenNode extends LogicNode {
|
||||
|
||||
if (object == null) return null;
|
||||
|
||||
return object.children;
|
||||
return from == 0 ? object.children : object.children.length;
|
||||
}
|
||||
}
|
||||
|
||||
@ -16,7 +16,9 @@ class GetContactsNode extends LogicNode {
|
||||
|
||||
#if lnx_physics
|
||||
var physics = leenkx.trait.physics.PhysicsWorld.active;
|
||||
var rbs = physics.getContacts(object.getTrait(RigidBody));
|
||||
var rb = object.getTrait(RigidBody);
|
||||
if (rb == null) return null;
|
||||
var rbs = physics.getContacts(rb);
|
||||
var obs = [];
|
||||
|
||||
if (rbs != null) for (rb in rbs) if (rb != null) obs.push(rb.object);
|
||||
|
||||
39
leenkx/Sources/leenkx/logicnode/GetCrowdDataNode.hx
Normal file
39
leenkx/Sources/leenkx/logicnode/GetCrowdDataNode.hx
Normal file
@ -0,0 +1,39 @@
|
||||
package leenkx.logicnode;
|
||||
|
||||
import iron.object.Object;
|
||||
|
||||
#if lnx_navigation
|
||||
import leenkx.trait.navigation.Navigation;
|
||||
import leenkx.trait.NavCrowd;
|
||||
#end
|
||||
|
||||
class GetCrowdDataNode extends LogicNode {
|
||||
|
||||
public function new(tree: LogicTree) {
|
||||
super(tree);
|
||||
}
|
||||
|
||||
override function get(from: Int): Dynamic {
|
||||
var object: Object = inputs[0].get();
|
||||
|
||||
#if lnx_navigation
|
||||
var crowdAgent: NavCrowd = object.getTrait(NavCrowd);
|
||||
if (crowdAgent == null) return null;
|
||||
return switch(from){
|
||||
case 0: crowdAgent.navMeshId;
|
||||
case 1: crowdAgent.crowdAgentVelocity();
|
||||
case 2: crowdAgent.crowdAgentMaxSpeed();
|
||||
case 3: crowdAgent.crowdAgentMaxAcceleration();
|
||||
case 4: crowdAgent.turnSpeed;
|
||||
case 5: crowdAgent.crowdAgentPosition();
|
||||
case 6: crowdAgent.crowdAgentNextPath();
|
||||
case 7: @:privateAccess crowdAgent.agentID;
|
||||
case 8: crowdAgent.crowdAgentPath();
|
||||
default: null;
|
||||
}
|
||||
#else
|
||||
return null;
|
||||
|
||||
#end
|
||||
}
|
||||
}
|
||||
33
leenkx/Sources/leenkx/logicnode/GetCurveDataNode.hx
Normal file
33
leenkx/Sources/leenkx/logicnode/GetCurveDataNode.hx
Normal file
@ -0,0 +1,33 @@
|
||||
package leenkx.logicnode;
|
||||
|
||||
import iron.object.CurveObject;
|
||||
import iron.math.Vec4;
|
||||
|
||||
class GetCurveDataNode extends LogicNode {
|
||||
|
||||
public function new(tree: LogicTree) {
|
||||
super(tree);
|
||||
}
|
||||
|
||||
override function get(from: Int): Dynamic {
|
||||
var curve: CurveObject = inputs[0].get();
|
||||
if (curve == null) return null;
|
||||
return
|
||||
switch (from) {
|
||||
case 0:
|
||||
curve.splinesLength;
|
||||
case 1:
|
||||
curve.equidistantSamples;
|
||||
case 2:
|
||||
curve.visible;
|
||||
case 3:
|
||||
curve.data.strength;
|
||||
case 4:
|
||||
new Vec4(curve.data.color[0], curve.data.color[1], curve.data.color[2], curve.data.color[3]);
|
||||
case 5:
|
||||
curve.curveMesh;
|
||||
default:
|
||||
null;
|
||||
}
|
||||
}
|
||||
}
|
||||
36
leenkx/Sources/leenkx/logicnode/GetCurveSplineNode.hx
Normal file
36
leenkx/Sources/leenkx/logicnode/GetCurveSplineNode.hx
Normal file
@ -0,0 +1,36 @@
|
||||
package leenkx.logicnode;
|
||||
|
||||
import iron.object.CurveObject;
|
||||
import iron.math.Vec4;
|
||||
|
||||
class GetCurveSplineNode extends LogicNode {
|
||||
|
||||
public function new(tree: LogicTree) {
|
||||
super(tree);
|
||||
}
|
||||
|
||||
override function get(from: Int): Dynamic {
|
||||
var curve: CurveObject = inputs[0].get();
|
||||
var index: Int = inputs[1].get();
|
||||
|
||||
if (index > curve.splinesLength) return null;
|
||||
|
||||
switch(from){
|
||||
case 0:
|
||||
var points: Array<Vec4> = [];
|
||||
var worldMat = curve.transform.world;
|
||||
for (point in curve.data.splines[index].points){
|
||||
var p = new Vec4(point.co[0], point.co[1], point.co[2]);
|
||||
p.applymat(worldMat);
|
||||
points.push(p);
|
||||
}
|
||||
return points;
|
||||
case 1:
|
||||
return curve.data.splines[index].closed;
|
||||
case 2:
|
||||
return curve.data.splines[index].resolution;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@ -15,7 +15,10 @@ class GetFirstContactNode extends LogicNode {
|
||||
|
||||
#if lnx_physics
|
||||
var physics = leenkx.trait.physics.PhysicsWorld.active;
|
||||
var rbs = physics.getContacts(object.getTrait(RigidBody));
|
||||
|
||||
var rb = object.getTrait(RigidBody);
|
||||
if (rb == null) return null;
|
||||
var rbs = physics.getContacts(rb);
|
||||
|
||||
if (rbs != null && rbs.length > 0) return rbs[0].object;
|
||||
#end
|
||||
|
||||
@ -8,6 +8,6 @@ class GetGroupNode extends LogicNode {
|
||||
|
||||
override function get(from: Int): Dynamic {
|
||||
var groupName: String = inputs[0].get();
|
||||
return iron.Scene.active.getGroup(groupName);
|
||||
return from == 0 ? iron.Scene.active.getGroup(groupName) : iron.Scene.active.getGroup(groupName).length;
|
||||
}
|
||||
}
|
||||
|
||||
@ -38,10 +38,10 @@ class GetImageColorNode extends LogicNode {
|
||||
renderTarget.g2.color = Color.White;
|
||||
|
||||
if (property0 == 'Render' || property0 == 'Render&Render2D'){
|
||||
if (leenkx.renderpath.RenderPathCreator.finalTarget != null){
|
||||
var img: Image = iron.RenderPath.active.renderTargets.get("buf").image;
|
||||
renderTarget.g2.drawScaledImage(img, 0, 0, iron.App.w(), iron.App.h());
|
||||
}
|
||||
if (leenkx.renderpath.RenderPathCreator.finalTarget != null){
|
||||
var img: Image = iron.RenderPath.active.renderTargets.get("buf").image;
|
||||
renderTarget.g2.drawScaledImage(img, 0, 0, iron.App.w(), iron.App.h());
|
||||
}
|
||||
}
|
||||
|
||||
if (Image.renderTargetsInvertedY()){
|
||||
@ -75,7 +75,7 @@ class GetImageColorNode extends LogicNode {
|
||||
var pixels = renderTarget.getPixels();
|
||||
|
||||
var k = j * renderTarget.width + i;
|
||||
|
||||
|
||||
#if kha_krom
|
||||
var l = k;
|
||||
#elseif kha_html5
|
||||
@ -84,11 +84,11 @@ class GetImageColorNode extends LogicNode {
|
||||
|
||||
var r = pixels.get(l * 4 + 0)/255;
|
||||
var g = pixels.get(l * 4 + 1)/255;
|
||||
var b = pixels.get(l * 4 + 2)/255;
|
||||
var b = pixels.get(l * 4 + 2)/255;
|
||||
var a = pixels.get(l * 4 + 3)/255;
|
||||
|
||||
var v = new Vec4(r, g, b, a);
|
||||
|
||||
|
||||
return v;
|
||||
|
||||
}
|
||||
|
||||
52
leenkx/Sources/leenkx/logicnode/GetKeyboardNode.hx
Normal file
52
leenkx/Sources/leenkx/logicnode/GetKeyboardNode.hx
Normal file
@ -0,0 +1,52 @@
|
||||
package leenkx.logicnode;
|
||||
|
||||
import iron.system.Input;
|
||||
import iron.system.Time;
|
||||
|
||||
class GetKeyboardNode extends LogicNode {
|
||||
|
||||
public var property0: String;
|
||||
var activeKey: String = "";
|
||||
var lastTime: Float = -1.0;
|
||||
|
||||
public function new(tree: LogicTree) {
|
||||
super(tree);
|
||||
tree.notifyOnUpdate(update);
|
||||
}
|
||||
|
||||
function update() {
|
||||
var keyboard = Input.getKeyboard();
|
||||
var found = false;
|
||||
|
||||
for (k in iron.system.Keyboard.keys) {
|
||||
var b = false;
|
||||
switch (property0) {
|
||||
case "started":
|
||||
b = keyboard.started(k);
|
||||
case "down":
|
||||
b = keyboard.down(k);
|
||||
case "released":
|
||||
b = keyboard.released(k);
|
||||
}
|
||||
|
||||
if (b) {
|
||||
if (property0 == "started" || property0 == "released") {
|
||||
var currentTime = Time.time();
|
||||
if (currentTime == lastTime && Time.delta != 0) continue;
|
||||
lastTime = currentTime;
|
||||
}
|
||||
activeKey = k;
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (found) {
|
||||
runOutput(0);
|
||||
}
|
||||
}
|
||||
|
||||
override function get(from: Int): Dynamic {
|
||||
return activeKey;
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user