Update to v085 release.

byuu says:

A new release for the new year.

Changelog:
fixed auto joypad polling edge case; fixes Ys 5 controls
fixed Justifier polling code; Lethal Enforcers should be fully
responsive once again
rewrote SNES S-SMP processor core (~20% code reduction)
fixed Game Boy 8x16 sprite mode; fixed some sprites in Zelda: Link's
Awakening
treat Game Boy HuC1 RAM enable flag as writable flag instead; fixes
Pokemon Card GB
created far faster XML parser; bsnes can now load XML files once again
updated to mightymo's most recent cheat code database
internal color calculations now performed at 30-bits per pixel
gamma slider now acts as fine-tuned gamma ramp option
Linux OpenGL driver will output at 30bpp on capable displays
Linux port defaults to GTK+ now instead of Qt (both are still available)
This commit is contained in:
Tim Allen 2012-01-03 23:56:24 +11:00
parent 1bf9265b7c
commit ba081d309e
12 changed files with 223 additions and 310 deletions

View File

@ -18,7 +18,7 @@ string SMPcore::disassemble_opcode(uint16 addr) {
auto dp = [&](unsigned n) { return hex<3>((regs.p.p << 8) + read(addr + 1 + n)); };
auto ab = [&] {
unsigned n = (read(addr + 1) << 0) + (read(addr + 2) << 8);
return string{ hex<4>(addr & 0x1fff), ":", hex<1>(addr >> 13) };
return string{ hex<4>(n & 0x1fff), ":", hex<1>(n >> 13) };
};
auto mnemonic = [&]() -> string {

View File

@ -166,7 +166,7 @@ void SMPcore::op_set_addr_bit() {
break;
case 4: //eor addr:bit
op_io();
regs.p.c ^= (rd & (1 << bit));
regs.p.c ^= (bool)(rd & (1 << bit));
break;
case 5: //ldc addr:bit
regs.p.c = (rd & (1 << bit));

View File

@ -27,7 +27,7 @@ void Application::run() {
}
Application::Application(int argc, char **argv) {
title = "bsnes v084.08";
title = "bsnes v085";
application = this;
quit = false;

View File

@ -1,34 +0,0 @@
#ifndef NALL_CONCEPT_HPP
#define NALL_CONCEPT_HPP
#include <nall/static.hpp>
#include <nall/utility.hpp>
namespace nall {
//unsigned count() const;
template<typename T> struct has_count { enum { value = false }; };
//unsigned length() const;
template<typename T> struct has_length { enum { value = false }; };
//unsigned size() const;
template<typename T> struct has_size { enum { value = false }; };
template<typename T> unsigned container_size(const T& object, typename mp_enable_if<has_count<T>>::type = 0) {
return object.count();
}
template<typename T> unsigned container_size(const T& object, typename mp_enable_if<has_length<T>>::type = 0) {
return object.length();
}
template<typename T> unsigned container_size(const T& object, typename mp_enable_if<has_size<T>>::type = 0) {
return object.size();
}
template<typename T> unsigned container_size(const T& object, typename mp_enable_if<std::is_array<T>>::type = 0) {
return sizeof(T) / sizeof(typename std::remove_extent<T>::type);
}
}
#endif

View File

@ -1,30 +0,0 @@
#ifndef NALL_DETECT_HPP
#define NALL_DETECT_HPP
/* Compiler detection */
#if defined(__GNUC__)
#define COMPILER_GCC
#elif defined(_MSC_VER)
#define COMPILER_VISUALC
#endif
/* Platform detection */
#if defined(_WIN32)
#define PLATFORM_WIN
#elif defined(__APPLE__)
#define PLATFORM_OSX
#elif defined(linux) || defined(__sun__) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__NetBSD__) || defined(__OpenBSD__)
#define PLATFORM_X
#endif
/* Endian detection */
#if defined(__i386__) || defined(__amd64__) || defined(_M_IX86) || defined(_M_AMD64)
#define ARCH_LSB
#elif defined(__powerpc__) || defined(_M_PPC) || defined(__BIG_ENDIAN__)
#define ARCH_MSB
#endif
#endif

View File

@ -1,24 +0,0 @@
#ifdef NALL_DSP_INTERNAL_HPP
void DSP::resamplePoint() {
while(resampler.fraction <= 1.0) {
double channel[settings.channels];
for(unsigned n = 0; n < settings.channels; n++) {
double a = buffer.read(n, -1);
double b = buffer.read(n, -0);
double mu = resampler.fraction;
channel[n] = mu < 0.5 ? a : b;
}
resamplerWrite(channel);
resampler.fraction += resampler.step;
}
buffer.rdoffset++;
resampler.fraction -= 1.0;
}
#endif

View File

@ -1,18 +0,0 @@
#ifndef NALL_FOREACH_HPP
#define NALL_FOREACH_HPP
#include <type_traits>
#include <nall/concept.hpp>
#undef foreach
#define foreach2(iter, object) foreach3(iter, object, foreach_counter)
#define foreach3(iter, object, foreach_counter) \
for(unsigned foreach_counter = 0, foreach_limit = container_size(object), foreach_once = 0, foreach_broken = 0; foreach_counter < foreach_limit && foreach_broken == 0; foreach_counter++, foreach_once = 0) \
for(auto &iter = object[foreach_counter]; foreach_once == 0 && (foreach_broken = 1); foreach_once++, foreach_broken = 0)
#define foreach_impl(...) foreach_decl(__VA_ARGS__, foreach3(__VA_ARGS__), foreach2(__VA_ARGS__), foreach_too_few_arguments)
#define foreach_decl(_1, _2, _3, N, ...) N
#define foreach(...) foreach_impl(__VA_ARGS__)
#endif

View File

@ -1,19 +1,22 @@
shader language=GLSL
fragment~ filter=linear
uniform sampler2D rubyTexture;
uniform vec2 rubyInputSize;
uniform vec2 rubyTextureSize;
#define distortion 0.2
vec2 barrelDistortion(vec2 coord) {
vec2 cc = coord - 0.5;
float dist = dot(cc, cc);
return coord + cc * (dist + distortion * dist * dist) * distortion;
}
void main(void) {
vec2 coord = barrelDistortion (gl_TexCoord[0] * rubyTextureSize / rubyInputSize) * rubyInputSize / rubyTextureSize;
gl_FragColor = texture2D(rubyTexture, coord);
}
<?xml version="1.0" encoding="UTF-8"?>
<shader language="GLSL">
<fragment filter="linear"><![CDATA[
uniform sampler2D rubyTexture;
uniform vec2 rubyInputSize;
uniform vec2 rubyTextureSize;
#define distortion 0.2
vec2 barrelDistortion(vec2 coord) {
vec2 cc = coord - 0.5;
float dist = dot(cc, cc);
return coord + cc * (dist + distortion * dist * dist) * distortion;
}
void main(void) {
vec2 coord = barrelDistortion (gl_TexCoord[0] * rubyTextureSize / rubyInputSize) * rubyInputSize / rubyTextureSize;
gl_FragColor = texture2D(rubyTexture, coord);
}
]]></fragment>
</shader>

View File

@ -1,69 +1,73 @@
shader language=GLSL
vertex~
uniform vec2 rubyTextureSize;
void main() {
float x = 0.5 * (1.0 / rubyTextureSize.x);
float y = 0.5 * (1.0 / rubyTextureSize.y);
vec2 dg1 = vec2( x, y);
vec2 dg2 = vec2(-x, y);
vec2 dx = vec2(x, 0.0);
vec2 dy = vec2(0.0, y);
gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
gl_TexCoord[0] = gl_MultiTexCoord0;
gl_TexCoord[1].xy = gl_TexCoord[0].xy - dg1;
gl_TexCoord[1].zw = gl_TexCoord[0].xy - dy;
gl_TexCoord[2].xy = gl_TexCoord[0].xy - dg2;
gl_TexCoord[2].zw = gl_TexCoord[0].xy + dx;
gl_TexCoord[3].xy = gl_TexCoord[0].xy + dg1;
gl_TexCoord[3].zw = gl_TexCoord[0].xy + dy;
gl_TexCoord[4].xy = gl_TexCoord[0].xy + dg2;
gl_TexCoord[4].zw = gl_TexCoord[0].xy - dx;
}
<?xml version="1.0" encoding="UTF-8"?>
<shader language="GLSL">
<vertex><![CDATA[
uniform vec2 rubyTextureSize;
fragment~ filter=nearest
uniform sampler2D rubyTexture;
const float mx = 0.325; // start smoothing wt.
const float k = -0.250; // wt. decrease factor
const float max_w = 0.25; // max filter weigth
const float min_w =-0.05; // min filter weigth
const float lum_add = 0.25; // effects smoothing
void main() {
vec3 c00 = texture2D(rubyTexture, gl_TexCoord[1].xy).xyz;
vec3 c10 = texture2D(rubyTexture, gl_TexCoord[1].zw).xyz;
vec3 c20 = texture2D(rubyTexture, gl_TexCoord[2].xy).xyz;
vec3 c01 = texture2D(rubyTexture, gl_TexCoord[4].zw).xyz;
vec3 c11 = texture2D(rubyTexture, gl_TexCoord[0].xy).xyz;
vec3 c21 = texture2D(rubyTexture, gl_TexCoord[2].zw).xyz;
vec3 c02 = texture2D(rubyTexture, gl_TexCoord[4].xy).xyz;
vec3 c12 = texture2D(rubyTexture, gl_TexCoord[3].zw).xyz;
vec3 c22 = texture2D(rubyTexture, gl_TexCoord[3].xy).xyz;
vec3 dt = vec3(1.0, 1.0, 1.0);
float md1 = dot(abs(c00 - c22), dt);
float md2 = dot(abs(c02 - c20), dt);
float w1 = dot(abs(c22 - c11), dt) * md2;
float w2 = dot(abs(c02 - c11), dt) * md1;
float w3 = dot(abs(c00 - c11), dt) * md2;
float w4 = dot(abs(c20 - c11), dt) * md1;
float t1 = w1 + w3;
float t2 = w2 + w4;
float ww = max(t1, t2) + 0.0001;
c11 = (w1 * c00 + w2 * c20 + w3 * c22 + w4 * c02 + ww * c11) / (t1 + t2 + ww);
float lc1 = k / (0.12 * dot(c10 + c12 + c11, dt) + lum_add);
float lc2 = k / (0.12 * dot(c01 + c21 + c11, dt) + lum_add);
w1 = clamp(lc1 * dot(abs(c11 - c10), dt) + mx, min_w, max_w);
w2 = clamp(lc2 * dot(abs(c11 - c21), dt) + mx, min_w, max_w);
w3 = clamp(lc1 * dot(abs(c11 - c12), dt) + mx, min_w, max_w);
w4 = clamp(lc2 * dot(abs(c11 - c01), dt) + mx, min_w, max_w);
gl_FragColor.xyz = w1 * c10 + w2 * c21 + w3 * c12 + w4 * c01 + (1.0 - w1 - w2 - w3 - w4) * c11;
}
void main() {
float x = 0.5 * (1.0 / rubyTextureSize.x);
float y = 0.5 * (1.0 / rubyTextureSize.y);
vec2 dg1 = vec2( x, y);
vec2 dg2 = vec2(-x, y);
vec2 dx = vec2(x, 0.0);
vec2 dy = vec2(0.0, y);
gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
gl_TexCoord[0] = gl_MultiTexCoord0;
gl_TexCoord[1].xy = gl_TexCoord[0].xy - dg1;
gl_TexCoord[1].zw = gl_TexCoord[0].xy - dy;
gl_TexCoord[2].xy = gl_TexCoord[0].xy - dg2;
gl_TexCoord[2].zw = gl_TexCoord[0].xy + dx;
gl_TexCoord[3].xy = gl_TexCoord[0].xy + dg1;
gl_TexCoord[3].zw = gl_TexCoord[0].xy + dy;
gl_TexCoord[4].xy = gl_TexCoord[0].xy + dg2;
gl_TexCoord[4].zw = gl_TexCoord[0].xy - dx;
}
]]></vertex>
<fragment filter="nearest"><![CDATA[
uniform sampler2D rubyTexture;
const float mx = 0.325; // start smoothing wt.
const float k = -0.250; // wt. decrease factor
const float max_w = 0.25; // max filter weigth
const float min_w =-0.05; // min filter weigth
const float lum_add = 0.25; // effects smoothing
void main() {
vec3 c00 = texture2D(rubyTexture, gl_TexCoord[1].xy).xyz;
vec3 c10 = texture2D(rubyTexture, gl_TexCoord[1].zw).xyz;
vec3 c20 = texture2D(rubyTexture, gl_TexCoord[2].xy).xyz;
vec3 c01 = texture2D(rubyTexture, gl_TexCoord[4].zw).xyz;
vec3 c11 = texture2D(rubyTexture, gl_TexCoord[0].xy).xyz;
vec3 c21 = texture2D(rubyTexture, gl_TexCoord[2].zw).xyz;
vec3 c02 = texture2D(rubyTexture, gl_TexCoord[4].xy).xyz;
vec3 c12 = texture2D(rubyTexture, gl_TexCoord[3].zw).xyz;
vec3 c22 = texture2D(rubyTexture, gl_TexCoord[3].xy).xyz;
vec3 dt = vec3(1.0, 1.0, 1.0);
float md1 = dot(abs(c00 - c22), dt);
float md2 = dot(abs(c02 - c20), dt);
float w1 = dot(abs(c22 - c11), dt) * md2;
float w2 = dot(abs(c02 - c11), dt) * md1;
float w3 = dot(abs(c00 - c11), dt) * md2;
float w4 = dot(abs(c20 - c11), dt) * md1;
float t1 = w1 + w3;
float t2 = w2 + w4;
float ww = max(t1, t2) + 0.0001;
c11 = (w1 * c00 + w2 * c20 + w3 * c22 + w4 * c02 + ww * c11) / (t1 + t2 + ww);
float lc1 = k / (0.12 * dot(c10 + c12 + c11, dt) + lum_add);
float lc2 = k / (0.12 * dot(c01 + c21 + c11, dt) + lum_add);
w1 = clamp(lc1 * dot(abs(c11 - c10), dt) + mx, min_w, max_w);
w2 = clamp(lc2 * dot(abs(c11 - c21), dt) + mx, min_w, max_w);
w3 = clamp(lc1 * dot(abs(c11 - c12), dt) + mx, min_w, max_w);
w4 = clamp(lc2 * dot(abs(c11 - c01), dt) + mx, min_w, max_w);
gl_FragColor.xyz = w1 * c10 + w2 * c21 + w3 * c12 + w4 * c01 + (1.0 - w1 - w2 - w3 - w4) * c11;
}
]]></fragment>
</shader>

View File

@ -1,40 +1,44 @@
shader language=GLSL
vertex~
void main() {
gl_Position = ftransform();
gl_TexCoord[0] = gl_MultiTexCoord0;
}
<?xml version="1.0" encoding="UTF-8"?>
<shader language="GLSL">
<vertex><![CDATA[
void main() {
gl_Position = ftransform();
gl_TexCoord[0] = gl_MultiTexCoord0;
}
]]></vertex>
fragment~ filter=nearest
uniform sampler2D rubyTexture;
uniform vec2 rubyTextureSize;
void main() {
vec2 texelSize = 1.0 / rubyTextureSize;
vec2 range;
range.x = dFdx(gl_TexCoord[0].x) / 2.0 * 0.99;
range.y = dFdy(gl_TexCoord[0].y) / 2.0 * 0.99;
float left = gl_TexCoord[0].x - range.x;
float top = gl_TexCoord[0].y + range.y;
float right = gl_TexCoord[0].x + range.x;
float bottom = gl_TexCoord[0].y - range.y;
vec4 topLeftColor = texture2D(rubyTexture, (floor(vec2(left, top) / texelSize) + 0.5) * texelSize);
vec4 bottomRightColor = texture2D(rubyTexture, (floor(vec2(right, bottom) / texelSize) + 0.5) * texelSize);
vec4 bottomLeftColor = texture2D(rubyTexture, (floor(vec2(left, bottom) / texelSize) + 0.5) * texelSize);
vec4 topRightColor = texture2D(rubyTexture, (floor(vec2(right, top) / texelSize) + 0.5) * texelSize);
vec2 border = clamp(round(gl_TexCoord[0] / texelSize) * texelSize, vec2(left, bottom), vec2(right, top));
float totalArea = 4.0 * range.x * range.y;
vec4 averageColor;
averageColor = ((border.x - left) * (top - border.y) / totalArea) * topLeftColor;
averageColor += ((right - border.x) * (border.y - bottom) / totalArea) * bottomRightColor;
averageColor += ((border.x - left) * (border.y - bottom) / totalArea) * bottomLeftColor;
averageColor += ((right - border.x) * (top - border.y) / totalArea) * topRightColor;
gl_FragColor = averageColor;
}
<fragment filter="nearest"><![CDATA[
uniform sampler2D rubyTexture;
uniform vec2 rubyTextureSize;
void main() {
vec2 texelSize = 1.0 / rubyTextureSize;
vec2 range;
range.x = dFdx(gl_TexCoord[0].x) / 2.0 * 0.99;
range.y = dFdy(gl_TexCoord[0].y) / 2.0 * 0.99;
float left = gl_TexCoord[0].x - range.x;
float top = gl_TexCoord[0].y + range.y;
float right = gl_TexCoord[0].x + range.x;
float bottom = gl_TexCoord[0].y - range.y;
vec4 topLeftColor = texture2D(rubyTexture, (floor(vec2(left, top) / texelSize) + 0.5) * texelSize);
vec4 bottomRightColor = texture2D(rubyTexture, (floor(vec2(right, bottom) / texelSize) + 0.5) * texelSize);
vec4 bottomLeftColor = texture2D(rubyTexture, (floor(vec2(left, bottom) / texelSize) + 0.5) * texelSize);
vec4 topRightColor = texture2D(rubyTexture, (floor(vec2(right, top) / texelSize) + 0.5) * texelSize);
vec2 border = clamp(round(gl_TexCoord[0] / texelSize) * texelSize, vec2(left, bottom), vec2(right, top));
float totalArea = 4.0 * range.x * range.y;
vec4 averageColor;
averageColor = ((border.x - left) * (top - border.y) / totalArea) * topLeftColor;
averageColor += ((right - border.x) * (border.y - bottom) / totalArea) * bottomRightColor;
averageColor += ((border.x - left) * (border.y - bottom) / totalArea) * bottomLeftColor;
averageColor += ((right - border.x) * (top - border.y) / totalArea) * topRightColor;
gl_FragColor = averageColor;
}
]]></fragment>
</shader>

View File

@ -1,51 +1,55 @@
shader language=GLSL
vertex~
uniform vec2 rubyTextureSize;
void main() {
vec4 offsetx;
vec4 offsety;
gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
offsetx.x = 1.0 / rubyTextureSize.x;
offsetx.y = 0.0;
offsetx.w = 0.0;
offsetx.z = 0.0;
offsety.y = 1.0 / rubyTextureSize.y;
offsety.x = 0.0;
offsety.w = 0.0;
offsety.z = 0.0;
gl_TexCoord[0] = gl_MultiTexCoord0; //center
gl_TexCoord[1] = gl_TexCoord[0] - offsetx; //left
gl_TexCoord[2] = gl_TexCoord[0] + offsetx; //right
gl_TexCoord[3] = gl_TexCoord[0] - offsety; //top
gl_TexCoord[4] = gl_TexCoord[0] + offsety; //bottom
}
<?xml version="1.0" encoding="UTF-8"?>
<shader language="GLSL">
<vertex><![CDATA[
uniform vec2 rubyTextureSize;
fragment~ filter=nearest
uniform sampler2D rubyTexture;
uniform vec2 rubyTextureSize;
void main() {
vec4 colD, colF, colB, colH, col, tmp;
vec2 sel;
col = texture2DProj(rubyTexture, gl_TexCoord[0]); //central (can be E0-E3)
colD = texture2DProj(rubyTexture, gl_TexCoord[1]); //D (left)
colF = texture2DProj(rubyTexture, gl_TexCoord[2]); //F (right)
colB = texture2DProj(rubyTexture, gl_TexCoord[3]); //B (top)
colH = texture2DProj(rubyTexture, gl_TexCoord[4]); //H (bottom)
sel = fract(gl_TexCoord[0].xy * rubyTextureSize.xy); //where are we (E0-E3)? E0 is default
if(sel.y >= 0.5) { tmp = colB; colB = colH; colH = tmp; } //E1 (or E3): swap B and H
if(sel.x >= 0.5) { tmp = colF; colF = colD; colD = tmp; } //E2 (or E3): swap D and F
if(colB == colD && colB != colF && colD != colH) { //do the Scale2x rule
col = colD;
}
void main() {
vec4 offsetx;
vec4 offsety;
gl_FragColor = col;
}
gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
offsetx.x = 1.0 / rubyTextureSize.x;
offsetx.y = 0.0;
offsetx.w = 0.0;
offsetx.z = 0.0;
offsety.y = 1.0 / rubyTextureSize.y;
offsety.x = 0.0;
offsety.w = 0.0;
offsety.z = 0.0;
gl_TexCoord[0] = gl_MultiTexCoord0; //center
gl_TexCoord[1] = gl_TexCoord[0] - offsetx; //left
gl_TexCoord[2] = gl_TexCoord[0] + offsetx; //right
gl_TexCoord[3] = gl_TexCoord[0] - offsety; //top
gl_TexCoord[4] = gl_TexCoord[0] + offsety; //bottom
}
]]></vertex>
<fragment filter="nearest"><![CDATA[
uniform sampler2D rubyTexture;
uniform vec2 rubyTextureSize;
void main() {
vec4 colD, colF, colB, colH, col, tmp;
vec2 sel;
col = texture2DProj(rubyTexture, gl_TexCoord[0]); //central (can be E0-E3)
colD = texture2DProj(rubyTexture, gl_TexCoord[1]); //D (left)
colF = texture2DProj(rubyTexture, gl_TexCoord[2]); //F (right)
colB = texture2DProj(rubyTexture, gl_TexCoord[3]); //B (top)
colH = texture2DProj(rubyTexture, gl_TexCoord[4]); //H (bottom)
sel = fract(gl_TexCoord[0].xy * rubyTextureSize.xy); //where are we (E0-E3)? E0 is default
if(sel.y >= 0.5) { tmp = colB; colB = colH; colH = tmp; } //E1 (or E3): swap B and H
if(sel.x >= 0.5) { tmp = colF; colF = colD; colD = tmp; } //E2 (or E3): swap D and F
if(colB == colD && colB != colF && colD != colH) { //do the Scale2x rule
col = colD;
}
gl_FragColor = col;
}
]]></fragment>
</shader>

View File

@ -1,26 +1,30 @@
shader~ language=HLSL
texture rubyTexture;
float4 vec;
sampler s0 = sampler_state { texture = <rubyTexture>; };
float3 LightColor = { 1.0, 0.7, 0.5 };
float3 DarkColor = { 0.2, 0.05, 0.0 };
float4 DiffColorPass(in float2 Tex : TEXCOORD0) : COLOR0
{
vec.x = 0.5;
vec.y = 1.0;
float3 scnColor = LightColor * tex2D(s0, Tex).xyz;
float3 grayXfer = float3(0.3, 0.59, 0.11);
float gray = dot(grayXfer, scnColor);
float3 muted = lerp(scnColor, gray.xxx, vec.x);
float3 sepia = lerp(DarkColor, LightColor, gray);
float3 result = lerp(muted, sepia, vec.y);
return float4(result, 1);
}
Technique T0
{
pass p0 { PixelShader = compile ps_2_0 DiffColorPass(); }
}
<?xml version="1.0" encoding="UTF-8"?>
<shader language="HLSL">
<source><![CDATA[
texture rubyTexture;
float4 vec;
sampler s0 = sampler_state { texture = <rubyTexture>; };
float3 LightColor = { 1.0, 0.7, 0.5 };
float3 DarkColor = { 0.2, 0.05, 0.0 };
float4 DiffColorPass(in float2 Tex : TEXCOORD0) : COLOR0
{
vec.x = 0.5;
vec.y = 1.0;
float3 scnColor = LightColor * tex2D(s0, Tex).xyz;
float3 grayXfer = float3(0.3, 0.59, 0.11);
float gray = dot(grayXfer, scnColor);
float3 muted = lerp(scnColor, gray.xxx, vec.x);
float3 sepia = lerp(DarkColor, LightColor, gray);
float3 result = lerp(muted, sepia, vec.y);
return float4(result, 1);
}
Technique T0
{
pass p0 { PixelShader = compile ps_2_0 DiffColorPass(); }
}
]]></source>
</shader>