diff --git a/.gitignore b/.gitignore index 2df65a565..e706f4140 100644 --- a/.gitignore +++ b/.gitignore @@ -27,6 +27,8 @@ *.exp *.zip *.swo +*.swm +*.swn *.opensdf *.user.* *.xcuserstate @@ -86,6 +88,16 @@ Player/Build/Mac OS X/Polycode Player.xcodeproj/project.xcworkspace/xcuserdata/i !/IDE/Build/Windows/Polycode.rc !/IDE/Build/Windows/resource.h +/IDE/Build/Windows2013/* +!/IDE/Build/Windows2013/main.cpp +!/IDE/Build/Windows2013/Polycode.props +!/IDE/Build/Windows2013/Polycode.sln +!/IDE/Build/Windows2013/Polycode.vcxproj +!/IDE/Build/Windows2013/Polycode.vcxproj.filters +!/IDE/Build/Windows2013/Polycode.vcxproj.user +!/IDE/Build/Windows2013/Polycode.rc +!/IDE/Build/Windows2013/resource.h + /IDE/Build/Linux/Build /Core/Build/Mac\ OS\ X/build @@ -104,14 +116,19 @@ Debug Release /Build /Documentation/Doxygen/output/standalone/Polycode +/Documentation/Doxygen/output/standalone/Core /Documentation/Doxygen/output/standalone/Physics2D /Documentation/Doxygen/output/standalone/Physics3D /Documentation/Doxygen/output/standalone/Networking +/Documentation/Doxygen/output/standalone/PolycodeUI /Documentation/Doxygen/output/web/Polycode +/Documentation/Doxygen/output/web/Core /Documentation/Doxygen/output/web/Physics2D /Documentation/Doxygen/output/web/Physics3D /Documentation/Doxygen/output/web/Networking +/Documentation/Doxygen/html +/Documentation/Doxygen/latex /Tools/Build/Linux/polybuild* /Tools/Build/Linux/polyimport* diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 000000000..a769e0874 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,7 @@ +language: cpp + +sudo: required + +install: ./setup-travis.sh + +script: ./BuildLinux.sh diff --git a/Assets/CMakeLists.txt b/Assets/CMakeLists.txt new file mode 100644 index 000000000..38de0465d --- /dev/null +++ b/Assets/CMakeLists.txt @@ -0,0 +1,6 @@ +ADD_SUBDIRECTORY("Default asset pack") +ADD_SUBDIRECTORY("Templates") + +IF(POLYCODE_INSTALL_FRAMEWORK) + INSTALL(FILES UIThemes.pak DESTINATION Modules/Assets) +ENDIF(POLYCODE_INSTALL_FRAMEWORK) \ No newline at end of file diff --git a/Assets/Default asset pack/default.pak b/Assets/Default asset pack/default.pak index 1fbdf8078..4f0e05f22 100644 Binary files a/Assets/Default asset pack/default.pak and b/Assets/Default asset pack/default.pak differ diff --git a/Assets/Default asset pack/default/ColSpecEmit.frag b/Assets/Default asset pack/default/ColSpecEmit.frag new file mode 100644 index 000000000..7785685bb --- /dev/null +++ b/Assets/Default asset pack/default/ColSpecEmit.frag @@ -0,0 +1,129 @@ +varying vec3 normal; +varying vec4 pos; +varying vec4 vertexColor; + +uniform sampler2D diffuse; +uniform sampler2D specular_map; +uniform sampler2D emit_map; + +uniform vec4 diffuse_color; +uniform vec4 specular_color; +uniform vec4 ambient_color; +uniform float shininess; + +float calculateAttenuation(in int i, in float dist) +{ + return(1.0 / (gl_LightSource[i].constantAttenuation + + gl_LightSource[i].linearAttenuation * dist + + gl_LightSource[i].quadraticAttenuation * dist * dist)); +} + +void pointLight(in int i, in vec3 normal, in vec4 pos, inout vec4 diffuse, inout vec4 specular) { + vec4 color = diffuse_color; + vec4 matspec = specular_color; + float shininess = shininess; + vec4 lightspec = gl_LightSource[i].specular; + vec4 lpos = gl_LightSource[i].position; + vec4 s = pos-lpos; + vec4 sn = -normalize(s); + + vec3 light = sn.xyz; + vec3 n = normalize(normal); + vec3 r = -reflect(light, n); + r = normalize(r); + vec3 v = -pos.xyz; + v = normalize(v); + + float nDotL = dot(n, sn.xyz); + if(nDotL > 0.0) { + float dist = length(s); + float attenuation = calculateAttenuation(i, dist); + + diffuse += color * max(0.0, nDotL) * gl_LightSource[i].diffuse * attenuation; + + if (shininess != 0.0) { + specular += lightspec * matspec * pow(max(0.0,dot(r, v)), shininess) * attenuation; + } + } +} + + +void spotLight(in int i, in vec3 normal, in vec4 pos, inout vec4 diffuse, inout vec4 specular) { + vec4 color = diffuse_color; + vec4 matspec = specular_color; + float shininess = shininess; + vec4 lightspec = gl_LightSource[i].specular; + vec4 lpos = gl_LightSource[i].position; + vec4 s = pos-lpos; + vec4 sn = -normalize(s); + + vec3 light = sn.xyz; + vec3 n = normalize(normal); + vec3 r = -reflect(light, n); + r = normalize(r); + vec3 v = -pos.xyz; + v = normalize(v); + + float cos_outer_cone_angle = (1.0-gl_LightSource[i].spotExponent) * gl_LightSource[i].spotCosCutoff; + float cos_cur_angle = dot(-normalize(gl_LightSource[i].spotDirection), sn.xyz); + float cos_inner_cone_angle = gl_LightSource[i].spotCosCutoff; + + float cos_inner_minus_outer_angle = cos_inner_cone_angle - cos_outer_cone_angle; + float spot = 0.0; + spot = clamp((cos_cur_angle - cos_outer_cone_angle) / cos_inner_minus_outer_angle, 0.0, 1.0); + + float nDotL = dot(n, sn.xyz); + if(nDotL > 0.0) { + float dist = length(s); + float attenuation = calculateAttenuation(i, dist); + diffuse += color * max(0.0, nDotL) * gl_LightSource[i].diffuse * attenuation * spot; + + if (shininess != 0.0) { + specular += lightspec * matspec * pow(max(0.0,dot(r, v)), shininess) * attenuation * spot; + } + } +} + +void doLights(in int numLights, in vec3 normal, in vec4 pos, inout vec4 diffuse, inout vec4 specular) { + for (int i = 0; i < numLights; i++) { + if (gl_LightSource[i].spotCutoff == 180.0) { + pointLight(i, normal, pos, diffuse, specular); + } else { + spotLight(i, normal, pos, diffuse, specular); + } + } +} + + +void main() +{ + vec4 diffuse_val = vec4(0.0); + vec4 specular_val = vec4(0.0); + doLights(6, normal, pos, diffuse_val, specular_val); + + specular_val.xyz *= texture2D(specular_map, gl_TexCoord[0].st).xyz * gl_FrontMaterial.specular.a; + + vec4 emitVal = texture2D(emit_map, gl_TexCoord[0].st); + vec4 texColor = texture2D(diffuse, gl_TexCoord[0].st); + + vec4 color = diffuse_val + ambient_color; + color = clamp((color*vertexColor*texColor) + specular_val, 0.0, 1.0); + + // fog + const float LOG2 = 1.442695; + float z = gl_FragCoord.z / gl_FragCoord.w; + float fogFactor = exp2( -gl_Fog.density * + gl_Fog.density * + z * + z * + LOG2 ); + + fogFactor = clamp(fogFactor, 0.0, 1.0); + + color = mix(color, texColor, emitVal); + + color = mix(gl_Fog.color, color, fogFactor ); + color.a = vertexColor.a * texColor.a * diffuse_color.a; + gl_FragColor = color; + +} diff --git a/Assets/Default asset pack/default/DefaultParticleShader.frag b/Assets/Default asset pack/default/DefaultParticleShader.frag index 647ac8b6e..3711d70ed 100644 --- a/Assets/Default asset pack/default/DefaultParticleShader.frag +++ b/Assets/Default asset pack/default/DefaultParticleShader.frag @@ -48,7 +48,7 @@ void spotLight(in int i, in vec3 normal, in vec4 pos, inout vec4 diffuse) { vec3 v = -pos.xyz; v = normalize(v); - float cos_outer_cone_angle = gl_LightSource[i].spotExponent; + float cos_outer_cone_angle = (1.0-gl_LightSource[i].spotExponent) * gl_LightSource[i].spotCosCutoff; float cos_cur_angle = dot(-normalize(gl_LightSource[i].spotDirection), sn.xyz); float cos_inner_cone_angle = gl_LightSource[i].spotCosCutoff; @@ -96,7 +96,7 @@ void main() fogFactor = clamp(fogFactor, 0.0, 1.0); color = mix(gl_Fog.color, color, fogFactor ); - color.a = diffuse_color.a * texColor.a; + color.a = diffuse_color.a * texColor.a * vertexColor.a; gl_FragColor = color; } diff --git a/Assets/Default asset pack/default/DefaultShader.frag b/Assets/Default asset pack/default/DefaultShader.frag index 40477edb3..f004c93c3 100644 --- a/Assets/Default asset pack/default/DefaultShader.frag +++ b/Assets/Default asset pack/default/DefaultShader.frag @@ -61,7 +61,7 @@ void spotLight(in int i, in vec3 normal, in vec4 pos, inout vec4 diffuse, inout vec3 v = -pos.xyz; v = normalize(v); - float cos_outer_cone_angle = gl_LightSource[i].spotExponent; + float cos_outer_cone_angle = (1.0-gl_LightSource[i].spotExponent) * gl_LightSource[i].spotCosCutoff; float cos_cur_angle = dot(-normalize(gl_LightSource[i].spotDirection), sn.xyz); float cos_inner_cone_angle = gl_LightSource[i].spotCosCutoff; @@ -115,7 +115,7 @@ void main() fogFactor = clamp(fogFactor, 0.0, 1.0); color = mix(gl_Fog.color, color, fogFactor ); - color.a = vertexColor.a * texColor.a; + color.a = vertexColor.a * texColor.a * diffuse_color.a; gl_FragColor = color; } diff --git a/Assets/Default asset pack/default/DefaultShaderNoTexture.frag b/Assets/Default asset pack/default/DefaultShaderNoTexture.frag index ae325aafa..b84ac62f4 100644 --- a/Assets/Default asset pack/default/DefaultShaderNoTexture.frag +++ b/Assets/Default asset pack/default/DefaultShaderNoTexture.frag @@ -60,7 +60,7 @@ void spotLight(in int i, in vec3 normal, in vec4 pos, inout vec4 diffuse, inout vec3 v = -pos.xyz; v = normalize(v); - float cos_outer_cone_angle = gl_LightSource[i].spotExponent; + float cos_outer_cone_angle = (1.0-gl_LightSource[i].spotExponent) * gl_LightSource[i].spotCosCutoff; float cos_cur_angle = dot(-normalize(gl_LightSource[i].spotDirection), sn.xyz); float cos_inner_cone_angle = gl_LightSource[i].spotCosCutoff; diff --git a/Assets/Default asset pack/default/DefaultShaderShadows.frag b/Assets/Default asset pack/default/DefaultShaderShadows.frag index 7afdf1642..cdd038bda 100644 --- a/Assets/Default asset pack/default/DefaultShaderShadows.frag +++ b/Assets/Default asset pack/default/DefaultShaderShadows.frag @@ -8,14 +8,13 @@ uniform sampler2D diffuse; uniform sampler2D shadowMap0; uniform sampler2D shadowMap1; -uniform mat4 shadowMatrix0; -uniform mat4 shadowMatrix1; - uniform vec4 diffuse_color; uniform vec4 specular_color; uniform vec4 ambient_color; uniform float shininess; +uniform float shadowAmount; + float calculateAttenuation(in int i, in float dist) { @@ -57,12 +56,14 @@ void pointLight(in int i, in vec3 normal, in vec4 pos, inout vec4 diffuse, inout void spotLight(in int i, in vec3 normal, in vec4 pos, inout vec4 diffuse, inout vec4 specular, sampler2D shadowMap, vec4 ShadowCoord) { vec4 shadowCoordinateWdivide = ShadowCoord / ShadowCoord.w; - shadowCoordinateWdivide.z -= 0.000005; + //shadowCoordinateWdivide.z -= 0.00005; float distanceFromLight = texture2D(shadowMap,shadowCoordinateWdivide.st).z; float shadow = 1.0; - if (shadowCoordinateWdivide.x > 0.01 && shadowCoordinateWdivide.y > 0.01 && shadowCoordinateWdivide.x < 0.99 && shadowCoordinateWdivide.y < 0.99) + if (shadowCoordinateWdivide.x > 0.001 && shadowCoordinateWdivide.y > 0.001 && shadowCoordinateWdivide.x < 0.999 && shadowCoordinateWdivide.y < 0.999) shadow = distanceFromLight < shadowCoordinateWdivide.z ? 0.0 : 1.0 ; + shadow = clamp(shadow+(1.0-shadowAmount), 0.0, 1.0); + vec4 color = diffuse_color; vec4 matspec = specular_color; float shininess = shininess; @@ -78,7 +79,7 @@ void spotLight(in int i, in vec3 normal, in vec4 pos, inout vec4 diffuse, inout vec3 v = -pos.xyz; v = normalize(v); - float cos_outer_cone_angle = gl_LightSource[i].spotExponent; + float cos_outer_cone_angle = (1.0-gl_LightSource[i].spotExponent) * gl_LightSource[i].spotCosCutoff; float cos_cur_angle = dot(-normalize(gl_LightSource[i].spotDirection), sn.xyz); float cos_inner_cone_angle = gl_LightSource[i].spotCosCutoff; diff --git a/Assets/Default asset pack/default/DefaultShaderVertex.vert b/Assets/Default asset pack/default/DefaultShaderVertex.vert index 6068652e7..025b3f5fd 100644 --- a/Assets/Default asset pack/default/DefaultShaderVertex.vert +++ b/Assets/Default asset pack/default/DefaultShaderVertex.vert @@ -56,7 +56,7 @@ void spotLight(in int i, in vec3 normal, in vec4 pos, inout vec4 diffuse, inout vec3 v = -pos.xyz; v = normalize(v); - float cos_outer_cone_angle = gl_LightSource[i].spotExponent; + float cos_outer_cone_angle = (1.0-gl_LightSource[i].spotExponent) * gl_LightSource[i].spotCosCutoff; float cos_cur_angle = dot(-normalize(gl_LightSource[i].spotDirection), sn.xyz); float cos_inner_cone_angle = gl_LightSource[i].spotCosCutoff; diff --git a/Assets/Default asset pack/default/GPUSkinning.vert b/Assets/Default asset pack/default/GPUSkinning.vert new file mode 100644 index 000000000..f7e7ccbc7 --- /dev/null +++ b/Assets/Default asset pack/default/GPUSkinning.vert @@ -0,0 +1,64 @@ +#define MAX_JOINT_COUNT 64 + +uniform mat4 skeletonMatrix[MAX_JOINT_COUNT]; + +attribute vec4 vBoneIndices; +attribute vec4 vBoneWeights; + +varying vec3 normal; +varying vec4 pos; +varying vec4 rawpos; +varying vec4 vertexColor; + + +mat3 m3( mat4 m ) +{ + mat3 result; + + result[0][0] = m[0][0]; + result[0][1] = m[0][1]; + result[0][2] = m[0][2]; + + + result[1][0] = m[1][0]; + result[1][1] = m[1][1]; + result[1][2] = m[1][2]; + + result[2][0] = m[2][0]; + result[2][1] = m[2][1]; + result[2][2] = m[2][2]; + + return result; +} + + +void jointInfluence(in mat4 joint_matrix, in float weight, in vec4 position, inout vec4 outPosition, in vec3 normal, inout vec3 outNormal) +{ + outPosition += weight * (joint_matrix * position); + + mat3 normalMatrix = m3(joint_matrix); + outNormal += weight * (normalMatrix * normal); +} + +void main() { + + vec4 inVert = gl_Vertex; + vec4 outVert = vec4(0.0, 0.0, 0.0, 0.0); + + vec3 inNormal = gl_Normal; + vec3 outNormal = vec3(0.0, 0.0, 0.0); + + jointInfluence(skeletonMatrix[int(vBoneIndices.x)], vBoneWeights.x, inVert, outVert, inNormal, outNormal); + jointInfluence(skeletonMatrix[int(vBoneIndices.y)], vBoneWeights.y, inVert, outVert, inNormal, outNormal); + jointInfluence(skeletonMatrix[int(vBoneIndices.z)], vBoneWeights.z, inVert, outVert, inNormal, outNormal); + jointInfluence(skeletonMatrix[int(vBoneIndices.w)], vBoneWeights.w, inVert, outVert, inNormal, outNormal); + + outVert.w = 1.0; + + normal = gl_NormalMatrix * normalize(outNormal); + gl_Position = gl_ProjectionMatrix * gl_ModelViewMatrix * outVert; + pos = gl_ModelViewMatrix * outVert; + rawpos = outVert; + vertexColor = gl_Color; + gl_TexCoord[0] = gl_TextureMatrix[0] * gl_MultiTexCoord0; +} \ No newline at end of file diff --git a/Assets/Default asset pack/default/LightCube.frag b/Assets/Default asset pack/default/LightCube.frag index 86099931c..bbd21e0d0 100644 --- a/Assets/Default asset pack/default/LightCube.frag +++ b/Assets/Default asset pack/default/LightCube.frag @@ -2,10 +2,29 @@ uniform samplerCube lightCube; varying vec4 vertexColor; varying vec3 normal; uniform vec4 ambient_color; +varying vec3 worldNormal; + +uniform float lightFactor; + +vec3 hash3( float n ) +{ + return fract(sin(vec3(n,n+1.0,n+2.0))*vec3(43758.5453123,22578.1459123,19642.3490423)); +} void main() { - vec4 texColor = textureCube(lightCube, normal); + vec3 col = vec3(0.0); + for( int i=0; i<32; i++ ) + { + vec3 rr = normalize(-1.0 + 2.0*hash3(float(i)*123.5463)); + rr = normalize( worldNormal + 7.0*rr ); + rr = rr * sign(dot(worldNormal,rr)); + col += pow( textureCube( lightCube, rr ).xyz, vec3(2.2) ) * dot(rr,worldNormal); + } + + col = col * lightFactor; + + vec4 texColor = vec4(col, 1.0); vec4 color = vec4(1.0,1.0,1.0,1.0) + ambient_color; color = clamp((color*vertexColor*texColor), 0.0, 1.0); diff --git a/Assets/Default asset pack/default/LightCube.vert b/Assets/Default asset pack/default/LightCube.vert new file mode 100644 index 000000000..ba735ebbb --- /dev/null +++ b/Assets/Default asset pack/default/LightCube.vert @@ -0,0 +1,28 @@ +varying vec3 normal; +varying vec3 worldNormal; +varying vec4 pos; +varying vec4 rawpos; +varying vec4 vertexColor; + +uniform mat4 modelMatrix; + +mat3 mat3_emu(mat4 m4) { + return mat3( + m4[0][0], m4[0][1], m4[0][2], + m4[1][0], m4[1][1], m4[1][2], + m4[2][0], m4[2][1], m4[2][2]); +} + +void main() { + normal = gl_NormalMatrix * gl_Normal; + + mat3 rotN = mat3_emu(modelMatrix); + worldNormal = rotN * gl_Normal; + worldNormal = normalize(worldNormal); + + gl_Position = ftransform(); + pos = gl_ModelViewMatrix * gl_Vertex; + rawpos = gl_Vertex; + vertexColor = gl_Color; + gl_TexCoord[0] = gl_TextureMatrix[0] * gl_MultiTexCoord0; +} \ No newline at end of file diff --git a/Assets/Default asset pack/default/NorColSpec.frag b/Assets/Default asset pack/default/NorColSpec.frag index 391b2ff75..e5728e9fb 100644 --- a/Assets/Default asset pack/default/NorColSpec.frag +++ b/Assets/Default asset pack/default/NorColSpec.frag @@ -7,6 +7,7 @@ varying vec4 vertexColor; uniform sampler2D diffuse; uniform sampler2D normal_map; uniform sampler2D specular_map; +uniform sampler2D emit_map; uniform vec4 diffuse_color; uniform vec4 specular_color; @@ -89,7 +90,7 @@ void spotLight(in int i, in vec3 bump, in vec3 normal, in vec3 tangent, in vec3 lDir = normalize(lDir); - float cos_outer_cone_angle = gl_LightSource[i].spotExponent; + float cos_outer_cone_angle = (1.0-gl_LightSource[i].spotExponent) * gl_LightSource[i].spotCosCutoff; float cos_cur_angle = dot(-lDir, lVec); float cos_inner_cone_angle = gl_LightSource[i].spotCosCutoff; diff --git a/Assets/Default asset pack/default/SkyBox.frag b/Assets/Default asset pack/default/SkyBox.frag new file mode 100644 index 000000000..ddfff7695 --- /dev/null +++ b/Assets/Default asset pack/default/SkyBox.frag @@ -0,0 +1,28 @@ +uniform samplerCube lightCube; +varying vec4 vertexColor; +varying vec3 normal; +uniform vec4 ambient_color; +varying vec3 worldNormal; + +void main() +{ + vec4 texColor = textureCube(lightCube, worldNormal * -1.0); + + vec4 color = vec4(1.0,1.0,1.0,1.0) + ambient_color; + color = clamp((color*vertexColor*texColor), 0.0, 1.0); + + // fog + const float LOG2 = 1.442695; + float z = gl_FragCoord.z / gl_FragCoord.w; + float fogFactor = exp2( -gl_Fog.density * + gl_Fog.density * + z * + z * + LOG2 ); + + fogFactor = clamp(fogFactor, 0.0, 1.0); + color = mix(gl_Fog.color, color, fogFactor ); + + color.a = vertexColor.a * texColor.a; + gl_FragColor = color; +} \ No newline at end of file diff --git a/Assets/Default asset pack/default/UnlitUntextured.frag b/Assets/Default asset pack/default/UnlitUntextured.frag new file mode 100644 index 000000000..dc41b4aaf --- /dev/null +++ b/Assets/Default asset pack/default/UnlitUntextured.frag @@ -0,0 +1,7 @@ + +varying vec4 vertexColor; + +void main() +{ + gl_FragColor = vertexColor; +} \ No newline at end of file diff --git a/Assets/Default asset pack/default/default.mat b/Assets/Default asset pack/default/default.mat index 7c9b4e7c3..695146a76 100644 --- a/Assets/Default asset pack/default/default.mat +++ b/Assets/Default asset pack/default/default.mat @@ -1,39 +1,51 @@ - + - - + + + + + + - + - + - + - + - + - - + + + + + + + + + + - + @@ -43,6 +55,18 @@ + + + + + + + + + + + + @@ -52,6 +76,84 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Assets/Default asset pack/hdr.pak b/Assets/Default asset pack/hdr.pak index 3affefbbf..357f849c6 100644 Binary files a/Assets/Default asset pack/hdr.pak and b/Assets/Default asset pack/hdr.pak differ diff --git a/Assets/Default asset pack/hdr/DefaultShaderNoTextureHDR.frag b/Assets/Default asset pack/hdr/DefaultShaderNoTextureHDR.frag index eed7a6568..234b2532d 100644 --- a/Assets/Default asset pack/hdr/DefaultShaderNoTextureHDR.frag +++ b/Assets/Default asset pack/hdr/DefaultShaderNoTextureHDR.frag @@ -60,7 +60,7 @@ void spotLight(in int i, in vec3 normal, in vec4 pos, inout vec4 diffuse, inout vec3 v = -pos.xyz; v = normalize(v); - float cos_outer_cone_angle = gl_LightSource[i].spotExponent; + float cos_outer_cone_angle = (1.0-gl_LightSource[i].spotExponent) * gl_LightSource[i].spotCosCutoff; float cos_cur_angle = dot(-normalize(gl_LightSource[i].spotDirection), sn.xyz); float cos_inner_cone_angle = gl_LightSource[i].spotCosCutoff; diff --git a/Assets/Default asset pack/hdr/NorColSpecHDR.frag b/Assets/Default asset pack/hdr/NorColSpecHDR.frag index f34fef7f9..d475653ec 100644 --- a/Assets/Default asset pack/hdr/NorColSpecHDR.frag +++ b/Assets/Default asset pack/hdr/NorColSpecHDR.frag @@ -89,7 +89,7 @@ void spotLight(in int i, in vec3 bump, in vec3 normal, in vec3 tangent, in vec3 lDir = normalize(lDir); - float cos_outer_cone_angle = gl_LightSource[i].spotExponent; + float cos_outer_cone_angle = (1.0-gl_LightSource[i].spotExponent) * gl_LightSource[i].spotCosCutoff; float cos_cur_angle = dot(-lDir, lVec); float cos_inner_cone_angle = gl_LightSource[i].spotCosCutoff; diff --git a/Assets/Default asset pack/hdr/fxaa.frag b/Assets/Default asset pack/hdr/fxaa.frag new file mode 100644 index 000000000..92ea12971 --- /dev/null +++ b/Assets/Default asset pack/hdr/fxaa.frag @@ -0,0 +1,153 @@ +// FXAA shader, GLSL code adapted from: + +// http://horde3d.org/wiki/index.php5?title=Shading_Technique_-_FXAA + +// Whitepaper describing the technique: + +// http://developer.download.nvidia.com/assets/gamedev/files/sdk/11/FXAA_WhitePaper.pdf + + + +uniform sampler2D textureSampler; + + + +// The inverse of the texture dimensions along X and Y + + +vec3 rgb2hsv(vec3 c) + +{ + + vec4 K = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0); + + vec4 p = mix(vec4(c.bg, K.wz), vec4(c.gb, K.xy), step(c.b, c.g)); + + vec4 q = mix(vec4(p.xyw, c.r), vec4(c.r, p.yzx), step(p.x, c.r)); + + + + float d = q.x - min(q.w, q.y); + + float e = 1.0e-10; + + return vec3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x); + +} + + + +vec3 hsv2rgb(vec3 c) + +{ + + vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0); + + vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www); + + return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y); + +} + + + +void main() { + + // The parameters are hardcoded for now, but could be + + // made into uniforms to control fromt he program. + + float FXAA_SPAN_MAX = 8.0; + + float FXAA_REDUCE_MUL = 1.0/8.0; + + float FXAA_REDUCE_MIN = (1.0/128.0); + + vec2 texcoordOffset = vec2(0.0005, 0.0005); + + + vec3 rgbNW = texture2D(textureSampler, gl_TexCoord[0].st + (vec2(-1.0, -1.0) * texcoordOffset)).xyz; + + vec3 rgbNE = texture2D(textureSampler, gl_TexCoord[0].st + (vec2(+1.0, -1.0) * texcoordOffset)).xyz; + + vec3 rgbSW = texture2D(textureSampler, gl_TexCoord[0].st + (vec2(-1.0, +1.0) * texcoordOffset)).xyz; + + vec3 rgbSE = texture2D(textureSampler, gl_TexCoord[0].st + (vec2(+1.0, +1.0) * texcoordOffset)).xyz; + + vec3 rgbM = texture2D(textureSampler, gl_TexCoord[0].st).xyz; + + + vec3 luma = vec3(0.299, 0.587, 0.114); + + float lumaNW = dot(rgbNW, luma); + + float lumaNE = dot(rgbNE, luma); + + float lumaSW = dot(rgbSW, luma); + + float lumaSE = dot(rgbSE, luma); + + float lumaM = dot( rgbM, luma); + + + float lumaMin = min(lumaM, min(min(lumaNW, lumaNE), min(lumaSW, lumaSE))); + + float lumaMax = max(lumaM, max(max(lumaNW, lumaNE), max(lumaSW, lumaSE))); + + + vec2 dir; + + dir.x = -((lumaNW + lumaNE) - (lumaSW + lumaSE)); + + dir.y = ((lumaNW + lumaSW) - (lumaNE + lumaSE)); + + + float dirReduce = max((lumaNW + lumaNE + lumaSW + lumaSE) * (0.25 * FXAA_REDUCE_MUL), FXAA_REDUCE_MIN); + + + + float rcpDirMin = 1.0/(min(abs(dir.x), abs(dir.y)) + dirReduce); + + + dir = min(vec2(FXAA_SPAN_MAX, FXAA_SPAN_MAX), + + max(vec2(-FXAA_SPAN_MAX, -FXAA_SPAN_MAX), dir * rcpDirMin)) * texcoordOffset; + + + vec3 rgbA = (1.0/2.0) * ( + + texture2D(textureSampler, gl_TexCoord[0].st + dir * (1.0/3.0 - 0.5)).xyz + + + texture2D(textureSampler, gl_TexCoord[0].st + dir * (2.0/3.0 - 0.5)).xyz); + + vec3 rgbB = rgbA * (1.0/2.0) + (1.0/4.0) * ( + + texture2D(textureSampler, gl_TexCoord[0].st + dir * (0.0/3.0 - 0.5)).xyz + + + texture2D(textureSampler, gl_TexCoord[0].st + dir * (3.0/3.0 - 0.5)).xyz); + + float lumaB = dot(rgbB, luma); + + + + if((lumaB < lumaMin) || (lumaB > lumaMax)){ + + gl_FragColor.xyz=rgbA; + + } else { + + gl_FragColor.xyz=rgbB; + + } + + + + vec3 hsv = rgb2hsv(gl_FragColor.xyz); + + hsv.y += 0.1 * (1.0 - hsv.y); + + gl_FragColor = vec4(hsv2rgb(hsv), texture2D(textureSampler, gl_TexCoord[0].st).a); + + + +} \ No newline at end of file diff --git a/Assets/Default asset pack/hdr/hdr.mat b/Assets/Default asset pack/hdr/hdr.mat index 61f1aac8c..c87b4f092 100644 --- a/Assets/Default asset pack/hdr/hdr.mat +++ b/Assets/Default asset pack/hdr/hdr.mat @@ -1,11 +1,11 @@ - + - + @@ -28,31 +28,71 @@ - + + + + + - + - + + + + + - + - - - - - - + + + + + + + + + + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -68,10 +108,9 @@ - - + diff --git a/Assets/Icons/Icon_base.psd b/Assets/Icons/Icon_base.psd index f737d07ff..906e72960 100644 Binary files a/Assets/Icons/Icon_base.psd and b/Assets/Icons/Icon_base.psd differ diff --git a/Assets/Icons/icons.ai b/Assets/Icons/icons.ai new file mode 100644 index 000000000..c55ea1b3f --- /dev/null +++ b/Assets/Icons/icons.ai @@ -0,0 +1,1662 @@ +%PDF-1.5 %âãÏÓ +1 0 obj <>/OCGs[5 0 R 56 0 R 93 0 R 130 0 R 160 0 R 196 0 R 223 0 R 250 0 R 269 0 R 288 0 R 307 0 R 326 0 R 345 0 R 364 0 R]>>/Pages 3 0 R/Type/Catalog>> endobj 2 0 obj <>stream + + + + + application/pdf + + + Print + + + 2014-03-04T18:26:03-05:00 + 2014-03-04T18:26:03-05:00 + 2013-08-10T21:14:26-04:00 + Adobe Illustrator CS5 + + + + 256 + 80 + JPEG + /9j/4AAQSkZJRgABAgEASABIAAD/7QAsUGhvdG9zaG9wIDMuMAA4QklNA+0AAAAAABAASAAAAAEA AQBIAAAAAQAB/+4ADkFkb2JlAGTAAAAAAf/bAIQABgQEBAUEBgUFBgkGBQYJCwgGBggLDAoKCwoK DBAMDAwMDAwQDA4PEA8ODBMTFBQTExwbGxscHx8fHx8fHx8fHwEHBwcNDA0YEBAYGhURFRofHx8f Hx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8f/8AAEQgAUAEAAwER AAIRAQMRAf/EAaIAAAAHAQEBAQEAAAAAAAAAAAQFAwIGAQAHCAkKCwEAAgIDAQEBAQEAAAAAAAAA AQACAwQFBgcICQoLEAACAQMDAgQCBgcDBAIGAnMBAgMRBAAFIRIxQVEGE2EicYEUMpGhBxWxQiPB UtHhMxZi8CRygvElQzRTkqKyY3PCNUQnk6OzNhdUZHTD0uIIJoMJChgZhJRFRqS0VtNVKBry4/PE 1OT0ZXWFlaW1xdXl9WZ2hpamtsbW5vY3R1dnd4eXp7fH1+f3OEhYaHiImKi4yNjo+Ck5SVlpeYmZ qbnJ2en5KjpKWmp6ipqqusra6voRAAICAQIDBQUEBQYECAMDbQEAAhEDBCESMUEFURNhIgZxgZEy obHwFMHR4SNCFVJicvEzJDRDghaSUyWiY7LCB3PSNeJEgxdUkwgJChgZJjZFGidkdFU38qOzwygp 0+PzhJSktMTU5PRldYWVpbXF1eX1RlZmdoaWprbG1ub2R1dnd4eXp7fH1+f3OEhYaHiImKi4yNjo +DlJWWl5iZmpucnZ6fkqOkpaanqKmqq6ytrq+v/aAAwDAQACEQMRAD8A9U4q7FXnPnf89fJXliSS 0jkbVtUjJVrS0IKIw7STH4F32IHIjwwWkB5JrP8Azk553unYabaWemwn7HwtPKPm7kIf+AxtlwpF /wAr+/Nf1Of6aXjWvp/VbTj8v7rl+OC14Qnmjf8AOTfni0kUana2epQ/t/A0Ep+ToSg/4A4bXhet +Sfz38leZpI7SWRtI1OSirbXZUI7HbjHMPhbfYBuJPhjbEh6PhQ7FXYq7FWA/nX/AMocn/MXF/xF 8wO0v7v4u47D/v8A/NP6GLad+Vmj3Wn21y93cK88SSMBwoC6hjT4ffKIdnQMQbO7k5e28kZmPDHY nvRH/Ko9F/5bLn/kn/zTk/5Nh3lr/l7L/Nj9qT655S/L7QqfpbXmtHIqIneMyEeIjVS5H0Y/ybDv K/y7l/mx+1jq6h+TZl4f4huwO0ht5eJ/5I1/DH+TYd5T/LmX+bH7f1sn0XyT5F1uMyaTrjXoUVdY niLqP8pOPJfpGP8AJsO8o/l3L/Nj9qZ/8qj0X/lsuf8Akn/zTj/JsO8r/L2X+bH7Uv8AMH5aaVpu jXd9FdTvJbxl1VuHEmveijKs2gjCBkCdnI0vbGTJkjAgUS+efMf/AB2rr/WH/ERkMP0Bt1X94WYf kD/5NvQf+jv/AKgpsy8H1h1+r/uz+Or7FzYOlS7zH/yj2qf8wk//ACabK830H3Fv0397H+sPveI+ SvI2n6/pUt5c3E0TxztCFj40oERq/ED/AD5ptJpI5I2T1em7R7TngyCMQDtf3sg/5VHov/LZc/8A JP8A5pzK/k2HeXX/AMvZf5sftd/yqPRf+Wy5/wCSf/NOP8mw7yv8vZf5sftd/wAqj0X/AJbLn/kn /wA04/ybDvK/y9l/mx+13/Ko9F/5bLn/AJJ/804/ybDvK/y9l/mx+13/ACqPRf8Alsuf+Sf/ADTj /JsO8r/L2X+bH7Xf8qj0X/lsuf8Akn/zTj/JsO8r/L2X+bH7XhP5kWy2uoR2yEskEtxGrHqQjKor 92YeCNSkHZ6ufFCEu8fqYbmS4L9B2YKCzEBQKknoBm2edfNP5yfnldarPNoHle4aHSUrHd38ZKvc noVjYbrF7/tfLqCWYDxTAyTPy3pKavrVtpzuY1n5gutCRxRmrQ/6uYut1Bw4jMdP1t+lw+JkEe9G +YvJWtaIWklj9ezHS6iBKgf5Y6p9O3vlGj7TxZ9gal3H9He26nQ5MW53j3sfzYuG7FXs/wCT3553 ujz2+geZp2uNGciK2vpDWS1rsodj9qIe+6jpsKYQWJD6ZR1dQ6EMjAFWBqCD0IOFg3irsVYD+df/ AChyf8xcX/EXzA7S/u/i7jsP+/8A80/oRmh/8cXT/wDmGh/5NjMnD9A9wddqf72X9Y/e8p/Nv837 jTriby95dlCXkdUv9QXcxN3ii/yx+03boN+ljWA8Lkkubu4LyM9xczNuzEu7sx8TUknASALLJk/m P8u9Y0rlPbA31kN+cY/eKP8ALTf7x+GanR9sYsu0vTL7Pm7HU9mzx7j1RY1ZX15Y3Md1ZzyW1zEa xzRMUdT7MKHNu659B/lJ+bLeYSNE1tlXWUWtvcABVuFUVaoGwkA322IxYEM386/8orqf/GE/rGY+ r/upe5zOzv7+HvfJXmP/AI7V1/rD/iIzXYfoDvNV/eFmH5A/+Tb0H/o7/wCoKbMvB9Ydfq/7s/jq +xc2DpUu8x/8o9qn/MJP/wAmmyvN9B9xb9N/ex/rD73mv5S/8o5c/wDMY/8AyajzB7N/uz7/ANTt O3f74f1f0lm2bB0rsVdirsVdirsVfKn5qf8AHaf/AJibv/k4M0mL65e/9b1uf+6x/wBX9AYRl7hv qL/nI38wZNH0aLyxp8pS/wBWQveOpoyWlSvH/nqwK/6obxzal5+IfMeBmm/lzynr3mK6+r6VatNQ /vZj8MUdf53Ow+XXwzD1mvw6aN5DXl1PwZwxmXJ7d5O/J/RvL6i/1K4N1qSKx9YExwwgqQxQd9if if7hnDdo+0OXUeiA4Yd3Mn3/ALPm7DT4vCPF/EEvvPOvk5dXfTIdRWZPs/WCv7gsTTh6n2Tt+19n MjH2bqTj8Qwry6++vwXaYu0scjwyNH7Ek8xflrpeoq1zpZWyuW+IIu8D136D7P8AsdvbM3R9tZMf pyeqP2/j3tep7LhPeHpP2PO7zyxq9letaXcPouu/MkFSp6FSOvTOkhr8U48UTbi6TsLUZpUBUR/E eX7UrkThIyVrxJFfkaZlRNi3V58fhzlDnwkj5Ppf/nHD8wJdV0mXytqEhe80pBJYux3a0qF4f88m IA/ySB2yYceQe0YWLsVYD+df/KHJ/wAxcX/EXzA7S/u/i7jsP+//AM0/oSzX/MJ8vflw2rIR68Fj CtvXf99Iqxx7d6MwJzJw/QPcHX6j+9l/WP3vnbyd5H1rzjfy/V5UjijbleXczAspc1rwrzYn7vfN f2l2rj0kbkCZHkP28k4sRnyejtc/lx+WcfCBP0v5jC7tszqfdqFIR7CrfPOaGPW9pn1fu8P2frl9 zkCWPHy3LCZfzc8xz6u97PFA1s+xskXgoA8H3bl4k1+WbuPs/hjj4QTxfzv2dzZh7SyQPfHuVdZu vJ+uWyahZQCHU/UAuYCOJKkNViB8DfEBuPpyvBDU6c8EjcK2P43D0XZODS6vLZjuBdfjYsMtb650 /Uor20cxXNrKJYHH7LI1V/VnQR5B5LUADJID+cfvfU+q6vFrH5cyarEKJe2STcR+yXALL/sTtlOr /upe5s7O/wAYh73y15j/AOO1df6w/wCIjNdh+gO81X94WYfkD/5NvQf+jv8A6gpsy8H1h1+r/uz+ Or7FzYOlS7zH/wAo9qn/ADCT/wDJpsrzfQfcW/Tf3sf6w+95r+Uv/KOXP/MY/wDyajzB7N/uz7/1 O07d/vh/V/SWbZsHSuxV2KuxV2KuxV8qfmp/x2n/AOYm7/5ODNJi+uXv/W9bn/usf9X9AYRl7hsz /NbzA+u/mDrV8W5RJcNbW/gIrf8AdLT/AFuHL6c2joQn/wCVv5b+X/MSfXtR1FJ/SNX0mFisgAPW YmjcT/kf8FnL9uds5tOeCEav+I8vh+35OVgwiW5L0/zB5v8AK/kjSBHBbjhEfSgsrRVVfUIJozfZ X7JLHc5zGk7O1GtyWTz5yk2nVY4ngB3HQPFPMnn7zP5yvorGWYWtncSrHDZRkrFV2AUyEfE9D4/Q M7ns7sbDpdwOKf8AOP6O5xsmaUvclXmjyb5k8r3v1TWrJ7ZjX0pftRSAd45B8Lfr8c2zSCv8vedN a0QhIZPWtK/Fay1K0/yT1X6M1+r7NxZ9yKl3j8bubptdkxct49yf6x5httemhvYI2i4xCKSN6Gjq zE0I6j4uuazDpJacGBN72+h9hagZcHENvUfuDB7n/eiX/Xb9edBj+ke586139/P+vL72S/lf5gfQ PP2i6gG4xfWVguPD0Z/3Ulfkr1+YybiF9r5JrdirAfzr/wCUOT/mLi/4i+YHaX938Xcdh/3/APmn 9DA/zhMv/KprHh9ktZ+r/q+mf+NqZk4foHuDgaj+9l/WP3vDvKs88PmGxMMjRlpVRihKkqxoymnY jqMq1kBLFKxezi5yRCVdyP8AzB/5SN/+MUf6sq7O/uvi06H+7Y1mc5iZ6F/vTJ/qfxGYes+ke96r 2S/v5/1P0hL5gRM4PXkf15lR5B5vU/3kv6x+99F+UTKfyLg9X7X1ecD/AFRcvx/4WmU6v+6l7mzs /wDxiHvfPvmP/jtXX+sP+IjNdh+gO71X94WYfkD/AOTb0H/o7/6gpsy8H1h1+r/uz+Or7FzYOlS7 zH/yj2qf8wk//JpsrzfQfcW/Tf3sf6w+95r+Uv8Ayjlz/wAxj/8AJqPMHs3+7Pv/AFO07d/vh/V/ SWbZsHSuxV2KuxV2KuxV8qfmp/x2n/5ibv8A5ODNJi+uXv8A1vW5/wC6x/1f0BhGXuGiJHeR2kc1 dyWY+JO5zaOiTDy3PPD5g054ZGic3ESlkJU8WcKwqOxBocx9VASxSBF7FrykiEiO4s4/Mr/jhQf8 xSf8m5M1XZn94f6v6Q6vs/6z7v1MF8v/APHe03/mKg/5OLm9du+3dV0nTNWspLHU7WK8tJftwTKH U+BoehHY5NreC/mP/wA49Q2Ftcax5Zu0jtIVMk9heyqgRR/vu4kIWntIf9lkSGQk8q0T/eM/65/U M1er+v4PpHst/iv+ef0JNc/70S/67frzYY/pHueE139/P+vL71NWZWDKSrKaqw2IIybivvy1lM1t DKwAaRFcgdKsK5JqVMVYD+df/KHJ/wAxcX/EXzA7S/u/i7jsP+//AM0/oSnzJ5ffzB+Wj6XGK3Et jC9sPGWJVkjH+yZaZk4foHuDr9R/ey/rH73zL5f/AHPmCy9b936c6+pz+HjxbetelMhqReOXucbU C4H3Mk8wQaNruoSyWl0puEAXmu4PEeBpUe4zN7I0UZ4KJ4cll1kMuTANx6WOjy3qX1j0mVVQf7tr Vae3fLpaHIJUfm7fSzjn+kpgp0nRVI5Ge7I3A6/0Ufjlhjjxc95fj5OzjOOAEA7nmlMj3ms6lFDB AGubh1hghjHxMznior3Ncw8uQzNuDlycZt9Q6lpCaN+Wz6UhDCyskhLj9plADN/smqcw9X/dS9zd 2d/jEPe+W/Mf/Hauv9Yf8RGa7D9Ad5qv7wsw/IH/AMm3oP8A0d/9QU2ZeD6w6/V/3Z/HV9i5sHSp d5j/AOUe1T/mEn/5NNleb6D7i36b+9j/AFh97xv8v/OflbRNGmtNW1KGzuHuWlSKQkEoURQ2wPdT mD2b/dn3/qdp26P3w/q/pLJ/+Vo/l9/1fLb72/pmwdNTv+Vo/l9/1fLb72/pitO/5Wj+X3/V8tvv b+mK07/laP5ff9Xy2+9v6YrTv+Vo/l9/1fLb72/pitO/5Wj+X3/V8tvvb+mK0+evzNmin1QTwsHi lnuXjcdCrOpBHzGaTF9cvf8Aresz/wB1j/q/oDC8vcNM9b059M1m/wBOkBD2VxLbsD1rE5T+GbR0 K7QP+O7p3/MVD/ycXKdR/dy/qn7mvP8ARL3Fnn5kKx0KEgEhblC1Ow9NxU5qeywTkP8AV/SHV9nn 1/Bgnl//AI72m/8AMVB/ycXN47h9Qeffzy8q+WDJZ2jDV9XSqm2gYelGw/37LuBT+VanxpkrYCL5 285fmL5q83XHPVrs/VlNYbGGqW6fJK/Ef8pqnI2zAU9AtpXs60KqWJDHbag6ZTLQ5M07G0e97bsT tLHp9JR3lxHb5IDV9HubV3nqJIGavMbEVPQjM3JpTjHeHltZAmcp9JEn5oXSrCXUdUs9PhFZbyeO 3jA/mlcIPxOUOE+9lVVUKoCqooqjYADsMk1t4qwH86/+UOT/AJi4v+IvmB2l/d/F3HYf9/8A5p/Q jND/AOOLp/8AzDQ/8mxmTh+ge4Ou1P8Aey/rH73kX5u/lBdXV1N5i8uw+rLKTJqGnoPiZupliHct +0vWu4yxrBeIETQSkENFNGxBBqrKwNCPEEYQSNwkgHmm9v5muhbSQXA9QsjLHKNmDEbV8d82EO0Z cJjLfbm4UtEBMSgaopVb29xdXCQW8bz3ErBY4kBZ2Y9AANyc1znPf/yh/KaXQ5F17XUA1UqRaWmx 9AMKF3I/3YRtT9ke/RYEs986/wDKK6n/AMYT+sZj6v8Aupe5zOzv7+HvfJXmP/jtXX+sP+IjNdh+ gO81X94WYfkD/wCTb0H/AKO/+oKbMvB9Ydfq/wC7P46vsXNg6VLvMf8Ayj2qf8wk/wDyabK830H3 Fv0397H+sPvfFnnD/jpxf8YV/wCJtmD2b/dn3/qdt25/fD+r+kpFmwdO7FXYq7FXYq7FWQecP959 N/1H/UmaWH95P3/reozf3OP+r+gMZy5xHrn/ADkV5Rk0fzs2rxJSx1xfWVh0FwgCzL9Oz/7LNqXQ RLzjQP8Aju6d/wAxUP8AycXKNR/dy/qn7mGf6Je4vSPMWs6UnCxkuY/rBerR1rQUI+I9B16HK/Zs cGYyltExr7Q6EYJkcQGzFdR8s209ZLUiCU78f2D9Hb6M6nUdmxlvD0n7HIwa+Udpbj7UgOi6mLj0 DA3L+b9injy6ZqJaXIJUQ7nBIZfo3TzT/Ltrb0kuSJpBvQ/YH0d/pzLxaWMdzuXZ49KI7ndvUPMV rb1jtwJpRtt9gfT3+jHLqox2G5XJqRHYbscu766u35zuW/lXoo+QzXzySkd3BnkMju9K/wCcePKM mteeY9TkSthoa/WJGI2M7ArAvz5Vf/Y5ENUi+r8LB2KsB/Ov/lDk/wCYuL/iL5gdpf3fxdx2H/f/ AOaf0IzQ/wDji6f/AMw0P/JsZk4foHuDrtT/AHsv6x+9G5Y0pDr/AJF8o6+xfVdMhuJiKG4AMcv0 yRlXP0nFbY4PyJ/LsS8zazlf99meTj+B5fjim2U6F5P8saApGkabDaMRQyqvKUjwMj8nP34otOMV STzr/wAorqf/ABhP6xmPq/7qXuc3s7+/h73yV5j/AOO1df6w/wCIjNdh+gO81X94WYfkD/5NvQf+ jv8A6gpsy8H1h1+r/uz+Or7FzYOlS7zH/wAo9qn/ADCT/wDJpsrzfQfcW/Tf3sf6w+98WecP+OnF /wAYV/4m2YPZv92ff+p23bn98P6v6SkWbB07sVdirsVdirsVZB5w/wB59N/1H/UmaWH95P3/AK3q M39zj/q/oDGcucR9w/mL5HsfOflm40i4IiuP72xuSKmKdQeLf6prxYeB8c2zzwL401nRtW0HV59N 1GF7XULN+LodiCN1ZT3B6qw65EhnzQHXc4pTHTtdvbKig+rAP91N2/1T2zM0+tnj25x7nFz6SGTf kWT2GsWN8AEbjL3ibZvo8c3eHVY8orr3Oqliy4JcQ+YY95g1C6a9ltQ9IEIARdq7A/F45pdbkImY jkHfYtVPJjBkUnzCSjtE0TVNb1S30vS7drm+uW4RRJ+JJ6BQNyTsBih9l/lv5FsvJfliDSYSJbpj 61/cgf3s7ABiK/srTivt71yTAllGKHYqwH86/wDlDk/5i4v+IvmB2l/d/F3HYf8Af/5p/QjND/44 un/8w0P/ACbGZOH6B7g67U/3sv6x+9G5Y0uxV2KuxV2KpJ51/wCUV1P/AIwn9YzH1f8AdS9zm9nf 38Pe+SvMf/Hauv8AWH/ERmuw/QHear+8LMPyB/8AJt6D/wBHf/UFNmXg+sOv1f8Adn8dX2LmwdKl 3mP/AJR7VP8AmEn/AOTTZXm+g+4t+m/vY/1h974s84f8dOL/AIwr/wATbMHs3+7Pv/U7btz++H9X 9JSLNg6d2KuxV2KuxV2Ksg84f7z6b/qP+pM0sP7yfv8A1vUZv7nH/V/QGM5c4j9B82zzrDfzH/K3 y/54sQLsfVdUgUraalGAXQdeDjbmlf2T9BGKQXy/51/Kvzl5QkdtSszLYA0TUresluR25MBVD7OB kWYLEMUtgkEEGhG4IxBQQ3JI8jl5GLO3VjuThlIk2URiAKDK/Jf5XecfN0yfo2yaOxY/HqVwDHbq OhIYj4z7ICcCSX1B+W/5WaD5HsmFr/pWqTqFu9RkADsAa8EXfgle1d+5O2SYE2zTFDsVdirAfzr/ AOUOT/mLi/4i+YHaX938Xcdh/wB//mn9CM0P/ji6f/zDQ/8AJsZk4foHuDrtT/ey/rH70bljS7FX Yq7FXYqknnX/AJRXU/8AjCf1jMfV/wB1L3Ob2d/fw975K8x/8dq6/wBYf8RGa7D9Ad5qv7wsw/IH /wAm3oP/AEd/9QU2ZeD6w6/V/wB2fx1fYubB0qXeY/8AlHtU/wCYSf8A5NNleb6D7i36b+9j/WH3 vG/y/wDJnlbW9Gmu9W02G8uEuWiSWQEkIERguxHdjmD2b/dn3/qdp26f3w/q/pLJ/wDlV35ff9WO 2+5v65sHTW7/AJVd+X3/AFY7b7m/ritu/wCVXfl9/wBWO2+5v64rbv8AlV35ff8AVjtvub+uK27/ AJVd+X3/AFY7b7m/ritu/wCVXfl9/wBWO2+5v64rb56/M2GKDVBBCoSKKe5SNB0Cq6gAfIZpMX1y 9/63rM/91j/q/oDC8vcN+g+bZ512KuZQwKsAVIoQehGKsR1n8pPy41hy97oNsJG3aS3DWzE+JMBj qfnjSbSP/oXj8rvU5fUJ+NKen9Zm41r1+1Wv04KXiKeaP+Uf5caQ6yWeg2xlTdZLgNcsD4gztJQ/ LDS2y5VCgKoAUCgA6AYodirsVdirsVYv+Y3lrUfMXl9bCwMYnE6SkysVXiqsDuA382YuswnJCh3u w7N1UcOTilypiMHlP83oII4ItStFiiUJGv7s0VRQCphr0zFGLUgUJD8fBz5ajQSJJjKz7/8AilT/ AAz+cX/VztPuj/6oYfD1X84fj4MfF7P/AJkvt/4p3+Gfzi/6udp90f8A1Qx8PVfzh+Pgvi9n/wAy X2/8U7/DP5xf9XO0+6P/AKoY+Hqv5w/HwXxez/5kvt/4p3+Gfzi/6udp90f/AFQx8PVfzh+Pgvi9 n/zJfb/xTv8ADP5xf9XO0+6P/qhj4eq/nD8fBfF7P/mS+3/ilC98mfmzfWktpdahaSW8w4yJ8C1H zWEHIzwamQokV+PJnj1WhhISjGVj3/8AFPN9X/5xu/MK71Ge4iksBHIQV5TuDsAP995PHpJiIBa8 /aOOUyRbIPys/Ivzr5X8+aZrupPZmys/X9UQyuz/AL23kiWgKKPtOO+X4sMoysuJn1UZwID6BzLd chNXtZbvSb20ip6txBLFHy2HJ0Kiv0nIZI3EjybMMxGYJ6EPKtJ/L/8ANDSLZrbTr20ghdzIyVV6 sQFJq8THoozV49NqICokD8e53+fXaPLLinGRP480d/hn84v+rnafdH/1Qyzw9V/OH4+DT4vZ/wDM l9v/ABTv8M/nF/1c7T7o/wDqhj4eq/nD8fBfF7P/AJkvt/4p3+Gfzi/6udp90f8A1Qx8PVfzh+Pg vi9n/wAyX2/8U7/DP5xf9XO0+6P/AKoY+Hqv5w/HwXxez/5kvt/4p3+Gfzi/6udp90f/AFQx8PVf zh+Pgvi9n/zJfb/xTv8ADP5xf9XO0+6P/qhj4eq/nD8fBfF7P/mS+3/imB+bPyD/ADB1meKdJbEy 8pXmZ5WWrSFTWgj8Qcji0eQEk1u2ajtLDIREbqKQ/wDQsn5j/wC/dP8A+R8n/VLL/wAtJxfz0PN/ /9k= + + + + uuid:98034e66-53cf-4745-b8bd-d999dcab1d94 + xmp.did:0180117407206811808397AB94C20DD2 + uuid:5D20892493BFDB11914A8590D31508C8 + proof:pdf + + uuid:ae9148ef-3000-a144-9ee3-ddb3557e196e + xmp.did:8AF5709C0E20681188C6A12CE4B46A4D + uuid:5D20892493BFDB11914A8590D31508C8 + proof:pdf + + + + + saved + xmp.iid:0180117407206811808397AB94C20DD2 + 2013-08-10T21:14:26-04:00 + Adobe Illustrator CS5 + / + + + + Document + Print + False + False + 1 + + 612.000000 + 792.000000 + Points + + + + Cyan + Magenta + Yellow + Black + + + + + + Default Swatch Group + 0 + + + + White + RGB + PROCESS + 255 + 255 + 255 + + + Black + RGB + PROCESS + 35 + 31 + 32 + + + CMYK Red + RGB + PROCESS + 236 + 28 + 36 + + + CMYK Yellow + RGB + PROCESS + 255 + 241 + 0 + + + CMYK Green + RGB + PROCESS + 0 + 165 + 81 + + + CMYK Cyan + RGB + PROCESS + 0 + 173 + 238 + + + CMYK Blue + RGB + PROCESS + 46 + 49 + 145 + + + CMYK Magenta + RGB + PROCESS + 235 + 0 + 139 + + + C=15 M=100 Y=90 K=10 + RGB + PROCESS + 190 + 30 + 45 + + + C=0 M=90 Y=85 K=0 + RGB + PROCESS + 238 + 64 + 54 + + + C=0 M=80 Y=95 K=0 + RGB + PROCESS + 240 + 90 + 40 + + + C=0 M=50 Y=100 K=0 + RGB + PROCESS + 246 + 146 + 30 + + + C=0 M=35 Y=85 K=0 + RGB + PROCESS + 250 + 175 + 64 + + + C=5 M=0 Y=90 K=0 + RGB + PROCESS + 249 + 236 + 49 + + + C=20 M=0 Y=100 K=0 + RGB + PROCESS + 214 + 222 + 35 + + + C=50 M=0 Y=100 K=0 + RGB + PROCESS + 139 + 197 + 63 + + + C=75 M=0 Y=100 K=0 + RGB + PROCESS + 55 + 179 + 74 + + + C=85 M=10 Y=100 K=10 + RGB + PROCESS + 0 + 147 + 69 + + + C=90 M=30 Y=95 K=30 + RGB + PROCESS + 0 + 104 + 56 + + + C=75 M=0 Y=75 K=0 + RGB + PROCESS + 41 + 180 + 115 + + + C=80 M=10 Y=45 K=0 + RGB + PROCESS + 0 + 166 + 156 + + + C=70 M=15 Y=0 K=0 + RGB + PROCESS + 38 + 169 + 224 + + + C=85 M=50 Y=0 K=0 + RGB + PROCESS + 27 + 117 + 187 + + + C=100 M=95 Y=5 K=0 + RGB + PROCESS + 43 + 56 + 143 + + + C=100 M=100 Y=25 K=25 + RGB + PROCESS + 38 + 34 + 97 + + + C=75 M=100 Y=0 K=0 + RGB + PROCESS + 101 + 45 + 144 + + + C=50 M=100 Y=0 K=0 + RGB + PROCESS + 144 + 39 + 142 + + + C=35 M=100 Y=35 K=10 + RGB + PROCESS + 158 + 31 + 99 + + + C=10 M=100 Y=50 K=0 + RGB + PROCESS + 217 + 28 + 92 + + + C=0 M=95 Y=20 K=0 + RGB + PROCESS + 236 + 41 + 123 + + + C=25 M=25 Y=40 K=0 + RGB + PROCESS + 193 + 180 + 154 + + + C=40 M=45 Y=50 K=5 + RGB + PROCESS + 154 + 132 + 121 + + + C=50 M=50 Y=60 K=25 + RGB + PROCESS + 113 + 101 + 88 + + + C=55 M=60 Y=65 K=40 + RGB + PROCESS + 90 + 74 + 66 + + + C=25 M=40 Y=65 K=0 + RGB + PROCESS + 195 + 153 + 107 + + + C=30 M=50 Y=75 K=10 + RGB + PROCESS + 168 + 124 + 79 + + + C=35 M=60 Y=80 K=25 + RGB + PROCESS + 138 + 93 + 59 + + + C=40 M=65 Y=90 K=35 + RGB + PROCESS + 117 + 76 + 40 + + + C=40 M=70 Y=100 K=50 + RGB + PROCESS + 96 + 56 + 19 + + + C=50 M=70 Y=80 K=70 + RGB + PROCESS + 59 + 35 + 20 + + + + + + Grays + 1 + + + + C=0 M=0 Y=0 K=100 + RGB + PROCESS + 35 + 31 + 32 + + + C=0 M=0 Y=0 K=90 + RGB + PROCESS + 64 + 64 + 65 + + + C=0 M=0 Y=0 K=80 + RGB + PROCESS + 88 + 89 + 91 + + + C=0 M=0 Y=0 K=70 + RGB + PROCESS + 109 + 110 + 112 + + + C=0 M=0 Y=0 K=60 + RGB + PROCESS + 128 + 129 + 132 + + + C=0 M=0 Y=0 K=50 + RGB + PROCESS + 146 + 148 + 151 + + + C=0 M=0 Y=0 K=40 + RGB + PROCESS + 166 + 168 + 171 + + + C=0 M=0 Y=0 K=30 + RGB + PROCESS + 187 + 189 + 191 + + + C=0 M=0 Y=0 K=20 + RGB + PROCESS + 208 + 210 + 211 + + + C=0 M=0 Y=0 K=10 + RGB + PROCESS + 230 + 231 + 232 + + + C=0 M=0 Y=0 K=5 + RGB + PROCESS + 241 + 241 + 242 + + + + + + Brights + 1 + + + + C=0 M=100 Y=100 K=0 + RGB + PROCESS + 236 + 28 + 36 + + + C=0 M=75 Y=100 K=0 + RGB + PROCESS + 241 + 101 + 34 + + + C=0 M=10 Y=95 K=0 + RGB + PROCESS + 255 + 221 + 21 + + + C=85 M=10 Y=100 K=0 + RGB + PROCESS + 0 + 161 + 75 + + + C=100 M=90 Y=0 K=0 + RGB + PROCESS + 34 + 64 + 153 + + + C=60 M=90 Y=0 K=0 + RGB + PROCESS + 127 + 63 + 151 + + + + + + + Adobe PDF library 9.90 + + + + + + + + + + + + + + + + + + + + + + + + + endstream endobj 3 0 obj <> endobj 7 0 obj <>/Resources<>/ExtGState<>/Properties<>>>/Thumb 370 0 R/TrimBox[0.0 0.0 612.0 792.0]/Type/Page>> endobj 366 0 obj <>stream +H‰ŒTËn1 ¼ïWè–I‰¯u‹ž‚"è¡`ôqp ¤ù Cy×vœ- È+JÃác¨‡O‡òðx¨åÝûCYž—Z<¤KYóã÷×åKùµ<>×r|)•ħΌ}TrçÒ…A_Å\m$¯·¸g\á@Ž\”z«7-\-Te$ƒ džÒ+9gÖY 2€S5n8ò¤á:×7Y®È¡)›¸IÓef¹6&¯Iš:Pj(ÕÕýàYÄ6û†žN÷a%ÔúCÐB›P=µý-{2«Ï3ºŠÆnÄu‘J@êw¸Ù¯‘°ácƒU§r˜êÌeqjÝ éÉŽýë]kÑFÖã7ÔÈЄW#ÔÑí +ß­´TÖ³Û÷ÿÐ;gäì×#Gßôêc7ÌQÚ7—à >Ñrñ²mØ"Ø uÞŽÓ?´Ýj +pÇ”gA®Jãíq”ª8- ²i½@ݲñ½Ïâˆÿ×¶6LnÖߣQy­mV/ŒzJ¹£øÍæD÷IC*o† ßp¶)>×ÙUÓO…¤Í!Ñ‘D‡.œHRMJPŽÛ)·& “Îê”ìjû£rfüðˆÇúiù#ÀÛ0!Ç endstream endobj 226 0 obj <> endobj 370 0 obj <>stream +8;Z\s0lFoP&4J7Y*@7k/Jurjcfb,aEK.B^UMM`A"V0(fl/D&"fJD%ao[_/R`bsN^Y +J;:>S-UpGIDKZOJ.'33\0-Q(+o3^CGTj^H,o0-8\qB6=#*2o*jk%TQ]KuJDL(nS64 +CIWeeUM\imoASr,boJ>7J>YX_QVMm,]fbQeX5ZW^Zrt.X`A:daGU>,t:HPH">$$4r +6;WA2k7=#BY.kEi/9352M(V%NFN(J!/sYV1Y2mN7o,oD1?/u@Dl6]N&"'s$NTngm; +$7RI)WdAigY0&U[=6gdP.HVCa7JE/hCQqJ0of(C.C9kg8Wn5*Elj]O>SXk);&\#?/ +@sc2d"aL,Kr/U&Kr^7Rs96]*KM@heY%c+/VKeJDJ-H8:6JeuFI1MhEK6anuLJeuG^ +g9dmOFf%e\h)+G<^03Jj7_[M*gM(d^?VRHT\Asn47G!6@S`%1gNf3-_lfHCrbEj=W +Jfk>N+:*QA#_F0)JfstMIKokpB+7-~> endstream endobj 371 0 obj [/Indexed/DeviceRGB 255 372 0 R] endobj 372 0 obj <>stream +8;X]O>EqN@%''O_@%e@?J;%+8(9e>X=MR6S?i^YgA3=].HDXF.R$lIL@"pJ+EP(%0 +b]6ajmNZn*!='OQZeQ^Y*,=]?C.B+\Ulg9dhD*"iC[;*=3`oP1[!S^)?1)IZ4dup` +E1r!/,*0[*9.aFIR2&b-C#soRZ7Dl%MLY\.?d>Mn +6%Q2oYfNRF$$+ON<+]RUJmC0InDZ4OTs0S!saG>GGKUlQ*Q?45:CI&4J'_2j$XKrcYp0n+Xl_nU*O( +l[$6Nn+Z_Nq0]s7hs]`XX1nZ8&94a\~> endstream endobj 364 0 obj <> endobj 373 0 obj [/View/Design] endobj 374 0 obj <>>> endobj 369 0 obj <> endobj 368 0 obj [/ICCBased 375 0 R] endobj 375 0 obj <>stream +H‰œ–yTSwÇoÉž•°Ãc [€°5la‘QIBHØADED„ª•2ÖmtFOE.®c­Ö}êÒõ0êè8´׎8GNg¦Óïï÷9÷wïïÝß½÷ó '¥ªµÕ0 Ö ÏJŒÅb¤  + 2y­.-;!à’ÆK°ZÜ ü‹ž^i½"LÊÀ0ðÿ‰-×é @8(”µrœ;q®ª7èLöœy¥•&†Qëñq¶4±jž½ç|æ9ÚÄ +V³)gB£0ñiœWו8#©8wÕ©•õ8_Å٥ʨQãüÜ«QÊj@é&»A)/ÇÙgº>'K‚óÈtÕ;\ú” Ó¥$ÕºF½ZUnÀÜå˜(4TŒ%)ë«”ƒ0C&¯”阤Z£“i˜¿óœ8¦Úbx‘ƒE¡ÁÁBÑ;…ú¯›¿P¦ÞÎӓ̹žAü om?çW= +€x¯Íú·¶Ò-Œ¯Àòæ[›Ëû0ñ¾¾øÎ}ø¦y)7ta¾¾õõõ>j¥ÜÇTÐ7úŸ¿@ï¼ÏÇtÜ›ò`qÊ2™±Ê€™ê&¯®ª6ê±ZL®Ä„?â_øóyxg)Ë”z¥ÈçL­UáíÖ*ÔuµSkÿSeØO4?׸¸c¯¯Ø°.òò· åÒR´ ßÞô-•’2ð5ßáÞüÜÏ ú÷Sá>Ó£V­š‹“då`r£¾n~ÏôY &à+`œ;ÂA4ˆÉ 䀰ÈA9Ð=¨- t°lÃ`;»Á~pŒƒÁ ðGp| ®[`Lƒ‡`<¯ "A ˆ YA+äùCb(ЇR¡,¨*T2B-Ð +¨ꇆ¡Ðnè÷ÐQètº}MA ï —0Óal»Á¾°ŽSàx ¬‚kà&¸^Á£ð>ø0|>_ƒ'á‡ð,ÂG!"F$H:Rˆ”!z¤éF‘Qd?r 9‹\A&‘GÈ ”ˆrQ ¢áhš‹ÊÑ´íE‡Ñ]èaô4zBgÐ×Á–àE#H ‹*B=¡‹0HØIøˆp†p0MxJ$ùD1„˜D, V›‰½Ä­ÄÄãÄKÄ»ÄY‰dEò"EÒI2’ÔEÚBÚGúŒt™4MzN¦‘Èþär!YKî ’÷?%_&ß#¿¢°(®”0J:EAi¤ôQÆ(Ç()Ó”WT6U@ æP+¨íÔ!ê~êêmêæD ¥eÒÔ´å´!ÚïhŸÓ¦h/èº']B/¢éëèÒÓ¿¢?a0nŒhF!ÃÀXÇØÍ8ÅøšñÜŒkæc&5S˜µ™˜6»lö˜Iaº2c˜K™MÌAæ!æEæ#…寒°d¬VÖë(ëk–Íe‹Øél »—½‡}Ž}ŸCâ¸qâ9 +N'çÎ)Î].ÂuæJ¸rî +î÷ wšGä xR^¯‡÷[ÞoÆœchžgÞ`>bþ‰ù$á»ñ¥ü*~ÿ ÿ:ÿ¥…EŒ…ÒbÅ~‹ËÏ,m,£-•–Ý–,¯Y¾´Â¬â­*­6X[ݱF­=­3­ë­·YŸ±~dó ·‘ÛtÛ´¹i ÛzÚfÙ6Û~`{ÁvÖÎÞ.ÑNg·Åî”Ý#{¾}´}…ý€ý§ö¸‘j‡‡ÏþŠ™c1X6„Æfm“Ž;'_9 œr:œ8Ýq¦:‹ËœœO:ϸ8¸¤¹´¸ìu¹éJq»–»nv=ëúÌMà–ï¶ÊmÜí¾ÀR 4 ö +n»3Ü£ÜkÜGݯz=Ä•[=¾ô„=ƒ<Ë=GTB(É/ÙSòƒ,]6*›-•–¾W:#—È7Ë*¢ŠÊe¿ò^YDYÙ}U„j£êAyTù`ù#µD=¬þ¶"©b{ųÊôÊ+¬Ê¯: !kJ4Gµm¥ötµ}uCõ%—®K7YV³©fFŸ¢ßY Õ.©=bàá?SŒîƕƩºÈº‘ºçõyõ‡Ø Ú† žkï5%4ý¦m–7Ÿlqlio™Z³lG+ÔZÚz²Í¹­³mzyâò]íÔöÊö?uøuôw|¿"űN»ÎåwW&®ÜÛe֥ﺱ*|ÕöÕèjõê‰5k¶¬yÝ­èþ¢Ç¯g°ç‡^yïkEk‡Öþ¸®lÝD_pß¶õÄõÚõ×7DmØÕÏîoê¿»1mãál {àûMśΠnßLÝlÜ<9”úO¤[þ˜¸™$™™üšhšÕ›B›¯œœ‰œ÷dÒž@ž®ŸŸ‹Ÿú i Ø¡G¡¶¢&¢–££v£æ¤V¤Ç¥8¥©¦¦‹¦ý§n§à¨R¨Ä©7©©ªª««u«é¬\¬Ð­D­¸®-®¡¯¯‹°°u°ê±`±Ö²K²Â³8³®´%´œµµŠ¶¶y¶ð·h·à¸Y¸Ñ¹J¹Âº;ºµ».»§¼!¼›½½¾ +¾„¾ÿ¿z¿õÀpÀìÁgÁãÂ_ÂÛÃXÃÔÄQÄÎÅKÅÈÆFÆÃÇAÇ¿È=ȼÉ:ɹÊ8Ê·Ë6˶Ì5̵Í5͵Î6ζÏ7ϸÐ9кÑ<ѾÒ?ÒÁÓDÓÆÔIÔËÕNÕÑÖUÖØ×\×àØdØèÙlÙñÚvÚûÛ€ÜÜŠÝÝ–ÞÞ¢ß)߯à6à½áDáÌâSâÛãcãëäsäü儿 æ–çç©è2è¼éFéÐê[êåëpëûì†ííœî(î´ï@ïÌðXðåñrñÿòŒóó§ô4ôÂõPõÞömöû÷Šøø¨ù8ùÇúWúçûwüü˜ý)ýºþKþÜÿmÿÿ ÷„óû endstream endobj 367 0 obj <> endobj 376 0 obj <> endobj 377 0 obj <>stream +%!PS-Adobe-3.0 %%Creator: Adobe Illustrator(R) 15.0 %%AI8_CreatorVersion: 17.0.0 %%For: (Ivan Safrin) () %%Title: (icons.ai) %%CreationDate: 3/4/14 6:26 PM %%Canvassize: 16383 %%BoundingBox: -160 -174 977 170 %%HiResBoundingBox: -160 -173.0004 976.1735 169.5841 %%DocumentProcessColors: Cyan Magenta Yellow Black %AI5_FileFormat 11.0 %AI12_BuildNumber: 256 %AI3_ColorUsage: Color %AI7_ImageSettings: 0 %%RGBProcessColor: 0 0 0 ([Registration]) %AI3_Cropmarks: 0 -792 612 0 %AI3_TemplateBox: 306.5 -396.5 306.5 -396.5 %AI3_TileBox: 0 -792 612 0 %AI3_DocumentPreview: None %AI5_ArtSize: 14400 14400 %AI5_RulerUnits: 2 %AI9_ColorModel: 1 %AI5_ArtFlags: 0 0 0 1 0 0 1 0 0 %AI5_TargetResolution: 800 %AI5_NumLayers: 1 %AI9_OpenToView: -327 663 0.5 1157 616 26 1 0 78 132 1 0 0 1 0 0 1 0 0 %AI5_OpenViewLayers: 7 %%PageOrigin:0 -792 %AI7_GridSettings: 32 8 32 8 1 0 0.8 0.8 0.8 0.9 0.9 0.9 %AI9_Flatten: 1 %AI12_CMSettings: 00.MS %%EndComments endstream endobj 378 0 obj <>stream +%%BoundingBox: -160 -174 977 170 %%HiResBoundingBox: -160 -173.0004 976.1735 169.5841 %AI7_Thumbnail: 128 40 8 %%BeginData: 9956 Hex Bytes %0000330000660000990000CC0033000033330033660033990033CC0033FF %0066000066330066660066990066CC0066FF009900009933009966009999 %0099CC0099FF00CC0000CC3300CC6600CC9900CCCC00CCFF00FF3300FF66 %00FF9900FFCC3300003300333300663300993300CC3300FF333300333333 %3333663333993333CC3333FF3366003366333366663366993366CC3366FF %3399003399333399663399993399CC3399FF33CC0033CC3333CC6633CC99 %33CCCC33CCFF33FF0033FF3333FF6633FF9933FFCC33FFFF660000660033 %6600666600996600CC6600FF6633006633336633666633996633CC6633FF %6666006666336666666666996666CC6666FF669900669933669966669999 %6699CC6699FF66CC0066CC3366CC6666CC9966CCCC66CCFF66FF0066FF33 %66FF6666FF9966FFCC66FFFF9900009900339900669900999900CC9900FF %9933009933339933669933999933CC9933FF996600996633996666996699 %9966CC9966FF9999009999339999669999999999CC9999FF99CC0099CC33 %99CC6699CC9999CCCC99CCFF99FF0099FF3399FF6699FF9999FFCC99FFFF %CC0000CC0033CC0066CC0099CC00CCCC00FFCC3300CC3333CC3366CC3399 %CC33CCCC33FFCC6600CC6633CC6666CC6699CC66CCCC66FFCC9900CC9933 %CC9966CC9999CC99CCCC99FFCCCC00CCCC33CCCC66CCCC99CCCCCCCCCCFF %CCFF00CCFF33CCFF66CCFF99CCFFCCCCFFFFFF0033FF0066FF0099FF00CC %FF3300FF3333FF3366FF3399FF33CCFF33FFFF6600FF6633FF6666FF6699 %FF66CCFF66FFFF9900FF9933FF9966FF9999FF99CCFF99FFFFCC00FFCC33 %FFCC66FFCC99FFCCCCFFCCFFFFFF33FFFF66FFFF99FFFFCC110000001100 %000011111111220000002200000022222222440000004400000044444444 %550000005500000055555555770000007700000077777777880000008800 %000088888888AA000000AA000000AAAAAAAABB000000BB000000BBBBBBBB %DD000000DD000000DDDDDDDDEE000000EE000000EEEEEEEE0000000000FF %00FF0000FFFFFF0000FF00FFFFFF00FFFFFF %524C45FD2EFFCA9399939993999399939993999399939993999399939993 %999399939999994A4B4A4B4A4BFD08FFA799999993999399939993999399 %939993999399939993999399939993BB994B4A4B4A4B4AFD0CFFA8A85252 %27522752527D7DA8FD16FFA19999A1A0A09AA1A0A09AA1A0A09AC3A0A09A %A1A0A09AA1A0A09AA1A0996E4B4A4B444B4AFD08FFC9929AA0A09AA1A0A0 %9AA1A0A09AA1A0A09AA1A0A09AA1A0A09AA1A0A099994A4B444B4A4BFD0A %FFA8524BFD05274BFD0527527DFD14FFCA92C9AFFFAFFFA8FFAFFFAFFFAE %FFA8AFA8FFFFFFAFFFA8FFAFFFA8FFA0994A4B4A4B4A6FFD08FFC993A1FF %AFFFA8FFAFFFA8FFAFFFA8FFAFFFA8FFAFFFA8FFAFFFA8FFAFC3924B4A4B %4A4B4AFD08FFA15220FD1027A8FD12FFA193A1FFA8FFA8CFA8FFA8A85252 %27272752527DA8FFA8CAA8FFA8CFA8A06E4B204B444A4AFD08FFCA92A1A8 %FFA8CFA8FFA8CFA8FFA8CFA8FFA8CFA8FFA8CFA8FFA8CFA8FF9A93444B44 %4B204BFD07FF7D27275227512752274B2752275127522751274B277DFD11 %FFCA92C9AFFFA8FFA8FFA852FD052752272727527DFFCFFFA8FFA8FF9A99 %4A4B4A4B4A75FD08FFC999A1FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8 %FFA8FFA8FFA8FFAFC3936F4A4B4A4B4AFD06FF4BFD04274B272727512727 %274B2727274B2727274B2652A8FD0FFFA199A1FFA8FFA8FF7DFD04274B27 %27274BFD04274BA8A8FFA8FFA8A06E4B444B4A4A4AFD08FFCA92A1A8FFA8 %CFA8FFA8CFA8FFA8CFA8FFA8CFA8FFA8CFA8FFA8CFA8FFA099444B4A4B44 %4BFD05FF52272752274B2752274B9F9F4B272752274B2752274B27522752 %A8FD0EFFCA92C9AFFFA8FF7D272752272727C1754B2752274B272727A8A8 %FFA8FF9A994A4B4A4B4A4BFD08FFA199A1FFA8FFA8FFA8FFA8FFA8FFA8FF %A8FFA8FFA8FFA8FFA8FFA8FFA8C3934B4A4B4A4B4AFD04FFFD0B27C1C0C1 %754BFD0A272652A8FD0DFFA193A1FFA8FF7DFD052721519EC19975FD0627 %4BA8A8FFA8A06E4B444B204B4AFD08FFC96EA1A8CAA8FFA8CAA8FFA8CAA8 %FFA8CAA8FFA8CAA8FFA8CAA8FFA8FF9A994A4A204B444BFFFFFF52272751 %27522751275227519FC1C1C79F7627282752275127522752277DFD0DFFCA %92C9AFFFA85227522728277BC7CC9FC1C19F514B4B5227277DFFA8FF9A99 %4A4B4A4B4A4BFD08FFA799A1FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8 %FFA8FFA8FFA8FFA8C3934B4A4B4A4B4AFFFF7D27274B2727274B2727277B %A5C79FFD04C19F4B27274B2727274B272720A8FD0CFFA193A1FFA87D264B %272751C7CCCC7B514B9FC1C793B54B27274BA8FFA8A06E4B4A4B444B4AFD %08FFC992A1A8CFA8FFA8CFA8FFA8CFA8FFA8CFA8FFA8CFA8FFA8CFA8FFA8 %FF9A994A4B444B4A4BFFFF52274B2752274B272851A5C7CCCCCC7B9FFD04 %C19951274B6F6F2752272727FD0CFFCA92C9A8FF522727519FCCCCA55127 %272827759FBBB04B2752277DCFFF9A994A4B4A4B4A6FFD08FFC999A0FFA8 %FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFAFC3924B4A4B4A %4B4AFF7DF8FD06274B7BCCC7CCC6C7512727759FC19FC19F998CB54BFD05 %277DFD0BFFA193A1FFA84B2751A4CCA55121FD07276FB54B27272752FFA8 %A06E4B204B444A4AFD08FFCA92A1A8FFA8CAA8FFA8CAA8FFA8CAA8FFA8CA %A8FFA8CAA8FFA8CAA8FF9A93444B444B204BFF524B27512728277BC7CCC7 %CCC79F27282752275175C1C1C7BBB5B56F275227512752FD0BFFCA92C9FF %A8274B27527B5E274C2751275227512793B56F27522752A8FF9A994A4B4A %4B4A75FD08FFC999A1FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FF %A8FFA8FFAFC3936F4A4B4A4B4AA827274B272751A5C7CCC7CC7B51FD0527 %4B2727279FC1BB8DB54B27274B272727A8FD0AFFA199A1FF7D2727272158 %5D51274B2727274B272769B54BFD0427A8A8A06E4B444B4A4A4AFD08FFCA %92A1A8FFA8CFA8FFA8CFA8FFA8CFA8FFA8CFA8FFA8CFA8FFA8CFA8FFA099 %444B4A4B444BA1274B27519FCCC7CCC7A5512827274B5227272752272727 %7593B5B06F274B275227277DFD0AFFCA92C9FFA12752274B51822D4B2752 %274B27522793B06F274B2752A8FF9A994A4B4A4B4A4BFD08FFA199A1FFA8 %FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8C3934B4A4B4A %4B4A52FD04277BA5CCA5812DFD042752A87DFD0627056F8DB54BFD062752 %FD0AFFA193A1FF7DFD0427575751FD082768B54BFD0427FFA8A06E4B444B %204B4AFD08FFC96EA1A8CAA8FFA8CAA8FFA8CAA8FFA8CAA8FFA8CAA8FFA8 %CAA8FFA8FF9A994A4A204B444B522751275227517B825E582752272752FF %FFFF524B274B27526FB5B57027512752274B4BFD0AFFCA92C9FFA8275227 %4B57822D4B275227512751277671784C4B277DFFFF9A994A4B4A4B4A4BFD %08FFA799A1FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FF %A8C3934B4A4B4A4B4A274B2727274B2751575E2D27274B2752FD04FFA852 %27272793B0B54BFD04274B2727A8FD09FFA193A1FFA852274B27575D5928 %27274B2727277771774C4C27277DFFA8A06E4B4A4B444B4AFD08FFC992C3 %AFFFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FF9A994A4B %444B4A4B4B2752274B274B515E5D58274B272752FD06FFA852276FB5B56F %2752274B274B27FD0AFFCA92C9A8FF7D27274C57822F5B2F5227524C7771 %774C27274B27A8A8FF9A994A4B4A4B4A6FFD08FFC999A0FFA8FFA8FFA8FF %A8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFFFC3924B4A4B4A4B4AFD0627 %21575D5E2D2727272052FFFFA8FFAFFFFFA1276F8CB54BFD06274BA8FD09 %FFA193A1FFA8A8272727512D282E5A302F4D77714CFD0427207DA8CFA8A0 %6E4B204B444A4AFD08FFCA9276FD1A527599444B444B204B4B2752275127 %4C51825D582751274B52FD05FFA87D27276FB5B56F27522751275227FD0A %FFCA92C9AFFFFFA82752274B2751285A305A534C275227522752A8FFA8FF %9A994A4B4A4B4A75FD08FFC9BB4BFD04274B2727274B2727274B2727274B %2727274B2727274B2776996F4A4B4A4B4A2727274B272727585D5E2DFD04 %2776FD04FF5227204B276FFCB54B27274B27272752FD0AFFA199A1FFA8FF %A87D20FD04274B272E2F2F2727274B27277DFFA8FFA8A06E4B444B4A4A4A %FD08FFCA926F274B2727274B2727274B2727274B2727274B2727274BFD04 %276F99444B4A4B444B76274B2752274B2D825D572752272752FFFFA12727 %274B272D6FB670774C4B2752272752FD0AFFCA92C9AFFFA8FFFF7D274B27 %4B27512752274B274B2752A8FFA8FFA8FF9A994A4B4A4B4A4BFD08FFA1BB %6F272752274B2752274B2752274B2752274B2752274B2752274B2776994B %4A4B4A4B4A76FD062751575E2D27262727527D5220FD062770717771784C %272727207DFD0AFFA193A1FFA8CAA8FFA8A1FD0B2752A8FFA8CAA8FFA8A0 %6E4B444B204B4AFD08FFC99275FD1A276F994A4A204B444BA8274B275227 %4B51825D593053274B27512751275227514C77777771784C5227522727A8 %FD0AFFCA92C9AFFFA8FFA8FFCFFF7D762752274B275276A8A8FFA8FFA8FF %A8FF9A994A4B4A4B4A4BFD08FFA7BB6F2827522751275227512752275127 %522751275227512752274B279A994B4A4B4A4B4AFF522727274B2751575D %343030532727274B272727524C7771777177FD0727FD0BFFA193A1FFA8CF %A8FFA8CFA8FFA8A87DA87DA8A8FFA8CFA8FFA8CFA8FFA8A06E4B4A4B444B %4AFD08FFC992752727274B2727274B2727274B2727274B2727274B272727 %4B27276F994A4B444B4A4BFF7D27274B274B51825D59305A305A2F52274B %2777717877774D5227272752274B277DFD0BFFCA92C9A8FFA8FFA8FFA8FF %A8FFA8FFA8FFCFFFA8FFA8FFA8FFA8FFA8FF9A994A4B4A4B4A6FFD08FFC9 %BB4B28274B2752274B2752274B2752274B2752274B2752274B2752277693 %4B4A4B4A4B4AFFFFFD042721575751272F2F362F5A2F2E4C77717771774C %FD0927A8FD0BFFA193A1FFA8FFA8CAA8FFA8CAA8FFA8CAA8FFA8CAA8FFA8 %CAA8FFA8CFA8A06E4B204B444A4AFD08FFCA926FFD1A276F99444B444B20 %4BFFFFA8275127522D52275227522F5A305A305A53777177274B27522751 %275227277DFD0CFFCA92C9AFFFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8 %FFA8FFA8FFA8FF9A994A4B4A4B4A75FD08FFC9BB6F522751275227512752 %275127522751275227512752275127522776996F4A4B4A4B4AFFFFFF5227 %27274B2727274B2727275330362F362F4CFD05274BFD042752FD0DFFA199 %A1FFA8FFA8CFA8FFA8CFA8FFA8CFA8FFA8CFA8FFA8CFA8FFA8FFA8A06E4B %444B4A4A4AFD08FFCA926F274B2727274B2727274B2727274B2727274B27 %27274BFD04276F99444B4A4B444BFFFFFFA852274B2752274B2752274B27 %532F5A305A274B2752274B2752272727FD0EFFCA92C9AFFFA8FFA8FFA8FF %A8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FF9A994A4B4A4B4A4BFD08FFA1 %BB6F272752274B2752274B2752274B2752274B2752274B2752274B277699 %4B4A4B4A4B4AFD04FFA8FD0B27262728362FFD0A27A8FD0EFFA193A1FFA8 %CAA8FFA8CAA8FFA8CAA8FFA8CAA8FFA8CAA8FFA8CAA8FFA8A06E4B444B20 %4B4AFD08FFC9926F05272027272720272727202727272027272720272727 %202727276F994A4A204B444BFD05FFA85227522751275227512752274B28 %53274B27522751274B27A8FD0FFFCA92C9AFFFA8FFA8FFA8FFA8FFA8FFA8 %FFA8FFA8FFA8FFA8FFA8FFA8FF9A994A4B4A4B4A4BFD08FFA7BB7553527D %5276527D5276527D5276527D5276527D5276527D527652A0994B4A4B4A4B %4AFD06FFA85220FD04274B2727274B2727274B2727274B27274BFD11FFA1 %93A1FFA8CFA8FFA8CFA8FFA8CFA8FFA8CFA8FFA8CFA8FFA8CFA8FFA8A06E %4B4A4B444B4AFD08FFC992C3AFFFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FF %A8FFA8FFA8FFA8FF9A994A4B444B4A4BFD08FF7D2727274B2752274B2752 %274B2752272727527DFD12FFCA92C9A8FFA8FFA8FFA8FFA8FFA8FFA8FFA8 %FFA8FFA8FFA8FFA8FFA8FF9A994A4B4A4B4A6FFD08FFC999A0FFA8FFA8FF %A8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFAFC3924B4A4B4A4B4AFD %0AFF5252FD0A272027277DA8FD13FFA193A1FFA8FFA8FFA8FFA8FFA8FFA8 %FFA8FFA8FFA8FFA8FFA8FFA8FFA8A06E4B204B444A4AFD08FFCA92A1A8FF %A8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FF9A93444B444B %204BFD0CFFA87652275227522752527DA8FD16FFCA92C9A8FFA8FFA8FFA8 %FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FF9A994A4B4A4B4A75FD08FF %C999A0FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8C3 %936F4A4B4A4B4AFD10FFA8A8A8FFA8FD19FFA19392999299939992999399 %9299939992999399929993999299939992996E4A204A204420FD08FFCA92 %999299939992999399929993999299939992999399929993999299929320 %4A204A204AFD2EFFCFA0C3A0C3A0C3A0C3A0C3A0C3A0C3A0C3A0C3A0C3A0 %C3A0C3A0C3A0C3A0C3767C76A1767DFD08FFCAC3A0C3A0C3A0C3A0C3A0C3 %A0C3A0C3A0C3A0C3A0C3A0C3A0C3A0C3A0C3A07C76A1767D76FD7FFFFF %%EndData endstream endobj 379 0 obj <>stream +%AI12_CompressedDataxœì½ëŽÉ‘&úñy~ -–µ~¿‹2³²fµ(I µ¤•06Õâ,/ 6[::OÜÌ>s÷ˆÈd³›œ…f†åèf–U¤»‡ßìö™ù?ü?_|ùäøÕ›yþÄß™Ãòÿp~ûüé»7ov`êáç/_~÷í»·Dúɯz°ñδ‡Ž?/ă¿{þöÛo^ÿì`óá?>зòó¿<}}øòéŸÞ¾xýÓÃO~Úè¿yñîåóö—ÏÞ¼þöî鋟jkíë÷Oßµ?ùÿþ› ‡ô3—_ü‚þüôõ_ž~ûí‹ÿ¯ýÑ&_|£Þ|÷ú«¯¿>½ùvxb“iÿËáPsn] öÿç‹_?ÿöêSí¡GÓ]û%¶*ë],Á¶/Ý¿yöÝ«ç¯ß}ñöͳçß~{~óòÍÛov8ÿ­½Å/ž~Ýþòôð‡ç/_¾ùëáôòé³ÿ³´1ˆ|xñòy{ÝWO߬¥—?þܺ?ž¾{ñò«_~÷ê_ž·p1Ùÿ‘«üí·­®V-}&rþãÏ_5Ê—Ïß½km Ò üúOs7‘ËOþé×Ï¿~Á“ÑF쟊jß¾ùæÕÓ·ÿ‡¾{x’«;$ëFþø›ç¯¾yÙ†–GÁ›tO|¥æ_ðl{~n_Íœçyñü¯?;üòÍëç2Ç·ï¾”é Áù¿üå×ß½|þö·¯_¼k=sDª2¿xóÕó—íùþý‡—Où͹Øñyà7Oß~ýü]›Ñ7/¿{Ç ­h mˆŸþí9Í“•~õÍó׿yó;îãïò!%0íE­í›meQ幬wsks›T U¢µç6)_´iúÕÛ_¿xý3 ™¾|ûâ«1{­Ê"ÿãÚîÊô_Õÿ¤«í­ß½{þ]o«æü‹i˜»_|Ù½¼þêüæ ý·´òÛô¿n+ã囯åoý3ÿ¥}ý»oäø÷?¶Yú¢m?ªsù%ÿ¥üñ‹—ßµ?ýãÛ7ß}óó×z³üD¶ùïž?k{¹MäW‡_ýË¿¶_Úžå•yøÍÛ§ÏZí÷þLÛ»ßüô½Õµ—{ûü lßä_õßïÿöýó?µ=5¾.ÔËë¿<ù曩ÚNyúú«Ãÿ~úö›ï¯ú‹—O_?}{`z¯ùñÅ_Ú_ž¶‘uÚTÚÉ7mpø+üȦ÷<0ýézúîÏíøyþú«o{ÝòëºãBûþú¾|Fkðíáôö»oÿ|øÍ›7/{µë?õÚAf*=ÿ÷ÑÆü…׿z-´o l[j‡Ïß]+íéÛ-´?þ=×~~úò勯ß>ýæÏ/ž]kàÊß{Kò·²°þöê_Þ¼|ñí«±ž&ÊOß¾{ñìåó/ÿöí»ç¯>xr—¯^´CîÆ6~ï3_þõé»g~|ñ/oŸ¾}ñü½»&àO/^ÕÖþ—ß½x÷| ЛWßàrøòÏO¿yίñîÏüä—½ÂøÇvôχû“'ï9õ­9œ^OÿÇ·O¿zÑJÄž~õüðî4?]6¿7槯–ZÌ]j"“Ëô!G'|‰9¶Ù×kû`}v‘(ÖÙ˜˜âb¨K8üÃOo§jZÍA«9h5­æ ÕPMë‹9¤Cl|º‰­ÆÆÿø k<}ûÑý³­6×þò©z×ëk};Ý÷™× ¼>§ÿøöùó×ÿ¢ê=üêíÓ×_?os{N’Ìñ_Œ5ÎxL4ÉdSL5Gs6÷æbµ­wÁF›l¶ÅV{´'{¶÷öbœqÖ9ç]pѵ×uÅUwt§Åݽ»¸o|“µ¼÷¡•è“ÏM¯þèOþÜÊÅ?lpíÏ!ÄB%Ôp §p÷á!šh£‹~‰!Ƙb‰5ã)ÞÇK|H&¹äSH)åTÓ1Ò9]ÒC6ÙeŸCN¹äšùœ/ù¡˜b‹/¡¤’KYʱœÊ¹\ÊC5ÕÕPcmSVk=ÖS=×K}8š£;úc8Æc:–c=§ãùx¼œÌÉžÜɟ©õå”NùT–S=O§Óùtº´òp6ç6Lg×Þ³½Ë9žÓ¹uäÜÚ<×s«çÜ=ÓÏ}+.TîÛ°ß·á]ÚÿŠG (qWÿ÷Þ²ðÿþG›nîà¥uT +õ„~Ží¨äVÚX¶·“âÚÛ¶r|håÒʹéFÂcÝc\Úµ9kÃÕÇÑ´Á»ÔûVÚ˜µ-m`SÞІ¹-ŸjÚ ß·a8•Zè'·9 mf\›!“Z9/m¤N­ê’é'µ©l;¥“Z¹PçÚtSiŸÛô·…Ó‚k Âć¶4îcï65Ƕ\j[4yik'µ乸¶¬LxhEÆóÜÞ’_€:Ò–`¦úÚrŒmQòÊmKÔ¶…jüC[²TÎ ¯ßs[ÉR +Jnkœ +¾×Ö¾#Å=´]AE&öìN(ui;‡JAI(EŒû€rrB9¢”¥mÖÂÓM‹ªùÿå~äÿ?îç²´¯_¸ªû‡S/Ç^Nµý<¨§N=®?/ü%”^*-þW?IÉ.O¦¨‘³ôðÜ_öü,Ÿª¢« +eRxºÍµ»)ºŽdY”È% hŒ`i¼ xÌGhåë ¾à¬§­Ñ¸ñá\âÈëñ̼‚¸^³ 7Íßî˺o”¶Ô}_ôËúßПÔ»è.co\Pd‡œw{¤¢·%¢„…_*XÔhõ‡‡Î<ð P¹G9£Ð05¶ÁãF¥ 4©)sI(qS¦øE§Í-ü?Þݶõçár¹Ü7rjçs½”K¾¤K¼„vT¹6¦müËý}c4§ûc;˜Ë}f†KqmhLã;—ƅΗƞJcT©¡¡q®Í iûêœB¸DaoðÌ s†{æ Çvê_HÌüÑ-̘'œžÁXüÞP"о³6­?åˆrB9£Ü/m¡Q¹ (·ÑmkQÀ‘Û•P"Šv^äµáª?GÈUmõK¹G¹ (û3(–¶ŒåéÞÛÃq”4•<•2•.fTÔµœ¦ržÊýT.SyeYPv*£ËóO˜JœJÒ²°–0J™JÊq*§^ÎSмð?—©<Œ²:víTæa]æŸEþaQKšJžJ™JÊq*§…¤ì6ÝP— ßòƒÁƧ5 eËCÚˆ…̬å!è§­Á?ËUò5!ÃÞ|bõ/vHdPÕÒó²†RÙÖ#i”´°D—¤õp¢©…I3B#NÚã};ÌQÛü¤vdÍš£eÍ1vÍñžO@:õŸi•®{Öé(¢S'—ºtÑv±ðF§M}¡­»Ñg}1@WœEáà³ÄKeȹ*ëæé™ùI•xOPIè²¥Êé—Í'ùí_’¯Í¿ +?·ü`Aú¸UF/Ï·åÔøùwP!Ïöz”¶S².çM9íÊq™4/*uW¶K'ïJšËÒþw%lŠß·)] ç×þPñóC¥ÏåCÅÏ•=—>?\ö”#-ö’v%ïJ¹Rê\þç¸+§]9ïÊý®4Ý`éZÒ¤¡­‹3»bw¥ÿÌ| Ò¦©Ô|¥œyHÚ#Ëáö° ÛÆîÙJÆVª…ׇ”Âë$³XËæ^/ž‹UóÐí\jáW…+.làò,ùXÖÞ.}%ÑZšW“¬'×vMÁʵ0{w?P©9³¤|ue-›¥õ>¥fRink4$½Ì +ª3¢Ì¬U™ãFÙk1M‡Y Ä f£À(o§¢K_7…J‹îº®ÝªMû¡ëÚѵ¨Ûªr«Ú}¼¢€WVmèSR5zx†¹£õsí#v+ÈP·’>”õYmWÁkõïÒÿtíßan™›K«ú‹üZúët ^w üÿ„ÿŸx°ÎlÂ9ÃÓþ¿ðpv›Nä8æ­üŸí=òËÇÿþd)ˆ³ Âe f!>Þ„ +·ûd?÷ƹÿ€ªý÷þ–p²lŒÂkᤌ"®ùÊVøÁe™–O#²}Úê>Wxõ¨×¼bw¾à {óù"—IoÜú"·žÈûïõD²6iº>Y£Toä¹û#-<’¢Y–‡_ÒÂ3Iö³¼À=yb«˜º(›º¢¸)ÙŒE:ç=<•–íQê­,Ý_IƤ‡…Ðá´Ìk·eSgÇ%»-W^KñYŠÇ’ý• œ•'vS^Ø9iÙØß˜›,_›LjRÚýý¥ ¦ÉîÒTòK¼¤K¾”KS‹›v¾Ü_.—vÌ(ÉðÒtŠÔ½8'u×ðR€P«æ)5ó¨Á$ö¢fN56ªÍ¯[ÞX„;.<RÎ(÷½\PÔ¤bÅ¥Õ6¦V¦°À3{QC«š;ÕêØmÚþIÎ(ìƒ]øŸ Š*ÌêW2¼ZæÔÆPb/°ó.jnU«g—›«Ž€và¬?÷½\f‡´¸¤õK£|¿kZmÍjñ…ÕUŸ ¯)êŠÖð/ø ‡±L hì6Œmj€f¹ñ ÞÂæ|)ÃØ7¹¡Ïª±°¿ádYÔ•Ðå‚Õ9 “erKé¶ÍÉÞ©?°–.,ƒk~c-à +;ô']Ê;l»;Ïr™lÃÃb¬Vä°s³MŽ6) é;GôÖÝ&.7-§+Î7-e麮êÁi­(¬l£Ÿ±*Ÿ±*Ÿ±*Ÿ±*¤¦|ƪüýUø«ò«ò«ò«ò«ò«ò«ò«ò«ò«ò«ò÷Vág¬Êg¬Êg¬Ê°þ^ê¼­²°ÑlWÙ#V¶˜•-j¸•…—S‡®ðš +,I¬Ñ+¿"–±ÊÅË2AYdÑé²@K†1,vL‹ÅX½gõÄ‚+›Ç^Œ²eAò’Ä¢T¨‹,Í#‹¡ T–h`)Êñò2 +~¹ð¼œ»­“X·PkšãU*65ÑžÔ®vbWQexL·­E–„< £´àÕÆFzÕv¶s÷<¨¿aøÔ·à–îGPïÁðÀCçÀ°¸ €ˆœÝÒÏ2êÎìvVC~·¹Õ•õ~m·—:a] ²t#ÃÖݶÌoíò[S|?–.xÚna;‚éå²3CHaGÛ²ñ¬!<[ûÄ(ùji‹`é¶‹¹lí·Êyöº»}è`N7Ì4·NËd­YÑì´†;]<¥…1Oq3*óø¤ÍXì?Ï&¸ilf±~ð÷Ÿ·JÂêó2ÕÍŒ’š{;#¥Z*mlWYqSi“<}.ӨɈ–i<çÑÆ}鿌)Ñ “¥†¶Ùè¶±õß–>ñ—¾ |¥Ÿ’……²£ÍÛ~[€Ø¢öÐm ã‡VàžâúnžÙ%Ó–Ro¥?OÚè-zèÆ×(nÿ®È3-Þ”Oø Z®8¬ÃeYÙ[h¹Yá÷”ϹÏþ{©pÖ¯i…³Š.ºô(‰?¨,W\zÃ…·¥½¿°b«¡ÞÒS?¬”‡ºÜøÃ.Ë=ôo®œ­locøtU}®ðs…Ÿ+üÄþ\î÷"¯–|5a¯öèÜ >wƒ¿ê¬E +Ó@¬Rw`uÖðºŠØfwé-w»Á]Á»ÎÄßá Þ ¼ƒ8^AòÆž¿_ß Þ{qèÁçà¸Sw]†N½rçî‡c ¯FV‘±ßܰÇxU˜ëC¶*œ(Ö¥ƒW 0ÁÇR½ïØTŤ*Õw¼˜âN;jlé8Ó:v¯Ð1h>H:c::`£+¸è€ŠÊÏ…‹!¢=‰CÌ(êHcÃÎ%ra«ÒÂøäÌe)âáT<°¢qÕº‡v.ú£2zW¼–a„ìerÞõ2[ª‡){mä^qúµèpMÐY—­ŒÕ½$Ë«ýýβ«üX‰óC Ýá&¤û¨{is ë¾ìÞC»¯€»— ß­ãÈ{À¼g¨÷ ön;hÙ!¾gÔ÷ØX÷+è÷ÿ^AÀ[YVò•ïû¼ò%wTø>¡Ã·ñ5R|ŸQã[ôø„"_ÎóÏý®\ve¿JWæÁ-è|?Ÿ‹¿ZÂ\–é—=0u[ß—¼/ËÞ~«Ô(mÑ.÷Ç(§/Ë]ÿ‘e™ÎÞOò³|ÿ#8E~T4À{âþg†O|XLÀ­¸€ó2!è£^(+ðHº'°¬‚ÖÞ¯°6+ÃÛ‰7c`V!ë0‚9”`R°+è>‰¥SWžu¨Á>ä`xÐ˲B í£¶Êùgïë³ìÙ—kÎÕ—d[¦ÃgÙœF{÷åµÀ‡klq¹â%ÝFI¼¯üSã³f¦å½VÕ^âròû£ËTȧȔ“–äsO©YMõÁ&JÐ^s¶–(¦­TωÜSÛN™RÉ»¬-ÈκÍFû êâ<´ÆS“²Ä†P+}½’Úà¹8“l ÑTÊ+ïkSÈ +g’¥ŸQ]i$›Gï>i­ÒO› +×HïË*{n%›þ{hy8Škب#…©W?¾îÃGŽyÈŸlúBþ”³çͧž9o>nÖlüøYku|ô¬¹røD{Žkú!Ù•¿øîí7/Ÿ~ÝO_þtÙüÞêt=›²üØ]ÐBœ²:ØQŽ«ä sÄ‚}oÛ°EÖ¬ƒxqß*ÔV¡µUCÞN2 ˆlGÄ*–„’Cr7ÅÔ_=O&––8U|é@”Šqæ}äÒM4†Í4LÈhÐÈ ·Év"já‰Ñ÷l¹¹Àz#œD¤›f¸ù 1oêäÒþ7£Ê¯É×àq*Þ‘â@ˆ/КԆ°‡„ûŽߢÀS)êíR&Mr¥:\æ²EÌØoBçLræ‚ñFIßSʶ,{RÿÓÿX¯{¿±VÖºÅ^Ø×u[84…ÃZ0­Z ¬áŒ•[¯Ú #0Ø‚°U$µÚ +ÕZHê@^ØLúÃÖVHÖB±JTÿ Qý»I–BÏÊDdÀ­¬S:-Äy•¤Â`.Ùçžaœ-ƒ©›ú^äÏž®Ï_êXýxÅ((|Ç@^±¥¤ÉÈæ‘¶‘né65m #ưU ëİAÐüø³[ÚÁ6V?¶Íf‚ÙÀø¸®Ó¯Õw¨ä[Õ{¯3«â;ôÐùç2•‡e2mm¥x·+þf‰Z–ñq[> lWÿÙÕïf÷7$1™‰ûêÆçÍ—¯·˜mË×+ûàŸÿ þî}®ë0ùqÃ=»}|‹wÃ|;nïzùÏ +Šÿ\áî +5*B°.`€ˆ+$Ä#DXD¼>°ËèžÞ'öITľfñDEvOyÁZFÊ> öžùÝ  •9cF\Bì u…¦›óÃLúœ;ñ(ú6mbئMÜ%NleÙdO,¶@t…¢[€U5 Ã…™·È K_€K¯~3ðé ^ Ũ{Å&øKñƒ]zæ®/Œ]?±xt†½vùT„÷ÔñìŠiXá9¸bàÛã¾Q«ˆÀ‡X®~’!´wWÕ$÷Ÿ¥fÀäµÄUY…á¿)“iuÁ‡[¦¾+ˆézzY³ìIýO}ÝßcÍW¬wYënÙý¾Øn„×,«øšm„Í:Æf޲™ãlF¤M¥¼&¹/H¯ KP#n4bÎé)+Ì`]Ý÷ø›ã²‰„ÄáPœq““1¯s D›â qê±¥'I 0=‘H†àÐ ‡@@ÄÒ#" +Öud„‡‚NþÕÙ¯§ÿi™@ý ñF™¹@ç¬yÁ‰õ!æËÄÒ¿4¢–®Å+)wüAƒ•ʲŠUŒbŽWrðp˜ËÐL=çž©‡9ÇÒ™GÅa9ˆ2‘ˆUâ'V⺫ÄL,¥íèeâ+ƒ·œ;`ü(h L¬&O Lgé|'LÜgæAƒMÜhÅ“fÎtYV,j”[½žvåx¥´Ÿ¥^ÿ¹i”™J¾V–ëdþÓì'é×óZ¶r’õÔ›Còw6æÚ­±ïŠ-¿t€Vºï®u¨©,b¦õ|^“­\íC¨Åxº +Ï…lø–¼XÝà{Ýíòéªü!¶à/¿û†îo|ó§wraâáw/¾~ýüÝ;ºmïößZk^oUl'º÷Žï45úèÐgûx²îP49DKÿr·éC µé^˜™ORÉèËA«9h5­æ€jZÍA«Ñ‹S{ÀÕCÌwtBéùôUóBúÈjïR´.DZ¡ö®$ã¹Þål?]Ço·ðIú_ã•­ð)+ý!›á·¯_?}õü«Ã× lÛWˆ+ÉmHéPº“nà¤Hå· EH7÷t~ +)ú»¡ø2Šw Òî¯hÎHû*N Mù:’3ˆIø>]Ø,)ýÐÜ”†Ì½dï¿g3>%e ”'¡;É–KVÜØØRmìëž µd¤%¬™¤` <}„ôŠlq­_|φUˆTÍÆŠ\¬ ƒyX5 +á„•à‰iX³D*( Pr3Lù÷f$Úàûÿïi¥ºÅxýI>Ÿ–ñBÃò|Aðüy•êO•ÏùSU‰béÐ;+fCÿ{©ëk™ŸÖç« +(g=~f(Õ Ì³y7cìJ‡ÑDnY¥Læï†6ôÊⵂ@¼Uþ¿@$ìES瑸âmþO³)ÐŒ{MéçÄ+cÈ¢$ƒ’üÙ¶ÙÑIܤN‘\T’ävÎ,½ˆHûÄñŒRŸ<˜$Í‘ôfø]Hk;a‘´‹,Ñ@ÖÃIû&MêžÅ³Ò6 )’`QnF ň2ɪH +ÉŒ•€Ú™Rs(m–(5ÔxÅm‰5ÖRx+SgEýQܳHb;§ß9uˆ9?=<ÒV¹Ç}Gö¥Ò¹ãÙŠJ%Y²4ì7 ¸’#2Žô¼ç£ÅÍz7[[×érnf½›<ÈSÆ~Íy—•X{®»­Ëx:R£ß=Ƈñeå6¾•ÿmd¬}Xgvé³WªQíÀ°¤—䎴c)ŠêØñK‰ðH¡G£šžçNCmO<.#xÚ½Î'„Ñ_;]º©ïsº¿ÿ„us®¹9¹ÕÀS΀Æ6¢ÇgT>‚Ëd$¦->e#Ÿœë³EIè–É 2Œ*³ie˜Wf#Ëljéæ*ËduQ»ËÚú¢y,Î=›ÅÖsœ}-+Óã5 íu(m˜á©³1fÙÚa4Mœ¦†SùíöYó0ùú]OÊ&)ØMY® S4\h€ž¢à”ÒS›;t¯à”eB¦hQ\ +ൃRΔ¢0 @Š€޲ é¡&;]n§@½R¾W@¸<,FÞ=âßó³|ÈC»ŸûÛeyßLùÏZáûÀ@k8аœ©íL­gqvÔö p8îÀn7ànÀ4u—óÆZµ%”¹Ê›kæªF]ÁFî×Yçêç+v²¬ÓºI,Ù†5 ”rÚrþ‰×U}®†r“­ø[ã„w´0§•‹¾ô+Ý2î¯ë ˆ9EʸÐáš,ÓGùžo«ô˓όº¬2ÐÍÙ¯ÖYèÆý¥•Üîš³êzrª}bª+©©æ²Lå ƒ†´É7ç‡ÛÞT±*;5}Y医Vö÷ž¼·Œ£VàB§÷ÝÿÝoçQ¬¸ü4Ý;Ýþ*É·–U¿Û×?_Ë„u5ÉUÏ„…§9¦a}ÛÃþ¾‡ãäˆÏ+Ï{÷¸/;—»»ár¿â|¿V”˜9Ï ëÔïÛ£à +Óßx]Vÿ(†} vÂÄåg[}œÂá/3ß³÷ÁØ›X›¸bç†ÙùýÌÌoñò-ßsï=ï^™o”qÿí;r÷¸ôàσ7ç)3èûÜùŸš%ß5w…÷}’ê>‚%sZ—])TÛG2åMmÒ¯ýŠÎí1Ÿ ²b¼&,4êðÔÿ÷¥ã ë6”¯'€Û§Û^¿IzR¸uj¸xŒÀdæqv• Ž=zâÙ“„pã^ùu¸uÚ7uf†_•Mþ¶ž¨m¹~kü.ñÚ>µÚËâ—.6¬˜sùÚí#Üîršå=ÒÚ-±kíÙ\T³ì·î­¹r[͵kj–ï»‘æÆ»7oâ}O,ÿ{ËM—õ²úõüüÛ„q‹¨sbnP'`¥gpò Õ`ä܃B)ÀËN¡GNÅL&¸¸° äúëA j|#U@È.­Ú9êóÈÆqa|\4Ôæ³30ÎGvUη])šÑN’Ê=ÄÆ¶I÷\‰t³½ájÜo5ßl•ó z¯ÕêùYñRdíÓ„“Ö3+Bƒðû;­¦³¥Ÿ+±1’F +°z)#Ó£ž–1Ázv<¬NÓ2%T\çEL8-æ+eæë˜æ ¬&¥N€2[­m>äß H¡yË÷+˜–ý>¿ºÁߪ¾‘ ãú&ý{Úƒ÷÷ìÔ!‰Nä9’æH–#IޤN‘ã,)!MŽIS¤LÙ±b,PsùÒ­åzsâÛ”låj'w¼KÅDNH1g†H¦ú~ahå &pÙ˜ðç v¥ìI½Ž ¡£‹ ïIÝ‘Mœ\ºú!j‡çÝh‡~XÝ3'{1c'*ÀÖó.t”™÷à¸YnìA’Âfÿñîkÿ]€ž¾gdíqðó¥ýoNÒYÁW¾òð¡ç?LºþeÒôuã&=~Ú{’µNò×ÝâÓ‚dš *Ã4²¾AëÚ5h3³­¸Ž\¯?Ó{Àúõgó®»¶ÿö·’mvÝ&Îý§??x?þîALÔZ’J¸Ù=ìæÃb.†`´™‚esÖ­EšíÐÞÌ›BÅ'+þ]'›ù\ᬠ+EW +¤“Æ%Žëææ[úÍ«L'ùøæ¬ÿë<ÿsbÿ9?_OÙ¿¹šãÒM›«t}ó-~cçÛZú¶V¾nÇÛ¦¡ß&ïYûTÛgíãÐŽ¥_ç1®ôè×zpɽ”Ííó ýŽeuÑÇyy™®šu·¦Ý ºjkÙ}¿7íJ^㿮ݯ¾¿$äúU!Ó•!ËÃéjy¿Ùûüp«¬ïùA`°ë÷ŒüÛÜf'æÀÕE@“™O#vrÖ™nê9>=nJ¢Sr]Ÿ£—]®\$É>GÆ×UHÎ* gŽsý + ¤þ´ý2•Тóý?G½ÿgù¾ €Nóí?ûûv·ÿ,g·9tV×ÿüðÛ–éPÇÉ|”ŒSd>?¦³c}ûÏ2¥÷ ûë6'Äöl¸rûÏrÍÁså xÿí?aöÈìöùÕ þ}ÛúÊmˆ?póÞÚ±Ëííù±{°'cè÷{­n÷B†)<ï2»¯\뵬rõ¬wÆf–­|ß·ò|×|™—DÙµ]¼ô»¼oãyk\Ýz ë%^‚þ^ïß¶§–Íö¯ïÚ^Þ5m]Þ¹}çŽ}—.Cì·í÷_ٵݯp“Îvëç³7®êZ]Ô…Š»g®lÔ›—tÝÚ£Ó]ú]oÐ-·ÞîΛ{s¹Ê‹?d_ÞØ“˸”ëƒöâµ=øQ;Q÷à{aïóò4Âùæõo_¼~÷âõ×OžLΟùË/¿¡¿xùËOß½{þöõÏ?y|úõw¯Ÿþtѹk'A8”p׺ãéß\-â‘ÿÿû¿¶¾ãûû¿ñ¯ÿ«}ü×Füë!~qø§6‡¯ä¿æPñ+þEª=<¶_Ö-­HøÊãü}úå5~…`\ÆZ7”¨æ`\•ˆW˪‹œ?ÈšZ MŠ ÖætøýSÂÕ™Ã/Ú?ÁÝ™\ìÁ“°;uÓ:šÇ|hÿ–œë¡Þ™P +ýLkówí‘|çJn˪u8Sëýû ï·.§6µÎÜEK3[)À5cÑûRÛ6Ïí;šúó]¬¦¬úŒü }ñõ.™æ¾{çù‰þý¹/!ß5îQz_bë›oþmsÛÌÎÜ•Ô$×%º’Ò]15Í]Iå.fCQ¶øúÜ“îªÉ­cm⛵™,§º/«iŽc¸óm õ9ŽöÎÊ”÷9õΧèú$o§^*Æ;Âõ©Î¶QJ}ª·Mëk)}žjí’N5º4OµvIçz;{s—tƵK:ãÛ¦·]šf\{¤3ŽÍ3®=Ò)ßLãÜ!L¼öG'~Óî˜øwQ +ϸ³w†ÞÕû»Øf“Wo•wÉæ»r]×Zw~üމzF_Í•»äÚdj® ­DmE§ï¸¶žhÈú3¡½m‰½ý}n§ÓЭ]ݼÌ3 ’ÓoâÞêAÒþ·qÞŽW”u~C{ÚÆí/hz›fõR¦w_è=U* êWÑ¥M§wïÒøcqë Sšö5VbqüŽu<¿O[rŵú´Zȵí!mGŸ_®?ƒÐ:úïS;†¾ô·D_·ï³}Ï’ïjqaõžJÓw(é.ÆÇï²?æ×,þÎ4)¿¿f±­‹mcöfðûüšý¼‚ÖÑÍtzÒ«@O·o£oIé3›¦2ï¾Ô¬¦þ(©[cÎ¥¯øþû´+”¦;G«Ð¥­Ì»/…;ÊV7žiëÌÐq¬uà÷¹NC_´tuó2ÛwœvŸv$í?6BűU”„ͤß7½M³z)Ó»¯_О*•„Fõ«èÒ¦Ó»w™v_д¯ºâûïÓ®è4ì­Cw–¶3ï>ía/ÐëÐß§v: }éo‰¾nßgûžóîÓ>*MßA×|ÿ}l‹NÂÎéU`gõf¦Ý§]ìÏàzúûh¦“Г^zº}}ËO!1­¤ö&©•è#É8ÙæCÆlÒ@ö…ÅOý¦s¨ñà!– ÚlۉܾœB$A«:âxÊÒÊ¡[1ͬ4íDŽM¥ð±w¢ýstS/²¿+ÖÄ«ò™ö¢ U2óh/Úü¦àÓ$¿Qèc•Í„ÐË41¬ú&Ué¬òKÑñ:Íjn‚^6ÊóºÑÏèm2wÊõymûÖÐzÒyÝ´¼U/æ9Öék¦9Öé,oÅë¹G:ËÚ#å¢}i—tƵKóŒkŸtηòõÜ'sí“Îù--ì·ËEô÷¦ðC{ÿQ€"9½ýîÛ?k=?ùåó¿ð e¿%EŸÕüï–•Š]ÁgõþI.M‘WµJn76î]}Ú5šî,ŽàU£ƒ¼ª}Trƒ¼i´RÅɎ9ï‡wWµJn7†&cزk´±˜”Ý®ÑA^Õ>*¹AÞ4êîR²ûámr±òm£ƒ¼ª}Trƒ¼iÔÞdu×hÓqB »FyUû¨äy³Oۚط=|“ƒMÞí¼A^mßQÉ òÔèùíò[îš4¸í>Dâs'ñщ]¶RŸÃ í ëÝ$ãëÛúHúƒú¤B`“. oÆÐ´€è¥çÿפr"b;X,Û§Û·*m%"Ò¡1¶xÒê‰]XÒå’JjM5#b{Ï5bu53±‰Ñ%a,ìb +š”#Žñ6òlÓOwëK!QŒˆñ® €•¾&òGHl@ït¡9é¾s—ELh*£¼×n ž ÖÖÞÏF[¥vVEx€ZE1;Ôž"zÒÖ® V*b5Fºgðd¤ðµˆîµv,´WÉÆ[¼ŸKÊî  +èvaaª²ua0mm½–ao¿WHÒô-y…Š'­|·½q{žIz=0½ÙKÒ]<\}÷g²»äžG²i.µû +‡,õ…lI%©d—Ð:Ý–)ˆœrb-­é—×k~)Iˆ ×Ýd¼&ƒ`*ëqž¥Ð¶öÉòˆ§ÙÆÃä­®oÙ·ŒtO—%¯UßD"ª÷Œn'Š•æª-õ†;¥9Gý÷dÇ´JB—nÙDº/†ÈÖ&]'Éz_DS­b²M‡>cëÛùùíZÛ†w‘sx›”S}‘JÚ¶²ì ªìÛ&ÖµÒ4 É|-D—‚©kgT`|¦! +d,HúÒmÀŸ\o—ôÛâm¿¡ÔM¬BAÉÖún0 ]½ué’« f‘Ó‘O¾3Ø}5b¡Ùò¾»ñQ«°¥Ï2¥¼‚ГSéK° jOK×t¹òn"i“Ží'¬c§xÐϱÏ}¦ƒÐÚÃŒßQ=ÝÑñÊä¬ë²-×B'i£FaBl²!ˆVÁÛÆ4!C`%2ì1L¶ +!¶ÕÅdáI':;mꈶֆ® +;§L£Äu2FI··(“‰†w/ +±“Aˆ¥¯§äÙÐö±pЦuYÔ*EŒë”1œi´3‹Ñƒ`’ΛæŒY¨6Á†'n=¥²U€¨Yv +MXáÜéîi´Èî%¢™ »§ª`FT_, ¢87bÀ~ ï "sm¿-U>ȼL·›®A_Ïêi‘íääÔƒ,— ŠD77îSœ³MìUmÄ  €ÏÙ&rŒs6ùÀAÂf48 «É•ˆ9@#£qéG`[ÎÙ jH0ýÄ!Õ):)HPŽ!C~3ù¾UÞÂVcÞÒ$¶‰¦MM5Ä6·q:°øû,H±t ‚UÚvôÆwx%ïGà,CC#•uÞœ­P/b_ù­:öÆAšqÞ‹°ÆX+© û ÎçÜ…™Úe¨€|Ÿ²ì@²¬ ÛGÓ°nÏZA`]ª‘MS«Ð{¹Öb…ÙÑr©`œÚâx Õk F=•B‘ªÐ”á¦"Yt›îGê‹›;…9ò+ÈÉxØ×­é²;x¸s@ÈÈ“âË£ÁM© ½ö€3rÊ‚GÙýÂú@¶©è÷…ѵ·0Öêµ9®2g™Or™Gl‰V©Jm1ù§ïJý:{¸ÿ&{w¸ú®ë[*Eæ•mÐØÊü +dÊõ ïI´#"éý\I€\/Ë´ÉKèíXÒÜu™Í,HƪVT½ÉZ+VH¡]*hd±}гEÖ ^¬FVL…È[YˆÄ(ž¡—ÕiÕ˜•,#~™œ26 ûm ’Õãu?4X8pçncæ÷h‡E kžDôý2|´ü'VôT=9i3ÁžÐôA²ØÏB ¨–Kd‘ü©ZÂH­xP±)ÄD,SˆQW)Ÿ.Î +™]Bl*¹î Rþvý}u ¼Júmó²òû +Ç©Ž'Éw'/5³%Q?‚Ç:ûI +öÙzÐ`Ôï˜[·Šl>Ò\…eŠÏ8=Ê› Ó™Žm²òTÐ4 b“jñ²Ñà´h¯äè¾]¦òj¼úžÀÒ6SL¨ƒå¶W '2ñ’V« /¢ŒÓ"DŒxÛg9Aug?—ìaRR2+éRD˜È⛣e{I«Õå +ÞNd·Û³l‹‘ZÛ C‘ZÀ"'¯ +JT0rPaŒ>k ñìQ„y¶a¶jNÜZ¥H‰àÉÈm†¬ÏÝ*å OGpÙj÷„5QkXÄ"å2"É·"¢°"d!'Ò›<Èß¿è5š\å3´^j—OH’ƒk·_á3>ó¬$m,˳¤>T­ÀÊ"iÄLâ^L‰î)«ŽjD¢Í$œÀ‚¶°qR}( I@ñêÏi癪F”לÄbŲrù"ˆIÌ;$«vI1©™‹Œúh[Z®BÖÎÖVÉ,.ÄÒ·U#WÙmÔ¯ Y‘´O "[àÐ++JRfŒ·~_©±›—ÈíbE!»Õv›šÌë™zj»u{7./û€µM +!¼]Md*uêLNªLzX€·BÞÎÝ»>0´p¾kå‰à5œšýɰ®àަ™&¡ªœAB%““¬2Vg ‹LÝÀ)–LRØˈ-W±ú;º¨R­Ví ƒ†Êç‚mâÅGý"^7˜d]’!›BŒ¡ÀORÉì‹W0 +ÏS'™fĴɨQ9xŠQ3(iUÔE2 áe‰Ó÷Ö³,HRtdЩ8i +›Æäíqô³¶Ÿu)´Î7EIȾfJâ—']Õzí;äV9ro“IèÁ»7¶j3c§*‰OâI&É͸a. +±¨hím3ôÊþRàrX¬:`iW­)€O;àÄB’5F·fŃѫw‡¬EIÍ<Œ"¸Û~¬ZñÔÌDÚxó$ü§dTÐgsWÅšþUÀÛº„9†FZmžªWGY”BLE­LúF FÃ<Ú¼IU-îp¼–õÙ^kR!Œˆ¤*tËoÊðæGe¼I-–do½‚`D9‚ܬ‡AÛ÷0y’pDE6ÆA!'=Î,jmDJ«RåY¹Š¥ŒàÊbÛ × ¦Å“ñÐiŒ~¿I=âðáÎz=ÿcVÿ‚SÉ?É^d"»ÀÏàV6 Øþ¥D;ÿ¢Èë³&¨ðƒIÕ§ARì˜Y„=jñx6é1E*•7 SÆU¬ +ä +!™Z¥n5ë6‹¹FA=½VÞ’ÒUbf{ˆ¡=›^9E̲âóéÜÃZBCá:Üà×L–CìV”Ó¶¸rñê+Þâ šº8†Û4n2¢®‰´Qu¸¨74H…M£kžíNéœnöåõÅ¿‘ûêÔ5ßzµ:†MùDSì¦Ò Á\»QÔ|ÚÊPèYÖ=#˜œmtéþb/&“Ùˆ.pVŸeX*º*ÚA#²ß\WŒOzp -î þ0rÈ2$} GaÂîV¡EßmÖ¬=(ÑÎÄ>Ü"0D@H ®¤…¨®cVeªžp$nëaࢌ)H÷ùˆˆºÐ!'²}„½¸§‰]gr•õ<†ª­Ó)𢽒k{¸Óèä2pÀ¦IRsÌpXï”Ór#ºè¡e=ÚO!¨ŸšnŒ]E#7¯H1‰ì¡Ã͆™b-X'b?Lª‡Ó¶©ƒº­¯ú²¹vñ°dp¯Òòôf°îùîÀ¦‘Á¶#ÀBTwlöÄS³óƒ}âäqó³jæ.rÃåN›ÆÚ]€g;£°E½E¬KWc)àß±„¾=Û! +¸j&$ñr„vZÊ–#SNLWOU‡vZÚ°Y21wb“ot¸•W†>5¬~[Øy™H· Ãb ¾Ÿ½°#Mb! Œˆ¸$îh­dZ°0Û² ò’B¾ÌÐr«Ú£È8 ­ˆÝ“¾âèpG˜ñk×uÈ#3ÙxcQfÛ” 4•JgäÖ +0¬ÅØ>Dd`\Ðõ-ÁD4à†˜8 ¡|Ì}^^%þÚVŽ«¶í;‹oÌ â¡uº¼ãÖá"u¯óº12±•$­ø"›tË“‹-VcL:ü$I8•²DABh’%‹ãK7‚…߃7˜Ž7ÇNˆÛ$¥~Ò'Á¤ "ñæ7"Ë—}¼!u+·Oy¢â)€$QA+tð&?Ûú A´ÖŽ/±¾8ÝMÐÁV‰Çɱ"ƒ C!¹˜T º8É’ìܰçÙXà™äv'ö<ÒY’Q a­¡"ñ·K=ü䧇ßÿohqíq/Љe¶¥ÀuXËÈRÃà¦0^´OÞ¯ÅY h&-Ž$?æè„¹ °±´Æ,È*‚íJ‡“,s¿­[R)*0^1C]‘ 6mñbM|ÒŽ@i½=ð‚ abª¦ð0äCiR¹ +¶½@5€±Ü$Ã\Gf“"丽3 7°(1ìB$a2 +PKÝÄjƒO¯åuÌ„eÒ©IÝñN %§4`È/Bñ‚Ý6#†WÂMQÈ/Žrø',a£3ˆ&ÁÒP€x$Ñ`wÊ]vâœb—lð“\K"-À­Ñ­×Ê5LA_[ó +vn»*?sîg°´—þ¬ YÁcú/ìàØ‡Ú‹wBMõ°KRǰYCw¥ø¾õâÖ6g=‘„ eZÔ¯ºÙb0­äú@N¿Ø O"ŒLÍr²HµÚ^Wb{Z+ðªŠ%'^vñÅa†DJ0vH +c…߈¬^APk†7ÝÝÑÎf.LDQYÈÕj§ +ªX£é…m—ñDŠZð©ã¼£QI‘&:ÙªA€Uû— SÒ8á:|¾c¼²LXéO‚Ð$Næ.j¬'ÉÎ}h&‹˜SïjMî߯Ö#ù©5À¨¥  š\±TÐ$t«½ARÐCr0Þ{µÐx§Žlfñ_x™+¶÷Àb[Ý&ž,‚1hí «íˆ Z†V£õ)‡=ѪœÔ¥O¢¢IºŽ› ‡Nƹ(b9Îä¾ è3Ó ú¬Ãð±½MH±!€pHSòßcwê‚e±¥4 ‘ì“l߯mtA.jdŽªvÓzW‰Œ®Âã †éA–$YÚ8t©¯5ꨠwÓ×°ë'i?X‡é‰χKv®%PDÒÚƒ1¢ñu- ÐI–#œºy˜b2™TÑ¢Ñbh 3ªÕgáo^Ρg*‡ÄÏEÕX™Iw±‡r×ãÐ#uªÛAe‰I_*bá&Gqñ*d&Ø_£@«µÁy¨„!ª§u´.d1B +Q§t°Ua j@dšh:Ô“²*Ý9=-¿ŸÕöC»2ªÙœg,<ÝÃîwöÝîOnÀ_u:ü•ú¬ܵÛƒE*º–ôxœA^Kõ~¨4Kñ +ðf:2«0—¬²h3ºÊ± +G‹v`-‘qiò<”%Å ß j-exÃMP4èˆz‰Ô)hgsà Óò,9dP€ûª¢byÇ‚qVDBZ¸¢^3¸ÒHH ZUöT–$Ð Át#½â¡¬‘l£$r©ðeÌ&Û(î”pn°5ÐáRíoà‹˜çÓPEu)䥊HIyVBÀ2.>Ù#8sJªU·<œÿ‚YLÖ©¦ n8DÝÈx¨JF±;䯍¹DþÄXq@¨XÆ ƒ„H]\Ùk,a¬ÃÊ|Ã`|uàVú“<(6’—|àŸ‹ïV‡Y¤é&g§QÄ6:|¦V^F'­(XˆÌ)8_³åÌð#ÆnT â4W¢íÄÎ{Ì$½› ‘TÌ¢½-ŒJóêô]’2HFNnZl•®µb6>0{ŽzLh“…3VÔ¼ë`!AÖ] Ü?m׳R7¡x«vSú¼Çȹ*€N½„œëDµlµ±zÓ75´ôûr5x«œ5Ô~)*JÊøz§ÆPšy…œxÚ…´ãɦ¦èLžy%“À§MÎ<`P*´Q8Z£Êþ>ÐtgHwd‘>DõW“eÆiì„'–%DVK…ÔÓJŽH±é³ÖœmFî˜Âá.˜"ߣ ùÒƒ$+†Üë“Âj‰LÒ¿’½OÐvز¡d¸Â°DÌž"0´”Ú¦¨ìÅ>(é©ò~FvH=‚¬–e#ñ)BFà‡TBLBŸYF‚ReÆÆ_AoAÖAéDU±”ÆdÕG“Wƒ«ˆJεÇF‹ÉaDÅùn°Ô8Hž:61>a| u*Z x-c½#,x ð2@<«uIÁJ9*ü9 ª +RQG‚Í.Ü)B˜Ï‹ž7¦£©ˆì,lUœTªõ*`’ã8iTF`Bk)ÌXWÙvˆ8 +¡ŸöÃc¢ú3ì›jÔíX²œr®J@‚q§”dNm’ì”ØôBÉÛ^xé<âÈÏ“æ3‘kPEU}ž4MÆj”$fEÊÃézº]/@Þö¬G]ݧ¾&‘z8>u-©ïX­Æü#õЕ±yÛ‹"O}iÐñLvU5~!‘¸ZÅ©¢p OrÒ7¯N_'òãŽÌ6=%ûìì{ÈVâQ·•Ü SÌš’½W8ÍšÔè—Fü*±L[•òF "yÂ"Y#ÆY:¨«ÖHì¯û¾ ²‘Ðf%‡äA† Ž|H–±Å….[¼ÉüÎ ‡,¢†)…¬¥dç‘ð<"sÈ¥´ÄÞ«nB%gR#G†*8%(qeª.rDõ]‡Žw=W¡($€“0zÁöoKJŸ ‡™ÙxÍD6µ©ù +™­ˆ\4ÿ!) +{s$A™Fã“Ïx%§²î%P›@•êÁì1eì¢Whï*jz„lîFPÓ. ¾0³ÊuŠügȺ†Õ°!RÉÓZp;¦Ê¡£aNlan§5GÈi/¸AAîÕ$\Þ² +\¬€tÕëÄòÉaõnd+à,ÉcË5††b~ºKDÉCH™–CqF2œ'¨ìÇyZêd•!×Oq¢Ê¡Hé¹ë…zÆzj#z A.ðš®Ó¦·òA\Çz˜ b š¯B*I°D1üôÄ P*‘ÍÀ˜ì´%®Ú²ˆÂù8`Lûà$P£DQútH”?ÕP½ë³•Øg̃lÊ_òÝX–s*¹Cèƒ$£bZõ>M S&¹½Œõµï @KJ°¬nd$»,e¤Í¡cG’|4vpLtD)Ì× Qøa÷«6E0ŒÉ`Z ލ2Ð5~ù0›`NÒIPà£CGâ +ŽûÒÏ-© Üõì)ƒªb F{J)ÌÔtô“ Jhô}ÇLUI.TÕzÂÐŽ&ŠgY±l;RˆšÓ°:o5F a€‚X²U„pø«Â]•LQ?ÊsçÀÙuÛ<È׃Ä-誈¯ÄF»Å†^…g”s©c€8–µ³¢ÛËhæ\›.«l PÏÉöŒQ’‰Ò6aïÄÏ ƒƒØºc„7õy2ÜáÔMœc·ºÛŠzŒÛn¨ 'QÍñˆø.ò‰SœÓNÔV'¹*H†¥lêËàwçulËsXª‰ãEQ©¡ ÎTGÕÞ)ÚÔŠN„Å'ÅËV+‘JÚ$L$iÇ׎æÐX)=®ŠT1·㜆µ× “(3¾Ê˜ÝJ=®Š•î̱j­V­³”¡O²Òͽ2š£TqwO¿0·RG˜žŸN,ƒ *+ŸMft©5JéŽF£ºYIÂËÑSÁø•(BJÇ*X EÐ¥ìPî8^3gݶ;äLE|j…bN‡9òê Ò©W»#Á`£ro AHSVHûä;%¢Øã){[Q©“dÒÊ5>VqÑ<  +L%¡YÖE*mdú¬Ýb4Àÿ”s ðèÔó˜Ð³êì&¢&ó´¡ç¤¨²’÷ô…t%4k®'¦ÒT:v€e©ù¢©MéÍgqùP¥ «mAA$;ònëNÙQ+òn$Ní¢‰†+P…µ‹§N9´€Ãá̈ÝY­A­#Ùa{Õ.DpŸðùIDIŸÂ8ñªÓª‹}YE‰ÚàØ^§ø Á:´‚´}ßÓÉÜý ªmq挄41«‘œl_Pº%tAjœX̲B޹ç 8(Ùº"Ìì! +êød¨¸ö³§aëÙ9IfO˺W½#Ï Êk\â§™‹h§ Ëtç¯# M—û u:µþ†ž‡ ÉÔ£}(½ˆ#„Þ:¸ÀåƒåI/ÐCF†‡þ}DäsÊ}Ò&Űh˜" sÀX‡QnZÑÃê`¢³Mtlj¿ØžêùÅí陼4òX9ͤ‚xDŽ8«š¨Ñ“ê~U–(>šDZRŒPÔÌç' gL+˜l¾TìŒ59GÉb¤±Fazh)91’ÆÒ)¼„p§¦Ç¬UÝÞäDŠà¿:Ö“}ÒR¯…ó‹oµ(Ú’Ö9rx¾ßAWl×ÿÃ"d‹¬•½³OÄò*,Š&¯ +ßjDU4u¤²>ƒ=×ÇËrlÅwÿwDøí8g# äœ×G*ð“½{a­¸)dÛÄàiä¢fn3 +†!‡…à^([•Ä3n„T9ø…ˆt )Pº+ µ#Ú|O‡AËhëË=«Ñ½ì]ôuâª*R´m ªQ÷©m_Rõ8e({sØ3+D蔄îÖÓ1žYÊç½Õìµ(Š\_ÞiV…ØqÈäoƱe%å¦|?«£•vH†{‘VœæPAä^p*Ñû-k87­n£^.Øë¦x0À䨣d€0}'' Œâ œY%¤&H&0ãŒD(ª‘1Šmœ®sRWd-±' spWXù×Ýòä%ý2jo`¥&)JpØga‹C_9&§;b½B ™¬þ]ðDª"õÁ¶ÈY5²µyRý•XûÑI.‰µUPv—œÇä¥FòïzNʹc“ópîØA2]JÀa`š‚’l’Ígd±ž}o„ù'ôpàPÕÍyÞŠæ\¦V§àÍ!{ÁÊW£:è© +dEˆ±¸ ÔØ¾¿š Þ­j +ÊžÒ€|ÛNO§á¾#ï>ð”ÕÐh.&# _ÉW¨É×Èü4Ø·£ù9ÔÎÙ‰¬/‚0ªBe¨.¨ˆ¼ÀWû‹!WÑwèhÖo*Ö® ’ + +-<‰@bø u;BAY¹&cwÈsf³¬Y®5«h••9,ÖÙ)Û¡jV®I +þ‘toÔ5DâÒ‰'QâJ@’ÃÞÙ²«w7=ç’ÞÍW;2‹;êb‹)©NµˆÀ ýÆDàO¾8T{¾Þšv#kܺ=º/‰ ¢zpK©’C‡î çí¾9ô#v“èªyöêRœRÝ ÏîëÖF;¤lÝè ϵ“„*¹Än‘WîêÖF{¾šu£ƒ¼ªÝi®ñ[äU£»º{ŽÅX® ï ¯jï¦[äU£»ºµÑ¨6Íu£ƒ¼ªr”÷‘WîêF£’aÅJPÆ«NIïêŽC±ÖQ´BŽš'Q¯1#–Ÿ\„^ì¦óAæ¤U&õzòÆAaUÍ7Àçr„²¨Óøª#àíytÛÏþ4«cN­&ý/â"  -ä­Z½²fã2*v 9krÄá£UòÆuËX\ͬ>UrƒÜ+ùÓº6hf¢ÙõtË£šÛd®äñzÝûF)¡†^p6‘³èJÛÚoQɶÑNÞ6Š\½ÛF“\´·­ý•líäm£Q‚ÿ¶F [ÜÖ~“Ì•líäm£A”‚m£^Dmí7ȨdÛh'ou#¬sEuYytì>fÁ‘[‡pqœpÚ95NBXA­ç«miÈŸç“W¯KI]Sƒ¼:¦F%7Ƚi”¬5î-bß6:ÈF¼o¤"‘p¤sÚ¨)W£Ó»¾Å´oNJ (§Km)±AwEÜAâ¢É/ ÑÚ^¡—9‰“óÉ ¢`7Útñ²é ¯ïЊ,¨©ØÛÔ–˜(ó^LŠ·Øt÷ÜS„ijq †yµ¦’µ˜NQhr7+ ÑWyä7OÈçËG6g Q¼>È´‰ú¥*`ü$²Ñà£ôÙndÞv÷¼ÝÕKLÁ«õ¶RòãŽÌXë->ç&•l¥È”ÐÛô¥¦AÕ&ƒþˆ +à*/ù ×I¿htÎÊuCçMŸIaV?ðÔ‡díĶËF"U´ÏŠYk©ÙÀÃ`%³y5Ý6ùÏÙÎZ­>ïºÁämïzs›î‘Ë…²&÷iì(&?ö‘Î@ŒLöV„ –ڳ̓ÅIpäb"kÊyד¯÷bß=Ü>«Ç. uäqMÝš‹d%äü±UM¬þ+€llû1#9öÍ~´ý [®_AIµ‹ €Ü|ùLɛ†ælåÄ1<‘=4Õª™3#Ã:©„Óô¬‘–=Û¯feèÛ|כ驴åþ.ŠîGž ¾Á&ãðãðOuú±›@0-îµ …r&’#ŽmŸ|‰Åë*tЉFeËé|Ÿˆ'LÔßl{:Rr)'¯çon F=ilaRWlLî(*ú¹R܈§6ïìè:0Åp¸A%49–ÑøÄ˜Öª8‡^oKmšEª5¼w‡¤‘æ…\&Á@'T«vÀˆ9c€ìWA¦ßIP¡’¡½ü¸tL¿—š9SÏüN:Gˉ—oeª¤ëø§Z_Ý÷+¡“õ‚œ’õl ?#]>˜Ô’Ê‘#&4Ècì¦^Ü ¯m|c¿œ©©ï÷ð‘]µ{p°ˆ#ùâ%–ÎUºZòŒ +"RÖ +_=[áºáËÕ¯0Å1î:¦=îé“VkÔgM{CÜ0 Þ„z«f§gV&DÜlB«™ çeKžFnלöÃ)„;áž=G×[Àl¿3ˆü”I#Ë4¼ÉS¶‡Õ2?ïêú±knôC õÕ~nKWý|¿5” +м׫bH¦J +€ñ”Ä6h\ø­¨1Ä2Ö.¥ÂA­2DbBN +¾èÝ#òàönCìŠÞ5ÎÊ]Gy !iî¹Uô>Pö+²9UÏ‘¯éHC™}¬ rz"žÚ!8ýò¥jûeSEúmj€Fï¤Írs`¿ÓÆõHõÒ¡A|•“BJ8œó,'ÎèÝ׈Ää\MÑê.GÄ]–Ë0°š²j÷ñRD 'ЉºcTC@ÞìuºT×"O¥(¼‹Þo„°Oê +ؓؿO9ú]yzcWd‡AS¿G¯t€~†Ñ·:˜³r|ðkiþWºýW~•éî4{w8I4D‹R×õGÂP;Sd¸MOo»OãÛx¨™³‰a9ð3Áv<èZCϼNGÀ—ƒÑ+ž¨R8u%# -zSt%Ó …ò +¹}ýHÇYÄcÓÏ&A3t‡[R¼Q¨÷ 0Šgd檛™3Ë+ ¹¯ïÒvªýqm‰ãÍÕ Í‡W kß3`jœè¢f½Ö;Ó eÛÓáMäÕy7š»AF/p RwUû6+ÅÒ½Ž¸Kã:VJã‘û³È-êiÑõÕ3’`Ìä)(xnî™{¡ÝË’eõ\Ö ëjy·<*¹A^³(×Óö®-jT]7:È«ÚÛ‹{ï)‘«vQµ(‹å\™:adÓž¯÷íQn ÿíòÇŸ—?^^uzûÝ·þâé»wÏß¾âéù×/^¯È?ùíë×O_=ÿêຘñý÷û¿òý æ`†ËïÿÖ~ù_íÿ6Ò_áð‹Ã?ý³9|EÏþzyÒD÷¦i|uޝ jCÔ4ŠBnˬS×T%Ðãôýk´þí×èÔ¯ +ÈCþÐèäíÃx ÀûJ¶ºh1“&2xDRÆŒp×(<ÞÉÉFH¼€3—oK¨"Q>x!Ž>HÆG&zKȃx]&G+‡B?‚+ÏF¯31âB”¼§ò,ÉÀò¬ X ¢oÑ_ÉÏÄD܉ãÈu< .EèUÍF6yaÄïH½ˆà’M«Ë‘¯M1V¨ŽÎj!:›1’kt??ï;ÝÛ  ÉJ[¤¿J8ÜùZ¿F‡MÊ8†™ÿ*[¾ÊdƆŽbÈë<ÜA^T3Ù`ÌP¨7I;·iN#É^y§³SÏð@£•<«ƒ_¸Ûˆ~)1)ÉYÃ=T!Wì1J/’eä)®!Åg úe +¨¶?iEÒtF¶³Ô Ë™~¹ªtËðÐ8+—û ±ˆÈáôk®•G«‘øÞ=*S¶h*dQ¥èë²k‘8*¼Y.e«@Wv­çƹ̰ãP-%zÇ’˜ÃøJ^zÒƒY^)œÕKoCp2(ñ1€”"'‚*¬1ûÁ™®ÜN$£âµý(™\ØJ9½ðµ:Už”Ô8LÔŽŠ9C›79¨‘ƒã¹™håqNBAÉÝÍÆ†ÂwÞz<‹,(ò´¬Ag»8AÏbÕ«ÂÄ 2½V¶©×«Ó-àNžˆ½Îvñ”ˆIHŒ¹ê_aÍY¹ýà“]JAû’*Äv2V]YF›²‰h˨®+`bYA²Yˆ¦+Ø)pdÀÍc·Aa¥7(UW{Œú,#@˜(¿k6u~ŒßíT}'rÀd¬?NEÇHuXvaF:‘u9: S¨ïÏQrBô……sêß’+Dá²T«® NM¢SÀ÷ + +‘÷¸´eÅ\¥—_Êð×8*€¢ç` bVÊ‘ Ž(©ï ÒµU¼-Î:i‹Y^€²À ,ãä‹Rbð£^戆iª>[œœ¬)\¥ãÄP´‚b+*`䟓¬+;d‚P'T‰!ËQÑÖÔ3Ί½D,ýü¤ÚÔÈ“•´u¾q ­KÝä.4` j[¡ù¡ÐãÑ—E²Bu©›;0…„_Ñê#€–<åmg +I$kjÇàü$PI†lb_gjØ)‚ÄÔ +  d‘Û(= ÓBÓ±qÂÕòÌ•ÖÜò¥©J2«…î£VˆÐ†«…áìBÌ>âY³ö‚Þä¨dAÚ ÑéºÖ´o_åŒ*·ÜÉÛ‘ãû•’…;™1a f,y¾ u÷¤u©OcN «»ê*$”f¶Zœ87¦bý,Œ\«öõpµûú^È'f‚d†À§F¶Ø-Iœu*A=–Äó"" ¥*ðÔ"Pj»œÅ 9ÐrÔÈ(t âL$°îo5GIãwõ†l¨&ê$/¡ï–q ädÙˆ³Ì²A”ÍÇĪã°FAYÇâJ <ªÖ’’º¹é— +‘Il¶)dt¸ #dûû£>-ü‚M )#a·kw¸ZíÐÕFD)xj +²ëòéÐoÙ6Ä+¨Ë|Ch#¢ú'ˆ¦‚ àöKWþÙ4]õ„HÆye®€hÐãÒs4¹í Œ”æº<Ü„÷ï6ðh:ŽT¦/ÝÐUp?‘ðBؘŠp:å…a¼YÅÖ¦lU9DÛºyU“º–2RÛ-ˆ.Šs‚j­ªñôj†ˆÁ? &íjâD4Ê£¯@žâÕ›ôYµf‘!Éé Zï”èì8q<\]”ðPN/¨ž@##*°yb]¡Ô¨œ·*§Jh,!eŠðXZÈTÐÅ4’¤¢¸‡PL0£ÚmarÄSîÝAä¤bº@9¼‡''Y£JA€v N]V†;ÖW¡’ éò¬s*~¶h]’êfë=ö¹`'Ù|jä” Bvý8¬fí²L£¦¸äjU* P¯ûĨɛÂsÔtÇ,DJ·`Žâ T«®ù.XÓ1[‚®O+É¡ë~%uâêœ>k"é¬"8^ ‹µCPØ#È ]AظUbèo½®÷,€£'b€ò€>Ÿ^)Ù¨ƒºí~R²aEõo»b:Qìfvdnr‡hGˆXÝıï$a½Í‹=HÃ|K™€D|7µßxÂä*b/‘0T…A‰ršìëÕ2 “%$Ž U%&“Dn}ÄÓFÌ5ì¦@ÝFQ&&ã–kõ¢Aº9ÛÈý'¯” Çô%!CÊ4}ÿF|¬²ŃBD¤É¸Öäº{Y€0¨òMàÐ@ `§æÔa:ÌÂ#‰Ô}‚¼,Îøvp +2°5õgÅòID•¸i~L04p!ÃÓBdÙ…DŽ}ä½UÇÛTA„QŒgh9Œæ ¢(ÍDt)Þšº«ÃHR’AÙä~£ÅùJÉ½š€ƒ’)Y«D(DLâ•Ëê2G½Ç⢨†tÚÃÕè`& g¸Ä`±YX#‘!öPò'²€ +±ˆÑ™ˆQ]ͼàÏ×ëÅXPZŠ˜á´Ý/GÙ>?gJÏNÎdÈ9ú©ê¦XH8.wõžûà§¾¸XßÑÁ‡UÒTaF bÅIJ5!ÁY%û‚}ªJÓŽYA„ë*7Íë67jضN®ï|2üùJQ±É‰ÝD"–;Ú”=!ªï³<|Ö +º +¬j,TµÊƒwì{0º¶©Y»%ˆ5ZtĈO2ÚF»ÆØy–D­µëñ0Ç\{7™NJ«oñ4G!½Rr4J†Ÿˆ¢‰YDx±í‰HiÿΨ Ào:L® È÷¸_íÁ¹oªŒdÊÑ-`:R˜G剿‰Õ¬0Ó‘êYÐ #^ Q·­¹Ò½Õ†N™Ú+Pà›é'ƒH3Îf¨Éô}ïpiÔ™2dE\mƒÝjJ°ÝÖD•váÞvœ!{ 7Q¯JˆFLPì3RdOê<Ñ"ÿ¼Pýì|PrFT b )ýâ;Újð¶,ZÉÌ$§Ho²ž‡œoPË):ޤ÷ ¢¨QgUÇ:³²¬³˜($9ÞŒYT »‘›Äõ„¢Ët× ë¬ Ä(Ò Éç8àxÑ~Y«ÐðÒM¯–¯¾Ö±Oyé>( ªÞwð:ˆ~¸œ4¾ xK[I‰ëU?¶C >NÒ Û¡€±¤~¥˜áÖ@^W z)JÔ…W;‚@‘iôxÓÜ$*à¨'ç—rH"WU¡‚ɹ‹ +Cm—D LªÚk÷õ–`$÷¬¾êT±: Uù) pLÍ:Õ@Òmˆ«ZZÆ(í¹K_u²ÚœbÀ\7W¹HĬv†îEæGÕþ1?:ˆs¥«hÏ8bÒb$ÍõU'—.šB£&bUh„ØÄÒ0\!èaÀ‰òsMÅ•Êwx±º6÷=˜Mí“ø8“Åè”fð BO®½òîëÓ³'èþöÞt9ÎãH¾Þþ8Âþâ®}ý²hÏÄœ€—°l'NœP@ (aL:$(Ysõ_åòdÐØàHd7Ôá˜1œ¬®·Ö¬\Ÿ³Ùæç!ejŒ~”¸«¢/‘;ÞX˜Ã¿=¨LÀ±¿iÛP¥ñ'wšú`¿¿÷- "ÚÓ¢±èW“OAV{TQÌU!’ÍLˆjÜ'¢ZMŠU}¸_Hnj\ä@h°”:Ü¡™t o£ºÃ5«,ï4Ä3’úõ)pÍÙ‡‡ÐBuñÅ‚÷Øi‰°Ÿ;WÆNDBë€Q&X¸·²%jrŠbE?CÛÉ’¢]/ TiïÝYQ¶+ŒE”kCÞ‹+%s¤‹HiæU$»‡DÔ¸DÒ™àþ r¬Ú/¢e¹9@IKWÑ{#¬s›ƒy ˆÁÿÀ2Ï•’›Ð$CX(š2’(ýѨW!ä¹þ2)ʳpÎ߇}O¨œ±2./j«ñ +ɤÁTÌ#šL³OZŽÙ2{1ô%ôAA$Ñ<‰=eYˆÅÄ.dŒŸW4Ô‹¦‘_Bœ3eW'–Û F²˜`¨>þT‹XÀèQÉÈ‚¡í*"9…wB¦^=«ÖÅõÐÞ*Y¢þˆìÐ!¨ÔoL AU"w¤ò×tKÎÈb"|mIR±žs*=EÁ` yL\“ ¢½e—fS¢„Üñ¾f¬Ð`ßÈb¹"´­¹ƒŒ-g’bN3åh bDÜæb`bòÜp±72¾q’<:‚Gò”ýj/ÉɾhËb[À‰SBœzº:ˆœ(c¼žs¼¸­ÆPÍ:h°&K™‘ p“1p+¢ŽX‹2-¯²ëù3!Çn'{ØÖ#Ï&%BznÕö -ú³³S%#UwL&K]ˆßƒÓP2Ô‰õHöËQOhYl›n#}ÃäD.–*/v—åV,*Zª¢ýC´­ò¼!S–ËJuˆå’Jl`ê‰V²¨¢ßeJ!dZ¡øû‚W1&çŠx‘²™XÌ™’ÍDÜÂhBªô‹vÀ•[±ÖICOذ"{  Œ 1cv2ô«E]Ï…z+‚°~¡5@heÛ@×€@CMqš¹Ï‚òx& %œØ&ŽkÐÔ³äÎISDÃÄy3­äÁçgt‹ì亨×Ók@Ÿ|¾ÃŸÚ4AÈâ÷j™"b1kjPœ&î¥ÃB)_îh0d¶ÅRw´®:7ÌzÔ¼…i09H9í 5N•é@ûð +>"\«òIzf¥ÝÝÆ3n‹ aõž*Ù[¤jSXÁl’n%üŒÁ™ÊäæQ‚æúéËQ ™Å›6X¬·<=ê-¡“¡GÎPz´¡\Ô;2È-uɃîÝø&ðÒøKlü”i%“¶ºiJ^¬§Jîʉ¼Fùr^ÑÒ‚º˜˜TT"T ßÚí|kT'}¬ãÖ9ÌÕÙédé?‡néUÖ@")pi±¸]‹¬³r1a22’F›÷)b«ì(Ĭд_¥Ój96ºa§JžûÀðnJÔ°TN(’^C\+˜7·„çT# o@ lŸŸ‡ëBÂ6`þâ5ÈJäD¬@G0ˆÅŽRú¤¹ Rá÷3w¥W“„)XÖå^Mqp›¨_Ò†9Pg¯‹P5õ}#dÆK·\yLÁgàölùb´Þ*Ê®˜‚Ή‰}‘Yý’šÖÉ´n fšуÅw8ñé QÊ®#ŸU…Å™áy¨$3¹Ý™3¸ËÞHœ^¢dd–ugŒÎY õ*a¶^‘9ÑA,` %@Ss aTÈwjÝ0Â(~'Îß#aÚY Rë& {±²¨^Y ¬Ñ ©­ü&À_ëÆ¼ÝÜÖMs’akhÊ*‘McVÔ?äRsH_€¹z:P¤CŠKbÖjmA‹ËjÖ¢Ê2ò4:Í]"¯æyEë0dÀà!d¡U©ù.°ìö +x0§É°p q [ ûU=iXkÍs­&9Ä4˯JÚUaK\‚­4'–¤uËë–³˜OÂc¥j[ø(µ5*-»ßÅ¥dªì+0?Š@DK[á‘dàÉà +Þ~Ýó:…©£_k»ªÐjË€¼dhãç©#l—ƒ6N• ëŒ>ö÷MTtIœa‹B£‹ à Åò1fÒÄx§ËCjŒ¾• IV‘(<U,¶OŸ ÛˆB%i§vÚàa ¨ +Wë5¬T Œq­…I–"w3Rí½¦[RXdUd0!¿¯#4ß;ÛC2Ì:œ¬n¬›Œ¸¡îEƒþËC.7#¥9$ÿ*`O¶OërÀ_kÙò|UÇX.Á’ëìÖŒQJýÊv»":˜I¥wØ·5­†vÁQÈì~ŠT¥ºÂFDGE† ¿‚êr˜gæ9h Y9\ÌQˆ*Ññ@*]„É_CnW†S†Æ«‘*\JÇÆ]†ô8ÒˆRCV˜¥WfBÜÿx%lž7ùµìpPx›H#ÀH[Ѫž&Þ/ŽG-ÓÏ„bëÖVÑë²ë¦eø¶ä“ØJ>E õbñ¿Œ¢/Ä{Ð~ð²'‰Wâ 0 †ßßÃ.šŸý>ˆø+å‚\ao©+tSÂ÷!þsr^Ÿ[¨Ñ\†;éÊÎücê˜ ø½´’·dcg¯Q›ç‰áYòBHÂôIuŠÐ†…‰†‰g‰¦Ð,®™é‘¼¶â’rTtBqj´«†#W©ê W˜;©W/ù{:.* Þg2£ÿý©i¼zdf“Þ¨BAõˆ¡–Ѳ¨-ÔŸÐÚŒ×P¼$"O˜5/®N ¾ÌKcÍݪZ¥M³Ó©©a™ÑD3â<Ëâ Uk“µ%Ô'xu)´a¡Lÿ°¦<¸8!@Š£ƒÿ°Ú?ôtæ¸:=I†wíš™z V¥ÓvÛØ â›ÄUG`‰º €t|ýt«{€ú5¶¥¨eÔˆÙîL»—Ó}i\|ªdU˘ÍÙæ¨ýlÙ(€3ò‚ôJDuq›µ\ŽÅûnŠÕ¹ ­,kS'ìŽÊdòH )1–²½¤¡ƒ*ùô1ì)•™œF1Úû‘p™«šCZŸÖ +·Á²Dœ øIyeÂ¥dËØ¸‡Ó\ ^Û¤SM–æµâ˜ÌÜy· Geh×nÁr%'²Ç™÷9Ã`Md -¥­qH狚}@óJI[.®ç´ÆWOð+<·ÙpÞ9ñ]Ró’ÂQ ­Î˸¬Ñø\™|*ÂC6«–fF¾×³L 8ˆ‚ÛTf´A€ãÙ)2®£êÌKIa ¢í‘M;N^+öy²iãæ6ˆc$%4MãóL÷*t…5 }1#f{'õ½H"#5¬:¬*’H`Å9fœSþ9eUÛÎÀóBD|/æô¥qÅŠhÃè@ý³´†ª~Q†¹ÊEâû„;×¶ÎÙ3ˆ§×䆆HBÎMmÈ:ÏÊ}‹ùô÷8]/\åG‹oŸç X‚”,€”[s`¨›–Æ|•f"›ž7ù}ƒVÄFÎTXu‘óëc°²jse1àÙî½W™…>VѪ5§4˜ zŸ1õE€ÓŸ*¹)¸:Í&¢ õÓQƒÑh ÅùCŒ#!–1V˜ .U@ ‰yªÂND“°|º?`yOÍc»ñgˆêLg«ºŸ{ FÛ¤°”N±dpSƒDP-”DwK §C_a·©­3@ÃMH¯hÖÁ”žé ŠRBd&£åÖÛ ˜Œö«!Ù¸ÎÓ½ Ä_5ïº:oXE/ +ÅV5ÝÛä”X2BâùY‚(Tòr»†ÚCt\QPi2ƒwݪg‘^rͰ¡ eAm!…ÃË´Ïivà2ë¦X|*'¤?쉈4JD ý&rKú‚UÅa`"!¤Ò’;R¥§ç¿L„6q¦pÒi°š=GČמñøÑ†û¹>±'â'cդ׈E²ÓAÛã¢÷À—àG ™]ÅA‹Dhµí¦`kÀ +—ß2ÕÒ i©³rõ®>8&BëêHåôŒn¨³*‘§Î–!žPÑË™^DNæ‚RлvCŧ¼åQN.á½çPzœ‰Ñ„–Ò$À‡ + *Ok ¶d±h NŸIȨJpüÙã‡3ª”¬fÔ +ºâ³Èß\˜l&å™Ò=ª +®œÎ ›(£¡<<*cè`\_ lÊÄÏ\{`À~O'€Þg%ê3V‡h®‹Tçiš=Ävà +À¡èØKN@>¢bÀ@0oæÙÐFÉÝÈ +Q“«½*Ý ½ˆhž ¡‰eKJ]TƒlYƒŒ.dCÚWÃ;<7l½¢¢´È€ÓÉÅÒ7Ü”Ÿ‹=Í-ƒÎ±I„9ÙD7²³U!&²wf‘øoq ä©R:gË Ð@=õ H"©@ŠÎ¦tYµ¼|ÇWì⪔Çphˆlb8݈(WëNl ®{"ÒÄ@yކA3£ˆQx1Ù„f|›C”×PÒ€‰Xãbü;xÄH ¾J¦6ÄnÀ`“) +jÀÔÆˆ\ÌYþíÍ`ÔaÐÌ~)Èøkò{outº¤‰h[ÉÆaÍQÔçìͲAh/;?Ÿì¦o”„´¸ ŠL³òðqåë%çËKNãò0lð‚7=tìŒYö®[é™±»¡ÖÓœ à®Kr°=˜šŒSÀl)VžÅT&‘âÜr¨ßMêœê:( +3cØQÒ¸3gÈ ´ þå,á>cmE,¦–Dà{æ`NÉU¶§m¨€Cr -mAV9)¹õÁ·ï› +UA²·&^9=†™Á$…ׄœAu¼óÀD­ÎЧ @Ç÷£Ý)p·z~ŽÓp¾,()Hű~Ï;Ë¿O}Ážã×[rÇiO!âpPË©¶V0HB„±0mR@|§Uäð1ˆšÉŽE˜^@ÆÀb2ãTâl‚ðÝ`P€j¢O¤¾ì9š’qpua©-¢#$`[“‰Q€šç «™³Ü^O8ékŠá·Úôhd&†Hº»ŠÍr ˆ˜ð)­R%ä,a[®YÙZ\µ ô.s”š¬bdê&’5q'b4`„†¥h]ÙÊ"2nS¯KfÉ™¥7iË:0«ªc|ÁA¹ ¨¬Ö”8¸Û0f w™ºt’Å´³Ö#(S\Þ$=¸m Œ¡ÃdÝT¨d ófh‚"¥U(Ú³¤LR=6šêwªdg²½z S6orÛ×±¢YÀ‡l&kŽûº‚ÁatÜ\øå&kH”JÉ5,œ*y¼T8¶zG’Õ?a¢°Zm1(–t}ž„7»“3HŽjþ>­ Ñšó¤j­Œ9Úal$Ø whsºE¬eº²ª‘Á.õ-M5@v±;¥ºìX0˜ÄT¶&ÄjhŸÈ (°%¡ƒme~Û_“{#Äh‚¹E@¤HnÍ5‚õxKäÏžÉhAC´Vö%-aCsµ +8èpëJ5TBÓëa÷ º`jð;“觪–èåÄõŽh¸'ICÙÀ^pˆºåº%¶P Ç¢”¥O6RŸãÀ<ßÂS”ÆÝ)äÕ{–²inÍÒ3ä29ã‰xn²…KÌØº ñ@3k;%’4wý*P3`=uɉM¬†BÔjh4·’f¿nn¥L8¢H ¥v¢ß/ü½Xµ×SÆn °[)´A ÷—0öU #•vc ¬ÎÙ·´h9Id +ÜÒÅ\#ÇKýß4 Öb ŸC@3ñ/vÔXtM¼¥J °M%6ó7‰Ò9U2^­&^A³Çc¦/olbUQ.2J$~ úŒ»¦ß¨ö Œm5c§†ÒÒgŸšHlfIQ[»§ QޱY€ˆÆDHuqàÀQëêÓVgÓ÷£ëÍ;Øê]ÏŒrEýê´=Êyò˜Â›úÖàÁ½&ÆD:õ †ØæŽy[ 5´C cmy´`r“bC5«…ÍЂ›±OƒRc³$÷µþJ4¤WöWC .ÉXÇNGs±RîDnÿJñÙ§JX€±ÒȆ/ÍŽd<襞…l@Aœsÿ>„ë¼ÀÃc“Uïró>e³Å¬ö»˜,kÈÁ6©8ŽåЍš£ù#º¤©á÷3ôœÂ2±Y\h†˜Ì1±ÿGK„-·ÅÉHäÐLæUà'B¡„ ­™ÔÐÃÀ2mL1ˆwAÈ굋±É7_ˆCBšÓàIéÀ™ÌÜ€>hšðÈÏ¢`wäƒñó »f°4D’f[øn§š¶¥ y°rÒNcG´ƒ„¨T + ¢?„—8´EXMZ1Ôü‘­ÁÐò\[¢C±0“.^x!Nøi§æÖ@qþ§²IÑ¡Ü15VÀ¼cx™B]*Ð/øöu1**>©bÞH"‡*•«˜Åà†‡p6UŽ@®Nûzš`™³:ï Nu³‰Ò¢öBR§­,x¿‚úHèc?5 aaUò»‚hÒFZôSÕË@´Ð šÙ‘ˆl)eHÉ +blGÒ= (”—ô7UÈT ÀªJ%ƒòŸ1ö!®4ý˜ÕiÀ |‚Ü4fR9Ž‚[α§JÖ bû¬öÇÌ&¶›Ga|àË3"“‹³#Ū$—qGÊ%êqKyF½3“&8Cs†æM%Ç­¨,lÔAAž·ÙÅé÷~بÇ`õÖ\@Åt.݆ ~Æd䈬9—Þln\À@4´ù¡20BVÃ8‘5xÊ[í3ïg nà!Ú+:P銠',Íw‡ƒ!¿rM·ª‹7••Ç@*bšK{¯úÊ0XD;êd2qÜTOñö¦Vô[…׌ _Â%hú/•ͬ…ÜÒ)WlâD®EZýFU›•¾ØQ¼»˜à3hbê LÓ!2z;nq½àñk J]qä»P6Âd©ƒ†AzÌp?+œ$sWˆ]q£YµˆÓ7@Jû2Uh ŠVˆ¦S%YCa\šõJlªñËøax¤å, +ÃöA|½Q+)]Ȱz2ÑáÅU/?u`¡®PÜ 2 -äƒ< &KiNR©Ñ\2ðC\ ØµÖ$Ñ:>§öW|R­¸FçÈÌ\ÕYYÌ9#¦l6€w„hÀQy– ‰H•àÝró­šxç ®¬ïRÃj!ŠÊ39' ?½5™x÷Ю ¡¶²Î6rž4#z`ÒN<…ÉeÁÙ\xDFF z¥"'xx\è¡`!A/+q|1$@‹ö>Ÿ Íød<BÊèÈgdõl,ÂT¤ÁDk‘Ì1ßVp<‹UóÎ0ñý"È‚±¡üxbÛ¥<bè‘M}À\è´a RŸ>k™˜§4/Ã<Õ•¥ŸgTˆV‚Í’=ö¨Üãé Δòcªáàg¤ª$PÍ¿·J˜Zé¹êVÁ;‡u­O®ç¢áSÑ,WaBJV+ ÅRÊ«Ã@‚ÿÖ•Cœ-F @(’íSªó’p‡ú`3ˆq)pZ-Y…^=²ÕJh„€ÐTÌÊò1èSV¯Q»4VQÓ€KŒÔ+Ñ[„·íZÏ×T•°AZ#LL®¶ +ÃÑýªÙ0‰i$”¦kZˆEá²…R¶5VkÐ,o¾Ì^£‰Íº²Ö>–(NÜXáÁÉ|ÝðÔb±8fê–À¥RiÎÛ«ŠÃÕD“Õ·CÌT3’xAå÷yQSpf›‚Ã=ô-—Þ.ŠIµ§#Nå¬eOPT’:-zd?ð«¶Zÿ&°L•8i¼ë³Ø][^{Í̧á26šˆŠP׬[˜HÜ}‰™69*C§9Ý¡,¥¯ÌTXà3ó}É[ ²C-po9è¡XÙ&`DD¬¼4Ý‘¡.¯'÷!k®ã¬þF“P!×ÂÑ–ßWyaƒ5½Zü^hV…¸‰À!DàüõÅÓAdmSv5gWíKR“)´åæ¡ ™æ”KtŤŒdïVk‘¼zZò¡Š‰R~^¬j¡òJíÓvÅ‹Èÿ§5ÆU ˜2’õcƒçÃ[úfì‹¼ÂÆP8ԵΪµNÿ½Õ—.K2Ç$ˆ'ªÇnÈ`mV4r†µÕ–Àçäñ„EÄS%#ºaÖn¦è}€ÚŒ&Ò¤ËjFòbí•ËkI‚i¹ÿGú¿%‚•ÆŸfĈfFP¤”Õ…Š6r à[ŠHT¢ÖŒ*µñC‘’ÙÚ҉糈‚â.Õ W%G[~Ÿáî¤Á¢†UFo˜¸dTɬâqÜ‘tPL͸WÍö5ÓÇ9à"jN¤©üè;&ý`Wk3dgÖ·jÚjv@ú"=…â5ÏŸ yÖEdožøÅÅl›Ey”Gaý¢~2çQôŒ« Ù0Þ`÷Ê3Mý*Àiµ +€· goÖåºÄg|>œd¾Öã´Ä¬`Åbº$#JÑþ\`6#j:Vo;±’l9Xч6W¼yè4ß^ˆ;n‹íˆòìŽ7‚ÖFC +ª¸*t\Ü{éF ç¥Ù((o5ëåA\§‰]K¹7 +WŽ– +âUl¨\îKh)‚QMh.ÎÓô¸ŽÍÊU¦ÅP æàQ2Ûiўͺ”؇iN°üO¿Ô”«ªµçY‚5sh¯í£$î-б™~Ž"Ù2·Ù̵$%¡Rn–ÄV›˜ºÚl‰X p%­¤H ¨±ëm⼑çÚÁ,Ïig&Yö¢`ÞL„ÖºV@¡ P{†‰— ¦b±PRfÇ6vógR²ªm¹U M(ÂIvHø[]ñŸhÅtÅ'»Îɪ 5sóÑ.Ù”é‰leÒ¹ˆ’é¼qVK*œ¥#»™1s±5h 6#e‘š„’áïÎÓYÝ,ô™(Ó, Œó-óŒ“g<êÚ¨ÞJù‹º\Ê,uX˜Bïð¹þ>  Va(ƒRµü%—Ìè`–’ÈÝ »»„‹‚¬Œ»¹Z)SÜ¡è)Tr·â`ëzgžé&O•!’M‰½8+Ê´FZ2ø t†2¡³»e Ó§ÔôÝØ_Ž ¬I–s¬8Pd¢Ú?J@<¸o"üIÁ"4š•+åZÀ§N}^Vrƒ~‚`˜2=]ÿ…X¬dõT»Šæ¬Ý-o— +Τ#b€.¾ŒµšY¥N@Ÿhùó#±ÂÀK8 âÇ äT6KªC- fùµÅ‚x¥¬|²€¸Ü$7[‰¦DøE +WkS~ÒæÙÐÊn*yùBkJ//ƒÎ˜àˆdâ ÷J²Z)ÍšFK/Éã4Wræ`¥š ðEɬ ÍŠƒÌÇÔVì{¥Ç &#DãH“XNG2Ø›j¯>1’)]dQXà7꼤jM,”á gj@c™šûcÅ‚dïøn‰¸„t¤V7ˆbi CE³…G ‡c0T%Íyc\o½ÕA?Ŧé ˜®=AEËjòB¼R¬öm_b}/ÊxRZŠe +ùR¨1ÀKz/~«ñ)=`Eô™K¡Bíì¶øŒÊŒáQVÚj$3s$i%Ó‘gª&Vi5*øÏX²i3ÙŸ*}ŒfÞ¼iÏ$Ì,•ÿÊB1[9§maY̦^¡—ºˆËÈk(e±ý"°iáDiK¤Íè`:P´‹h_g=3™pYë ʢ̩’ÑÏràŒ§÷`B lM°5!¥ì¬$û|•sö'l›"“‹Æ0Ù à¬{m‹à]"Ê«J.–^ê&Ñ™R{—¥Á‰»AŸópûdºôEx?»[°7"co"öRq5öÔYÕ"Iˆ¦¡‚¤Ñ®›âMÜ +Á|4¡ÒtV!Y(|uˆÈe)ToB¤›•Í&Þ¢†âñ |Ï[9¤;®O€ØMOóbtSe£¤ñ`(o“æcM²MÒÐ$2 ž–n†4K‹¢:Eœ Ûnâ[h[Ô¹¦Œ±-óã‚,FPÆZŒ4Z”­aBÕ ÀQË.EÉ ~®hà#u /aëK¥å1•ذ¯m祡Þ÷D§Ø¬æ 2ªÙÍâjŒPl2um7«Ýr^)þÓQ‘¹ûÈÙª³¯Vˆ³Ø+‡ +уX´Z!Úê­ÖÒÎ’ Ûø> ÌI^’*Šø´ªõ‘ˬa¬Ô@ V:Ä;qoœ˜Õ~™áÜ,—¢ 3Ãvê·<*|ÁºA8¢*V¸„d2+»Ë²€ÌêV¦Ìx³åKq1fHj³˜3×AǸJ@z؈Š3Y1úÕñ–z(„yU¢¶E!Žb™xÔVãh¶jíÕòœÏ\p-Nª„Õ–§Dª+U[`\ΓÁ:³y' Ì3N--°DÅ-5‰ 8 ÕHV‡µÌX*ŠóœJǬň’Ö¬õ$¬­Ÿ]wÆO‡L AgK¥b;T›+S©PÑÖEV~Ð9tcÆC\c2<¸ßÓåd·¥ò3gO]¬4H7Í +KFÔú`¨× F9ÅdN_DS-ɨ¬¢×D;~¬Sƒ)[2‘Õ¹ÍR3!dyP¯ð½ÑkÕïR­š¨2ƒ+H \mÂSPkÕœ“™H”ˆ(ð t zžUêIâ¼´WÛeÛ£Š¶¹¢NuÒ$ÑÍaÄɘI‘ž¯” Û°‚¥Éµo0ÔÆh5Ù’`;€C4[! ©N¦ÍÐ áìÙ4A+Qà ®A€EН ~ÐG +)6‰(JQ’ˆ¬Ý7Ø…Y»²2ÍiÑ™P)‰ø*Ä8 ¡OD¸SòŠ˜h€rwf€·©L…>›±9‹ºdj£ÞŸbÖbê@`P+ûÁ=\®w¨VŸ/‰+ø +d+|ë]`N Bqª@­ëC9 õ|½¥‘…f§Y?~É*I3{§/á•mÆø÷¥â**]„Y1‰[ˆ°pÇ5Ö®K€šÙMM3u¡[íô$98¿Ó*ÆÍÀ#Z¤Í¥]Þî˜Ì€¯ˆ+%ãbEáŠJÔŠœ•ýŸÜÒÐèr+Ô"gFBÊ­t«•°\^‡X‘[ÊÁiÈçCUÀl™de©«<ë QS31{«áâªìFBâ œCE–ÆÒ N*äŠ ÅÿAÔV³`å*מVwZ A/’)/D˜+²¤8 0*Žœ¥V…,nt0•/ÄFr[8³¡NDz¤™P„ša²I&­PÌj·…¢ à鶬èsÅò1WPè)~Ôržâ¶„­tÍ/+–JýR<Ò+·Äx«nCýÚ…E!"Z¤5Ò\hX¼—Gè@S±9§Jަw !d“1¤t.k–º$ÔÔ|ñÉPå(øÆá1vM·‚g1ºë¹vàŽ(j›Ñ*DÔ“ÂE4Ø Ž™·Ýõæ¿,— µž£a¾ÄµÆì„¥#²½ Áò¾@\G‡Hß´hÙ;Q Ð21Í󖬶 ȉEÁ·(Ž*½ÞÆd%Äè~%¼œá»¨¬ü@d&kÕ%Ccó…Q™.ÏÈÏ UÀ®”ŒÍhØúÙà(XÑM3Sð6l¸HôSëJ‹Õ€ ³’1KàƒµÅðÝ$ÍœÏ$î2øû“¹8ò˜ðÈ}²˜Ö¤OÑÊ–[Æ'NÌ7ä3)`ä‹ùokÈX³nU:²œ%ô =µL7\jfÉ.† +ÒŒ©”‰5ÃøÊ"¦¶„ Ž0Òìò#ÓžÏ:Ìo,¯>ׇ=shÁ÷"X´n1i®WïEÛ"¶˜;€_ܹ‚)̨¤Y’ÚêI¨‹Hó²àdDASKÕéëÝ•q³Òkš%åªÕ¤#XºiÈÍÉVÌÅ:K{¤fwmz‹hw;ÃDDóeΚ¬DÖ õÕÀ˸_ˆ)4Œ°j†ój6BȲ˜…©1ÙÁDÈ ä‡%’‹?eQîVm/UóÂO»{²byô1ƒ½ªƒ4ÀS]¾…_ŒÜ¿)"u(þ¢ ¬0çÏî.‧ãQ1(Ci–þP$à˜Þ±Yódây-a©G( ‘™‘šýœBO²ÅÕÒr<Ü4 %Ì!,hY™b‘þ½T,Vvšv=N­<=èPÿœù†¹¥UíèS¸ ÉŒ±y 1LÑL…YB_mU +Œ›Àð¤˜/MUËölOL$–'ÝB±dSžHH'rÁÔ˜{€ ‘3¯ Y>›6W;À’XGÕ¶>¡¸á£“0ÎŒ:XÒuà +Dè ˜5×0Øâ–hlô2ìX7«vš©ÙÒRNu pM3®¨Hµ"ü^k$rœŠrŸ`yrµ`dªÜìF6P4´…»$Í‹,$/­¨_ºE¯fŒ©³¹H*…R ¢‰Üj8³tÓrBqëd(Ì‚5^,²•BP«Õ<Ý"õ;@¢PK‡cÉ(q¬œÈ©Ê±[ja‰W‰Dd{GØ¢AätKmHüGÐPhNš QwFoÆf.ÖdÜ;¨ *m±€«ˆ ƒ`~ªÌÍR˜ßØ€ŠBÂÞ„9ša´Q¼SÀjæžØ@“-HýE2oš±(Q&±3 +Iäùœã2ÌÛp_š˜š|¢•™NÞ²ièöt;‡H# ¦2š™Þ¤ áJT',EÒR@yöQÀV¦,¸’XâáÒ׬\8‘Õ™¤ùÆÕ•CáåÝ ÿôFGÓ` °bqfÉ$&wóÉUtl2nšÉ;‰jFOäÄ©&Aã&˜Æå%÷u¶œ žVöo±­oA &ä{u°Žuœiâ”N¥Å()·BD˜_\ÀPx ú*ß½QÑsH'ÑhEj«†õ :Fàë²é6³j'2;»Ðäˆ%‡yiÉ$Ê·L<"[Ê8U7Ô)Àƒ$RˆAY]QÂÂéÂ"QáÀxA…UISŬ°Ä;‘˜äá™B5ªÔí,%øŽ¡.pŸ pŽ„½â$Ó%¤¯JE¯ +'ÃÓ2ÜnÁ,ŒÉêûQÛ)½uuâÁ‚š‡»ä'°²Ó`†8ëgp)X°Ž –´Ä¶InBˆ±7èHdw¬FÆ`hŽ›ê+ôÚd¦Mö»RòÔùµÔE`ª©/Î/yÑÎùDÈ€³ÅþECtäú&ðd¯DÕZ¢D¡ªôª§# +4uø½kžóû„þ,ªwŸ¡•ƒÉ—Öê ÈcKV(‘Zê]6-1‰éº#5äl­3›0ïÒ,A4 ¬ð¯¨AZ6KDΖ„½º¨àE@ÖuÔ¤q +b5áYT¤Myʪ Àa ){›Ò +¯û—w +X5Qаª<ŠÚjm¶tÀo”tÀÑœú°eJˆ +‹É–õ†YÅÚd8ÛÒ« Ûz‘nÚ`Åáj¶ÛYÜè`‘ qõš%ofà!b‡Ÿ•îû$I»",É3'7­öiÈÝ"^—×Qm‘žr·‡\øè ¨@“ t:+ÂK³ ú Þà‰¥’»­“ Ђ†m^“2É”E5óÝÕ†-xçÚ¦º,>*EeZ–¨ê•²©¼˜ã)Ùì¸Ì1°ißAîyÎ+0,¿¡£ˆ&é!n®7 ƒe-¿Ü+0»œÙ²òfÊÖÀ§–bVË”ïXÁ9@Ô°Ù²8"<áûósâú³ æCŸÙÞà"+û2MüyÆÆÓˆy+E^+í¤;ÇŠÓ1粬Òræ^#Ђ~gˆ õjZ'*׿d‘IÞ[tÂ(صŽT +ä&'Áé9[,“•±b\üVhÒÆÃ¶8pKYj„pÈÈ nëD´ú-q:v>.Üt–ã´8Ì¥JÊB4äV&>x\ßñÿýPŠf툻áŸ])Yq·Xî3l¦Ô u¡ø$¥¨!*,º‘“©ꨡIW!÷é²nŽ@ êÂ4!¡0²laQ kŒAD•¦ºšù9$BÞ„Pã©9D&–øÆ×Nm +€.À8ê™çÄøh¯Î†KœÒ2aB&_ãøÏçÚrÏ +ìYœ0[Poëã._ÏÈeðZÍ=²¡N•]ΛT +‚9¥óŽqF:¨˜%àB>6!ƒb5â½%˜;”,Œû°E@ªÂ.°ÂÈR¨b^yÚgYR8}¸oÝ`ö\À¯"TB#– V9…?8 ý§h¡¨ÉÙäM³P‰`Eé4r‚&TµabÞ¢ì:Nª¬Iâ,H¤À´§Löb¼k²¨¢_ª¥°¾ôP·ºú±X⊓èŸ+%» +r(øQÉ +±¨œ¢ ŒÁYq%rœ™¼q‚C8 ô>–ldë [½ÍÚˆ5ò!§³·k®Ÿk±ÇHÔ]9_éCáDaXE‘§~mG»fYÐ( ×âŠìl¢>x`1eLv˜éØ:y“¥¯Yùijm€†ÁÍ>´s2 -±a9Ìq„¢–Pà‡…¦Ó‡;Áл•Þnr ¨÷ï'ßëd.Œ– —‹šlaÙ­¸¾$è,»q³“yPÀØô[í L:Ë‚1Ò:ÕáwµÚ|$È8ÑÜëV¯YŠ­"[q¯”Œ2¨MVa+ÍŠ¦LWE³–-;§÷I6KT©‚º}+f@ê’Ë)o_£¨fùB:÷HæZp-ì YЬî¹9¬©è—ç14"›Vk%pV„4€âÑÇ9øÎdº%€†P§zmI…{xçȼi‹ËÈ´†O³úÕ†¡ßü"ÎìÌɼ~×ð»ïùNflvÖ×0M<^MQ³Ê0Ý€å”gq¹ˆÄKÆ2ÓáªaA¦d{GÝ&ø®Ž3c/Xhw ó`6¼Žh›q25F42â÷˜7+tC(aü1ÓГ³aeDNØ š­Å­[íÒËžžgõ¨{K;ƒ(Pž¦ˆäJ­[*ÌQU 2&d€€*|¦„x„Ür%Y+±h –K1]o• ŠH#‹›Je µ"d‰4à¢a–ª¯®Xàææ 0µž¯” ìñbÙiy–·YÊ";«zXˆ$j« íżÁŒÛ€šÕììûž¹á"ŒRjœì!#)‘y ~‡ËÓŸ>ÜÉ©í©i™ìŠS6½G]n„ðå¼ "˜+Q°—æ2‹f感›É ‚ƒRåh VÜüþ¹íH3°U6bGÌ€g(3–È€)fÊ@\"Ú¨mµ+•¶x“Ê Dãîǧ½u| Û öÖh¥‰Ì"•ˆéffaÖ`]•´âD˜©àCÕ[#*£ÚÏæ04‚¡¸ëŒpòÆN"«¿§JÅá‰â3?}°‹SÛ ØÏ»  `/ +h¨T„Ý‘€bµa™å«è²Ïm ªKH¸ãˆ¬›EˆD€ +«õ2¯'ç(ZÕÉ„h”ÙœÀ\´{cÕ´(‘±$oq0pÜ3ª R´½Õ¡¶I‘Á8Zö)¾ob/Õ4„Ø”,a–W°8˜Ý¸·-ˆËð½æcä9e3 +Ñ2nÅÆ0²¸Ì˜p ØTUh:ÕÖ“ñDvâÒµšâ¬ß®äQg),l`«”­p+ñÞQ°ÀŠEÁnw&0¡†Iã’|`*Kåj-ñaƒÌà™ G­§EHjÖ}µpÿÍ=·QÌÇ“›\ýðç²+k:j® ;ÝìÛ—-£?K$Ë•’˜%LUˆ³•|#S¤)³ð&yq,è›Sôcw½8Žk¶M•,”›'>X§1eH­ÓÁ¹º¸È7;Á~“¥2m¼tœÔâu§ÚAY%ï{z,_K~1Ú>Ü7>j…ª½BêcJQ½èÝ"hŠ·È¢ôãbõõ€$òKb˜¥Í´úZF!¼ðKnäDN¤x»A† ·9\GíH…ä¢VQÙQí€0¦ŠxbW«}©¿  "ZAãj•ªj•«?Q;|ãnjOµ˜ucÈWµY=Ê*ÎíS%kJ<™ÔKÆ]4}9³&¼Ô¼C®‰hÚ¬Ö)™eä¢Õ¶æBɘÚÜÈDKûµüu7*jC®”Ó8D!ªG×Ñ‘Džkµ J­ðQÑ6ÐÔ¿ãêba«µ‰ŠV«Õ·™u¥+“9œi¸"›¤l-õê/â0u©ŠŸñsoB.LJµÂõÇB9:UÐpW,¸j᥮,HjÕBA‰áV[5ñ–§•Æ/ùMn…\¦uѲ"żNµ"‡ÈÓVj³ÃYà Æñ÷ˆó­»+â%âä +,%ãУ¸®,Â)n˜êF…3–ŒZí})vf!–õ†aµ¬n¥£ŠÅµõssˆé£ÏÛëÙ<œã\zà–ÍÃã¤4oEÛÔC‰v¸f5‰)çZQ‰Z¡¸Xžo›õ5‹¸ç¥ƒW¾ê2® +\W “½UãðóзºpoÕpš‚eD+‰JÄb&¨j¨I­H1(¹¶åÕªàj†¨Òêb…HÐø¶Z=fd–¹™åQá…¨«—†jm^|â‚,lÀNÁ„˜¬"tUlËÐ ˆK+–¨k‘mÑ2ÀÜ„ãhÙ8W[2KZFκSALöÆÌ%Ä5Ó´qF¤Ú˜‘™ÛÒRnbþ4‹¯âÚf![¿¾¡„T±Å¥˜pQ¤òàEõà "[!6ö¾â„F0{D¿ b6«#Ê£5|lš˜ÙŒèŽd«1lµ.Y#j¹Ìž°šAÔHv.g' ¬ùåe'›ˆVÉÒ6ÂÁi¾T}£aU«ç&G@Ñ`ÒW²7>“¬Ž/Î=1*­=¤¿”TzÛ|¯À.3̺0rÕ +5†U㊇ Zù¢‰£ÈmQªá"Ü«_?ö=OñKe¨bXÄãêxëqxùï»5»€Eq.êK‘IfP©„‹gÉR”å™BÐ_ÍV`Z9ŠKifi„t-Vü–$$áZ¬pT1¿w-Èáv +Ž«D« mö·ÍáBê+m¦W%u›äñbdµØ,»ò}Oû÷OCú\A&n5 +‹:n ë¹ W9yi)ãÕÒ©Àö5IÂð΂á6ov2Eà fÉÙ1XQbM%ED»(ÐUeL^ûy˜0ðìŠá׉U^¬NÁæ÷çÅ«†ùÍ<Ô¦¶÷¾°a"à ’+üôá>tÖH¬ød€ÛŒÈ æQdƒ¨i6\ìS´½ny†D´ÚÝìR®™ÕŽz53Ò6G0oµëðõL”ŽÖa\à§Nß¿ +Àzl#ÞO?ç@®ÐÈGW%ÒLA åNм@E5í*Æ«ÖîæÜnͪÑQ6QQ}‹õ\ÈÖ)0ƒÚ¬©[MÈm±34 Ëîdƒ€<Ë­t@o§×äÜø’&ÔvgrrE’ц«f4"GäžðšÁdÒ5¶.ŠÞ.A=Œ ÆËÑîiu«Zjv¦Ê3&?7%¿Ÿ×ÓÐ޵[±§àâòû †ñ¸ê`ÝÊ}¸fIªƒ¸<ê2Ò†2î/zªýj–…ÓL­S´@T> ±CKng8žŠæAðšÕz»[v¸µ†rGühøN RbFá²æž¹fˆÝl ®YÌêC—JGëál…g¯u¤Å¸&©E^¾—áM¡.Uؤ3jŽ8¹"K !ZÑa)„øP¿Êz7™¿-®›Þá¤$ùMáɹîãøŽ¾Œãáᩇ}=, «ˆÅ×,´N¥ Ѥ€ÀñÍÁ¯6DBÓ äJÉZbò)¢Í ÊìkYâèÔ;2ÿ\³úSÔ³¨eï(YíÚb“ïžR.°)!‰ô1U³ÔÞÌÇ£•ÜŸ?<1̘2øaÐè„þw¥d˜‹Ú‚¿ÐRÙg,ăÌÞïuƒÞqýæv Ù0´øÑ‚…)HJÏÈ&½£â<4hF±°-Ms¥dÍŒXì\½XÏ*/£5׈ ÕK6ûóÖ¬Ö7!óÙ¤ng…¥êFÈP¡¦\P›Ë5âÞüÜsǽ…Æ84á’NƒB×±A¯€&HŸK°òNéÿû&òÀ¾¾TýÉ2R=æJ•¹Y§eŠ€DÖÌâd¨Tß–J•]ã“ÑŠñµEþji ÉZi©Mh§Y`¡¥ÅgÆ&kè»(dCOÞÏ +ž±óÒу#€ˆDËÏ·ˆŠ‘øJÉjk +ÎÜ Ò‡øZÇWZò ¥jšëAD‘ß ZS‹µlnXz3õÞð¦' ‘Å9AAªŠõNˆ­Šz ø¼çOp˜†šµ)ËnBT|­à¦Y+YqKÂK‚ý¼¥Åhúv²\×n€ýß¿7Àš|–ÖeQôñm+–®74ßV º[ >n$ÈœªÒ©ù¬ 7xá¦ø½ŽŸ +ujb1m¶œ—à }óàh$8 ©TZuÙNm ¼!~Y^D3Š)ƒåjJd‘-­Pšý^ì\–M÷{cY78\J4OS®W³å’ü=ˆ­"¹†„FX ¨ðY,+¾uËCÕŸ§Jž.¶Jcð0 Z&zs`6bv^ýð·¼aÈ!ë¶™odI}¹Óå²0}¢á ÈÈ Y2@n}©" Ї& d¤ÌÁÆÄ\Ðk¶^ïŽì¿š¥ +:ÕÈfNE€R¯f”®f'bƒÐCQÙLm +j}?êâu‚z´9¤ÅDªÆ +¯È£u‘1³…ì,™³ +™8_BŽn| WýšŸe")®h\²Ÿ”Y‘”¨©éYÓÑ„¨>¼‚%Þh[>•{5$$6(=4‚ó9´»=ch¨wVᇉݎ¨"ƒ8Á”J†æ©Ð' èyªä:€„RÁP‹|Ô1è‹‹¤™‡Wâ峿>ëG¿üÕÑßÿã{þúÅoþ½}þ»ëŸ¾~ûæ«?ÝÞ^¼¾â§_^^ß!ÿò¯××gW/Ž˜z4ÈGÉýê™;úÍø¿¿ûìíø/äø?ÿnüÿ=þø¯Aúö(ýþèÿü_wô‚ZþùÙqmPÆp)LObjÆòÖD:3ÅŠLòé]²ó’rzz·“ï!ÏN®Ç—ÿøŒº °Q‡äÁÄÆÉ¥„÷NÌ–«“Ó-¢wÕSþ?°©ýýì™—á'’t)î¦Ö¨Å…eø\þ3.äã¡äRpÉ8ƒHj ¡¼ rW<Í(2›á÷`TJ -QÚrêÓ1¡(ÂÎm«$ö b¦l¥TåS²5¨dB Jõ™Rôk¥° :‰ÜSíäÎD-1ÇDΜ=§¨ +ÃЉ„œÿˆQl×L¤åbb´;ŸÜë’'­7%×&o9“ŒÀPˆMhʲ‹Ñ:ȱz!§âº¶ >h\‚œˆ™\+Jô¯AMbÊäãš‚J¸J5éÁŽFdk<3’VšŠ5Š‹Ÿ)ÝÀ½2X¤p¢,\%4±Øðn4å…ä¯ ÒoÈ@- \ˆEIcüxLZÀ!"?¯ Wô‘e‡(JœL‡°èËëp ÉïñLÇŠ¦Ê›(…2‹8Å‘m^¯þ-9DúžS$‰0>/Qe”ƒA®%nÉ€G˜ï'“¥"I‚T¢ +츗¤€Xç +]ä WÑÖ•ª3R%|0†H£0þÊs=Dl +öæôÄ’Û¶¼v0Y”ì"Q@P1®]pä‡RYïJZQùSü$tÐäþ1¹P&“u ±•6\–›ÑÇùó«Ì–=Ýú±.ÑwL ±f’@lx‘›¹3^„R"öbɃ ^¯ü™¹:`ø¹‰ºàƒÈÑáÂ÷}"qà +b*è@|„ܲ6%éM…Ó‘5ƒëPCùyðaN$¯é:Y©KÁÄJ™@ÂtÆÛo´$ЉWä&ö!ŽÜ#RܘYU‘AÎb”ý +ºZôÆd}‹Ê­KS•!vzX¤ YI•ê ñ&úy=ñM¤%*'qÛqŽU*Â9ìT|£êÇ8²€Z¦åÎ…í’ âDZ£WÔ ¥ ýÙL#kæÝ¢D/rц¨6î?´’ Î£í<LboU‡• +Ä*Ó.Èð,‹(ÚÉܔރXl\.Å]Jÿmú&V2p1ßLª&èr‡¤£¤#Œï“cŒŸ£91ïDÖâŠGD+å7&Õ5ÙoU¢g…”ÃWä÷ŒþMpZîI¾å…s2r¿“¼Tr2)Iâ7YƒÓùcN$rëÓº bPëðè5‰Ãj|*‰ËUFÀdÌ`èñX­Æ©Ü|;\Ö8³A ’“‚ŽŒsÔš&¢D£Xµ$2CÍX? +ç8\AÝh[ÈÕ„{¼ T{X«Tr¦3C?§¯2Hv“³ +ÉÐQÂ= ƒÙW@U½–/ªç™“6è¥)y: µÎAÁ±Žo ER«¹ÝÓ;­Û„´×qäIšŽB·[Œ>=ˆ‹T*Pšã¯¬o%G:ËcNš©Dkz‹¦7oÙÀo|©Â¨)À¨3ëkÁ” +§}¤qE8UêÈw_ùšP’3BKíy©›&‡É»Îeéøºs‘ÂçÚro +yC½€‘‰ ÉâüûñPÑ’P„·FñÖš´¥k¬o0E"ñ’k‘dT£ò o0“½Î@4*!òcGDâíF,˜kœop”‰[Äi´2,™ÑQ©©¦PY'«pÂ. G%G‘¦™èÐV.åa.jÏÅ hò4ãd +15~,ÈZ¬”øVqݨ4χ*‰5F-Ü–ù'Ùm …U‰6a"dÄàSÂd†Í"") ¼%›¹‡B/ÄpJ&šµº«ÚÉd:—\±CˆaSŸXshÐò{’®XÄ&r×ó’ä’1: +·ôÚ2ûƒ,ÍHb££+× ŠIµºä ëÈs&"Q…Éãfv%CcïˆvÀ0äLÓ;L´!¨;Ø~é÷jÛ¢èJ1®ð·"ÄS/Ï +— Ã=¤|e¹†™ê.I84ÏÖ‰íºE˜;Ø„[šé3ì‘å“%18¢P¹®l4Hª?K„H˦‰„ÐêÔ'B(ü±Ì.ƪêH.lò¡ÏGIc GnKÉX-6ˆwF&Ð&›vLÐfh±›°ì¢¥çeY?V„’Ù…»Ó^„SÆÞ4uˆ,¼[4$zyý¿×¹š2B-ÅÒOx¡Ap,14 ²2R,x¿ 4±é1&¤|ªËnà&1¤Úaz’jÛâŠ"™OˆNÜ*¼­˜F7d ¬’<¬@G6ç.ƒ¨ñSd +©N˜Úº" ^9dµ)±ÉM&¢Œ•ˆÓÊ%µQ>æÄ×@‚¿Xå*k³îÅ|Ld€qØ·²è9«&íÊ*U¢ï +ÍœEÂ×… Âæ92\À£À”£Ï\ê¦+W“±Eç­Õ2*÷Š5J'¦*öŠxC"®ÀÊØ„¹r›É±XWÄD—“»Bd]’·‘ 9ϵõ¥åf ‰ƒrfR‚á/‰•1½ˆHwhz¾Z…=Ñ夿ç’c²3CÜ/Bìâ¼7Îéá_\+±è94³—ƒ>ÁwVß3²€ÓAe*øW²­JäÇSˆM¬¿„@AÞU € ˆ°¨ÖM¦S21n(Rö{WbÏ– z³da hÃ%t f3‚¨l¢×]™ñ2ÀÏ«~—‡ažÐm›r3SsLOc„ã”Í´Ð(’CfÖ +ö`̫ʊš‰^”R:.æ¸$•@ìÚ‹5àéz!‚ÂõB˜Âšî¡ús ¦GÌ5‹ªNÐz‚"‰T4¨\‚ 儘,@ÁLä"r áãTŒ.÷˜OC0Uf˜\AÛ”’¶õj#dEË›ÂÆÈ¥Å‹6• ü:3îå (jü’gáY„6dÞ÷"‡DÉð»êÝ$šú«"èC1M×5˘wøvæ¸* J¿,A9¼Ç{²p:ÖÊKfŽAa,&“T ÉUÔò yôóL©¨…=ú¹goM"QQ¤Á~:ºú¡4}"§²tiiQ+„Ö%¾ž•ÈhGús£RŒW[ûœÑ$DžœsœÏ÷Eòd"~þ‡›ë?½¾¼¾½¼þòøXÈà³þó?|MÿâüÓgû·½|5ºyökûóè_žýúï¿?ýÃÍ‹ úó·—ç·—7×g¯¿û¡øäè—ÿ¼zu=þéx ëõåoo/Þüêè=ûõo^¿>»×âü«ËW/^_\Ó¿‡£_ÿûõíü7ú·ß}}AÿöKïÜ/~uôë¿^_žòg£ßë/ï6ýæìÕ[iûíå‹Û¯~¸1E6QÛ1mý¿vyF_]\~ùÕíÖSBó>§ßüûç¿yõõWgŸûmgvùb´|Çt¨ÍŸÊö›óÏ­÷åŸ;=ï¶žÇwa7_ü×Åùí§7o¯_Œ¡}zóŽUŸÓzɬl4½}³õïüæm1£]a“·o_ñöÕÅõùŶË#?Ýr]ð0«mçóúâÍÛWÛ³J4ÿð÷rÛÎ鋳7ÿúúâÿ½»ý-½÷«>ðíô®ß^ýñüö웋í/èú“>±ë›Ïn/oÏß!bÌù½áÖ¹|õˆ ÞùÍ6è{&â`"?¼ ç¼øËÊN~xÄ?° x{.¯·Ý˜›¯/^ŸÝÞ¼ÞzWævš^^¿ãî­Âµýà³ùìæíëó‹{}öõW—çÛOë³úˆ·æùÍÕ×7o.o·¹4?ÅXxyç·ýÛ‹—GŸ´½Ý›ÑÞi{é í=0‘ƒ¶wÐö~ÚUzùúlÁ¯þpsùæ ïí¼¾·µMl?Õ½­ƒºwP÷êÞAÝ;¨{uO•£ôäÔ½GÌhÔ½O/¾¹xõÙWg/n¾}.¾ãðTÔ¾ÇÌ䑊ßþhC_¼zûާ}ÿ´†­…ê7·/~{ñÍå èRçú£$° é©Éÿvöö͛˳ëOåLî‹,}óò囋ÛOÎéÅöüñÅN«Ù/¶²^|Œ7ë1ìz_®ýùþìÑ…óõÅùß¾ãþîßmß:²âÍÛ×/ÏÎ/>;?{µ½Uôî>øä^‘üO‘ç7¯n^ÿË·_‰ê¹¥˜ðÝc&*­ŸkøàŽ¿­÷fÜÅ·¯Î^ÿîŸ_ß\_\oÍ6øágùØI>¿¹~s{ö“œ?Ü'Ýä8;·õAØq]Ó»GÌe·ÝŒCo~Ä\þ{ë¹ü÷G”Aþtsy}{ª&¥a¼øL¯ê©>TyècËCOÓGò^öƒ]w•<òî‹fô'É®0ƒW—·:»|—Œ²Ü`ëïï06,“¡¦|"g¯/o¿ºº¸ÝÞ§¸OìíÑœ`×YÛ‡ñïªæóí·çcw¶ç +ï˜ò:¿Ó;·ŸHÜ÷·ståÇ;!öEúýÅë//h%Ÿ°ô÷â§À!dè}™¼?„ íEÈÐó››WŸ¾¾¸øï­}»/”·Þ 7á>â¨í¶÷'²•žüÔ,ÛÛû^l/| –òâòÕÙöÞÛ}²^|ˆà®ýBŸl iðúìÅåÛí9 šîðóêŽðŸ£?ý?·1ÿµ½ØŠæ»,"í‰9è9¥Äþìc_øÝ[k}{öžÑ®s‡í3[÷åLüÇg»rû^>*:íåå«W‰zõQl-×[ÏçìüüíÕÛw»k—Y­?ùð²w¼«‹/æ]ä«3æíÇPG^_°€¾õV½xqy{ùÍ#6Ê~°»lóå뛫íï7Þ]£ìíÍöŠÈÍG˜ÈÙ«oϾÛú éáöìõ£¤ iÿÁçõêòúâlët‘ó³Wç¿¿y±ý=š?øðI€[K ŸÀçÿÀSØ–YÜýÕ‡Wö>—_P½Âí}SÒú#©$g×—Wg v_qèΟ\0Ìö3Úu…踂avÔúq†Ùsïù“ †Ù~F»Î¿ž~áÅG¼5;î”Ü<ïóqñó¾WèÉ…Ãl?£]gà[ËŸûóˆ—uçÃažÎËs‡9„ÃìÊcôôÂaΟ\8Ìö3Úõçõ10O8æ"Òž„ö3æüÉ…Ãl?£]çO:æL|OÀåÿÙŸhžG\µ=ÙÃ}D |Düßa~ºpŸó&ütøHßÐÆßüûç¿ed˜ÏgŒÜJÚŸ'êé¢ã¼ÃµÙæÚÔŸóµÙzò‡ks¸6ºüçÅ«?½:ûîóÇ¥0îbôÊÇ,ô¡1 ÜQÞ:BO†úçGé-¿Ø'Î÷úâêæ]¬—]ÁzñÛcÀ^>"ØËS…yó5‹l;¯}‚ñGþ“àŽ|ÿßÿûdü=þû“ñGO-®ûòúÅÅËËëËí=”ã®^œÝþö¸.äß:±§.ä—/_¾Ý¾Ⱦp„GNk×™ÂÖnoÞ¾~9T“Ï¿~çG»Í½ŸŠOEèó›ë¡]o£6~·»S”Ëÿ¸£xç7|jß]¼zuóí¶ó{uùåW·ãßÏ êwë)ÞÿÙÁèv0ºýD7õP°÷`t;Ý~²‡â“/__\\2ä—‹O.¯_\~yóÉ7—7¯.n?y}ñâ“›×g×_n}^ָݰÆ=æQ?˜ä&¹sfk›ýÅ«ñ?eðY~ñáå¶õÄÎþûòêíí;Jò­Wí?š½ç·—¬€¿øX1¿eðTyÖù7__œ±øõL\ðcÍžk†”öÁuýÝ=Œz#÷ϯ‡Jü‹Ôæ?BtÉcgùh»Ûæv›ƒÝæ`·9Ømv›ý±Û¨•Fì6jÄaóÍÁns°Ûì6»ÍãÏÜS 18¡F¨÷Ãgª¬P qøŒCnù} +÷%(r14^]Þþéìò]¦¡ýãøŒ½`q\Ýyöö$Ë„l™v;c{ȉÇÎØ~G~NØéí<`OÏñx¾—(½ëècÒ];¸S»övp§Þo­÷öÙÝûÁsêßÞq̩Ͼ:{qóí¡~Ñ_ÿ'€SûÉž"ºÀÖ`i‡Œüz<ö4#ÿæåË7·4è×/µ1ûr¡¶.úâ»íƒÛ¾Û鉼ãµX'òÏ0‘§ˆßþG¾G?àýyƾìæc4]Ù•ãù؃ 9¨f;§šùä~±í‘üöòÅ#¢¢´õ‡7bÅígôÕÅ»C÷–)¡ùŸÓT ÷±ùØç­÷g{qécHK™ÉöìÇ`FŸ«Q£Œ{)vŒ;z¡ÚS1jl?‘ƒQãƒ_ÿƒQã)íæÁ¨±Çr0jìœQƒÔåÛ³GÄ¶í¬²|P̶*ðúìüöìÕn.·˜—o¹2øÒž×ùÖÖž}‘Ž·ŸÑ®çɺ“­!u¾8{sñ¯¯/þßÛ‹ëóí…þ{¿úð[õÓE‘í,,ÐõÛ«?NòÍ#ÖŸ|ð™]ß|v{y{þ£üjġֹ|õˆ ÞùÍ>½ +°£};:`=b£>ÖÑ#ž½—¯o®¶¿QÜø#Lgë:;·7Û‹‹7a**–Zö …êIc1=BîýÑ¡˜>Šáh{袟Ähô—·¯¿xûj,ö>Ù%{“€øå}Oì¼É;˜ZÖ­Ÿ\üØ#f´GñcóqøüÉ›{`ß~‡v;‚lûyâÇößMq»ÊgOÆIA³zjn +w➸uë螃I|s‚6‰Ô½ƒº·wêÞcûAá;(|›Ó=(|»¡ðm-˾™ÇAáÛ…ï©Æ¥=M•ok‡ýÔø¶žÞAã;h|ï ñ4¾ƒÆÇúQ~rß#f´GßÜܼøòõÙö wgÕ½cÿT¾ÇÌä òí¿Ê÷1"•@½ãi;[kAû‹wñDsrˆ;Ên€Eðób»Žßq%Ýafö?ÇïøÐ±¿)§¼/œëQ“ÚuÆõòÕPÿ¤ìõ¿|ñêìüŸ éæë³óËÛïþåî7·ß½ÚÞ ¡­?ÚeúWšæÝ¥§/¼a°ÊçOéz=M3öÿ[i×­Ùãî{"8ìc5ÀG–2Ý&÷Ø +­;Îà¶zóöõ˳ó‹ÏÎÏ##ÜùÑÓÒ!>´÷bkÅNèó›ë7·g視jy÷·»S”Ëÿ¸£xç7|jß~õˆ|ïWäLÿ~¼Å[¶ÌðþÏöÉ€Îc2(Qekè¡‹Wã<ÊØ¼üâÃ_иõsqöß—Woá¶öMÆúí%ó½Suü~Œ¨1æÁ§z“÷HÚ{Ú»û"ñD¤=‘>Î#»³°v¸’¿ûç×7×7¸»¢ Æúhqwó‡{'/Ä¥ƒ¸ô³—>Ó;|—vI^zš6ó'úý§ð`2ÿ)_õÛ?]¾KˆÙ?ްõûþwøª—ÉPÓ>‘³×—·_]]<¢ÆÀ>±¸Çº6vž½}˜ÈȽLÛoÏ?>Æîl­ãýã-׉øÞ‘w`°­‰‡·óðvÞÎ}y;ßK’Þõ÷ó±wjןÎí_œ:·g +;þtn¿#?§§sìЇ*OAzŠ{ñÓ `ÂS…"úË£1u÷EÜò´¢:ûκä K›<€,½÷ö\®¶FdØÆþoã§oÞ?µ|Ý9ýÏCeÛió_Ûß14߃‡z_dQNsû=.à¾pŒ§œ~ø3ùx’öžB|üñÉçóo­Šì:*Æñ“Åxʦ¶½ƒÇ c/mÈ_ž 2òÓŒâ{¦½ûÞºGëNûÂö1ae [ç5î oðÛ—ßGYû}Øú¾Ü¥=¶iKþþòâõ¿^¾~r¦¡]àÛ»²Ï·g_l¿û`Ð G[;xî{œaïÎo>×Ûëó?ï7yr§ì¤¹£ŸÇ9û·Ã9ûˆçÌÿ\ØÙ§+Ä„•ʺþËë³ë7/·¨"±;çÆ.†ê§(§=M‹È{æÈìº1ä}†}ÑáöÑ‘s¿yõê'g +û£ë¼·Ü—Cº7±š?ÅÇUþé¿ùwï>ÿÝõ +E¤L”Ïÿpsý§Ñ#Î ùÓ‹//¯×xö‡¯¹$ÿôÙwW_ܼzöËOo¾ýÕ3wô›ñÿöÙ[û;úã3wâ\kC7=é=D×éš\ ?œóžÿè!'úoïzs‰þHÞ×rô÷³g3våïßÿñ¿Çÿ5Hߥ£ßýŸÿëŽ^ÐWÿü¬Ÿ„æC>:.'!÷Z®)µãÑq:)½†zÔNªO-‡ñ/=”£rBÿ;ÉBGçÏŽÝI®Å×pÔOZ ¹P)G"ÍÁףѤµR}?ŠíÄÕ1ÎçÏŽýIñ9ŒF‰¦•üI,1´£ÐƯǧþö,œt¼?ŠáÄûRêI(qüwýS·{VN¢K¥…1™>†Uh©Š? +þ¤ùÞŽ¾yVOrì®ù|’ÇÚ•Zkc u¬ ÷'µŽ§ññTó— OBŒm´'5:^sa,—òïî$•8&5V`z?¦)Å^2õÚ\+´Ž!øÒŽ6Öúù³—Ï~ñù¸å·w˜Ù/>ÿåè$WW{YŽè§ŸþæüüíÕŸonÍìú‹ÏÇáü|Rßö´ë'.´˜éF§!¥ƒÑ耟ÇJ2–¢ºÑjŒ.§ÒÚØxŸN|Æ×}jÌ ”1v’[ê…¶6׎ŽûI.c"çÏšnß±wc’=Ó&'Ÿó85ǾœÈAHzlŽCxÞ÷zÒK‰icÓúÑé˜Eu)÷ñÃx’ûXS:™pŒÃ§”ÇF–—Rç”PÇÉk'ãÇ!Ðk÷.<´È±¹Ulçý¿":3µõqìNÆ™+;–sœèqèÆ!kÍóás±Ó9ÇÇñüÈÈwÃùqª·›Ä”è…îQb .Ò·ÆBôNK3ξ«cJeì]ÚHL€OúÝí=ÿÑÎÚ`:>'=kã.òº=q7†:nÖø#Ñ_tÔÆÖÔ8îåñX!žãÕ3Zï1©AÊc"ãÌyðÔŽÇ¥ínœ("ktâØˆF“ÞëG¿üÕÑßÿƒ^”÷`ã¼:ð´ŒÅ§8vmŒ].Ò˜EïÔ¼Tæÿ´!nœ‹£ÿ¤O•ÖÇeeÚ¸"´IylwãÛ>¸÷`ðD“ñÕÑu§]½V.Õ–ù Œ~i¥³ëã1 &Áwº–ã òÌèäøà<µêrchWÇDhmS(t$ê8:1ÑÑ¡³Q\¥-ד7˜AÌôª5}>F×]6p¼=)ßÓ(}¡×gÐÊ`A|.3íöøÈà7ãl¬ÓÇÕÿg"Àæ|®ž=0ëÍ•y`ùÆÒÊF®š²||MÖ±òÉ>öõ$òÆGMl¼[ƒ?÷J<±ÐïÆEre\°ñýõWi\Î0¸e×|ü9ŽÁxÎ+ XDû;D„SgŒî +_«èiðž·~°N½ôÔ¸ÍØ°’dž#—{ä¡ÇdìÏ$âØ9j“ˆ‡Ó2}š9ˆ#Ž;ž¢è;®¶ñÉÁ‚\(r ÚI+žÙ@ ‰~58PÊNb“|$\jM8P̉8Î õJ,g½=ãØ„Bæ äq:Ggmó8>thwƒãNŽVqläxV;¿±Ô5Õ´ñ8Ó]¤çË>ºë4DZaÑÑRݯJ£óG|ƒŽ‘䙄F<Žñ"¦ÐƒÌ”šx³ÍÅ,]%7˜+}Ž%?ú\f3Î/ò dº:ô»¬¼žÎ/oÛxq´Ç¤Kº.czͧÎÿÔ:KDSVB¬2WyÎ<Ïs¬_œ@Œ¯BG‡¾7öuÜK¾|‚ä¤Éwèh^¢“CÙ¨oúÞ5MÂëúÐÃØ{—£œªçïÊÓ6(¡èôÆÐ¿ãjZ š M$ éˆVÀ3—¥×tM23ãñr ]Ú4~¢IœÄ0¥ó$Ç×[ º$ú}zâiÇ ’M_gYŸ†–èöòd3¿¶Ä’+24=ûÌ2oÊØ0™ÛÝöã 9ÿCÖ›T° ã2XCÖ›{âGöf¬±ŸñªE¡ðŽa~I'·yGœƒd’‰É‘~ãXh»kbž´¸´ïždgR­sQ¾X,¯4VÄd•2 #qH#½öï¹j›róÒn.üÛóÀ>´ÑÇaóÈ'–¶‹ÞÜKyèÙ®ï¿,­êË2$&ºËM¯~ kühˆ,®…ùFÑ+FJ¼bሷ¨UÝ"æJ|"‚×RoPëºAã¢Ðþ°Àûˆ%ÑBnzŒÆÔ豪ȧh\f^—õ:DüÐg‘ ÆÉáY‹ÈŲÀ`T$0x*L`Ê—Lª Áƒ–r '*À¨p2HUHap0Ç¿é–!’hIëBR É8–˜ Q?ž¸ µiãf{©CÖ¤)%2Ú1Ko5×>˜IÉ<:î˜,ÄÊh‡éGhWÞQºÐ¼?ü¬y:ÚÂ!íå£Mb#)îU%D}A) ¹Ìg–¶Øƒ@2<=;­ q¾*Ä5+ý&ÄÆ¯“1>‰DoÜ7«h=˜x1)Ïä`ôÕ+ó ™nÝ­Bߡǀ޽|ëð^УÂzÆ|Tèøò³—‡~Ä|ßÖŒï€è½òÊ-gxéµdap¾–Ä bŒ¢³Ë)c™Æ-yˆÜç5Üèã¶q"ïÙŽõæÑ߸^ û#ÝbšQZ+ٔƋelÚý3÷ g<íêÙC|ïg|€{]pzŸã9»“‡Ò¥á³Ü`§›,÷Ç~é7…ìGtÆ ¬´vCyi‘)Â:\+¡Òû'ë¸m®ÞëÚЪò‰W×ÇDFü±‡QŒÔĵ¤+½ü–EÃÊÇkŒ5æ®z ÛM˜æEt¾'@þMZ1g¾ÓêN_ÿù£Íò}Ŧ„.6‰ûrü¦¬ÿ€F@GÓ‰£qLL‹,އ$ûMñSEx@‘øñˆ÷´:c4¾2#ÂL$OᄞTí¡ä ÍSWNp\¼$]¹VHµˆä´ ='qÏ¡n{ô K×cDíì^ ŒY¸ûx´yÂß±8Ö®*ó®•9œ¨7‘¼ãý†%üIž<Ÿí,pÒQ„¤ÊêÝqþXK7t_Q%Æ ]Nä)' Ea‘?ø(‘b Â7Ätø½šmH¿¦#I%Ãßxœø°ŒW*8\ÇÉM¼b¾Er¼ÝûöwïQ"K‘èùIÞß+r§8¯6 B’§‚ì”¶ŽœpQ<-A¡³$ˆÚîýàCßãd—‘Æ"’TD»<Ø%¹qÉ.›ØBFçDÂ^nÎ5=­J'Ú†L­­~×~ÆyL!“;)‹vÈ2Ðw4^eôE®¾n|ÌÔÏg:{#=‰A™dBxÊì-iL,’¨Lsùt|ëùcííqšö›–kÖÞ‹`;ÅN“ΦÛ*2¯»ÞýÜ,²»t2/ñfùÈ–ŒälsäÝ¢ÿݪÚaÂ`Àßð×èÑÑýÊl!oª˜ë½ýnm¥+r‡¢[¶ÐÆw‹¼ÈÞ§šuÓZÔ¾ø§MóNŒ-2JÞµ*]îenÙVh'uß:ÛVRÌ®,7—ü¿¾?ÖÆ½Ÿ ó#›ŽÌCqõŒ6Fü}<‚CÄ'¿91k8GçµTá+t#Xz9&®ïØj6~媘ØzìÜõ`DE^ªÒ˃Y$¯61¬Q£(¤ÞAú<;Š\[búÿÙ{»]M’ô:ï +úêD}° лa ‚ÛAɰ΄aM‹¨jÆÍÖÝ;ŸgEVuUõ I±w‹&tÔ½£¾ü‹ß÷g­õžÆ^ÚÛQ"-DZ’ëoñ«nKý«ïú­úò>ÐÔWà Yä$¿gÚíuç\òÕUç™ÏŸHݽƲw¶BJ¬ÄóÓØ•ºfû½4Iuâabxåg" %Òy_ôS½õe‡~Ýå?1.ÿ<ìѯæ'sú#;þ1ýàãZW¢ê,Ôûøu[†kÞ}s¯Ž¨‘éKëáîšôŽžîlÕÄ|gÒ:‘Bìוé{Ö#FG3ÊõËàëµòõ‚ú‰e÷ó;Nÿ½†Vp»²×ßV‡süÞ¬6“ýºð¬_Ž˜ªíSÓ1°?ënš·uvâϾˆû´ùoß`YÏíUÞiϾÇïÌxÝÇóG«»ï`/«3KŠpŒ×—ûhc~º÷;^³&EõSÙ¢Kùô¸¯?ïç²ý⦜zÎ’mÐh¸ßâ>s–É‘š©÷_cFk +­'^ôÕ­þëÏô’…Т™?pK‹e˜÷"‚“‘áøvMÜ[ÉчrnÏÿ¾¼Þ'Ê›¯nós½ß§ üÓÿ÷“ψy¾ó «”åOá.ÿø7°~>‡^ +ºüS Ér|ŽšÄ9î>:¶ñ{8­ð‚Ú…[•’žÿRÄ)<¿{ñä¿<1ÒöÙ¿½xõs?þzõéŽÏk¼ûæÛ¿ùæÑ(ûöï¾)oþâÿú-8Ö_¿ùÛßÿê׿ùá·@ºì^He¹z+€…ÃÃÐÙÔÎÊ›oÿö›/îÏYsö¼M¶··u½¹7¡õÍ·î¡¿Ÿùí=:ç›ÿ×ø¿þfü¼CöÝÿæóûy0²}ßc¬9 €eÊÿ°é`eÝ–cï^Üfíý“¹ÅÿpÛÿð—_ϙœyÛbÖ¼œ÷.P¦ÃÖ 4"E‚ÛN°Ž³‰‚ÜMx´uÜvø©ñÖójùhúâ^ßó7?ÝÓÇFIßÿòo~ÿ›_ÿÕû_ýö‡,¸û_þú‡_½ÿüŸ…—OÛÚÅ¡y‡³ÊÝ |¸™rv“W¬˜K¾ãu€@¼ŽC˜æ½ýáÜGà÷?q¿×{õ{´î…‚#Cp÷´ËyþÆaõ—ú¼hü4^´o€Úí±i¶úhÉ[ÿS÷|½÷?ë[£Gã^ïc¬L˜‰‹ù†7¾§tp³‰ŽžIÔÎ=;z‰ó{!~Η7{½×žQ¹ú¦Íû‰=Ø~ýøáyÅžW\?š9#o›À_}y¯Wœ,¨ðÓÃíœF>y8¶\B6ê~E¬L'O?ǧa€Ë—™QŸßíõÞ›4E?˜BŸã$l *”»ã^™HÙ£ž8Ì£g¡8æ¬ùÕ _q¦Üfóqwç…ùÜÜZ|:Üc=ÀðϦîuŒórÁ=/nÓЦ/nöz¯½î©  ¸cÈC¡SþCåá·¡÷qêLàÌæÌŠ{óaV@^øþ뛽ÚkãÑÎÛá p1µåп ¿~ç¬}°Ý1-w{ç·;vÃ]Z¼õç{ãíC0sÜîoÈ]tœ~ò—·û“_Wþ‰_ÇvB–ñ4<Í£O=1íÛø†—`×û »ŸÂÁ5% p¸^l1_ÞëÕ^šÃ~fë.•tϾ·îìWÙ/ØÙs|Ám"¬Ä+1ŒóÇmÏ^íïi}û$®<ô{‚0«Éã’ö˜ÃùñÙœ©ÏL%‚€wÓ—·z½ÉQooñveÆHzâƒn÷þÍâ\óÚï'#ÅéâVxcaŸ~y£×œ—x†ôO‡P€Ãžì£d¢N_‡ëŒ1ê|š>¿ÕëÍ{y‘…a ^=Qå{ZÞ»Ûí7\®Á+°òB¦\{>a4ôå^홞÷xl&Æ‘·»ŸÜ®³åíú=­y»{O›™븷êÑa´ñv_Üæ§E‘ÂRÎäî}[zÝg •È,%³è,]Ã9P†èrÏþyd^|q¯W|ëÕ ˜q>,HY(D¾tÞ¶Û«99‰Æí.œÝ4lÎ’0×r£aû&qèöÝ®Ÿ8‰rê4sñ·Ï|g K¾ºÝŸüÚúO£ÑB“1{ :I •ç-ok&o™C~7g&Ò™h2@—5×¼/nøj/ï~qïñ¸©ã|ÞtÇÊ›&ûñ‡ßÿø‡NªXú_ßòûþ¶Á8CÆàxòü¼öù ÒxÛãz~6{fH_v2ÞÞa'}»×ëø“`§¡|gˆùºOd*ž8˜IGR[ûwYÊ¥¯Ï i |uÇ×{û«í¬v–œÁÏûù§pñ¬¹ÇY ¡îã÷)íãTªº+_Ýñõ^ÞðÊù)¼’ßNPŸ¢0Î#’„Ÿ E~f’ø“9ùÕÍ^ïµ™Åé`gGÅFàñPCžö¾7ÿdßó»=9V§' òù _ñåàu¨¯™ÃôôUö›žkí7½}¥úé‡{rÅíähå Ø|~ËW{ýòyÔÏ(ñOÓOÅóø]›òcï‰Øì} 8"½ö1ÛŸú¨oÿæ'¼õü_>Ö­ø*¢{û8o¯v”Ò-¡íÝ}ããdQòØÿpŸWÐQA~ùó³mòô§Ÿûîu÷oÿôÝç¼-ÚYo{ñ³»ïóëLįùŸ0Œ~2*ûÓ¯ýùÿžáâÞ&¥=T;PsÁùt¨‹ÕÊCïáºgÓÛ΂ýòç÷ô¶8Zϯ¿}÷ºù·úæ·=†6ôÙÝÿÜhýÓc_Ù¿?áaüÔòú©ü‰þcÕûß3Vä…?vÏY;Ø=Bìßãª{‘‡@þÅÏïÎ?ÉõÔý|Õ?ðæßþé›ÿxaýèæn¨þ©FÿŸÈü|¼í¿-í¯~õ‡?üðûßþ¯ÿïÿó»ßÿáßoéïÿíï~÷þóßüá¹óÇßü:Ò8ÿê?ö¯oô×?üíoþË~oRñÓíú%e¾©‡À­7¢ÙíÃÏÌ]å¥VÌ}Åg7]ÿîýoÞýðïÞýêýo~û·|îÿþÃû1,ðžÄúßÞÿî¾üÍ¿ûÃï÷Ÿï¯øZú%9È¿ýÇä!«ù¬7@Ç ;0Àl‘P‡ÛGþ¶ ˆuG"bÚÊøê½XËᄅǎuR(×}òÖãz¨;+Ž:×j OêöÔDû{þ>¿º€–Weç¾ ´3T½vt¶RE¤¯Ø³a.¨8÷‘Þ WV;ü³½jÞK¼Ëˆ®÷Jf0Ë””tÜ{ÐÄö¼gûÝ2?ŒàÜ—{±ŒÆkeƒ»å·ké#¶Û)µåž'}ëÔ6Î_д!â±Þ?ºúöÏØåéÃÛÜü ´#VÞr¡#Þ ‰1&öF7º¿ÿÊu¯6n‹x}´`ŸÀwwØ÷PÞor/z‡tõtÚÝ$Ô™†JøÍ©?† wGŸ4tÿ¹è N|Ûž›²%Ã5˜6¨(žÔç"\'ÔÁê¤T“àÜìhaÝ»+o,Ðv÷2M÷†ûÞ!;wËÙGæ¶H7ììÌ~R߇ €­À¡ó ¨uÜMç}(ìÙs8OõÉh¸§ž¿¸NÕkèÎél2š].ìÍ¡ÎMf;}9vÐ<ç>Z>é:Ów÷|â“ï³ÿ¾ßšn©ÇÓ-ð·Ì ®¼É=ò“¾=ˆãxS¬@úŸÄ4>þ„ÒtÕ¼ZȇÂ_]èCðçÞ‹˜@µÜ_q↱1ÃHFÞ ãº×»; hòS<ÁÜ›Q;ï£ïøéntÁŒû]Ìl3ÛåñAÓEÓH—nÂõ¥ALjC#¨g•!WÖB“Ís®y›4°-žb]ž|ë½0 ²ð²t Àãƒ8ß ¶ß;Bãœ_BÝçA»8Æ ÂÙœškš µ»åî*5˜ø†‚ô±…õ àÞí¸{»òôæ°3Æև_#Q‡=愈«¬‡tY¯{¾;Ž^¹§&*pîuö¾"/"¦uÞÝ&È~ù±G|._FZ +ØÀ`AT»·q3ÛYóêа³Ôqœ¯éS¸ ]w3gΙtˆkwê†(½¡ƒÖ#¤T¹èþlž»›Éu¸Ç/ìDLJÒèípoŽôáôï +Xå@¯ìY÷ûô2’Im¹×Hâ’•Íëž=939ŠFi`÷96°ÇØ +¥¯±/R’¬®ô˜‡5{0˶º–'P±7Îñ¼Üd]ƒ²bC8I“Íì]œß÷a812ô åÚâhÎTóTBí¥xÿ ”.U%8=å³ß—UâB +”àÁb\ΉèŽâ^‡ôéX{‘ÓÀ¶»`*ⳎàÃÏæ%6¢Ã á±*W=4ÉE$8¿ÔTA[³ä>õÝU %]ûWžUM˜>-`SµœÛp,n$˜V%*Øàt†ëq··=s»µ÷H¿]÷¬˜GÄZhþûE„®År]$®ï¹4ÙÏäå¶0O¶êC´÷á~¬³È1„„Vc XTBœHc7EÖ[E,]„ ±¡0ï.‡ÒÏ£ä|§gR‘Nz¨5‰¦%øyºãÑ „uŠM3M•ü×·Õ¶]ýÍWþÀé?áhþE„ÚîcåUÿ·x;ªX¡Ð:ØÃ—Hˆ»–J˜Qf*lLÆÅ! ²ãž|Q›ãåÁþ¾ã:7²csԻ¦=êöŠØ‡›‡ßVê^Vï=­µg–:ÝϸÀlF'sµ„?^ê¦ÖpÆV€}ŸyŽjk·ß»„ö?V}«B®¼Ú¦Ò™mÇ¢,Òœ÷Ô}ÙMl_¶´•/sÏÆ°èPñïçß^–(NÁÒò®oÕc¹O‹óÞLrY ž¯AÁ½U›`ö‘ Bµ d¿Š’6|×ùVFK1ë°"ý| L6w`,i-ªVöe-À”SÎPói²ùïuåqv9±q`òŽxÕ)ºˆ4€¦:khê¤Hå¤ëÿÏŸe6–7ñ—¿ýÝoßÌYièd††Ãqž÷¬¼Ýû†â‰ÓyoóëˆjØ]à¡à§^Úâöðw߬/­õ{”,ödüäïÍÌÉ•”7»ÀÖÄ2 @¦D†ÔÅŞùrB ç|ènUØúg=JŒ…ž“S·Ówÿqo¬Ý†ëyR*œ%Zn5¶‰Ëu²ßÝ3¬&oÁLðI lIœë÷›—{„'g{+Ä‘{zÞ+J P6`˜ ÷å÷IÜï£Ø%©.mˆR–(",€+a’­WŒ?È$~Äןͷ °ÚÅ'"!«rÈLzÔŸœ¨Á¢š¨1àp_=®í¾‹¡9>òüÆëþÈy÷êÄ¿0†s_}q"c +±1 tá¥ÌœCtý}6ÌÄ*fsØë€Þxܱî.¬ÿË×#Úîùê±.èïʉs7tà±…øð· Óºy&,t«>“î.œù@ìŒë´áÀª¿0 ¼¤§‹.†±{ Þõâï~øÜ3§èÝpÛF¡ìÃRà“% ¶PÁгØp÷¨½Ô¢Äô!3G<\þ^•Àv‹yÈ=Õ+d.'ž|tµ·CpUî‹Ó{O£5¿Æ`ĸbj§ Œe²®ÐWî†{¥÷5þh¨EõEúŸ9Ñûo]}ÖÃqw?cw^ÏÊsƒ[û(~o“GÀRu§x;sññ£Ú ¿wí⦚Ö³ ¸SÌH:¬ºƒ3ŒdõUsˆsÑddyv"®ÅÁߊ¾ñ· +Âl]KÍaš®:|ßãÚ;ÒÅ1iÃÞ‘ˆqúwÑGg ‰·Ò„ð_¸›¦¿´¿wOJBL¿,ü·Çu¢ûÝQ f1TNöe Ù}χûĹs¨ÝÍÈŸ»A^³£*ÉU#3˜AB¨A(Õ1dž ŒÍb¶sk=f¼z"Jreˆ:öKŽ#ºö^‹ù~Ávd¾÷xù° âß,‚ÕƒGÀã`§ê<£òÉj¼bÍ튲p²•F¿š½ââ œ%˜.ªÆ&Ÿ´ˆ¹A¹dÇñ8]5Ûådž4ðv–ÄŸ¾M ÕŽåXfÜcÈW–O^O‚Àµ½<>á6»;~ˆS@Ò_ç§lM…û€~5{;žûun‹îöKnËÝB\Ý®—7‡°ó·q†£1¼ ±æ•K&ÚîÍ·7Ūâ¹~rÉÊSJvî¼j¶1á³@Âßíöu<‰qÛ@à‰£sH¥´‰¨$Ó¥]û7}Ù©‘Dô®‡Ãü„óXXx@æ‹æ¸ÂàD3a™ÁN•À)D±‡sܱ¹†à°q‘c¯„Ž«ÖÎ=çÀ:ž]Z÷m̰Q'.Z¢ŒËºúé3é‹2, ðòVT +6Ri·ÍD=Õ¶æowïÜñœ™ oϳµÎXE… º‘€+&k¨r³ê½ûVu´Ê@[(Ø¡¯ÛÑ ‡NèšpRKè·] ÷ˆâ¡í.!ùB{‡~yò;f sèÐNº¶ ïi1zŽ.‚ålhãÈ9{›v +š&q×ÔÓ$þN#ùœ»áÞõ;³³/Ñνÿ.Ûˆž†^η; bþ&ðËô{[äz\À3ʾo¾4Â_Ñ Í£ ÷Øûÿê?^Å…zš&“5I†pÅI¿@Äe¾è‚bWŸtP±i’a ®ˆey¹eš'êÛRÒ b¯•h= ŒÅÅ8¸†Á0a¢ÓNÜŽ«–èåÎ4:³Œ £`e^¹Ü}t¹O¹§ûŠëˆdþu<¶[É5°Èl⪻«?^òcÛŒ€t%Å8][`Ì×\¼¶Šãkxjõ O O©’ én¼¾iËJ¬ s2¥VÑ™½w‹åDÚäAÔÉ ÛpÅ%`käóG ±/ýÚk-Ùµ·PÜ5»=qIæÝ+U°‚`û©A §oFž§Ô#b`„¸À¬ÞVŒ_‚P$b¾3ˬ¬Œà‰„à†½”- +vw Ùù]ŽÛX¬OŒyŽ-”]Ø{Ï*¶¼‰ÈÖõ•s)‰›ÅÙDdŒÁjÐVY±©a¨DïŠ1à {çbC!z·3&}¿,ý8b¨Œý¡Æ;Þ?Wlá'ÅÂIƒ|\Ï5/ö2c2;!m\±{óc'S:€œ²pÙs—´ \}äT%Ç€³IŽÙà9­JbŒS‹”7¼•$ÉsuŽ#Ú%"o}{¨D&¬—r†ä…¤õܼ<å,BBþîÄ 5€<ÂaÀG›Ã1KbÏþEªîœ™$B±å4,ƒ]ˆa4Î}… Ÿr 2ǦÃsHüp4$ôO°GáIâw=ý€MSª”­KKl_«—øb²¢ÅR4o`“x’Óç}ׇx$aëXÚÙD(´Ãÿöšy\â ‘f_Dè›*&È3 +ŠÜ-<‹´œF'ÓHºFâNœê÷9³®(œÑ ~Ìj …p–éý $1-ˆË¥c+´ÒŒ3âH ¿÷2^+‘)¶vâM¼å9Ä+.³m¾¯`…¬‰ûíYö˜ýÓ/Þ m<>Š5ÐkÆÒ®3ô™½b5Pfr2gD'™e„‘!|׈."¹ ;³Ì@- ¦Í®M•HC¾¯ëÎEãöŸu¹Ûž ÊãÛ/:ræC×™ ñž P¨LA=*7éñ­ø$=ʵåë¾íêâ]# )ú¥±çp–'= ­lõ€ÀÓ×féÃårrñ*n–Õ"Ï&þ‘]r‚8N76Å¥të´á€Å÷õ²žsŒXþ${ ÄåXyÕ1.Asà’^é7š»°Ë{[>þªq ‰€’"ñ;|kL^/Ì¢¼S +ÍæEDþÖ<wÕ·½Î#®£($ã(’8½ ú$׳ ‡ÐÃ&¸¦&A̓Š= 1Î?IxæÏËQ6—(dÆû“~¤WLRðÍðvéJ§ñ1(†¨a6Ô˜]"ÛBHv~Gà"Ò¼÷Jà@žY…÷4­ü­΢ʦQ¨½ªT:$ónGMý’“CDÌÁâ]ÔÈ–•›÷²„TÕøp¡?I +¦vާs/µVÝiò¹â>p +X…ÅA¬Í­>¢ûfâW6ûIÚfèè_½åÍð™°Yf0sì £N°ÄŸ§b›§Š/vl#cQÚ;Þ$qIÌ0ݶܼîw«'(¿Òë2د½K’\`4Žƒ£ƒ›± Oq¼Îñl­ck+ìð.Zéœ:f&f‡Ï~,Î#ËÝH;¡ |¢¦–ÀpwbÛãì¶½Î=>‰å­”ä /ÅsÄT +Uy2áÛr&œÊùUÆþ~3gœ!lЪўÓcgÉԜ9)]— ž½‡õ˜Vè!¥¹Û+Ë…ùpx¼ÒŸÌ¸Q„@Îr¸<Ë&Æ9ó‘ÿoôÛÊ8e2Ø•°@ö‹€Æ¥¸âFuOÁÍë=oG2wZI¤m3™ÃÂ8)õÙcš€ÛD5$FQÆ»Î5÷‚UˆuZ èÔΟ(I9I KuV!–Ô•i€õU"*@¯t/¬_²Û×Ìž‡M·~ð|wKõ´v"ô¶è°}ãp%]Ó±4‹Á‡Æ€ Ñ©%}‰aúѾÐ3`!.(ÑnÆŽÁ‚Æ –ååÜBz„a,L’ÔQ«ÊËœx{IÈI”©#]¾"4‹è‘d|ÙXŸ+…#L +â3‚„I`üˈ„ø›2OhqvežÇÜ áöUÝ:Z+AÞsT‰„£ÇE¹¨y†#¥Ê÷ÚB² .i]rÇ“R'ìÅÛFÀŒôÔ0XP2ÛÙïû¯RÇÀ”|f Eöÿä¶üÄÒ¡a[¨Q”ôÜéÇÆÆ1cµ+ÐßH™ˆÄâ^Qƒ˜ë1H‡hQ7î"œà¡·ª¶d:]vÑ ÌtÊ´ª›BÁL Ôƒlц–$ßhÀ7ì%`ú“äSÆJ+UÈ„^¤þ¶‹!Â%ÇTâœ"²þ•±hƒˆ¬w<0‚Ç–¦ ]ôh‹bÏ+®ö¡‰È”TÈÁ©ˆ‚š„~ËåÌ>`îÆ,‹Š2º¯oô ±aŸx+wSzÁôè% ˆ@å½/ÝÛóB³µœ°)pó ¼T· â±€»ÚL–©y4ŸßøäÈ$¡|ó±šèΠ +º…?Œ<Éᮜä‘6‹L1MøÂˆšoüÛH ˆ{Œ7åñõq gO•Žu¦ÇHíØ¨—k»5‚o»;•I}uÄzâQ ÝÎãxÀ2¬Ó¥ý¡ '‚yMdn[ÞO‡†âÔõ*ÓÃ|k‰ßŠÔÉ‘ce _ñƒžÃz>kÑH!a„µË—’-Á¥³WÉ×€yâ¨obÃy])㜫 +-­•CKÇG ñ9?~™B½|ÉÐÇá“ì ¾Îøà Œµ×Ø´/·Ñ÷$^Dž"Vu»#m{*®™R QZß<ó,DâÔ%ý nÂpt³†Ó½G#˵† +:¢ %нñ` !cõLŽn61dEèí<ê}¾ùýN©>ëŽÄ%©—ŠnQÒ”­©šá”%Ý{[ÁMæP1ËѺA9`9-3h¬tùwß`ƒÒbOÇ`Ãa§M…/B7íþ .Œ¥¸¶H6¡ÆÑbMq“D;ÀÉÑ_˜W/ìÉÕ%!¼®ð4©åN(°• ˆd÷xvµš‘ê~¥}‡›¬®cFDy+;loË´'¡“€Ü5ÀMð>+^ÎŽ +ÀMa“½F  +wñ×9ŠÊ/î âÍð ñe©aRæTØPæÎ‚Ge„‰£KéÜ!a¤ÄÙ9]·”Ý\:§Ü˜î¿üM—@d”P-!âž)‚}AÒHAV#¿X°u׎`äÅsIèŠÅI"iÈösáf¥:õƒ¦Eø +×f¿“7¡Ê¼°Òñ0¹ùQ£çàÏïe†Y' q‡žÏ +¢ìqOËÖSÌÐܲýQÎî,wˆ÷åU .=1]ÿ­…¿0wŽûÊ3fu8vfÚ;ð¯Ýïú6’rkÒÛä ­XˆÛÈ2´]~3ì:¬´¶žz”á¸a‡žW˜Šò¡à—’ÊOZíý ÚûHæDë3 ô"q¹5ÑÑÙÒÉÛ¹á`l‰Ú´Œ©aÍog »Ä’K‡I°&¨§PM•©¢ÕŒ‹˜œ†cS|NdšúQS@dt®Ð" Ñ&妄)¸üKo’‹&¦ÑäòŒý«c¿¦ô Uúñ¹®$ub›0ˆóªÝšÃ ¬-Ãç•ÓÛ6wÈ&À¦ïmJ;`HÚÑçñԬɤÙÁ¡¢¶$«÷0ìŠÆF‹*W–gºª.Á€dH ¤‚&k¢E|]"sc×"ßIæF ™,U!Í¢hÑ0ãâ¸])¿dAЉ3Ev89L˜­šš²–'–Vx•]Œ¨'’vm“×Z˜·ÝgÑaº:Çj–pö„‚'­E÷lNœA±ßÐ9?Ü€s{Y÷ ¾?â¯Fv@oóîÄXø:ôͯ;†Ô—{Úmã›Zñפ‘Œ§¸pËD(= $«–ñüë°ÌÈÏ"TBP‘žQ¯»OÈ€ +X~T`çÚû·Åˆ¿¦hèi-¨KÙ=º]ЍŽ_›ñöb+{¾÷\7±¦C2†ë®3Û.4R%‹·[#›onLOzbgÚF¾”¥ vRLMÖÙB¤}­²ñý9[Ê]ÒKòè0r’ú¾¶&NßÙÐklË«‡XcÙyëá.¹¶µÐ·ÙCĤ[ÃC‹"ñf&V?7án²ÅQOøM2%Ä[ §c¿¤HÛ8#›ûò/j-Z"œ™ÉožÛ™¢Íh\K®%ÒÝ.1Íz^€Z@ æ±ÿVkÉß¾¸9á·HÁÏ/°o÷¦h›è‰ƒLXÉñ(?ÊÔ#RÇ™š6àe ~o©Q2+KûX¹²'JVzn1\³¡‹!|ÅD;VU·ú@Á¡ +™ªáÊHããÔGuŠ·ôþ,c$¦ÀM!Uä›6ûÐÈÔŠ‹ÛrÝÝ`*ƒ U’–¬h%”aiæ0/sJ÷LeOZqýéåÈÉšFÛÒ:ÝpþJ´ÓÌ¥9Š>.}³v_ ¶Ÿ + +qëJº© òŠÁÇ.¬^ q­z¦bùñC È )ÀxŠË§‹{Š Õž•Šõ”bB)ü’EK«UÙ«zªšèa%¹]®?jUr$ä©ú®2«d!øòæ+ÇðC0/úq‹gM‘âl<‚ endstream endobj 380 0 obj <>stream +ûNêFâsÕ,ÐÀâ¨ÃOêHâ0« TEíYÒfÈ2 œ( d†&wÁ¼ï»@Ìj%DH¤¹;Eemq@Pn÷-Õ»Є`X$Ü­ÉU›£î);‹» ››cOìõ;SŸZ$]ôå¶ÂÅ£ZÎóNI`V¼vN¢Elƒ¸:'µî®+ ëÛø³F¢È¦klRЕˆ…†¤fÞ ðÇ™m˜sH޼„¨¾Áf—Û,£ ²¹ž–•XsvZÆkîj×Gž”1ÈTïî vïW˜X3œ§Ø‘Ú=]£bÂåV4cbkyÌX—î|˜Qªè8·W¢"_ÆIÀƒiû×GkÏé‡?! +,5ñ–t_¡c©™Rå^x0š:ÔäXÍœWPÒ/Õø´ÁìjMí­'·´jèˆl1› cÓ“®€åH1’µâÆ(û‰%zh8°5…(¾m¨ZäwÚÒ©FŠ>‚©ÂÉï1È`O‘ŠT¯›u¥Àö ß¹X7,"ðGÄ+‹â|«¿ÝKvéÒbpp’2ÑËVàÆ¹Ip0acxÈìÑB(ˆßM#óDy$Ýšv/^ggìKcŸ±4Ž™Ø¤0â³4Ñ™ï|ß8gÜNΨgx®°®#"M +ÇÛ$ê-—êõÛxp“÷6 +­ôc-òe?Uìû–‘R…Ю3«š>¿.µq² D|~B>kEñ± ŽUæ/mæ¾QéÒ´¤€-Q‡#Ñ“*/²úú3Æ&í_ÄÏÔ(RU. X›µ=5fâÃ"b !‹ lRÓ°º"ú„bñš8g§'‰ÁÖ'´*â„å"z¼b$+¸Ñ£@€5¤!›mÌ19óÊK’•+{Q?fQ«3eÎÂâeBÖÎ@lXÔLjФx’lbB*ØVœe£)§ç#RÆ~¤hÈ‹{ÎÆæÊwk*¹­`ẌԠÖ:Xe6Òºœão lšWV8 ³ýnõ•€Ãö–Üf‹&öPs+W*ÅMz‹É€þÄJ%Æ)]Ĩy‹*‡&’°)•ñ>jH]ç®`—#9ŒMg™¹’Aߘçc‡+æaݮùƒ3˜(åõþLiÓÈÓÂG*ðB¤ƒ>7pâ ÞÍ}œœ]TO@.'³›bxsÙ(ÍÄ€jxó9Þ¶ÂúqÌûÞÚÉøÞ¢9˜»ût3‹µ²ÿNA|@­VÏþ{ b§!îs„üfÏÖ6/fÍ¿¾í™%/y ­p ¨föôÂH¥Ì'„º¨z¡$0À¾ô{K"ƒáÖíIÎ>rˆ§¤$ù´®´ÁHøîEB˜¶{yêåZØ™Èa±2mMÏ“¶׉ëêŒ`ö®ÀÈLit½ -9Ή\•l£!SsYfj$Ø%kŠü.†$‹¶2QÔSCÁ›h'µØ˜Ù‚ôœ’©Q!ãðÜrxX ‚Ü8Q5ñ¬¾v¦ËZÕ$°“À‰ß*[mº]TžC4‚½êG`®eÛ°ß}ã)‰V1*è„PÅ}¢¨ÙÅ>ÞîlUC[V–rFʤÎ0ˆdÔ›_b¤¶C7ªW#œqpÎÍÙñfÉ#{3ÁË#ô EKúŽ(áX/Ø )DCW„Óª]a„,=A735·B<Ö½ ½â»M5E–HOö-:§ïC`ºÍha›RP4 $—p/ʹc"|Ì:ž[ šëq™ 5Çbá‡\Õ"|ÅgW|o›ô0Êé”9Dz‘© +Gý|úin!̈Wì8”Ø&aº˜ÑI£÷§\¨+![wjЧô^%?"ÎÇ-ó¸=g˜¯ÉZËe}é­±Æh î*—ýJ9T¾ó5­yË×IVml&2fð‡–ÓZ}sãwøº`Ú HÓSºÑ%.¡þ‰#S •U­à”&5¾£^vŽ f˜`DäãÂ&†[Ϥ­ s[èš ¶C¶#¥?´jGj¨;{G²œj¦&Ë&r‰XtòwÒËFÜàȤ?ŽD¶p˜qŸµåàȶ¯©¢„=I¨')ªM§ž¹ÏÁOÈÃ…[ðƒ­JqF +ÑZ ÷VF€&u¶_‘m0O•iu#Öeä?Ò\÷–Á÷î3x›÷2œU;Íù©¸‹À- "åˆÌÛXªþ4ûßÂAÍJ„¿4ÑzŸüªv|Ìj[‘­Ö‹—ñ*ØDp\ŽžìÈ6{ô7Þ>ç¬6^Û”æ-HK6l^ǮȌ.Êþ…°À÷¨ÍäÔ80ÍE™ƒåì{ƒär¢ˆCí°‡ÉDHøº¾¨æÝ‚d²bž¢[ª‡"lýó"žïÜBŸ/2ã€Á²4¸¿»3ïøîëƒðû_Î/¹Ú? Kc¼Š;žuÐ7ž€P¯õ†xk\L“9°Ó%´­;9Ê'KÓ³³î®+Ì¢Ó´­Û “[w›@U‡Õw²½%ié â +UY§ÓØ€'¹búHÓêôë9Ì·æ‘nÚfД¢8Ú*û)¢FÁw]Î(ô Ùæ•%§§0®Á=ÏÐíØ§åü‚&õ¿ ùõÉ’_Z²mÃMrŒ}°ÉÊém ‚ò‰ˆŒ'Ç‚¨o‚Àø£ÑI(M…íVªŽuÿ؆âÐ(`þ6ñwœ° E3®•üÄ`)ó'Äñ­¸t`N§Ñ¹’Šðuk7*Ók6õÊ–9£È?Ͷ§Hˆd¼+In#B²ËW8\ƒS5†’Zàs›˜’j ha%4Ñnñ9 +ü)gž!R"À»-ÜNçùüKŒ&Œ‰f.i¥Š—½+釸y1| ¡ÌHÜ• 09¤¼ †P³ã9ßÎ~¢[r&  +ÏÈ¡vŽ4S¬WRC¼=ç#.‰ƒkí^9I¦TÚ_ð˜¡c^fذ$¤Jñšç œ*ùs®Dù‰® +ëéòT´ldEŽˆ¼ö¾ƒ÷«¶QÙl¸¥Æ¸>òÎjé`²Y»¸Ë#¼ýŸË†°R[4ËÃTijrî"ÓüFŒfóSõ²Ž¤8{pe²>wƃ·QFMuD±3º=ÄZðƒ«DF^:bÏd\?FÐ, ºj€ºfb+E©½+œë!6 ŸþJÑX.éì„1aeºï^‰ö3bÑàW¨7äùàõëZ¨¿ADóŠÔp»6/D¿IP±—¢ÀÄ€ix‘ÿÛ2C ôTѯ¸téj`(· dZ–ÏÁ­BùøkŸî ¶*êe±Q…jN–¶²s—’"!&—p ´5 +² dŽ’ëHPÕ˜ÕÚ²d¬IÛü+ƒcEúcq&~æhñYà4”|çUÙËó[nÆûŒ¾‚V Jž¼ã"èƒðAÞ©a"Î>Pv1'RAÒGiÁ½¬î™˜q°ÉÞ‘($ä|‰ÚSrÃU»g˜ðº@ÿ"Ì ÅóPÄÍ>v0œ?dbnןáò‡-Ì1Èß-êý»,˜íÊÙA±H“ƒpŠ+TA²}$˜AdÔ'0"ø²!åc˜„B_|9×&gæ‚%@œî ±ù Ú‚#b»’†R}z#oô3¸4Ö™4´+›:KÑ^©»Ét¹º5Ë%d”/é¨[8qÎ(˜†]˜Èx–&¤=ß­À˜©–cSÜ¡/—õlIlŸ(±[+gÍ3Âk/ +—™ŽÉQ¶Ø®ZÖ}Sü[ªbXùŠÝR=#4uK7®ˆ6ô­áŠo9V´x¡s+ˆó¢¸èr)À5Œ”˘s—í8w=øÛ¹ÚúÉ ‹Ùù:¢¤¾K—?=©ªÓ‡ïÛ±¤žˆµ=Žß=õ‹÷+=zCüÐÏïš<ü96„úJ•˜öˆ¶•3 Sà;€/UaЬ ¹26ß²i#$94ì%À"ÿc×he'»ªfåÖ¦yÅ|‰©0Â;¶ÂžˆÙR6åru’`‡œQã,B à|F!ZÃ\4£ë 4ñ4dÅ~³}ãw0Ú5b/UÝxM({8q±±”°q° Ön°»ZÉù!FLkNW› $#Œ:’˜mWaùN{OFèØ¸ÃÚ„Š#´dL…ðLh[EðvüZÛ¨ŒnÃíca€$<Õ™?,]°•#i’ÆKpãI ߃÷í»Œïgæèk"·[Ýœ—š,ɇ çnç`ŒZÒ„ (roT{ Ggõ8îx`ÛM zxG`Ù@™1ᦄ3¢µæg0O°Eé›a94 -âêU7—‡@ã8LsKå“ý”v°Â0ëÊ›ºeI¯D›äØI¹º§‚ÆêæQJè¼0ç#⪻Â_3Bã ±-Îô#’M# +TGä8OOUKò½ˆy~騜-ö P$,1÷V‚mBÎ&)z„Yl¯ÉëZæ ›©¹ûæXVAýil¶¢¿nû £e#Þ`ù(J›ç(Ú}òEþ! )ÖŸÈ1[Ö³Ã{® ±ßåÇìbBðŒ” +¥¥…¸áÏuŒJddø ø?¾øSÒhÛqÅuÙä l#L;M)ÓÈ".{z‡ûEœÀò4Æï/-âØ®à¡AkzfØ`í—&±`#A¡™š4ºd0iôˆ,™œ<›\QR4ikè4u– 袳O9â^­&]„ C]ÔæÁ#œ¦ Gä^,7{™›o97ˆúö!Œ"4Á*¥N¬ÖRK,/ÖA£'-å—#äR.æ,¡â¿DDŸÙqŽ0H­‚I7ž©Ãqbf JíÝçÙ$öz•ÀUn3Üã4t›n9þ1öjäT Tl5FÒ2å9‚ÄL,ó„B&h ãÑ1nú–G>dIݾURjb•fKº³ëOÕÛjŽB8[Ý  ²òÈp .å¡ï"/Á¨¾§†¹©Áæmij@NÌÒµ£ósÕ^,æ°¬”¢•Nƒx›ûêÀ¹ÐŽÝ ½5Œ*p³®î‹®øu6áZ¿§)x, Æh„Ñi@)´žü¹ïHB6}YÉ\`Å}ˆìTki¥³1©§Ü|”IiÒÞ&Ÿ»VÅ•§Ó¸äšj,Žï¡â‚€_å^]Áv+ Gõ­ j¤“ j?ö §jÀa¤ªbZ Øþ.3Š+úî fÁ“‹"Κ†!ZŒ'±ŽqjÓ!ÑéVm:d9ß_ [Ûd|bY‘àÝBÓ­Ö™åCÎ%ÙdHcQܶ'·,ÖŽ•¦žR€UØ"+}j‹6½ÿò&"ÕdŒ‹‡ÐÇ:ºâû÷Æ) M¥^úó0XÜéĶî2ž­ɵßÍ`{y²Ñ@è’ZOI¡>ô`b&”GØOÖ‹¦E— +Ëœlž°x}f‹ÙKùXçÒb[3ÿÍ`­P<=‚·%]SmÁŽÅŽÝoÀ+Œ*ÚVe¯›ý¾íŠ*Ù¸î¤Ùê¶&ì‹Å1{Јe&/DÛn¨j" F˜j +™<%áøQR™çf˜Òâ¹Írj\ûVîìf{ÙNç/wR-¢nî1׉;Æ@¹ÂÚ嘑Ÿ_™m¯iÑ[uzC>¨šz-sÓBWËÛJ<#væ¡MÈÀѬ3¶óK ƒ‘8zá»°'&Áqô\(]I(àr ªzÚ'÷)Êi*Ðq¯AöîG *I?ò“:Žç•°Â‹HÅ8à™·zy#WÁó¶ X0gÙ˜jvp´ç8÷ÀO,ã5 öëËh%Õ"X’j9~à åB<Ôõ™(›ì|ìÙñ| T»b†u´m<«?ޝ dÏò¹’+V#"ã+´…<[qìK‹].}#†5×Îh +y†ooéãj¥˜?©à@‘™ bôÑ¡‰Ô¼$ý#e¦”O~éí¥N‰·µ¾\“2Æáepmµtýµ™¬~õ“fÏ)Iêc‚´:áB ?µÚòRîc|¬@(µì•#üê »h)㤠+%ôëÜ ô¤åX68Gè婢4Q®Ô¿ÌOÐx–`1F Ç&äI@kk%®¦ŒÍ E±Ÿ©>ÀõÃ`Öµ)CôI2ã+<à #p;r.TZKi’v¼¡à("ŒÔP€¶/|âTB¹XN~ÃŒ±C‹ÕIèW ¡¨ÚäÀõ·›8É{î;£º*Ó&\®få«PtFêç æ”La8¦È9‘][Rº«%ÁoΞØûŽ8:ÈA)ß9Co«Hñô Cß×´m­ kŒT"ÆÜJ`-aVÐGŠÖÁvê…QbùX[­‚£ ?0_6?($ +™îq¸íh›™€,+¢¾çfе!ÅŠ]6­0Ð\–“~mÅôo! ¾<â®vÂwÁS…Їf|Ê£’B¸®YG‘à â!ˆž" *‘BL™EaP+W>¯oÑåTíÈR™‰“XÎu +®¶—/úˆã]†ŸÍ€àÜ¥$ùµ«O±Xth¿(ÆIôd]’S«­jl`t±ÐŠ2qíÌٽˎüüžíràžÙ´ö4úçYã~rhx¢¤ìê`i˜3' +„à·¤@Ý@íQÍ¥+™ß+!q¹¡ÄÚf Ëü–{éTªßv̈*¢LÍhø©_.\½í"¤ŠZ¬\Ãÿ²ü-çÆ›=Ŧ¸ ÁËÉÒ䤭e 7¼ˆY)¦ÜKpÄÉÄÕ¸ŽÝ"‚ЦŒ!Õ:ž ×IHÕ2÷L™Qm(µÇ†eüÍ‘¡’Šg§ôVª&ç™pt‘‹š IÓö[1ª÷ˆf^÷ˆ"ãçpqÜRâ=Ô'9Ò¢ï!\BJ¡›û‡ß#:¿`Z;IÕŒ + Ÿ!2I”šÈUêFD,JTl€ã®$Ýç'ó[#ÃOwZSjEèô­Øtò;MÑ”º^[Ý©±i•;èÏÅ"sQm¢­‘§ÀäL¡¹‘vnºôí|™¿ðê-ï©õîÒ.a1m*¯©ú×Ò½ý{0ØäP„:vþÁ-§ç!£Øè»VU·nì[³ŽÔëªms’¦Sù‘Ò¬ÊÓ³ùûØEžRÕ }*„9WDñ”°ÅHšcù¯©³3RM{ÓÈb®ž* +fÓ!ïáàðîêZcWí~sí±Ó+Dšˆ_-wiaêò°Zg†®mVš—p‘?ÉàG÷4ž/$ŒìäŒG’Ž?n>“dF;ëJ +Zx¹$JŠ%ä‚` Q‘Ú^ +3|÷ \|¢ʨEÀ?"p²6}‹˜ñi’~Ša'qÍ`MÕVW +|¶ítmXÄ„¼®ì´¡.¤y·ãw`ÊQû…ØHQŸv‰~Šö#±ÅTpeU•MîúÞR@°¬öxj{Ë ÖT?´æ‘§Š¸²\–niÙ࢞”À:‚®}ǃÔÙÚWÞwåoáŽBr±ñ s^-×”TÙº0‘9ßvΧPÈŠ8ˆEŠŒ béC ~̈ÑÀD´–\C^¦neƒ€öL¹j ¢ Δ¥=N6Ó h‹>ØZ€ ˆÓDZcp6q/ú·%lºFR1ÎWER›‰1¡±y¯Q±IE‹"FúŒ»6‘îÏÓ?ΘMÑq#árEõLaM‰Ï.W à4¼Á¡©BØâ‰ñó+EZÂÂz£¨EK ´í¹k§r‚BYÖKM…?ö¸°°=;­?ž0è +L¥AŒt÷Žû ¬58£¨P—fÞµ•LJä(ÖC0œHH0× ^(Z…xÝ©Ouì†fð‚°ÉQÔ&KÃPÊññÝUâÐ +1¤¯Qrv¥L¬!•šS `7s*”ˆ¯ݾ¶ÝwKžÁiR_©Æv޽ —Z…KrRa 2äÕØô§xæ±¥&p–,iS9l„ÃZ—ÐÅ„^m…Lí^àÇ +ª¢Ew +NÕ`˜U§Ñúö°µ¾0±ñ¨¿WH•H/§¢8Øì!ÝXc—x›Î¥†µÌ ª½ä„äвy5üØ–ªÕg‰€Ä•¨0¶SÝbâJT¢ªVD£ n±_Ω +9´S¡Ì¡HŠ + +æ–$Gø±ˆqÛ¦n/˜J½EnXNò7ØqŠbr0-U»×&yœ¡@áÆ8k˜Ám£¦ÜvЊ‹2MŽEgûiš”Šù£4<ņC©ÛžoZŠö•âj܃ çÏÊÚ SHŠìO®AyZtðŒ$³eJžaÎæe3ëZ‚âXÑÁXµº-’)\‘#¸>Ðdå¡Q6F›¶‡E¡7ìñÈ5[µÛÃŒ·¿0ÞÄ穵gœÓäW=÷r•‚;ò3—ÖÒÚV6çsߥ‡ßÅg ÒBÙ#ᔯ^©d¡£$¿uÙÇs‘@e¤ dÓ€¦ÕØ»­ Ô£ä¨ÏEŸ9,¿ ç¯ÿÃUwÿ`™Í°ÌÁ VêÏüº!аþì{Ï~=X,ÿˆg¯£sÂ4-™Oúv£e+%îˆRžq|GËæÁ–Щü<7J5þìˆgÖ…MìÂò¸nçVVÔŽjÎ+€ïpŠƒp){„HT´P¯D%‰2­™«Jô…zÎ>\A„ÖC=Ì ‰ÞqeWÍÙÁÛ¿¯·]”ÚŽ~Yc%qÎ/˦ձó,œ”–È‘ÓXh¸¯6:^+Ŧ`7¾ºKÐÏø;¥"øXO7ç)v› £É¨œ[¤L+rwÀ/¥cò?øÿ¯øÇ(Ùu|×*[ +xÌÄ…?Ñ¢@’ÐÔ[´OÞ¤(Z3*Â,·jZt÷Šx*"úêúÖ@A{[×–ý%¦´H¼vkÌÏG¬ëJqÓ)YG€XQÿ~¤¨H€—$«Q›…aIœm:¢Ò"A.é_ýKY$ÒÂ0ÀúZ©1,¼ŒHÂ÷˜Ûr4gä07#7}”ïqpÅ:Ëc%£¬~I.7¬5K9è f5ƒ~Îøp]sDö›x“ÞQ™nŸøw¹pC'ÏH¬ˆß¥{©Ä ¬³Dku8GØuì¼â?—À¾e(D¶-BÇ{£Ù?7 çn†û&‹Å%¤v×s?w­ì‰M aYëˆ#Ń\“»¹Ç…þÇH¦»Œ?ZT±Ó>Rbs¢;BlD¥ÄÜ^‘Ÿ=*zu—ñf|eO=’Æ‘‚*-W·ýŽ¥Þ¸ Q嚇úwמ•FØSÚT'ÈàÛÈÓ! +I]ð‘2´k·ò݈u8fÒYü á}Ì(r/5f‚ï¾Ñ7¯¶ˆÄd׿‡—?A³Ú8Õøûò²eî–K‰<ɮǭ·þãõùŠÞs˜-^žù@ .{h+"…,<%§ëþQ£tÄ +0JA:²Þ¤7Z¸èЧ¢FjÚãåc¨*“œ%9ˆsm¤î6Ó•ŒÞ+ä®ò%*Nä}$æS²P¹Ðu†.H;Œ4þd+£Äåz€ÄÖjÁ'ÅC|&Ià*„_€Ô6È×ܨ|Áøà¡ñ«˜¹Ì%ªÆË%n¨ý$S¾’õSˆ!EoÎ΂¥ôø{¤˜9$hææKõ­ìD" +Œ ŽÒÑøÓ‰-6êÉÅ*žO–©gS¦ìè¶K¯Û‚L™ªä+â Gø˜‚wHÌâP2…`β'y¬Ä³g59ˆ®ôT •ѹ+ãCÅÅ +ýA^a- °÷ß<]…IR’Ü£í'lƒ3,NçÁ+IzàLj-VLNðÚIYàÆï| <0Zpdç‘2zÏ/XdÞ!WS?|i_„DóŠ¿­»m) ﺶv\ÜÏË*3‘ذÎ@ž²}}S+²›hš²³®Ö«¿ø[(3CYÚ{iìŒ5aɹK²ãÒDœ*Å^Ì–¤­·ø®s=e¿ÝË{Or2ê’dÏ”ýu`”GòtשÇM Ã{XA¤'yÜ6EÁ + rlôyðgQ°æYVHKK©TÂLÕ398w±.)àÔÅd5"%ØÑðoºOè;Œ¬‚Ò2”b´ oñs¢-3¤Ë¿ûæ 뜸Ê+¬ÛÞŽcHìg­\¡d¢-ÅU¿ö’cSÀÝäºrÁgûËÏâ›ÿ‰<8…T‹/#ÅÔâÃðˆš\'­ +FüJ=oCdR=¤SëÖÄHÔߊótîêéÔöG¹M¼‘>>:#LIò"j1ß®cóS9üápP6bÊ>{9ïûljìµs¹°%þ²Ž(Âû‡Aýâ0@9Ò -T¡wÓJ‚R.%jÖ£ïY+ëpE_dWïâ0$ t 7µ¶Ž¼hXι¶ü•\J‰¬üº"e»=Þ¯ž»¬ÒÆGÔwÑì<òóžU‚¹…ÉÊ+EpS æ ˜‹J +ŠA"“ê0b—–©äP#³QZr[©v1»©±TŸXGoS/œQÊ4îõÀ4œéF…«è_x(Gòîæu˜»½GìÚôSªk/‚qâT³Ú nêY‡™5‚ÿƒ6MJ¹Œ(Ä¥8/œQ+;¥ÚÈ×bŠx¨±\“£ëaWŽ­G¦¦D A˜nÖ--Â\stê(Д”â™sІhfœ9ï­hS›m¹îw^¤^[ÙØÔjh¨G”=ä« ç5kÓó,£H§û6ǃ5w`(ÑaìÿՔ°»îýjªÈ{õêxëãkû÷§p^ ï0%n+gÉ Ë3Gêí•vï/Ë”nXªâ&•ß(OttüÖ.ì bÅom›†R€å“¹nÛj%÷×<„¨ÎQŸpÆIž‰Ê¹äëà?ŸŠ~ Ÿº9”Ʀä‘Åðó/!&„ +š¹h¥@™gäSr›ˆ¬îÁ—Ö‚áÿê*#É­5™4Xjž‹P€ª€Y8ÃõP~–N%Ë@ ®„%vyî#€Hã|Û¡óŠPÈ={†ý0šß¨šˆ¼ˆÒêfßhâǼóI&Óç§&¯Ù&ª§JÏ ýúÒ@çÔ„B=ÅêAN¾£‰SÓn8c¨QŸ¢«TÉ;Ÿz‹t&øÞW¯Ÿe‰¾$£³Ò€"WÊÊ”„V,çXM¢Nåµ”Oe:Ì”­ÝG¶’Õp_¡ú«‹ “¶â%±É1nn™Æ5­ð°-vwˆï ™µ-Ör*˜R,½„¼HØ6D7ø \é×èK1ç­wš‰n[, Y ¬mvú»¯—É/–.2óÏ!ÎÚ^#‘B·º½¶OÛ«Ûêä Ï(¡zXÌ›D‘aoŒ £Ü­õØú9çÎ0Mô¹€Z[ë¹=Ê_,ð/iü>#_Vg¬Ö&:#QU`dßè# ª„™¨W<œªRÖ‘–g×%½/…$•ÖÙ×RÓ“ãªì’+ª8ã:AŠþ¶²Œ~Äç¢+Ç|86Ú•ó§~äÕ7Q;ü"¯‰3¹ŽØ•/A{öí©¤nŽ…|éÏ $¿ i÷´\"ûwÕƒ\·Éo›þ„>•4ôñÔê½v¡B‚ø)dK[ S|.yoÓfl +»¨^¦¢’#T:C,¿O`K$TUÜ«j̼:K  --Äky¾Qu ”½ôM[¯êZââ/P;Ï:tcÓæó<W/ã)aÊ’%‰¼`àÕ­ÛHWi ¦«Bb!e +Ãx“#³É“ºy›/KX ³‡—Ñkµx~Ôì´Â<½É4ȸXÐþxÌ] R•I‡s¢p®ÙØÃÌü3Ù"Nè‘ÚY°‹ sœ)n•`ZæÈüa!8xE‰¸7.•TX•âjêOùÖè‚ÁR“·G“ÖùÔ‘ß(Óœ†žº—³‰›³MÂ-«†Â7ÞȪ¬B+O|¹v_ÓžæYçóŽ5QtúÈòêŠIÁ°×–S¥‰xdâJaAgågÚÀ¿ØÕ¾¬P!WÉÛÃÂÃ-¿·5ˆPŠŒ€¬(¯µ%ìŸ)йl¤óLl0uÞ·vZCÓBÁ!œ5˜Æ¦[Û%!#áÚØ­ü„;HYe³.çn +ØÊ6Þ¼¬¥¬¼vèU,È&¥ðÊÓdÙ´ºéÓ)i\à¶©ö¥¾«0·᜔$=°üÔn,WËžèùÜÏýÞ6Ë,(èà…3À?RZ˜…Àb$ÉÖ”#¤-‚½ˆð³Q´G‹Äú¦Äí¹+ UëÅ4QÂð¢¥d…±JÝ•×K±¡”±‡¥ZÚ„;ÙÓ)kß/ïøGJuR ®}É;C |çK†\DÓ)Úo×]´…“§Õ´³å’Íi›BG´éxo›[´Yú‘Ž2¢8Æ|8?½”–;5˜w¬Îf—÷Më"GÉ Ë€ +}¤¥O_´E°Åi7Wú¼¦"›3ˆ­´)¢qE‹ÌèL{à´œ¹LW}—'µ,_Ë鹜ÓÊOpÙ¸‚Ÿ´„)3·©³ÙóRf-D£\û)§>¸ð™$&Èd-¦2ùƒ¶fÅFÓŠDð +jr¥ÈMz0ïmJ¡S2ÈíÌ…ÇyæWnžþfΔú^—2yÞ*u\¯¨XVeÁ}s‘ɹÓ%ÑM p®SW8m–û©Šgg!ˆðÕ^óšÛðz*•œŸ¶a¡e ]°b ö¸ªqu®™ûjûð—úÞ˜’Ô^0ð|0Ðå€÷#lfMG-N){W|â®P”I¸³F8ín²dryÊ4 BRzüïLcMÏ-s½C˜Ð!O#±õQ#<Œg\7#˜†H'öF4êÈ:Ye¯Š M!‹Ê–,Yù§ÐˆÚYd†¦[\• +sªºÀ)]A0ºÁ’%JåÃÜÆæs›êg$‰ÐBfÜSEº»Z*jhêŽÃïSÒº/¦:±ö1¢Ü¡¨ÿ;‹'Ž-æQ¡ûV ­a«> tZÄ£™!UY¦GÚå#ÅÓp®#|AcÛ>-%­Î½ó%CB·v"†#Â…T‘ákå7Ð'Ja)M16‚hÚHíO†ïµ.©:a­­Ó—µG–¡m]Tx‚"?~%öD£Z‚¤¥.s Ä +®aÊ0wÔ«±ˆè4#[ËŽ13'’S†oÖ®½,LÜØ¨DáyÇØÎЈ3plfLGâ”KgZ@p>Zö !€10ã3Í”tÉlvo‰…y…qEË åW°MðŠW±ey¦’›"œÅ亳)~µ:_1_—O+y%%?cAÖý¢t}:؉c—[9&Q +m¶]¿!>Ë‘2°T=1ë$JÊr§Âû.c„jJ›óU¶¼/•B Í _,ÕTÅ›¨ï*+Zëœà–2:»úíÁñNµùˆæ¡ãR¼HFjeÄO.¤'Ëâ«äërè©àzõ°àÁ/xÝ,¡ßÊü¶3•/dsÊ«¢"‚:ú´²ü s ¯^Sk¸ð**–sªÌ0l/5GÁ–VÌÄ·U|A5ûÓSIU”'¸SêB‘Ö°Þ+2ª‹ñ"£¢Ào´úC4-°W´ala[pC!¬ÀC6,¨èòÚÚSûãY†*beÈŠîðÐð˜K‰I*¯o¥§|7K-0q¯Ð›Q.¿ +ukýš®kžw~¢”–ÜÍB£û#Wõüh@Á²ªôÔûN‹…¶¡¹égêÝó™gÔ—Ç +_¨Ÿ–ŒO*¥ÃåÙt Däñ$ìV“¡Ü]iÔ*ÍÜ^ž{k«ÔDé_µy•1Y3¿ZÐÄép¹ÎR½#̾=ÔHTŠõun Ít§Ó¨<…U¬;éãæŠ¬¾å®Ô8bf6›•z]…É[ý¤™I‹t†òS¦X•~E HáhÖÑ1´j9]Y[3©C0lÏ× õÎî†3ÙÑ”gY‘ +[\Ñ_`AF1# â{Ü$<˰N[d¹Æ.5EKFe”]ø«ã«²Z1¢þ¥Ä:ÿÇ ÷t¾ü_£Õãc\¢¥ 󯬾ÂVÀ®œoìVÑ%ñÎB$Cöä‹z ¬#ÕyivN‹‡RÃ}œ¿`5ÎQƒùéœýRàï*[–ø1ÒßXnb¥ŽvÐÆ=‰xôHFi×G6´"ªÃýc©`®*œðµË·lNšÜP±Ÿ’‘ŠÁÈQîn!tKWÚ‰ô(2 ;®èm”òÔ[‘[+QþïÕ2ÊîE>d°ÒÖcj3ÇxI¹×]p´ ¢¤¢àN×FO©o¡…cî"0n(í +üÌJãqY‰qUK… Ú,¾T;jÄË¥Z&Âx;åq?ßYKBû™< )sp– oW +5Èèõ€mꂌ}™eÁdÑ‘Vææ‡á³î‚ +Ô‘4xg›£Xþilé,Ð8Ír€sSO°ºÃ¾Û:&ïì”XðtÔŒ:ÕÊøÒã5-TÖâcÑ‹ˆ>óRÄŽ¾WÉhæ“GôÔ²§ž:Lê±uÐb–;ºI›BM®Ìí:´Ô ì9ç·± X„ž:dBÑãCQÎ8 !¬Cé;‚¥Î×w)î¢ê÷®³~QIp‹ÞsÊ¢-sEË]4²*ýQu7…‚¦ÃØê«) ó&AûíQ¡^­G•!äoÐî!3Ý_Ÿ+Ò°#h_­×Wô5Iõ(å‹ÕaÃG¥O¿?7QV ©"œaµ}&°„^+H²¾ØÉÄ™oOT<á‡o”•$ZE„ *ÑbÄË]°ÍQS>¬ QVÈÜZdMwY¢*²O¥î¢±àÊæ#¸†9rªþ €·î ‚ûÔ¬nXDHß¾•>×4Ƀ¢Î4ˆyÊ­Š\>RVÖÎ<¢Å¡RYb•cíV V´]5¤¥¢×J›)nzÏç­¤šÊ×èÁ8FÃD” œôƒö]Ö™~… +vV ¢ºËSsq¶ª!¢]¼±ÕA›Õµ i0º‹Ápy[PQÌiàá¢åáéÀM®Ñš[8Lg¦V"9ý+Z<³% ñÚÅR¬Súñš]/V5÷4¥dŒŒ„¶Gç.µ»!ÚªðA/êsŒA-Ôˆ:~˜ø‹mÁŒ÷-vÍR‘©WdØæžŠ>i•·4ÌGÂiž[€,eeè2îÏØŽ[}Š—dëf"SÁŽUÉdU~\hy‹p­±t^Zj1ôîeÑØé©;ü)`·K˜H‰ÿ Ἢ¶¸¸Â¡â"å”á`:åË>6Ô{/^¾‘|ß%1ØTÿIô&Òí+tŽ1žTޝÿQÓ9öoÚnXŸÿ]Ûz®ùñnðŠûä³8}Ûû¤¶Ó¤¸ÊÿÌ@ïÐÝ +ÁH¾Â¹¾dóèŠÜ+͇oP ´`r83 O`Jª­tÕD ±o.Õ™ŽÝ£œŠÃ‘ÈɪÂ>Øy뚘FwæØEÌ#èj¢…Ðô¹k)G£ô|³Iè)rW8ç„ÆG:ÝR&ÙJTºÅH€P {ª^Â3ÿ’$‚5 ”Õ™€œ;ç¾N(/ +PÔ šZ/ÈÞzØúHò¨q,NÝÂ\¤ÖÕeæ7ëJ=dÉûu=¥*‘A:c¬n~¼¦BlÆÑÔBÜgD íI¶•]%CUº¢X‡uðh0€Þާ܅å[$[ƒ¦¾KvJzQªø Dn×F¦IÍk@vvÕŠú8KðÍ¢Jˆ‘µeX<_”9oÊvTKGMÝE+vZÀùeJƒp”f€±/†&ÎÔeRH–àY’¬Êm³_d|FÉë¥YIF©ä k‚sh}XÏ9îdÀb®™W†\š1GIJOÐ5ÆÈÌb¥Ì÷½ëXF,”ˆbIÊRu/›{âÃŽ ÞÝŒ°£Þ"Ñâoä×´+’b‘ÐJh\ ¦\ò8ðN`Ý‚…˜»Þ¢J& è3Mµ "ņñ‚Sˆð´EehËú÷ ~Ú—KýÏA8òÏ»×ÿû¶?ø­DÂ9hˆ±ÄF¾}c˜· øÈÊ[-R/òŸœÁgÿ{v¿Ôë}Úý¾dí‘už€LYô´8¹ 8¡‘zž[ânWo‡ô;£Ï‘¢ÔXÀæ¥à/)zDæVTÏ-{ëßFž,6ó >üDWm>«× !‘ ‘«†¤Ž­vÏE¨ð:bœI„Z+ì]+£âb¹!X&ÍîùäzþÔ‘{^@Ä~é©U¶¢Ò‚Axª9æŸEÒŸ ~™ Ø -!q©á :ƒÅ"NŸX‹Ö-„‡ègËžA·ð7 WD9EA´w^1òÙŠü;Êürà^11ì“›_g5æò)·Ñ.j)§–'"mGå'ý5Îñ/)G¸áö™Ìúkgi¡ßÇ”–—…ÌÆNZ>žÆØ”­7áÁ£‡š‰­v¦.Ã8†»âDèÁzÇ/’„×Ãw—X}Y@ÊdOêI¥lST:>-ˆé¼·…ýn¨ð4c›Nb1Õ)ø!÷ÙøîîA4Ü¿çþ{öü}í¿W.P³hl¥ Ó:0Äè‰ÃŠÁ«ì»È;Õ” &7DÕœY“Ä“Á]÷;x?³msÙÚ¡Ó—*‰ïð7êsWË v3U$ ¥¶žš4YEOò’¨ž‚ãÌ-…çËaÅ< )rò7!¥/¦Ä+.‘¹•ÒÆ®`ü!°Õ¬ø êâÌ}$ó›m'ÅØ +¨¿Ä"YÇÖ•è‘üe•m.ƒ‚º„°øûJhHD'W9ÙK|Hïøµí#Eáx" &—âWM(òpS;·B ,þd?Y[*xJƒì¹`Eÿ qb.‚Ã~¼ï³ôÏåj±ë0èÑ@þ½-–Qf[[À“¿!Ky…wPMó]nQ|)Î +Q}â…NµC(jã߃CÁt"ƒP‚ ÷¢ŒXc‡¯@ó ê]sW¥0.œR‡üçddšš'Jï0||‘î^‘ TëŒ?Ÿ¬#ʈÏ8¼ó‚”O[,Z»å+ž1ø¯¸xQ25vµ"fXAůG4Ð!J; ‚N~‰Å`¸–ž„ÿÝ®¸~%òõƒ8Œ’šIØó©j¼+µS‰ +Gj4¯QrÅC çVî¤ô¾ézš°‹I Á÷¶!5˜õÑsÍ¥è t_ãË´dŒ|2ý„:ä9ÒÀ¡ ä"mB²£Yöþ}Z¦w)©3]¶šrÛ¶ô м68³¦>½¯À4þp-/ùHà a–WÙYWrDyÝ¢ÓPSÆoDf͆žŽ:v9›†M÷dO¢Á‘tx™y’õº’§3wPQƒFj +ƒïgd.Yê¡å"iz¹è€›nÓ=åÒ3RU`Faߟ;YòãóšÒ"¾ÌáÔ“tª Á‡¾P»öwEȯÅWÌ`–ÆÁý Iž|J4¨ ðáYð-…lD2¡F ‹ê豩h˜3eDz*¢#/@QLšðfp×(÷ý–¿×•LWê ZPTø‚Ï{¦°(» •…Ê« KCŒ8å^TRêdÈ&zoKé¹JMY°U©=Ãn$¯ýτÏ-$éIö©-BÅ“ŠD9£0€³V^^Q9y#ÝÖò÷®ŽB¸V+&¬M=UÇs§! +áŸuýkº+èß<ë̦~®”#ó«†DCi:Ât¨sweyëW8"±áv%üÁÒŒ É—­ íduõ®hvšò4LBzX +s*C¢ê&iRÈ+có2Ûüè lTsÛ`aþF8kk|úgÍ#DšÙpøûˆƒ©`jÌ”¼0\Á;Fõ40ø.U4Qöî~•A$¤O°“È’ž:…¤0·!(Xc]/ıÚtÙSÓ¿µf,¢¡ Ç3–ö¼Êdmc Æ.ÜÌ$¹ÑêþAyÛÀJÚ÷Wnðã±xÅšGåÝ0|,_Á`À£¸RVÈav™4%ÙÒÉwù%¦'0eùRè.ÓÉA.»q\î}+¡#‚滚³•Õ;Üå·iòü¿Ôº¸BÝ…q™´4,ØÈ`Ff Ö¹$9ÐŒÀ¼ðÑüdóï)ë×Mð¶MŠ+)]mQŸ÷6YœædóÊ]¤úÓ ³O¦îû¡ñ— ή-mÃ9Ò0®ÝÐó÷šûmñz羯šÉ=ñ,Ò|mm̼¯aì«'dÇ´èb_nKÙP2 œçîÌ +£Ý¦+}W-à"ä`ÒPÔÛ¶¸Á»ÏG’8Îç ÇzóÕX¿ª0ÁUõ¼kùH¤š™6†(2ÜÓ‰åöãHú ¤±Æ«Tù‰%P£oÒÞ˜®€x4˜(¹¤ðÔÝ?mÛN=ò3$HEÜ—‚g¤ß"›ëyÝ»¡á Ø´mÄBW6ÀQ£(ZK–<éB‹»TCQAT}Byq4X"ö´F§«B¡dZ4×&Ìrši¾·IoŠ œ@@Ë {¨lŠj8¹¨Gù(MMeCy{i8VênÀþ°akœ±¸N3šé XØÇL_Í%ƒˆíЛ5*© +Úã-Šý¦y쿨…›K«(è! ê[Л¾²Á‹°’ù²éÊX×P$-ûålØ×x†Ú¹—4]›öªÏúrJ½êBã}Ï’¸¼yKÔ—ŸÇî`Œsk¸…ŸFäT¨üûü?×BÚׯqy»Q޼Ak¨]#6‘¿]yDäF`A¤g D5ݤAšWêf^6¨Üµjz!‰Lµi9ÿUul‰Õ}Ÿsžþ¦!«`%4ih†/ÓÔRS† ‰$5l¡XgAÌ)åhÃD¨–88Uíz69ø `Žò²D\õ¨t¥–¼Ë®ØPï©“1£·YŒdÔpAy(-È”¦¬2I ÷8šZt$«ówÙ:GăëÜ•ÀžÿÝ7i*J(jŠÑ@5ü¢ï·!׌0PÔ"AmuxƒõÕ€ßôA»$M©ó<ªé…ˆ©ž›ü4ûhž)¼—‹,eHÑSÄvø‰vCtq•Q‹µé ]¾¯t6(úHƒ_È"è¦Øç»}ÍVÆ’PeCõ‹ôŒwC´ä”Qýjr¼&݈gÍ]U”%]4êzÎL "È ƒã¬~Àž%o(ŽâY3u«Ë“T¶‚ê¹o!X+C€—ÆAN ¤{…ºbð{øWYOmTDoG8Š©­ &ö^Zdm€Úc_ª¶R Ên,§ ðÕ’m¨ÖFM†íW œ|šlêçUÐ?¾P +æ´}Y™¹ll¬â3@}\÷Š-º*/@úG9 -(Ð-õ’¸‰¶Ñî:ש¿9Siö)9i›”}±3wq‘"ÑåØn©Úw[«ãj» Ò¡:÷u–ßùjð^ó$ðµk¯úå‡ÝzIBï´¤ÏÕwþÒW9.H„8¼ãš×ägÖÐÖÛ¡/÷TomI°yÌ1OfŽõ+pƒÙGÎõYòB!]”!ETà +“[›UPÙ‹Áç m¨äø%Ös̓TSà¢j²È&Є4±S†n.AŠgá²Hö&ª o½ü¸3  ïòÙ=ÞŒ0<ƒ #Ÿtm ݦÙsñ44'µóä¹Ñ›¯Ê2oA8ø6Ï4cìögôʼ6œ˜O'8o'·trûÅâ4–Þˆ66ùÃn;C J‹.9@dÉå.[ Ö=™¶E‡?‚PiÙµŠKPã´\á ‘Ø‰Pã ì]ò2ÁoZ>œcEáÔüª¿éaŠ_ÁßÙdµ +^¶™wÒ6¢Á’ìÅM°Å¶ò´Q=Š–c¿’¼Ç´\OK;„(CͶh£/- HiwÑrZœbÉ~›Dp4î¹’+Fî’å…‹·g5°ãV~d…¾pέT}(’˜!™s7¤®måx~tåÞ÷ðn~2¿ð×\ ¾&ÃãÇs6I{Ÿ»Þ.çÚ#pŒ+-Úå¿H¶ôRugX¿\Vô‡´¡òG›~-¾’UÎ%¯2€LNŠoÎúçL²ú§9qZ‚¥²œ:¯×.¸E‹Œ\—Šö´Q€œ–hÓ¬<¢EÃÖó¸öubÑÒVÌ‚Ò&›Æ6)¯Ó\Š7'Šfìy)µ´aÏÒ÷çþ¼¹Ÿ×Ó+V¼§a°ÁÑ",%/`µIûN+‘vÊÂl®òd‡ `¦<›¤=ÙÖCñµä£7äóõ°¼fN’’öñ<þ£8Ÿü© æÌ¨püã^R‹®¼~!ë·[ ÈZËêu~°QcÑFIÚþN)‹8“$´iÛu(²š7´Ñ͈FËÛÄ–cSÄ+i";ócËB’¹”H‚mçŸÜS^Ð&¹[4]G8âým8϶•ÀB{Vß§±E’Îr¹ÝeM<›Æ~3Çi:eŠÚ8cžîÀ„MWÌšjî&Þ%M[éI©Úh±Ašš%®iò[iÒñ»fKm-%ï÷S1x7Õ4I‹±i\»­Ó¼/U׉ÆÑ„ƒƒ z:]+ôÿcï]’&·±tÛhš€Ë€ Àvt5‹èJÍ;ÿëk}›RDeÝ4;²ªºÇ²‘ñCNw>@`?¾‡çFМGxm9ßÀýXVþðÈ©w´#«íÏM_¹é=]Œ<›«ŽDá„£ò8y_±Ý±:kì|žr}½7Ò©0w{†ž‰¥Ÿ¼Cízæä·oê5×Úõüž5fÆÆý|ý-~ÊÈy~ëÏÅÀÜÿú½?1cÎ)ÜwýZ+…oýÑžË>ôÜV“‡2u&ö®)öάþ¡íÎŽ)7²Ó°EáØq„ŸdØ »Ž#Ÿ²¥ÅˆÏBÔY‰QngŒÅrç×ÃîÕ‘ %GÞ[[Ïqû(ú€ñv–»¶ªMŸô±z¾Z6ßû°óˆ ­ê}w©@´ˆJû©f•§iåì%Îq¨÷]g9Ý$“eÄR8ŒÄ@ýºƒîñ3kç¦L3”o©êÀUwEçGfÎûL;œ¡lœåˆçí•ð Haš¢bì¿0ÂníSr½ùµ£ƒÑ:¢à1BßÂÛ¤vó;H#øÆý®¯‚œÚd)×ãÍq£<É9µa,‹~,¼7>àsäòšòØÌœƒáWÐO­ Yb*â; CË¿š$úPgÞÏ·ª®”±ýŒù½;Ô¾W|Sv¬úLs¯ýq6ÿÄ·ÝC|2'™ÏÛ6êéj2̳´áøL“‘;;÷?Óˆd»Ø2hZ̻߯¹²Œløý’—¸iQ eÄ +¸Ë˜$xÖÒ cÏšœ£'=oŸ(B~ÿÈȆ¢ìqA\ˆ‚-žo?ºÝ”9 +à£Î:#²Ò¾þý¸=ê0èsüÝW«UöSžxsŸüèÊ·”±2[ùŠLÁ{ƒs¾gpyJ  ÀÈG¾Å_Æ[yí^ß4BPA;vzNU3w„Ð9Ÿ9Z=é5vÕXõ©^'.oéûG÷3#_~kÖùXtRgÅ’˜tÍpT—§‹µ3B>ƒ®¡n‹Ì?†èUr\…ýS:öY$i+/ò³[¸ÌrJ–ôyÿçÀ¥ØSÄä*ÙD¾ VÙ =Là_ò£\ï*.šnUÅ'–BÇgCò´A‚î(Þ§Ž¾z¥Ž3 ZÇTÍ?Oo¡Ì}‡®+Ÿ¸žÏô|Ç&—wÀ°a=zÙùÙ¸R¾§&lc¡1›z}ñ 0¯{ùÑÓ+ª³™éRŽ57² ï|_SïªE:nõ¤‰2écÉW"Z׸b³ÔÃUxz©!2Ü’"Ñ­¼g€‚ÅùgrÕ`©ÂÉŒ/&¸ð82ËPLéZ^%ó{oþ‘é?ªjv¡!ÿù‹úN„Ës†€I’fLï?ÛÑÖÏv}ñàêW”¿Z¤ A²/µ®Z´O´;ñÏuˆ²”…Q¿³ÃHÇã@“ꥣãN9Dú¸ß²Jäl™Ñ3¸Ëèâ+§w+û$/`uýÎx‡È°‘¶ÂVι}—Ï—†i"º¬»8‘Å»Xš‘sƒŠþ¬šËÎ]ЕpVU„9lœï.r×_?1¦;S„2¢?PžkÔã8ýáñüÌi;4a÷§Ødœµ§”ÒÜ;©ü³ÒX¦®4Üp3ÉõÅ"Èð& <“þÿ™¡›zZX“[­^I.ˆM+]«hóR 9xæh6‡w­¤úÁõ˜™uŽ’Þç½×žµ{hûãWQ›í~HϨȢ'|½¾è ƒÛ±³ÆhÂd䊘®æ¢5²kÄ9ìØµr-—âÏgT(è˜1¢D†7`®G‘:âöNÖòSÑ?Rü@k¯î½’Ñ‘ÎtL‘ +Æv ŸûÎí=10©gP2Òóˆ²ó߯¨üýI]?ŒüTvÍß®ÃuÁlš{ ü‘MRgÔ#:¯l>NóÆ8ÿ Ñf܃’B7!PöÉÐzD.ÄϨç A“Ψ}ÖÑ"'5ã]&%dø ¹8:Ø€¾ÇÍŠ:ž|žc®Ð±Zõ޲ÜÛc|Æ€È ?5-eùs.vö¸ó%йß×;$[åÌ­€B†QràÌÉ,Ô‘ˆ>_fþë/òwÈ`ÿð—%3£¦…Q{PýM“­Ë=!rºF¡šõ|ÆûÌÀæ¾sÑÍû/V;Çôò³s‰V<‹ùË3Ò[ΛÓ3çX@üßFjSM·«Þ"‘AêÀSdKt`?OÍ:|«W-³áÎA³¾åoÓã§²jf:þøy«fFrœ¹w¨ÿG-S|²ÊÛßcêö5•–Õ¹£Lù'#:ºñh€îÝg8‡<†÷Û­ßÖ’)lõ~^ªÁ%´N‘öAªñ]’ÌÚ+ª`sÃû „'¢ WiÈža>Šuô׸…ÚŠkѨàF¿â<ƒÉzÁJ…LH–tFvÌóÍB|¡€’?^nü½sÀmÕCã¨3Æ÷Æw¯Ð{[yäoñZr< “û œ…/8‚¢Ò™ñFoFºm_ý†’¹õj³>ƒfL¤î+ëI³Œ~ç‘H,Aø¶Ý%ðÉ“! àÆ;²Î£pºbx~Ïr‚¼b|®‚êqçokDŒÐk¥ç|PÍ|׿s„8Á¯™1ùU5uý{äˆ~^¿~?£~&Ùˆ3— þK(³­{ÓD<0szp^êŒñ˜ÚÊs>æO±­þqoš;Ú½Ò"¶^wéè[ü¯¤VòԔﲋ²i/uªvD+¶¸7Ôq]`vKÀtóLFt;Ur¸Ñš·Aaç  hm mõîS’>’][=U­zÝè·ìzª-ÁH§:…^T«9Ä͘/솙²]‘—ì +ÅT›dÝ|Pè-@Zæ-V +Ä`"ƒGù.á~¡fɨ~¦ªëæ±/ª0l°‘ë +„àv§³ÚåÀ^ñÒ-gæ~–À®Ö¸ ˆO7i1]hŽaÌ}K½dÈ”kv™ºÛA¼]ÔSLÀ•* U–w6— ÝN¬€|3P\Ç^šg +-ð¥½( rý*ÉîÌì‰(ÏÀAÅcÎŒq~3`L}±0V­¡ix$³ä /&Q»˜ij‡†ô ý¿n±i+…he yr]jQ`$¡ûªU€i­ñØö^ETô[€åQÇÖFµ¯Ï8.-v¹í6þVƒ¡[êËë7óÕ³³U¶î‚åÂÚ®UÖ¹z „Y÷Ç£×ÞÐ[Øõ8®Ã¬\A.ø¾"¢1Õ>Êkdµï÷#ŠuõtzÜcQ\˜5¯ÎÈè2Ehnd½§ôœÅvRߦ\ï–)2b¦ûªÖG´p†mˆÀ[HŽ(I¦‘àRºª·P^òj+Ñjlš6°˜I*Æ«x†ËлŽ»vÊÉÇSŸÙYëÄQŒ½’R(…” NGÐ¥ø$æ &Ã,× ¨¬ W`Ó"Ð9µ”fÀ“‚÷¤¤bU÷J±ÕÇøõ!qGS^Ýâæ‹š-Loú.¡)³î !_—v*“F=~Y L¯Z0^ ÜÝo"æøõ— q¹¥ÈÀZ +¼†³þúþŸìuÿŽôñŸò‹ôñañ,‰¸ÖB“ +®£*vôU1Ã-á¾|z¦Ô"¸´jDÚ}¤3Û±[ ;?ËÅS3mÐÁ®Âº´`{w91þ'ýÿïžÍ·På{[Þ]*§Üôøójs÷gD+lRà.2Ê05ËÚ*E_Q«ØiŽYëDŸ¡ AWRüþ‰Q~ô6 õQ'êfg>b¶¹„IM%ûûÃÆw\•åsÅ-aH³ÐˆMÑ÷–×J‰û/Õ¼Ðijér~»S[ðb 2ZB$dëmÃÆ»œq ¦Ä›•m¾-ÍÀÝr®â€DÑŒÔÍÕ²’q–7p~0zJNwÙ¬sÌ Û¶ôŠ.êÜë6°:ã‚$6(æT ßÓrÕ^©\(ažèh•\A*Hˆõnc‡ýJ‰Úvâa2Æ.pG ¼EÊÎμšÓÇ(mÁª0ô4æèÌJVg¤Ô\Ê´Ä´mT¤…±Äh–¿žqbq¾¤}Œiàa$ð^¯o¶5~–üÌФŽÒöèÑ¿÷S[@?„?Eµñçâ’¸%ƒ4vVÜ@ˆzK ’K<Èt‘e!øÒ³Ç„%ÓÙú–Â\¥&'MŸjû˜ +Q>H9ì¢T¦\¼P€FR]F0üB”Î蜇ZÒ4LyˆÙÊ ]äƒ>ïBØÉÎ'ŒÇ1Ì—c¶QZßWf•Y5?×@ÿÄ^¢$-˜bý¡úw'ÈйBío»tLà3wío LD®ìçw± |ݨô‚Ul63xw6¹(/“Ú+ ððºÑ¹%Úí¥r”Òä)L‰ÀžØÓçe:£.“ç‡H|Þ¹?,Ä÷>{%^ßÒ®¿´–lMÛ.XÞÀ“›>I9{ús>QpùÌZƒt A¤NÙÊkÖ›£È°¬®rz¬záËßõ·‰¾™×Ê Ä ¼òØùîÛ&nT_@ÌÊ–éî ŠŒW1@Mþž9‰}wï²k!ªÄD‘xm‘U˜Eáû…ÿÿ0”±@÷ßa»<F(ÃóÀó¹v)o¢ÍqœÒYXgè葇ï2½„Õ nIH/Z„D³¹ý+öäÑhØÙœ·g!,È{ÜÁ®kÊúƨ¢#xÃVsÃ˵Òxëÿ0ú_õH¿E@ß;J¿Êl ‚É{ûç/ˆ§ŽKÅÛ+˜—xqD|^z2¿—A§ög èlhºFJ3{¿Îšd^…ïÞy‰|;uVP¹M[j¿-&ÆÊIºXZ6îÕæ<¯ØN(œ+ýA2Šx­t™7A´æ|‡ªP^˜­0’Úu©ÿ£Üb ¢ïV" á+zñwpi-ò ) +óû/:XÏØ=…5ãvW +ZÍrw»¬ÉºÊc3£dxõ@k€5pèüœ°>*YŸ§Nk,¬ÐI«ËUZ‡‰ä¡bàÑq§5Eü¤S#oY?“Î;´ôŠlèTD‚—ÖXJ¾6sé{öô·¬vô§£}ÎÇ…€v©ÞÖ*ë{jª Æð•zÞH3V«R<¦¾F`Š›qötcûȃÑ/u+òßÛ}“FÆü6n»Îp‚ì=ÞºV𾿯Ù]-»e]§g}œ-1¯³…£Sþ„Û÷“{Œ…åyöçúÖÁô‚îóÜu£r®zÅëQÏíŵÍíÞ%.­hžÈºóˆöù·gÖÖ,ú«h6%lý^½z•:t½# _¹þLøi廿O²³~¼·Èc`‡©6Ò{ÒÍs…=dQÅûÎÈzêQú¦3o.äxgKM¤,îv`¾gaL‘f%á…j1XýîÕú—o–…t^È.zÄ0ŽWV¢ÝãÈ[2g’e}ûáƒ!Fd?ÿ¶“}ï^‹†»$£ëLÊáN65|"‘„szØ=Õé5EëzÆ©2[ug¨/Õ«Þ©2mXŒï¼G$Ì=/v^ØÃ®#uÕ•ìÍI®èèåÕk_û“þ÷”è÷£B*†àCöÈÆA“ïŠJÕ JÍR„³U_u{ÐZ©È®ÆÑÖÚwµ] ± å;[ƒS¨¶yÉó„L°¢Úïn>„ú­;ø¾¦8uv׋2“¤0Ëžæ‹"U4›wºÎ/Ì6(P¯JP^ò<¦ßc[á}ÙS©¾Þ,­¿3Ù‡¢#äF[Gr ýËψ"xé©{x[s…/ÄH•=AÑö^F èïpjÄë1–ô_*+OÈ $‡’³gß /{4h‘X~ñ­\›þô,%Ü '`–ÝõË+*Šù!E9_±êL·céÄËU[Üã6Ä ‰Z$«‚î˜Ïß}Ö; sŒ;Lwê};µ<Ù¹pJ>¨HóîB¯‘ZŠpçnE +­iŸåôf ’€:ùнï´ì‘\ɹ¼j2YD[7Š2;~S• J°ÔíÛ€¼¦Ý³«ˆëèj•¥R>ÕÆ¤rÜ©‘D 'Kpø¯™'ï,2· ’°rñÈJˆ˜T—êË/tí§:Ö®Òy÷†R„¢(IjÚ‰;wV¿D „èßL}´P*At•[Ïl´?…M”Ùø‘ ×;ŒýiFƒ<Äb&Í­Ôu™³2tP”g³•J[s ¶¹²©dågŽóê÷û¿X¼ñ„#Gûô!‰Ï¸' V´wV,]Wp+ŠVioj†>ñ +[ä„ZEèZ±¶p‰Ï¯ð¢®qþ;‹6/`×çë½z°§°ÏÄÉS<½u,™¢î{)<ƒxÍR³·ÿ²Ð + Ω}&¾¦lì\úÄŒ€âgÌ|º·°P¸ä-ö2hQöXÐáúŠ%ű‘ +ß±‚V€w Ë/ŠŒ¾ ù°º +v“à¡ç[h6rª•ü×åˆÄÇ žfå©u|‹ÖtN¶«MK¼{hÉ$¼Uó¶§hóÒþ'Ãä¥À=ç» `ðjyçßY Ro Í›PëP¿¿çJ¹Žµa‰Q2×[1ã‘h=–uZ|gË%(0à¡à“ ­[€ð@^ã›ÊÅ_ùÑwØàmð&™—Äåg—Ñ¡(>w¸5{˵c–L¯ÎG3z4çaíË'hˆ{D—‘§Iº.Só Šöqd|µ²Sa¾€ébþô)ûc¤F†¯PÓñ{¨“uf;‚ÏÆ°Ç:授Ë8Êý|¦Ù‘ É(ï–áè9å¾ÌÖû_v’Ö‘ =^…Ì4Ãi¶=F–€å aLysFêJŽ²à… ¹|•ŽxëxÜ©´eÆrÜ©”gÞFsÉïßÏ>o¾'A^ÅÃô|o¬=vœ·0nMr¼ÔûW^ƒwgÎ<~ õæ÷m›M,:†ljî­å ì„+ü!âjÇScï(îï˜îQViE©>Æ)Õ]éÛóœ2J©=iülKL.O\W°H¿M|îˆ7M·7Ñ@‹ï_u8ƒ¿ÔW’ÐÁ;ÊvÚq%I®œz<’é- ï;2HÝ b[Ó#¾SƱI-åÇÝqÆx˜*µ‡ Íai!ñ¹ªìJúÔm“#!×U”ê%¡¾ÜË-³‰dxøô8f¤…síÆÆvuÅOGR*ÊgªÐ iY3ýíȪ¨½ >áXÚSe_ýî›·þR’W—" Þw¸ð4µà+¡Y¿uñ»DËô™™Fïn­ž›–ï p+‹”¡ Œ|àˆðÚÄï@bð¯c€O䈘îìÝÐ:Éοµá»[Œþ\0r'›·ÖÕ"w_îðUzÈ>¢ãK²ÿ™Gvvo“Â8i¨ô™ŒeñøÜ)úóÜ— "?çAË!äµg«vÁ´RÚ@;éœm¦ÝÙh=ò¾Ò•£›È+ÉDîªOŸQ´¤Ð¯‹ùDw04Yâ5Z3´©á¦¾S¬Ï]¦Í2ú;{¼U-¾òçŠPr®Ÿ¿&Rl‰¦‚½{gæ|Â5ÀÒ:æÌüc@¶°¬ÀAÞ‘Lf~è !!»}ûLÉq^qn*QçìÊþÝaÖ FÕî™ÅF¨Ï€*ùž­D†»ÿŒ¶«_Bßd^‘"Ô8š(›z*\›÷…µ‡“ݹžíß¹žÙ¯º Ÿkžuô™%¥‘;7®Ü„{Õݾ‰>½sT¹ý‡wÒ œ'¤˜Þã$Êc½ãGÃ5cësfoä®`õè\ @9ç§M*k‘íù„ß®©|Ž¡áIJíÆw ¢L‰ô*Ûl”¸GRÁƒdZƒw¸`Ò:KÝÚ%éÔzžؼ +ý0G8Šç¬ÆðìQ;ð=JçcöàõØËÀ;ýVgç݃ÎX›µêÔ­mµÌúÔ ~x©ÿÅjÆšubg×÷ *Å=v ŠàvB.™×#_tæ0že»æÿç"üQ Ap~´¹ÿ /ë¥R ÜÑòI·Èá©¡¶â„©ì\¤¯õšº¨™~‰Suª˜CêŸ .Q»QÚ™”7úY¶³#š`)®ñ‹}=èFò Åyft…‡ô@mã{GüïÆ×Ù¯ ›¡ v"(†ü;å[îRÞ.hðû ¼ÆvQôv3`mPwÜ#.bsˆ'È1¾h³@(¿¿‡Z"€y9™¡Žêð\¥ÕÎgxVœÎø[Øå\ID¾xŒÛ;¸èg ˜ú‰(>3`ˆ1wºX_=è=o8îÃÏô3öˆf¨ž0ø€¹*}õò=¦ H¬ +#‹çÂ2™ƒØæN÷Ë?ßû<À<ñÚ6ßÿAim/Ý®\5õaÎ…h–8s x\ó3àÜ`UòYGóf‘Ìœ#*Hï!m=pSi`uÍJXHŽº‚àw78ºèªÖ@¼M3À ·ù 5Õ™iPF…Ž8{Ú)£q¥ÅÔcáƒ_¯SZipÀIé˜öXUkã3ˆ§ ¦É´÷[t§€tXmö¼`êI“Ú]%Û×С žÁ¶[få¾Ì1#Ê¥$¸àêxÛµsÿû$£ÔKÌ[W5ÀZÀV½4¿þphõ®7¯\H?Aé÷ÿïæ‹c¹úUÀjùýŠ<‘ݽb%ýÃÚó/ÖÂ|Âq+pËèÌcÓ Áe¤kSryƒärr›‰E9¯„vB¬A‚чÏÎ#U1Í&*›•ð´jâ½+ìöYÒã™sK¼?J”y¡»¯Þ_¡ßÖÝåµü×pUîº3ÿÿû÷4–§¦Û £I2¯#U+ì ˆô–e™·úL£?âF¿¥4@uf”ö +啘À¾^:é¤ñŠk±KeÖ\¬W£Â©¡°GÚLDi’´pn¯Zqùñ§¦Hz3è%eý~ì’þ¡±¯.^—§þÔ‘ÿD%×ï»ÌÈ_½ÓÚ®‘÷Ç(%wõšòýezóR*ø¨Ï¹˜¿˜pQ©iо¾IbB@F‚/—%µÒ ¤y–0åÜm)‰v:awñ9?“®âc‘W—¯°Š¾‘œ.4.l0>µCê_G„û)"ÝZ@ïÜ ûZ¬Æ3¡‘q§¡³OÅr¨§de9bEâïÌdéÀîóÙëTfM?“#„¡ï -ïgÀ‘Þóöd,{×ÚÇ®/'©¦#@Ý—>óþœ%¼”3í˜\Š•N4ÆÖ9ÌÂ(—ì¯ù¹ù¾çŒõc*ñ·ïj‡ðë1ö³=æ àhÜ·f¤÷ªC—#‚âiZ˜>;dߟ!ºdL¨Ž×vÔÍpÆ!2Øèœ=ߺI¦ÄgÙºd0…q^%¾•g2M;r+'™Û]õLÙ GS1È÷‚žÕyÆô©Ž³}Ö3õî* +ÈëѤœ +úñ¼…;†K¼ôÓÆ“ȳ¶>5SÍ((¤ïõYÒ`Lœ³$˜òQÀ‹©UµÆ¼cÕaÔ%j¨¦6×¥UÛµëm:ÀA·ïÇõ¼MηQ²›¾Ìªuœ‘µôœ7™^™z‚ ™ž¹tx#M Ïz$Ö;Lj“‘‡˜ƒ±œØùé³t—“Ú¦AƒSu’ksE@-|‹™Ü% +­ì³ Aã´¨ïG ó=6«mÑ#õÇ€F£æ¾•ˆü¢òA88ÒãËSñq¥v %f‘Dó^ÍÝi]üÜWÔ¥& £– †IÍfŽ»ãÆ8ï\€AÏT†úÎ6TŸcàÌ4AÔ _Óž'·# ì®6îÄ O…êÙŸÃ{WëÛ³ƒ¼¬ìáö°°ŠP4M*”#QGõ#éyO‹š)1òJêîÈA0Àî^¾Çݧ²ö¿U[ì=b/²_É‹<³Њ×ñ9ËKÞóüÂ{dÆ–—`âˆæmëKÔƒoµ7åQÕ*üU[‘ ¥®]GMÕMUšþvùù8Ó]e/±ÝæV Ax +¬÷LŒ®€7ÝŠ:T´EÓ}`Z¹y!gÁPMžß!äŽÇˆõXÙÜ‘AÓâ‚Póv¹nà*,Ì! +Ot;jEâé)#F¼'ÔAʨhËÈN+±C×s¥¼Äz ê cü¬û‹Û=E¿ñx¼]}£å­+ªÚñb3æ›Ëãgh?±=~«Ž\Á,‘4šj9Ò{y´ˆE3$ùý`â9³çÕ§~h=…âÓzA¤Ù‘€£ÃÍ:s`6D‰óòëCË’rÊSåÀ–A"¦ßFŸ"ŸÁ‹‰¶×¥™ß¥Œ0»¬"Íê´P öž·_ÌÓ~¢Š?&ºK$öLñÞ‘¶{‘¶û·³„Z^d°Ëë“<¯ ™_=˜ht™Ì]©²ø„J÷ÖuóE…@b+sl¥ùžxbëZ%QNuê¤F +E»ÂUþ +øÝ/ª%ûk1µ‰YÞÅÿ¿JyÝÅA"` žã:µlk¶‰¸Y +hûÓ»±Þ—0•œƒ…÷¾ë*°« ‘ÊœÍòÍnÙ.t9Æ®I¨DÔcv꜈Q“VF}Mæ£ýœþ\cÙ6ò^t[öfQýÔ®èe´çH²ÍøZó7Ê·gõ’ÓñÇÔÉžÑ:¥†}?1›"ž€RöY!#M b¯û§èß­öRéŒ00ÂþRÈi£´Á€yêðö˜4¨ZUÉIŽØ ñš• úžÄ9¢pa’bߟA6‘Ç€‰åø^†Wxò<­+ËpÕ=™jU  g+Gˆ M«oór$º¬£_x)ÀLÁˆ°ú¤4·…dSl‹¾:fÍ—5ºÂ,¼šÜ +y¢‘fŠ\ ~—uE~…­•_yލ.J¯ðïƒÕ8tóA‹Cc; p¿ÇH¶ÞçFü’|ð¸RÃç•YFÌï{šR‚°Ô@—æÞ wr•é«?8µ?½ÇdÀ­#òפv‚1#˜– ÅyÍö9Ž+•øtÖ§Týœ:W‘¸<¦c«NÓ6&XK‹Y\Œå¾]."ÞQd€~×®jÕ©à®\ö…¶>r•uf—Òîš®Ì~j\G¶Uü`k;¼ ÏlÝ´<’ÆÜßVl i¬=ðq0•æ¿gæä®¢‹5#“Ý£Kïa#Ä.DF#Rzz§… +^þÊî‡} +€lTdd2ÓP‹~å›Ý(·±°öÒ ™ºŒE#6M=•/v¯4U ´)Rc¬çs¦QêQÕ×ÿUFv›Ï[ ù´cV¢|½½ßïÞzFô—4/!1ghǨRtN¾Èà Xï]?GXêˆ;º'I¢Š¿=qïæ]$ÚŒ°*ñ{g~ΡåÏ’™èÔ½D¯£ÿIkÍÏqT=#åþ#§°‰F2]9PñÜûHgÑ‘÷mݪÎ?åÀ›ÊëyÖÜûô1%‚.ãè‹É¿5 œÙûª +tÙÌŠ÷)ñ´f3y®* µâcð>Mtóˆ×~*Ç|7¥;:îç·ÉãÞ¨þÌ®•‹³a¡KáþëYèE\+eº¼aQ¤qùñ5”» H*–n¾¬lxWÑ}}MjxW»”"Ûœµãî‘Zñì6›8ïgMÛìë›ÊŠæ¬i&1¶Ì7ò䯳^Çäû ®À{b:ÐãÊR¼²ðî™.«=ˆYánà~Á,¢qg‹î)Ùî2yóÉsÏZÖÙlÍíB4øtm)-±®o©æÕ1pžœæ¬ûÙÚyw(‚­+¯z­2šà^fŒÇV©„eÄ9ØC¿ªãTôdíµýãÈ +I +¨~÷¾\Äc"æ¸5`wû®ˆ ,€!àÉAôÁŠÔå ›T¦WàÂÛ +úèÌ=ãç2G°Šõ%l«ÄW&˜jÈu™mîZé¾LôÁZ–\i»Æ‰rb™‡ŠV%–£fŠPlAÔ›²_ІEVêÕXÓƒ-ÝCCǨ‘]©Èqî2P[Ál¾@®->ß«8ýà¦û低ÃÞoþU·ð~­‘a™–ñœÆDÅÙ1@·ºM¯U£"H¢ï üYXˆ„ì)—²1Š—ëüÙ3HZKò븬bKÈûêAraÏjò[Ä嘲*ñKl×I꘿¥ ?Ñ¢®ÓšòLjòD¹ÌÄC¨ÖKÂþÒê§LI€Þ¨ÈÓ2'>O.Ía’›<¸–¥6‰ÍP®Zƒ-qÍZ¸’¸e¦>WüZ|m ‰ þwe²ßAŸóJ^OFœ|'ŠkJÜÊB²wàEYá@ +ïÒ<ÍÉÞn¬Wd²^¶ôzâµ?˜µ’}ÿg;Gkw¶­4¼(vÝ»Îö¤È E÷ü)žÌë{õD¥–Â<ÈQï<±‹ÃûiAÞÙ×´ãzž%§¶Ó/`¦Ñ¢ºd—\i£X¼XÖî22¼íj= “%·É‚¶tŠ×™ØÝ½úÅntßù™õùž{¸R¤P®×Å©’a”ýœzJõ–ŒÒjÖ,ž'«{_/A¹VÆ0ïEDAqÆEÆìt•`îLºô^«vz ýÉÁ‡h¾þÛ³À¬C(€fM±§ (Y¹U2µ@?ܱ›¶µöP[ÙX©y¥°x©ILݽ^™ ‚鎤˜¹â½!Gߨ`5xÇLÀ{õD0ãQü×&õ-¨¨±‚CÝ3kç[¥I¬h‚×ì²Ûêöíá3Â:çoŸñ{;=)¾Óض C9Éj£Üf3x$ãy?­_Ñ+t¶‹Mà”Âp´Ye¨…}ÏufŠU×I ¯ÆGg®I[ …÷”¨ï@öxGøëðª´Ùc˜ð.X‘#í +Z|Ì1Ÿþ¨lÈIɳI®¨Ðc¯gê1•Óö´[¹µ™5¥7ˆÄ”YfÚä­ÝX$—¸” c¾:D°»6 ”Spˆ?ô‰¡bXé{>ÜI Jvûš)v‚«Oõí½¾ƒÅL©Ü¯d@ŽÒB©@ç,h×]DG¬ôéT[}!ˆ+‡B^:]¼À ¨Fñv6PÁM½Aé^â{l'zZE°Ê/ž€¥éÎC¦œÂH@½Þ÷ÚÔki_ÑKqÄ&¯±ész*¤"Ì ¡-â +{èê¶1Nh œÛùH>ÁÌñ Ò`Ç +BSJ³«½£ph;îþ¥²#àˆ KÒ¹cWknÑ¢"Z AÞ +Þ5J­‡A*å2õ;| »(:öQ]©ºÒ.x]´Ë››£Øò=“;Ò +;+ ƒHL˜ì`w)böF-,µ%žÒÖsÂVsµ`_¸)XmÆc…húÞµ»ÈÐlWGz%NF—…KжZ³t%OF4³k¹ºÓª±b~Õ¢Vì(í†Íâ¯âêñþJŽd„tvßQ£ô°[ú +7_0B¾ž_[GT96½þt  ÀìZX#u=J`·Áik"ëæyoA#’™°~x{{¯ì¥gM6 +©^©€ö,¡ïDar–SíF¤þ1Y‚žQ´õ½„V©G >æ×™º£?.æP%ñQˆF‘OÉëZ”tø¹”Å^v¹V£|*µÅu”öñ kë¹ý~×#ê÷Èrë¢0æÛs¡G~—ŽY½t²ésò Óc%ÇZ—ô7‰øu†…§öU 28F4$¨Í-_֫ljê.˜#îô wvuÆÜÎh–§dq©]ê$Øô¿‘A»ÒQ ’ôæqÖô1 dre¶Û¿[:uu˜PX’òsÖ—›ç³q¹1À½Û¬ ²ù€0µ¼¡úlæÒ]nGnê»w G×aC…õܨÃOé‚ëßX¨|^ÏýÖõz¶ºKgÒ @iýÉs2»£ãîÀ½£SÞŽ*zË‹¸¹ZJÎ Ùò4žžåúöÒØ£ïO7÷½çeEÓiÐ9HÑø¾Â7q¦¾ÓÕ±öÑž—é"îs%t#[+™Ð«„¼L<Ü3`óz—‘sÁ·”UV7žwÅbF¢jS¦i´{¹Üß*DD;ò|öq•ôÙÒæ¬Hb¬LBC›Š´ƒ´1+Þ˜[ÜGô9!<ñ_®QDZ›ÝÒ ï„å~êšÙBÎ+#n°îW}>¿ç~@,Õê]7†©Î~à¥4ÖåSô9φ + œ}œO`‹êÈVCTV‘u­gÉoï˜Ù=œ+6TRÚ¢AÖáQ[`ľ¾[ “bʯÑg<*GWí}ãƒpU†ž ø£åS;M<Á^‡&;Kß-¿÷¨§¡UFw:ƒ””RzW§ +oÓÊù %x/)E÷zÞtÀtHƒÚqiÖÒ«¡ç-}|5Ô/ öøÁì橎(f£²ÛnV¬¼ë*X« öièÞc>á&5´Ÿ®ƒ”ÂãÈzÎGãDÐ\GM]ª¡ýƒ^³ìм~Ô1(í²&ÙÃ\…`”–•ëb%:v}aEvÀù/É×èÇ Öúûù—nr-w3qt56ŽOË~VK×ô,PS…>6—p¥ÙGâ;DK×—yD˜³¬©² ¶„ï®Ý’¬ñümècÛw¨#Õ®:îÜi¾÷þ 1“ÆRP0Ó{ €‚—¿ª¹ .©ë†ôi1Ìñ(úÞU§L“2MÂ{ÂYÊÊ*4_\¥` Iá—ýÌÚžoDŽÈ©ì°°^3MêãA;íŠÿ(V†š0’8õ³‚ÅME•ë[igÓm}•ë qSu¦‚nCñçÎ>ÝÉ»åÅqÍzŸÐm¶È×v$*ìù+¡§=Âðóª +sæV¯2;§Ì1€kn§)}—3²Gê¥a>.ЀЋ¹÷Lïïs•êurUW²®ïôH)*¦_†-˜›ŸQùþ^Œ_ISÔ,íÌPøf+Vj„»c]t™r*ŽŸ‰”(Æö3"-¡Uƒ¥X¤€CÖ:KL<Ÿ¢=rLK‰u±L±Ÿ¦Äw¥ä¾Y™|ºö .d£¾Hƒ§3€ ç‚«Ñ« ¤Ž!±¡æ$Ù8M¡O÷£°ÛLxâÜÚßž +6` t^쎤ŸþS–ã÷Ùcì¥R'k׋{˜G€Å`Ý̺®Wì’ßA¦Ê;]v:žJ:MÕ+¿,&O{r馻ΠVU阗’ø©ÝQæýè—úˆÙÚo"w$~íÚ¡iÉgÕøxœˆGÉÀ®¸ÉíGŒF Ï[, +wiDQÑJ‘*’ƒSs³WÔ¤—ó*ü¯"$|•Q÷ñ>6ÛtD´6=»èNÊá•Úž‘]=Sxª7i÷ †"ºa°õ–eˆ‡Y„¡âï–Ãx—ñç{Á‡>å˜là^*<êõ,n)ÈÑm… Ähd¤ã‡! fob{P°=ûCð÷1¾¡ ˆ%­N*qÕ5_6'Waªõà9 ä­q]±ÐV°ø/qòZ›ÓÜCÍϹ°ó«WÖfL‰$…ÉE ÐnžCKïéªû od("ø…úSè[Ž=.ö\—––ÈJ•É@ < çaoК¡ÀM±ÃœWv{‚ˆ §‰ÚødhFc§ð –g ¨–ÌnE£yü¥±§$M%ªËà¯|L_iMá-NRýeí:Œlšã/C9ºjo*ò#ž$¡£Ð'W ®Ö¡ö—àlÚYç"[l+3'±Mü`|r0ùÁÁC©LDêÁKjôõ F²Z*QD©ek¾Ì g—*û{hE£œµÁ‡ÊÄìêTñ¾u¿Eè– fG¦ïŒšÃ«•̇ŽòC~}€ñúÁ»: ZtnU-L°C¨~B¥CjeÂ^Í¡`ðN¦`1Šhæܨ?£Ÿ#†î e+«ß&ý{è©»€Â:E,¿Gxû *åãPéUžSÆöÊÙ +f¯C[Ìž––?õäVÐ4Odïtìcûö5ç+–}DþÛë^9ðøŒ4—ûùŒ¯¹v¥`ùI7ÏÜÈK |¯`ž3“Ê!OfùºŸ.¸>ÉZò¡„OZ–=OÚÈùš=‹»$¬^â,L›u=¬œ‘ÂÕ{Åÿbvü&_ +c?lÛc¦P¡ïgÅòeè%3â4ϲƒÐññ,Ët„$UmIƒC~´DÁÜgT:¡›’,ùæ˜ù© É8i«ÖV Îó™Ó«8ZSãzÍ6ÏŽPæà–\@ÉJ8U Uø*axIãI¿¥;ãÁ»ˆØîhšnTâîÆ‰éžú˜ `Éë¨]ÈÒЈògJbÚ÷s™c»(NM‘\8}Ïlü<ä"}>OÓöþì]ª§ŸŸ•þ,øó ì÷“·ÿ¡ÌêÜ*‚ÜÁZÏ£t(´r§L(ÉËzìz6ëÉ(§já&e¹‰8¯Ý£ö-EyakIGþ^­*ê!¤é·ÝŸU…Y¶Î# +Ûb¾„¨x‰Š×Êo֎כ/Ò ÞÀ.6R4.ùlî+:P/ö³¨¤hÄCw{J„f:Úøt)‡Ú4æýž\»~ZÊ3>oåX^¶ [NE‡!–³¶ I&\1ÌÌÄA:pîO·kOƒÆ3ÆU&š%Û=kų¦³Õh¾" _%¸Ìr°AÊ…©½¬ÃÁ6z”x;bÓù)âÈn“ØSĉŠ<˜Ôà(#»"«T1—F4^¦v‡AÞÙµ Vo ®ý1~Ÿ=;-~Wç8"Ϫ§e%yħ‚zâ×öoõÑŠØ +m*ü-Ì%»­å@Ž3ò£+Ò³™ªWæh"3ƒ•æ×¾š ÷Óþt«ékøogýÞU,L04NEK¤ËÈ=ä}ÐYÍ`öú!Ðj$ø„¤$ÒóW ê« ¨Y{MÓPehͺÂzøÚÕ]xEÔ>\ÅQB[!„áqµ;yêÐÉž, +x1ƶJ¥#²Á´²'t/š\iâpÝ“¨fÞ ¿«FˆJz¯@º’l*~OÁ4!âÓãvÈ¡(ö(‹Mœ#ò‡òz³Fr N@'¤>\ýO¹òÀð°©bÉJˆ²ž¢WW艅·Vàšþ´ ÛoÑ£”e_ +àͲLÒø{ÀóÈÿ^Ñê4d"Èâ‰ÖPêHq1ι?xž@VH2Ö.ô”^A<Öv~àb¦²ûWu÷wX9PWÏ$ÿtºTž¢ó_KoòµkÇVÍÓô¥´XÕ&—ˆ%svöªz)9Ó‹¨èVW¸ÔûÄUí'î *˜ Ø)'½èN¥¢[5hÜAB!–•ÀÿMÜdñFóf<À¸ý„è#1S0 +» §ò®¾¥âýý›ÒÉ6Êtê(¨†Šz íÂjè+­~”`×õ±§ZD»·]*D!åvxù³ô«Ÿ{õaçŽ;¯÷Oû(àSG°Glxx ®„p'A/±w XÀx†)O¢ðÓ$mœß@)ø]ñðûý4m¡dX†61Èþ¬)Ê·Ì5êkÄì*¯5òÒ­ì§·3-•þ1Q:‹¨VlÉÓ¸N9˵„!•T²…·ãY†è'þUó&LwÎøçÖgâ©$¼ØÍï'¯“WÚÒµÐ䨂óp²üB¸ºâÞ”7yAÀuvÃ+zýÛ5ìhOÒ£ù0ÆGM-e7-Â]I%^kwhåôh‰…_¸Ú÷nOÕ*®£Ëÿ(A–Û¢ɪÚ~ç™_?ƒR$ƒwxe¨ëÀаËØñUPÌpRéTT (}~BÂ×× – ¹'éŨÓò+³Xz%{iÈËKú5§]à|!«ü!òZúÇÞw$ZŠ|Gÿ¨‘X4°ŸCî=?a“@‡êæñ‰æòñöM„Í"YúžCJàm­½&§ޱ„s£]ŽË¨2µyÜ: ]ï@&*⧆­o ±c s¢õî‡IÁÛ1!{ò"<À9±\ ìúq=z-¶UEN[‚L¼ÕËÞpÌØºl|Þ=¹õ€× ‚*;€Kvºgг Pt0GÐiUì"[¨0t• ñUá¹›Åhçc–£”fXɆQ =DZàÚ¦Ž³$Š(þ±Þx© .Aå®×6wøÕÖF/m”MV‚Ä,«1ªºvmÑçS¡åv¤uú3˜ê#^-×xp‚3‚±>Év²KKciÓ‡}‚Rp5ÅvþéæÔ?p¯3eŠ"ö³œÝíŒÕ¾9åÍ +™Ýq«ðÒ«R‹ UˆDŽëÑÔ°Å/ðŠFÔQå弪v{^gP½çп#'þÝ™E]0ÍK¶möï«ê Mªí삞1±¬Çz?ϬMòÏüö¾«˜f+ýžÝUOC¨c·[Y²3RaI<C)4¨~E*Õi…Ÿðˠȡچ€ Xye08 +¯ò.ï„|¤È¯4lŠÓÊ.ËMåqŸ!$_èÏ*\ÃPÝÈ ×]Ôb\‚_¡…$\Ê̶9]2ptÐçê•\HZ7žv iýâX‚܈)…Ÿ»œRcìÂRê“}dÇÙà)2bw5?¶2ÎÎ6Âb:Ì·’ÜÝ‚â&×^NK=Qá +ÒË–M„F¯@ ñéß^°Tòºâu+½7Ò:#ñn‘$MÎ;MnVýÔ±I9Wx—nqýn»fÑ À-©ž´»0u½R¶a‹¦Ÿgš°ÄHmW‘àJc±¥.䯔¥Úc +³±›hzFQâ3ßOJ}yj]=3E]ŠÎßbóÄÏQ<7qéÜé]f/wØ„ Õ¨E´"ˆn»­kèéÊg4¨¦’÷6²Ô +±›ì:#ÔNlS˾.üÛ„8=4ñ·Fïß•þKêrÿŽwôÁ6ò½wô_>ÝÿöiTPþ“¿}¸³@ü'Ÿ>ÿöéItñßÁÄúþ¾äL“ñ´½eL;ÍS—×ö+9zè·Ü‹óW™(ª<Þ±UŸ,$ÎùàQʘ¼eËÚÚéøK•|™¦×Slf÷òõavhΙ¾1s~N0£þ¯³(åM™•þpG£`«Û.¹“Ô‘%ÆT‚ ´Rìi»nWbKr-=>åÅ„î«ëé–STl»Vwlj?ŸR*x¥U°Í<£Ò§í Ëëµ(˜/U„·ŽúåYд-h@#9€ipDÉ@¬G“üÅBðÿ¾µÿCßÚo%áï]܈×Ç‘•±ðù s¨k&6ß¿ÆõíË[¼˜¾]|uù#à%·MrÓ±íN#ž]ð(UR9äÛcFöJ‹>]¤+p +)©[–”ÃIá÷(%¯yÔöièRèz¨ÑÊKÇ}ÿµ€ú­·± ¿-þ¡^×¹s-¾7Œ%ùöÙÔ `Cƒ‹ŸšKž­p Ï5ŠÐL†¨…Z¶õž£wq¥Ðù™[Òô™[…¿âðB©, ÙHj®Óo’æ³tAÞåºå0G$sÍg_¶åï}3ýÌÛqߥn~>4_vg ªèsްÈ.’GT½xϸÒøK‘bP4J×P,¸à„J¾(÷q”WÁ8/MâXvCí«äz—J Y£¨L /0!lÙþ“Ü”í6ÑÄÔŸö~ÒàYLõÕX ˜$ ™Iĵ¨@NŸ8_ÿÜ™Màjí¯ äV&™Z]Œ§¯£Tÿ(ãÖ +™VRήÃt5˜eÔ™ÆXÛe‰WONÏR•+`øæ’Ôh…޼“‚ZšU9R¦·™Í’ëT88§«E2˜‘Ë6±jÕüÂGq†¹‡zÃeϘ’;÷dø¸´ í|)|Cëù>XÇ­)DGWvà÷ûé?˜îìñß"^¹~F–ŽÐ|d•Ò>£)z çé%Ù eV*(>ädÐv\ïJ¦;o‹¶LDr²o>ØR–‰ Õ´óGLèÉýV›·è9µ_›3È/YÉ +‘Z-DV‡tFTÁ¶YÑ+ÏÁ"„MÒ´ÈL™ÁQu{µ(™˜„À=î½ä`·¦æ¸çµ£ÿ“!úÿ¢™ö-2þÞ-U«Q,þ2,« $æ«i¤é{²Ñq„©8”7"b”`´0ãú]bi-µ|4yÑ–Éò^Óv6°Q¥ú^øJS,ñàºJ j‡«.œ ºßA0U±òšT–ØøŽþ…gY|•ŒA;{y—]®@9… …¡÷t'víQÔ#ì‘§êÈÓÝÀµìQ~,ÁÌñ_[Uð(Í%ߢÔv‡(ØøŽk Ô•t³dØ€›tÇçÑb3¸øxsÐÔ‡n¦É«a¨Vãöœ–ˆÖoO‹”–&À# +<^= ¥#£¨ò~×t:åÛ»É3ѼÖ2)œw˜™°¨c‹ .íC¶t€:&Ï*3 +¤œ¤·$Wïø ¡å+èõ+µ ­Qs÷.ýñÎV[-‚úWÌüjDÏ™½W«Ö« Õ PËýs]Ñüx?šÒD*Èö•Y×n×Ziêûs[7#~D’¤[«âU®­è¯·(#‹ô0vÞ»T_<¬§±¯ò@‘ÈÈ­Â.>2K=ýªÁo[• Q¼•BE5øC߬ÐlZ)L´Rê0`Õ#®å¥ÒɹK)‹_G*öu—Œ4 ^MÝ‘©™GÒäﺚxAr*'°så}ÞE{ße:ïG캼t_[¬°’»[õ{èT"ñ«{†•K»‰þ,$ÎQÍMsè×\wòF~Ä7VZÙûŠpÒº+&·äÅ^r?ýÏ;úÅ$¤ïÄA•¯éG¤© u¡¬<È™›©Ä«åþêß„ ¤¸â½ñÕÖåvÉv[ÓJ4/ ºÌ²ÀI€Ô±Ùê“@x¬E4÷Îb‚¢í;M]=%5¬áãÉÎß…­ã ZÁŽ£=†Ø>¤”/•‘="À(1³2;6äûŒ+´’æWTwvÅ“<ô¸UOkÎB$:Ü&Ç"–ríB-³XÐÉelÌêêK=±ŸhÚwŒ=ÆÕ>ïx.Þ—&–™kÇQÛm(É>øPn\Ûõ÷`„Tæry"‚o[J3^yE¿ÆGÐ7_šºÚÖÏúÝA)ñ/Ê>ÐÆ¤Û=ŸfX‹2!«ëmED¾­ôoå°àöz´[Å Öq<É«%5ÍÂ6Ê—4&¸‘@‚Do½’¿as”Óä +u¸fä\CE›NAÙÕw»ÓïÔ6¸UõDóY2y.õWŽÍÀn°ËáËv(Íõ©!kï*ý¬‡K‰í{îûùAY±h†wè*ÿ®hnÕSÃaBï;u6@»§àó-`Œâ7sºíT¥3Œ‘ÇŠ K¦……ó.9ÙíY %óÿ€èãěƖ©TR KW8ÉÔtj²Xfü›@w +øPëT¿$_ÿ©C z…U$¡œclCO_ÙÕxÓaî‡Õ­Ë©0¡nÄ#ÑŒv½‚^÷÷ëc¿'Ò¾±6LœÍU±¿#Ç:*y¥ÄÈbE¬Gùz‰"¹œˆDMn9Ñ!7mFÃBáGìý¹n>¯=ú¾ðùw\#L H9ÞË­GvíUtÊ‹c^䆚DŠÿ¨¾ _¶(4;_Ýw¾:1›Ïüûàö'¢åqsí±_1Û‰KkV2½dSWXåßM"@¶¡»ðƒ”×7 Y‹«l4mï‚þ0í¥ÌMÉæzD΂óÌ26£°ÒÏ(p*•»çK?Ÿ'[¥ó¼ +èÃ"'aoöÐúÔI5Œäñôf$š(¡0R qqÒ)@DùQ«¸®È%x{$ +P\,Û’`Ã[ÓU—g”[Ø„ËÏ€‹¯ï2 …2‘Â{§@wœå^º6AQ›ØSFͰõ—‘ìO_Á~£PeÊu*¤÷J„àZ˜£Õ~ºnµ«|`/mŒ[)°±2u¥[ÜZ^—_¨wwé÷\R¨‰HRI$:Õ˜Ï](Û›vÖ(×f1t×~ªc¢¤îbm[ë‡cèÐ'—IŒ><üDá€ÊÓ³’ rILÀn`©’ϬÀQ\]»oÁ9Ò¥%Xþ|œ¤RU +•wÒìlÁs¸˜³üÅD¶à[C…¦ ²ï¶³ ÇMœùzð¸i°¿Â š&S·Ò²‹`›ŽzúJS!šÞé¯ÄÆ–*Š F2¶`VŠA&>cýù.|ÿ,m(Aä"ÑHÚ® `#B‚t'Nx:l¢¼Uå:+CðÝ«MÐX""¾ +Ï$Îê#-l1‚t™Qó.Â5xŒÈ]äš0H n—f/@F§Ò‰Vç®,~S¿CèXµÈ_Õå8E­w·A•¾ØöÀó›­RÊêò¥OÝ—Ðl D²"³÷/õߢþ¡üšÞ3îvÞ(¾˜Íûna +ÉD1^?âWÒêÞ¤M ì½³“Ev²a Í"7PVÞÔI£Â4Â…%È8®U"þFÜfèRP? ý^”þAG5Úþ€¯Ç~ÄózDQµ$Þak#x(^ $òË/²([†¬!h4W—Ä”§ªy­úÀò> ÔŽ~K–M ·Å¨(J; GÕ"òöõýâj’ν¢=#º?Ú áÝ{Ç -µÆ¯¿ØÞŽ¿S*'§»«ÚšaÌÀÑk!_ ô~¢ [Eë:¼ØøtNG*Z˜Dà¸:¯¼ámGúA f`9‡+S2šÖ3,7q3Ø”¤«G™86ö#§ +E8¯Õ! ïÝEX6%%ÓÃŠÚ +>•Â&ââtî¨ ¾$CöYúzÒ²X^u œ~ºîóa~`=˜m¡ÅMËMç:³ƒ*eð3Büýƒ¡âSÇ!E‚úç/Êȹƒ¯ð´$§š¬9­‚‰´Ò¯çÑX‘2w­4 òüâHÊ”8¸ª÷bQ$5lµîÝÇåòÖFñÊIÊ] îËNBJÑø±©~?¬T¹®÷Ãв¨\×·F­g…Û£ Ð( úôÕOµ£…›Ä¬+#šÖ‹˜·DZ!vŸ22Û¾¹7MVÈhDúÌ +Ù ¢ôÄIÉAÑ—Èp˜Y!}¸PÚõ¹‘«4²/%¥ +ê”[ß(âš:žg­¬÷·Z©{ý8C½õ}Wšò‡°txZËHq¬ªÄ…ûšTà¨+¯{DŒ÷£Ç¹)6ñpK-\=µ¢º›ÍÌB/ß– +ë†WF²§ú¯ñAKû•Ö q33pÕê~凣õ»Á5{x\YóX;ØÀÊ£ú.ý<ØY}ÖPθîYö(ÂÐKÏ ózæ½@V^ògy=,GCþ:ÏÏVž%6×1Š¥¬‚‹p×ã®kˆj AÓ¾ž5üqØ…ãY; gEV5ýÛe±_à w¿‹¸÷ž û©äçSÓhFJäuUPÌ$3ù(Ñ„ —é"·Lž‚S.öÎÞ¦#ßioóÚ‘—ç;ƒ°¼®Èå¯Ú¶4Ûj[^×#p¯5Ó&U3Ž× +†ˆâÈ£l¹ŽGx$ô€‚úF¬öq³!®-å>`Õ®h¿b[VA€,ñLUe•1•&ÅiƵµ|J%Yž5‘jô»Fìã9ŽhÉEõ®=€I¬@—ºüö[žlª^ ˆ‚ËH`XÆ(™ù€±8ô¥¤ð‹A;}Ÿ»FçQ4f®qó1Q¤fs…ϸŸNSãÙjÅœQ¦a ”g Úý¢`:ŸH’øc^ôS­#[c¹H¥ÉI*nw‰2ÞpfDLþ»³®E•ZyÙóÑL"1Òìb¶rºãŠu¿Ô+ Ã=$o=ñ"èÆ¶¹M,Ã>˜þµŠ¦®€·M·5£ä_]³Â« së¡•Žn(sÔÎyÉc¥s¶Æ§åö\ˆ–¬;áh¥R«^Ș4§H­ANNùRÝýÕDI‚߇gÕžÕ'`ýCž3nx»\Oµ'ÜV{ H½Tƒš¢¸½ +z7ÿŒÜüv:ü×̱»&×_àŒå$Ê+©¡%=dj~PrÑ¡ŒäeË?ß7 övÔoí=Oÿ»ÍœQ»üÕ"oOkzÞmWb”C-Œ•ö´o>j+úHð³W¹6½?®bÈfÄè@!Ÿ HŠefÏשä}ÄŠÁt Àc‰£ë€¨‘˜F`âî íù›˜ÞA²Ñòbñ}O96pïdZï£î‚áã +²ÄK5ïPÉ гÏóQm€ª”å‚iÍQ“†@y|ŽÐÿÅoXì©d¡¹·ßO ’’$Ü+ÄŠñ§[Ÿõ–÷šŒ”Q #R‘Yò`žéòÏ72Q zoÞ€p®Ç£×d¨ëÞ­rá—_J:õ×ë.%DcQ¦øã—–Î +EßI̺cTô»½HZPî•ÊDà]Ÿ¦ó—zîbIÅçkîÇÍðŸÝ™î˜ÎY[-èTÉ 8𪳀©õ4´j»cJ¿üÛN:kg)l—Wnn­ ¦VŠñ)¿¯ô/¢="á5Ÿ“Ó4Dª• ;.é(ã£ÒŸW V¨ô±gʺŒ]»*›¼abGG?_ã:xD‡‚˜2MñÖU pêótš š–m v…ÈÌ¢+®R+Mu¹î™’J®ß%þ–CZ9­©áÝåiu<¦§G¼”¹ég‰µ˜mYa8,A ÕÚ…Ãnñ`‘ÆcV.h;à@éÑãAÁ&°´8§Áì$`wÏ$Íà6gÁç²^z6•ñl“é ØþÙƒUÊ] À¼&×GÁ«°üÞ±xÔŠèJ)SzŸZâ­¥§°Ž[y6FÌ*øž%¦È¨”9#ê_5#¡”²€—𞘵²Óÿž1\Ø›ôÑf$ + @I¢R²ðÑ=5DRü©ÎAºVöùŽK̆Vß$fU·Ô ës)-h/×¼ÜõS½øÇ!ßé:M3F1ßÓt‘!üÈHÞ@¥Zæ©¡Èëq+òbŒi¡gêŸ+\,y!pÍçêì§z÷pJËüä=^õUásð,µž¥ŒäÓ*J™*³¾wO™ÌQŠ/Y.ætBã uãIމD9Îrv ƒIv•Ç.ñ~šd£ez…?0½-œò·å;/Uª5õŠºUêQg´råR<2¥ôîG4µPt–XR0¾W1Ä"ŠÒç$Y!v‡y×ò2øÜ¥@âŽÎ€Hš^JbdMgIÙÏŠïÕÏ2ùÕMÍRÎ.ª€Ô"î”\7µô }m},ÿv/¸ÇÇ&Ž¡+vœ{ï_✠¦äH«J®öv³äq K¬ÿF¨EnÄŠÕq¤¯gtˆ¼ˆ\S´Y²›dmÞ‡U;¡v¡`+SÕ(¸¥8CdirwÜŽ»üPÀä©Z‘Õ®“Àê®ûÃßñô|´§ÆõÖfM‰¡ÁfÉxÌŸÂàÌøÒ÷ª‘á~–Mͪžu%G£¥«±âÕ4%Z­ï«•QªÝHŒØÙD8.½ÉïÓ­*…qÂnl‡G„¤~÷AJ¢4-„¢† Ò·Ô÷áÓ]`çäÎYóJ;”Øïâ]¾Åˆ7b¹8¹ Ë÷žf)æÂÿcGŸ3ÚWúr©`H­|UsŒïa“B¥·õa#ÕdK»¾1Z»Dˆ=­ì“%å};•¼b؉-EO¾±iH+ôM\¾ ç¾oàÞ¾q.]Y:.¸ ¶–¹Dm¸àI‰¶ðt¥bð‡# ~P>¼ü +}ÎqäáÅ&<36·²$Âõ€ãÁÐÒö¢w`Þ£ü¯#”ÁÄ_Åqæ¤X ¿KxþIÝ™_À‷ðo+X˜¯ýÛ +'E¹ÿäÓó;½‹û?=ïëoŸ¾Ø©ÿ;pЦ óý¦/ÄŸ³. 9w`E¨œ7c@ÄØ.X†1T|Y^Všr¨ßbƒÀŠ…ÜË8ECœïXŠ‘Q¨ÂHi4ÊöüyP?A/‚BíÅÔ1hÌfp—Äoòlt®Å9`­ÚÎÐw>žpw”a7t +Ðlp`eêm¶à“¤ñúMÜ’%”XÓ §nˆß9É+ X¼ÞTˆBon.‰ŸµbQþ—1‹ü„Â} DGafz»éžEþ?KáÖ0³nÉ?%Uó_ôÿQ/ú§Ö¶ïq06ßû +@F*äâA åðav«OA‡$ÝÆ;JÕ¿á#Ñ/‘O¿o6dÅ4¹X3Bô¡²ÙËLÑž÷€"Ìš…Ò¦ +A¬¡ös‰ÛŽ0ÐhÉÔ»$ŸÂsWJþ@"¥ùEG+ÃòLÐ.€%iœiÕÝ"°l³ÈÎúèèÝc ú÷dÉ NàJ­‚e±B â”wT*]±q+=ÕNù( vV¢ve}XàO#Ñ¥3Ш-ðq•?Ý˨/Ží;‡ ÷lµ:H Ñ*Þ³YáWQlI!0Ã3Q±Hå*êˆP¹Ž°&冾¤ÛwæU·³œZeÁ^¹7Mƒ2o±n9”LJ°:£8‘¯‡Å,• +ék¥Ù}VøsJŽÒVÂ3«.Dr=œ$­R5*1kDÍyv‚AD˜å*1Ôžö à*Q]änï)À(ЮiýW 7Ó¬‘[÷~¶öM Ó/aõÚO;ñJuöâQ”bÏ;¡k•*ý n êŠò¶5ÿ¾@¦|õaJÀlº2E=çok¼ŸÁ9¹¼²G}ëªWøì¿þðNÿD’“×L'ýZ±—´ƒj¤˜Dg¡ÝÑwqn*©>ÞÁ—Ò–‹ì$r<Ñw[±}¡Ý"˜Â¬† ÷¾=¾(K5'Özdp)F}{»Ë\ì|û/Œøn]m?à ÇoÖØÃ]¨‘,”²šä5຿Âw¨Då“_UÖL®h8w´EާoÀ¨\ìnù.,Ê +!;3{õ‚ùI¬Gî?xv4– 1ÌY–,Âç;Üá$• ^jmnr­B!®$òd*jû®ì)‘Íþ¸ ôG[Ð@¬Û+!Ú½‹&â¨<ý”˜š~*·J`¹«ð‡â‘§-í'ÿÃUqga$Ñ)ºŠ”®?À€¶(Ì=° óJv!/ ¸ó› |fT'Ò"!3û6V³L‰Ô_š9è˜ù¥¢ ¾±ö¼öv4»Ÿ "©¼gÛ¡õjaïúÀÓ8í|‡¢­¾ƒ»€§\vµ/t µÈ½§¹ v +aéPü (Cîï5„R„%œ.dºË €·]ÿèžh š™JT.ê©ÇðY—–S€:ÌQ–…8!¶þ%b¡›¾?@™N*öªñ/°¤ Ã"!(§÷4,_’‹ >u³¼JC–…ÐO’•áKbªüE¾ˆ›7t êdðhWQð°ÏÛx‡†¼ÆÓÁ÷]€%­áž®û mzÿZd¿ôŒßC2zŸÛE|鎼Ã˦Xó3Õà9¹Y`_=H‚¯¬jn@«ùÂy Ë‚ Ãg×!Ԁȗ®ëù€¥¥¿/Q?s—˜åé~})äÐqº¤s»‹z’=ŸÞŠ|gdL”á +%–’$’£”:ˆ9 “0[¯† ™‘»í£K”äN pÁ›×csO”‰hR×åhW°2 +ŒçÈôUV÷Ä—’o?Wø36—þýæ‚Ì·"1ü.·—n· Né|„Y‰ÁϘó$éȯÌWMtÍš<Ê5à@‘X½×3nõ†”_HT1;(Ún»WñÌð4°eÝ +Oý%&…éFÑJÃîe]•h{ª*êýÚC¹X âÙ%ßžrÿŽ“L éË@À:Œ½CZÍ¢Ê=tKÈðËãHº $@_“vÚsØÊX AZ„1ÁDÆÈàÃì÷ÒkS£+c$/Ž5w 5ó‘nÏ+¢cü“«³—ó€d%*%á©—5›³Lµ~ïùŒD·U<¸æí´ÌçG +†°ˆ0Ÿ1–6 +ÜéLXÑ?‹,§Ëiå;;%3ªÝvÃ"pÞ]àœi¶•©üø E´Rd.e‘ü ÜÎHœ~‰?¸ÖÕ¼ 3-â™5ÓL_«®üù£¡â»Ç,qÌWxá›ìˆƒo0&Zí/¾àÔʘñ¨´kŽ’n2+†ÀgV°ŒEÌðavjCˆ·CFqÄϹÜÃì”!ÛÜíñ(ï&‡/µEì,Pøù¸@%×üaÑú™È2¯×êØéà†£_–B¬ÝF½È–鋮讚ÒïäÃk1[Lj¢z–Ó —-‰¹*Öw­Z¼±DxÑ~ÒF0~¨ÞH¡¾Ea,`ùÆ–$@¥+!ï‘;²ªÖnU Õèè}²èô q†¹Eµ )pÓî\æ\xrGlòÎ.†¥Ÿýò¬ô©¼½r}Å:ÝF´µ“ò;ûˆ¿˜3B Ä¬­Áí4:C‰Q & šî£:Q ¬ïtÐéEñí16i…t:?Ü‹% ã¥@y»Fz‰ð„ ;2¹Oœ}Tf-¦Œp1ÏEÈ}V ‰ò°YßÜQ³H95Ê´!RJùèë–ƒr€Ä7¯å$ä1Æ´†Í˪yÞI+rW°¯V®ji®kËOYLÎý]‰¦‚lá) j玌험ƒäsRš"ƒDfÁîå”Þ PFi,aÒ·„èBô…c¸þ(ïY¥ŠÓ4ÅI†ë "~•hXW¹‹”ú@l1‘~ƒ1…˜,¤©¸6{‹¥E±üN³Ë÷ôŒ¹Æh¤¹s îØ|Hæ#Ý—ÃGþ5™¥ÑZuág@á‚? ùý;õuÐÈ ÿ;”œÇÏè-y_]’Ç_–dŸ> °ÙÒ\µ£@Œ8ûÈ¥Ãiuf=ÖX»™i¥«ÂäVO]÷!G°+aA>«*ð5Hþ7=Ço›Ï÷¦ª®ÅÉYF2d!RíŽYEÔú:ú!–¦s÷H-%× +vŸ+åÌäV]ÛŠžYôiºf÷òÎf쬾dŽï"q’ÿ®"Ñ +æ"œ-B'MV¥îβžÂ;5²hÄ“.æW@‚ÂÏ£ÞsE[éu‹2OÞdÍýÈ$0¦ãަ¡#ˆ^H¦ô(%0h +ôB} º ý1!¾>¦½N,¼]¥›ó%æm¿²ì:¤;0©ê=kˆŠè‹ +ÉJÌÖ+PÄX·á7C_9ýNOwǵç=4UzõÐ]bõçofD4U¡„k t-Š$nçÀ¦Þ=u~îí¹xaœ/³ì»bÜâ~ºŸÊ£Ze¤R÷-ôlÊŠ yU¸ýý3oZöbQsË; +å/ÿÃëÂ;ã«ìÒ¿r&R”¶ÚG„Jß½¢›,.0~說MUØ'P‰Å4ñ‰½ó3‹g½ eO›À-y«i¼Ø½* Ê‹¶SéÊ»ÕÁí»WXôŠÚ~ìUhNŸ&ÑŠ}p˜› ö¿¥"?Þñ&®|™ü[^n…b~xßfÅÈoͽêÕXà~%¡ázZ»$&l¿ìšÚ1_ª +Óψſ7yuÈ~j°€@å¹±YíléÌʧk…âïŒ~ çHIðK| …ÞÑo‹ÂÇ€Ù©kæÇÐ|(%x§©iÙ†Cg#½#Å÷и‚Cñ K9€–¡3y°\‰Ÿ8Ç3áo±ˆ†‘¸ÿ÷Œåå9Gm«“¥»%_ûƒÝ¦O>5î¤Ãîü‚8Z$ÑÐ~–v¿S«©ZzÞƒlÕ×'ñû¯_-¿×Ê}GÝtôq¢‰õ¾jÐH4ê}t¥¥Ã¤"†0–…7ʲ|Üå{¥W7Od CSʪ_å8£äÈ)®ªî°JJ™Éz¯ÉÝ\%½¨×T$pè%út§ïý<ËžÞUdÒaú(€ª‘Ã]CÚÍÇó¼ø:Ï…îß¡­“Òú1*æÈR½JÀ´¯èjzÚ %Ì¸Ï +¾›TP±æ‡}È`º}·îºžÚ<Š‚Êm×idb.o¿êíep%8¦apܺ>ÜåážË:Jå#qŸ?{–`Ž7ókîœDŸˆãÜOEõ*…ª#ª©²¦¼ÊÂòè^‹l¦û)â®cpO£ÛµÝèÂÉï×üüp$¸bÍî3ÄÂ-²ÊBñÌ0ˆÞûsÿêzÏ‚õxmŠÀ Bgʼn‘]²±UΤp¥äÑF“!=À—ær¨e:-o©¦ŒÎÜc>Wz•æÕ=úÀçhÌ£SÑ0n#’Zο>ç>#G¢/‰Ò½ŠXÌRÅѧ\1–ÇÀ…—.‡¾_¦0ä0ùn6®"¢s¨ò­ÝÌæx§ƒSÓÝ9†§ŠÄ±«EÓ×CGáž®VÖíév^$ûé]Gœê¿æÈÒ=«‹«Ký(*AH~¿àüƒúý#˜ýje÷?ÂrŽ’º€/EОýõ)§<î–,Ó¶æ¥n[ŠËu¬¤ûõÐx)ÓYq[°æ_ʬÏ<öíИðÞIzþÁbýÿ¢gúmŸþAð²€!á®Owé}–¼t'ÒI0Ùçcdz“Ha?ƒÁFP+`ë­ý¹«æ}Éû<ÊP‰­©*Oï}´Ä‹îÄçjì^Bpð¸×xJ0Zã¤!ØE´«µßH‘‰•¬³ŠfêP+ø/Q‹Œd¿3ò Þ7k)V¬ž9²'IÉÇX%•V50û¾î œ.Iêi³ôÌ¡9©Ã…;>¶#QÏ×ør^êÕ~ÂEö¼m75¦+_&3ù=øÖd’³ÅçË¢¯xët˜¦Zj•Ž;ºGŽŠø…’^£V¿ÐÊŽj<"£÷§4Áýð6 ¢ø^Z…bŽÃæî¿–z¡%ÁŒuÝð÷ŠÁK™P©êj ŽUÚG>–œÙ +R÷KÔ¸RÆçV¢æñ{Ôð–zg¸!ÁËGî*¥ž±Ô`v.&Ð@p0”3ßzK‰…ò<øGËï>¢Ì!1 DÙK +÷ŒJƒòO_Ð +$6_m®lìÓ6Pz¼} ¿i«ùÅ×îØWÄ—u$ùCY÷èFÐ+eüXEÁÛöÔw{²^×<‘ݪeýøi ÜJ‘9ÊïßòŸ™èˆó¬{ÔôÜ-ÖKQð(¥_µÍØ"ÝnÑ5µ¥0—~Veçþ~ „Œdå ;žÊŸŽ-Û¦ÆWð¤·JHóãh܃ƒ!ý2²ýRs®e>JXÇ™Y¨Ô#Ë»ï!4• s\î¯R®27q²ÏžW°êFm$H¡–¬V‘9¹Í(û`ý ýG¯‘ÒhSøS”ó¢Y¾q÷‘ïL´¼>B}ë/ +7¥ÐA)5FdE_w©ËÉθh¦U÷fgutl”*€?+à3vQ1ðNŸ:!‹Ü±˜ £Ah ÊÓ®³¤“¯’¥­ Ã #6Mï^Çïat½á+Ï1·ƒxØ«O(|Æ…9vç8MÙB˜è£âè:Nøâ×\_°ÁÂl¡%œõü—µŸä•›©y6Ñ©EOåÀÿ³f`(W1Ù}þÕhQÜîμžçÝwϤt#bGþYôöÑ™0O]þª…U%IÕh™°ç½ê¸&ˆñ×,Ħ¢Ül©§§qó\íÈ”U7‹×(*bê)v½D‚Ò™à…EpìÎ>‚dÌ(“BInGh†ÔŸÆA¸ÍÏ ;b•\)´µŠ-øžÛ Þþr«=Ç•íÿËÞÛ.×u\×¢OÀwØçT¥J:R«¿WŸTn•;>>‘c•ä8òMR*„%ÄÈAÛÊûìw1f÷êµ7 ‰AÒö¶l ˜X_Ý={öüÓ·eÛŽÖ¨}#JRÒrõÛAZRÒ¢¸Ðîêæ“‚ qW4`jС¬é²õ ¢ÚxDh€WD¹H€Œ"På0T `DÓ¸LR•Ï,MK]Žw7 UxNÔ"ùº°¸Ôh•¾Dù̃…É¼ÙÆ>6Å‹³È3œ˜Tha@+(,d +ƒÚ‹J‘ª|ˆ×ò6fn$ª''*H$¢p¡F©:Vüy +ÖÅíÌ!`‘»9BQ0`© ¹éžù$3{CQ—¦ï¿®É#(‰¥ÎVô#ô˜*t]äL{EA0ç„Ì|ÆûØc‰ \ +вw‘ÿÀ²A_­×'8¤*3XôÄæÞ™O ÈU†.líÍÙÀ’+˜#öÈ:ððW›Y"ó8õŒµÈnÃê! &B×ö¾Lp_¤ù(£ÝRzßUšÕã©_’5Þ:25„“‡È(1§cÊnë_³üÛ,ÆA)ÛpÖô§f½/™…a¦@ÍdZ-n‹–#C+ڂĵ–?¼%qÒWv‚´›ØÁ)d%WDøãÕè‚.«¤ì]¬õFõsYÁ&ÌmÉèÃ!°Jø  +:]ÕÖYí"€jµ¬T mˆØLÀB&öꊭ–m&~0:½3Ç—ÙCˆÇ[ ¼‰jvwTv•ãíÝø½Ì|HÕ‚UëìâÙÙ×îÙ +‰ï Aâ¬]FW^Sz²ÜW–ïÌ¡'ª¯%€–™ ä.¡ú0W+ca³›ôÂó•>qže£QÉX„{q…VzŒê’•'g @ ‡#¼_¬BC&AÕ³µ'bŸ¿™H8 +5D Ó:"{¨?’µ ^ºËÃ+qd‚:ÁÍí $#õ{3q…Î –"Œ†P‹$ ¼Ýœ|áp^Ô-ü”T…ÚÁoU)#¬AG]3N>E$U ‚µ®)¦€HՆȒ(*-TãPÀJÄwÊÞÀð@@Ã8N5 +½Ô‰¨{Ø“'OæÀÁ ð‡y h<ŠÆ7)§‡ ­\Àw+úzÞ•åÐ œŽ¹B' ái¾açdK× + OÈÏoGdÏ*Ø »¥ÑYïiˆM,G¤_:«Ô–µÐç½éÀŽ÷¹£_BëC˜5{LÞ`,*ü¦²Z7•Ð,7¶zƒñ ,mk€W3x ÅQ¥œeëè&¤{ ®¥>s^­Z³¶ÐzBµà¡S»…8ÚЮ ‘÷@ûÌþÛ \÷LŒÅвr +ìšÔN/þ¼ÝdÀæ«0˜2ÍW¢4ô’ôyv(Ì­™A`ÿw›Í8óÝ…™¯Ð;±½07=˜–F¶jASóQd•mSWgc¤YuÎÑi6­5Ì$• Ë?Ò7ÛŸƒÀŒJ2ˆV–ug‡<ói+Å”¼ ¦W¦†³§<ø„©[0—`ÿà*ÖxТš´X!½¥½T' Tt\DVÕIðç•Öi-NòÌKfÁùÃaäð;SäÔ­ˆÚ”–†.(x\ièÕ«ã%’skÛÂØ(H„ŠõAˆ_©„­ý&ƒl0}޳Èk-ÊŒ…0/DU%a2˜‡¢¤6’‚îÉÌ$FX®F‰I”È3B^#p +150ðÂ*`éDX¯™¦||LÔW}®3¤í…ñ9q„$Ѽd¨KÏÖd>è&"`7ðPÐŒKå8u’{¹>1˜“Ññš™q,qJ’À8uã| …IžIrSi5’$d½I^eRŠ>§"T1S¹÷¹ã!ϼ A~°µ8ãôÀYƒ…¡^ÁIü" ŠW-6áÌßÊ–aŸä•‡Ý•h{ˆRsÔ¯yM¹y éwº/UkÌä¬îó4ˆHA›ØEk”ÌgO­khÈGÔ“}‘øtÄZuÙ°ØÙËþ˜–¬à¼àqX¹>ÙûØYf¡8¢ Ñ? µ Àæ—gcÒ¨fÝF? ضl ¥|(ÑÖÍÁ¿4Ò”êMÖ†¾|:=3¤@n°ñ”ÅÏA‹$Ísû¦ò"ò¨n<¤TÍTŸas‚#\Û‡²4cÁj¤ŸYà} ,Š´ªÅŠÉ^ÇZm,¨oÍ–èœÂ¢‡¹·ZªÉ@Jг`› ¶„ñv0<î¼&ç1Ù0ÕCPü$ ¥ç¨BvY5àa1,;«‹µ«àâÜæ`ðÁ4=H–Ðá†C%@XÙK*³ÓÛš‚yØ…™¬UIÜ á„—@1r¬(Uh(çBLPÅ( Ïìß ‘¿çÞ¯uŠ^7ñt +ì?ZW<—ø9™µgj׊N$•CdÍǨ™Ê”Ø0v2T{´F²¾v ©ÄY=MˆËÅÑÆY‰aô&°_YôíúY~ºÉqÚdsT–Ûrnq€j’hÌ‘–’."êŠ(´ˆö×äAMÃ3ÕGF;bUÇÎA¶¯DþæR0Á„¨Í¢d÷0…Õ‡üŠTR„b€n‰jÂ+Å#ÐE X‚… ²•]Ê€O•WÄÚúS 9v¶îŸÑ[LŠ€ªÓ|Ù­µÙΰ‹&õ`ePu¶0Àb×îØ÷Á:Q„$Ï_C—b"ä÷€@Cœ•õ"*üg AxĨØ#^ Ÿ¥@«BnPTå8ûkŸ0NB$d|p¶° ·CyÙòpfÁ-êÅÔÜ¢a«™M–àÑ_`ç€P]‹ä°Ãa²”_Áøû<ïVé!Xƒ*ýƒÕª¦(ö"[‡q?Y…ZĔӬÏ>½%i‹dÜ€z €`à qeõö'Ìl‡¼SF;4ý9YHâ#͆Óz¢¼wõœHV%ãyªFj“·u ,<@lçZú} ã,¤ \ÂÕè0»J+(%Ëß̶rU÷•b÷·›”¹S0Pjì÷Íú¨fÍ'}“ӹʑ³È\<Ì ›#ý¨ø—Es´= +µ™ÐF!è2 Cuí>*‹˜(ÕƒÂsˆÌANgK’öHAÇ"XÉiP;Ù=¢ØWQŽpõZ-ÐÚyå(')tR[ó¾ä‡Lð IÏüæR5 +ïÖr=d,ÎÍé %Öíľçb‹ÈÉ>?H‡¡»6ˆ~]À;e4ä.¡Õ…WU§u䔦›¬äÃñ¾¢Fs'J†g÷X¶µ`>dlO¦ÚxB¬T,$ÊOÔÅLZ÷¨³>ÙʤP×Á£[‘afØýÕÎÚ³YDµ»f=9Ôdû +4}u,£î£X»-Q4ª8í>Ä‚@CšÍ¥h¬aôˆ=£åTD± qÃ8B!62Ø‘Xxô¬ãšlz \;(¸jŒ¢Ô?ËÁ¦éEŽ3<ÉV·T³¾Ìî#¾O¤)j9aiô´*·Æ+ÎJî8Ù#ÉÇžX©µ8¶+<{´GÊíšÆ_û¿3ô{Ñúr s¯?rDcþËd³VŒq›ÄЬ†'• +y¼±½˜­ý0Ò +ƒÎ*º-—P­³<),,bÁœ°½1•.‚ÂîAŒáfaq£ÄM§”´Øð  I6hä{«cxB€&@X"¸:=ZI_˜‘LŠ+“ŽÒ’h™Ò”Eá&#Å{Q&ßï[ŒWÒ ˆ_ŠÆò\¶¥8ÏE­Mà©sä©lÛ„é—Öx*gåáÖV +P¸«33&ñ;AÜN4½DªM á±ðþR5OÖæ«â$D64¿Û[º°Oxà#§uÅ7ÃÅ/6ÏÂK`;(Ú¤¦€NgËK¨?¬o|SÐé?ÈçEÒT“g7MG(-eʃB<`ìÄIH’bD÷І‚†j†R 3/ë*ÆËyu-)ËÊŵQL1YèZÕ(AtÂ]Uõcî·ÉÞVäwÑq¤kÝÃwf”0.FW-–_Z¿¦¨gQ¹¸±HÑxé¥=3l88B]P;R°ýb^ž4ÎÚÙÚ¤0Òí:Z¦ C' [´I¡¨cé¦Ä%©þÁYyŠ)ë8Ÿ¨)cå‹WÀ%Ò48'4Iì-]p¼HÑPRf¡z­%Á P®]27‚AŽœó »ªD"ð-µ]EDج%am“g÷âʰþQ\"Nb £·³©€þÕ,]MÞ®D “šE«¥€® ÊN(ªë‰¸#5:™ÖÀt“IÙj‹ŒŠM„³ŽŠÜŽªÙjP$ìÎ[«Ú=þe&™¯Vß„¼¯·ÖÖJGœìpƒ«›Ûup&!ÇÞÍû$¶Aê·zfÌ8õ•^¿®29΋D“ ÙE-…¥¨ÅfIú&RrÎoV›eË£")©7š(­h2±ê¥VFLR±%,¾ÝºYÕeyo.¾ %šW,P|º­;Sf´Q¬º²¿å³;lîkÐ'˜n‚ F‚5qBÛ3…%™Š  ¼\ÛH_’H+@IG è$@+˜H {À #äØnc  4oºŽLIñHŒ%«mŠWö)S:¢A8y"ñF¶M&íÊðåŒU)a¦öÚªø()ýÖ@ ²C¸Û0„"Ì>ió$EØüšMÎ\õSÜÎïƒò[镪×wöã`´œÉç6™ðöañˆãÁ ç`Þšw)dƒ£ÈÖyûJÄLü>Y¡œyj‘À4j–ó³b$ËÆð,®Iª,n2t••€$aþU/hIá¶,LxX.…Pþ;¹íˆÄꦢjD+ñÎÙ@Pž,x^—Tʨ$#Ï!©%6’t+"u¼õß#)W#$AYØóK@¹kuï![ò1>mê`Ë…ºR°«Ú\4°£^l@h„\UdI¨þ“¶VIDµÙó­¿0áµ}-4ÛÂÌë[•ÜÈ v«ºW²Á9+O˜áA· +Þ¦^í– rÕÈBNg£·düB§&ægüÓEìB$ô·jž„b}0j` ÑŠŠl½Š¿‹:b>X«Òwg*IÌŠ9Ñ­ ÍH»œx'ÆHQE®Qಮ$HL¢ ••ª–Œ R’Èü$9U‚8µ[js)¢P€jË#)"M¯%‡±Ô“²$6Ú—8¶)­r3Р[ ÁycÃbŒ«–l£7[Nƒõº,1ê0[Ù›H¥ØTÆÛÚ0 ,.DV©¶4ÌMÞ_¬m2gÛDü¤–/Á¹˜ªÔ'PÚ©î-1³¬8±óÛR·ÃÄxn&ô!¹2"¸¤ó›4¦ËæÔwæ94a=!ðÇr:ј¿Î``l×Em1ô^鯠Ê;*IÊÕnÍtàVUYŠÞ‹‘ÆXm°{…ˆ…kèTfqFÉTÀ£«r‚XHÀÒ¶Ôn hVoZ–fÔ±ÒînêfTû¥F+¢Åìú™³ +ÈÐ`áðnÕO‰½Bí+UŠ˜ð$«¤l-Ò8±6e:IeÕ,çŒ]yHK*=H­÷hxË™M8±ÈHE?bÑœ}Nœk[ž¤ùHEõö‘ð,\öå£ +€É&¬Îà\p¦ÈNÅÛüpÔ6…Ý0¿‚%ÍFh]<í:Bc½¦TúÝ¡Q'V6ñÝ>£ÍH‡Õu‚!O•Î|L¶Ð6V~ŠÊ´UѬØY ‡Ûè!åŒÞ6·¸†HCfb>ƒ"‡{ Û<+,Á…#¶®¾­”‡¸z­V<çhã8Ö,E³Õ~$×$ AšÅÎø#»ÞèøCOêy²ªJ`½|*âÜßÁØ‹ 'D"‹ƒ¤”BDó«eoEª¹iïV© y&mÑJó–-¡Â&bže}0ñÃ@ÑIˆ<±›@‚žË2ASxAÓ‚¡ŠS­¿HrÑ „ëT‹JRú$i,[‘b‘¤``€C$6^ËUåÃíV«aä^Úˆ?‰¤±ßÞìB†i V×Èr?›6Ö´*»‹@oj[Ea[;4"_Õ0¶‘ÊÏÂ¥gÍ¿—°-`(JÊs¿“¹ZœK&R‚·úÂûBð€`-߸,VW$b6~s°%=Å^Üía¶¼pr'ÕÆpÙZ·‘|#Îíºl\# ó}ÎP-$4Ô|î³>¹…‚qä²ìÆ‘Œ,p©qЇÄ/@x;rajØÁµälŠ« S…SœÅ…Ä,L‘RiGfVõ?ѰÆJ?Kö<I¤&üœÚÍô4Pˆücwj5Aœ‹¶R½j±j#MÿDi¼ÄŒ8Û™*9ˆH\‰‡¢ª¹ÝªÓŒ°Á¾Wb&xóôP~«!& 6Ôµ‡ªøk«$>¦A¶6'Ø(ÁÛ¾ÍQ]@„H/mh= ¥¦$1MHSÙ† +¢ FLs».÷%lOÛ¬êƒ2=Þ¦"K'˜'qýíøG§ÁÔ´'†A’W•kÇHÌ¢›¼àŽ’¦'¨üš}®H£1ÈŠlU £%Ë?ùX‚:³ÈÜ2ÚQ_Zµ·³žjGA«_ ¥Qh#é>:®Yçžì6ú£ñ—É +Zê< % X&Ð&@©ö^fE’2B!Ð'A„ä'-6š‹v•Ýæ qAyÙ¤¨‹ðڸĠp¹ðáÄJF½ ‹-ËŠíZ1Ú‰ê Zìþê­~'èOìä¡Û¢Ÿ #Â;{ÝŠ#aßÍn<*H¯í¾D%€-ü¬:ˆ9¤ø>÷5ÚŠ+ïç€ ´ +Æ«é«ÊªšÞHì«D’× ¥Þhžrñü[ÊÚ$âC4oƒvµx,ËPUX†1'Í–ô+¡ºO؆]꤮c ¤—åS%| à‚?a“D濱þé"?+åP=4o#8 “sñL·9Ö8ƒD­Íî#.[>)êÇ…€'ãW¿U†tz2€¸ Q 2q“%ÓÚœYŸ” ¿CM­ì+˜W®†:¬áJ·&ÅJN؆”jz}Ît±I›’=½;.ìu›U{P(… :M|4²àÅÑQ êTÀ’5“²²1)ÞÙzS÷Ƽ工ìBsÍ*“ešÖ• Ö“„Ò_r#H‚ˆ`ÔFÚôgŸÉÌVÀʲù&2‘2Ÿ”aÍžó‚ O ·¼¨TÒîhˆ M’t$ªžOöÉ4C2b²[gá ÓH"ÖIBˆä¸’U»©Ø­ThÔØW8ðA½ŽˆÔ­rfÐdÖÄlOt6°æ¸r¹üT1µt‡-ÂËà¼;³å´2ÓÜH†ø^‰FÊùàÖqQÔüÇÛùÃá´Ô{N£àÞ“µ@IàX˜ÆÐÀÞeaþDõVê³’Ä¡faʲk›s¢QMG+0àÖ€²a±±×(¾á‘eYi M*É®FÚ`N†ÄôhÆÓì68¡6ÓËè²´Cv˜ôv[F㦅†Üî#@ÕnèP7ïÕyëÀ¿hfÄSûÊ¢7 ÅleV…I׺'€r)P H‹ØFìbÈBß3}m'ă}ºPz5'ÐX)Jp©}9ñ)œW{GM^¥û’K²4Îz± Jà})JÐhÏûÔ;¨öIóäúm¬ccZf´‡3-ÓY„DfôZÏ)öûà/-{]@+9rF±÷møçA ½~ÖÅÜJ±àïÌœ)©KœßÙf%ÓõEGI;Ö3…ú–ê…£úû9ÃTD½p²š gkÒFhíé‰? 7“pí«A;ÞËjÙ±R¾f%±¢Y\ÙK–! ÐÅã’| TåZ-¶·bÅ,Õ¬›,*.¶£%®Ô"0A¤'ømm‰eAð-µm`ßÚ6\sýL/bë[#‘µã á-@ðÊ1c¡äGT`¯2[, |˜]5qL…°Â2ÅM¹B5ËáÌ;*cIjlë IV“º’³óg®6ßÌ/ÇL"¶ ” Ð ÍfPV Ý¦iˆ*ë†ð„@¯Q]O\x&2v‹ ‚Ý“aÊ€ÄS×€q­±* À‡¡­U$1ˆÚ°’ÍœOM0ð݃V"Gå÷±•Ï“Ò{\KlÁRy5&j™ÄI´0²ôv»4 õ£Ñ^2|άßdÅ)¼å‰Úöš‘WÊ~óð”Y ž´N=*ð!‡äòA3Å¢„G"Û´ÀªŸL¨2rK°-˜°ÈvŽ´œ¡¤à¶ŠlO¾$4ú6A‰¤P`*Ï$<ŽJëŒÕº®Vaʪù6.`“¢µ?`æ$;ËXÊòÚÀÞ€àd£oôj @[ ¸ŠeT$õdn‹°½Ðb°¶”þ·ÏfÔêú +~±†¡ÕYïߤô’”Z®B¼.[+æ…âÕÉZ ã}ÐArK¦óÁ:-@Bݵð™hæYB(1ƒÉŠJeZܪ÷WR¢Vd7ïD9èÒ/¥õ(^QÐR[f:] Àn@›%Ë£94 Ðæj™ŽOêCq¦n$ìa²/E£KMÜs²‰¡6[fsObò˜Æ,dÛOP¼‹»¨ëH0ƒÜ7L!B‰rÌò¾ª^µì>­6ÐNÑÎ\[4[eâÕ4­mŽ* S1=,D4±ÓÎÒžx„1ÀŠ’j^EAâC2ÜÞ7#aÝÒ§ÆzIgÚ:O_Å4$F%ìT9ØÐ%[ãkUçgU„há ©ÂtîZVÝ;áb¨÷U\47÷LK¢ŽîÕ&Ù&(£3iÁI1nèIšŸ²]!Gtº”á)¦%BþXö؉šÇVdŠ’‹1 äJà,¨#Py„ül"‹âÌ€É_(Y±qW¬Pa¦¨nrIAÄ1œ¡µ!¨‘Øâ9«„²¼'CÌ§èæ™lpo¸/ÁÒ£¾22”мÉÝÄPp=‘ +£ë åPy•ZJ€Â1à``o•ƒ£â{ÎÐ~žQ4õ×~~‡fž Ð9Õ“Y‚9”4>#º® Rg«jáB‘o’&‡b}Xк^–hm³Õ mgsË@£œÃóÔB6÷êsK´jA­Ìgœ¼¬güŠJ2Û¬ ÝE /UäÆÉ9é(à…S*X“JÚ‡–{Idz¸‚ªÚi#pƒô.ȨèCÂ(µD:wmd4:\³¼sá&`~0Qº¡`°E–ÎìΜ)n‹â“¼Ùè¯Á¾Ei^pããÅ€KQRâóEhRp¡§×À¨š…Ž#âiž”A@|[è™-™AH'³¨3•B835“Ëá–(7ƒpŒÕeg™´jir&é1¯µÅ÷€¤,@ ×r“ˆÓY>QÖð2ƒ° FáJâ!O1MÆþ–¯¨Ð=ÒÆL7dÚ¤5³¹ƒAG¨+8+䈘åOXŠ)]JÊò–¹=1=¼Pe´ÌX#‚ÍÖ¢ùˆÍG·2)hÊ…ýIÄ`P¨ ”¬àŠmG73kEÊj\Äý‚Ü{ÄdB§¾Ök*[»äìôY¬OM ¶Ýs‰ÈST«µWÅ"°§\ü¶Ä_…G‚¶лöctfa‰Ðºù¤bAá$Í‹l b@M¼1ìÊkÏ(€ Ib<:G– É\û³Aœcõ-ž-Á ¯V­TU’9{EXz,A B¢þ¨¸ Ð"ÈÚw^¦'ç­‹½óä'*JDžxNáø Š&˱¥¨¶y0'؆¨|–*(‘ÇÚBñüj*Ö­‡ [ §dEJ"aÌÖ¹—Ç$8‚{×ûÖ-(A°ïØD=½ØœÞ ¤fP™¦Þ²ÉØ3¬|¯“æoniW+î)XÈü‰Tm­¿åÕ#ǰ mPóñLìûN»-d¾'|@Óç$])2‰´Ù²ín2 廢HùñLHU¥ó0Ïš™ubÇ:§PŽÆÆ×“uÅ€»É[´lv=aé4YÚ+¡g†y\tª#aNe!Ïf뉂št–¹p0¦8A‚³R(ªùo„öR<_•O…_ï-B =Ê¡oùƒLNq- ™³úPb·f†Àpfë¶Y¶!j€k/üdM-ÐÇ1*;݈•9¡“+Ç[¦#D¹f O\ò¬ íx¬_bçOÅ7Ñ©»X³X­‡‡++Ä^Êô l½;Ù‡”aWa„©z +·)šj Û”6M`hÖÿ[{QP”§ëƒ%Dë®ÏTá-ºV{ÖØkqÛB.Ã+Þºx/Z“çZ»¤Ì1 +ÎFš¼W]È‘pKUC‰na,„´ +±dù¬©ƒÃd¶vt· ¢1Û£I˜×…RQìš ZݧÖ_Z¥{Á’…9bv3 Ö÷7¦Þi³Ç‚YÎ+8£€¿4ÃNÇŽ{\sß4d$®–VO %ãzÊAÊ–[˜<15—loÿ…Êp'¢š<&¶µ¿š­Gö-ë²4e‚ˆKÊ8VO¾†³Ë2GµÍ‘Ï•õ䤓ýðVL¶g:Έ’FØ*úò5 ¹ÍªI‹Ù:CÈH†-¢=œÄõj%É­IØSmaJÀf'vª³ÚD5©ª~ŒBã)Æ©–»ÈìY)‚^¥) ã=SèÐG~fR«‚¨ú¦‰x “:M@1¿J®filÄ°Ñ +ä³'r/@©œ5*ˆaÙ¢„‡ç2ì?"DÒ͵zgbûfø÷]³ü«q +ÅfäŽtv^°‹ÄÁ¢Ü³u‚I”AIÿ¾Ô‰²ö™°îœÉúBe…(›íDé'¼ vª•9jIÙê!¨é s±—§,ÙAÿžôõ$Üoð­âdƒËF´ ŠÉ°¦%gC%¢®fëÊ&åÀÿœmG–3iãŽû”°lËí`ÀóœDÛ¾ÆF• +ßÑÓÿ b RéK·Òäf«Dßš?0|<'4b x†4gv¶W¿Žˆ"ÂÙ[’Uó¥ÙIHAÉFòR a¢ÖpWÁ‚¥BF€çá¡‚zv׈¹Cڵǒ¿ ®„§‚…È0{ƒ614°,y™-Ö õ!ðwZ{HiÄL"NÅÄK n²Å¾¢·çÓG‘þ–ÄxkÀ—yÄÙÇß øéùžŽýÂ-¿SÛ+pdÕ '‹ 6à Ëà¬2É9ÂQxÄ–áe'ÐVe”̽†(ƒñ´”8ä¼’súÆÆ°Qsä|†®Œó³8?!ÛíSuBÁ 7+f&Ôæ€Ü ¤eÁ«b¢›!;q»ÐÁÆ>{†1öFoþcÅ0ÝÉUÙÀèLtª[‚ó/²”«›–"Ö(„*d§Ü O³á¨è8w2ŒbŸÅ¹Žòœ”e™/%öäòÂ1†Q‹™ÐÃlF™æ2 ^KŠ7.›Éœ‘¾(6˜[æxOÞÜ-´Ñ&žÛAäª#,?á¸Ðë0™YN$A6ÌA¢Ô†¿NDÄåa1¹œÒÞf/!þ ÄFš![‚…I +ÕĆÎ舄x¼ +4˜˜ Õ¾J@°4ä[„A4œÔtášœÖFˆ„’@@X¼ðjdb 9‘Úö ÀÑzdØ +ŠX°³[c¢ õ~ÇmÐNžM51ÜÝÄÄÒP•=BWs¤Â1)§‰:@UŒUˆO,ºcûzPxKJd‡·-Û”8BÒ™¯[¶Ü”¤ +³r +éµt Q9vY…7NþîèÕ+}b©-³$½ÊAÑDFùXP iæ«õôhО“2ÏêÒ ?,|ÛˆáòÔägz&§± ¿;|æàÔGÃÅ43–¬|ÅIÙH'üJµƒâÔ-Bm˜„éE¼eb=+òȰšz­b©fJÅeó´Õ$Š ¡ËWœ¿“MB  +Mfײá0­l TU0/.óXN@+x©”ÐZ&+ :³Ô{ì:¦æ¨ÄˆÅ­B!LÌÌIjÍð»a¡X¥iÑ:ÍÂÌF¦t†“ÏpÄ<1æÁN™˜CKskÈÉÈlCñø¤à%ôC–ò“¥îí¸{T½¶“ùÍ0Ó®èSA󧊧 +•tbœØk0ÖIͼÄÓ4zöe@:ÍBCvp­Í ;@h:f µ30£L´Búj,ª1„6«6ÅlB‹@Þ­šØ!¨Ãüž!³Š³šµÀ¦Ï—TêŠ|¤0¡ [Âê¦F¨•²œq ê‘™šˆÅf¸,“ wqvìMÌ>ˆxPE’:UQ"~ÌV×I‘4Ž]*)Ò‰ Ó†œr9åS‡û Šcó]aшşJ‹“«ÿ‡ûTk€…º¨æÅY⤠+5ñÃãKó¬ý"LjAÂì b€À9rSP‹˜zâK´Ü¦Ü"v ýü¬¹“ÿÊÁc +ûœ“eÃ-µH㣌Pì\VMTræŠuÙÖ¡2s ±ÕÂŒ>P±¢\Ÿð`GÌN*|‚øÅn³– ¥ª_/|L,šwó$à½ÖKº¥#á˜0%¡¢( ré]dæ: +Ø;·ZS AJV¸ +íÎd>JmAÇR¼¥v‰(ÒÌ3€PÛÀKR¨Í¥ü¤…ðç$À‚d9ܤÐI "b!0 +©Ú¤áœ`æ™fi¢ ²:Àà@q­ø$x\ðl-‹ŽJ|¶ò!ƱE«šg@¨œ%]4%^–ɪvmXÖÏ/·  ! +i='Ii€\^lÿøÕÚ¢ÂE÷yºsÉ(¨iMfGñ¾ bÏd‡:9ê28κ³DØk-YŠ!<©'s"älµž?ôô2K%O‚U!h¸õ½4Fšc´2 ìŒDP§—,ý‚5M±ù†±iÓâ„ú7,#  ß&YY ’Å @ˆQK|¦ûX…óA^•”u«dúapŸ@äG@  \ov“½¢ì±¶#5¿HIM¹cAE݉Õw9ý°„BþkÉÐþ·4p;/ãù‘EyÆ( _¶“a‰AŽ|S·œ¸hµ;4<ŽUø á)9¬¤?xëP2b'1¨)g 24 *R¾@./SÝqÚʼL<°Âzô*™À3g]žXæs¶E’2 åGgdý2gÀ }ÙLؼl©¨’FÏk¼¯2Ã…Ín|ÖEs\Fžœ…¦/GèÔ4J܆˜J°±Y÷é#K«[ø ÁçB£Ñá”JCŽLȧœ¨kX +,÷‹êͬH W>¾dfÑ»<ÜiV%²=ÖÓ¬nÙ$§r¸¢½½-xOªu5…CxPƒ8ÆPQœfd!më‘í¥6ÇÉ)5!áÔì·éä‰ö[rj& +¨¤ Å'µç°û¿Å`ô‰'ý²Ž˜%ÄâÈÌeKœÇ€Ra¼4÷ù]Ô;ª˜'#€>!¿Ä1L†æ;@ @"î0¬±Ðø-S˜Œ` vH˜›Å PZaYz;¿™­ì!l`O`.˜ñšÐW&Zò³ø‹å/3OÀ¾eU;eÁz‚½ö±+ Àæ®>l môdûÊüž@rÀ.”@”.…É +Ý;Â!sç–ýûô³”e›Ãºö •‚PÓ¨M@¯I %Ñguð¥l©hy…«dœJc]/C¦üÂq¾wS`ê<MÒ·ÛØ¨ÛÊí¶àºÇd×wAkà \Ó/1¦EëÞÒž9;ì7Mˆ’hrH¬®(¦_²éf‡© 6Nˆ)@Ë8ÖPµƒìwªÜ²;­.Ú»–›#Ó„ <ÜqÓ Ù´«ü.Õ›)#ª6G÷<­çx|üK—>»øóùågç7¿??»ý⻫¯Ÿ]þüúôëËó?œ§':ËæX.ŸÞÞžß\ÿüÏÏŸÝÜþæ»çç{I#ã5|Ê/^^<=·Ô’tø ÏÏ¿¹xq{Ãï_ç„Ò€ý Ÿ‚/sP`©sH”30ÕåW§Ëlüy¹ióÜúÅåÅÙùg§—ËLýâæâé?·Ñlæçï–[¦¯~~ýTçïñ«O–º6ÊËÍWç.Ì÷ñò¿/ÿ´°ÿëå²j?ëçì´süšå¯ß-¿üßå‡ÿZHÚÅݯvÿþŸÓîérù—Ÿ³®Ly +La<]8êE´(: é1h‹ÁX,»ÃûNîx¶ÈbHYUz2Yâ eåQ.OfŽy™J^lòÿûµ8‚ö"W…ùxýô8A“0úö^­`!ı#®éc¨ÒYš¤„êd`a&kÜôxö4ÁîªrÐ0uûœÕÎ>rçÁ{Îðr¨W®¨”} Qû!·î<ÐUa\±Œ‰*}µÔëÁ«&zÔÅ!•¬Úƒõúüˆƒ÷q +BOmAÙ†ÌPd±Ü@DšÍ±A).gºŠO ’9CI¬ÖÉ9X#nSî* zUgi ã;ÙXÑ©?ÝÅ*ÚG»?œÇ±e À+Î9CïL_Rð„AnM½ñ~v4> —aºŒpW`åӈȺh¶Ÿ=C9-ýVÞšêžöÄðç#kŠ ;H%rµª:»2FPN$ …k7RaƒxàV¾ÐòØSóaîöEx{¢'“­Ò¼zWò[gõ_wAæ£2 +YE-¯@E7…¹ž~ (ipLDÓýª +<ößÀ1O³téÇÂÉã ÷Åd}äxË!g5––q8¸ž2úç0àPˆÀW’( 93ŒªJ\úôåt¬²»qòÎQíÏàÏMQ»*ð{8Y”Ò0ÎÅV÷Œê*xé¢3M…òfÌ2së춇z3ãhQ’q‚ÔHHï¡hKêHpö%ë„€êd¦ß~9Èk^¾À©%ûcð€PXáw£4œ-ÍCæfµbbc¶ ü¢ ÒªJ4y¥oÑÍL”?2§cG)vÉR¤¥xFK&! TýwÅd$Ùûub ø¡þ²KŒ +e%¦„C2(‚×.Š"èÐÖØr¾4 - +ñ›é”Ά›l¡#Í%¶B³<Qô„V á.+°­bY¸JT|`:ˆw¶ +ª°S¸åp1¶³L%ÏÌD5B®“&KR€Õ~Ê@ý‚óZÈ f© 6fÕ#«©d“º‡èÍðäWu'P…!)L+ +AVÑ3 YœÏ™ä'¶Ž1´7Ðb½rãhPBåÎf¡³å ;„0å¾#ö"Nš,_/±]ªdeDÁU`K´!E– iølaÓ`µ”>Ëö +JÎÀ«w­—ª^]ô“ŽJNŒÃÍZ7)8¹$ò›=è¯ôYòÆò>$Ñ'§yZvIÕé–ì˜3ð<¸t'jÌp<á}Ê­ ln‘ÎI¼íˆ6‚Ôm·†ip\©×òÔõ°Ã†ãº¡R„­?âûÎN~V¦úzB5 O"0@ù uIGSTvâ5úÖÏ0!E“‡Î>hìMIѪ2¸&µÝJd”HÆrtŸQäZ2°pñ*T3SUØ{9³²¾eê‘8÷£¬}xǹà1Ê ‚,ÈLÖ…jÃ1òÚù¥¾§- Ña¾“õÉ…S éOÜ»\ì¸NY"ñŒ½c™n…,`¢°0å‘?\L`.ÎzÓ0(ã,ÕR¤dÒÏú˜'IùËt B•tÕÛfk&6û"¨ÌÔÙ¶#aàfVoDÃÖDEMUgåå®(0ÔdµØ8™·ÌŽ–í›èå„S¦¨–‰œpÕ2bhâ*æ”ÀÇNà*Ô½“c ƒàŒðe‹ÜÏšË`˜ƒú€dF¾ŽE0l õR“™¤Uœ9;E힤K¼ß$`O%zF† +—f59: 8µFÚ3*¥¸­f¹6Ä™AÚæ$ÈBDÃŽíÕ…õ1_<ò˜Ý.ý˜Eù«Bø`oFu- +“ôÑjkš- ᄪº÷øJèEø7*3. #¨ÙŠm«tù„Iã ÎÌgH B8˜†€ñ3žñ8º¤õÿU†>R€¢¡FšóÝ’éé£ô´å¾ÒôôÂdà¤LOò‡j$ÈndÉ +»bkÑñ€WŽ?V•âç}Fz1ªéðùêã”QN‡är2ßÑI©êÆôQ)zÈãtÌÇÅi“ZZ%À“&mmÊ6Ö—ªªŽ\µ| +¤³æIÙõ€eB~m¶YGO¨ªBâQ)„X€Ã(š'CL\8÷½2yͪ¢ò„‚ žž¨¼A.ÏL/‘À›kKÚd0ÔÁTl,jHdJ¶÷&š‹³v8nJBË` Ò=£V–eE ËBY)0ð5bDpSc8ø·à—c +ò@÷ÁÞª²•}dïšb•Œ=/_•¢áQ`HÝ)K%Ö×ÍI^KmLx‘zNV$ÂÃ…=à@Êöð…Ï-Tü’ÁçadLJ´B’±U§¬†Ý(ù·ö) Ø(³A¢lÆÂ–IsyÂClQî˜-Ç'¸§±æ)˜¦ƒªï°­gaíT®k|BÈ2ábãÉ’•–Åp2Jo'ì 1è]L¦0a^3ClÓfNýT ®ùò7Ó1'ÉM8+ ȾIY¤½Â©•Ãkê _¶Üø•ê2På„'î½3ZìŠ\–¦¤óZ91©Àíž¼|6¸É\pSHï­ +‡DkòI *aq¶¢¯²bD +®Ø)XåICEhKU ®R]¢7¤¨ï%·-ztÖ£Ìr5[Ài±‰ØI”9Ä$‡¼`’p•û šbŽ#±0»Í‚¡É(9’ÐTª2Ý‚Šü9‹•#È7SZ^ªÌ®›8q‘¦ YŠ“S) Âñj˜ø‚DäªBth„ˆ¨ÐëÂLÊ~œ˜Z‹ü˜C;1þÇ3Ç ’ e§Aü°÷ïd%íH…Y$psørú=x&WÍŒð%<ƒê.¸bRz ;b/G$~W0„+˜ŽžtdâL†c ®Än#sŸ£Ögœ©[Ž…‰LÔK^Îz8yà‚Ç e¸‹é ñˆŒð,ÏòŒ€—f¥úB6@¦cê­*L2³U HQ¦å‡væ ŽÒéN#gÅÁ$£® |@bI-‹‡À‡`‹elÁ.ø!híÙÈÿ"oÚ÷6'jûxöVÄÌÌö ÷›Þ_®(KBÝ1ò +²\ÑŒ*w*Rgòõ"Ëà韘†ŒR<Ë[rÛ¢ð`âaÆ;¥ÈLyä)¹ŒûÑ¿V&ã“0‹ñ;xµIÊÃÁ[.W w(9øno°òÀÜ‚w›(¼@€sðR0 µ9ðxËÕpAŠ L]a÷!i +G£BïdE˲¢`&H@oÇ{=w3”ð>!ÛÁ,r½ýгÆr€À_ãåàsÜ¡Šb>vêDZ‚·;rÄ£P1?Ï‚S/O&~#_ݘáw«´}¡i&¡s±GÙ,æÀ„ÂÅ“›+fÒ’ )táýB +[R¹Ô}–W³’ÀK(=ˆòSÌʾ_& +² 5—p +:£¹öŒ§FbÁ\q³9†Oݰ£ŸÀô–-rÓDë:ÈÓÖ•Š,v,]¦±Œ¼B— T(6¼0À×zÇÊD¹¬VòÑ-[¯Zª+3¦ Ô…Õa"@h çè–fI{P Z·n^žZÜC«|̤ŒV@(à!…E\AG>báÙéãèBXî‰ê˜N? ”+´¤SÕé,­(—ÖïrÂØÙ£Â†¾Îœ,¹ +R+ÊË 67ˆ&Àw9N×`U¢HEV½¬Í/ÂiPc«²?£ÓS ì‚©ŽÈÁ#þ×cFþv‡®RúéšÎ¤cÔ‘@®\Ñ,Ó•3[¼AO×f4΄‹Ïædn-‡ `­Ye†H• +£yLJ%”ïáëø¹¡à!iv&49í¦²CÙg'qÖÙf°hä]ÏA'´òb¡MEø±f¹zÙï8|¿0­75åŠf}@>[S¬lPè¾õ×äg&fT6w—ÏÉC²0¼oâë]‹E¥>6.ar]ƒKi4˲2Û §ïÔ`sh`Îæù<Ñ}ô@n&>Ó83 Äíß'‡ºu›csTzÓg»È['äæ„¥L}´÷c¿æZgdï²$&b·ã‡É³Ô³NíÓ·|ðïãDÿç‡;|æ—ÿ ߘŸ’ŽÇŽÝË„¿3áG^0üèfÆ–'Ö·g¼'òu+9y Ò- +`›û^ýÄÙËsé/oÏÞ¾‘ ?ö—gBpš¢€hC?q$þ´¯¸÷ÍÞÞÌò½¹=xû:^0ü¸¾9Eø‘ñ*RXœS‘…5€z¾ÆŸ{¹J|y{ööJJ_ì/*Þ€Æur¬*¬¤Z¾gþ½/öòàúËÛ³·o nûãúrÇ–òbÇ×ñ‡å#Âô=Óžìånå¶°rÛðFW·?ö—/†w “…Åp+œí´œ5I¥ºPï}yn¬ÞßíVf^X6?õ7‡åü*œáˆô5üÒ¦û_Xô•ÅVëïHãý]‹‰—9J—&À·²=y¿çm³Þæúð:Kõw¸²ù©¿n±š38xZ8C¢Îéâ÷½­êm¾/¤ëƒ[ßâëæ§ö>ŠÈ'Ø-3‹ø€šÑ˜†=ÓÑŠŒøÏîËÓG|„Oÿ™?^ÚEå5¸`ø±:{Õç?Q4þ¿`øÝŸ^÷^ +èMhÇo¦½Î(F…oèJDòAdFI°ÞHŠLg³ˆ`·Bí<³[Y(bnwz#0ìJª 2’ݨþÛëˆ~0·ëmÚW´;YǯõöN]dðGëï–Z)ìYãØå¢’Úàèd¾~îŸ(CDf؈”E¢wfT×iY‰* âgG½“RœM=jíCžcqAl×)ƒ+ã´ ˜ý=©­áÍu¬oç+?'0zˆQ¹Øo¥‹˜D³ôÊs¬¦ÒH³æ‘0ííVÍ`.c[&Y“Äè…#ŠñUìVoν.(mÊ•¬ãˆïV +oŒn³¹Œ+Éç3&룕œà+‚š€¨㳕åÄ$bN›zà­¤(£ÎníDò¿= QqÔfTû µ7šcãéÛ¼îôÙÛd44ñÜbŠ‚=Ÿ$»Uñïá:Tx½¢ Ж¢xT9ã´Æ@0Hl:;·f>\*Ü É›+"=ÊÄÔv"Ó¨HòÕ¾#§l¤4÷%Íôãƒh@3ÁB+˜¤©ï°Ô9?úýë&3­g,w–Mœ%BVlüJ!‰Î†™(û”©ôIêD¢UØÃÚô&_Û;£­ŒŸW1ÄV\™èÚFºÕu™¦æ½ ÁËÚ&©ÚüÊîç”g{ÚÛÞUöÎå´k[†ívf¦Wÿê'b,:ãˉIÔ¸ –›ë’­ŒKÉSÙ Õ +D´¨³ñ¯²)æ–ϱ!1Ðo·1)„¬‡ÉAŰôÒØ6oíŸkñÂd,ÅL&Yk²‘¤ô=âæ2àdx½4w¹Íä"@Sç|eåñŒ+M–ö4¦¢g²šY+NK¦VV4×9PKêr’eÚ2È[wÅDMÂGU?Z‡\0üXêƒ(&¯ü oZ1‰v@Ñ—]SL´¨ƒ#üÒˆÌû±s‚j*m»¹’¯«`£Ü16yR‚õ\3ÜEê>e uIêJ´O‘ks3½zE0¨S°þ«=OöøÉMò(—ã*u‚á·Í·CS€W M&ˆ"ÁæJPÔ§@›„#)éb.M±àÚHMÔ ÛψmìÄ@Eö¡¯!–mHÌ×jD}°úã!?­)dJ¾©~È4”óà”tÒd+];™VʰШ\’Øí¤ÒÖŠgCñÊ,jw²à—Ý@g×VÁžeøÁpΩ1W]IU‘³½U™…›ç«©-¿ÃÍ«n¤ÔC‘¡è·\#ÂÛlIãØWbNM=°b΋^ém¥&ß§[©­›e±½6.óß:©qoå­)©“DŒBñb“ζâŠtg™±¾µÜ)ævÎ%cªÈMÖs1‹ùõ%I&ë ª>’¢ —$»Õr¡Æë¼Û{–o}UYôߵϺ÷iÊJâ² €ù} MûL&ÆéÝT×¹‹VŸ¸O !oV#*N 5‹]¶Í5 gê, ó/®;Îr‚7$?(¯ØØ.Zôg`N¼Ô¹=nêÂÀë±5š]wD -l6ST~ûv;Å•k×›û6éoPÍÂfW£>ÀúÓ®šŽúZ›ˆ e}§e›p5ídR´èÙ­&{ØLMÚ,Ÿ\ãfŠVA»·öBâÚÈ`B¢•"KÿF©ÞŠzÑßjœ7GDl‘©~’ô)_›‘ÔO¥‘h‡WT:ÐxÂE!—mOBØX1mNLCSŽÕ¨2Öí™ÜwÎx&G%¡lo–é=ûØ_¾ÉëbGkÚÜ´ˆ(kuÔ5¢åRî «½|Ú5Âe/¡,T%ô? _»ã] Ì¥k`­·!¢\©«]ÃË¡¾ò'¼yï”2H'…¸®F­Eb +;H®©äJÅi8ÐT…Ìß~ð©ã)Hù€2hg¨$ûÍÔð¹y)¶îJ±Sö®Œìl$¦~s¤®AûuûÅ<^×øÙWŠÄŠáLçÙûË¿8š-5Œk ¹aO®ÄÜîlks}ÞLyJÿ¸4vΛĶ*a%Çj£]ni9£MèM-ùùH™}¢€w(9B•¤&jx¼Pò*ͨ fËÅDŠ“”&¼! gÞJ4Û¾e¹Õ­93)­ßéíN¦šÔ³“›U"ŸyÇ®gÁo¦„D½õì‘WÇ'†ÉòWù+Èg~Þʪà0š`V%Û¤þJ&e%öÉ#"ævŠåØ[ +¦pŒ &Uf³¨ÑMÃ:wažÙÄYθ©©sc‰~a³<÷‰,!^Ýü1ýó6ŽAÀýЮªÍ'øñaJFÒhtb—D짤…hN +ŠçµFß6ovA[~¨ø.ì1T³ž7¼Óªz÷ˆtWmH,©ík-ÑÃmŒqœŒEؘU;¤aNVbŸ»ö°u‚û;Ç…èßÖ–K)T›%UÖ†›†±Ü$¸fˆ§DhÒ-q•c{[W* »Ñ@%£á„÷,#Ä"“$Ÿ‰4¨‡¬TÔ!Ùb5}PütMXëØÝH¼Õ·4uð ùÖ5i¼¹‰MQõVÝÏ/i+쟰¾ØÙíÔiŠê@é^¡f¾3O°“ꮽ±;"æuÎ$xó×y•C3‹^N=oÆa#Ù†9^ o †æÁ¡ˆI +õ€¨Èt#ŠÄšt +ãdo–Bµãðs›ŒjŽQo•⬸Êû”¾Ò±k¼Jd´oÛb”…¤î×5àJÍÿëM뛻طƒ~ÞDk¦.{ÖpŠÈ‡{ƒÕÁ/eÀYMë–4ñZ˜‡xM'ñš•Ø\R,¡»­ÜlãÝ[®IÙ5^ãVáÖl/•Í7’Í[ep}³ø¾§щ SS”kÜSQ -`¬f$‹Š„U'X6(ûÉvkÙZ:ûHj™F‚Oj¤f¼5a¾^˜ÿ½§ endstream endobj 381 0 obj <>stream + ü‚o ý­M‰Y?NùgB¶!°è©ÕÒ5NŠÀ(ö¦OÅ›ûÄ®}ô1ýt½—íøí!› Ú8qAÏÀküÙB6#ià¨N\9OBtßl\±ÏÇÂ'Üð{é|Üw…r"6;*š!°ÙR½×<„lÖ­Òc6¬ÂÛîíhšúpv›;ãj1›Fc6ÖT;%[ ¢ o´=6j¢†Ë51Öðë…”´ÝP-ƒf³øÑ 66Â8®çÚHVí Þ£ ÇÇ33œ÷Ï +«¥Ž”>çuˆÙ¬¤1hщÝ{çÚù´ÆlÓ>7G"ªÖNmõÒµ6¯ÑVis6÷­S7yh´¹9X'ÖÍñ_Jn‡óÒA%v£Nim«tã`3þ}5f ÚLóliw†“¹=“áÇg²tžáM–õ´9¡ûîOèdU››z”€$ÈÉ­²,ygP*’!þ­šG2÷ìÞø·ºÌ§[‡˜óÃz^þ«‚ C¼¡ñ ®–d è-‚¼réH\)Ø$ÃÄ3¹y)S*›C¤}œp¢5“]ƒ?ÄMÃÉ×uð2\×uú)ìÝ< Wr|EßCã§vøä`étÃÀÒ&ùµ[~pØšDÜΤ1ר#ó˜ü:Õ¼·‚•8nfs.lvóJ£ïÐhÕØ–œÙ¬A5‡Û#Òш$©é ÷|‹sJ³uäêFx°ëz‘‡ 8v hêÕJJkþÍJœ[^«LúB+³tOñe óVAVpo£F Yq³Ã×IÍAî½e­6bâ‘5ìú`Gôf×Cªv}°Óƒmi+©OÊHì ¥r¼aŠ[‰Óf)‚¹5‡%k>aaêJ‘Ô7ÔO6CN§9ì6|Ò˜í‰ÌuÞ¼X*Åæó< ç½ax“ûëh׆‘}NVÒ0wCcɶgMo*‚c³—6¹wª÷eoeûÖõhÜC)˜°aßP¨·DÀVôB´_ÛÂü¼©‹Ù2µQ(„Ò|Ì?¤>'#± òö°6ÁÃ;×u¾­­—`«ê›2²Ó0ú‘%}.QõîGY¶9ݺ›È'¬ãŸÎ(û^-zÀ±±fUÙSŽÚ>x +z@vIXK¡ÃZ +íÖúçáÇð0ÕØ¯ü •¶âÓaÚŠOi+>¤­øtGÚŠOûi+>¤­øtGÚÊöº©}Û6mŧ;ÒV|:H[ñé mŧ;ÒV|ÚO[ñi?mW]Ó@”¶â×Âì–ŽâÓAÚŠOw¤­øt¶âÓ~Ú +Ë!Ù\^ÒAÚŠOi+>Ý‘¶²¹.Ûäî§­0o"í¥­øt¶âÓAÚŠOw¤­øt¶âÓAÚŠOw¤­øt¶âÓAÚŠOw¤­l®SÚŠOi+>Ý‘¶âÓAÚŠOi+>Þ‘¶âã~ÚŠûi+>Þ‘¶âãAÚŠi+Ô¼1u{YK[ññ mŕٖ¶âãAÚŠi+>Þ‘¶Ò½\b$òV¤­@O-=)‰)>Þ‘¶âãAÚŠi+>Þ‘¶âãAÚŠi+>Ü‘¶²¹N‘NÒV6cmi+>¤­øx¶âãi+>í§­øt¶âÓi+>¤­øt¶âÓi+>¤­øt¶âÓi+>¤­øt¶âÓi+ÎúZéøx5„¹·VúF4Ã,´g¥Ó–Êû”+Ý·:ýÍÃö­tï°ÒAܳÒÉ[+½‘6Vz#n¬ôÍͲÒ7¯•¾÷)Ö¸%XéÃÀÒ¡•¾ÎÓð°=+}3çÍJÖ¦Yé›´6ñÐJo´Ë-l3ZéÍ´±Ò÷‰¥{VºOVzsQm¬t÷¬t\{V:½`ûV:ˆ{V:7äÖJéÀJ§þ¶µÒ¹«¶Vz#m'åÐJß'êìIV:_»µÒùyûV:‡±µÒ9Ø­•¾™”‘¸g¥SܬôÍR4+}X²f¥ Û¬ôF¬ô O˜•iu`¥ï]èZ?›C+}óbÓFÒ•¾7 Zé›ÑÊúÞÌÉJ:°Ò9Ç[++±µÒ›U²±Ò7+Û‡°o¥ï1”¬ô ó4;uXLÝÝ·Ò‡×6‹™Ÿ·g¥÷Slµ¾~IVú0wëÃö­ôÍ: ß¶o¥«ê×Ã÷ÐJßc äM³Ý'XéÖ­ô½Å[ï§æú¢+çÀ¥ÁÁˆõÈ]H› XWkÝ­ø nÅ0AF›ù!lõWü€7ŸÊ p` $wK=©“áã†ý-͈Ð-õ¤†k$uK=™Lt¡YêÉ:†¹…úRCj&©ÝÉŒ·Íu +î»Ð,õdŠqûŒ¦·¥`3K=µ¾T¡[ê#©[ê1¦~§°›¥n$;luK½] š¥’iU®¤ºNÑJ4K# Áš¥ž •Å…ÁRoÁ @¶›¥nMIêQždX uuÃÛ»Lí‹\è–z²\ ª[ê êÎf¨wWJè†zjmÂ`¨§ÖQ,tC½…ý\è†z²6HX‚n¨'«,‘ʃ—mÆ›ž[áp õ ¯Ü +_!±Ÿužá;ºž›L ÝÌÍVŒy3;½“ü`§Äf§ç^Â× õ¬R>@‹w;= ¤Äf§¯¡în€gËw~¥Uk¯áº$Ü9çGƒd/HÝNOj^Ibl!tÁ¥ñ;‚­i™ôÇÁPOf‡Ñ¿íÉVÕÛ õd5ÕDQŸW_F²GÍRGbâðÝoý-©3¾²@†ëÚ™Ó-uÄ@K›à?×:ûn¨'õÈ!)Pº¡>›¡ŽgÙìšž¬ gwµ] Þw;==ÑEn€IRi0Ò“õ?Bs3Ò:·GMÝÛS4ǃŽEVúF·Ñ“¡y8×mïÖt«‘Œÿl ש۞[!E²¹¡6ãD—ãf£ç'3Ïê…Ôlô´ÚèšÞðáÙÆ°Ó›Ú$íÎmw7=·åÝöÃj£'ua¯CÊ…õÒÌ]d ÙˆÇÐÔ¹Þu†n£'Kv¡ÛèûGéÆF–@âü&eP±w6)ƒ“&; t–¬Á­n›-·îÝ&àI7"yUUoaÀÖˆÍ衺¸¹9Sv# ˜ðSÝÌʦG°~â¬UÐ+iH_]‰ L=úÜ€&÷ Û"åÙ+ù™8.o’~…³¶ [(°bü7"©UÛòMxD9ØÜ'•M*®}žâ Ø›…gÖîàhÙX«a¥Œfe#®P`vø H`ìKÒ¦ôn„ê2oôj“MÖ zѹ-|Û17ÇÖicÄ/rmŽÆ¬AÕ¸qIe(nÄeê¤1kp%¶¬AÙhÎYƒÊ,w~“5蚤]³§UÂu³Þœß8[ªqÑfõ«mÅ1i°÷ó“ÛY2àùj럺¶ë#Û‘Ø1¼ØBH¤0QSn·Åë}¸®Ø2®O,¥Û"‚u%fý:EÔÜ Ö2 +7`sSºÆY<ˆÛC€JþÖ•¾¦Žˆ`&)¶`Ͷ"f¹1¨ª¶¤¥:qe½fc Ú$þ†‘ÕwrÃð¹3rßòìnöTÏmR]²…\S×­ÒS]m~HlL=øØ›‘2¤ vÒ˜2¸›r׿zHtv|n”Qvh·"€a»¥æ:.µÝª®z›õOj.·•Çi=Þ6ÄbÜš4ètä¬Ç@Ðö¸HÖ¥~=Uú´‡!ip%IsØ“]š¶^’v·=ñ%ÎNËž48ÉH]YTºq=l)íž-,˜ª%‡›[¿âA ˆÖ¿w«-'m´ +™£ê! íÍð÷•™MΠ_Õäð*xÁ:ÏÀ1ÞÚªn8 IÌ„nÓX+eˆFtâè[£ý¥k`ø¸*ð–)<ìHoh¤›ÝÜ¢›íÜâ›ãÕ®ÌPÛÄͧ„Ö¨ÛÑW›pèшN5„•؉þ°UÛpM”›¦Ò•U7o4œ©c¦ 7»y Žñ…©ßnAg¬»!ê  ƒûØ Á_K«ž¸ëW÷±}aðwwÑê?^Iƒÿx%έ¶Rð^¥6‹hOùVîFIV ë*-À§==¼ÏÊè*®Íñ³!zsìd!ß7ÛpÜùè§T·;¿yn9ÚæA^Iƒy%öͧ^bÃ{…¸6KáÕ~\2o1ÒaaŒW´=÷xB «â~MŒÈF€ÄbÞÌõ½j¾°ù:;¶£H±L}°BGßLÉJ¦n%vy$H&.ÄÜžÖm¸€šæ÷¶a]~ÁGmʳëÎ﨎dØáþÀv¢õ—N®yd{8屇ծ UÉY„w`¦‘Ôgd$ÚÌ­[§·¿s\†Æ}±V5 /©“;8l‚}ä#?t•q F ÄQ’m·ŒÈµ¨éQ^ý€Ò:D +üS,© Ëy Š3Ät6â‹XQ*;H% €ûe1€½_? \æë}Ç›Ï!”eÌÞ`s™È—Û©ÝÚ‚Õ©¢âX€-ۋ€͠À«Ý…`š{A¼'š íqæë¦HYÔÅ`è7ω›rÏÄ“‡v‚ÁPÖ8¤»Z.iè¡ :G™±¦Ö†Ö´7ú¤:µð¹´ÿÒþkTÔ7Ñnpk5èÄΞ-V?}cí©—FÙÎC#Öž¶N« ö§fÈí:©wãv=U$Ûimh ìóCÖðî èÚÕåxû>‘ÍFÔGÊ· Ó;E°aøÒš§ã–p}a˜/AúíQ(êÇ @`lilš?ÈfŒjÙ!CÖ;ƒhÂ\™ÀQ· •ÓHM'c5bÛJäÀÍͦòñØp4qçÁ™“ô<×>µ?òØæ­“Æáwâš$"¶áAÐÐäçAСæé›•ñ¬#Û¬ŸéZF:%ÌÞâ7±3Wë-H¦×xi°:±}¢uåÞkyóEð$åyóÝÁjJ1>Ø$Íã&ŀޮy¦ä•y’lÖFÒ8©¨4'=ÍV×uop5ÎXW24…s]qxmÅ_X›£ Oõ“|ä©ÐÒmÇ{-`eÛЂ±#wC¿Ö4åòCgðºUVÊ:ø•¶*ùÒÔÑß¡5±‰Éù=†êPâãÂ$›µaù”m¾a¨Ð q· ¥B^`cû°¢³ +¨ÞÜÑœâ¢þí¥§¬ÍÒ­Ô áq{€Ð‘aZ+]õ‰ç"ÆÛ0Tèçß +̶@[Ù„>Ê[Ì—Í¥–,';R§±Ç–:~@¹s‹`ó ³<5­Ó†“c¥öÔ1½žCæEÜ;±B߬ëÙÖ vÆ#0Xvüö…^óþ”é°½]Á ¼&ö>r™‰9öDå.ðtíÇ£Ÿ ('“A„IK3Í—Ý]ö'¢Ø·Œ3«J׉mƧnçªxxâ¶ +«ØÉúœ”…²/·¶ÎÆ›3iÁ“CQÙînr×ÔššT¦Çq"ŽnW´˜h5±RL[á +YºÛ÷¤«1ZӠ߈ +vÚÅݯY{XëŠÌŸÙbñ‹G$’Ô“nØ9Öþ2´›]þÖ[É~¡§¸Æn´‹ +Óÿ>¼pó¾áu›·m^¶÷®í«ößÔZOÞ3Ns~ãÖR—†ÖÉpç[¼gNiû“]ğΨØ7ý€jRÁ.ׯÚIJÕè—?…öÌ0„è†ûÝîà= ¶_à©yýjmºbk¡'šo±Œ«Ë ‘6Ò^áß’Oz½H©í¼yͽÞÉ¢.ׯ6Rþ¼^Þjñ'sôn÷ØH:ß{Ùn+Ëü&Ì¿4vù'‰Ê%:c´·] C§xZ§xù3ení+{õhËÛÕøiËi[6ì ËF–ØòËÀI[Û2`ÿ6°Qç|h^G8ðІ…†)Ù®ßvÒ‡eß2Ä–[ú¤â²qå¶Ë:,ø–¶|Ò¿í +‡¿Ú…|S²à/„!^cûB®?h˜Ñ©ÏèvëÛ²ç®ñà×Y:ÿxóØ.Ïh¿ŒÇÊz~Ãöt;[Fæéü4 O·_Æf¸ÀíöYVOï§Ì87÷_» lZ–ÉBÒø÷¸Œ¸aŒ¾ìuüܺ~n_þvW³äãdLÆ(2xûÍa|zXŸÞ_ß/ˆ›ÄÍd¼I}ãÈ õÍ«$ŒöºšÊÁè÷WÁ‡Í*øÁrZ»õ)¶:àº-Wp톩G¦ÑÉ7°Ù¿mùñ€_7ì<~o;ÇïÏ›IØrâ>#ngp³ú̱ežæÚðÞ¸:Ôúº°Å–mØjÃuã÷¾a}ço•Þ¤Jô—ËJ¯©)íxoÒ»ìÙ=ÿúèï¾úèã›ÛŸ]œáÙ§7ßíþ÷Br»~y}»ûàã_ºðÙéííùÍõÏÿüüÙÍío¾{~þáîïuÉ'Ïž]n¯¹>ýúòü//žž¿ÐUéðAãX†ÇÕ"¡Ëèܤ|…R8}~~zù«ÓÛ›‹?/—nžV¿¸¼8;ÿâìôòâú›_Ü\<ýçóïôÄXþ÷¨î>øp÷å¿=ú»å–髟_?ý⻫¯Ÿ]â÷„_¿ú—gןÝ\\ß.w?~,ò'Ë^xô/Ïñ—Yùìòåòÿ¿þú¿ÎÏn}ðñÓg_Ÿï>¹yùâÛݯN¯O¿9¿Ùýúæéù͇ßÿ·þxrzyyñÍÍéóo/ÎìÊß,³úÑÎïžß>Ùýú§Ëχ—~¸{¼}ïõw\ÊGüÓåéí^xýçÏ^^?Ý^ËGÜqC¹ï†Ãk5—ïX˜ò××Ë"»yôÉ·§—Ø=Þ}qvsñõÂkËÕ|Ä÷ÜòO—ÏnN/w_Üž_µg?ÕßûU|.½¸üúü¦Ïˆ±ØÏüÙùÙ3pð—å}†]òD<²0Õ†CÞ0§íúåÂKöÙËOÓØ×ÉþñY!½üvÿò«On–wyÎ{?½øzÙì_|±\8>øê„Óðòæçok0÷pÅ2‘#ð+Xÿ…W™~溸âŸïûÚ×zùº÷øjò®ÄþZ÷ ¯ {CË?mÀÃ?oü½šj°_›všÙÊÿ>ìKû㥎/|œnÕ$»xoÙ›ä²üóŠ“ü&wÀ]"kù®}1G¦üh÷¯××§WçOwá£]û§í‚vœ0׸³M ýíí^s6ïùÈA°î}Ÿ.[îØÅiýT|Ñ}ŸiçÀÎÅ‚^; ¿­%¸OÊ/c:ó6Î9ÿS»mç>Ú#øaaž úm}"ù£ÖaùýÖëÍ2`Õ_–uüâö»Ëó>úçëgºæ/‹¾÷ÁÇ×Ï®]k™òþeYÀE}úèã3 »]ñÑɳ«çØ3ËQ³ ÷,,zq½Ó¢~(F´Kþ~QÆ?úíÅ‹‹…}ñÀÃ'|q{zö‡WxÂ'§/.ÎÆÛožýáüÇßïù‡Ë_ߨËG­wú³ÛϱàO^þ¨ËlvÒÊñh]®/¿[žÿ—þk§lå¤0îîßÿsÚ=].üòóG¸ö˧ˋοû‡Ý£ÝÃC?ü¡/ð¦;òÅ¿]<½ýö‹g/oÎx—ëøøéõÄõ9¿=¿y±üħ|ss +…~¸~•­ûí'üK~ u^ÿúxÅÉùõ2™çOù /øÄí³‡»}®qøµÖïx)ûÛÓ›  |ìo~{zùò|}ørÅ>þå'ç§/o/~ÿòRC|ñÙéÍéÕ ŒÔtüØqþvÿðh÷ÑòÇÛ;˜ääÙõÓ—·?†?¾ç)à›W`Ôïe+êOÁ½gz¾gŒŸ\ž_?}SƒäÃ^y”ëðÖûïÈ£~þçó³—øþ÷Þ#ØüQ°½®`[ÄZqï³`›§y9ÉÕ{ä\ö£œK>Ô×—sÓæY5u£ÔKþ(õŽRïK½p”z¯+õRˆÆ{,õj¼KÆ /s+óOs=AFÁVî’­GÁvloW°Å£`{]ÁæK +ï±\#.Õr-Maø5'—~‚\«~Þʵ£™z”kï^®¥£\{=¹†Ú´~›¤ÎÝ +[eQDqÉë ¶”ö,ÑplGÁöÎ[> +¶×lMßç¸BZà]rÍMsÚ¢Ãu¯,×bÍy#׎úÚQ¬½{±VŽbíuÅZJ%½ÏúZŽ£"5Õ{¢§nÊcÀs¼ëÕƒ +S£ +åÎ£Ì w Ú£Ð; +½·+ôæ£Ð{]#uÊeŽï±ÐK%lSCü½Î¸Maþ B/¸1Ħ0ºù*`IŽBï(ô޵ЫG¡÷Ú,á™Þg¡7d¼m3Fò(åJH?Á‚õSÞzæÜQ›; +¶w.Øæé(Ø^7”æð^§üFWóFœÝ“âøëæ®WWçÜFs%nÕ¹ù.…ò(õŽRïíJ½c¡Ãëªsï±¼K1Œ3ï~¾ŠH«éÎŒâ£Ð: +­·+´ŽE uBkJˆá.;ôÕååÕQ^½kyu,?xmŸÙSyŸ££5å{ÊʼIÔ¯{eIææy ˆÖ±‚ë(ØŽ‚í] ¶cùÁë€;õþʵ”Ò(¾rœ7Zz3et[ þ˜¥{”kï^®Ë^?­¤ò>×UåGÁVf¿‘kC™çO‘kÓ¼ñö£ ÍQ®åÚ»–kÇêƒ×–kèBûþºÏ&u*ÒÇÜýI²¯äC+wV5E×Qt½]Ñu¬0x}Sízß_ɕʴI‰èPþFT²iƒàâ1ã(×Þ½\;¼vÚÙäæ÷ÙÒŒ5rmF™×˜î-Ý|¥tÚ½ÐÀ1Gã(×Þ¹v¬xm}­Vd¨¾¿‚-M~ÔÐ +šõ +›»³BôÕcž9l¨óÑ= +¶w.Øê±N൛O%ç÷X°•šÆpdò—šså^“ñ•,Ñyó–zÈá(׎rí­Éµc%Àë˵èßkyŠcý¦÷ñhh5çcºÆQt½{Ñu¬ø««XÔ­;ÓjBíÑß”Uï^Vk^4(Åò~#ÛÎîl4·íDP^_ˆù´É­s¼KNÅÚQ¬½]±v¬x}w|¿Ñ3’‹÷ÛºÑñ_òÑ|uwÿ4oò3æ»ÊªŽrí(×Þ®\;VüÕ™–ç»Ûd…¹-Òlù' íK´p4@íÝK´c Àßœ¦6Ÿã%G%í(ÒþDÚ±6àoN¤-ªš“ÐâOiªrkG±ö>‰µ_^^¾¼º¸>½=ßýîüòòÙŸvQî5¹÷§dï;>~úìëóÝÉéååÅ77§Ï¿½8Û}róòÅ·»ß,l÷!—÷ž'} ›x5.~rzñ|Y‚Ï._~sq½Ürn‹ñÁá×Ë~sqû:‚»6‘´kßô-BIÂçç—§Xúß<£ ûů¿þ¯Ïîùß»Òîùí“Ý?-þp÷C£¶A,7Ÿƒ7¦ÕWï½ìgçÏ^~ñkÉã;Ž”'S¬ób£%L.%,?ÌÎÕ8c…žÔ<¡ÁÃBK±N~ǂԲœ4_~üèÎ…ÈöÖ«C›‘ô¯×gÏž.½¹¸þf÷ÁÏ.^<¿<ýN¿~8èÁ!Eù­8Í4_nZV–Œ6\ÿ9ØòúüÅ‹qš:q3Uí¯í9ûܾeó ‡¯ÛüùG) ÙÜâÍËÃ{Çò´¤'S³'û:/¬÷¤L.F/†ö5:ðq£u.xü 3ô]‹ü@rñ׿ÿý‹óÛÝg§·ß~¿,Ô…¸î^1¸yV—ß»‘á?5õêƒg¿qûá ;þ×õísüÞ" +\]^\ýÈmp”û(÷ÓÿyO¥¹?Jóï•æc±>¨4ãBëጙ_ßÍÃw4üÉ× ‹}ð¯××˺?Ý-»íéÅbmî|Ö7=®ÚS·!Üî“o–›?ùä§ÛBo—KÊ‘I~“4aùÉ·wòK˜>¤éÊ-HUü å—ô6ó;pp¼1™œ¾émŠ„´¼+ kí\v3~˜çCÚ³ûBõÖ¡÷%.\ñåé+ÏèÑmôŠn£O%q·¨†—Ðð¾ü‹r½mkïu˜?Â\;ºr_sOÊ€ùÍÍéõ‹ß?»¹ú~C­_v¯‰6<èÇY¤}/ÎN/Ï?½Xtö"óìöôöü«Ïɾã¥÷î±½ý¸}æV\¶ÒË{¹áxÈ™£â÷cdÊßž>}ö§£Â÷—¼ÿÂ|!/ð;°íî>Ø~vóìù®í†ï;Öp¡®»÷lÛ<ëGžlíóŸŸ^ÜŒ +ÞÙ‹›3†ûÑ÷ôôæã__^?5åQüñü†ZTKkY®x)]ïI‡!ùàÙóS>÷3=×’}Y ð¼Xëú!Ì© rUBM©Â‚G( ç]ʤøëO[Ðg—7›‘|ûìæ¿XPÿ­Éìß\¼xñò|÷Ùés ç(¹’û-ùïêä§yBœ#|u‹¤HŽ<Ÿ§X8ßO"üx …»š§ù½ñŸ}ññÓÓçË/_=§´þê³ëÛˆç.¼~½ˆÚ§»_½¼¼½¢»ïú4Ø—¯¥)ÿã„þGŸ\´?|üÅÉ/9§Ÿ#p¹üéïþÇòŸÿ™÷ì?~ùß'ÿ>À¯?/¿ýÃíÏô{}ü?þç“ÿïÿAæàÓÓÛSŠÍ©?»9ÿãÏnöLóo2¯Á]_˜ßáZÔå,Ús“O)G8ÀKŒµ"›¤Îs±¥¦iŠ<‘kLS×\œÈøÏq·½±ÝöÁ_ån«ÇÝöÓcu¿¸9?¿þ{˰ýûݯoN¯¿9¿;"û€Áº¿Žmöõ_í6‹¹ÍÞY.gIïÊîù÷…zºðíFÏWÏ/»ÑsƒáóŸjÔÞ“«v×°©Ÿ-"ïvËß}üËúÕϯŸ~|s˯}Jå«yvýÙ öÙõ7‹üÉù²?;½<¿½=çˆ>ûšÖûä‡P3Õ\&ï¡~×ÉåÆnüûçËÝ/no˜1öŸr.¾üïGûôGŸNÖÿöíÅí¹þöSËÂ/æ¢=kšfFükõè­ Ó!N¾R§qŽ?TÞˆšš:O°.\t®d>êäW¿ûçÝççOÛÓB‰ÎòÂŒÞ\ ®­÷HFÛms˜µ¦©L‘èË(Ò”ø¾>0Ç2qhayy,ë£(õûÔäÌD_·¼Ñ{›FÌÔŒäø¬¦Žéæ“ïNÛ½õÜÄ@ô“:Å)Vþ0-_ÃÏS¨L”¨Ë¬âO)ÏXŠþ¸O._¶UšÜ”mfu÷2Ÿ5k,ÕOÐÔô'üêô›óëEÆê!®Ìn +šC·h™ž“T–E¢jʦӌàò sûÇe!~õˆçýîë´ûçåǾæS.Nìé™=Çš Ú®„|O=ÜãéÉž¹ì*<ìwÿ8§å‰ë3CJ ï­±:Ÿ¥{Y¤â™eõò®ñ­˜»0~²—tçƒ1ï™|<•eøÄe¡&Û*m:½Æ¯GÍbóþ¬Îæ¯íYZ¹Ú!Ã:òåbìè2lÂ9X‰Œk©Rü<¦äüÓ‡\Fþ)uªäG·|=W>.Œ)a¸ìNeß=ɶJyÜ‹óÔ7O\¤•„Tª‹ªE™ªŠœÕî,Ûj‘d% +FûD><¾Y”ä&îØÅ곎†&I2·uù7?HX-·áøÄZž‹À娧EhOâ§eðœ ÉÕ¾*´œ\m¾t:ü'ayD%ú©pùÛ˜]“lAû||”ĤÇÃ|êkí2yUQ¼q9ÍSqv*qO­f~ãÍ$±4¬¹ž½Ù’.L37wMŽ·/»ÆE;ucNës°Œ&0Ó(0Ótç“C^ÄPÙ™€›%ƒçE$qÑÛ\v ,k?…õcCÚœuÙÄ%füžÏ:IϤEMIÛõêN[iœÐÎC:‹üKÎÙ!iB³m«ˆO 2€ßˆOL¥XÍ•Êþäç0›†Ìù¶g4Fð¾ÇãfFkÒôÇh‹çe„ÔMò"òÃú,¬ı¥H.ŽøÔ˜ÚØSg‚Ù³Èná†È±/»;fX¨^Ĉ"»ÿŸ»ï^O'ç=7À=dè½÷z't¡w½íûîþq®ýØž>L#ä÷ì÷Ýg³Àx$[–eI–,&Ÿ¹˜"ÅCmr^;“s=n——ì˜ 7¯ Ïqò9ìæ>LÈeH;´‚ Ð^ò‚›"° €Bóð8]Úi÷ãú Ø^}IÖ…ÿ¹™bÑØMA¥hl÷y¸„À+ì/(9Ëðzê%勛ܜ]8«ÌK‘ÂÇâ]¸ ¬ß ËêbÉ7˜´Ò(Õ„ì7¤¿ÏÅ\ˆ~‘]ð¾'€Ï– €iGÆD¦Ÿ)H·xãh=ˆ3¼Rp‘ íÂ9"à[6°(Ïå†k“.j3$WµËs>zSôТ'àÂá\¸š7 |11ðУgØvæ&á¡@#zøpȸ&3™/ÞnÇ7ÜÂp¼pZPHÓ°oÜOÀ2n—7¼Õ[{³:@ëK¸ð›twä/x‹·ûÕû˜Áo(Àp' ˜ÝÔ—&«ûab`½‡ÿÛc¼¨¦øN:9ù‚UW¿`1ââ–ÜlK‘æ–/€¿DŽ0<®t¹½@yFüIÚÔÿÑ~ü6@Au­íùä:ò8½RÀ¦ŒV†‹r´“ÙÈ\s‚ë§Xß­)´†œÐ¨‚R¬ÜA½lÈ%àÆUîG¨> +*ÐwQ×ÜÐ.E[P— ì@UD4ö:°‡Üy<\S…„ë%á)ëG}¢<àA#õ±€àã'6©´{HËÅÃ40˜p=´@ôáóíôƒî"Ùä2ÒËÖ3I¹åejÚLxô†ÈNìÜ.‡7r€†k‡>Råò‘2Ö'0ù”†íM‘ ÀátáÓ,I7¨ü¤U5$¾üN†ÔctR:†ß«A@HãF Ø[ÈärbäĘ› +›é)Gß@€fï{N—?@Ï$W2>ð×C.ÔÄe³Zßè¥úªC&B«Z\Ë÷µã+´ÏóhënüÞ—}øœ>¨1"EhÏ^\YäêoŽ‹èjÄÚ ¸eéó9pÛç$Ü'^ryBÆ!dÒÖ†Ûƒå÷¹¸A—®¾:¼nÆ0i)åe[Ð ¤ÇÇlýKÓÿ"%¢”5~tû˜Æ—œÕy¡Ïî×Ûqÿ§–úÿ&)üKöà`Sù¬üçø0xÀ[¸ç%&—ÿøº}ùÔ•ÿVéuùŸÿÁ»ñ¿¼ ®Psü_>Ë‡Ë ¬hênrÁÁþÞx)gЉ†ÿ®v¹Vß-9ªõÚÓr†E¶üwÇeqxíRcú¯¬…øïÄçB®©áü-g8ÿÛábÙ„†1=Þ€¾SZ,o•ËÔ“Õã;ÿ6$õð;}ÐMð¯ïþ`ú·»°_Ü&x¬ãký¼ØÕœð>Èá.Fcø‹ÇévúÉ«àò·Éå6=Â(ÓÙqw¼¼9ßV(rJ^Û iXz=¸·µt<Ñ’_²í”¢‰Ãëò»¨¦¤w&99ü5¹’A¦ŒåÏ;oÕË⺸üµxk.þ{{KÏ7·Ét³ÛÜ)CIàÄn±˜ÃåÙ¦ç€ tM^ާøe1ÁºõŽp¢:¿Æ‰ÁŽ9Ú€õùýž7Ÿ/@ìj(¬²…¾~ß-.LAß[‡.Í+Ɇ€CHàoŽÇ+öhLÔMÕx ¸Ãùfñœ ÔõÅ®y¬ãhQ7ªÇëŽ =u’=±Ë~ÅÁY#d_© ¶Aÿ{åÅ|sß¿Õ×ãîNÄFr‚&‘rÄUü~B· +އşÝÛc´tZ&d¦òâº~«O®·Åeó +³d`#Ô ;ëÊývºß$Þ¡®Yäé\irXÝ'«Å[õxºŸfápˆ›Ì%=âù•"Î3ûÉuËèD}ÚX9¼}=‰µî¢²JO“ùœm²ÛÐ*z<ÿ¿ßŽÔØhX|`h´¤ yÛ޳íju9’ãhºÔƒŽå)Œ;&®|d5¼ ‹ùíºÙßwšÎNF×ÐÍ#§ ¯³¿ÎÍ´&‡ )æÌ5ÍXðÍ㉽ÞÏêPId<¥Ðìî8ìê‹Ó}w}`‰êâr=- È‚NŽ H©î&‡ŠïF’§/k+³;/¯ùa§ßpp¥1†Ãæºt§®°FÌ?ùÃ|ñßÌær%^Aøün·<¤¨ó ¤ô0Ðú`ÐßåðJÁ È–\ìvéÿÞ¨­Mv/(;öÆbv<Ìh}gx‹9rx–%gÊð=èqÆd÷€8»Œû}¶ñ<ªd/:½6îS B3ÇÃíZ‡K¨lÀÞGíoKJæžÈõø×âr‚ç(„ˆA¡"r&—¦wv}´&ÀÂyA"Ëò×Þ’ôVßä0ãÙÌv›Xÿзõ_ ™¬ÀJ½’óoñØí’$®êEKñ©%DŽwí:½0NVXXdiÕˆ¶yùf&³uË0lì÷ ÁEc`¶ ÁE ñ  ¡ç‡Ñy½MPâÑåôÁb¸Âøë¤vèôâÏÁ¾t<äºCy’Àl!»*1ªÓdF©}nÆfAhPÛ¨Ý'p¶ÞJ‹¿;qö;ßnsX¼]‰JÞ}žmÃ5»ÜäÔ…<,g’O]Ê@ËÒq6ÙAí†Ù–Ù  ”+¡S9ÜV»ÕÎ0ÀZÏÜw;RI!*³€§;¶—1¼Éá¶yĘ\IeDlhåÛ.A®ÐÂ¥¦ ɬÙd´Ö8 š1–˜«¦20,i¦ú/Æ´6cÑË€6½Ÿ.æÕËq¹Ù-(É)<4“56þÞ®1K«’«‹^ˆN¾~vÖ›Ùú¡Ÿ~Ñ9` +fQaGãfs¼Mi! ЪÉpÃHnu xQŸî†ïùLåMx¨¨_̱º…Û¶7‹ÿ€el±½ÉVIà.@HâdFЦ¨ˆü¥è‚È©,AŽ(â&J>&ûe£½R¶ïãÔ:ÝÜöÈ™,sÈÁÓú´Úo­S8ÇåÒz¿.€BSCÊjyxiÃ9àK¸ËŒ3½Â8˜ÍY©‰‹’ Ø0Œ„e0?ŒæzÛYç8D¤ñ,ñ©_#ÚÓî9ïœæ{ðxwÝ©Ó\6pü´–zƒoªO§ ÑLd‚A;íVà›$ÐŽqŽ@ù[1Ýòüó möÀ¶¶îëËjx;ž${‡·¼ÐÈEúˆ·e±à4°³Íߦ¿¥.`m^ħB¡ç•·û3Í„I’ udht"À8ͦµBv¸p(%æ‹ëfuà±ã¹ ÑÚ˜žg!F£¢ÍK3j7¹âbJÖŠ€o±¦ãqÔ»‹®FñÀT„×|A¿Œu`KÊlšâ±òˆãœ¼­/s+PˆaVè£ï‰Ûp ìòz.ä¬àmu"œtb¼€°®¬¢ÌG´ùëÁ¶ä¶BwÉ’°$Ûý%>Æëì´›ý-Ì*x›Ùá*ÆÄ Í ¨€ 7”ÀøÀ<í&'i:íDúŽ6ªÅêêbR¶º’iù’œ‹šãRš^rÞ,ƒG=±Ru²›Õv¶,Ùë%ÚÌ.Ç“D“¯›Ãò(ÑìÂÈœ—B +}€ÓÉå*2lMBjwa5¾1†$Õ–±ÇÈÐènÈhLotÒmÝà[EËÃÍ:߉‹<¼Íé²<Äälv½OÉõè⛣+(enóÎâèPSmrX¬&ôñŒ@£×®l¶›ƒ8¬c† JÒ@æŸÿ«õºžÌ—…•`£Å Ëd)#Ʊ §!»•OÎÿ÷deyÈèƒN«Ëƒ"ïh¹â¶hGì‚´•ÏÇ_t;Üy"Þr·9‰6€ž +ä¨`Jt¾†ÇÓLDª W‘G æwa…4¸ÞOˆ½þïì ¤¼s.$q•ä2¿\¥÷Ôjy?ÌD8 +oC¸iH®’ØDÐ;“Ã<¢M­¤ŒˆÙž¡Zé[Ö†õ­³˜¾%“ШûúF§RíÞþrJ˜q{ ù1ÔC¾ém !FûÃùÛÞíÙþï­ˆA7<ÞÖ¤5¢Îœ–·8ÙüöÏ1Ýcúä°ºùïbÌûåbÆé]ir½‘g§ùc•’'–À¬‰çß²dšÉaNzŠsâ/Á²o8z s_bcr¼¥«§QáoIãzöLÕÁ<><éÓˆ·Íùý¡¦»]‚Œ3fD‘se¶Ÿž¤Biœ%w˜®b/ ’ÄDyñдBøGLw²]¨U“’¯ÌÔ?ܵµñ“’礄մÂ9¿aNÀY“7¼-ï˜ÝÛ‰ôÏ.³ù°ŸÐM8ƒpl‰˜“ÝŽ<9–°p¡ßˆ:Ñá—*àY"µXrñV×íæLƃˆX‚Í.@™º\pÈòœTð%r،β…Yá8Í­\@”yÑTð, 酇ϢÈÊ“¸¾.}T ‰ÖF])[%e&šK$ÑX–U%WèÃ.;¦uçê±`%v l2åþ?þJ÷¾Îh³¥Ó?ÿüƒa†q +ÃŒ:%†é J…Æï)¥+ÿÐÿÀKòœ±K-ïÔøµ÷ V;ðASƒWæù³‰Ø"‹ú£]ÃçžX{p6€:úšÏòø5„¾&\þÐ;üZÁ7¿fAøu†¾Æý5× |Ð+!ìpÜ_ý^‚O†üÚTh²:kÜ >a#e=«Ù~¸À'ó|;Ü\›6øuÔ“²Ö-WðÁâ@O3ÉÅå ¿¶àWKz=ÃKÿ,ð'àI8×Ó úŠÆ=ózöôóLr9ˆüãýòžÖéiӮ̄S÷¸ÕUz7º6K5Þ<_5™tÂè´ÖÒSu°—ZêršTñî"’!bºD|S\…­©¼«ìL©{Aû¸í¨ÿóö¤¾Ä¼ÞÂfåmZº_Î4fIØÖû¨‰œ…þES '¯µ$&ϸýÂd }êË@*ꛇ†VßïOfúyáTÒïv¯þ–(* ºÄbf°%MUƒ¯TŠÞ‡W«!{N] Uëvnø,$;†ñ+6Ñ&Àb8ïü£&u6­ÚžÊè¤ÎÆ÷œ{kÌ¹Ô cM{œ»ÇÅд¤¾q»ôŒ·ã¨g2V“Ó™B9åÔ”Û—¦sjgê”?/¦‰òª1}—ýVÓMY÷)4fýH•0;3ùsØrì™Ó×ÌÆ\™]Uæî ê2ÏÆ®´y?Ýv,ÊCsk±h¢&‹/h‰[â玥¸[-mOße÷;eËÖZ_[î«/»Õ”è•­^ítg­O‹µØÖw¬íRXm[9ë®pØÙ”WØf©×§6ÿ\å±%﹡­¼¹l_­ÂÐÑc;W‡S»^Û]-ÕÖþî™dì…[Ueo¯Rû´æòدC“×—‡ãÃ`sD†µ#?±—­£Ïé˜:’GÇ!Wm;5çMÜéŒèLÎè1½s“îóÓhÍ:¢èsžK*Ëj\^·aìJzÆ-WÕ—(¸uÌõ]ZùÝÊNŮиm·¤É û´îBפtÕ7÷¢«¾º¯.õÕcÚož@ǧôd“I§e«<3ËÊî9›>¯Ñ˜x÷úãœ7“°5¼Ízcäîô;ï9ÐÕúŒ}·×0Ì3`?Ë‹Ÿ¾vÔ¹õÍ/G£ï:‡{K¹Òñ‡s¹“¿˜L¸ý_ÑЇSŒ}T•˜3à\«„¶} +ÔÒëp`¼PƒõÆM· f”YïÙ`w±>7ÍHJ¡ ©‹»cÈÌfB©”îj¦¥Ð¼SÐ…n{'l׫<áXò{®®gùðÄß7„Ï˯qÄòÞMF¢Æ¹!ò1YÏ"£¶º9<®¨9[¼D#©é úQ4棣¯2˜ýèisÒ¾[,Éí{ôã2x¯œJÕ÷IÆœz¿œ&˜­’µÅâ>—.ÖÐk°Ø|¿=űá`wMz›xzÞ]Å;«Î2þ­›¬ºðö;¨Ý÷‰âÉ~I YUât™™š¤õÃèJÆ¥h²q»’Ëi¶RÞç)_åãš*T¬ÖÔ ³Ž§Nãr#mÓF7é„ßhH·úX<½1î»]çûš ÙÖÌÇbÛÊL+·k "YÅ?Ìæ Kv ™•³g“á’³ŠÉ\ªzÙæ>×éhnoÜ­òæf1š ßùVo–̲—‚áîü(D*S¡Ñ\ +ëö"ZÔWz×bäkØ.Ö“—›[Iï¿tK‘‘3¡Ð”º¸±´n÷¾ËÖ.G7‰d¹ÙÚ8Ë߉öaò­Öqoº÷Ññ¨>>öá~ªb-gB•ÔÐî¬ô”wsåœÞêªÎõDUÍ¿÷ïÕÑmx«aƒå½æÏ•µJ@¯QhjK“×P×Ûªöú»ëÛWoûmïõýG«Ð°-´­FÖVŸ6†é‰)û¦¦¿ùnVƒúJsu[N[ÆY[ÕJôŠÁÖW#Vk]š¡mÛ3Œ8Úåu´Ü^jÓßC¢áVh:ñÕ²Õùòk”Ë2šþôæߟ“>ô¹Ú–g]ÓXí릭iwÐsú¿°Éfù\—Þ¿šæàñkŸ6”zŽåÕØ+ºVÃÞ|8ö ‘ÖOj6_ýþô`«V¡„>‚ÓA«Ö( ŽÍoèžxUà +öµn|ÆÖÈÚj¥Gy½Ã7š}®Mcƒ;'ŽíxØ»Í&êü´?yµ;“¯Xµ1¹ç«µi¨^­NÛóvuz1Në3ÿÇ­­ÐÌWGov,æ's¯yõ=¯Íì—ù¾Ý3,Ü»gQ|%»d¨¾tåγe¥óy_n—1×ÊåtåW•†v´Ú^ØÚU;„ÖUÛ¾½Þm/—»omj­`osø(íóÛ[å¿}Óþû´ÊG¶ín¾m§â¾íõû4Ù…Ó5ÿ®{w,öÊÏ}t 7öû¡3S8èôÝ!­sô3›=r4û¬·c!çê×Óhøä´WT§jc4>MXA¡9ûÇQϹ“Ÿ(/˜Ëº¸Ä¬ƒöedôe¯Í2ð£”Qìdxîp·ã[Џ]3r9Ün¦£å¡),'á«Ð÷à¥,è”ëxyûÝ€صj*3ÂÓ`@âáFt¾X=î6äA+ïƒüø\þÓhùCüåãá8[_ŽûEêøŸžŠþà…yŸLïaP +æøº$''<Óg³ . é@ÞÃ/r”G¢¤2…D¡ºaS2æIØcCâo^&§“DC„= O3Ôi¦m(טtS|L1ßÙ4¦º³˜ÂH8ÂÇÓ\ƒ9x›\o·õ∠z».np®oÿY/o×É_W'‡7&O£õ0¹ÂŸñ¢o•6b}k]Hð— ìïãýíXìíxx[à³PãàV°¶Å†…ÈüQ¯ÀXáíƒÄlñ¶AãÉÛnò7ÌfÓXOû¹Þgkؽü!…"Zh08¶`‘;èÝqI£ß\ßî‡-¼&ß*¾RÉ™ g—ÍI"(ƒ5Quñó[šUðÃÎøE$‚l\_¬Ð=Jrû +ÎcµnŠûSky²ZäEþÉ–ÍËfÏèíó'l3S±ß¸ß‘š^©5ÈÑ-nÈ4þTò=x5ãÓbN^Nƒ„—-îaq4”Ÿƒu¶YnHO1oì%NPtXÈjÀPÿ9^¶²ÙM-"Èž8©s +ì.<ËÃØ»øôHžñ»eí+üó$cK‰Ï.ÇéäVšü½¸ˆ­ +Ò‡bzñA"úò©™}ØÈ¥w>¢qþ0âE'Ó[¿ –ä̓Xâ Näšk‚ŽÒêÓs³ÁXbÓyNˆ[‚MS5Ù}ÂJ ·MFN©Œ±CÆÜÍõ}?=L6»«ôVÁá&š`½ËÁÝ;ñ(s¸)23(„)FHe†Ã]õÏ­+Z–)©ˆ¢7ȈghJ· V,O´1^`Žïg{%1Ñ’\ͤÃÌv‹®]¸î…¥¿äÆ*(,øÅ”Œ„x`ãè¸>”¤ÌI6 rSQð›PÒ£ô©­û½¢í™Âˆ.4ù4\ÚŠ%»d÷ëàê€2˜Y¯Kn&Ö«ÚÛÊ¥½ª`¬•–ÝïÁR_WŽ]î3_&í,û5ÀÎVÙí×Ôwje¶«c¡¡Õ ›Oרµè´)4±P » +·Ä*W+ÅÂîE#¹‰Dg)«U·z@Ušw>_*£ ú¾²·Ô÷ áþ²˜ãûcéÏ7nkSÔ«ºgRnu'ñ½ÓušÔÒ^˜òSûK_»ÖëÇ›Ik[)³]p o3ƒXðjÝ›RfÍ=£ÏΗ + "Vf X2µt|ð0î3¸L¬oɵïËÁ"ÇX›š9JçXø]×Áá€._“ÃÕð>iÏð<KXüßêxâ:à}øœÌï +Mà[oš¥gžš>¹vBá¸Æ¥5%>Ìcx×Ê$wc´]P­C³Ùd ?mLéeicvØmße£7ÃÂ<±Ó¼ë,Sÿ/5´gØC,TX»o¨=ˆÅ3ÝÞ)‡l¾}?²a)ÄYªpm²ùt _ÇeŸ7IÛ̯£Ñ[Ì‹ÄÎWÝã#è–4±d>¬ê¤ÍÏÌK¾çUE}ÉãÐnÏ{AçT5@`£ PÔkTÁ)éy;ÞÚÒ)𨼂5Ûó’Ý1P•S¶IX›ÁL_ˆÅ  ÔD¡±O•y7úlŠfÂħp']Ä›'Íé1ÌÙuæë~ÚMÑhÚìL½¯"œN$š ÑLRð* 4J¨ è8ô‘:l´p£ß±”B2EîúLjçHØ ž ø“µop8Qïô 5oÊx³p»?’’3“ º“ÿyÁàoM ÇÎÖMœK§{=°Èèµ'Crí­¥§ö°1µ¸\ÌöE5¢:‚“ƒ"F)ˆÛ–q®Ô¢5•ÿöMðµO¨¿qÞã•a¢œI. ~»£8g’ó} Ož9È&vÞH‡†í½•"_‰\SáôA¡½XTRÙ­fPÕÂè¸ß¾ v޽嶛Oæ[ö¤œ¦ûƒC‘Pþz\&7×JKK¯ê4¨ò9zT¡EP»+¹n€ìU6E +_éªä˜ <Õ”ŒÙÛh¾7Æ…”mÑgpK]Îo–«X»™ŸdÒþÚ§B6ÙÓ}Ч´ùøáÌh½ÁO0ç÷@z¶=kIhì&¸`Ööt% #ºx}w22ÛÕ&9 þtn_}m´Æ‹ÝÆA¡aˆpòù(Öôi+ÉÒ¹é`mFmj× Y{@=“°5¹‡XRž)ç)1ûðù–µÓ0Þ6‚M4{L››³ b­ö§Úx]è)²©ÍáÐ0ؤ™*s–L8í1 §p,+ãoÞ,Ç4‰]Án…sÕ£×§Ïÿ¾1ºcí…Ýçù:´ìEý> þÌö¢cÇ?`9“¢þ3 +~›Ä˜¿9V -éwˆ–âÅ¢~•$_üŸ *z‡|›D€~ÃÿèOÐî½>}§ð&°1Äâ8¥™Ýƒ˜p–}Š ‚ÑyêÂvž"zƒw v@AXPWÐ0DøÕhà¿ÀШø6—DD—‘–ˆ6 ‚¨ï½3Þ.Ü*aШõþôâLRâ‡1’Šáƒ8=¿t$”aœ k¢D¦ö‰‰àL 5N,K t”hÞ‹’tb‘oGÅùqÄÌê 5—ì±È™4ü9*â)ó+À"ÄRlñ…•˜£¢Yå‘g!AbáôF” ÉÁ3lÌKTÄŒ1Ÿ2$'ó ‡ÀG`÷†ù.n/ÊB +á¤h^(ràXسŠÓBÄû(Á·TºÃ×à`2)>4ƒèù¤–Ç/ˆäŸQÄÉ`pÄ;ÔÄÓïàËp•ü"žF«’á6x+ä'4 8 |¤`M§àß.bß ÄñúrUjeñÄÙ0âÍvñ‰9[…&«ÉŽ€ZâÑ{ºÉ½¦ gF·¦1²W1Sºý™#·2‹š¡N0´®•Äl§Ð ÃW_Àlb˜fŽ0XŠ ¯¨~†ž 5å }•2q×V]@v{@&d (4þcÁîK[ÌKwªø~v3±$F‹xýÐhÅ·ü>m*Ùôì§»L, 9‘FEÝ@_8‚|¯yŽW’‰*ÔaíjÏ" °@¡Æ)CÙð…f­zÜ×/ÔS…‹qÌ îÉ òñb§ôd¢g-Í6{­,.„êWò&À¶Ý·R¶ƒUÜv· vAÚ˜Ï×,Š©eíÚ°mª=À’és×·]NeOÜ4…¦1Ds(naÆ+™Ôwg”Ü|{m¡å^³ú­Ñhû|õùvÎBëG„ÛØ hP€“ `ÿ?Y^#刦VÃ;Ž æŠŽ¢VÑ%צnÒ¶‰F‚Ä\±éIJE¶€í­Ú†9Õ"?@êRAçA çy‚92*´QœÏéPÍí€eAëÿlíŸÑÑÀvQL‚ÍM™ñ(He»¾%0ÇÓN»3ë.sÁÖ0CŸc¤à<–&ÑŽk©eË”µ/¦™”Ó0Oã+Yª³>À×0¬Ý +­+_f80ª2éÃ}ìÔö~böÃfµ­ç÷ÍJ…D­céî?ÿÞÕÄkv&e˜“hf?jí¾í}Zµ—ŠÓm#s}ί-˜—äºMÑìhDrÖÀ +fÐZLlË& ÚW—ôš BOË0±uF0úå°çû0©ãRû‹öv‹÷oc†»›\º'kÍôÞžœIM_,Tì*ù¸ÆØ°+ñFvþ æ%rqÆZXÍšˆþî\@–[¢@޽¯¯w4ɪËp@4¯éÑÉ1…Z:+ÃIæô씡å½úo¶&]€ ¸³gú1èMÎBIg®C¡ ì2Ç¥Ç8¨–Mrõ2É\½.¦GQ|ýÚÞ¸65u/’«—ѸzW€“3†ê.”Iy¾œ¦hw`|„(ÃÊg€-1Vo¢4Z#z‚y‰z ÷”µ>· ôj‹jz•™C’p 泜ÎÇMÊöŠ‘ôÔ-lëÅp éy%:—ME Ú‰xà6ïLì+Q°‹±w¢rw}Rׯi“Ñ»dìX4ßAVòªUg,Ø9oBËÜt ~f˜ p‰Ûî©Bƒvì°¹ëÍ!ØŒ½›hi×;ý6ó÷J-N•R'¼ª’[s¦ŠÍ²’FïkïJM°Ñ½«Ó#ûaZ6SK…&m>–ïqO,ÞKϪ;G]"<“+ÍÔ€Òwµ}´ãÍR .Àå±óa}ìb ÜãÕºmÜ ýK0ã#˜†zƒoGà˜’((ýVfZÛ¸HÑóLò +ƒ‰( úœ«{Ü?=­¬Ž\ÛLü¦ècMõÙ’ÑÎ#Ç´”wÒ ¨ývùÃ’ØííKÀ/Û/o4XéÆB§€ãqhD;8/ ¥Ï_;¡&Áù|z~lòeIÙ_ºLráÝÆ›©‰#±ó«üv§ù¼‰+µš0†„yà"t>™}öÀ¢I•ãõfìüȪkr)_cÁŒfâ}÷~f“Éö;¥z‰ýÅÞÌz©ü¬^M¸;ÎÛÃÄUØ<쵌¾ð^ +äú™¡“hŒVô†Šêø2µR ܾàÉšêÐÆHƒBZ¶\!Óõµf¹;œžÚ¾NlÍ©U72#•¶Æg꧇߷òA>Ò3{KOF½B,›\½x³©ÅXÌòž§À–/Á%\“_û ’Se’ñPcùO ÇR榇òâ½ lãéfܨ¯îqb™° 6ÁšJËÁܤԳˆ XIïÛX8zêÀ짃]Ëb‘Øên…„»«lå"}·NÖH 5EoíC,Ûü0?ÒGæºóÆŽp7, D:°~]*`"|hA‡ûȺÕ[¼ØuÌH#Åyj¾˜™g gz å+Û-q!|¯=Œ•J^à×ôqüN-…]jjÜh¥ñ%œ‰ïJäåð ˜}x”üHÚú©b1lÃU5g­aɤK}&¯+É íèäoÚ¼‘L<šðjNžT~²Ó<ÈŸ]Õh3¨CYjMóÊ$–ì6ß«ÝLÌ~9?L3˜Ñì&l ¾p‚•ãƒeøîg€uõܸô5}Còì*¦aŸ¾Deôù}<°î¨2©j¹‘ÑœÜ@¸t«˜+ñå¦×"¡O´‚…Îð⾆*ÿåba‹ÕÀ‹6Òf¡³¸¥]kLµ»õ‰VZ  qp‰e¿š¿Ð €…ké +bi”NÃ²É Â¡+Ì¡É\Œ×#Ór[©ˆíI‹O”ñ)ç{À¹r¦&l?«Éü€_¶wFã[)3³S#UQB=Ù+§¬•u˜ÁµQ¯ù˜1Ì^ ¹5À”ˆíÐach˜µ‰#8o¡Å)ÓñîÌ„ÌJÚ€à1æ¶}š/3úb6ÀœdÄ«µ¾ +>ØÅÓÓ·^å*±Wú¿šR,w:“«’Þ_–Ž+<ñ…j•j¼qo97Ï%•ßo®`ÛJìAwÞ̸¬ k•`¸(Vž—TüÓMêþÀÑ6KO9‹D»¦¶3HOÆyeboÒ26h’ÇÚÇûÇ'˜é ãÜžŒ±îF +7”åLbÎx*wþƒÜ‘‘øü¾è>3Úûi‘pßÀÒ–6Ã&GMöz? Öô.ñý]¥òóÚ-UÐë3ð4ŸÅw“=RÒ7m#1î@T^K|,¡Æ^TsÈwæxÍ8;älï`ŠãÂýfÏ÷²aÎz Æ”Y3ø1œÞ3ã¡syÌE¦1Y)²H0Ä?\ç84ömlŸúÔR{RyoÅ[$3ÚYm,,›¡yfãjô‡¦<2qçÅe8¸€BàžÄÝ:Ý1àèmqözƒa@¡ËO>cWç2U, ΩEx¨¤' éüc7Ø¢—' >ú6opZÑÊ}¾ÆU.—Ý–LL·h²ã¼h¡à†4 ª`kŽYÖ~€ÏœÍ~ù›ýô´ï¤Ví†#½(ÁEÊ,j—Æå1%i4 + :‚L®{ás&^‚åëýÈv z½gG žš%'Û”qc›ù®ô"ýµÌ§)÷$jÒ+¾‘{ӄ̈îe‘ØB«~ùšãœ-“ºÇѱd h:K*sˆ—ÁþÙ4$¶¡°…PP8W@À5îÍ9¸KChM™ð26 ÇæÁÔŠ!-‰çMAe‚v­xýX{£gÖi2C'táRÚ”§&‡ðF€·dTÞK-÷TŸw ¢‹çêéuÖô7ÈOao«:·•Ú2g4=°›kusª€-íÌàOoû¼Œ£-!£)ù‚ØTã;¼£àkuH-rñú¯ QŸðCïp3{·ž†ñâ—ÁOÏ> Ð¥gwåÐ_ɹš—¹ÊŒÌí g,œfúÈÙ¶ÙpÆÉÕ:£ƒ§Þ~V—)'o6~<_m0º£’lº*æi5-‚…I»LŸeF×€þ[Î貚LnO™EÀ@ꨭ©‚IåabÞÝÔéÙÔ<‡Qç;ñë^©…?‚úZ˜åéøb¡¡åo&}#`uÎ…m³LâÇø"Cw‡âÒÇC±Ð •ðòMGj°8½ßØÅl$0 /ªs°þ6a0`–*šFwLÐáHVLh?Ü+q˜OÔUçúšƒwG'É ÖÓõb2ÎÎ)»Úr@Û»KÀ&Ø+ºßšf¡çTãp±²Â +Çé[er\ aÔU9×AÈ;J}¾ãú#6 :VOçÈàÑyð&Ç#ÞŒ{›Âc°$Œä\±tšÈ;ø~p7ôæ°Ý]oÖ}?y}Lþ°}ƒå·ùnŽy ÈúøŸÜfþxPcò×¢|ßÝ6§Ý"ξ¡'ôØÁU©œ]Ðö/ô ï"Ÿ)P<%óÞ ESÔ3öÛj³ÛLe3؆n.øÉéׂ.êAú„„\ïÍ[˜MÙmNULÀbèF©§NS¤î]cW.‚Yl:  Ѻ c†¯f^oÀ£ñÒŠ™îÁf.¦0‹½ì´Û"]=BXÒPs_×2è\j Øtu%ü.?ØAö_(–ÑOí¹Ñ˜M—K42›O…X1púsáŽ5sür·Ó—þ—=õ•é63‘xdæ0Æ}‹K]3%-Æ:ÀWò + ÕU +¨¡JËmTº^.¡k 1çí6wݳ«?sk;‡Ç­Ö>×:æ + ö2°_öÿnŠf1-8š—ÔÖj»ú³nÿ9ô ¾fwàínŠ´”û5~¤YßÐÌ­¼H‡ê ÔpÐÒH½›bVÃ4Œé/W‡æÂ´ê軕Π‘FªÐÐh¯:sÙ"€Ô³ÖOtÝ4?R·¡o +;Wü#Uf†n…FÕ:íJ|cµgŘR¯J}¸"H»C{fùÑ ‘¢ _ +m k…Q“i6çl +’×ùmŽRÀ‹Ó4{N;€“û÷h ¢5<Ϊ¾ëêí€Ô}|`¥’=A ­êt¤Ï~t¢‘ÒœŒ£]ëCSilâõeÔ^¤Ã÷q]i¨´.}¨Ï?Ö06¸ªÖ‹:?ÒZÒ=«÷%>¤¦Ð.¥‘‚ya³’ñý+VáGêîv홀ýƒ©2³ò©ë{g…©BcÏ ±zUší¡œB:±gÍã6?Ò¬=®[è}]„T¡áø¦2¿H»=‡ÀïîAÞt›a!ý +ÛK>‹"5r*4WenqôÖ''@ë;qù·Ôûš õª|Ûyf(„4e/„9ØcÍŸƒ±ïËGi#¤s "-..;R(ùÝ=‹½12(ùÆzUs‹Þ—AÏ‹´­;¬‘6Öµñ!UhÇÚËÚÛÅS˜iÉ¥iebá?ÒSAɇÈdˆ¶=Ê*oîUì÷bŠi9š kƒ/ÒAe›GHáþò8Öï®gž@Ú÷Û×…éÇ÷i_ ø]¤ B;Ê[‚¾h&Li·eOoö^¤þ‹‹ ,@Ž´Á3wÑÜG¾tê2pá«41"¤N]TŸc´dƒqˆÔL#X ZöûL +ýÈ…‹ôzÔF¤·wg¤XÒ3àH]Gž-Í—k+Š,­õQ*å-h¬iòö +sJ/ŽôÝQ´p¡ùäþÀw—:˜,"¤ V}¹L¦ˆÔÎAz¹ÄG’ éÕó$vš÷`ÍÊ!¯ò¸7„þ°˜Œ¼ìNåZÚá1(ø´[sìÛBO×öÜXs§Ÿ>H~ zSZ·ÁhMI²_ß ç©×ï5‰§×­ÿaUzkå'ßs\(VóÁ¾àS¿ÓX ?]O†:ŠbÏãNí×LðiÉtxw +?V–!ú)‡b^UE9›æÞöç´¹pëŠ?]êÎλ-Ó†TN—eðb­i¡qà{ŽK¹”i{|ÚÑLm˜ðÓ~*!)Æó|¬úÔ‚O¿oÕSNðé¶íLÔè§Ûo3Ñ¡ÐÛ KµGðiÁéŽ~ +SL3;L%¡·µ˜6ß7 +>MÇËÓ…àÓ‚ó]é¦Xsª A§žœ=1’cêCœ§¦fíúN¶ZXâö¢Thh´ÈÒ@êUA;§ÇÔÝýD +6‘oGcŽY:‚Há–7B:g"u7€Ì@ëÿH4HçZ­ŠFŠ´ +©‹C^¨ýS#ÍîXH ]d½2Ѳq +"EÚ¿R`íH#cau ˆøêF +µA¤ + Ôÿ×üc cV1¤% R¤SÐHáÚg¡…:E“5«‹‰}"&Âüšïå´Þ÷æÚhéU‡éÏŠd;Ïšà;BZ$À˜ûqBZ>,ݱ~©Sg8h¢™û….ÚFŒ³âmUÐLÿ‰žôó&a¬C™„ÛûÑ“!|¢ êˆh §þF½ˆ²½\“4Äì¢E>šÖÌå#©À$J·ýƒ‹œðFï{ Ûz‘ÒPêL™ð™U¨Æ)ˆ%Ëî¸ðNø”@—[Uðƒ*´w#I\o'Ä1cU-›€”Ç tØ^ðh4èdÈÛJâëÔ6çtŠÙ¥Ñ]X·lvêÞí ?LjH¤åàž(Jþ!:ó<ãÓa‚ãShè¢?‚3HÎ_C)=*j|F‚ÇøF•×–±äÏßñ'nYÈ$–0‡1ñ¥Ãî$±B–§8‹æ+ÖŽŒsV¶~žò¼tÊÍÁÉ/SÞ™ø +Äé®&[ôEÏ0Í=.AÑ£bÝt?aê2‹€Z\ôðÓn˜†: A`þ˜ÓfüA;ÇŸv] &(¶…V%òÇñ­«|~hÌ] ÎõÞn–¥H-j‰ÓÞŽ¤LœQ1w1ƨU«–˜ý1šXÚǧÄ'aïsî“A[j2ù|lЂÎ}¦¦É|rLÔºêîçÿ!aì»ôúhŠœÃàdäÔáæ Œ¢;ã”Çø¸î&¹ëN|Õ½r¢ü¼‹Î¥Êan9ÌðOÏÄ8¤x` õé"ßtŠ1'þJ;¢yŒ’‚¼Q©Q]bô å–åíY4;½ + 2f_ÜZ¶-ìqJh‹¯lzœ’yVJ “+z¶þ«B#¸1ÉÐéÆ@›éÝ·%…æ™ùÍBšäž×žøÖþ<Ë–Ó<Ä*éW¤K·÷‚à^Y©ý‘£Ò. +täÔ)4òfXù¢Š…”ÎÏšÁƒFT±xbþ²Õ= +çä€52úV¥,`ì åÙ~‘'¼°®¸óÌ ™ZÝË]~‘bl‰öd¿Nl?ÌØwy0\oÑëµcÖù èOŽÏRhy7hgâsIÈ1†‚Í¿*oïÊ_[•‰Ï³ú9C?}˜É<2)Ñ´–øóÔé*µrJ·äR'z¥¶eI7¿b˜ãÞõ"=MÑ«¸‰ÇéÁÉ]‘²:ÂÑ-F‰eÏèn#›¶¨Uº®{OØäø¡$;ȈŠíq®S+á >å!ϧŸ@ àÔš &¢ÃðŠ’W;šg:¥ú¡`KK¸ä7ø¶,Wäi ×ÞŸNÉòˆÎÒ¤ý\Mžöõ}çá¡^ç7˜*us¤×,-X±p¼†€Mxí’˟ĉ•±ä/qA³YI€XO.q±¨%ŽÞp©[{àXØKœ×ê–ö.®UÊóˆù +\—,·7b.Yއd_`kðF±´K ±ˆÙÚMR€q e¹î ÊJâv¥pgoË?po>8;Ñ€P¤ÊÃlÉŸ+Ξ*à÷Qhļtû<Üí¾ä÷d‘öõqD€¿RÇ+‹“r“åi8±T騫J>v&2Y†2 ©\äªÒÏÐ×ÇP4âý‘ë_p½·|:G0l±§6Â"GþÙz Ö âC“æó"½ùQû˼ѧ"wßB#Âé€wØ›ÞOÀ{KãQH̹<ÂŒ$œô +ÎV'¤Ëêp6:©}ˆ©Ëâcaluð°Jnj߀£¿ÉaÁUÇàä$èõ9Ÿ÷ã˜?|»ûTTö$&äoåó¶â–…¿{bññÛK`Ñ(4/R Yßò²á° ÉçzCœ‰ãp^]8Êš¤¡2ù8Ïï{”½ÏŒuBøÜ&Ê>Ô!í¬gkœI¦ú+' I|ªÐH/CÛ æ´>}Æ' DÀXN£,R2ÔÏà™µ‹q%Úukxh×-÷O†Dãõ$abçW%Z‹–h¤õúŒÏ&-Ñ2O °ŸK4jUfÛÇ_h`Öø$ÚÓkÀy^¢=ø.p8¯K4åõ³WGä4ÈœÐS§86…†O`mBí£˜Í<:ä×QÜæ:;ÞØ<¾qøÛ«°äþ²Ô…ÅLÍ, Öy寷˜qüsbkcO Y…FPÌþ0ú½˜ÛÈ–fYâ?QPÀ¬q‚š„$Œ4yª½ ä‡p^@P jÎÉ»4ö¡»L÷2Ïiö¾j|/ã3A킽‚_-¯è÷ &(ë×—-¾ÏŽ\oÃw!¸vžÒïÅ}°Øëú}ÿÌ·>»‹ÁY{Þ¥õ¸‹A8¯è÷ (Ô^øó] ÁáÑïù PÖ«¹»¡ø^ˆ$LÿüònÈÚ GÜsb„üú+áHPþðÆa{%+xŠÃÔF‘)a’Ò,ãôíóó_5ÇÏ€½ººýâYÛâ±Ö"“¥î’3 ±YD`.­²Ö¹Èéiгmñ\ɧãtI#PläòânLI«åa[JZm²ìJ“3ñ9 ÏŽ\°÷AÏœòH)cqM¯¿m € žÝPÞ¹õ˜K*–~ÃC(æ{NňA„39²ÊòÁâÀ{W4øçé¿—Õ)™ñ°¸ˆ_ºô~õ”¼–1([îºÂ,‡Ú3·‡iÌw aÞ\š/ƒ¿¹åõ:æ.ö˜AÇÉúqx’É¿C'ˆeÐ f >™C'žAÇÌ|%‡N<ƒŽ¥õ½C'žAÇÊ|!‡N<ƒN$[ð©:ñ :”-ø 9tâíˆÜê—sè.+ƒŽ²,^̡ϠCú˜t+ Y$ìªËðiÛÂ1ð™@œ`]âõô¦¤·òò¥2ùÇÁ°)®öËãëéí¥Ø»ýsSÇ8ãKq6~L§‘Å®ì\Qj×ËI[|/ÚRNöŸ0ñ8,yãC¾>©Ì9Ùããx®׋|¢Ûžï’@棄çJ¬K|)sp,O%ÍÉ’5iN(Ûó\Õí©P<~Œ7äy·±ÓÝ ö²¿ªkQ‰M!3Ù ù¬cq2˜óW=Æx²›Ð± ÛJ’Jv¶OÅÒ2- Â2M¥Ì4'›•),;Ëñxb3už¥2åɬIæ)£­J!³wê¡|*Å徕efJÛö€X|gÙ”>öè:“rný'Ž›¼Ûâ šýÁ‰UVPíÌchJÜ<0IµKvß]*þ‰<¾©Tþ‹Šå´NMN‚W¥càu‘µJ¨S1¾Máù“Œ—E0ϲ=˜¼ó'7Ï‘ʉcvt:ök¢úäC(3å`Ò/Ÿblë‹“L•?HZ¢ýˆblǯ3ñÙµ°rœÀzžL.yºìƒÏ÷ÑÌ¢–zßo)ž/Ç!`«dïÈ< ÆÎ¡þ‹®îœ c²ø$Œ½UN¶±'€£øRW£€y¦nÕ—*÷£Ù¥÷R¥á_0ÅMÚÉ uIì4#îíÅräiÎ2Ž<Ñ€Ä¯Ü yOäÉE¨þ^>ôZ¿¸eäÃñÜ*˜Äõó|8V4ùŽî·óá¸÷Žâq¿'yKÀ¯äñN¬d„Zþ,Žk‹ ëÀL¶×3ëá÷k1‘;&RH·”Ù>ÊŠ‰”Zû×­ëU5 ‰$^ÏFGpÌR³/Žœ<A(¸>†àüB&×C$ƒH ¼´DƒÉu±ÇxgF-ˆgÝÙøñ¬;KLg +z{>Û¯,C:ùˆáUxu`¢×ÊðÉ1ÁeØ=É]†"Ú8¤÷sö"/£ýÎmÀÎsËG Àù•DÈ”MÞ.& GøJ`Û´„¬¯–¼ í•æÜÖh0?ú°`ò™øÅN’5•‘êPþ4@„1f¡‹…”‘:QŽd$‘ÊÌH(§r\©~¿’‘Ú?ÿNF*„ó©0_ìõŒTå72R!y×@sLb¾¸>´@„/M}:ÈÈ`!fŸ½ ?_^†œT8~9öÛ©p(ž_4¢ç7Ráxçå×Sá~î·dSLÜv®|!ŽyJ†û#©p<^…? +Çï“iŸuÅC¦(`ÞŸ,–ø´ŒU•s#<[KZ%Ì"¹^”G–GQ†¾ÙîIæá’ágÙè]™G8b·óQηåÑX¤“›¹yu}Áëp¸›éåÙèõ «Æ™ùðs€J\F+ëì®=¥Tó{:Pdž™f´šŽÙn: ù3Íw_• O}¥/±xÎÛ,$ÖY2™°a‚Ɖ܎4;v— ;‹/ë ZIDŽRO8ïÌ_ V™ìÅNv Í*e¦ó™Ô³6jÂØQ(ÃÎÝýKv›Û‘Ú3Í„X†¦²)O„ŽEf-Rn.VÐtexá¸ÉnîÏu‰ªLÈISêÅ’ÝVRv=¾Ð^(ÃΫR½÷¾P²[O,ël/žawl6‘jK‹õ\éB¼_íSiú£—$¯úà +„ÖØvhVÁ‚%F>|”Ù.Ä×÷*°Zºû%Y݆*ÞŽØ:K^Eôƒ´’¢™ûŽ»¡ŠùŽe„ܲ5XÓ 댨ª=þNjt„*×k"\Çê,£KŒs1‘NI§JÅyàÖëoV’ã+;Âs§Š ï’X%¹§¼p½”ìI‰lHt'äq}REäDãúäó“D9ÁññÕ}“*6"w|Òµd]ªÎç.µêÇÉ_/Ù…p!"ÁpW¹èÄ=½?ɦû™æÙl:>;€¬Åù{Ùt2ï‡y1›ŽÏ'È]/¯gÓñåÒý4óQ8›ŽÏÏiÿJ6‹,Äšå½ì¥l:>P7„ü ›î‡;ò“Ùt|ç4Ô^ùkÙt|¹tL{ÿw²éørédÆö<‘MÇçk'+°ÿ^6ßì"Oï¯fÓñ)7ÌØÑßɦãË¥¸mþ…lºÇ.­UšÒO³éø”S…æ·³éøæ'êÅl:.(ÉšÂ?ʦÒ-7›N>Å^ɦã€âž‰ÿR6Ý(öt6hŽÕ¯eÓñçVÿv6€å—³éøNK81ð¿MÇ'ØÖëodÓIœŒüR6ôþòÙt|Ä µñßʦ“Êäúl:¾\:Áz|¯€ÚdÕ{êê&á*^E*¹µ9d&>%;‹øî£P-›Ÿ×«{èÒ žòüv½:aíBn&ÝSt¢©ÄŠ„„9™’Š…L`%(d= uŠÓ%¹¢@Fu9öÁÔº):õTÙe±.ñRJ:‰”]™pc[DïŽÂ•c¹Ô“DasqÏU-K¬ÐÝËeî÷\ÉTÉRæN°º\QV®’Ì2wB™\òédHˆÇ'Ó…î^›«l÷$§ÊŒ¨—JV™;I"$ÌËeîF¼ÐÝËeîðÜ7‰BwòNÅ_¸çÊõÞrˆO¢ü<‹â¯…(Š´Êýã\0´ˆA. +žòÀu’±‡Rü Óè ñ,ê—•H''¢Y!a(Ä!;¿` ;|"ˆ RGÐõÇædy¶f•Ž›O/M3ßÝPBç=Ùêþ‡‰]¬h(ÀTñಣ¡(Éø1ÙÑP˜¼ˆnñÀf$û~%óñõ£XQPä:pböe‘UÛ• +#²«ñ+AyÈdçÍ–ÎeGÀd%Ö +Õ€à&Ö6‰µÃïÝ +ýJ!cÄc ·r„sï$e“”’õ^Åb¨Gº0ï1‚ZÔŠ¡^\·¿’™BÝ|ü¼&ñªtÿ­ +†àAUøˆXöÚo=U¨Zèô ^{YÀ¡ˆEu>çn Àá¼Z­‡B.B¾Ÿ‹UmÉHdxê„×`âY†m‰ ²<ð/W¸#ó+…jÜý`òWü´nõsîÄ2_X†$£î~¥Ržd>‘¼Jy¯æQ•ò^_†"îØyIròC~RáN¬Þ+¬q÷|…;¹·šÃì§×kûgZͲ^e'Ö`ÒjŽBŽ¢ƒ×¦ûYb-7óÑóªÿæ–yÓ¡Ÿ»ç +ÁùÜ|¸#Âù•|β]ž-& G8–“Ĥºf•Ë{&¿/‰‰y* +„ùaõ!ïEÖ}“™Æ$'‰Ir-QÆ.&'‰=H ß) ŠšÁ4¦ ^V†º¨ÙN­Ê ^Ϊ”“Ĥ‹¸Ê÷OíÊϧ’˜„"ˆ`íD‘ö9Åð“Èá}áV~ŽbÈsYâg¯ñ9®I«ð­bÐ~y¶"ÝSå)…õ±îï]MÕE§o¿”ãÚ¼šêyfz}ªÜ£D¥<Ûs\ë“ +Ñõ,ã +º$;ëY,Ç•ðÁ‰(ùøñ‘ªÌxËEš©­#™°:)Õ¢ÐPhRï¦F3r›¼àS¶Š²û2ŸýÌÜ©‹*SøÆƒ\» ß1ñ‰•„÷ñ^g"eåÃ)4WU»[eºªØuØ‚É^W Î œ„w¹ƒ¶Læ¤áÙB¹^•ÚW7 „’ðú‚HÁX”™“Kp¬ö¬ê«%ˆT—ŸZWBuØ 4R*“‹&pÕãd e§¦]u>-]GÐÅÉr4èß{;>¤ + $0·æ+ oÂÍýc’·® + õª4îÔ˜öÁr3âF"H³j¯0ÒlMõŇÕ}óçX©•\¤u±:‚Ŷ0Òtº•aEv´jøÜL}"’0ïÆˆ=ûí\I»`;f~¥}l Æe@4·4½M‚1wÝ\¥“:¬!¼=ìÍ3)â'¦6:™a“€¨: bU1“œ”±7Ønb“¤‡D8%é1´J°K“…;•”“Ú$Øtý­Ê,È¡+Q™E®')%Z%8u•Y‚Æ3!hbYwœ›'_ÈJ“ˆÐÄ©¤ÇOœC¯gÇGy­aÞÝÓUºô¥Å]/²‰þT”–B#Ö)a?ñs]ÒR8N‹èÏCl,G\¥ûQ#ûð:ýX3åg¦€—qõµ„v˜þ­Ë»LÞ ‡2²‘DÝËrOy†éWn¢¥s®ŒûQ²ÿ‘õS÷ZÓ?ô,³oÑ„Y€¯^BËwWœ`¾˜”ÕòxWœˆÄïcð±N2OÕ;–0™_³‘'®„ùé©7žp'´ŽÑ‰•tù:Vϸ~~i—ªÇ'èUqŸ¶þËóúˆ@í']äSøpð¹D²‡z/”eÏdz*Ç*ðÔ=Œ.qò_æY1ß±¤6Îê#TŸ8­f¬Ú'S·j©œÌ{®DR¥JKÌ}ò>ÏJ'êÈfN]cÞ*3O“ª¢£ãf׊ãì*¯QL2—Gþ Ù'—/RL²žÎS+¹ûÀÒ…ù´C‚b?Ì”›(û¡1Y9€"Ñé2²åæ>ú“ŸÉ”›ȼáðù,@¹9€Èkýã,@«ˆX˜Ü8Øç²åæBŠý< ¦§¸Ü`çX=›(7²‘”(Ð¥‡@æy%ÞŸÇQ½^”O~E¶WŠò1Æò‹ò=xþHQ>©Šl¿S”ÏWP^/ʧÐðh…¿^”ï±>òŸ(Ê'\ù7‹òɪ_ùJQ>¦WvÊ+Ø)‚DR©ÁÂUý^¿ªðwCɬë'ën¨—ëú1†ö wC Õõ{.Né§uýÄ«úýèn(žº~â^!~Küùº~üÌ'z7ÔêúIsòoÔõ ¡ ¾ÕŸÔõãMG»Õ\>adgÊòÞýƒº~â7Qgâ/ÖõÝè’hû…º~âná§n ©ë'nêòFAÿ ®_š$½ô^«Ç÷ 9ïOÕõ‡ýü¿Q×Oü@E!Î+‰ï܌ԟÖõãQ5Uý˜Õì/Ôõã,C;»ªëή§ÓSìT]?ɼ×_©ë'^Õâäëú‰\32R_ªëGçoññ"çž«×õ' Ò`¡®ŸÈZ»n]¿TÏ(Ev=>‰rr²ëñ½Tׂ»|žÍæx¨ë'’÷ m»x¶®Ÿ¸&o<øº~B{xûñ$ñgù[2Ôyò$QjJÕõ?ofGü¼®›Ú\kñ§õøž Ò®Ç÷ ˇªê÷z=>9ILR·7<Æjo”½usñá9·õ‹¥º7¸SßÊf½¸ îŸÇÅ»þpùΚKìàýÀ:ï:µKŸõj–½|Þp_«zîc$Ö>Ê팶š6f5 c˜sun›Â.]ÌžIöÌrV°g½Úúå2è/W'U ½Ó«gÚ«R™–MSÄféX]¥€’L{û¶]®ý#,6Pá¹Õ2„•_ú*ñYí jª$¬'y;4Æx¬Tà#"îM çÅ¥ÆÇŠ2õÝ¢ªj5£êÃÅ–Ñ:Ý«»j7Çæ0“´@&‡bwóÉýãÕÊ”“ˆÅ3ͦƮ^Ì‘½_=²Uögù€¡U“Œ¤9ÚßÊk|tˆz5É»B“úÊ,>QÍä [ÿô~$çʘí.Äl×y(Pm«éNÌ5d;D³»ög?žóbS§}&Áâå3õÓÖS»ÃX¨„]Шâû#ääx±Ó™ÒíÏü\ÝYaÒ®7룞îYNvÛM£F7\Û @d·ŽlI9¡~3ÑGvhQ, 8ÙSгú‘çóŒöŽœ·ä„_8ìéPCÁ®™ð&Ó“jè5 ëvê¢znU³âfÞ$„S³Ãà°‹c–« ¯ØÙWÏAÌU7c²•[xÚ4£§Nƒ78'‘6­$»Aí·;w MÚ0î°·ÕL{¬'Žåpfxð.ц3;ú$1áÒÜó ²?†Kël¹Qo]ÚPÏC|JM6T;Ñ®½s{SI6™ÔN9‹×›}B“žZb¸Y‡Q<±öo'/¾ÙªûXË’›”X{”›ä.%'Gn•£>ÝI” Ãî/)ðæi»µù(7¹e‡uhà@¬ùѵꌈbŸ[>«‰ÏáÕnÛ*­¶²ªu©û›Ý®·^ 9¤% +GþVðÕm@iÎŽt?¿M°sD¡ „629ñ}ðNŠ”œéu0ôÆŠ<PAOCÎ&àSÊÙyf>(êaìëæYØcrB‹F\†˜“š)õ›™ø­å€<]¼ákç’ãm|Î’ª%»Ý3-‰(:˜Ò0qö"=yU² èÑÖ‡ÞzV6iÆÃw‹lo‰õØ)GÎ’q@Lع÷:”m5#Òÿ˜§ üúSgôpÝ)ô.ÍÞÃK™"ôÜ>×RP»ˆÌ´LõÕº;”v‘s&›S7­o¢ß`!ã2Œ5\÷iõ ”$ÿ¸ïÓ~9 i”X¤ åfý5Ï놉ÒÞOP¥A`ZÏ1ÃàÊ÷h ²¦0a—péElà™‘ÎiØ'|P ÖF[Ù!Ÿˆß&g#ñÛVi+zê¡Øu΢ûž¿ Q£»6èÂÙ±Žqß”¬´šïš EU'šR5´–i +íZPòWJø¨Ë¾zCOÏË'v´¥qåî’èöH½¨ƒSu½˜ÏåV«†I±;†(Àpbº¯=-¤F^2PÑýaZ?` Is™XµÃÇ$ Ü6š + ìÏ–QC +]×€!umPDè3‰pïU(p®<1ó‘r2‹®XM¯'ˆ0y¯<¦C"H1攈‘€Ê¬GU,÷' EйUùÝ’Áx<Îľʖ óî@$Ò’ªÕ.Ä:üJ ¼ßÎUŸ•Ä \ѨQ7 +÷a¢ÌšÄûÆ"Ú &3ühh ")ö³a€-hkœ º"«’9?ái==`,,r)¡§I)<Œ÷àö€ »nŒœ2ìzš"}!¥žfl{.Õ˜BŠ—ÍØ- ø-JøÅ9Í5ñÝgrèv?æ1¦Ôý-ÁJñØ@ˆ¯xY<ƤÄxŒ¦—Çä³)cÅ 2 ¿OéMQòàÏI©G³A yL6“#éÀ à,ëò×À”¾dpŠÉ =âLÅéÃoixUþ˜ÓÉ"%®]È=¡O ƒáU0 .Ù¤”µ#cºT¿F€p^‡lÆW×§Å]|)H ñ>ÈØ‡ ´ë“/ÂFÄ•$Ťw"ž}È(—+…¥¥Ñ™è:òŒ¯ƒ[°@u©ƒÉ"~0³ø^Ø‘Ê4> +ÜR|a¿å Ø/¦™5‡™+3Û˜gªúžÑ™GóOÌ|>•0sù³ÂP¯ùŸ²õ1¾Œ¡pƒ±<Œâ¹ý‚X€1´”W‰ê½Âçe`‚3ùŠžg‹GŠQv,Ó1ÍÈ M¹qÚŽûÆÿŒGœö‡ÃUù§<â´?ŒåyÄi8ò[þ!8íGXþGœö_+þ Gœ†‡NFþGœö‡œüG<â´?œòÿ8íÇÇòg<â´?\¡ùsqÚï†ðˆC׊‡ö¤ÎÛ´PhèÉ…T>¯UFœŠºHÃHÈ©Ì}![àÎf‡ëö콓ž ŠÁ>8%9=ZC0.º‰r¸ ET:S–ž I*JYй<Vðôÿ“`×lÑ€Òk¸ôâòäí0ž FÑ‘‘t@þü¿æ®}­m\‰?ßÁ)P‚É’-)Ü!$Іk¹÷BH „³gÿÙg?3’o!Ò¥ývÛ¯T‘íÑ\~3idÂõÇ%ù|9ŠIe-̤òlÔ·¨C¦ùÍÆ»:pÅ›În:PޯʧÙ;·lÑ8è5 Â-繩h·âº”6<Æ1G°Pc½Jaô +7¾Û Ö‹G ±›¢ÍösO¤´½8Õ„ÿš·„”¾-„òíÒîÓvo«wÕ¹êÚÓÖŒUZZ§t¿{qWíµÛ{íÿõ+w­§Ûv·o—íÒÒ—•õuéWÚ­»‹¶­OÌùßDjCÏî^§ÏM lkkü-W.Uífmlw®Y¹$Çó©Mo“ßï=䦿_¯™¬mgׯ¹i¯ú%—_{œÆÇf6Ûè™Õûo`j¬Òî-?­7ê‡ÏOoö©o{µ9ˆ©‡ËíÝÅÒcg¶´¶tò¹rR=ø²Xê“;=G‡Eó#*©£'1 ùìÌf7yÎ)Mà‰Œ +È2^‘¥Ütaq{?åŠ;6Q¾M¼°s–Øæk¹©‡±îhËÐÓÀoð½¢bX÷IæÖêÙYäèÛwÚ%,sHNw:óº6Î3XÒÁ!ª•ôGô¹|غnã/¯[+„î6ù€ßS´V ?–óNènfý²ÎC—ûå~TNZ'É„¾ÔW‘£üâì ¹ÀÆg:KÑ…g®…IxUÒ—¹¶2š…é±±ÚaÖGÿ)šºÏéãõ“ö/ý1q­$9 KhÛUp Ö^Ö@Ã3CRŸý¬KHµ¶À´Û[%ŒÓ0ÀÃ8|Ü×d§ŒqJÛ_Yœ€úÇfÇðjpaJ>®'ǾE£h°Ÿ€Æ*¥æìÇr§;¹_]å×CE$íâ0Ÿ +¥€ÃîJB1‹žY¯L1ú­ëšbá“ÜQ²V]œlïUÖ/ær¡7Ñ(¤ï™©Öš0“máÇÙX”84y,z3 ¯ó-GO1ab{¾_ +a~Dèy}½-H¹ÌÌtÞðæÕº¶:oâɬ°}Á5‰Ð´7»š¢£kÂÇ£h:¼ùJ¢V#AÛ¹7¹PŸ¹¹¹`© ƒ$Ì 6&BYnn®Ø²dr¦Ý(\®|ª[Ë?ä˜LÅCãû“_WŒ@Éóö`tʦé#7å/­,ý¡5º˜‡V“F­‹ä8B+œ·ú×|ˆ¿P*ÌψçÃFõÛÆ„Òó¬q?ž= +ò©VÄ|ê4ëlGdìcGÑj¨üÒä†ÿ©Óꮥ¾_Â렱 +Añð(WØ<ɇqó©¼!Vó……½\áêÇ|Îéîüc©ÂyaYKÅùƤ.Sc´,†Ñs•T´ «É“ÞäbîT›£åŠ;­WÍÞéÓìzéê¨ïšLïjº\Ôµaˆc«Ÿ²~³[8ØüüÌúxaF½$´š8vºÀ³¸ªxRu¶‚{u7¦ˆ=Ýñ! XiÅ…tô~9ІåbcClÔû[Ò$È´¶Ÿ—q!=¬Ê×:5¶i2uHhKaâSÛ"&x¦"vmßÓ†€ÅÔ«cñYéä 4ódöÕ@z (N©Ÿ}o¯9û¸ÚðãBóγ=T³]ä¤kÕ-ùy¨ØÝÏ?Æ$¶‡‹ÝS5—¯-ÝF%õ»èûÁªòÎFª*ŸWÇl *Œå&–Ç+‰Œ²þr)E€VjT#OÍ„nH×¹xéj~U'´yó¨ïà!ê;î•®¶ºÅdí¢!7°3ZÓÕæJ­éfªÍsÞ w}”#®6?+µ¦vÂ~zsYïåŒTm~­äNïc%4Ò*Л¡ +NÒ*n“ļÿ©’ûó‚{²KöJµùµ Y'VB8hx¤T›dÌõõ»ṃ¿qê`¡¼Ý¡àžcÍÇú¯ðpO_Õ3BßÎEk\8ïBDË™sU¦¢å3QšD°’éâ/òGK}@ݧæXNò%wzøÙ^ëAT¦öd夺T¯Î-ÍõF+IšúOüåí¢ä»K’0ÊÛEÉK’ñžèçBú‚ê7“úÉçt¥|üæÌçtp`y\ÜÏïÄž.Ÿ2õÆ©tï~>>¾¾9¾Ðáq­—̰TÂàbJQ#Λ4\KǧËSûcè@¨Ûpý H.‡KŽé;SAp"žvJaqraÕ¶’míìk²,µÕ¸vÖÓ(üÞ¶‚Ãt{)Åx……Å™Ÿ.n }=W¼”ÇQÖª³¿¢WØ©Ï%¿¿=Þ¶&j§'²³ôåiüjõäbKÀ{4Á¯wúÔX =â¸ÁâýHž.D.ˆ*‡NXÜYÁàrXŠ~{´öù)®;i´Åt¨KXR8dq‹«3·|< )žµ´áOâúô ‰[t`wô›×ˆk}',]£¬ÜŒG[×~$ËI0Bõ7©ýZq9ícªœ®þž¦Ê\ìca •z +?Žq â”Æ-/}ßF‡cÏ|ßk§úíàx¥R”…juuã ª<öo}Ò>/ˆP7Þ¬81ïÛ¾½ágÞö›úÍ~áNïoÞðKWF~߆Ÿ©Yˆß½áw÷×¼5­Sz¶Ú½H­‰ èùÒî?Ýã þÙr»sÕ­7ÿl÷,j›¿þâO¡lêIÛó}øàcoýÜÊë{mZ°ë]k⬴ÔëW®Zý«»n³÷§]Æ®£úþzÅ.ÛæÞ3¸wÆÎ7ä î†K¬vž‡g±—àßÑ0øÒ=´¶,âŒø\ÙØ RQlP&‘ —+!±Ã àÀó=Åuƒ2Á죦EB€ìŸðá4®¡ë›{ÃþzJì r×¢±âB‰ +ì[óYJ—µõÿ¾í`'1ÿµ,sÕ§æ³3ð€3HnÆ}•s;~ÒÐ3äÂ1ðCúÞç„.­'Ð +¡‚{Hª$ÏÆ†ïûµw2ª{¨8ª…‚RtEõPT ·7,E]¢„íxÜ p¤[+\"ÈÉ”ËxØõ¸ þWŠc<ƉômI\&3=„êÀd&òëIâ¢`—¾ô{WÝŽ_^^jwïúM¼5—sAµ…% $ŠF)º¼à«Àô Á=_£Ãd€2;B±Àv”çR +¸µ¤))´!\Á5š}O¡5ÿ ÌºE©ïúgZT }Cä^ÓrBýÅ$L + E8Š«‹†e±,ÁdT"€›–ã.U³g*=-´˜‰A ª¶C˜÷˜ëQ†6s(ûq¥l&]p{ˆÖËò%\Ž*0"Þ¸Ï7- “hl& " L"<Ád +Å{6ƒHE}éû4›mÇóÑþÒ¦¬…Q* ·/ '\Ƙòƒ…>xÊH¦ÂØÈ\‡$ -l®Ih²@€ íÜê>ÎÀä/mæLxý¤€0æP*ð¹z¹—%£ÀàLE -Ši÷ò„@@*åsŸi¹ñ+` Eôñ¥Ô2Ïç DØF„"že¬o`‰¬óÀ°ÎDP¥ C"u¥/¢¾j/KÚó*Gæ¾¥ì|Á>:Ī'—xB-1…°©%—f:T´%6@^=)Œ§:|âäNÃK)œJ) RûàH]A„Úõ»Î@‰M÷EO¾,!Pò” ž‰ˆf…ÐÓ'ò.$1s¤’^ ÐŽÄãà‹:¸†–e³ðqQÓÇÑÏ¡‚aÐÅI”e~ª³žtÒ@¹à!(eôpFWúÑK`“AÔ¦" ôŠ™™â…Ö=‹=ÌB$L÷zŽØ<çè6s¨,Ž¢g³úž³I÷rCUªŠ(Â0ãHÅihª5,1Ài¼øD¸’0õ%jõ(Pã©®zÜEA› ."?~2«/õèåûS%.1´(Ïæ0’ððw9 +üFäbŠ¡Öñ!8Á¬ŠD¬§zZcÜe¢$}žË)0iD(îˆkYq—R `@dL†â¨’§‹{ZVÌRÜs’ìW¦Qœcä'óЩÎÉš0'C¶¬s´OÜ%}WAœÅ.xPIyŠ#ªyØ5@ë_’Jq ú1Ìk=P)¤©·Úà­`µƒL‘H¨hþu`ƒ9Ê•a¨ êyà‡©ý©È‘UB0 ¨‰Ú°q +Ë[ŒA$GGGSœ2H¥qa£Êt Ñúçs©!ÀXrœç€sˆ[òÎ! ¥@{,Ì V‡áûåS]æA Æ=Oa®]€@ƒ‘½ †}°rÀ‚0`A¼oˆÚ¿ ›B̲=ÄœïGà-G|sN@IÀ¹d•C@q|­Y‰ ûåÉÔ»“ L©ŒL#ÈÊ4‚ŒLC ÏëÃô~E¦¡²2¬¡²8ÊÈ42è½3ÓP™F0œiÙ†ÊÈ4†©ý’L#E6ZÙ¤¹LV;I8,@h¥¬¾äÙK«¾lvµV»zïÉq¬‰‰íf§½×k^ýh÷¬Îcó¿m»Ùí"¾Û÷pÅîôÚý»^Û~ü~÷öÀ#Ñí«[UëÿOðW$ endstream endobj 233 0 obj [/ICCBased 375 0 R] endobj 5 0 obj <> endobj 56 0 obj <> endobj 93 0 obj <> endobj 130 0 obj <> endobj 160 0 obj <> endobj 196 0 obj <> endobj 223 0 obj <> endobj 250 0 obj <> endobj 269 0 obj <> endobj 288 0 obj <> endobj 307 0 obj <> endobj 326 0 obj <> endobj 345 0 obj <> endobj 354 0 obj [/View/Design] endobj 355 0 obj <>>> endobj 335 0 obj [/View/Design] endobj 336 0 obj <>>> endobj 316 0 obj [/View/Design] endobj 317 0 obj <>>> endobj 297 0 obj [/View/Design] endobj 298 0 obj <>>> endobj 278 0 obj [/View/Design] endobj 279 0 obj <>>> endobj 259 0 obj [/View/Design] endobj 260 0 obj <>>> endobj 241 0 obj [/View/Design] endobj 242 0 obj <>>> endobj 214 0 obj [/View/Design] endobj 215 0 obj <>>> endobj 187 0 obj [/View/Design] endobj 188 0 obj <>>> endobj 151 0 obj [/View/Design] endobj 152 0 obj <>>> endobj 114 0 obj [/View/Design] endobj 115 0 obj <>>> endobj 77 0 obj [/View/Design] endobj 78 0 obj <>>> endobj 40 0 obj [/View/Design] endobj 41 0 obj <>>> endobj 365 0 obj [364 0 R] endobj 382 0 obj <> endobj xref 0 383 0000000004 65535 f +0000000016 00000 n +0000000350 00000 n +0000043667 00000 n +0000000006 00000 f +0000228507 00000 n +0000000008 00000 f +0000043718 00000 n +0000000009 00000 f +0000000010 00000 f +0000000011 00000 f +0000000012 00000 f +0000000013 00000 f +0000000014 00000 f +0000000015 00000 f +0000000016 00000 f +0000000017 00000 f +0000000018 00000 f +0000000019 00000 f +0000000020 00000 f +0000000021 00000 f +0000000022 00000 f +0000000023 00000 f +0000000024 00000 f +0000000025 00000 f +0000000026 00000 f +0000000027 00000 f +0000000028 00000 f +0000000029 00000 f +0000000030 00000 f +0000000031 00000 f +0000000032 00000 f +0000000033 00000 f +0000000034 00000 f +0000000035 00000 f +0000000036 00000 f +0000000037 00000 f +0000000038 00000 f +0000000039 00000 f +0000000042 00000 f +0000230875 00000 n +0000230906 00000 n +0000000043 00000 f +0000000044 00000 f +0000000045 00000 f +0000000046 00000 f +0000000047 00000 f +0000000048 00000 f +0000000049 00000 f +0000000050 00000 f +0000000051 00000 f +0000000052 00000 f +0000000053 00000 f +0000000054 00000 f +0000000055 00000 f +0000000057 00000 f +0000228577 00000 n +0000000058 00000 f +0000000059 00000 f +0000000060 00000 f +0000000061 00000 f +0000000062 00000 f +0000000063 00000 f +0000000064 00000 f +0000000065 00000 f +0000000066 00000 f +0000000067 00000 f +0000000068 00000 f +0000000069 00000 f +0000000070 00000 f +0000000071 00000 f +0000000072 00000 f +0000000073 00000 f +0000000074 00000 f +0000000075 00000 f +0000000076 00000 f +0000000079 00000 f +0000230759 00000 n +0000230790 00000 n +0000000080 00000 f +0000000081 00000 f +0000000082 00000 f +0000000083 00000 f +0000000084 00000 f +0000000085 00000 f +0000000086 00000 f +0000000087 00000 f +0000000088 00000 f +0000000089 00000 f +0000000090 00000 f +0000000091 00000 f +0000000092 00000 f +0000000094 00000 f +0000228648 00000 n +0000000095 00000 f +0000000096 00000 f +0000000097 00000 f +0000000098 00000 f +0000000099 00000 f +0000000100 00000 f +0000000101 00000 f +0000000102 00000 f +0000000103 00000 f +0000000104 00000 f +0000000105 00000 f +0000000106 00000 f +0000000107 00000 f +0000000108 00000 f +0000000109 00000 f +0000000110 00000 f +0000000111 00000 f +0000000112 00000 f +0000000113 00000 f +0000000116 00000 f +0000230641 00000 n +0000230673 00000 n +0000000117 00000 f +0000000118 00000 f +0000000119 00000 f +0000000120 00000 f +0000000121 00000 f +0000000122 00000 f +0000000123 00000 f +0000000124 00000 f +0000000125 00000 f +0000000126 00000 f +0000000127 00000 f +0000000128 00000 f +0000000129 00000 f +0000000131 00000 f +0000228721 00000 n +0000000132 00000 f +0000000133 00000 f +0000000134 00000 f +0000000135 00000 f +0000000136 00000 f +0000000137 00000 f +0000000138 00000 f +0000000139 00000 f +0000000140 00000 f +0000000141 00000 f +0000000142 00000 f +0000000143 00000 f +0000000144 00000 f +0000000145 00000 f +0000000146 00000 f +0000000147 00000 f +0000000148 00000 f +0000000149 00000 f +0000000150 00000 f +0000000153 00000 f +0000230523 00000 n +0000230555 00000 n +0000000154 00000 f +0000000155 00000 f +0000000156 00000 f +0000000157 00000 f +0000000158 00000 f +0000000159 00000 f +0000000161 00000 f +0000228795 00000 n +0000000162 00000 f +0000000163 00000 f +0000000164 00000 f +0000000165 00000 f +0000000166 00000 f +0000000167 00000 f +0000000168 00000 f +0000000169 00000 f +0000000170 00000 f +0000000171 00000 f +0000000172 00000 f +0000000173 00000 f +0000000174 00000 f +0000000175 00000 f +0000000176 00000 f +0000000177 00000 f +0000000178 00000 f +0000000179 00000 f +0000000180 00000 f +0000000181 00000 f +0000000182 00000 f +0000000183 00000 f +0000000184 00000 f +0000000185 00000 f +0000000186 00000 f +0000000189 00000 f +0000230405 00000 n +0000230437 00000 n +0000000190 00000 f +0000000191 00000 f +0000000192 00000 f +0000000193 00000 f +0000000194 00000 f +0000000195 00000 f +0000000197 00000 f +0000228869 00000 n +0000000198 00000 f +0000000199 00000 f +0000000200 00000 f +0000000201 00000 f +0000000202 00000 f +0000000203 00000 f +0000000204 00000 f +0000000205 00000 f +0000000206 00000 f +0000000207 00000 f +0000000208 00000 f +0000000209 00000 f +0000000210 00000 f +0000000211 00000 f +0000000212 00000 f +0000000213 00000 f +0000000216 00000 f +0000230287 00000 n +0000230319 00000 n +0000000217 00000 f +0000000218 00000 f +0000000219 00000 f +0000000220 00000 f +0000000221 00000 f +0000000222 00000 f +0000000224 00000 f +0000228943 00000 n +0000000225 00000 f +0000000227 00000 f +0000044791 00000 n +0000000228 00000 f +0000000229 00000 f +0000000230 00000 f +0000000231 00000 f +0000000232 00000 f +0000000234 00000 f +0000228470 00000 n +0000000235 00000 f +0000000236 00000 f +0000000237 00000 f +0000000238 00000 f +0000000239 00000 f +0000000240 00000 f +0000000243 00000 f +0000230169 00000 n +0000230201 00000 n +0000000244 00000 f +0000000245 00000 f +0000000246 00000 f +0000000247 00000 f +0000000248 00000 f +0000000249 00000 f +0000000251 00000 f +0000229017 00000 n +0000000252 00000 f +0000000253 00000 f +0000000254 00000 f +0000000255 00000 f +0000000256 00000 f +0000000257 00000 f +0000000258 00000 f +0000000261 00000 f +0000230051 00000 n +0000230083 00000 n +0000000262 00000 f +0000000263 00000 f +0000000264 00000 f +0000000265 00000 f +0000000266 00000 f +0000000267 00000 f +0000000268 00000 f +0000000270 00000 f +0000229091 00000 n +0000000271 00000 f +0000000272 00000 f +0000000273 00000 f +0000000274 00000 f +0000000275 00000 f +0000000276 00000 f +0000000277 00000 f +0000000280 00000 f +0000229933 00000 n +0000229965 00000 n +0000000281 00000 f +0000000282 00000 f +0000000283 00000 f +0000000284 00000 f +0000000285 00000 f +0000000286 00000 f +0000000287 00000 f +0000000289 00000 f +0000229165 00000 n +0000000290 00000 f +0000000291 00000 f +0000000292 00000 f +0000000293 00000 f +0000000294 00000 f +0000000295 00000 f +0000000296 00000 f +0000000299 00000 f +0000229815 00000 n +0000229847 00000 n +0000000300 00000 f +0000000301 00000 f +0000000302 00000 f +0000000303 00000 f +0000000304 00000 f +0000000305 00000 f +0000000306 00000 f +0000000308 00000 f +0000229239 00000 n +0000000309 00000 f +0000000310 00000 f +0000000311 00000 f +0000000312 00000 f +0000000313 00000 f +0000000314 00000 f +0000000315 00000 f +0000000318 00000 f +0000229697 00000 n +0000229729 00000 n +0000000319 00000 f +0000000320 00000 f +0000000321 00000 f +0000000322 00000 f +0000000323 00000 f +0000000324 00000 f +0000000325 00000 f +0000000327 00000 f +0000229313 00000 n +0000000328 00000 f +0000000329 00000 f +0000000330 00000 f +0000000331 00000 f +0000000332 00000 f +0000000333 00000 f +0000000334 00000 f +0000000337 00000 f +0000229579 00000 n +0000229611 00000 n +0000000338 00000 f +0000000339 00000 f +0000000340 00000 f +0000000341 00000 f +0000000342 00000 f +0000000343 00000 f +0000000344 00000 f +0000000000 00000 f +0000229387 00000 n +0000000000 00000 f +0000000000 00000 f +0000000000 00000 f +0000000000 00000 f +0000000000 00000 f +0000000000 00000 f +0000000000 00000 f +0000000000 00000 f +0000229461 00000 n +0000229493 00000 n +0000000000 00000 f +0000000000 00000 f +0000000000 00000 f +0000000000 00000 f +0000000000 00000 f +0000000000 00000 f +0000000000 00000 f +0000000000 00000 f +0000046055 00000 n +0000230991 00000 n +0000044093 00000 n +0000049047 00000 n +0000046361 00000 n +0000046247 00000 n +0000044855 00000 n +0000045491 00000 n +0000045541 00000 n +0000046129 00000 n +0000046161 00000 n +0000046398 00000 n +0000049123 00000 n +0000049347 00000 n +0000050337 00000 n +0000060495 00000 n +0000126084 00000 n +0000191673 00000 n +0000231018 00000 n +trailer <]>> startxref 231188 %%EOF \ No newline at end of file diff --git a/Assets/Templates/C++/Linux/HelloPolycodeApp.cpp b/Assets/Templates/C++/Linux/HelloPolycodeApp.cpp index 4e9255403..5e6020782 100644 --- a/Assets/Templates/C++/Linux/HelloPolycodeApp.cpp +++ b/Assets/Templates/C++/Linux/HelloPolycodeApp.cpp @@ -7,9 +7,7 @@ HelloPolycodeApp::HelloPolycodeApp(PolycodeView *view) : EventHandler() { CoreServices::getInstance()->getResourceManager()->addArchive("default.pak"); CoreServices::getInstance()->getResourceManager()->addDirResource("default", false); - Screen *screen = new Screen(); - ScreenLabel *label = new ScreenLabel("Hello, Polycode!", 32); - screen->addChild(label); + // Write your code here } HelloPolycodeApp::~HelloPolycodeApp() { diff --git a/Assets/Templates/C++/Linux/Makefile b/Assets/Templates/C++/Linux/Makefile index f11aa92c0..7bb1f453f 100644 --- a/Assets/Templates/C++/Linux/Makefile +++ b/Assets/Templates/C++/Linux/Makefile @@ -1,6 +1,6 @@ CC=g++ -CFLAGS=-I../../Core/Dependencies/include -I../../Core/Dependencies/include/AL -I../../Core/include -I../../Modules/include -I../../Modules/Dependencies/include -I../../Modules/Dependencies/include/bullet -LDFLAGS=-lrt -ldl -lpthread ../../Core/lib/libPolycore.a ../../Core/Dependencies/lib/libfreetype.a ../../Core/Dependencies/lib/liblibvorbisfile.a ../../Core/Dependencies/lib/liblibvorbis.a ../../Core/Dependencies/lib/liblibogg.a ../../Core/Dependencies/lib/libopenal.so ../../Core/Dependencies/lib/libphysfs.a ../../Core/Dependencies/lib/libpng15.a ../../Core/Dependencies/lib/libz.a -lGL -lGLU -lSDL ../../Modules/lib/libPolycode2DPhysics.a ../../Modules/Dependencies/lib/libBox2D.a ../../Modules/lib/libPolycode3DPhysics.a ../../Modules/Dependencies/lib/libBulletDynamics.a ../../Modules/Dependencies/lib/libBulletCollision.a ../../Modules/Dependencies/lib/libLinearMath.a +CFLAGS=-I../../Core/Dependencies/include -I../../Core/Dependencies/include/AL -I../../Core/Dependencies/include/freetype2 -I../../Core/include -I../../Modules/include -I../../Modules/Dependencies/include -I../../Modules/Dependencies/include/bullet +LDFLAGS=-lrt -ldl -lpthread ../../Core/lib/libPolycore.a ../../Core/Dependencies/lib/libfreetype.a ../../Core/Dependencies/lib/liblibvorbisfile.a ../../Core/Dependencies/lib/liblibvorbis.a ../../Core/Dependencies/lib/liblibogg.a ../../Core/Dependencies/lib/libopenal.so ../../Core/Dependencies/lib/libphysfs.a ../../Core/Dependencies/lib/libpng15.a ../../Core/Dependencies/lib/libz.a -lGL -lGLU -lSDL ../../Modules/lib/libPolycode2DPhysics.a ../../Modules/Dependencies/lib/libBox2D.a ../../Modules/lib/libPolycode3DPhysics.a ../../Modules/Dependencies/lib/libBulletDynamics.a ../../Modules/Dependencies/lib/libBulletCollision.a ../../Modules/Dependencies/lib/libLinearMath.a -lX11 default: $(CC) $(CFLAGS) main.cpp HelloPolycodeApp.cpp -o PolycodeTemplate $(LDFLAGS) diff --git a/Assets/Templates/C++/Windows/Polycode.props b/Assets/Templates/C++/Windows/Polycode.props index 05b62a6b5..8b6726785 100644 --- a/Assets/Templates/C++/Windows/Polycode.props +++ b/Assets/Templates/C++/Windows/Polycode.props @@ -13,7 +13,7 @@ $(PolycodeCoreLibsRelease);$(PolycodeDependLibsRelease);$(PolycodeWinLibsRelease) - $(PolycodeDir)Core\include;$(PolycodeDir)Core\Dependencies\include;$(PolycodeDir)Core\PolycodeView;$(PolycodeDir)Core\Dependencies\include\AL;$(IncludePath) + $(PolycodeDir)Core\include;$(PolycodeDir)Core\Dependencies\include;$(PolycodeDir)Core\PolycodeView;$(PolycodeDir)Core\Dependencies\include\AL;$(PolycodeDir)Core\Dependencies\include\freetype2;$(IncludePath) $(PolycodeDir)Core\lib;$(PolycodeDir)Core\Dependencies\lib;$(PolycodeDir)Modules\lib;$(PolycodeDir)Modules\Dependencies\lib;$(LibraryPath) diff --git a/Assets/Templates/C++/Windows/PolycodeTemplate.cpp b/Assets/Templates/C++/Windows/PolycodeTemplate.cpp index 867c05171..a88e14070 100644 --- a/Assets/Templates/C++/Windows/PolycodeTemplate.cpp +++ b/Assets/Templates/C++/Windows/PolycodeTemplate.cpp @@ -12,7 +12,7 @@ int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLi MSG Msg; do { - if(PeekMessage(&Msg, NULL, 0,0,PM_REMOVE)) { + while (PeekMessage(&Msg, NULL, 0, 0, PM_REMOVE)) { TranslateMessage(&Msg); DispatchMessage(&Msg); } diff --git a/Assets/Templates/C++/Xcode/PolycodeTemplate.xcodeproj/project.pbxproj b/Assets/Templates/C++/Xcode/PolycodeTemplate.xcodeproj/project.pbxproj index e7d808768..5581bc01c 100644 --- a/Assets/Templates/C++/Xcode/PolycodeTemplate.xcodeproj/project.pbxproj +++ b/Assets/Templates/C++/Xcode/PolycodeTemplate.xcodeproj/project.pbxproj @@ -300,6 +300,7 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LIBRARY = "libc++"; COPY_PHASE_STRIP = NO; GCC_DYNAMIC_NO_PIC = NO; GCC_ENABLE_OBJC_EXCEPTIONS = YES; @@ -310,6 +311,7 @@ "\"$(SRCROOT)/../../Core/Dependencies/Include\"", /Developer/SDKs/MacOSX10.6.sdk/System/Library/Frameworks/OpenAL.framework/Headers, /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.7.sdk/System/Library/Frameworks/OpenAL.framework/Headers, + "\"$(SRCROOT)/../../Core/Dependencies/include/freetype2\"", ); INFOPLIST_FILE = "PolycodeTemplate/PolycodeTemplate-Info.plist"; LIBRARY_SEARCH_PATHS = ( @@ -318,6 +320,7 @@ "\"$(SRCROOT)/../../Dependencies/Lib\"", "\"$(SRCROOT)/../../Core/Dependencies/lib\"", ); + MACOSX_DEPLOYMENT_TARGET = 10.7; PRODUCT_NAME = "$(TARGET_NAME)"; WRAPPER_EXTENSION = app; }; @@ -327,6 +330,7 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LIBRARY = "libc++"; COPY_PHASE_STRIP = YES; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; GCC_ENABLE_OBJC_EXCEPTIONS = YES; @@ -337,6 +341,7 @@ "\"$(SRCROOT)/../../Core/Dependencies/Include\"", /Developer/SDKs/MacOSX10.6.sdk/System/Library/Frameworks/OpenAL.framework/Headers, /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.7.sdk/System/Library/Frameworks/OpenAL.framework/Headers, + "\"$(SRCROOT)/../../Core/Dependencies/include/freetype2\"", ); INFOPLIST_FILE = "PolycodeTemplate/PolycodeTemplate-Info.plist"; LIBRARY_SEARCH_PATHS = ( @@ -345,6 +350,7 @@ "\"$(SRCROOT)/../../Dependencies/Lib\"", "\"$(SRCROOT)/../../Core/Dependencies/lib\"", ); + MACOSX_DEPLOYMENT_TARGET = 10.7; PRODUCT_NAME = "$(TARGET_NAME)"; WRAPPER_EXTENSION = app; }; diff --git a/Assets/Templates/C++/Xcode/PolycodeTemplate.xcodeproj/xcuserdata/ivansafrin.xcuserdatad/xcschemes/PolycodeTemplate.xcscheme b/Assets/Templates/C++/Xcode/PolycodeTemplate.xcodeproj/xcuserdata/ivansafrin.xcuserdatad/xcschemes/PolycodeTemplate.xcscheme index a601765ee..b9bee1ad0 100644 --- a/Assets/Templates/C++/Xcode/PolycodeTemplate.xcodeproj/xcuserdata/ivansafrin.xcuserdatad/xcschemes/PolycodeTemplate.xcscheme +++ b/Assets/Templates/C++/Xcode/PolycodeTemplate.xcodeproj/xcuserdata/ivansafrin.xcuserdatad/xcschemes/PolycodeTemplate.xcscheme @@ -1,6 +1,6 @@ + version = "1.8"> @@ -22,21 +22,24 @@ + buildConfiguration = "Debug" + ignoresPersistentStateOnLaunch = "NO" + debugDocumentVersioning = "YES" + allowLocationSimulation = "YES"> + buildConfiguration = "Release" + debugDocumentVersioning = "YES"> + + + 1.0 + + sans + 0x161616ff + + 0x494949ff + 0x36526aff + 0x232323ff + 0x3a3a3aff + 0x545454FF + 0x00000080 + 0x00000000 + + sans + mono + 12 + 12 + -2 + 12 + UIThemes/dark/arrowIcon.png + 20 + 2 + 2 + 3 + + UIThemes/dark/textfield.png + UIThemes/dark/textfieldMulti.png + 7 + 7 + 7 + 7 + 6 + 14 + + tUIThemes/dark/reeCellBg.png + 0 + 3 + 3 + 3 + 3 + 0xc6c6c6ff + UIThemes/dark/selector.png + 0 + 4 + 3 + 4 + 0 + + UIThemes/dark/treeBg.png + 0 + 2 + 2 + 2 + 2 + + 19 + 0 + + UIThemes/dark/button.png + UIThemes/dark/buttonFocused.png + 12 + 0xffffffff + 0 + 4 + 12 + 12 + 12 + 12 + + UIThemes/dark/windowBg.png + 24 + 17 + 17 + 17 + 15 + sans + 0x000000c8 + 12 + 15 + 10 + 25 + 5 + UIThemes/dark/closeIcon.png + 6 + 6 + + UIThemes/dark/scrollBg.png + 1 + 8 + 8 + 8 + 8 + + UIThemes/dark/scrollHandle.png + 6 + 6 + 6 + 6 + + 30 + 27 + + sans + 12 + UIThemes/dark/checkboxChecked.png + UIThemes/dark/checkboxUnchecked.png + 4 + 3 + + sans + 12 + UIThemes/dark/comboBoxDrop.png + UIThemes/dark/comboBoxBg.png + 24 + 8 + 8 + 8 + 8 + 6 + 4 + 7 + 4 + + + sans + 12 + 10 + -1 + 3 + 10 + 22 + UIThemes/dark/menuBg.png + 7 + 7 + 11 + 7 + UIThemes/dark/menuSelector.png + 0 + 0 + 0 + 0 + 3 + + UIThemes/dark/iconSelectorBg.png + 7 + 7 + 7 + 7 + 7 + 0 + UIThemes/dark/iconSelectorSelection.png + 24 + 0 + 0 + 0 + 0 + + UIThemes/dark/colorboxFrame.png + 10 + 10 + 10 + 10 + UIThemes/dark/colorboxBg.png + 4 + + UIThemes/dark/colorPickerMainBg.png + UIThemes/dark/colorPickerMainFrame.png + UIThemes/dark/colorPickerHue.png + UIThemes/dark/colorPickerHueSelector.png + UIThemes/dark/colorPickerMainTarget.png + + UIThemes/dark/hsliderHandle.png + UIThemes/dark/hsliderBg.png + 16 + 8 + 7 + 7 + 7 + 7 + + UIThemes/dark/file.png + UIThemes/dark/folder.png + UIThemes/dark/boxIcon.png + diff --git a/Assets/UIThemes/dark/treeBg.png b/Assets/UIThemes/dark/treeBg.png new file mode 100644 index 000000000..c15292f47 Binary files /dev/null and b/Assets/UIThemes/dark/treeBg.png differ diff --git a/Assets/UIThemes/dark/treeCellBg.png b/Assets/UIThemes/dark/treeCellBg.png new file mode 100644 index 000000000..3c9e3ed01 Binary files /dev/null and b/Assets/UIThemes/dark/treeCellBg.png differ diff --git a/Assets/UIThemes/dark/windowBg.png b/Assets/UIThemes/dark/windowBg.png new file mode 100644 index 000000000..2b706f523 Binary files /dev/null and b/Assets/UIThemes/dark/windowBg.png differ diff --git a/Assets/UIThemes/dark_retina/arrowIcon.png b/Assets/UIThemes/dark_retina/arrowIcon.png new file mode 100644 index 000000000..60d702d98 Binary files /dev/null and b/Assets/UIThemes/dark_retina/arrowIcon.png differ diff --git a/Assets/UIThemes/dark_retina/boxIcon.png b/Assets/UIThemes/dark_retina/boxIcon.png new file mode 100644 index 000000000..203447250 Binary files /dev/null and b/Assets/UIThemes/dark_retina/boxIcon.png differ diff --git a/Assets/UIThemes/dark_retina/button.png b/Assets/UIThemes/dark_retina/button.png new file mode 100644 index 000000000..ab6f0c918 Binary files /dev/null and b/Assets/UIThemes/dark_retina/button.png differ diff --git a/Assets/UIThemes/dark_retina/buttonFocused.png b/Assets/UIThemes/dark_retina/buttonFocused.png new file mode 100644 index 000000000..7e8e0e065 Binary files /dev/null and b/Assets/UIThemes/dark_retina/buttonFocused.png differ diff --git a/Assets/UIThemes/dark_retina/checkboxChecked.png b/Assets/UIThemes/dark_retina/checkboxChecked.png new file mode 100644 index 000000000..6cfd48a7b Binary files /dev/null and b/Assets/UIThemes/dark_retina/checkboxChecked.png differ diff --git a/Assets/UIThemes/dark_retina/checkboxUnchecked.png b/Assets/UIThemes/dark_retina/checkboxUnchecked.png new file mode 100644 index 000000000..240a4c827 Binary files /dev/null and b/Assets/UIThemes/dark_retina/checkboxUnchecked.png differ diff --git a/Assets/UIThemes/dark_retina/closeIcon.png b/Assets/UIThemes/dark_retina/closeIcon.png new file mode 100644 index 000000000..6d6d74b02 Binary files /dev/null and b/Assets/UIThemes/dark_retina/closeIcon.png differ diff --git a/Assets/UIThemes/dark_retina/colorPickerHue.png b/Assets/UIThemes/dark_retina/colorPickerHue.png new file mode 100644 index 000000000..dd369e5c7 Binary files /dev/null and b/Assets/UIThemes/dark_retina/colorPickerHue.png differ diff --git a/Assets/UIThemes/dark_retina/colorPickerHueSelector.png b/Assets/UIThemes/dark_retina/colorPickerHueSelector.png new file mode 100644 index 000000000..85b0c0efd Binary files /dev/null and b/Assets/UIThemes/dark_retina/colorPickerHueSelector.png differ diff --git a/Assets/UIThemes/dark_retina/colorPickerMainBg.png b/Assets/UIThemes/dark_retina/colorPickerMainBg.png new file mode 100644 index 000000000..b31aeaa59 Binary files /dev/null and b/Assets/UIThemes/dark_retina/colorPickerMainBg.png differ diff --git a/Assets/UIThemes/dark_retina/colorPickerMainFrame.png b/Assets/UIThemes/dark_retina/colorPickerMainFrame.png new file mode 100644 index 000000000..7ac8f014e Binary files /dev/null and b/Assets/UIThemes/dark_retina/colorPickerMainFrame.png differ diff --git a/Assets/UIThemes/dark_retina/colorPickerMainTarget.png b/Assets/UIThemes/dark_retina/colorPickerMainTarget.png new file mode 100644 index 000000000..ce68e8d00 Binary files /dev/null and b/Assets/UIThemes/dark_retina/colorPickerMainTarget.png differ diff --git a/Assets/UIThemes/dark_retina/colorboxBg.png b/Assets/UIThemes/dark_retina/colorboxBg.png new file mode 100644 index 000000000..6fcc288a4 Binary files /dev/null and b/Assets/UIThemes/dark_retina/colorboxBg.png differ diff --git a/Assets/UIThemes/dark_retina/colorboxFrame.png b/Assets/UIThemes/dark_retina/colorboxFrame.png new file mode 100644 index 000000000..2d9f2c830 Binary files /dev/null and b/Assets/UIThemes/dark_retina/colorboxFrame.png differ diff --git a/Assets/UIThemes/dark_retina/comboBoxBg.png b/Assets/UIThemes/dark_retina/comboBoxBg.png new file mode 100644 index 000000000..15c74d392 Binary files /dev/null and b/Assets/UIThemes/dark_retina/comboBoxBg.png differ diff --git a/Assets/UIThemes/dark_retina/comboBoxDrop.png b/Assets/UIThemes/dark_retina/comboBoxDrop.png new file mode 100644 index 000000000..20407628c Binary files /dev/null and b/Assets/UIThemes/dark_retina/comboBoxDrop.png differ diff --git a/Assets/UIThemes/dark_retina/file.png b/Assets/UIThemes/dark_retina/file.png new file mode 100644 index 000000000..f2b33af83 Binary files /dev/null and b/Assets/UIThemes/dark_retina/file.png differ diff --git a/Assets/UIThemes/dark_retina/folder.png b/Assets/UIThemes/dark_retina/folder.png new file mode 100644 index 000000000..c853a39da Binary files /dev/null and b/Assets/UIThemes/dark_retina/folder.png differ diff --git a/Assets/UIThemes/dark_retina/hsliderBg.png b/Assets/UIThemes/dark_retina/hsliderBg.png new file mode 100644 index 000000000..4990df201 Binary files /dev/null and b/Assets/UIThemes/dark_retina/hsliderBg.png differ diff --git a/Assets/UIThemes/dark_retina/hsliderHandle.png b/Assets/UIThemes/dark_retina/hsliderHandle.png new file mode 100644 index 000000000..20091daf8 Binary files /dev/null and b/Assets/UIThemes/dark_retina/hsliderHandle.png differ diff --git a/Assets/UIThemes/dark_retina/iconSelectorBg.png b/Assets/UIThemes/dark_retina/iconSelectorBg.png new file mode 100644 index 000000000..c7b54ffd0 Binary files /dev/null and b/Assets/UIThemes/dark_retina/iconSelectorBg.png differ diff --git a/Assets/UIThemes/dark_retina/iconSelectorSelection.png b/Assets/UIThemes/dark_retina/iconSelectorSelection.png new file mode 100644 index 000000000..916fad865 Binary files /dev/null and b/Assets/UIThemes/dark_retina/iconSelectorSelection.png differ diff --git a/Assets/UIThemes/dark_retina/menuBg.png b/Assets/UIThemes/dark_retina/menuBg.png new file mode 100644 index 000000000..a974ed2ae Binary files /dev/null and b/Assets/UIThemes/dark_retina/menuBg.png differ diff --git a/Assets/UIThemes/dark_retina/menuSelector.png b/Assets/UIThemes/dark_retina/menuSelector.png new file mode 100644 index 000000000..f9d778666 Binary files /dev/null and b/Assets/UIThemes/dark_retina/menuSelector.png differ diff --git a/Assets/UIThemes/dark_retina/scrollBg.png b/Assets/UIThemes/dark_retina/scrollBg.png new file mode 100644 index 000000000..a7e1cf5b6 Binary files /dev/null and b/Assets/UIThemes/dark_retina/scrollBg.png differ diff --git a/Assets/UIThemes/dark_retina/scrollHandle.png b/Assets/UIThemes/dark_retina/scrollHandle.png new file mode 100644 index 000000000..e5e1fd976 Binary files /dev/null and b/Assets/UIThemes/dark_retina/scrollHandle.png differ diff --git a/Assets/UIThemes/dark_retina/selector.png b/Assets/UIThemes/dark_retina/selector.png new file mode 100644 index 000000000..d9c00a1db Binary files /dev/null and b/Assets/UIThemes/dark_retina/selector.png differ diff --git a/Assets/UIThemes/dark_retina/textfield.png b/Assets/UIThemes/dark_retina/textfield.png new file mode 100644 index 000000000..15c74d392 Binary files /dev/null and b/Assets/UIThemes/dark_retina/textfield.png differ diff --git a/Assets/UIThemes/dark_retina/textfieldMulti.png b/Assets/UIThemes/dark_retina/textfieldMulti.png new file mode 100644 index 000000000..ca8e22b5a Binary files /dev/null and b/Assets/UIThemes/dark_retina/textfieldMulti.png differ diff --git a/Assets/UIThemes/dark_retina/textfield_focus.png b/Assets/UIThemes/dark_retina/textfield_focus.png new file mode 100644 index 000000000..298ce3fde Binary files /dev/null and b/Assets/UIThemes/dark_retina/textfield_focus.png differ diff --git a/Assets/UIThemes/dark_retina/theme.xml b/Assets/UIThemes/dark_retina/theme.xml new file mode 100644 index 000000000..11584db0b --- /dev/null +++ b/Assets/UIThemes/dark_retina/theme.xml @@ -0,0 +1,187 @@ + + + + 2.0 + + sans + 0x161616ff + + 0x494949ff + 0x36526aff + 0x232323ff + 0x3a3a3aff + 0x545454FF + 0x00000080 + 0x00000000 + + sans + mono + 12 + 12 + -2 + 12 + UIThemes/dark_retina/arrowIcon.png + 20 + 2 + 2 + 3 + + UIThemes/dark_retina/textfield.png + UIThemes/dark_retina/textfieldMulti.png + 7 + 7 + 7 + 7 + 6 + 14 + + tUIThemes/dark_retina/reeCellBg.png + 0 + 3 + 3 + 3 + 3 + 0xc6c6c6ff + UIThemes/dark_retina/selector.png + 0 + 4 + 3 + 4 + 0 + + UIThemes/dark_retina/treeBg.png + 0 + 2 + 2 + 2 + 2 + + 19 + 0 + + UIThemes/dark_retina/button.png + UIThemes/dark_retina/buttonFocused.png + 12 + 0xffffffff + 0 + 4 + 12 + 12 + 12 + 12 + + UIThemes/dark_retina/windowBg.png + 24 + 17 + 17 + 17 + 15 + sans + 0x000000c8 + 12 + 15 + 10 + 25 + 5 + UIThemes/dark_retina/closeIcon.png + 6 + 6 + + UIThemes/dark_retina/scrollBg.png + 1 + 8 + 8 + 8 + 8 + + UIThemes/dark_retina/scrollHandle.png + 6 + 6 + 6 + 6 + + 30 + 27 + + sans + 12 + UIThemes/dark_retina/checkboxChecked.png + UIThemes/dark_retina/checkboxUnchecked.png + 4 + 3 + + sans + 12 + UIThemes/dark_retina/comboBoxDrop.png + UIThemes/dark_retina/comboBoxBg.png + 24 + 8 + 8 + 8 + 8 + 6 + 4 + 7 + 4 + + + sans + 12 + 10 + -1 + 3 + 10 + 22 + UIThemes/dark_retina/menuBg.png + 7 + 7 + 11 + 7 + UIThemes/dark_retina/menuSelector.png + UIThemes/dark_retina/iconSelectorBg.png + 7 + 7 + 7 + 7 + 7 + 0 + UIThemes/dark_retina/iconSelectorSelection.png + 24 + 0 + 0 + 0 + 0 + + 0 + 0 + 0 + 0 + 3 + + UIThemes/dark_retina/colorboxFrame.png + 10 + 10 + 10 + 10 + UIThemes/dark_retina/colorboxBg.png + 4 + + UIThemes/dark_retina/colorPickerMainBg.png + UIThemes/dark_retina/colorPickerMainFrame.png + UIThemes/dark_retina/colorPickerHue.png + UIThemes/dark_retina/colorPickerHueSelector.png + UIThemes/dark_retina/colorPickerMainTarget.png + + UIThemes/dark_retina/hsliderHandle.png + UIThemes/dark_retina/hsliderBg.png + 16 + 8 + 7 + 7 + 7 + 7 + + UIThemes/dark_retina/file.png + UIThemes/dark_retina/folder.png + UIThemes/dark_retina/boxIcon.png + diff --git a/Assets/UIThemes/dark_retina/treeBg.png b/Assets/UIThemes/dark_retina/treeBg.png new file mode 100644 index 000000000..d9ace8f2a Binary files /dev/null and b/Assets/UIThemes/dark_retina/treeBg.png differ diff --git a/Assets/UIThemes/dark_retina/treeCellBg.png b/Assets/UIThemes/dark_retina/treeCellBg.png new file mode 100644 index 000000000..cadaa5276 Binary files /dev/null and b/Assets/UIThemes/dark_retina/treeCellBg.png differ diff --git a/Assets/UIThemes/dark_retina/windowBg.png b/Assets/UIThemes/dark_retina/windowBg.png new file mode 100644 index 000000000..870e4f209 Binary files /dev/null and b/Assets/UIThemes/dark_retina/windowBg.png differ diff --git a/Assets/UIThemes/default/arrowIcon.png b/Assets/UIThemes/default/arrowIcon.png index 6c6e88b41..3dc48b89a 100644 Binary files a/Assets/UIThemes/default/arrowIcon.png and b/Assets/UIThemes/default/arrowIcon.png differ diff --git a/Assets/UIThemes/default/boxIcon.png b/Assets/UIThemes/default/boxIcon.png index e09642d81..a45ad1fcb 100644 Binary files a/Assets/UIThemes/default/boxIcon.png and b/Assets/UIThemes/default/boxIcon.png differ diff --git a/Assets/UIThemes/default/button.png b/Assets/UIThemes/default/button.png index a25973f04..577c7b04c 100644 Binary files a/Assets/UIThemes/default/button.png and b/Assets/UIThemes/default/button.png differ diff --git a/Assets/UIThemes/default/buttonFocused.png b/Assets/UIThemes/default/buttonFocused.png index e5f1d2be4..2ef3c14a3 100644 Binary files a/Assets/UIThemes/default/buttonFocused.png and b/Assets/UIThemes/default/buttonFocused.png differ diff --git a/Assets/UIThemes/default/checkboxChecked.png b/Assets/UIThemes/default/checkboxChecked.png new file mode 100644 index 000000000..eabb077a2 Binary files /dev/null and b/Assets/UIThemes/default/checkboxChecked.png differ diff --git a/Assets/UIThemes/default/checkboxUnchecked.png b/Assets/UIThemes/default/checkboxUnchecked.png new file mode 100644 index 000000000..1593a62b4 Binary files /dev/null and b/Assets/UIThemes/default/checkboxUnchecked.png differ diff --git a/Assets/UIThemes/default/checkbox_checked.png b/Assets/UIThemes/default/checkbox_checked.png deleted file mode 100644 index 0fb1678be..000000000 Binary files a/Assets/UIThemes/default/checkbox_checked.png and /dev/null differ diff --git a/Assets/UIThemes/default/checkbox_unchecked.png b/Assets/UIThemes/default/checkbox_unchecked.png deleted file mode 100644 index b4a6a3d90..000000000 Binary files a/Assets/UIThemes/default/checkbox_unchecked.png and /dev/null differ diff --git a/Assets/UIThemes/default/closeIcon.png b/Assets/UIThemes/default/closeIcon.png index 41ec00a82..8e217b994 100644 Binary files a/Assets/UIThemes/default/closeIcon.png and b/Assets/UIThemes/default/closeIcon.png differ diff --git a/Assets/UIThemes/default/colorPickerHue.png b/Assets/UIThemes/default/colorPickerHue.png index 54aaa33ab..25df07a14 100644 Binary files a/Assets/UIThemes/default/colorPickerHue.png and b/Assets/UIThemes/default/colorPickerHue.png differ diff --git a/Assets/UIThemes/default/colorPickerHueSelector.png b/Assets/UIThemes/default/colorPickerHueSelector.png index 07af79179..19f0557aa 100644 Binary files a/Assets/UIThemes/default/colorPickerHueSelector.png and b/Assets/UIThemes/default/colorPickerHueSelector.png differ diff --git a/Assets/UIThemes/default/colorPickerMainBg.png b/Assets/UIThemes/default/colorPickerMainBg.png index aff2e983c..e29f8216c 100644 Binary files a/Assets/UIThemes/default/colorPickerMainBg.png and b/Assets/UIThemes/default/colorPickerMainBg.png differ diff --git a/Assets/UIThemes/default/colorPickerMainFrame.png b/Assets/UIThemes/default/colorPickerMainFrame.png index c5163db41..740b4eece 100644 Binary files a/Assets/UIThemes/default/colorPickerMainFrame.png and b/Assets/UIThemes/default/colorPickerMainFrame.png differ diff --git a/Assets/UIThemes/default/colorPickerMainTarget.png b/Assets/UIThemes/default/colorPickerMainTarget.png new file mode 100644 index 000000000..77e3f58f6 Binary files /dev/null and b/Assets/UIThemes/default/colorPickerMainTarget.png differ diff --git a/Assets/UIThemes/default/colorPickerTarget.png b/Assets/UIThemes/default/colorPickerTarget.png deleted file mode 100644 index 081bd9e3d..000000000 Binary files a/Assets/UIThemes/default/colorPickerTarget.png and /dev/null differ diff --git a/Assets/UIThemes/default/colorboxBg.png b/Assets/UIThemes/default/colorboxBg.png index 7a9441623..128a4441c 100644 Binary files a/Assets/UIThemes/default/colorboxBg.png and b/Assets/UIThemes/default/colorboxBg.png differ diff --git a/Assets/UIThemes/default/colorboxFrame.png b/Assets/UIThemes/default/colorboxFrame.png index 62d88b211..76c64e964 100644 Binary files a/Assets/UIThemes/default/colorboxFrame.png and b/Assets/UIThemes/default/colorboxFrame.png differ diff --git a/Assets/UIThemes/default/comboBoxBg.png b/Assets/UIThemes/default/comboBoxBg.png new file mode 100644 index 000000000..b9144d682 Binary files /dev/null and b/Assets/UIThemes/default/comboBoxBg.png differ diff --git a/Assets/UIThemes/default/comboBoxDrop.png b/Assets/UIThemes/default/comboBoxDrop.png new file mode 100644 index 000000000..bd9b9ce40 Binary files /dev/null and b/Assets/UIThemes/default/comboBoxDrop.png differ diff --git a/Assets/UIThemes/default/combobox_bg.png b/Assets/UIThemes/default/combobox_bg.png deleted file mode 100644 index 1c3883adc..000000000 Binary files a/Assets/UIThemes/default/combobox_bg.png and /dev/null differ diff --git a/Assets/UIThemes/default/combobox_drop.png b/Assets/UIThemes/default/combobox_drop.png deleted file mode 100644 index be5621cb5..000000000 Binary files a/Assets/UIThemes/default/combobox_drop.png and /dev/null differ diff --git a/Assets/UIThemes/default/file.png b/Assets/UIThemes/default/file.png index e57153f8d..6163c5f15 100644 Binary files a/Assets/UIThemes/default/file.png and b/Assets/UIThemes/default/file.png differ diff --git a/Assets/UIThemes/default/folder.png b/Assets/UIThemes/default/folder.png index 5d655373f..f37208ef0 100644 Binary files a/Assets/UIThemes/default/folder.png and b/Assets/UIThemes/default/folder.png differ diff --git a/Assets/UIThemes/default/hsliderBg.png b/Assets/UIThemes/default/hsliderBg.png index ee9ca6fbc..23c458307 100644 Binary files a/Assets/UIThemes/default/hsliderBg.png and b/Assets/UIThemes/default/hsliderBg.png differ diff --git a/Assets/UIThemes/default/hsliderHandle.png b/Assets/UIThemes/default/hsliderHandle.png index 55a816444..246b96836 100644 Binary files a/Assets/UIThemes/default/hsliderHandle.png and b/Assets/UIThemes/default/hsliderHandle.png differ diff --git a/Assets/UIThemes/default/iconSelectorBg.png b/Assets/UIThemes/default/iconSelectorBg.png new file mode 100644 index 000000000..8b723020b Binary files /dev/null and b/Assets/UIThemes/default/iconSelectorBg.png differ diff --git a/Assets/UIThemes/default/iconSelectorSelection.png b/Assets/UIThemes/default/iconSelectorSelection.png new file mode 100644 index 000000000..43671a7d9 Binary files /dev/null and b/Assets/UIThemes/default/iconSelectorSelection.png differ diff --git a/Assets/UIThemes/default/menuBg.png b/Assets/UIThemes/default/menuBg.png new file mode 100644 index 000000000..cb03f5b65 Binary files /dev/null and b/Assets/UIThemes/default/menuBg.png differ diff --git a/Assets/UIThemes/default/menuSelector.png b/Assets/UIThemes/default/menuSelector.png new file mode 100644 index 000000000..f4345b7a4 Binary files /dev/null and b/Assets/UIThemes/default/menuSelector.png differ diff --git a/Assets/UIThemes/default/menu_bg.png b/Assets/UIThemes/default/menu_bg.png deleted file mode 100644 index 94b2ff723..000000000 Binary files a/Assets/UIThemes/default/menu_bg.png and /dev/null differ diff --git a/Assets/UIThemes/default/menu_selector.png b/Assets/UIThemes/default/menu_selector.png deleted file mode 100644 index cb3ea1efc..000000000 Binary files a/Assets/UIThemes/default/menu_selector.png and /dev/null differ diff --git a/Assets/UIThemes/default/projectIcon.png b/Assets/UIThemes/default/projectIcon.png deleted file mode 100644 index 23bddf8de..000000000 Binary files a/Assets/UIThemes/default/projectIcon.png and /dev/null differ diff --git a/Assets/UIThemes/default/scrollBg.png b/Assets/UIThemes/default/scrollBg.png index 17646b0cd..4a65d13cf 100644 Binary files a/Assets/UIThemes/default/scrollBg.png and b/Assets/UIThemes/default/scrollBg.png differ diff --git a/Assets/UIThemes/default/scrollHandle.png b/Assets/UIThemes/default/scrollHandle.png index 04aaa583e..3d54f3231 100644 Binary files a/Assets/UIThemes/default/scrollHandle.png and b/Assets/UIThemes/default/scrollHandle.png differ diff --git a/Assets/UIThemes/default/selector.png b/Assets/UIThemes/default/selector.png index cb3ea1efc..0bbe38597 100644 Binary files a/Assets/UIThemes/default/selector.png and b/Assets/UIThemes/default/selector.png differ diff --git a/Assets/UIThemes/default/templateIcon.png b/Assets/UIThemes/default/templateIcon.png deleted file mode 100644 index b23e4aac5..000000000 Binary files a/Assets/UIThemes/default/templateIcon.png and /dev/null differ diff --git a/Assets/UIThemes/default/textfield.png b/Assets/UIThemes/default/textfield.png index b64942573..cb47028fa 100644 Binary files a/Assets/UIThemes/default/textfield.png and b/Assets/UIThemes/default/textfield.png differ diff --git a/Assets/UIThemes/default/textfieldMulti.png b/Assets/UIThemes/default/textfieldMulti.png new file mode 100644 index 000000000..6a2a9d893 Binary files /dev/null and b/Assets/UIThemes/default/textfieldMulti.png differ diff --git a/Assets/UIThemes/default/textfield_focus.png b/Assets/UIThemes/default/textfield_focus.png new file mode 100644 index 000000000..c02942179 Binary files /dev/null and b/Assets/UIThemes/default/textfield_focus.png differ diff --git a/Assets/UIThemes/default/textfield_multi.png b/Assets/UIThemes/default/textfield_multi.png deleted file mode 100644 index c20466f59..000000000 Binary files a/Assets/UIThemes/default/textfield_multi.png and /dev/null differ diff --git a/Assets/UIThemes/default/theme.xml b/Assets/UIThemes/default/theme.xml index 80d476b0a..557a7dd81 100644 --- a/Assets/UIThemes/default/theme.xml +++ b/Assets/UIThemes/default/theme.xml @@ -1,29 +1,38 @@ + + 1.0 + sans - 0xe4e0e3ab + 0x000000c8 - 0x322d2bff - 0x262120ff - 0x2b2624ff - 0xe4e3e0ad + 0xc8c8c8ff + 0x31687fff + 0x616161ff + 0x868686ff + 0x000000c8 + 0x00000080 + 0x00000000 sans mono 12 12 - 13 + -2 + 12 UIThemes/default/arrowIcon.png 20 - 4 + 2 + 2 + 3 UIThemes/default/textfield.png - UIThemes/default/textfield_multi.png - 9 - 9 - 9 - 9 - 5 + UIThemes/default/textfieldMulti.png + 7 + 7 + 7 + 7 + 6 14 UIThemes/default/treeCellBg.png @@ -32,9 +41,9 @@ 3 3 3 - 0xe4e0e3c8 + 0x000000c8 UIThemes/default/selector.png - 4 + 0 4 3 4 @@ -53,7 +62,7 @@ UIThemes/default/button.png UIThemes/default/buttonFocused.png 12 - 0xe4e0e3c8 + 0xffffffff 0 4 12 @@ -68,15 +77,15 @@ 17 15 sans - 0xe4e0e3c8 - 14 + 0x000000c8 + 12 15 10 25 5 UIThemes/default/closeIcon.png - 11 - 11 + 6 + 6 UIThemes/default/scrollBg.png 1 @@ -96,22 +105,22 @@ sans 12 - UIThemes/default/checkbox_checked.png - UIThemes/default/checkbox_unchecked.png + UIThemes/default/checkboxChecked.png + UIThemes/default/checkboxUnchecked.png 4 3 sans 12 - UIThemes/default/combobox_drop.png - UIThemes/default/combobox_bg.png + UIThemes/default/comboBoxDrop.png + UIThemes/default/comboBoxBg.png 24 - 6 - 6 - 6 - 6 + 8 + 8 + 8 + 8 6 - 5 + 4 7 4 @@ -123,23 +132,37 @@ 3 10 22 - UIThemes/default/menu_bg.png - 4 - 6 - 9 - 6 - UIThemes/default/menu_selector.png + UIThemes/default/menuBg.png + 7 + 7 + 11 + 7 + UIThemes/default/menuSelector.png 0 0 0 0 3 + UIThemes/default/iconSelectorBg.png + 7 + 7 + 7 + 7 + 7 + 0 + UIThemes/default/iconSelectorSelection.png + 24 + 0 + 0 + 0 + 0 + UIThemes/default/colorboxFrame.png - 7 - 7 - 7 - 7 + 10 + 10 + 10 + 10 UIThemes/default/colorboxBg.png 4 @@ -147,15 +170,16 @@ UIThemes/default/colorPickerMainFrame.png UIThemes/default/colorPickerHue.png UIThemes/default/colorPickerHueSelector.png - UIThemes/default/colorPickerTarget.png + UIThemes/default/colorPickerMainTarget.png UIThemes/default/hsliderHandle.png UIThemes/default/hsliderBg.png - 10 - 5 - 6 - 5 - 6 + 16 + 8 + 7 + 7 + 7 + 7 UIThemes/default/file.png UIThemes/default/folder.png diff --git a/Assets/UIThemes/default/treeBg.png b/Assets/UIThemes/default/treeBg.png index 877ad45ea..24bce3620 100644 Binary files a/Assets/UIThemes/default/treeBg.png and b/Assets/UIThemes/default/treeBg.png differ diff --git a/Assets/UIThemes/default/treeCellBg.png b/Assets/UIThemes/default/treeCellBg.png index 245d0891f..ac44da9fa 100644 Binary files a/Assets/UIThemes/default/treeCellBg.png and b/Assets/UIThemes/default/treeCellBg.png differ diff --git a/Assets/UIThemes/default/windowBg.png b/Assets/UIThemes/default/windowBg.png index dac409d3d..8f5e6524e 100644 Binary files a/Assets/UIThemes/default/windowBg.png and b/Assets/UIThemes/default/windowBg.png differ diff --git a/Assets/UIThemes/default_retina/arrowIcon.png b/Assets/UIThemes/default_retina/arrowIcon.png new file mode 100644 index 000000000..e63f12a7a Binary files /dev/null and b/Assets/UIThemes/default_retina/arrowIcon.png differ diff --git a/Assets/UIThemes/default_retina/boxIcon.png b/Assets/UIThemes/default_retina/boxIcon.png new file mode 100644 index 000000000..203447250 Binary files /dev/null and b/Assets/UIThemes/default_retina/boxIcon.png differ diff --git a/Assets/UIThemes/default_retina/button.png b/Assets/UIThemes/default_retina/button.png new file mode 100644 index 000000000..53214f035 Binary files /dev/null and b/Assets/UIThemes/default_retina/button.png differ diff --git a/Assets/UIThemes/default_retina/buttonFocused.png b/Assets/UIThemes/default_retina/buttonFocused.png new file mode 100644 index 000000000..249aa55b5 Binary files /dev/null and b/Assets/UIThemes/default_retina/buttonFocused.png differ diff --git a/Assets/UIThemes/default_retina/checkboxChecked.png b/Assets/UIThemes/default_retina/checkboxChecked.png new file mode 100644 index 000000000..bb7a142e2 Binary files /dev/null and b/Assets/UIThemes/default_retina/checkboxChecked.png differ diff --git a/Assets/UIThemes/default_retina/checkboxUnchecked.png b/Assets/UIThemes/default_retina/checkboxUnchecked.png new file mode 100644 index 000000000..d7b9edbcd Binary files /dev/null and b/Assets/UIThemes/default_retina/checkboxUnchecked.png differ diff --git a/Assets/UIThemes/default_retina/closeIcon.png b/Assets/UIThemes/default_retina/closeIcon.png new file mode 100644 index 000000000..3aee216d0 Binary files /dev/null and b/Assets/UIThemes/default_retina/closeIcon.png differ diff --git a/Assets/UIThemes/default_retina/colorPickerHue.png b/Assets/UIThemes/default_retina/colorPickerHue.png new file mode 100644 index 000000000..25f42fade Binary files /dev/null and b/Assets/UIThemes/default_retina/colorPickerHue.png differ diff --git a/Assets/UIThemes/default_retina/colorPickerHueSelector.png b/Assets/UIThemes/default_retina/colorPickerHueSelector.png new file mode 100644 index 000000000..a8ebb7d59 Binary files /dev/null and b/Assets/UIThemes/default_retina/colorPickerHueSelector.png differ diff --git a/Assets/UIThemes/default_retina/colorPickerMainBg.png b/Assets/UIThemes/default_retina/colorPickerMainBg.png new file mode 100644 index 000000000..b31aeaa59 Binary files /dev/null and b/Assets/UIThemes/default_retina/colorPickerMainBg.png differ diff --git a/Assets/UIThemes/default_retina/colorPickerMainFrame.png b/Assets/UIThemes/default_retina/colorPickerMainFrame.png new file mode 100644 index 000000000..3e4773dec Binary files /dev/null and b/Assets/UIThemes/default_retina/colorPickerMainFrame.png differ diff --git a/Assets/UIThemes/default_retina/colorPickerMainTarget.png b/Assets/UIThemes/default_retina/colorPickerMainTarget.png new file mode 100644 index 000000000..ce68e8d00 Binary files /dev/null and b/Assets/UIThemes/default_retina/colorPickerMainTarget.png differ diff --git a/Assets/UIThemes/default_retina/colorboxBg.png b/Assets/UIThemes/default_retina/colorboxBg.png new file mode 100644 index 000000000..6fcc288a4 Binary files /dev/null and b/Assets/UIThemes/default_retina/colorboxBg.png differ diff --git a/Assets/UIThemes/default_retina/colorboxFrame.png b/Assets/UIThemes/default_retina/colorboxFrame.png new file mode 100644 index 000000000..bc8b004ec Binary files /dev/null and b/Assets/UIThemes/default_retina/colorboxFrame.png differ diff --git a/Assets/UIThemes/default_retina/comboBoxBg.png b/Assets/UIThemes/default_retina/comboBoxBg.png new file mode 100644 index 000000000..a52374eee Binary files /dev/null and b/Assets/UIThemes/default_retina/comboBoxBg.png differ diff --git a/Assets/UIThemes/default_retina/comboBoxDrop.png b/Assets/UIThemes/default_retina/comboBoxDrop.png new file mode 100644 index 000000000..abe5adc2a Binary files /dev/null and b/Assets/UIThemes/default_retina/comboBoxDrop.png differ diff --git a/Assets/UIThemes/default_retina/file.png b/Assets/UIThemes/default_retina/file.png new file mode 100644 index 000000000..f2b33af83 Binary files /dev/null and b/Assets/UIThemes/default_retina/file.png differ diff --git a/Assets/UIThemes/default_retina/folder.png b/Assets/UIThemes/default_retina/folder.png new file mode 100644 index 000000000..c853a39da Binary files /dev/null and b/Assets/UIThemes/default_retina/folder.png differ diff --git a/Assets/UIThemes/default_retina/hsliderBg.png b/Assets/UIThemes/default_retina/hsliderBg.png new file mode 100644 index 000000000..ad2b36ae8 Binary files /dev/null and b/Assets/UIThemes/default_retina/hsliderBg.png differ diff --git a/Assets/UIThemes/default_retina/hsliderHandle.png b/Assets/UIThemes/default_retina/hsliderHandle.png new file mode 100644 index 000000000..76d3277c6 Binary files /dev/null and b/Assets/UIThemes/default_retina/hsliderHandle.png differ diff --git a/Assets/UIThemes/default_retina/iconSelectorBg.png b/Assets/UIThemes/default_retina/iconSelectorBg.png new file mode 100644 index 000000000..4db5efb80 Binary files /dev/null and b/Assets/UIThemes/default_retina/iconSelectorBg.png differ diff --git a/Assets/UIThemes/default_retina/iconSelectorSelection.png b/Assets/UIThemes/default_retina/iconSelectorSelection.png new file mode 100644 index 000000000..96bd70abe Binary files /dev/null and b/Assets/UIThemes/default_retina/iconSelectorSelection.png differ diff --git a/Assets/UIThemes/default_retina/menuBg.png b/Assets/UIThemes/default_retina/menuBg.png new file mode 100644 index 000000000..12e13782e Binary files /dev/null and b/Assets/UIThemes/default_retina/menuBg.png differ diff --git a/Assets/UIThemes/default_retina/menuSelector.png b/Assets/UIThemes/default_retina/menuSelector.png new file mode 100644 index 000000000..97a3b87ef Binary files /dev/null and b/Assets/UIThemes/default_retina/menuSelector.png differ diff --git a/Assets/UIThemes/default_retina/scrollBg.png b/Assets/UIThemes/default_retina/scrollBg.png new file mode 100644 index 000000000..a7e1cf5b6 Binary files /dev/null and b/Assets/UIThemes/default_retina/scrollBg.png differ diff --git a/Assets/UIThemes/default_retina/scrollHandle.png b/Assets/UIThemes/default_retina/scrollHandle.png new file mode 100644 index 000000000..e5e1fd976 Binary files /dev/null and b/Assets/UIThemes/default_retina/scrollHandle.png differ diff --git a/Assets/UIThemes/default_retina/selector.png b/Assets/UIThemes/default_retina/selector.png new file mode 100644 index 000000000..efafda341 Binary files /dev/null and b/Assets/UIThemes/default_retina/selector.png differ diff --git a/Assets/UIThemes/default_retina/textfield.png b/Assets/UIThemes/default_retina/textfield.png new file mode 100644 index 000000000..af78a6c22 Binary files /dev/null and b/Assets/UIThemes/default_retina/textfield.png differ diff --git a/Assets/UIThemes/default_retina/textfieldMulti.png b/Assets/UIThemes/default_retina/textfieldMulti.png new file mode 100644 index 000000000..ca8e22b5a Binary files /dev/null and b/Assets/UIThemes/default_retina/textfieldMulti.png differ diff --git a/Assets/UIThemes/default_retina/textfield_focus.png b/Assets/UIThemes/default_retina/textfield_focus.png new file mode 100644 index 000000000..5f5c8b602 Binary files /dev/null and b/Assets/UIThemes/default_retina/textfield_focus.png differ diff --git a/Assets/UIThemes/default_retina/theme.xml b/Assets/UIThemes/default_retina/theme.xml new file mode 100644 index 000000000..32db3ec69 --- /dev/null +++ b/Assets/UIThemes/default_retina/theme.xml @@ -0,0 +1,187 @@ + + + + 2.0 + + sans + 0x000000c8 + + 0xc8c8c8ff + 0x31687fff + 0x616161ff + 0x868686ff + 0x000000c8 + 0x00000080 + 0x00000000 + + sans + mono + 12 + 12 + -2 + 12 + UIThemes/default_retina/arrowIcon.png + 20 + 2 + 2 + 3 + + UIThemes/default_retina/textfield.png + UIThemes/default_retina/textfieldMulti.png + 7 + 7 + 7 + 7 + 6 + 14 + + UIThemes/default_retina/treeCellBg.png + 0 + 3 + 3 + 3 + 3 + 0x000000c8 + UIThemes/default_retina/selector.png + 0 + 4 + 3 + 4 + 0 + + UIThemes/default_retina/treeBg.png + 0 + 2 + 2 + 2 + 2 + + 19 + 0 + + UIThemes/default_retina/button.png + UIThemes/default_retina/buttonFocused.png + 12 + 0xffffffff + 0 + 4 + 12 + 12 + 12 + 12 + + UIThemes/default_retina/windowBg.png + 24 + 17 + 17 + 17 + 15 + sans + 0x000000c8 + 12 + 15 + 10 + 25 + 5 + UIThemes/default_retina/closeIcon.png + 6 + 6 + + UIThemes/default_retina/scrollBg.png + 1 + 8 + 8 + 8 + 8 + + UIThemes/default_retina/scrollHandle.png + 6 + 6 + 6 + 6 + + 30 + 27 + + sans + 12 + UIThemes/default_retina/checkboxChecked.png + UIThemes/default_retina/checkboxUnchecked.png + 4 + 3 + + sans + 12 + UIThemes/default_retina/comboBoxDrop.png + UIThemes/default_retina/comboBoxBg.png + 24 + 8 + 8 + 8 + 8 + 6 + 4 + 7 + 4 + + + sans + 12 + 10 + -1 + 3 + 10 + 22 + UIThemes/default_retina/menuBg.png + 7 + 7 + 11 + 7 + UIThemes/default_retina/menuSelector.png + 0 + 0 + 0 + 0 + 3 + + UIThemes/default_retina/iconSelectorBg.png + 7 + 7 + 7 + 7 + 7 + 0 + UIThemes/default_retina/iconSelectorSelection.png + 24 + 0 + 0 + 0 + 0 + + UIThemes/default_retina/colorboxFrame.png + 10 + 10 + 10 + 10 + UIThemes/default_retina/colorboxBg.png + 4 + + UIThemes/default_retina/colorPickerMainBg.png + UIThemes/default_retina/colorPickerMainFrame.png + UIThemes/default_retina/colorPickerHue.png + UIThemes/default_retina/colorPickerHueSelector.png + UIThemes/default_retina/colorPickerMainTarget.png + + UIThemes/default_retina/hsliderHandle.png + UIThemes/default_retina/hsliderBg.png + 16 + 8 + 7 + 7 + 7 + 7 + + UIThemes/default_retina/file.png + UIThemes/default_retina/folder.png + UIThemes/default_retina/boxIcon.png + diff --git a/Assets/UIThemes/default_retina/treeBg.png b/Assets/UIThemes/default_retina/treeBg.png new file mode 100644 index 000000000..75f64f145 Binary files /dev/null and b/Assets/UIThemes/default_retina/treeBg.png differ diff --git a/Assets/UIThemes/default_retina/treeCellBg.png b/Assets/UIThemes/default_retina/treeCellBg.png new file mode 100644 index 000000000..cadaa5276 Binary files /dev/null and b/Assets/UIThemes/default_retina/treeCellBg.png differ diff --git a/Assets/UIThemes/default_retina/windowBg.png b/Assets/UIThemes/default_retina/windowBg.png new file mode 100644 index 000000000..1e21826cc Binary files /dev/null and b/Assets/UIThemes/default_retina/windowBg.png differ diff --git a/BUILD.md b/BUILD.md index 0193e1c3b..697b7a6bf 100644 --- a/BUILD.md +++ b/BUILD.md @@ -91,16 +91,12 @@ You will also need to manually build the "glext" and "wglext" projects. To generate and build Debug and Release builds with Unix Makefiles perform the following steps in the Polycode directory from a terminal: - cd Dependencies - mkdir Build - cd Build - mkdir Debug - cd Debug + mkdir -p Dependencies/Build/Debug + cd Dependencies/Build/Debug cmake -G "Unix Makefiles" -DCMAKE_BUILD_TYPE=Debug ../.. make - cd .. - mkdir Release - cd Release + mkdir ../Release + cd ../Release cmake -G "Unix Makefiles" -DCMAKE_BUILD_TYPE=Release ../.. make @@ -112,7 +108,7 @@ The Polycode CMake build will look for dependencies installed as static libraries in the Release folder by the above dependency build step. It will not use system level versions of these libraries, even if you have them installed. The only exception to -this is SDL for the Linux build, which you must manually install on +this is SDL (1.2) for the Linux build, which you must manually install on the system level. If you want to build documentation, you must have Doxygen installed @@ -157,21 +153,18 @@ templates and examples that will build out of the box. To generate and build Debug and Release builds with Unix Makefiles perform the following steps in the Polycode directory from a terminal: -NOTE: You need to install SDL development libraries on your system +NOTE: You need to install SDL 1.2 development libraries on your system before doing this as they are not automatically installed by the Dependencies project above. You can get SDL from http://www.libsdl.org or using the package manager of your distribution. - mkdir Build - cd Build - mkdir Debug - cd Debug + mkdir -p Build/Debug + cd Build/Debug cmake -G "Unix Makefiles" -DCMAKE_BUILD_TYPE=Debug ../.. make make install - cd .. - mkdir Release - cd Release + mkdir ../Release + cd ../Release cmake -G "Unix Makefiles" -DCMAKE_BUILD_TYPE=Release ../.. make make install diff --git a/BUILD.txt b/BUILD.txt deleted file mode 100644 index 0193e1c3b..000000000 --- a/BUILD.txt +++ /dev/null @@ -1,248 +0,0 @@ -# Building Polycode and dependencies # - -Polycode uses a CMake build generator for automatically downloading and -building required 3rd party packages and Polycode itself. Polycode is -setup for preferring custom static libraries over system ones, so -please use the dependency build system even if you have all of the -dependencies installed on your computer. - -The first dependency is CMake. It can be downloaded from -http://cmake.org/cmake/resources/software.html or installed using apt -or rpm on most Linux distributions. CMake 2.8.8 or greater is now required. - -When Polycode and its Dependencies are built, they will be available -in the Release/YourArchitecture folder under the main source tree in -a structure that should mimic the main binary release. - -If you wish to build a 32-bit version on a 64-bit machine in OS X, pass --DCMAKE_OSX_ARCHITECTURES=i386 as an argument to cmake - -## Building dependencies ## - -Polycode depends on a number of third party packages that are not -included in the Polycode source tree: - -* [Lua](http://www.lua.org/) -* [Freetype](http://www.freetype.org/) -* [zlib](http://www.zlib.net/) -* [libpng](http://www.libpng.org/pub/png/libpng.html) -* [PhysicsFS](http://icculus.org/physfs/) -* [Ogg Vorbis](http://www.vorbis.com/) -* [OpenAL](http://www.openal.org/) -* [SDL](http://www.libsdl.org/) -* [Box2D](http://www.box2d.org/) -* [Bullet Physics](http://bulletphysics.org/) -* [Assimp](http://assimp.sourceforge.net/) - -The CMake dependency build system will download and install static -version of these libraries into the Polycode source tree. It will NOT -attempt to install any of these packages into your system. - -All dependenices will be installed into the Polycode source tree under - Release//Framework/ - -Instructions describe using CMake on the command line, you -may prefer to use the CMake GUI if unfamiliar with CMake. - - -### Mac OS X and Xcode ### - -NOTE: If you are using the new Xcode that is downloaded from the AppStore -and cmake complains about not finding Xcode in /Developer, you have to run this -command to update the Xcode path: -sudo /usr/bin/xcode-select -switch /Applications/Xcode.app/Contents/Developer - -To generate an Xcode project for building Polycode dependencies, perform -the following steps in the Polycode directory from a terminal: - - cd Dependencies - mkdir Build - cd Build - cmake -G Xcode .. - -This generates a PolycodeDependencies Xcode project in the Build -directory. Building this project in Xcode will download, build and -install the dependencies (make sure you build the ALL_BUILD target). -Note that you need to build both Debug and -Release in Xcode - -Note: Release is "Build for Archiving" in Xcode4. - -### Windows and Visual Studio ### - -To generate a Microsoft Visual Studio (any version) project for building -Polycode dependencies, perform the following steps in the Polycode -directory from a command prompt (for VS2010): - - cd Dependencies - mkdir Build - cd Build - cmake -G "Visual Studio 10" .. - -This generates a PolycodeDependencies.sln in the Build directory. -Building the ALL_BUILD project in the solution in Visual Studio will download, build and -install the dependencies. Note that you need to build both Debug and -Release. - -You will also need to manually build the "glext" and "wglext" projects. - -### Unix Makefiles ### - -To generate and build Debug and Release builds with Unix Makefiles -perform the following steps in the Polycode directory from a terminal: - - cd Dependencies - mkdir Build - cd Build - mkdir Debug - cd Debug - cmake -G "Unix Makefiles" -DCMAKE_BUILD_TYPE=Debug ../.. - make - cd .. - mkdir Release - cd Release - cmake -G "Unix Makefiles" -DCMAKE_BUILD_TYPE=Release ../.. - make - -## Building Polycode ## - -### Notes ### - -The Polycode CMake build will look for dependencies installed as -static libraries in the Release folder by the above -dependency build step. It will not use system level versions -of these libraries, even if you have them installed. The only exception to -this is SDL for the Linux build, which you must manually install on -the system level. - -If you want to build documentation, you must have Doxygen installed -and in your run path. You can get Doxygen from http://www.doxygen.org -or install it using a package manager. - -### Mac OS X and Xcode ### - -To generate an Xcode project for building Polycode, perform the -following steps in the Polycode directory from a terminal: - - mkdir Build - cd Build - cmake -G Xcode .. - -This generates a Polycode Xcode project in the Build directory. - -Build the "ALL_BUILD" target in this project in both Debug and Release -and then build the "install" target, also in Debug and Release. This -will install Polycode into the Release/Darwin/Framework directory, -which should mirror the binary download from the website and contain -templates and examples that will build out of the box. - -### Windows and Visual Studio ### - -To generate a Microsoft Visual Studio project for building Polycode, -perform the following steps in the Polycode directory from a -command prompt: - - mkdir Build - cd Build - cmake -G "Visual Studio 10" .. - -Build the "ALL_BUILD" target in this project in both Debug and Release -and then build the "install" target, also in Debug and Release. This -will install Polycode into the Release/Windows/Framework directory, -which should mirror the binary download from the website and contain -templates and examples that will build out of the box. - -### Linux ### - -To generate and build Debug and Release builds with Unix Makefiles -perform the following steps in the Polycode directory from a terminal: - -NOTE: You need to install SDL development libraries on your system -before doing this as they are not automatically installed by the -Dependencies project above. You can get SDL from http://www.libsdl.org -or using the package manager of your distribution. - - mkdir Build - cd Build - mkdir Debug - cd Debug - cmake -G "Unix Makefiles" -DCMAKE_BUILD_TYPE=Debug ../.. - make - make install - cd .. - mkdir Release - cd Release - cmake -G "Unix Makefiles" -DCMAKE_BUILD_TYPE=Release ../.. - make - make install - -This will install Polycode into the Release/Linux/Framework directory, -which should mirror the binary download from the website and contain -templates and examples that will build out of the box. - -## Building Polycode Lua ## - -Note: To build a complete distribution of the standalone Lua tools -as it appears in the binary release, you will need to build it on all -supported platforms and copy the module libraries and deployment -templates from all three into the folder. If you do not have access -to all the supported platforms, you will not be able to build a complete -distribution! - -To build Polycode Lua, you need to build the bindings and the player, -which are disabled by default. To do this, you need to add a couple -of variables to the cmake commands above. To build the bindings, you -need a python installation with the PLY python module. You can get -the PLY module at http://www.dabeaz.com/ply/ - -Note: You will need python 2 for this step. If you have python 3 installed, -pass -DPYTHON_EXECUTABLE=/usr/bin/python2 or whatever the full path to -the python2 executable is on your system. - -To enable the bindings and the player, add the following options to the -cmake command. Otherwise, the steps are exactly the same as the regular -Polycode build for your system. - - -DPOLYCODE_BUILD_BINDINGS=ON -DPOLYCODE_BUILD_PLAYER=ON - -Note: You need to build the "PolycodeLua" target before you build the "install" target. - -After building the install build or running 'make install', perform the -following commands in the Polycode source root: - -### Mac and Linux ### - - cd Standalone - mkdir Build - cd Build - cmake -G "Unix Makefiles" .. - make install - -This will create a Release/YourArchitecture/Standalone folder with the -same structure as the binary Polycode Lua release. - -### Windows ### - - cd Standalone - mkdir Build - cd Build - cmake -G "Visual Studio 10" .. - -This will create a Standalone.sln solution in the Build directory. Build the -"install" target of this solution. - -This will create a Release/Windows/Standalone folder with the -same structure as the binary Polycode Lua release. - -### Final steps ### - -Since the standalone Lua distribution is supposed to compile packages for -all platforms, you need to do this build step on all of the platforms and -merge the Standalone/Modules and Standalone/Publish folders with the one -you just built. - -## TODO ## - -It would be good to create a CMake build template for people to create -new Polycode applications with. - diff --git a/Bindings/Contents/LUA/API/defaults.lua b/Bindings/Contents/LUA/API/defaults.lua index 2151b5e41..4a707fa09 100644 --- a/Bindings/Contents/LUA/API/defaults.lua +++ b/Bindings/Contents/LUA/API/defaults.lua @@ -69,9 +69,6 @@ Services.Config.__ptr = Polycore.CoreServices_getConfig(Polycore.CoreServices_ge Services.MaterialManager = MaterialManager("__skip_ptr__") Services.MaterialManager.__ptr = Polycore.CoreServices_getMaterialManager(Polycore.CoreServices_getInstance()) -Services.ScreenManager = ScreenManager("__skip_ptr__") -Services.ScreenManager.__ptr = Polycore.CoreServices_getScreenManager(Polycore.CoreServices_getInstance()) - Services.SceneManager = SceneManager("__skip_ptr__") Services.SceneManager.__ptr = Polycore.CoreServices_getSceneManager(Polycore.CoreServices_getInstance()) @@ -133,6 +130,9 @@ end function Update(e) end +function fixedUpdate() +end + KEY_UNKNOWN= 0 KEY_FIRST= 0 KEY_BACKSPACE= 8 diff --git a/Bindings/Scripts/create_lua_library/create_lua_library.py b/Bindings/Scripts/create_lua_library/create_lua_library.py index 9b0f9508d..5acb24a9f 100644 --- a/Bindings/Scripts/create_lua_library/create_lua_library.py +++ b/Bindings/Scripts/create_lua_library/create_lua_library.py @@ -16,7 +16,7 @@ def mkdir_p(path): # Same effect as mkdir -p, create dir and all necessary paren else: raise def template_returnPtrLookupArray(prefix, className, ptr): - out = "" + out = "%sif %s == nil then return nil end\n" % (prefix, ptr) out += "%sfor i=1,count(%s) do\n" % (prefix, ptr) out += "%s\tlocal __c = _G[%s](\"__skip_ptr__\")\n" % (prefix, className.replace("*", "")) out += "%s\t__c.__ptr = %s[i]\n" % (prefix, ptr) @@ -27,7 +27,7 @@ def template_returnPtrLookupArray(prefix, className, ptr): # Note we expect className to be a valid string def template_returnPtrLookup(prefix, className, ptr): - out = "" + out = "%sif %s == nil then return nil end\n" % (prefix, ptr) out += "%slocal __c = _G[%s](\"__skip_ptr__\")\n" % (prefix, className.replace("*", "")) out += "%s__c.__ptr = %s\n" % (prefix, ptr) out += "%sreturn __c\n" % (prefix) @@ -52,7 +52,9 @@ def typeFilter(ty): ty = ty.replace("virtual", "") ty = ty.replace("&", "") ty = re.sub(r'^.*\sint\s*$', 'int', ty) # eg "unsigned int" + ty = re.sub(r'^.*\schar\s*$', 'char', ty) # eg "unsigned int" ty = re.sub(r'^.*\slong\s*$', 'int', ty) + ty = re.sub(r'^.*\swchar_t\s*$', 'int', ty) ty = re.sub(r'^.*\sshort\s*$', 'int', ty) ty = re.sub(r'^.*\sfloat\s*$', 'Number', ty) ty = re.sub(r'^.*\sdouble\s*$', 'Number', ty) # eg "long double" @@ -78,9 +80,9 @@ def createLUABindings(inputPath, prefix, mainInclude, libSmallName, libName, api cppRegisterOut += "using namespace Polycode;\n\n" cppRegisterOut += "int luaopen_%s(lua_State *L) {\n" % (prefix) -# if prefix != "Polycode": -# cppRegisterOut += "CoreServices *inst = (CoreServices*) *((void**)lua_touserdata(L, 1));\n" -# cppRegisterOut += "CoreServices::setInstance(inst);\n" + if prefix != "Polycode" and prefix != "Physics2D" and prefix != "Physics3D" and prefix != "UI": + cppRegisterOut += "CoreServices *inst = (CoreServices*) *((PolyBase**)lua_touserdata(L, 1));\n" + cppRegisterOut += "CoreServices::setInstance(inst);\n" cppRegisterOut += "\tstatic const struct luaL_reg %sLib [] = {" % (libSmallName) wrappersHeaderOut += "#pragma once\n\n" @@ -90,6 +92,8 @@ def createLUABindings(inputPath, prefix, mainInclude, libSmallName, libName, api wrappersHeaderOut += "#include \"lua.h\"\n" wrappersHeaderOut += "#include \"lualib.h\"\n" wrappersHeaderOut += "#include \"lauxlib.h\"\n" + wrappersHeaderOut += "#undef near\n" + wrappersHeaderOut += "#undef far\n" wrappersHeaderOut += "} // extern \"C\" \n\n" luaDocOut += "\n" @@ -110,8 +114,10 @@ def createLUABindings(inputPath, prefix, mainInclude, libSmallName, libName, api for fileName in files: if inputPathIsDir: fileName = "%s/%s" % (inputPath, fileName) + if os.path.isdir(fileName): + continue head, tail = os.path.split(fileName) - ignore = ["PolyTween", "PolyTweenManager", "PolyGLSLProgram", "PolyGLSLShader", "PolyGLSLShaderModule", "PolyWinCore", "PolyCocoaCore", "PolyAGLCore", "PolySDLCore", "Poly_iPhone", "PolyGLES1Renderer", "PolyGLRenderer", "tinyxml", "tinystr", "OpenGLCubemap", "PolyiPhoneCore", "PolyGLES1Texture", "PolyGLTexture", "PolyGLVertexBuffer", "PolyThreaded", "PolyGLHeaders", "GLee", "PolyPeer", "PolySocket", "PolyClient", "PolyServer", "PolyServerWorld", "OSFILE", "OSFileEntry", "OSBasics", "PolyLogger"] + ignore = ["PolyTween", "PolyTweenManager", "PolyGLSLProgram", "PolyGLSLShader", "PolyGLSLShaderModule", "PolyWinCore", "PolyCocoaCore", "PolyAGLCore", "PolySDLCore", "Poly_iPhone", "PolyGLES1Renderer", "PolyGLRenderer", "tinyxml", "tinystr", "OpenGLCubemap", "PolyiPhoneCore", "PolyGLES1Texture", "PolyGLTexture", "PolyGLVertexBuffer", "PolyThreaded", "PolyGLHeaders", "GLee", "PolyPeer", "PolySocket", "PolyClient", "PolyServer", "PolyServerWorld", "OSFILE", "OSFileEntry", "OSBasics", "PolyLogger", "PolyFontGlyphSheet"] if tail.split(".")[1] == "h" and tail.split(".")[0] not in ignore: filteredFiles.append(fileName) wrappersHeaderOut += "#include \"%s\"\n" % (tail) @@ -122,7 +128,7 @@ def createLUABindings(inputPath, prefix, mainInclude, libSmallName, libName, api # list of classes that don't get the garbage collection in their meta table - disable_gc = ["Entity", "ScreenEntity", "ScreenShape", "ScreenMesh", "ScreenLine", "ScreenLabel", "SceneLabel", "SceneMesh", "Screen", "Scene", "Texture", "Image", "Camera", "ScreenParticleEmitter", "SceneParticleEmitter", "Mesh", "Vertex", "Polygon", "Polycode::Polygon", "Material", "ScenePrimitive", "SceneLine", "SceneLight", "SceneSound", "ScreenImage", "SceneEntity", "ScreenEntityInstance"] + disable_gc = ["Entity","SceneLabel", "SceneMesh", "Scene", "Texture", "Image", "Camera", "SceneParticleEmitter", "Mesh", "Vertex", "Polygon", "Polycode::Polygon", "Material", "ScenePrimitive", "SceneLine", "SceneLight", "SceneSound", "SceneImage", "SceneEntity", "SceneEntityInstance", "SceneSprite"] # Special case: If we are building the Polycode library itself, inject the LuaEventHandler class. # Note: so that event callbacks can work, any object inheriting from EventHandler will secretly @@ -148,7 +154,7 @@ def createLUABindings(inputPath, prefix, mainInclude, libSmallName, libName, api # Iterate, process each input file for fileName in filteredFiles: # "Package owned" classes that ship with Polycode - inheritInModule = ["PhysicsSceneEntity", "CollisionScene", "CollisionSceneEntity", "UIElement", "UIWindow", "UIMenuItem"] + inheritInModule = ["PhysicsGenericConstraint", "PhysicsHingeConstraint", "PhysicsPointToPointConstraint", "PhysicsConstraint", "PhysicsEntity", "CollisionScene", "CollisionEntity", "UIElement", "UIWindow", "UIMenuItem", "UIImage", "UIRect"] # A file or comma-separated list of files can be given to specify classes which are "package owned" # and should not be inherited out of Polycode/. The files should contain one class name per line, @@ -164,7 +170,7 @@ def createLUABindings(inputPath, prefix, mainInclude, libSmallName, libName, api f = open(fileName) # Def: Input file handle contents = f.read().replace("_PolyExport", "") # Def: Input file contents, strip out "_PolyExport" cppHeader = CppHeaderParser.CppHeader(contents, "string") # Def: Input file contents, parsed structure - ignore_classes = ["PolycodeShaderModule", "Object", "Threaded", "OpenGLCubemap", "PolyBase"] + ignore_classes = ["PolycodeShaderModule", "Object", "Threaded", "OpenGLCubemap", "PolyBase", "Matrix4::union "] # Iterate, check each class in this file. for ckey in cppHeader.classes: @@ -182,9 +188,6 @@ def createLUABindings(inputPath, prefix, mainInclude, libSmallName, libName, api else: # Parent class is in Polycore luaClassBindingOut += "require \"Polycode/%s\"\n\n" % (c["inherits"][0]["class"]) - if (ckey == "ScreenParticleEmitter" or ckey == "SceneParticleEmitter"): - luaClassBindingOut += "require \"Polycode/ParticleEmitter\"\n\n" - luaClassBindingOut += "class \"%s\" (%s)\n\n" % (ckey, c["inherits"][0]["class"]) parentClass = c["inherits"][0]["class"] inherits = True @@ -192,11 +195,12 @@ def createLUABindings(inputPath, prefix, mainInclude, libSmallName, libName, api luaClassBindingOut += "class \"%s\"\n\n" % ckey if ckey in ignore_classes: + print("INGORING class %s" % ckey) continue - if len(c["methods"]["public"]) < 2: # Used to, this was a continue. - print("Warning: Lua-binding class with less than two methods") - continue # FIXME: Remove this, move any non-compileable classes into ignore_classes + #if len(c["methods"]["public"]) < 2: # Used to, this was a continue. + # print("Warning: Lua-binding class with less than two methods") + # continue # FIXME: Remove this, move any non-compileable classes into ignore_classes extendString = "" if len(c["inherits"]) > 0: @@ -316,8 +320,12 @@ def createLUABindings(inputPath, prefix, mainInclude, libSmallName, libName, api wrappersHeaderOut += "\t%s(L, inst->%s%s);\n" % (outfunc, pp["name"], retFunc) else: if pp["type"].find("*") != -1: - wrappersHeaderOut += "\tPolyBase **userdataPtr = (PolyBase**)lua_newuserdata(L, sizeof(PolyBase*));\n" - wrappersHeaderOut += "\t*userdataPtr = (PolyBase*)inst->%s%s;\n" % (pp["name"], retFunc) + wrappersHeaderOut += "\tif(!inst->%s%s) {\n" % (pp["name"], retFunc) + wrappersHeaderOut += "\t\tlua_pushnil(L);\n" + wrappersHeaderOut += "\t} else {\n" + wrappersHeaderOut += "\t\tPolyBase **userdataPtr = (PolyBase**)lua_newuserdata(L, sizeof(PolyBase*));\n" + wrappersHeaderOut += "\t\t*userdataPtr = (PolyBase*)inst->%s%s;\n" % (pp["name"], retFunc) + wrappersHeaderOut += "\t}\n" else: wrappersHeaderOut += "\tPolyBase **userdataPtr = (PolyBase**)lua_newuserdata(L, sizeof(PolyBase*));\n" wrappersHeaderOut += "\t*userdataPtr = (PolyBase*)&inst->%s%s;\n" % (pp["name"], retFunc) @@ -647,7 +655,7 @@ def createLUABindings(inputPath, prefix, mainInclude, libSmallName, libName, api outfunc = "lua_pushstring" basicType = True retFunc = ".c_str()" - if pm["rtnType"] == "int" or pm["rtnType"] == "unsigned int" or pm["rtnType"] == "static int" or pm["rtnType"] == "size_t" or pm["rtnType"] == "static size_t" or pm["rtnType"] == "long" or pm["rtnType"] == "unsigned int" or pm["rtnType"] == "static long" or pm["rtnType"] == "short" or pm["rtnType"] == "PolyKEY": + if pm["rtnType"] == "int" or pm["rtnType"] == "unsigned int" or pm["rtnType"] == "static int" or pm["rtnType"] == "size_t" or pm["rtnType"] == "static size_t" or pm["rtnType"] == "long" or pm["rtnType"] == "unsigned int" or pm["rtnType"] == "static long" or pm["rtnType"] == "short" or pm["rtnType"] == "PolyKEY" or pm["rtnType"] == "wchar_t": outfunc = "lua_pushinteger" basicType = True if pm["rtnType"] == "bool" or pm["rtnType"] == "static bool" or pm["rtnType"] == "virtual bool": @@ -728,7 +736,6 @@ def createLUABindings(inputPath, prefix, mainInclude, libSmallName, libName, api if basicType == True: # Yes, a primitive luaClassBindingOut += "\treturn retVal\n" else: # Yes, a pointer was returned - luaClassBindingOut += "\tif retVal == nil then return nil end\n" if vectorReturn == True: className = vectorReturnClass.replace("*", "") luaClassBindingOut += template_returnPtrLookupArray("\t",template_quote(className),"retVal") diff --git a/BuildLinux.sh b/BuildLinux.sh new file mode 100755 index 000000000..aa2a8eff5 --- /dev/null +++ b/BuildLinux.sh @@ -0,0 +1,21 @@ +#!/bin/bash +# ~/bin/build-polycode + +mkdir -p Dependencies/Build/Debug Dependencies/Build/Release Build/Debug Build/Release Standalone/Build +cd Dependencies/Build/Debug +cmake -G "Unix Makefiles" -DCMAKE_BUILD_TYPE=Debug ../.. +make +cd ../Release +cmake -G "Unix Makefiles" -DCMAKE_BUILD_TYPE=Release ../.. +make +cd ../../../Build/Debug +cmake -G "Unix Makefiles" -DPOLYCODE_BUILD_BINDINGS=ON -DPOLYCODE_BUILD_PLAYER=ON -DCMAKE_BUILD_TYPE=Debug -DPYTHON_EXECUTABLE=/usr/bin/python2 ../.. +make +make install +cd ../Release +cmake -G "Unix Makefiles" -DPOLYCODE_BUILD_BINDINGS=ON -DPOLYCODE_BUILD_PLAYER=ON -DCMAKE_BUILD_TYPE=Release -DPYTHON_EXECUTABLE=/usr/bin/python2 ../.. +make +make install +cd ../../Standalone/Build +cmake -G "Unix Makefiles" .. +make install diff --git a/BuildMac.sh b/BuildMac.sh new file mode 100755 index 000000000..19961aca1 --- /dev/null +++ b/BuildMac.sh @@ -0,0 +1,114 @@ +#!/bin/sh +# +# A complete build script for Polycode on Mac OS X +# Author: Mark Austin +# +# Make sure you have the following libraries and tools installed prior to running a build +# Xcode +# py-ply +# pkgconfig +# cmake +# +# With macports you can easily install most of the needed libs with the following command: +# sudo port install py-ply pkgconfig cmake +# + +# Note: Building with macports versions will require changing your python version to the macports version +# so that the lua binding build +# sudo port select python python27 + +# +# Start +# + +# +# Validate that the build command exited cleanly +# +function validate(){ + if [ $? -eq 0 ]; then + echo "[INFO] Build command executed successfully." + else + echo "[ERROR] One of the build commands failed!" + exit 1 + fi +} + +function cleanup(){ + # Do not remove this by default since it takes a while to download stuff + # if [ -d Dependencies/Build ]; then + # rm -fr Dependencies/Build + # echo "[INFO] Removed previous Dependencies/Build folder." + # fi + + if [ -d Build ]; then + rm -fr Build + echo "[INFO] Removed previous Build folder." + fi + + if [ -d Standalone/Build ]; then + rm -fr Standalone/Build + echo "[INFO] Removed previous Standalone/Build folder." + fi +} + +# +# Cleanup +# +cleanup + +# +# Build debug and release static dependencies +# +mkdir -p Dependencies/Build +cd Dependencies/Build +cmake -G Xcode .. +validate +xcodebuild -target ALL_BUILD -configuration Debug +validate +xcodebuild -target ALL_BUILD -configuration Release +validate +cd ../../ + +# +# Build polycode player and bindings +# +mkdir -p Build +cd Build +cmake -G Xcode .. -DPOLYCODE_BUILD_BINDINGS=1 -DPOLYCODE_BUILD_PLAYER=1 +validate +xcodebuild -DPOLYCODE_BUILD_BINDINGS=1 -DPOLYCODE_BUILD_PLAYER=1 -target ALL_BUILD -configuration Debug +validate +xcodebuild -target PolycodeLua -configuration Debug +validate +xcodebuild -target install -configuration Debug +validate +xcodebuild -DPOLYCODE_BUILD_BINDINGS=1 -DPOLYCODE_BUILD_PLAYER=1 -target ALL_BUILD -configuration Release +validate +xcodebuild -target PolycodeLua -configuration Release +validate +xcodebuild -target install -configuration Release +validate +cd ../ + +# +# Build standalone +# +mkdir -p Standalone/Build +cd Standalone/Build +cmake -G "Unix Makefiles" .. +validate +make install +validate +cd ../../ + +# +# Build IDE +# +cd IDE/Build/Mac\ OS\ X/ +xcodebuild +validate + +# +# End +# +exit 0 diff --git a/CMake/ExternalAssimp.cmake b/CMake/ExternalAssimp.cmake index 3502a69b9..4ec7d48bb 100644 --- a/CMake/ExternalAssimp.cmake +++ b/CMake/ExternalAssimp.cmake @@ -4,6 +4,7 @@ INCLUDE(ExternalProject) SET(assimp_PREFIX ${CMAKE_CURRENT_BINARY_DIR}/assimp) SET(assimp_CMAKE_ARGS + -DCMAKE_CXX_FLAGS=${CMAKE_CXX_FLAGS} -DCMAKE_INSTALL_PREFIX:PATH= -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} -DCMAKE_TOOLCHAIN_FILE=${CMAKE_TOOLCHAIN_FILE} diff --git a/CMake/ExternalBox2D.cmake b/CMake/ExternalBox2D.cmake index f50e9b5f2..154e3ca71 100644 --- a/CMake/ExternalBox2D.cmake +++ b/CMake/ExternalBox2D.cmake @@ -8,6 +8,7 @@ SET(box2d_PREFIX ${PROJECT_BINARY_DIR}/box2d) #ENDIF(CMAKE_COMPILER_IS_GNUCXX) SET(box2d_CMAKE_ARGS + -DCMAKE_CXX_FLAGS=${CMAKE_CXX_FLAGS} -DCMAKE_INSTALL_PREFIX:PATH= -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} -DCMAKE_TOOLCHAIN_FILE=${CMAKE_TOOLCHAIN_FILE} diff --git a/CMake/ExternalBullet.cmake b/CMake/ExternalBullet.cmake index d58c599cd..288908808 100644 --- a/CMake/ExternalBullet.cmake +++ b/CMake/ExternalBullet.cmake @@ -3,6 +3,7 @@ INCLUDE(ExternalProject) SET(bullet_PREFIX ${PROJECT_BINARY_DIR}/bullet) SET(bullet_CMAKE_ARGS + -DCMAKE_CXX_FLAGS=${CMAKE_CXX_FLAGS} -DCMAKE_INSTALL_PREFIX:PATH= -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} -DCMAKE_TOOLCHAIN_FILE=${CMAKE_TOOLCHAIN_FILE} @@ -11,6 +12,10 @@ SET(bullet_CMAKE_ARGS -DINSTALL_LIBS=ON -DUSE_MSVC_RUNTIME_LIBRARY_DLL=ON -DBUILD_DEMOS=OFF + -DBUILD_CPU_DEMOS=OFF + -DBUILD_OPENGL3_DEMOS=OFF + -DBUILD_BULLET2_DEMOS=OFF + -DBUILD_BULLET3=OFF -DBUILD_EXTRAS=OFF -DBUILD_UNIT_TESTS=OFF ) @@ -25,8 +30,8 @@ ExternalProject_Add(bullet DOWNLOAD_DIR ${POLYCODE_DEPS_DOWNLOAD_DIR} - URL http://bullet.googlecode.com/files/bullet-2.78.zip - URL_MD5 99d4070864c9f73521481ba9cda25038 + URL https://github.com/bulletphysics/bullet3/archive/2.83.5.tar.gz + URL_MD5 87e42fad2216801d5cef0af7e547ce08 INSTALL_DIR ${POLYCODE_DEPS_MODULES_PREFIX} CMAKE_ARGS ${bullet_CMAKE_ARGS} diff --git a/CMake/ExternalFreetype.cmake b/CMake/ExternalFreetype.cmake index 27398a3d8..61b5b6b08 100644 --- a/CMake/ExternalFreetype.cmake +++ b/CMake/ExternalFreetype.cmake @@ -4,20 +4,22 @@ INCLUDE(ExternalProject) SET(freetype_PREFIX ${CMAKE_CURRENT_BINARY_DIR}/freetype) SET(freetype_CMAKE_ARGS + -DCMAKE_CXX_FLAGS=${CMAKE_CXX_FLAGS} -DCMAKE_INSTALL_PREFIX:PATH= -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} - -DCMAKE_TOOLCHAIN_FILE=${CMAKE_TOOLCHAIN_FILE} + -DCMAKE_TOOLCHAIN_FILE=${CMAKE_TOOLCHAIN_FILE} -DCMAKE_OSX_ARCHITECTURES=${CMAKE_OSX_ARCHITECTURES} + -DCMAKE_DEBUG_POSTFIX=_d ) EXTERNALPROJECT_ADD(freetype PREFIX ${freetype_PREFIX} DOWNLOAD_DIR ${POLYCODE_DEPS_DOWNLOAD_DIR} - URL http://download.savannah.gnu.org/releases/freetype/freetype-2.4.5.tar.gz - URL_MD5 0e67460b312df905dc1cc1586690e7b2 + URL http://download.savannah.gnu.org/releases/freetype/freetype-2.6.tar.gz + URL_MD5 1d733ea6c1b7b3df38169fbdbec47d2b - PATCH_COMMAND ${CMAKE_COMMAND} -E copy_if_different ${PolycodeDependencies_SOURCE_DIR}/../CMake/freetype.cmake /CMakeLists.txt + PATCH_COMMAND ${CMAKE_COMMAND} -E copy_if_different ${PolycodeDependencies_SOURCE_DIR}/../CMake/freetype/ftoption.h /include/config/ftoption.h INSTALL_DIR ${POLYCODE_DEPS_CORE_PREFIX} CMAKE_ARGS ${freetype_CMAKE_ARGS} diff --git a/CMake/ExternalLibArchive.cmake b/CMake/ExternalLibArchive.cmake new file mode 100644 index 000000000..3636dbe28 --- /dev/null +++ b/CMake/ExternalLibArchive.cmake @@ -0,0 +1,28 @@ +# Build a local version +INCLUDE(ExternalProject) + +SET(libarchive_PREFIX ${CMAKE_CURRENT_BINARY_DIR}/libarchive) + +SET(libarchive_CMAKE_ARGS + -DCMAKE_INSTALL_PREFIX:PATH= + -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} + -DCMAKE_TOOLCHAIN_FILE=${CMAKE_TOOLCHAIN_FILE} + -DCMAKE_OSX_ARCHITECTURES=${CMAKE_OSX_ARCHITECTURES} + -DENABLE_ICONV=OFF + -DENABLE_TAR=OFF + -DENABLE_OPENSSL=OFF + -DENABLE_TEST=OFF + -DCMAKE_DEBUG_POSTFIX=d +) + +ExternalProject_Add(libarchive + PREFIX ${libarchive_PREFIX} + + DOWNLOAD_DIR ${POLYCODE_DEPS_DOWNLOAD_DIR} + + URL http://www.libarchive.org/downloads/libarchive-3.1.2.tar.gz + URL_MD5 efad5a503f66329bb9d2f4308b5de98a + + INSTALL_DIR ${POLYCODE_DEPS_TOOLS_PREFIX} + CMAKE_ARGS ${libarchive_CMAKE_ARGS} +) diff --git a/CMake/ExternalLua51.cmake b/CMake/ExternalLua51.cmake index 7b2d893dc..ff2a6323c 100644 --- a/CMake/ExternalLua51.cmake +++ b/CMake/ExternalLua51.cmake @@ -4,6 +4,7 @@ INCLUDE(ExternalProject) SET(lua51_PREFIX ${CMAKE_CURRENT_BINARY_DIR}/lua51) SET(lua51_CMAKE_ARGS + -DCMAKE_CXX_FLAGS=${CMAKE_CXX_FLAGS} -DCMAKE_INSTALL_PREFIX:PATH= -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} -DCMAKE_TOOLCHAIN_FILE=${CMAKE_TOOLCHAIN_FILE} diff --git a/CMake/ExternalOggVorbis.cmake b/CMake/ExternalOggVorbis.cmake index 80c7593e6..2c78339ab 100644 --- a/CMake/ExternalOggVorbis.cmake +++ b/CMake/ExternalOggVorbis.cmake @@ -4,6 +4,7 @@ INCLUDE(ExternalProject) SET(oggvorbis_PREFIX ${CMAKE_CURRENT_BINARY_DIR}/oggvorbis) SET(oggvorbis_CMAKE_ARGS + -DCMAKE_CXX_FLAGS=${CMAKE_CXX_FLAGS} -DCMAKE_INSTALL_PREFIX:PATH= -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} -DCMAKE_TOOLCHAIN_FILE=${CMAKE_TOOLCHAIN_FILE} diff --git a/CMake/ExternalOpenAL.cmake b/CMake/ExternalOpenAL.cmake index 3ae9a4135..cc7e7cddf 100644 --- a/CMake/ExternalOpenAL.cmake +++ b/CMake/ExternalOpenAL.cmake @@ -4,6 +4,7 @@ INCLUDE(ExternalProject) SET(openal_PREFIX ${CMAKE_CURRENT_BINARY_DIR}/openal) SET(openal_CMAKE_ARGS + -DCMAKE_CXX_FLAGS=${CMAKE_CXX_FLAGS} -DCMAKE_INSTALL_PREFIX:PATH= -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} -DCMAKE_TOOLCHAIN_FILE=${CMAKE_TOOLCHAIN_FILE} diff --git a/CMake/ExternalPNG.cmake b/CMake/ExternalPNG.cmake index 41b4c86ed..ddc7f4cce 100644 --- a/CMake/ExternalPNG.cmake +++ b/CMake/ExternalPNG.cmake @@ -4,6 +4,7 @@ INCLUDE(ExternalProject) SET(libpng_PREFIX ${CMAKE_CURRENT_BINARY_DIR}/libpng) SET(libpng_CMAKE_ARGS + -DCMAKE_CXX_FLAGS=${CMAKE_CXX_FLAGS} -DCMAKE_INSTALL_PREFIX:PATH= -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} -DCMAKE_TOOLCHAIN_FILE=${CMAKE_TOOLCHAIN_FILE} diff --git a/CMake/ExternalPhysFS.cmake b/CMake/ExternalPhysFS.cmake index f50bd7bae..6d4327090 100644 --- a/CMake/ExternalPhysFS.cmake +++ b/CMake/ExternalPhysFS.cmake @@ -4,6 +4,7 @@ INCLUDE(ExternalProject) SET(physfs_PREFIX ${CMAKE_CURRENT_BINARY_DIR}/physfs) SET(physfs_CMAKE_ARGS + -DCMAKE_CXX_FLAGS=${CMAKE_CXX_FLAGS} -DCMAKE_INSTALL_PREFIX:PATH= -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} -DCMAKE_TOOLCHAIN_FILE=${CMAKE_TOOLCHAIN_FILE} diff --git a/CMake/FindBullet.cmake b/CMake/FindBullet.cmake index a0f6e55ab..62ed690f9 100644 --- a/CMake/FindBullet.cmake +++ b/CMake/FindBullet.cmake @@ -18,13 +18,11 @@ SET(BULLET_SEARCH_PATHS SET(BULLETCOLLISION "BulletCollision") SET(BULLETMATH "LinearMath") SET(BULLETSOFTBODY "BulletSoftBody") - SET(BULLETMULTITHREADED "BulletMultiThreaded") SET(BULLETDYNAMICS_DEBUG "BulletDynamics_d") SET(BULLETCOLLISION_DEBUG "BulletCollision_d") SET(BULLETMATH_DEBUG "LinearMath_d") SET(BULLETSOFTBODY_DEBUG "BulletSoftBody_d") - SET(BULLETMULTITHREADED_DEBUG "BulletMultiThreaded_d") FIND_PATH(BULLET_INCLUDE_DIR NAMES btBulletCollisionCommon.h @@ -119,27 +117,6 @@ IF(NOT LIBBULLETSOFTBODY) MESSAGE ("WARNING: Could not find Bullet SoftBody - depending targets will be disabled.") ENDIF(NOT LIBBULLETSOFTBODY) - -FIND_LIBRARY(LIBBULLETMULTITHREADED - NAMES - ${BULLETMULTITHREADED} - HINTS - NO_DEFAULT_PATH - NO_CMAKE_ENVIRONMENT_PATH - NO_CMAKE_SYSTEM_PATH - NO_SYSTEM_ENVIRONMENT_PATH - NO_CMAKE_PATH - CMAKE_FIND_FRAMEWORK NEVER - $ENV{BULLETDIR} - $ENV{BULLET_PATH} - PATH_SUFFIXES lib lib64 win32/Dynamic_Release "Win32/${MSVC_YEAR_NAME}/x64/Release" "Win32/${MSVC_YEAR_NAME}/Win32/Release" - PATHS ${BULLET_SEARCH_PATHS} -) - -IF(NOT LIBBULLETMULTITHREADED) - MESSAGE ("WARNING: Could not find Bullet MultiThreaded - depending targets will be disabled.") -ENDIF(NOT LIBBULLETMULTITHREADED) - # ------- FIND_LIBRARY(LIBBULLETDYNAMICS_DEBUG @@ -224,30 +201,9 @@ IF(NOT LIBBULLETSOFTBODY_DEBUG) ENDIF(NOT LIBBULLETSOFTBODY_DEBUG) -FIND_LIBRARY(LIBBULLETMULTITHREADED_DEBUG - NAMES - ${BULLETMULTITHREADED_DEBUG} - HINTS - NO_DEFAULT_PATH - NO_CMAKE_ENVIRONMENT_PATH - NO_CMAKE_SYSTEM_PATH - NO_SYSTEM_ENVIRONMENT_PATH - NO_CMAKE_PATH - CMAKE_FIND_FRAMEWORK NEVER - $ENV{BULLETDIR} - $ENV{BULLET_PATH} - PATH_SUFFIXES lib lib64 win32/Dynamic_Release "Win32/${MSVC_YEAR_NAME}/x64/Release" "Win32/${MSVC_YEAR_NAME}/Win32/Release" - PATHS ${BULLET_SEARCH_PATHS} -) - -IF(NOT LIBBULLETMULTITHREADED_DEBUG) - MESSAGE ("WARNING: Could not find Bullet MultiThreaded Debug - depending targets will be disabled.") -ENDIF(NOT LIBBULLETMULTITHREADED_DEBUG) - - -SET(BULLET_LIBRARIES ${LIBBULLETMULTITHREADED} ${LIBBULLETSOFTBODY} ${LIBBULLETDYNAMICS} ${LIBBULLETCOLLISION} ${LIBBULLETMATH}) +SET(BULLET_LIBRARIES ${LIBBULLETSOFTBODY} ${LIBBULLETDYNAMICS} ${LIBBULLETCOLLISION} ${LIBBULLETMATH}) -SET(BULLET_LIBRARIES_DEBUG ${LIBBULLETMULTITHREADED_DEBUG} ${LIBBULLETSOFTBODY_DEBUG} ${LIBBULLETDYNAMICS_DEBUG} ${LIBBULLETCOLLISION_DEBUG} ${LIBBULLETMATH_DEBUG}) +SET(BULLET_LIBRARIES_DEBUG ${LIBBULLETSOFTBODY_DEBUG} ${LIBBULLETDYNAMICS_DEBUG} ${LIBBULLETCOLLISION_DEBUG} ${LIBBULLETMATH_DEBUG}) IF(BULLET_INCLUDE_DIR AND BULLET_LIBRARIES) SET(BULLET_FOUND TRUE) diff --git a/CMake/FindLibArchive.cmake b/CMake/FindLibArchive.cmake new file mode 100644 index 000000000..b589ec4d6 --- /dev/null +++ b/CMake/FindLibArchive.cmake @@ -0,0 +1,52 @@ +# LIBARCHIVE_FOUND - system has Libarchive +# LIBARCHIVE_INCLUDE_DIR - the Libarchive include directory +# LIBARCHIVE_LIBRARY - Link these to use Libarchive +# LIBARCHIVE_LIBRARIES + +SET(LIBARCHIVE_SEARCH_PATHS + ${POLYCODE_RELEASE_DIR}/Framework/Tools/Dependencies/lib + ${POLYCODE_RELEASE_DIR}/Framework/Tools/Dependencies/include/ +) + +SET(CMAKE_FIND_LIBRARY_SUFFIXES + .a + .lib +) + + +find_path (LIBARCHIVE_INCLUDE_DIR NAMES archive.h + HINTS + NO_DEFAULT_PATH + NO_CMAKE_ENVIRONMENT_PATH + NO_CMAKE_SYSTEM_PATH + NO_SYSTEM_ENVIRONMENT_PATH + NO_CMAKE_PATH + CMAKE_FIND_FRAMEWORK NEVER + PATH_SUFFIXES lib lib64 win32/Dynamic_Release "Win32/${MSVC_YEAR_NAME}/x64/Release" "Win32/${MSVC_YEAR_NAME}/Win32/Release" + PATHS ${LIBARCHIVE_SEARCH_PATHS} +) + +find_library (LIBARCHIVE_LIBRARY_DEBUG NAMES archived libarchived libarchive_d PATHS ${LIBARCHIVE_SEARCH_PATHS}) +find_library (LIBARCHIVE_LIBRARY_RELEASE NAMES archive libarchive PATHS ${LIBARCHIVE_SEARCH_PATHS}) + +if (LIBARCHIVE_INCLUDE_DIR AND LIBARCHIVE_LIBRARY_RELEASE) + set(LIBARCHIVE_FOUND TRUE) +endif() + +if (LIBARCHIVE_LIBRARY_RELEASE) + set (LIBARCHIVE_LIBRARY ${LIBARCHIVE_LIBRARY_RELEASE}) +endif() + +if (LIBARCHIVE_LIBRARY_DEBUG AND LIBARCHIVE_LIBRARY_RELEASE) + set (LIBARCHIVE_LIBRARY debug ${LIBARCHIVE_LIBRARY_DEBUG} optimized ${LIBARCHIVE_LIBRARY_RELEASE} ) +endif() + + +if (LIBARCHIVE_FOUND) + MESSAGE("-- Found Libarchive: ${LIBARCHIVE_LIBRARY}") + mark_as_advanced (LIBARCHIVE_INCLUDE_DIR LIBARCHIVE_LIBRARY LIBARCHIVE_LIBRARIES) +else() + MESSAGE("-- Could not find LibArchive!") +endif() + + diff --git a/CMake/FindSDL.cmake b/CMake/FindSDL.cmake index 58cf036fe..6098b8c75 100644 --- a/CMake/FindSDL.cmake +++ b/CMake/FindSDL.cmake @@ -12,7 +12,7 @@ FIND_PATH(SDL_INCLUDE_DIR PATH_SUFFIXES include SDL ) -#SET(CMAKE_FIND_LIBRARY_SUFFIXES .a ${CMAKE_FIND_LIBRARY_SUFFIXES}) +SET(CMAKE_FIND_LIBRARY_SUFFIXES .so ${CMAKE_FIND_LIBRARY_SUFFIXES}) FIND_LIBRARY(SDL_LIBRARY NAMES SDL libSDL PATHS $ENV{LD_LIBRARY_PATH} $ENV{LIBRARY_PATH} $ENV{LIB} diff --git a/CMake/PolycodeIncludes.cmake b/CMake/PolycodeIncludes.cmake index c4f3d362f..d103638b8 100644 --- a/CMake/PolycodeIncludes.cmake +++ b/CMake/PolycodeIncludes.cmake @@ -20,6 +20,7 @@ FIND_PACKAGE(Ogg REQUIRED) FIND_PACKAGE(Vorbis REQUIRED) FIND_PACKAGE(VorbisFile REQUIRED) FIND_PACKAGE(Lua REQUIRED) +FIND_PACKAGE(LibArchive REQUIRED) # Use SDL on non-Apple unixes IF(UNIX AND NOT APPLE) @@ -39,4 +40,5 @@ INCLUDE_DIRECTORIES( ${PNG_INCLUDE_DIR} ${OPENGLEXT_INCLUDE_DIR} ${LUA_INCLUDE_DIR} + ${LIBARCHIVE_INCLUDE_DIR} ) diff --git a/CMake/freetype.cmake b/CMake/freetype.cmake deleted file mode 100644 index d615c61aa..000000000 --- a/CMake/freetype.cmake +++ /dev/null @@ -1,81 +0,0 @@ -CMAKE_MINIMUM_REQUIRED(VERSION 2.6) - -PROJECT(freetype C) - -IF(NOT CMAKE_BUILD_TYPE) - #SET(CMAKE_BUILD_TYPE "Debug") - SET(CMAKE_BUILD_TYPE "Release") - MESSAGE("No CMAKE_BUILD_TYPE specified, defaulting to ${CMAKE_BUILD_TYPE}") -ENDIF(NOT CMAKE_BUILD_TYPE) - -# to distinguish between debug and release lib -SET(CMAKE_DEBUG_POSTFIX "_d") - -SET(freetype_SRCS - src/autofit/autofit.c - src/bdf/bdf.c - src/cff/cff.c - src/base/ftbase.c - src/base/ftbitmap.c - src/cache/ftcache.c - src/base/ftfstype.c - src/base/ftgasp.c - src/base/ftglyph.c - src/gzip/ftgzip.c - src/base/ftinit.c - src/lzw/ftlzw.c - src/base/ftstroke.c - src/base/ftsystem.c - src/smooth/smooth.c - src/base/ftbbox.c - src/base/ftmm.c - src/base/ftpfr.c - src/base/ftsynth.c - src/base/fttype1.c - src/base/ftwinfnt.c - src/base/ftxf86.c - src/base/ftlcdfil.c - src/base/ftgxval.c - src/base/ftotval.c - src/base/ftpatent.c - src/pcf/pcf.c - src/pfr/pfr.c - src/psaux/psaux.c - src/pshinter/pshinter.c - src/psnames/psmodule.c - src/raster/raster.c - src/sfnt/sfnt.c - src/truetype/truetype.c - src/type1/type1.c - src/cid/type1cid.c - src/type42/type42.c - src/winfonts/winfnt.c -) - -SET(freetype_HDRS - include/ft2build.h - include/freetype/config/ftconfig.h - include/freetype/config/ftheader.h - include/freetype/config/ftmodule.h - include/freetype/config/ftoption.h - include/freetype/config/ftstdlib.h -) - -INCLUDE_DIRECTORIES(include) - -ADD_DEFINITIONS(-D_CRT_SECURE_NO_WARNINGS -DFT2_BUILD_LIBRARY) -SET(COMPILE_DEFINITIONS_DEBUG FT_DEBUG_LEVEL_ERROR FT_DEBUG_LEVEL_TRACE) - -IF(WIN32) - LIST(APPEND freetype_SRCS builds/win32/ftdebug.c) -ENDIF(WIN32) - -ADD_LIBRARY(freetype ${freetype_SRCS} ${freetype_HDRS}) - -INSTALL(TARGETS freetype - RUNTIME DESTINATION bin - ARCHIVE DESTINATION lib - LIBRARY DESTINATION lib) - -INSTALL(DIRECTORY include/ DESTINATION include) - diff --git a/CMake/freetype/ftoption.h b/CMake/freetype/ftoption.h new file mode 100644 index 000000000..eff91e4f2 --- /dev/null +++ b/CMake/freetype/ftoption.h @@ -0,0 +1,886 @@ +/***************************************************************************/ +/* */ +/* ftoption.h */ +/* */ +/* User-selectable configuration macros (specification only). */ +/* */ +/* Copyright 1996-2015 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef __FTOPTION_H__ +#define __FTOPTION_H__ + + +#include + + +FT_BEGIN_HEADER + + /*************************************************************************/ + /* */ + /* USER-SELECTABLE CONFIGURATION MACROS */ + /* */ + /* This file contains the default configuration macro definitions for */ + /* a standard build of the FreeType library. There are three ways to */ + /* use this file to build project-specific versions of the library: */ + /* */ + /* - You can modify this file by hand, but this is not recommended in */ + /* cases where you would like to build several versions of the */ + /* library from a single source directory. */ + /* */ + /* - You can put a copy of this file in your build directory, more */ + /* precisely in `$BUILD/config/ftoption.h', where `$BUILD' is the */ + /* name of a directory that is included _before_ the FreeType include */ + /* path during compilation. */ + /* */ + /* The default FreeType Makefiles and Jamfiles use the build */ + /* directory `builds/' by default, but you can easily change */ + /* that for your own projects. */ + /* */ + /* - Copy the file to `$BUILD/ft2build.h' and modify it */ + /* slightly to pre-define the macro FT_CONFIG_OPTIONS_H used to */ + /* locate this file during the build. For example, */ + /* */ + /* #define FT_CONFIG_OPTIONS_H */ + /* #include */ + /* */ + /* will use `$BUILD/myftoptions.h' instead of this file for macro */ + /* definitions. */ + /* */ + /* Note also that you can similarly pre-define the macro */ + /* FT_CONFIG_MODULES_H used to locate the file listing of the modules */ + /* that are statically linked to the library at compile time. By */ + /* default, this file is . */ + /* */ + /* We highly recommend using the third method whenever possible. */ + /* */ + /*************************************************************************/ + + + /*************************************************************************/ + /*************************************************************************/ + /**** ****/ + /**** G E N E R A L F R E E T Y P E 2 C O N F I G U R A T I O N ****/ + /**** ****/ + /*************************************************************************/ + /*************************************************************************/ + + + /*************************************************************************/ + /* */ + /* Uncomment the line below if you want to activate sub-pixel rendering */ + /* (a.k.a. LCD rendering, or ClearType) in this build of the library. */ + /* */ + /* Note that this feature is covered by several Microsoft patents */ + /* and should not be activated in any default build of the library. */ + /* */ + /* This macro has no impact on the FreeType API, only on its */ + /* _implementation_. For example, using FT_RENDER_MODE_LCD when calling */ + /* FT_Render_Glyph still generates a bitmap that is 3 times wider than */ + /* the original size in case this macro isn't defined; however, each */ + /* triplet of subpixels has R=G=B. */ + /* */ + /* This is done to allow FreeType clients to run unmodified, forcing */ + /* them to display normal gray-level anti-aliased glyphs. */ + /* */ +#define FT_CONFIG_OPTION_SUBPIXEL_RENDERING + + + /*************************************************************************/ + /* */ + /* Many compilers provide a non-ANSI 64-bit data type that can be used */ + /* by FreeType to speed up some computations. However, this will create */ + /* some problems when compiling the library in strict ANSI mode. */ + /* */ + /* For this reason, the use of 64-bit integers is normally disabled when */ + /* the __STDC__ macro is defined. You can however disable this by */ + /* defining the macro FT_CONFIG_OPTION_FORCE_INT64 here. */ + /* */ + /* For most compilers, this will only create compilation warnings when */ + /* building the library. */ + /* */ + /* ObNote: The compiler-specific 64-bit integers are detected in the */ + /* file `ftconfig.h' either statically or through the */ + /* `configure' script on supported platforms. */ + /* */ +#undef FT_CONFIG_OPTION_FORCE_INT64 + + + /*************************************************************************/ + /* */ + /* If this macro is defined, do not try to use an assembler version of */ + /* performance-critical functions (e.g. FT_MulFix). You should only do */ + /* that to verify that the assembler function works properly, or to */ + /* execute benchmark tests of the various implementations. */ +/* #define FT_CONFIG_OPTION_NO_ASSEMBLER */ + + + /*************************************************************************/ + /* */ + /* If this macro is defined, try to use an inlined assembler version of */ + /* the `FT_MulFix' function, which is a `hotspot' when loading and */ + /* hinting glyphs, and which should be executed as fast as possible. */ + /* */ + /* Note that if your compiler or CPU is not supported, this will default */ + /* to the standard and portable implementation found in `ftcalc.c'. */ + /* */ +#define FT_CONFIG_OPTION_INLINE_MULFIX + + + /*************************************************************************/ + /* */ + /* LZW-compressed file support. */ + /* */ + /* FreeType now handles font files that have been compressed with the */ + /* `compress' program. This is mostly used to parse many of the PCF */ + /* files that come with various X11 distributions. The implementation */ + /* uses NetBSD's `zopen' to partially uncompress the file on the fly */ + /* (see src/lzw/ftgzip.c). */ + /* */ + /* Define this macro if you want to enable this `feature'. */ + /* */ +#define FT_CONFIG_OPTION_USE_LZW + + + /*************************************************************************/ + /* */ + /* Gzip-compressed file support. */ + /* */ + /* FreeType now handles font files that have been compressed with the */ + /* `gzip' program. This is mostly used to parse many of the PCF files */ + /* that come with XFree86. The implementation uses `zlib' to */ + /* partially uncompress the file on the fly (see src/gzip/ftgzip.c). */ + /* */ + /* Define this macro if you want to enable this `feature'. See also */ + /* the macro FT_CONFIG_OPTION_SYSTEM_ZLIB below. */ + /* */ +#define FT_CONFIG_OPTION_USE_ZLIB + + + /*************************************************************************/ + /* */ + /* ZLib library selection */ + /* */ + /* This macro is only used when FT_CONFIG_OPTION_USE_ZLIB is defined. */ + /* It allows FreeType's `ftgzip' component to link to the system's */ + /* installation of the ZLib library. This is useful on systems like */ + /* Unix or VMS where it generally is already available. */ + /* */ + /* If you let it undefined, the component will use its own copy */ + /* of the zlib sources instead. These have been modified to be */ + /* included directly within the component and *not* export external */ + /* function names. This allows you to link any program with FreeType */ + /* _and_ ZLib without linking conflicts. */ + /* */ + /* Do not #undef this macro here since the build system might define */ + /* it for certain configurations only. */ + /* */ +/* #define FT_CONFIG_OPTION_SYSTEM_ZLIB */ + + + /*************************************************************************/ + /* */ + /* Bzip2-compressed file support. */ + /* */ + /* FreeType now handles font files that have been compressed with the */ + /* `bzip2' program. This is mostly used to parse many of the PCF */ + /* files that come with XFree86. The implementation uses `libbz2' to */ + /* partially uncompress the file on the fly (see src/bzip2/ftbzip2.c). */ + /* Contrary to gzip, bzip2 currently is not included and need to use */ + /* the system available bzip2 implementation. */ + /* */ + /* Define this macro if you want to enable this `feature'. */ + /* */ +/* #define FT_CONFIG_OPTION_USE_BZIP2 */ + + + /*************************************************************************/ + /* */ + /* Define to disable the use of file stream functions and types, FILE, */ + /* fopen() etc. Enables the use of smaller system libraries on embedded */ + /* systems that have multiple system libraries, some with or without */ + /* file stream support, in the cases where file stream support is not */ + /* necessary such as memory loading of font files. */ + /* */ +/* #define FT_CONFIG_OPTION_DISABLE_STREAM_SUPPORT */ + + + /*************************************************************************/ + /* */ + /* PNG bitmap support. */ + /* */ + /* FreeType now handles loading color bitmap glyphs in the PNG format. */ + /* This requires help from the external libpng library. Uncompressed */ + /* color bitmaps do not need any external libraries and will be */ + /* supported regardless of this configuration. */ + /* */ + /* Define this macro if you want to enable this `feature'. */ + /* */ +/* #define FT_CONFIG_OPTION_USE_PNG */ + + + /*************************************************************************/ + /* */ + /* HarfBuzz support. */ + /* */ + /* FreeType uses the HarfBuzz library to improve auto-hinting of */ + /* OpenType fonts. If available, many glyphs not directly addressable */ + /* by a font's character map will be hinted also. */ + /* */ + /* Define this macro if you want to enable this `feature'. */ + /* */ +/* #define FT_CONFIG_OPTION_USE_HARFBUZZ */ + + + /*************************************************************************/ + /* */ + /* DLL export compilation */ + /* */ + /* When compiling FreeType as a DLL, some systems/compilers need a */ + /* special keyword in front OR after the return type of function */ + /* declarations. */ + /* */ + /* Two macros are used within the FreeType source code to define */ + /* exported library functions: FT_EXPORT and FT_EXPORT_DEF. */ + /* */ + /* FT_EXPORT( return_type ) */ + /* */ + /* is used in a function declaration, as in */ + /* */ + /* FT_EXPORT( FT_Error ) */ + /* FT_Init_FreeType( FT_Library* alibrary ); */ + /* */ + /* */ + /* FT_EXPORT_DEF( return_type ) */ + /* */ + /* is used in a function definition, as in */ + /* */ + /* FT_EXPORT_DEF( FT_Error ) */ + /* FT_Init_FreeType( FT_Library* alibrary ) */ + /* { */ + /* ... some code ... */ + /* return FT_Err_Ok; */ + /* } */ + /* */ + /* You can provide your own implementation of FT_EXPORT and */ + /* FT_EXPORT_DEF here if you want. If you leave them undefined, they */ + /* will be later automatically defined as `extern return_type' to */ + /* allow normal compilation. */ + /* */ + /* Do not #undef these macros here since the build system might define */ + /* them for certain configurations only. */ + /* */ +/* #define FT_EXPORT(x) extern x */ +/* #define FT_EXPORT_DEF(x) x */ + + + /*************************************************************************/ + /* */ + /* Glyph Postscript Names handling */ + /* */ + /* By default, FreeType 2 is compiled with the `psnames' module. This */ + /* module is in charge of converting a glyph name string into a */ + /* Unicode value, or return a Macintosh standard glyph name for the */ + /* use with the TrueType `post' table. */ + /* */ + /* Undefine this macro if you do not want `psnames' compiled in your */ + /* build of FreeType. This has the following effects: */ + /* */ + /* - The TrueType driver will provide its own set of glyph names, */ + /* if you build it to support postscript names in the TrueType */ + /* `post' table. */ + /* */ + /* - The Type 1 driver will not be able to synthesize a Unicode */ + /* charmap out of the glyphs found in the fonts. */ + /* */ + /* You would normally undefine this configuration macro when building */ + /* a version of FreeType that doesn't contain a Type 1 or CFF driver. */ + /* */ +#define FT_CONFIG_OPTION_POSTSCRIPT_NAMES + + + /*************************************************************************/ + /* */ + /* Postscript Names to Unicode Values support */ + /* */ + /* By default, FreeType 2 is built with the `PSNames' module compiled */ + /* in. Among other things, the module is used to convert a glyph name */ + /* into a Unicode value. This is especially useful in order to */ + /* synthesize on the fly a Unicode charmap from the CFF/Type 1 driver */ + /* through a big table named the `Adobe Glyph List' (AGL). */ + /* */ + /* Undefine this macro if you do not want the Adobe Glyph List */ + /* compiled in your `PSNames' module. The Type 1 driver will not be */ + /* able to synthesize a Unicode charmap out of the glyphs found in the */ + /* fonts. */ + /* */ +#define FT_CONFIG_OPTION_ADOBE_GLYPH_LIST + + + /*************************************************************************/ + /* */ + /* Support for Mac fonts */ + /* */ + /* Define this macro if you want support for outline fonts in Mac */ + /* format (mac dfont, mac resource, macbinary containing a mac */ + /* resource) on non-Mac platforms. */ + /* */ + /* Note that the `FOND' resource isn't checked. */ + /* */ +#define FT_CONFIG_OPTION_MAC_FONTS + + + /*************************************************************************/ + /* */ + /* Guessing methods to access embedded resource forks */ + /* */ + /* Enable extra Mac fonts support on non-Mac platforms (e.g. */ + /* GNU/Linux). */ + /* */ + /* Resource forks which include fonts data are stored sometimes in */ + /* locations which users or developers don't expected. In some cases, */ + /* resource forks start with some offset from the head of a file. In */ + /* other cases, the actual resource fork is stored in file different */ + /* from what the user specifies. If this option is activated, */ + /* FreeType tries to guess whether such offsets or different file */ + /* names must be used. */ + /* */ + /* Note that normal, direct access of resource forks is controlled via */ + /* the FT_CONFIG_OPTION_MAC_FONTS option. */ + /* */ +#ifdef FT_CONFIG_OPTION_MAC_FONTS +#define FT_CONFIG_OPTION_GUESSING_EMBEDDED_RFORK +#endif + + + /*************************************************************************/ + /* */ + /* Allow the use of FT_Incremental_Interface to load typefaces that */ + /* contain no glyph data, but supply it via a callback function. */ + /* This is required by clients supporting document formats which */ + /* supply font data incrementally as the document is parsed, such */ + /* as the Ghostscript interpreter for the PostScript language. */ + /* */ +#define FT_CONFIG_OPTION_INCREMENTAL + + + /*************************************************************************/ + /* */ + /* The size in bytes of the render pool used by the scan-line converter */ + /* to do all of its work. */ + /* */ +#define FT_RENDER_POOL_SIZE 16384L + + + /*************************************************************************/ + /* */ + /* FT_MAX_MODULES */ + /* */ + /* The maximum number of modules that can be registered in a single */ + /* FreeType library object. 32 is the default. */ + /* */ +#define FT_MAX_MODULES 32 + + + /*************************************************************************/ + /* */ + /* Debug level */ + /* */ + /* FreeType can be compiled in debug or trace mode. In debug mode, */ + /* errors are reported through the `ftdebug' component. In trace */ + /* mode, additional messages are sent to the standard output during */ + /* execution. */ + /* */ + /* Define FT_DEBUG_LEVEL_ERROR to build the library in debug mode. */ + /* Define FT_DEBUG_LEVEL_TRACE to build it in trace mode. */ + /* */ + /* Don't define any of these macros to compile in `release' mode! */ + /* */ + /* Do not #undef these macros here since the build system might define */ + /* them for certain configurations only. */ + /* */ +/* #define FT_DEBUG_LEVEL_ERROR */ +/* #define FT_DEBUG_LEVEL_TRACE */ + + + /*************************************************************************/ + /* */ + /* Autofitter debugging */ + /* */ + /* If FT_DEBUG_AUTOFIT is defined, FreeType provides some means to */ + /* control the autofitter behaviour for debugging purposes with global */ + /* boolean variables (consequently, you should *never* enable this */ + /* while compiling in `release' mode): */ + /* */ + /* _af_debug_disable_horz_hints */ + /* _af_debug_disable_vert_hints */ + /* _af_debug_disable_blue_hints */ + /* */ + /* Additionally, the following functions provide dumps of various */ + /* internal autofit structures to stdout (using `printf'): */ + /* */ + /* af_glyph_hints_dump_points */ + /* af_glyph_hints_dump_segments */ + /* af_glyph_hints_dump_edges */ + /* af_glyph_hints_get_num_segments */ + /* af_glyph_hints_get_segment_offset */ + /* */ + /* As an argument, they use another global variable: */ + /* */ + /* _af_debug_hints */ + /* */ + /* Please have a look at the `ftgrid' demo program to see how those */ + /* variables and macros should be used. */ + /* */ + /* Do not #undef these macros here since the build system might define */ + /* them for certain configurations only. */ + /* */ +/* #define FT_DEBUG_AUTOFIT */ + + + /*************************************************************************/ + /* */ + /* Memory Debugging */ + /* */ + /* FreeType now comes with an integrated memory debugger that is */ + /* capable of detecting simple errors like memory leaks or double */ + /* deletes. To compile it within your build of the library, you */ + /* should define FT_DEBUG_MEMORY here. */ + /* */ + /* Note that the memory debugger is only activated at runtime when */ + /* when the _environment_ variable `FT2_DEBUG_MEMORY' is defined also! */ + /* */ + /* Do not #undef this macro here since the build system might define */ + /* it for certain configurations only. */ + /* */ +/* #define FT_DEBUG_MEMORY */ + + + /*************************************************************************/ + /* */ + /* Module errors */ + /* */ + /* If this macro is set (which is _not_ the default), the higher byte */ + /* of an error code gives the module in which the error has occurred, */ + /* while the lower byte is the real error code. */ + /* */ + /* Setting this macro makes sense for debugging purposes only, since */ + /* it would break source compatibility of certain programs that use */ + /* FreeType 2. */ + /* */ + /* More details can be found in the files ftmoderr.h and fterrors.h. */ + /* */ +#undef FT_CONFIG_OPTION_USE_MODULE_ERRORS + + + /*************************************************************************/ + /* */ + /* Position Independent Code */ + /* */ + /* If this macro is set (which is _not_ the default), FreeType2 will */ + /* avoid creating constants that require address fixups. Instead the */ + /* constants will be moved into a struct and additional intialization */ + /* code will be used. */ + /* */ + /* Setting this macro is needed for systems that prohibit address */ + /* fixups, such as BREW. */ + /* */ +/* #define FT_CONFIG_OPTION_PIC */ + + + /*************************************************************************/ + /*************************************************************************/ + /**** ****/ + /**** S F N T D R I V E R C O N F I G U R A T I O N ****/ + /**** ****/ + /*************************************************************************/ + /*************************************************************************/ + + + /*************************************************************************/ + /* */ + /* Define TT_CONFIG_OPTION_EMBEDDED_BITMAPS if you want to support */ + /* embedded bitmaps in all formats using the SFNT module (namely */ + /* TrueType & OpenType). */ + /* */ +#define TT_CONFIG_OPTION_EMBEDDED_BITMAPS + + + /*************************************************************************/ + /* */ + /* Define TT_CONFIG_OPTION_POSTSCRIPT_NAMES if you want to be able to */ + /* load and enumerate the glyph Postscript names in a TrueType or */ + /* OpenType file. */ + /* */ + /* Note that when you do not compile the `PSNames' module by undefining */ + /* the above FT_CONFIG_OPTION_POSTSCRIPT_NAMES, the `sfnt' module will */ + /* contain additional code used to read the PS Names table from a font. */ + /* */ + /* (By default, the module uses `PSNames' to extract glyph names.) */ + /* */ +#define TT_CONFIG_OPTION_POSTSCRIPT_NAMES + + + /*************************************************************************/ + /* */ + /* Define TT_CONFIG_OPTION_SFNT_NAMES if your applications need to */ + /* access the internal name table in a SFNT-based format like TrueType */ + /* or OpenType. The name table contains various strings used to */ + /* describe the font, like family name, copyright, version, etc. It */ + /* does not contain any glyph name though. */ + /* */ + /* Accessing SFNT names is done through the functions declared in */ + /* `ftsnames.h'. */ + /* */ +#define TT_CONFIG_OPTION_SFNT_NAMES + + + /*************************************************************************/ + /* */ + /* TrueType CMap support */ + /* */ + /* Here you can fine-tune which TrueType CMap table format shall be */ + /* supported. */ +#define TT_CONFIG_CMAP_FORMAT_0 +#define TT_CONFIG_CMAP_FORMAT_2 +#define TT_CONFIG_CMAP_FORMAT_4 +#define TT_CONFIG_CMAP_FORMAT_6 +#define TT_CONFIG_CMAP_FORMAT_8 +#define TT_CONFIG_CMAP_FORMAT_10 +#define TT_CONFIG_CMAP_FORMAT_12 +#define TT_CONFIG_CMAP_FORMAT_13 +#define TT_CONFIG_CMAP_FORMAT_14 + + + /*************************************************************************/ + /*************************************************************************/ + /**** ****/ + /**** T R U E T Y P E D R I V E R C O N F I G U R A T I O N ****/ + /**** ****/ + /*************************************************************************/ + /*************************************************************************/ + + /*************************************************************************/ + /* */ + /* Define TT_CONFIG_OPTION_BYTECODE_INTERPRETER if you want to compile */ + /* a bytecode interpreter in the TrueType driver. */ + /* */ + /* By undefining this, you will only compile the code necessary to load */ + /* TrueType glyphs without hinting. */ + /* */ + /* Do not #undef this macro here, since the build system might */ + /* define it for certain configurations only. */ + /* */ +#define TT_CONFIG_OPTION_BYTECODE_INTERPRETER + + + /*************************************************************************/ + /* */ + /* Define TT_CONFIG_OPTION_SUBPIXEL_HINTING if you want to compile */ + /* EXPERIMENTAL subpixel hinting support into the TrueType driver. This */ + /* replaces the native TrueType hinting mechanism when anything but */ + /* FT_RENDER_MODE_MONO is requested. */ + /* */ + /* Enabling this causes the TrueType driver to ignore instructions under */ + /* certain conditions. This is done in accordance with the guide here, */ + /* with some minor differences: */ + /* */ + /* http://www.microsoft.com/typography/cleartype/truetypecleartype.aspx */ + /* */ + /* By undefining this, you only compile the code necessary to hint */ + /* TrueType glyphs with native TT hinting. */ + /* */ + /* This option requires TT_CONFIG_OPTION_BYTECODE_INTERPRETER to be */ + /* defined. */ + /* */ +/* #define TT_CONFIG_OPTION_SUBPIXEL_HINTING */ + + + /*************************************************************************/ + /* */ + /* If you define TT_CONFIG_OPTION_UNPATENTED_HINTING, a special version */ + /* of the TrueType bytecode interpreter is used that doesn't implement */ + /* any of the patented opcodes and algorithms. The patents related to */ + /* TrueType hinting have expired worldwide since May 2010; this option */ + /* is now deprecated. */ + /* */ + /* Note that the TT_CONFIG_OPTION_UNPATENTED_HINTING macro is *ignored* */ + /* if you define TT_CONFIG_OPTION_BYTECODE_INTERPRETER; in other words, */ + /* either define TT_CONFIG_OPTION_BYTECODE_INTERPRETER or */ + /* TT_CONFIG_OPTION_UNPATENTED_HINTING but not both at the same time. */ + /* */ + /* This macro is only useful for a small number of font files (mostly */ + /* for Asian scripts) that require bytecode interpretation to properly */ + /* load glyphs. For all other fonts, this produces unpleasant results, */ + /* thus the unpatented interpreter is never used to load glyphs from */ + /* TrueType fonts unless one of the following two options is used. */ + /* */ + /* - The unpatented interpreter is explicitly activated by the user */ + /* through the FT_PARAM_TAG_UNPATENTED_HINTING parameter tag */ + /* when opening the FT_Face. */ + /* */ + /* - FreeType detects that the FT_Face corresponds to one of the */ + /* `trick' fonts (e.g., `Mingliu') it knows about. The font engine */ + /* contains a hard-coded list of font names and other matching */ + /* parameters (see function `tt_face_init' in file */ + /* `src/truetype/ttobjs.c'). */ + /* */ + /* Here a sample code snippet for using FT_PARAM_TAG_UNPATENTED_HINTING. */ + /* */ + /* { */ + /* FT_Parameter parameter; */ + /* FT_Open_Args open_args; */ + /* */ + /* */ + /* parameter.tag = FT_PARAM_TAG_UNPATENTED_HINTING; */ + /* */ + /* open_args.flags = FT_OPEN_PATHNAME | FT_OPEN_PARAMS; */ + /* open_args.pathname = my_font_pathname; */ + /* open_args.num_params = 1; */ + /* open_args.params = ¶meter; */ + /* */ + /* error = FT_Open_Face( library, &open_args, index, &face ); */ + /* ... */ + /* } */ + /* */ +/* #define TT_CONFIG_OPTION_UNPATENTED_HINTING */ + + + /*************************************************************************/ + /* */ + /* Define TT_CONFIG_OPTION_COMPONENT_OFFSET_SCALED to compile the */ + /* TrueType glyph loader to use Apple's definition of how to handle */ + /* component offsets in composite glyphs. */ + /* */ + /* Apple and MS disagree on the default behavior of component offsets */ + /* in composites. Apple says that they should be scaled by the scaling */ + /* factors in the transformation matrix (roughly, it's more complex) */ + /* while MS says they should not. OpenType defines two bits in the */ + /* composite flags array which can be used to disambiguate, but old */ + /* fonts will not have them. */ + /* */ + /* http://www.microsoft.com/typography/otspec/glyf.htm */ + /* https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6glyf.html */ + /* */ +#undef TT_CONFIG_OPTION_COMPONENT_OFFSET_SCALED + + + /*************************************************************************/ + /* */ + /* Define TT_CONFIG_OPTION_GX_VAR_SUPPORT if you want to include */ + /* support for Apple's distortable font technology (fvar, gvar, cvar, */ + /* and avar tables). This has many similarities to Type 1 Multiple */ + /* Masters support. */ + /* */ +#define TT_CONFIG_OPTION_GX_VAR_SUPPORT + + + /*************************************************************************/ + /* */ + /* Define TT_CONFIG_OPTION_BDF if you want to include support for */ + /* an embedded `BDF ' table within SFNT-based bitmap formats. */ + /* */ +#define TT_CONFIG_OPTION_BDF + + + /*************************************************************************/ + /*************************************************************************/ + /**** ****/ + /**** T Y P E 1 D R I V E R C O N F I G U R A T I O N ****/ + /**** ****/ + /*************************************************************************/ + /*************************************************************************/ + + + /*************************************************************************/ + /* */ + /* T1_MAX_DICT_DEPTH is the maximum depth of nest dictionaries and */ + /* arrays in the Type 1 stream (see t1load.c). A minimum of 4 is */ + /* required. */ + /* */ +#define T1_MAX_DICT_DEPTH 5 + + + /*************************************************************************/ + /* */ + /* T1_MAX_SUBRS_CALLS details the maximum number of nested sub-routine */ + /* calls during glyph loading. */ + /* */ +#define T1_MAX_SUBRS_CALLS 16 + + + /*************************************************************************/ + /* */ + /* T1_MAX_CHARSTRING_OPERANDS is the charstring stack's capacity. A */ + /* minimum of 16 is required. */ + /* */ + /* The Chinese font MingTiEG-Medium (CNS 11643 character set) needs 256. */ + /* */ +#define T1_MAX_CHARSTRINGS_OPERANDS 256 + + + /*************************************************************************/ + /* */ + /* Define this configuration macro if you want to prevent the */ + /* compilation of `t1afm', which is in charge of reading Type 1 AFM */ + /* files into an existing face. Note that if set, the T1 driver will be */ + /* unable to produce kerning distances. */ + /* */ +#undef T1_CONFIG_OPTION_NO_AFM + + + /*************************************************************************/ + /* */ + /* Define this configuration macro if you want to prevent the */ + /* compilation of the Multiple Masters font support in the Type 1 */ + /* driver. */ + /* */ +#undef T1_CONFIG_OPTION_NO_MM_SUPPORT + + + /*************************************************************************/ + /*************************************************************************/ + /**** ****/ + /**** C F F D R I V E R C O N F I G U R A T I O N ****/ + /**** ****/ + /*************************************************************************/ + /*************************************************************************/ + + + /*************************************************************************/ + /* */ + /* Using CFF_CONFIG_OPTION_DARKENING_PARAMETER_{X,Y}{1,2,3,4} it is */ + /* possible to set up the default values of the four control points that */ + /* define the stem darkening behaviour of the (new) CFF engine. For */ + /* more details please read the documentation of the */ + /* `darkening-parameters' property of the cff driver module (file */ + /* `ftcffdrv.h'), which allows the control at run-time. */ + /* */ + /* Do *not* undefine these macros! */ + /* */ +#define CFF_CONFIG_OPTION_DARKENING_PARAMETER_X1 500 +#define CFF_CONFIG_OPTION_DARKENING_PARAMETER_Y1 400 + +#define CFF_CONFIG_OPTION_DARKENING_PARAMETER_X2 1000 +#define CFF_CONFIG_OPTION_DARKENING_PARAMETER_Y2 275 + +#define CFF_CONFIG_OPTION_DARKENING_PARAMETER_X3 1667 +#define CFF_CONFIG_OPTION_DARKENING_PARAMETER_Y3 275 + +#define CFF_CONFIG_OPTION_DARKENING_PARAMETER_X4 2333 +#define CFF_CONFIG_OPTION_DARKENING_PARAMETER_Y4 0 + + + /*************************************************************************/ + /* */ + /* CFF_CONFIG_OPTION_OLD_ENGINE controls whether the pre-Adobe CFF */ + /* engine gets compiled into FreeType. If defined, it is possible to */ + /* switch between the two engines using the `hinting-engine' property of */ + /* the cff driver module. */ + /* */ +/* #define CFF_CONFIG_OPTION_OLD_ENGINE */ + + + /*************************************************************************/ + /*************************************************************************/ + /**** ****/ + /**** A U T O F I T M O D U L E C O N F I G U R A T I O N ****/ + /**** ****/ + /*************************************************************************/ + /*************************************************************************/ + + + /*************************************************************************/ + /* */ + /* Compile autofit module with CJK (Chinese, Japanese, Korean) script */ + /* support. */ + /* */ +#define AF_CONFIG_OPTION_CJK + + /*************************************************************************/ + /* */ + /* Compile autofit module with Indic script support. */ + /* */ +#define AF_CONFIG_OPTION_INDIC + + /*************************************************************************/ + /* */ + /* Compile autofit module with warp hinting. The idea of the warping */ + /* code is to slightly scale and shift a glyph within a single dimension */ + /* so that as much of its segments are aligned (more or less) on the */ + /* grid. To find out the optimal scaling and shifting value, various */ + /* parameter combinations are tried and scored. */ + /* */ + /* This experimental option is active only if the rendering mode is */ + /* FT_RENDER_MODE_LIGHT; you can switch warping on and off with the */ + /* `warping' property of the auto-hinter (see file `ftautoh.h' for more */ + /* information; by default it is switched off). */ + /* */ +#define AF_CONFIG_OPTION_USE_WARPER + + /* */ + + + /* + * This macro is obsolete. Support has been removed in FreeType + * version 2.5. + */ +/* #define FT_CONFIG_OPTION_OLD_INTERNALS */ + + + /* + * This macro is defined if either unpatented or native TrueType + * hinting is requested by the definitions above. + */ +#ifdef TT_CONFIG_OPTION_BYTECODE_INTERPRETER +#define TT_USE_BYTECODE_INTERPRETER +#undef TT_CONFIG_OPTION_UNPATENTED_HINTING +#elif defined TT_CONFIG_OPTION_UNPATENTED_HINTING +#define TT_USE_BYTECODE_INTERPRETER +#endif + + + /* + * Check CFF darkening parameters. The checks are the same as in function + * `cff_property_set' in file `cffdrivr.c'. + */ +#if CFF_CONFIG_OPTION_DARKENING_PARAMETER_X1 < 0 || \ + CFF_CONFIG_OPTION_DARKENING_PARAMETER_X2 < 0 || \ + CFF_CONFIG_OPTION_DARKENING_PARAMETER_X3 < 0 || \ + CFF_CONFIG_OPTION_DARKENING_PARAMETER_X4 < 0 || \ + \ + CFF_CONFIG_OPTION_DARKENING_PARAMETER_Y1 < 0 || \ + CFF_CONFIG_OPTION_DARKENING_PARAMETER_Y2 < 0 || \ + CFF_CONFIG_OPTION_DARKENING_PARAMETER_Y3 < 0 || \ + CFF_CONFIG_OPTION_DARKENING_PARAMETER_Y4 < 0 || \ + \ + CFF_CONFIG_OPTION_DARKENING_PARAMETER_X1 > \ + CFF_CONFIG_OPTION_DARKENING_PARAMETER_X2 || \ + CFF_CONFIG_OPTION_DARKENING_PARAMETER_X2 > \ + CFF_CONFIG_OPTION_DARKENING_PARAMETER_X3 || \ + CFF_CONFIG_OPTION_DARKENING_PARAMETER_X3 > \ + CFF_CONFIG_OPTION_DARKENING_PARAMETER_X4 || \ + \ + CFF_CONFIG_OPTION_DARKENING_PARAMETER_Y1 > 500 || \ + CFF_CONFIG_OPTION_DARKENING_PARAMETER_Y2 > 500 || \ + CFF_CONFIG_OPTION_DARKENING_PARAMETER_Y3 > 500 || \ + CFF_CONFIG_OPTION_DARKENING_PARAMETER_Y4 > 500 +#error "Invalid CFF darkening parameters!" +#endif + +FT_END_HEADER + + +#endif /* __FTOPTION_H__ */ + + +/* END */ diff --git a/CMake/openal.cmake b/CMake/openal.cmake index ca3b3dfba..15891eca4 100644 --- a/CMake/openal.cmake +++ b/CMake/openal.cmake @@ -55,7 +55,7 @@ OPTION(DLOPEN "Check for the dlopen API for loading optional libs" ON) OPTION(WERROR "Treat compile warnings as errors" OFF) -OPTION(UTILS "Build and install utility programs" ON) +OPTION(UTILS "Build and install utility programs" OFF) OPTION(EXAMPLES "Build and install example programs" OFF) diff --git a/CMakeLists.txt b/CMakeLists.txt index ae3377a93..a12811810 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -16,6 +16,11 @@ ENDIF(NOT CMAKE_BUILD_TYPE) # SET(build_player OFF) #ENDIF() +IF(NUMBER_IS_SINGLE) + add_definitions(-DPOLYCODE_NUMBER_IS_SINGLE) + MESSAGE("USING SINGLE PRECISION NUMBERS") +ENDIF() + # Options for what components to build #OPTION(POLYCODE_BUILD_SHARED "Build Polycode shared libraries" OFF) #OPTION(POLYCODE_BUILD_STATIC "Build Polycode static libraries" ON) @@ -47,9 +52,8 @@ SET(CMAKE_PREFIX_PATH ${POLYCODE_RELEASE_DIR}/Framework/Tools/Dependencies) # Process subdirectories -ADD_SUBDIRECTORY(Core/Contents) -ADD_SUBDIRECTORY("Assets/Default asset pack") -ADD_SUBDIRECTORY("Assets/Templates") +ADD_SUBDIRECTORY("Core/Contents") +ADD_SUBDIRECTORY("Assets") ADD_SUBDIRECTORY("Documentation") IF(POLYCODE_BUILD_BINDINGS) diff --git a/Core/Contents/CMakeLists.txt b/Core/Contents/CMakeLists.txt index abd2592e7..1841b887a 100644 --- a/Core/Contents/CMakeLists.txt +++ b/Core/Contents/CMakeLists.txt @@ -19,6 +19,7 @@ SET(polycore_SRCS Source/PolyEventHandler.cpp Source/PolyFixedShader.cpp Source/PolyFont.cpp + Source/PolyFontGlyphSheet.cpp Source/PolyFontManager.cpp Source/PolyGLCubemap.cpp Source/PolyGLRenderer.cpp @@ -37,44 +38,32 @@ SET(polycore_SRCS Source/PolyMesh.cpp Source/PolyModule.cpp Source/PolyObject.cpp - Source/PolyParticle.cpp Source/PolyParticleEmitter.cpp Source/PolyPerlin.cpp - Source/PolyPolygon.cpp Source/PolyQuaternion.cpp Source/PolyQuaternionCurve.cpp Source/PolyRectangle.cpp Source/PolyRenderer.cpp + Source/PolyRenderDataArray.cpp Source/PolyResource.cpp Source/PolyResourceManager.cpp Source/PolyScene.cpp - Source/PolySceneEntity.cpp Source/PolySceneLabel.cpp Source/PolySceneLight.cpp Source/PolySceneLine.cpp Source/PolySceneManager.cpp Source/PolySceneMesh.cpp Source/PolyScenePrimitive.cpp + Source/PolySceneImage.cpp Source/PolySceneRenderTexture.cpp Source/PolySceneSound.cpp - Source/PolyScreen.cpp - Source/PolyScreenEntity.cpp - Source/PolyScreenEvent.cpp - Source/PolyScreenImage.cpp - Source/PolyScreenLabel.cpp - Source/PolyScreenLine.cpp - Source/PolyScreenManager.cpp - Source/PolyScreenMesh.cpp - Source/PolyScreenShape.cpp - Source/PolyScreenSound.cpp - Source/PolyScreenSprite.cpp - Source/PolyScreenEntityInstance.cpp Source/PolyShader.cpp Source/PolySkeleton.cpp Source/PolySound.cpp Source/PolySoundManager.cpp Source/PolyString.cpp - Source/PolyTexture.cpp + Source/PolyTextMesh.cpp + Source/PolyTexture.cpp Source/PolyThreaded.cpp Source/PolyTimer.cpp Source/PolyTimerManager.cpp @@ -82,7 +71,7 @@ SET(polycore_SRCS Source/PolyTweenManager.cpp Source/PolyVector2.cpp Source/PolyVector3.cpp - Source/PolyVertex.cpp + Source/PolyVector4.cpp Source/tinystr.cpp Source/tinyxml.cpp Source/tinyxmlerror.cpp @@ -91,6 +80,12 @@ SET(polycore_SRCS Source/PolyPeer.cpp Source/PolyClient.cpp Source/PolyServer.cpp + Source/PolyHTTPFetcher.cpp + Source/PolyRay.cpp + Source/PolySceneSprite.cpp + Source/PolySceneEntityInstance.cpp + Source/rgbe.cpp + Include/stb_image.h ) SET(polycore_HDRS @@ -112,6 +107,7 @@ SET(polycore_HDRS Include/PolyEventHandler.h Include/PolyFixedShader.h Include/PolyFont.h + Include/PolyFontGlyphSheet.h Include/PolyFontManager.h Include/PolyGLCubemap.h Include/PolyGLHeaders.h @@ -134,16 +130,14 @@ SET(polycore_HDRS Include/PolyModule.h Include/PolyObject.h Include/PolyParticleEmitter.h - Include/PolyParticle.h Include/PolyPerlin.h - Include/PolyPolygon.h Include/PolyQuaternionCurve.h Include/PolyQuaternion.h Include/PolyRectangle.h Include/PolyRenderer.h + Include/PolyRenderDataArray.h Include/PolyResource.h Include/PolyResourceManager.h - Include/PolySceneEntity.h Include/PolyScene.h Include/PolySceneLabel.h Include/PolySceneLight.h @@ -151,25 +145,15 @@ SET(polycore_HDRS Include/PolySceneManager.h Include/PolySceneMesh.h Include/PolyScenePrimitive.h + Include/PolySceneImage.h Include/PolySceneRenderTexture.h Include/PolySceneSound.h - Include/PolyScreenEntity.h - Include/PolyScreenEvent.h - Include/PolyScreen.h - Include/PolyScreenImage.h - Include/PolyScreenLabel.h - Include/PolyScreenLine.h - Include/PolyScreenManager.h - Include/PolyScreenMesh.h - Include/PolyScreenShape.h - Include/PolyScreenSound.h - Include/PolyScreenSprite.h - Include/PolyScreenEntityInstance.h Include/PolyShader.h Include/PolySkeleton.h Include/PolySound.h Include/PolySoundManager.h Include/PolyString.h + Include/PolyTextMesh.h Include/PolyTexture.h Include/PolyThreaded.h Include/PolyTimer.h @@ -178,7 +162,7 @@ SET(polycore_HDRS Include/PolyTweenManager.h Include/PolyVector2.h Include/PolyVector3.h - Include/PolyVertex.h + Include/PolyVector4.h Include/tinystr.h Include/tinyxml.h Include/PolySocket.h @@ -186,6 +170,12 @@ SET(polycore_HDRS Include/PolyClient.h Include/PolyServer.h Include/PolyServerWorld.h + Include/PolyHTTPFetcher.h + Include/PolyRay.h + Include/PolySceneSprite.h + Include/PolySceneEntityInstance.h + Include/rgbe.h + Include/stb_image.h ) SET(CMAKE_DEBUG_POSTFIX "_d") diff --git a/Core/Contents/Include/PolyBezierCurve.h b/Core/Contents/Include/PolyBezierCurve.h index de48ee7e3..f4ca60b42 100755 --- a/Core/Contents/Include/PolyBezierCurve.h +++ b/Core/Contents/Include/PolyBezierCurve.h @@ -19,17 +19,16 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ + #pragma once #include "PolyGlobals.h" #include "PolyVector3.h" +#include "PolyVector2.h" #include - -#define BUFFER_CACHE_PRECISION 100 - namespace Polycode { /** @@ -142,12 +141,6 @@ namespace Polycode { */ void addControlPoint2d(Number x, Number y); - /** - * Returns the height of the curve at a specified point on the curve. Heights are cached into a buffer with a finite cache precision to speed up the curve usage in animation. If you need to quickly get 2D height out of a curve and you don't care about total precision, use this method. - * @param a Normalized (0-1) position along the curve. - * @return Height value at specified position. - */ - Number getHeightAt(Number a); /** * Returns the 3d point of the curve at a specified point on the curve. @@ -162,34 +155,59 @@ namespace Polycode { * @return 3d point at specified position. */ Vector3 getPointBetween(Number a, BezierPoint *bp1, BezierPoint *bp2); - + + /** + * Removes all curve control points. + */ void clearControlPoints(); - - /** - * Rebuilds the height cache buffers for 2d height curves. - */ - void rebuildBuffers(); + + /** + * Returns the Y-axis value of the curve at specified X-axis value. + */ + Number getYValueAtX(Number x); + + /** + * Returns the normalized curve position value at specified X-axis value. + */ + Number getTValueAtX(Number x); /** - * Removes (and deletes!) a gives point by pointer + * Removes (and deletes!) a given point by pointer. */ void removePoint(BezierPoint *point); - - Number heightBuffer[BUFFER_CACHE_PRECISION]; - + + void setHeightCacheResolution(Number resolution); + void rebuildHeightCache(); + + /** + * The point after which new control points should be added. If NULL, new control points are added to the end of the curve. + */ BezierPoint *insertPoint; - - std::vector controlPoints; - std::vector distances; - void recalculateDistances(); + /** + * Accuracy value for X-axis curve evaluation. The higher this number, the faster but less accurate X-axis curve evaluation is. + Defaults to 0.01 + */ + Number evaluationAccuracy; + + void recalculateDistances(); + + static bool cacheHeightValues; + static unsigned int defaultHeightCacheResolution; protected: - - bool buffersDirty; - - - + + unsigned int heightCacheResolution; + std::vector controlPoints; + std::vector distances; + + std::vector heightCache; + + Number minX; + Number maxX; + Number midX; + + bool distancesDirty; }; diff --git a/Core/Contents/Include/PolyBone.h b/Core/Contents/Include/PolyBone.h index 348ac83e3..001e78e9c 100755 --- a/Core/Contents/Include/PolyBone.h +++ b/Core/Contents/Include/PolyBone.h @@ -24,33 +24,30 @@ #include "PolyGlobals.h" #include "PolyString.h" #include "PolyMatrix4.h" -#include "PolySceneEntity.h" +#include "PolyEntity.h" namespace Polycode { class Mesh; /** - * Skeleton bone. Bones are bound to vertices of a mesh and when transformed, move the bound vertices of the mesh along with them. Bones are subclassed from SceneEntity, but have their own hierarchy system. + * Skeleton bone. Bones are bound to vertices of a mesh and when transformed, move the bound vertices of the mesh along with them. Bones are subclassed from Entity, but have their own hierarchy system. * @see Skeleton */ - class _PolyExport Bone : public SceneEntity { + class _PolyExport Bone : public Entity { public: /** * Constructor. * @param boneName Name of the bone. */ - Bone(const String& boneName); + explicit Bone(const String& boneName); virtual ~Bone(); - void enableBoneLabel(const String& labelFont, Number size, Number scale, Color labelColor); - /** * Returns the name of the bone. * @return Name of the bone. */ - const String& getName() const; - void Render(); + String getName() const; /** * Sets the parent bone of this bone. @@ -141,6 +138,12 @@ namespace Polycode { * @return Full base matrix. */ Matrix4 getFullBaseMatrix() const; + + void rebuildFinalMatrix(); + Matrix4 buildFinalMatrix() const; + + + void intializeBone(const Vector3 &basePosition, const Vector3 &baseScale, const Quaternion &baseRotation, const Vector3 &restPosition, const Vector3 &restScale, const Quaternion &restRotation); /** * Id of the bone. @@ -150,12 +153,15 @@ namespace Polycode { Matrix4 boneMatrix; Matrix4 restMatrix; Matrix4 baseMatrix; + Matrix4 finalMatrix; - - + Quaternion baseRotation; + Vector3 baseScale; + Vector3 basePosition; + + bool disableAnimation; + protected: - Mesh *boneMesh; - Bone* parentBone; std::vector childBones; String boneName; diff --git a/Core/Contents/Include/PolyCamera.h b/Core/Contents/Include/PolyCamera.h index 21307bb09..15b47fbe3 100755 --- a/Core/Contents/Include/PolyCamera.h +++ b/Core/Contents/Include/PolyCamera.h @@ -23,8 +23,9 @@ #pragma once #include "PolyGlobals.h" +#include "PolyEntity.h" #include "PolyVector2.h" -#include "PolySceneEntity.h" +#include "PolyVector4.h" namespace Polycode { @@ -36,16 +37,41 @@ namespace Polycode { /** * Camera in a 3D scene. Cameras can be added to a scene and changed between dynamically. You can also set a shader to a camera that will run as a screen shader for post-processing effects. */ - class _PolyExport Camera : public SceneEntity { + class _PolyExport Camera : public Entity { public: + /** ProjectionMode: Orthographic projection, with manually set size. */ + static const int ORTHO_SIZE_MANUAL = 0; + + /** ProjectionMode: Orthographic projection, with height specified used and width scaled proportionally . */ + static const int ORTHO_SIZE_LOCK_HEIGHT = 1; + + /** ProjectionMode: Orthographic projection, with width specified used and height scaled proportionally. */ + static const int ORTHO_SIZE_LOCK_WIDTH = 2; + + /** ProjectionMode: Orthographic projection, scaled to viewport backing resolution. */ + static const int ORTHO_SIZE_VIEWPORT = 3; + + /** ProjectionMode: Perspective projection, with field of view specified. */ + static const int PERSPECTIVE_FOV = 4; + + /** ProjectionMode: Perspective projection, with bounds set by edges of frustum. */ + static const int PERSPECTIVE_FRUSTUM = 5; + + /** ProjectionMode: Manual matrix projection. Use setProjectionMatrix to set the matrix. */ + static const int MANUAL_MATRIX = 6; + + /** * Constructor. * @param parentScene Scene to add the camera to. */ - Camera(Scene *parentScene); + explicit Camera(Scene *parentScene); virtual ~Camera(); + /** + * Builds the frustum clipping planes for this camera using the current render modelview and the camera's projection matrices. + */ void buildFrustumPlanes(); /** @@ -55,15 +81,14 @@ namespace Polycode { * @return Returns true if the sphere is within the camera's frustum, or false if it isn't. * @see canSee() */ - bool isSphereInFrustum(Vector3 pos, Number fRadius); - - /** - * Checks if the camera can see an entity based on its bounding radius. - * @param entity Entity to check. - * @return Returns true if the entity's bounding radius is within the camera's frustum, or false if it isn't. - * @see isSphereInFrustum() - */ - bool canSee(SceneEntity *entity); + bool isSphereInFrustum(const Vector3 &pos, Number fRadius); + + /** + * Checks if an Axis-aligned bounding box is visible to the camera. + * @param aabb An axis-aligned bounding box + * @return Returns true if the AABB is within the camera's frustum, false if it isn't. + */ + bool isAABBInFrustum(const AABB &aabb); /** * Toggles orthographic projection mode for camera. @@ -71,13 +96,30 @@ namespace Polycode { * @param orthoSizeX Width of the orthographic frustum (defaults to 1.0) * @param orthoSizeY Height of the orthographic frustum (defaults to 1.0) */ - void setOrthoMode(bool mode, Number orthoSizeX = 1.0, Number orthoSizeY = 1.0); + void setOrthoMode(bool mode); + + void handleEvent(Event *event); + + /** + * Sets the orthographic size of the camera. + * @param orthoSizeX Orthographic width + * @param orthoSizeY Orthographic height + */ + void setOrthoSize(Number orthoSizeX, Number orthoSizeY); + /** Switches into frustum mode and sets up the planes. */ + void setFrustumMode(Number left, Number right, Number bottom, Number top, Number front, Number back); + /** * Returns true if camera is in orthographic projection mode. * @return True if camera is orthographic, false if otherwise. */ - bool getOrthoMode(); + bool getOrthoMode() { + return projectionMode == ORTHO_SIZE_MANUAL || + projectionMode == ORTHO_SIZE_LOCK_HEIGHT || + projectionMode == ORTHO_SIZE_LOCK_WIDTH || + projectionMode == ORTHO_SIZE_VIEWPORT; + } /** * Returns the width of the camera's orthographic frustum. @@ -101,29 +143,72 @@ namespace Polycode { * Returns the current FOV value for the camera. * @return Current FOV value for the camera. */ - Number getFOV(); + Number getFOV() { + return fov; + } + /** + * Sets the clipping planes for the camera. + * @param nearClipPlane Near clipping plane. + * @param farClipPlane Far clipping plane. + */ + void setClippingPlanes(Number nearClipPlane, Number farClipPlane); + + /** + * Returns the near clipping plane of the camera. + * @return Near clipping plane of the camera. + */ + Number getNearClippingPlane(); + + /** + * Returns the far clipping plane of the camera. + * @return Far clipping plane of the camera. + */ + Number getFarClippingPlane(); + + /** + * Sets the parent scene of the camera. + * @param parentScene New parent scene. + */ void setParentScene(Scene *parentScene); + + /** + * Returns the camera's parent scene. + * @return The camera's parent scene. + */ + Scene *getParentScene() const; + /** + * Sets the renderer viewport and projection/modelview matrices based on the camera's setting and transform. + */ void doCameraTransform(); - void setLightDepthTexture(Texture *texture); + /** + * Check if camera has a post filter material applied + * @return True if the camera has a filter material applied. + */ bool hasFilterShader(); + + /** + * Binds target buffers and renders the scene in multiple passes based on the post filter material. + */ void drawFilter(Texture *targetTexture = NULL, Number targetTextureWidth = 0.0, Number targetTextureHeight = 0.0, Texture *targetColorTexture = NULL, Texture *targetZTexture = NULL); - Matrix4 getProjectionMatrix() const; - /** - * Sets the exposure for the camera. The exposure value can be passed to a shader for HDR rendering. + * Sets the exposure for the camera. The exposure value is passed automatically to post material shaders using an "exposure" uniform. * @param level The new exposure value. */ - void setExposureLevel(Number level); + void setExposureLevel(Number level) { + exposureLevel = level; + } /** * Returns the camera's exposure value. * @return Current exposure value. */ - Number getExposureLevel(); + Number getExposureLevel() { + return exposureLevel; + } /** * Sets the post-processing shader for the camera. @@ -147,32 +232,109 @@ namespace Polycode { */ std::vector getLocalShaderOptions() { return localShaderOptions; } + /** + * Returns the number of local material shader options. + * @return Number of local material shader options. + */ + unsigned int getNumLocalShaderOptions() const; + + /** + * Returns the shader option binding at the specified shader index. + * @param Specified shader index. + * @return Shader binding at specified shader index or NULL if index is out of bounds. + */ + ShaderBinding* getLocalShaderOption(unsigned int index) const; + /** * Returns the shader material applied to the camera. */ Material *getScreenShaderMaterial() { return filterShaderMaterial; } + + /** + * Clones the camera. + */ + virtual Entity *Clone(bool deepClone, bool ignoreEditorOnly) const; + + /** + * Applies clone parameters to the camera. + */ + virtual void applyClone(Entity *clone, bool deepClone, bool ignoreEditorOnly) const; + + /** + * Returns the camera's projection matrix. + * @return Projection matrix. + */ + Matrix4 getProjectionMatrix(); + + /** + * Manually sets the camera's projection matrix. Projection mode must be set to MANUAL_MATRIX. + * @param matrix Custom projection matrix. + * @see setProjectionMode + */ + void setProjectionMatrix(Matrix4 matrix); + + /** + * Return's the camera's pixel viewport based on the last render pass. + */ + Polycode::Rectangle getViewport(); /** * Toggles the frustum culling of the camera. (Defaults to true). */ bool frustumCulling; + + /** + * If set to true, the orthographic projection will be set with the 0,0 coordinate in the top left corner of the viewport. Otherwise, the 0,0 coordinate is in the center. + */ + bool topLeftOrtho; - /** - * Shifts camera frustum by factor of the frustum size. (x=-1 will shift the frustum to the left by a whole screen width). - */ + /** + * Shifts camera frustum by factor of the frustum size. (x=-1 will shift the frustum to the left by a whole screen width). + */ Vector2 cameraShift; - + + /** @deprecated use setProjectionMode(ProjectionMode mode) */ + void setOrthoSizeMode(int orthoSizeMode) { setProjectionMode(orthoSizeMode); } + /** @deprecated use getProjectionMode() */ + int getOrthoSizeMode() const { return projectionMode; } + + /** + * Sets the projection mode of the camera. Possible values are ORTHO_SIZE_MANUAL, ORTHO_SIZE_LOCK_HEIGHT,ORTHO_SIZE_LOCK_WIDTH, ORTHO_SIZE_LOCK_WIDTH, PERSPECTIVE_FOV, PERSPECTIVE_FRUSTUM and MANUAL_MATRIX. + See the documentation of each individual mode for details. + * @param mode New projection mode. + */ + void setProjectionMode(int mode); + + /** + * Returns the current projection mode. + * @return Current projection mode. + */ + int getProjectionMode() const { return projectionMode; } + + void setUseGlobalFramebuffer(bool val); + bool getUseGlobalFramebuffer() const; + protected: - + + bool useGlobalFramebuffer; + int projectionMode; + Matrix4 projectionMatrix; - + + Polycode::Rectangle viewport; Number orthoSizeX; Number orthoSizeY; - + + Number nearClipPlane; + Number farClipPlane; + Number exposureLevel; - bool orthoMode; Number fov; - Number frustumPlanes[6][4]; + + Number leftFrustum,rightFrustum,topFrustum,bottomFrustum; + + Vector4 frustumPlanes[6]; + Scene *parentScene; Material *filterShaderMaterial; diff --git a/Core/Contents/Include/PolyCocoaCore.h b/Core/Contents/Include/PolyCocoaCore.h index f2f55634f..2636e160b 100644 --- a/Core/Contents/Include/PolyCocoaCore.h +++ b/Core/Contents/Include/PolyCocoaCore.h @@ -59,6 +59,9 @@ namespace Polycode { int mouseX; int mouseY; + std::vector touches; + TouchInfo touch; + PolyKEY keyCode; wchar_t unicodeChar; @@ -89,6 +92,7 @@ namespace Polycode { public: GamepadDeviceEntry() { numAxes = 0; + numButtons = 0; } vector axisElements; vector buttonElements; @@ -113,16 +117,16 @@ namespace Polycode { class _PolyExport CocoaCore : public Core { public: - CocoaCore(PolycodeView *view, int xRes, int yRes, bool fullScreen, bool vSync, int aaLevel, int anisotropyLevel, int frameRate, int monitorIndex=-1); + CocoaCore(PolycodeView *view, int xRes, int yRes, bool fullScreen, bool vSync, int aaLevel, int anisotropyLevel, int frameRate, int monitorIndex=-1, bool retinaSupport=false); virtual ~CocoaCore(); void enableMouse(bool newval); unsigned int getTicks(); - bool Update(); + bool systemUpdate(); void Render(); - void setVideoMode(int xRes, int yRes, bool fullScreen, bool vSync, int aaLevel, int anisotropyLevel); + void setVideoMode(int xRes, int yRes, bool fullScreen, bool vSync, int aaLevel, int anisotropyLevel, bool retinaSupport = true); void resizeTo(int xRes, int yRes); void createThread(Threaded *target); @@ -132,6 +136,7 @@ namespace Polycode { void removeDiskItem(const String& itemPath); String openFolderPicker(); vector openFilePicker(vector extensions, bool allowMultiple); + String saveFilePicker(std::vector extensions); String executeExternalCommand(String command, String args, String inDirectory=""); @@ -168,7 +173,10 @@ namespace Polycode { unsigned int nextDeviceID; bool checkSpecialKeyEvents(PolyKEY key); - + + Number getBackingXRes(); + Number getBackingYRes(); + protected: @@ -176,6 +184,7 @@ namespace Polycode { PolycodeView *glView; uint64_t initTime; + bool retinaSupport; VideoModeChangeInfo modeChangeInfo; diff --git a/Core/Contents/Include/PolyColor.h b/Core/Contents/Include/PolyColor.h index c085ccec1..c7029df18 100755 --- a/Core/Contents/Include/PolyColor.h +++ b/Core/Contents/Include/PolyColor.h @@ -41,8 +41,13 @@ namespace Polycode { * @param b Blue value 0-1 * @param a Alpha value 0-1 */ - Color(Number r,Number g, Number b, Number a); - + Color(float r,float g,float b,float a); + + /** + * @copydoc Color::Color(float,float,float,float) + */ + Color(double r,double g,double b,double a); + /** * Default constructor. */ @@ -196,7 +201,7 @@ namespace Polycode { /** * Returns a new color after blending the second color with specified blending mode. * @param c2 Color to blend with - * @param mode Blending mode to use. Currently possible values are Color::BLEND_NORMAL, Color::BLEND_COLOR + * @param mode Blending mode to use. Currently possible values are Color::BLEND_NORMAL, Color::BLEND_REPLACE_COLOR and Color::BLEND_ADDITIVE * @param amount Amount to blend. */ Color blendColor(Color c2, int mode, Number amount, Color c3 = Color()); diff --git a/Core/Contents/Include/PolyConfig.h b/Core/Contents/Include/PolyConfig.h index ca75a853c..3937a1d00 100755 --- a/Core/Contents/Include/PolyConfig.h +++ b/Core/Contents/Include/PolyConfig.h @@ -91,6 +91,21 @@ namespace Polycode { * @param key String key of the value. */ const String& getStringValue(const String& configNamespace, const String& key); + + /** + * Sets a string value that represents boolean (true|false) key. + * @param configNamespace Namespace to set value in. + * @param key String key of the value. + * @param value The string value to save. + */ + void setBoolValue(const String& configNamespace, const String& key, bool value); + + /** + * Returns a boolean value by eveluating a string key (true|1 = true). + * @param configNamespace Namespace to get the value from. + * @param key String key of the value. + */ + bool getBoolValue(const String& configNamespace, const String& key); private: diff --git a/Core/Contents/Include/PolyCore.h b/Core/Contents/Include/PolyCore.h index 21136d3e4..15116bc2c 100755 --- a/Core/Contents/Include/PolyCore.h +++ b/Core/Contents/Include/PolyCore.h @@ -43,6 +43,11 @@ namespace Polycode { class _PolyExport CoreFileExtension : public PolyBase { public: + CoreFileExtension() {} + CoreFileExtension(String description, String extension) { + this->extension = extension; + this->description = description; + } String extension; String description; }; @@ -52,6 +57,7 @@ namespace Polycode { PolycodeViewBase() { windowData = NULL; } virtual ~PolycodeViewBase(){} void *windowData; + bool resizable; }; class _PolyExport TimeInfo { @@ -87,9 +93,11 @@ namespace Polycode { Core(int xRes, int yRes, bool fullScreen, bool vSync, int aaLevel, int anisotropyLevel, int frameRate, int monitorIndex); virtual ~Core(); - virtual bool Update() = 0; - + bool Update(); virtual void Render() = 0; + + bool fixedUpdate(); + virtual bool systemUpdate() = 0; bool updateAndRender(); @@ -206,6 +214,18 @@ namespace Polycode { * @return Current vertical resolution. */ Number getYRes(); + + /** + * Returns actual current horizontal resolution. + * @return Current actual horizontal resolution. + */ + virtual Number getBackingXRes() { return getXRes(); } + + /** + * Returns actual current vertical resolution. + * @return Current actual horizontal resolution. + */ + virtual Number getBackingYRes() { return getYRes(); } /** * Provides the current width, height, and refresh rate of the screen. @@ -215,6 +235,9 @@ namespace Polycode { */ static void getScreenInfo(int *width, int *height, int *hz); + int getScreenWidth(); + int getScreenHeight(); + /** * Creates a folder on disk with the specified path. * @param folderPath Path to create the folder in. @@ -247,7 +270,7 @@ namespace Polycode { */ virtual String openFolderPicker() = 0; - void setFramerate(int frameRate); + void setFramerate(int frameRate, int maxFixedCycles = 8); /** * Opens a system file picker for the specified extensions. @@ -256,7 +279,10 @@ namespace Polycode { * @return An STL vector of the selected file paths. */ virtual std::vector openFilePicker(std::vector extensions, bool allowMultiple) = 0; - + + virtual String saveFilePicker(std::vector extensions) = 0; + + /** * Sets a new video mode. * @param xRes New horizontal resolution of the renderer. @@ -264,7 +290,7 @@ namespace Polycode { * @param fullScreen True to launch in fullscreen, false to launch in window. * @param aaLevel Level of anti-aliasing. Possible values are 2,4 and 6. */ - virtual void setVideoMode(int xRes, int yRes, bool fullScreen, bool vSync, int aaLevel, int anisotropyLevel) = 0; + virtual void setVideoMode(int xRes, int yRes, bool fullScreen, bool vSync, int aaLevel, int anisotropyLevel, bool retinaSupport=true) = 0; /** * Resizes the renderer. @@ -291,13 +317,24 @@ namespace Polycode { * Returns the total ticks elapsed since launch. * @return Time elapsed since launch in milliseconds */ - virtual unsigned int getTicks() = 0; + virtual unsigned int getTicks() = 0; + + /** Returns the target number of milliseconds between frames */ + long getRefreshIntervalMs() const { + return refreshInterval; + } + + long getTimeSleptMs() const { + return timeSleptMs; + } + + Number getFixedTimestep(); /** * Returns the total ticks elapsed since launch. - * @return Time elapsed since launch in floating point microseconds. + * @return Time elapsed since launch in floating point seconds. */ - Number getTicksFloat(); + double getTicksFloat(); void setUserPointer(void *ptr) { userPointer = ptr; } void *getUserPointer() { return userPointer; } @@ -364,13 +401,13 @@ namespace Polycode { void loseFocus(); void gainFocus(); - String userHomeDirectory; String defaultWorkingDirectory; void *userPointer; long refreshInterval; + unsigned int timeSleptMs; bool fullScreen; int aaLevel; @@ -386,6 +423,11 @@ namespace Polycode { unsigned int lastFrameTicks; unsigned int lastFPSTicks; unsigned int elapsed; + + double fixedElapsed; + double fixedTimestep; + double timeLeftOver; + double maxFixedElapsed; bool mouseEnabled; bool mouseCaptured; diff --git a/Core/Contents/Include/PolyCoreInput.h b/Core/Contents/Include/PolyCoreInput.h index 54cc3107c..7087305d9 100755 --- a/Core/Contents/Include/PolyCoreInput.h +++ b/Core/Contents/Include/PolyCoreInput.h @@ -134,12 +134,18 @@ namespace Polycode { /** * Returns joystick info for specified joystick index. - * @param index Joystick index. Must be less than getNumJoysticks() + * @param index Joystick index. Returns NULL if index is invalid. * @return Joystick info for specified joystick. * @see JoystickInfo */ JoystickInfo *getJoystickInfoByIndex(unsigned int index); + /** + * Returns joystick info for specified joystick device ID. Returns NULL if the joystick device ID is invalid. + * @param deviceID Joystick device ID. + * @return Joystick info for specified joystick. + * @see JoystickInfo + */ JoystickInfo *getJoystickInfoByID(unsigned int deviceID); void addJoystick(unsigned int deviceID); void removeJoystick(unsigned int deviceID); @@ -161,12 +167,31 @@ namespace Polycode { static InputEvent *createEvent(Event *event){ return (InputEvent*)event; } /** - * If set to true, will fire touch events on mouse input. + * If set to true, will fire touch events on mouse input. Defaults to false. */ bool simulateTouchWithMouse; + + /** + * If set to true, simulated touches will have type TYPE_PEN. + */ + bool simulateTouchAsPen; + + /** + * If set to true, will fire mouse events on touch input. Defaults to false. + */ + bool simulateMouseWithTouch; + + /** + * If set to true, will not send touch events outside of the screen as define by current core resolution. Defaults to false. + */ + bool ignoreOffScreenTouch; void clearInput(); + /** + * If set to false, will ignore repeat system keypress events if a key is already pressed-down. Defaults to true. + */ + bool keyRepeat; std::vector joysticks; bool keyboardState[512]; diff --git a/Core/Contents/Include/PolyCoreServices.h b/Core/Contents/Include/PolyCoreServices.h index 70353375e..fceac1559 100755 --- a/Core/Contents/Include/PolyCoreServices.h +++ b/Core/Contents/Include/PolyCoreServices.h @@ -34,12 +34,12 @@ namespace Polycode { class Config; class FontManager; class SceneManager; - class ScreenManager; class TimerManager; class TweenManager; class ResourceManager; class SoundManager; class Core; + class CoreInput; class CoreMutex; class Logger; @@ -66,6 +66,7 @@ namespace Polycode { Renderer *getRenderer(); void Update(int elapsed); + void fixedUpdate(); void Render(); void setCore(Core *core); @@ -81,6 +82,13 @@ namespace Polycode { * @see Core */ Core *getCore(); + + /** + * Returns the core input. + * @return Core input. + * @see CoreInput + */ + CoreInput *getInput(); void handleEvent(Event *event); @@ -98,12 +106,6 @@ namespace Polycode { */ MaterialManager *getMaterialManager(); - /** - * Returns the screen manager. The screen manager is responsible for maintaining and rendering 2D screens. - * @return Screen Manager - * @see ScreenManager - */ - ScreenManager *getScreenManager(); /** * Returns the scene manager. The screen manager is responsible for maintaining and rendering 3D scenes. @@ -161,9 +163,7 @@ namespace Polycode { Config *getConfig(); - ~CoreServices(); - - void *focusedChild; + ~CoreServices(); protected: @@ -182,7 +182,6 @@ namespace Polycode { Core *core; Config *config; MaterialManager *materialManager; - ScreenManager *screenManager; SceneManager *sceneManager; Logger *logger; TimerManager *timerManager; @@ -192,4 +191,8 @@ namespace Polycode { FontManager *fontManager; Renderer *renderer; }; + + + CoreServices *Services(); + } diff --git a/Core/Contents/Include/PolyEntity.h b/Core/Contents/Include/PolyEntity.h index c22ebc56f..28e1fe2a2 100755 --- a/Core/Contents/Include/PolyEntity.h +++ b/Core/Contents/Include/PolyEntity.h @@ -24,9 +24,11 @@ #include "PolyGlobals.h" #include "PolyString.h" #include "PolyMatrix4.h" +#include "PolyVector2.h" #include "PolyQuaternion.h" #include "PolyColor.h" #include "PolyRectangle.h" +#include "PolyRay.h" #include "PolyEventDispatcher.h" #include @@ -34,11 +36,23 @@ namespace Polycode { class Renderer; + class _PolyExport MouseEventResult { + public: + bool hit; + bool blocked; + }; + class _PolyExport EntityProp { public: String propName; String propValue; }; + + class _PolyExport AABB { + public: + Vector3 min; + Vector3 max; + }; class _PolyExport Rotation { public: @@ -64,8 +78,13 @@ namespace Polycode { class _PolyExport Entity : public EventDispatcher { public: Entity(); + + Entity(Number width, Number height, Number depth = 0.01); + virtual ~Entity(); + + void initEntity(); /** * Main render method. Override this to do your own drawing. */ @@ -74,8 +93,9 @@ namespace Polycode { * Main update method. Override this to do your updates before the render cycle. */ virtual void Update(){}; - - virtual void transformAndRender(); + virtual void fixedUpdate(){}; + + void transformAndRender(); void renderChildren(); @@ -89,7 +109,7 @@ namespace Polycode { virtual Entity *Clone(bool deepClone, bool ignoreEditorOnly) const; /** - * This method must be implemented by all subvlasses implementing Clone. + * This method must be implemented by all subclasses implementing Clone. */ virtual void applyClone(Entity *clone, bool deepClone, bool ignoreEditorOnly) const; @@ -128,8 +148,18 @@ namespace Polycode { */ Matrix4 getConcatenatedMatrix(); + /** + * Returns the concatenated matrix up to the specified parent entity. + * @param relativeEntity Parent entity, relative to which to return the transform matrix. + */ Matrix4 getConcatenatedMatrixRelativeTo(Entity *relativeEntity); + /** + * Returns the concatenated matrix, multiplied by the entity's anchor adjustment. + * @see setAnchorPoint + */ + Matrix4 getAnchorAdjustedMatrix(); + /** * Returns Same as getConcatenatedMatrix(), but contains only roll information for rotation. Used internally for billboards. @return Entity's concatenated roll matrix. @@ -157,38 +187,39 @@ namespace Polycode { /** @name Hierarchy operations. * These methods add and remove entities to and from each other. */ - //@{ - - /** - * @name Child position operations. - * - * Move a child in the list of children. Affects display - * order of entities(entities further down in the list will - * appear on top). - */ - //@{ - void moveChildUp(Entity *child); - void moveChildDown(Entity *child); - void moveChildTop(Entity *child); - void moveChildBottom(Entity *child); - //}@ - - /** - * @see addChild() - */ - virtual void addEntity(Entity *newChild); + //@{ /** * Adds another entity as a child. The children inherit the parent's transforms. @param newChild The entity to be added. */ - void addChild(Entity *newChild); + virtual void addChild(Entity *newChild); /** * Removes an entity from the entity's children. @param entityToRemove Entity to be removed. */ - void removeChild(Entity *entityToRemove); + virtual void removeChild(Entity *entityToRemove); + + /** + * Moves the specified child one position up the render list. + */ + void moveChildUp(Entity *child); + + /** + * Moves the specified child one position down the render list. + */ + void moveChildDown(Entity *child); + + /** + * Moves the specified child up to the top of the render list. + */ + void moveChildTop(Entity *child); + + /** + * Moves the specified child up to the bottom of the render list. + */ + void moveChildBottom(Entity *child); /** * Manually sets the entity's parent. This method does not add the entity to the parent and should not be called manually. @@ -220,7 +251,10 @@ namespace Polycode { */ bool ownsChildren; - + /** + * Sets the ownsChildren flag for this entity and recursively for all its child entities. + * @see ownsChildren + */ void setOwnsChildrenRecursive(bool val); //@} // ---------------------------------------------------------------------------------------------------------------- @@ -237,6 +271,11 @@ namespace Polycode { */ Vector3 getPosition() const; + /** + * Returns the entity's position as a Vector2 + */ + Vector2 getPosition2D() const; + /** * Returns the entity's position added to the combined position of its parent. This method is here only for convenience of calculating certain properties and should not be used to get an entity's actual position in the world. To get the actual world position of the entity, use the entity's concatendated matrix. @see getConcatenatedMatrix() @@ -250,7 +289,7 @@ namespace Polycode { @param y Y-axis value. @param z Z-axis value. */ - void setPosition(Number x, Number y, Number z); + void setPosition(Number x, Number y, Number z=0.0); /** * Sets the entity's position with a vector. @@ -276,7 +315,7 @@ namespace Polycode { @param y Y-axis value. @param z Z-axis value. */ - void Translate(Number x, Number y, Number z); + void Translate(Number x, Number y, Number z=0.0); /** * Translates the entity relative to its current position with a vector. @@ -315,15 +354,22 @@ namespace Polycode { @param y Y-axis value. @param z Z-axis value. */ - void Scale(Number x, Number y, Number z); + void Scale(Number x, Number y, Number z=0.0); + + /** + * Scales the entity relative to its current scale. + @param scale Scale vector. + */ + void Scale(const Vector3 &scale); + /** * Sets the entity's scale. @param x X-axis value. @param y Y-axis value. @param z Z-axis value. */ - void setScale(Number x, Number y, Number z); + void setScale(Number x, Number y, Number z=1.0); /** * Sets the entity's scale. @@ -342,6 +388,12 @@ namespace Polycode { @return Entity's scale as a vector. */ Vector3 getScale() const; + + /** + * Returns the entity's rotation as euler angles + @return Entity's rotation as euler angles + */ + Vector3 getRotationEuler() const; /** * Returns the entity's pitch combined with the combined pitch of its parent. @@ -366,6 +418,12 @@ namespace Polycode { */ void rebuildRotation(); + /** + * Sets rotation from euler angles + * @param rotation New rotation values + */ + void setRotationEuler(const Vector3 &rotation); + /** * Sets the pitch rotation of the entity. * @param pitch New pitch value in degrees. @@ -419,19 +477,56 @@ namespace Polycode { * @return Current roll value. */ Number getRoll() const; - + + /** + * Returns the bounding box X value. + */ + Number getWidth() const; + + /** + * Returns the bounding box Y value. + */ + Number getHeight() const; + + /** + * Returns the bounding box Z value. + */ + Number getDepth() const; + + /** + * Sets the bounding box X value. + */ + void setWidth(Number width); + + /** + * Sets the bounding box Y value. + */ + void setHeight(Number height); + + /** + * Sets the bounding box Z value. + */ + void setDepth(Number depth); + /** * Sets the rotation with quaternion value. - * @param Current yaw value. - */ + */ void setRotationQuat(Number w, Number x, Number y, Number z); + + /* + * Sets the rotation with quaternion value. + */ + void setRotationByQuaternion(const Quaternion &quaternion); /** * Returns the current rotation as a quaternion. * @return Current rotation value. - */ + */ Quaternion getRotationQuat() const; + + Quaternion getConcatenatedQuat() const; + /** * Orients the entity towards the specified location with the provided up vector. The up vector determines which side of the entity will be pointing in that direction. * @param loc Location to look at. @@ -441,7 +536,7 @@ namespace Polycode { /** * Orients the entity towards another entity with the provided up vector. The up vector determines which side of the entity will be pointing in that direction. - * @param loc Location to look at. + * @param entity Entity to look at. * @param upVector The up vector. * @see lookAt() */ @@ -478,51 +573,38 @@ namespace Polycode { void setColor(Color color); //@} - // ---------------------------------------------------------------------------------------------------------------- - - /** @name Bounding box operations. - * These methods modify the bounding box of the entity. The bounding box is used for culling and collision detection. - */ - //@{ - - /** - * Recalculates the bounding box of the entity based on its size. - */ - void recalculateBBox(); - - /** - * Returns the bounding box radius. - * @return The bounding box radius. - */ - Number getBBoxRadius() const; - - /** - * Returns the entity's bounding box radius compounded from its children's bounding box radii. - * @return The compound bounding box radius. - */ - Number getCompoundBBoxRadius() const; - - /** - * Sets the bounding box radius. - * @param rad New bounding box radius. - */ - void setBBoxRadius(Number rad); + - + /** + * Sets the anchor (center) point of the entity as normalized half bounding box coordinates. (i.e. -1.0 or 1.0 will offset the entity by half on a particular axis). + * @param anchorPoint Anchor point as a 3D Vector. + */ + void setAnchorPoint(const Vector3 &anchorPoint); + + /** + * Sets the anchor (center) point of the entity as normalized half bounding box coordinates. (i.e. -1.0 or 1.0 will offset the entity by half on a particular axis). + * @param x X Offset + * @param y Y Offset + * @param z Z Offset + */ + void setAnchorPoint(Number x, Number y, Number z); + + /** + * Returns the current anchor (center) point of the entity. + */ + Vector3 getAnchorPoint() const; + + virtual MouseEventResult onMouseDown(const Ray &ray, int mouseButton, int timestamp); + virtual MouseEventResult onMouseUp(const Ray &ray, int mouseButton, int timestamp); + virtual MouseEventResult onMouseMove(const Ray &ray, int timestamp); + virtual MouseEventResult onMouseWheelUp(const Ray &ray, int timestamp); + virtual MouseEventResult onMouseWheelDown(const Ray &ray, int timestamp); - //@} - // ---------------------------------------------------------------------------------------------------------------- /** @name Rendering properties * Methods and properties affecting the way the entity is rendered. */ - //@{ - - - /** - * You can set a custom string identifier for user purposes. - */ - String custEntityType; + //@{ /** * If this flag is true, the entity will always face the camera. False by default. @@ -539,21 +621,6 @@ namespace Polycode { * matrix when billboardMode is enabled */ bool billboardIgnoreScale; - - /** - * Normally, translucent textures do not affect the depth buffer, but if this flag is set to true, this entity's alpha channel is written to the depth buffer at a preset threshold. This flag is set to false by default. - */ - bool alphaTest; - - /** - * If this flag is set to false, backface culling is disabled when rendering this entity, rendering both sides of each face. Set to true by default. - */ - bool backfaceCulled; - - /** - * If this flag is set to true, the entity will render in wireframe. - */ - bool renderWireframe; /** * The entity's color. @@ -581,7 +648,8 @@ namespace Polycode { bool depthTest; /** - * Entity blending mode. Possible values are Renderer::BLEND_MODE_NORMAL, Renderer::BLEND_MODE_LIGHTEN, Renderer::BLEND_MODE_COLOR, Renderer::BLEND_MODE_PREMULTIPLIED, Renderer::BLEND_MODE_MULTIPLY. See the Renderer class for details on individual blending modes. + * Entity blending mode. Possible values are Renderer::BLEND_MODE_NONE, Renderer::BLEND_MODE_NORMAL, Renderer::BLEND_MODE_LIGHTEN, Renderer::BLEND_MODE_COLOR, Renderer::BLEND_MODE_PREMULTIPLIED, Renderer::BLEND_MODE_MULTIPLY. See the Renderer class for details on individual blending modes. + This blending mode is overridden by the material. */ int blendingMode; @@ -594,7 +662,6 @@ namespace Polycode { * If set to false, the children will be rendered even if the entity is invisible. */ bool visibilityAffectsChildren; - /** * If this flag is set to true, this entity will render only into the depth buffer. This, effectively, means that it will be invisible, but still obscuring other entities. @@ -626,77 +693,242 @@ namespace Polycode { */ void setBlendingMode(int newBlendingMode); + /** + * Returns the first child entity that has the specified string id. + * @param id Specified id to search for. + * @param recursive If set to true, will search all child entities recursively. + * @return Entity with specified string id or NULL if not found. + */ Entity *getEntityById(String id, bool recursive) const; + + /** + * Returns all child entities which have the specified tag. + * @param tag Tag to search for. + * @param recursive If set to true, will search all child entities recursively. + * @return List of child entities that contain the specified tag. + */ std::vector getEntitiesByTag(String tag, bool recursive) const; - - Vector3 getChildCenter() const; - - std::vector entityProps; + + /** + * Returns all child entities that have the specified layer ID. Layer IDs are used by the entity instances to separate entities into groups. + * @param Layer ID to search for. + * @param recursive If set to true, will search all child entities recursively. + * @return List of child entities that contain the specified layer ID. + */ + std::vector getEntitiesByLayerID(unsigned char layerID, bool recursive) const; + + /** + * Returns custom string dictionary property of the entity based on the property name. + * @param Property name to look up. + * @return String property for specified property name or "null" if this property doesn't exist. + */ String getEntityProp(const String& propName); + + /** + * Sets the entity property for a specified property name in the entity's custom property dictionary. + * @param propName Property name to set. + * @param propValue Value to set for the specified property name. + */ void setEntityProp(const String& propName, const String& propValue); - void doUpdates(); + /** + * If set to true, the y position of the entity matrix will be multiplied by -1.0, inverting its Y-axis coordinate system. + */ + void setInverseY(bool val); + + /** + * Returns true if the entity is set to use an inverse Y-coordinate system. + */ + bool getInverseY(); + + void doUpdates(); + void doFixedUpdates(); virtual Matrix4 buildPositionMatrix(); - virtual void adjustMatrixForChildren(){} - void setRenderer(Renderer *renderer); - + void setRenderer(Renderer *renderer); - Vector3 bBox; + /** + * Implement this method to do custom ray hit detection beyond a bounding box check. Always returns true by default. + */ + virtual bool customHitDetection(const Ray &ray) { return true; } + /** + * If set to true, the entity's transformations will not be affected by its parents. Defaults to false. + */ bool ignoreParentMatrix; - bool enableScissor; + /** + * If set to true, will constrain the rendering of this entity into the viewport coordinates defined by scissorBox. + * @see scissorBox + */ + bool enableScissor; + + /** + * Defines the viewport coordinates to clip rendering to if enableScissor is defined. + * @see enableScissor + */ Polycode::Rectangle scissorBox; - - Vector3 position; - Vector3 scale; - Rotation rotation; - + /** + * Flags an editor only entity. If set to true, this entity will not be saved to file by entity instances or show up in the IDE entity editor. + */ bool editorOnly; - - /** @name Class and ID strings - * These properties can be used to set and retrieve string-based ids and - * tags - */ - //@{ + /** + * String ID of the entity. Can be used to retrieve specific entities by their ID. + */ String id; + /** + * Returns the number of tags this entity has. + */ unsigned int getNumTags() const; + + /** + * Returns the tag at specified index or an empty string if index is invalid. + */ String getTagAtIndex(unsigned int index) const; + + /** + * Returns true if this entity contains the specified tag. + * @param tag Tag to look up. + * @return True if this entity contains the specified tag, false if it doesn't. + */ bool hasTag(String tag) const; + /** + * Removes all tags from this entity. + */ void clearTags(); - void addTag(String tag); - - - - //@} + + /** + * Adds a string tag to the entity. + * @param tag Tag to add. + */ + void addTag(String tag); + + /** + * Entity collision type for physics module. This is set per physics module documentaiton. + */ + unsigned char collisionShapeType; + + /** + * If set to true, will automatically process mouse events and dispatch its own input events if mouse events intersect with the entity's bounding box. Defaults to false. + Attention: All of the entity's parents' processInputEvents flags must be set to true for this to function including the parent Scene's rootEntity! + */ + bool processInputEvents; + + /** + * If set to true, will block input events for entities below itself in the parent's entiy list. + */ + bool blockMouseInput; + + + /** + * Returns the screen pixel position of the entity using a specified projection matrix, camera matrix and viewport. + * @param projectionMatrix Projection matrix to use. + * @param cameraMatrix Camera matrix to use. + * @param viewport Viewport rectangle. + * @return Pixel position of the entity on the screen. + */ + Vector2 getScreenPosition(const Matrix4 &projectionMatrix, const Matrix4 &cameraMatrix, const Polycode::Rectangle &viewport); + + /** + * Returns the screen pixel position of the entity using the last projection matrix, camera matrix and viewport that were set in the renderer. + * @return Pixel position of the entity on the screen. + */ + Vector2 getScreenPositionForMainCamera(); + + + /** + * If set to true, will round the position of this entity to integral values. Use this if you need pixel-perfect positioning in 2D. + */ + bool snapToPixels; + + bool mouseOver; + + /** + * Sets the default blending mode for all created entities. + */ + static int defaultBlendingMode; + + void recalculateAABBAllChildren(); + void recalculateAABB(); + + /** + Return axis-aligned bounding box in world space. + */ + AABB getWorldAABB(); + + /** + * Returns the bounding box of the entity. This is used for hit-testing as well as visibility calculation. + */ + Vector3 getLocalBoundingBox(); + + /** + * Sets the bounding box of the entity as a 3D Vector. This is used for hit-testing as well as visibility calculation. + */ + void setLocalBoundingBox(const Vector3 box); + + /** + * Sets the bounding box of the entity. This is used for hit-testing as well as visibility calculation. + */ + void setLocalBoundingBox(Number x, Number y, Number z); + + /** + * Sets the bounding box X-axis value of the entity. + */ + void setLocalBoundingBoxX(Number x); + + /** + * Sets the bounding box Y-axis value of the entity. + */ + void setLocalBoundingBoxY(Number y); + + /** + * Sets the bounding box Z-axis value of the entity. + */ + void setLocalBoundingBoxZ(Number z); + + bool rendererVis; + + /** + * Layer ID. Used by entity instances to separate entities into groups. + */ + unsigned char layerID; + + std::vector entityProps; + protected: + + AABB aabb; + Vector3 bBox; + + int lastClickTicks; + Number yAdjust; std::vector *tags; - void checkTransformSetters(); - void *userData; std::vector children; - Vector3 childCenter; - Number bBoxRadius; + Vector3 anchorPoint; - Vector3 _position; - Vector3 _scale; - Rotation _rotation; + Vector3 position; + Vector3 scale; + Vector3 rotation; Quaternion rotationQuat; bool lockMatrix; bool matrixDirty; - Matrix4 transformMatrix; - Number matrixAdj; + + Matrix4 transformMatrix; Entity *parentEntity; Renderer *renderer; }; + + typedef Entity SceneEntity; + typedef Entity ScreenEntity; + } diff --git a/Core/Contents/Include/PolyEvent.h b/Core/Contents/Include/PolyEvent.h index 340bed52a..6a3fb6dcf 100755 --- a/Core/Contents/Include/PolyEvent.h +++ b/Core/Contents/Include/PolyEvent.h @@ -60,6 +60,9 @@ namespace Polycode { void setEventCode(int eventCode); void setDispatcher(EventDispatcher *dispatcher); const String& getEventType() const; + + + void cancelEvent(); // In order to prevent "namespace" collisions between events of different types, all event integers must be unique. // This is managed by arbitrarily assigning each class a "base" constant, and adding it to all its event type constants. @@ -86,16 +89,17 @@ namespace Polycode { static const int RESOURCE_RELOAD_EVENT = EVENTBASE_EVENT+5; static const int SELECT_EVENT = EVENTBASE_EVENT+6; static const int REMOVE_EVENT = EVENTBASE_EVENT+7; - + static const int RESOURCE_CHANGE_EVENT = EVENTBASE_EVENT+8; + static const int EVENTBASE_NONPOLYCODE = 0x10000; bool deleteOnDispatch; - + bool cancelEventFlag; + protected: String eventType; EventDispatcher *dispatcher; int eventCode; - }; } diff --git a/Core/Contents/Include/PolyEventDispatcher.h b/Core/Contents/Include/PolyEventDispatcher.h index 235b71f85..32aff5853 100755 --- a/Core/Contents/Include/PolyEventDispatcher.h +++ b/Core/Contents/Include/PolyEventDispatcher.h @@ -64,7 +64,21 @@ typedef struct { * @see EventHandler */ void addEventListener(EventHandler *handler, int eventCode); - + + /** + * Adds an event listener for specified event code if it hasn't already been added, otherwise does nothing. + * @param handler The event handler to add as a listener + * @param eventCode The requested event code to listen to. + */ + void addEventListenerUnique(EventHandler *handler, int eventCode); + + /** + * Returns true if this event dispatcher is registered with the specified EventHandler with the specified event code. + * @param handler EventHandler to check. + * @param eventCode The event code to check. + */ + bool hasEventListener(EventHandler *handler, int eventCode); + /** * Removes a listener for a specific handler and event code. * @param handler The event handler to remove as a listener diff --git a/Core/Contents/Include/PolyEventHandler.h b/Core/Contents/Include/PolyEventHandler.h index 5d3c4165d..68400d453 100755 --- a/Core/Contents/Include/PolyEventHandler.h +++ b/Core/Contents/Include/PolyEventHandler.h @@ -35,20 +35,12 @@ namespace Polycode { * Default constructor */ EventHandler(); - virtual ~EventHandler(); - - void secondaryHandler(Event *event); - - /** - * This method gets called by an EventDispatcher that the handler is listening to if the dispatching event's code matches the code that handler is listening for. Typically, you subclass EventHandler and implement the handleEvent method to handle specific events. - * @see EventDispatcher - */ - virtual void handleEvent(Event *event){} - - void *secondaryHandlerData; + virtual ~EventHandler(); - - protected: - + /** + * This method gets called by an EventDispatcher that the handler is listening to if the dispatching event's code matches the code that handler is listening for. Typically, you subclass EventHandler and implement the handleEvent method to handle specific events. + * @see EventDispatcher + */ + virtual void handleEvent(Event *event){} }; } diff --git a/Core/Contents/Include/PolyFont.h b/Core/Contents/Include/PolyFont.h index 2952cfee9..58d22105e 100755 --- a/Core/Contents/Include/PolyFont.h +++ b/Core/Contents/Include/PolyFont.h @@ -33,7 +33,7 @@ namespace Polycode { class _PolyExport Font : public PolyBase { public: - Font(const String& fileName); + Font(const String& fileName, FT_Library FTLibrary); virtual ~Font(); FT_Face getFace(); @@ -47,8 +47,7 @@ namespace Polycode { protected: String fileName; - String fontName; - + String fontName; unsigned char *buffer; bool valid; FT_Face ftFace; diff --git a/Core/Contents/Include/PolyFontGlyphSheet.h b/Core/Contents/Include/PolyFontGlyphSheet.h new file mode 100755 index 000000000..47ba2c887 --- /dev/null +++ b/Core/Contents/Include/PolyFontGlyphSheet.h @@ -0,0 +1,86 @@ + +#pragma once +#include "PolyGlobals.h" +#include "ft2build.h" +#include "PolyString.h" +#include "PolyVector2.h" +#include +#include +#include + +#include FT_FREETYPE_H + +namespace Polycode { + + class String; + class Texture; + class Image; + class Font; + + struct FontTextureGlyph { + Vector2 offset[4]; + Vector2 texCoord[4]; + Vector2 advance; + }; + + /** Wraps a sheet of rendered font glyphs on a Texture. + * Use in combination with TextMesh to render text from minimal texture creation. */ + class _PolyExport FontGlyphSheet : public PolyBase { + public: + + enum FontTextureGlyphMode { + /** Regular anti-aliased font rendering. Colour is pure-white for clean custom tinting with an alpha channel. */ + ANTIALIAS, + /** Using distance-from-edge calculation as described in the Valve paper. + * + * "Improved Alpha-Tested Magniï¬cation for Vector Textures and Special Effects" + * http://www.valvesoftware.com/publications/2007/SIGGRAPH2007_AlphaTestedMagnification.pdf + * + * To make the most of this: + * set renderer->alphaTestValue = 0.5 + * set sceneMesh->alphaTest = true + * set sceneMesh->blendingMode = Renderer::BLEND_MODE_NONE; + * + * Or use a custom shader - alpha values of 0.5 indicate the boundary. + * */ + ALPHA_TEST + }; + + FontGlyphSheet(Font* font, int size = 32, FontTextureGlyphMode mode = ANTIALIAS); + virtual ~FontGlyphSheet(); + + void setMode(FontTextureGlyphMode mode) { this->mode = mode; } + /** Set height of font to be rendered in pixels. */ + void setSize(int size); + + /** Scans extraCharacters for anything that isn't currently in the rendered sheet and rebuilds the sheet if necessary. */ + void addGlyphs(String extraCharacters); + + /** Convenience method to build a sheet with all of the visible ASCII characters. */ + void buildVisibleAscii(); + + /** Convenience method to build a sheet of glyphs with one of each of what is in characters. */ + void buildGlyphs(String characters); + + /** Creates the sheet given a set of characters. */ + void buildGlyphs(std::set characters); + + /** Returns the currently rendered characters as a set. */ + std::set getCharacters() const; + + /** Used by TextMesh to generate the vertices for the given text into the vertex array. + @return the next index after that which was used */ + //int renderStringVertices(String text, std::vector& vertices, int index = 0); + + Texture* getTexture() { return texture; } + + int tabWidth; + + protected: + Font* font; + FontTextureGlyphMode mode; + Texture* texture; + std::map locations; + int size; + }; +} diff --git a/Core/Contents/Include/PolyFontManager.h b/Core/Contents/Include/PolyFontManager.h index 21d76c430..a53ef95ef 100644 --- a/Core/Contents/Include/PolyFontManager.h +++ b/Core/Contents/Include/PolyFontManager.h @@ -25,6 +25,8 @@ THE SOFTWARE. #include "PolyGlobals.h" #include "PolyString.h" #include +#include "ft2build.h" +#include FT_FREETYPE_H namespace Polycode { @@ -58,15 +60,31 @@ namespace Polycode { */ Font *getFontByName(const String& fontName); + /** + * Returns number of registered fonts. + */ unsigned int getNumFonts() const; + + /** + * Returns the font entry by specified index or NULL if index is invalid. + */ FontEntry *getFontEntryByIndex(const unsigned int index); + /** + * Returns the font entry based on the font path or NULL if no fonts are registered with the specified path. + */ FontEntry *getFontEntryByFontPath(const String &fontPath); + /** + * Removes the font entry from manager and optionally delets the associated Font. + * @param entry FontEntry to remove. + + */ void removeFontEntry(FontEntry *entry, bool deleteFont); private: + FT_Library FTLibrary; std::vector fonts; }; diff --git a/Core/Contents/Include/PolyGLES1Renderer.h b/Core/Contents/Include/PolyGLES1Renderer.h deleted file mode 100644 index 81d35f4db..000000000 --- a/Core/Contents/Include/PolyGLES1Renderer.h +++ /dev/null @@ -1,143 +0,0 @@ -/* -Copyright (C) 2011 by Ivan Safrin - -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: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -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. -*/ - - -#pragma once -#include "PolyString.h" -#include "PolyLogger.h" -#include "PolyGlobals.h" -#include "PolyRenderer.h" -#include "PolyTexture.h" -#include "PolyGLES1Texture.h" -#include "PolyCubemap.h" -#include "PolyFixedShader.h" -#include "PolyMesh.h" - -#include -#include -#include - -namespace Polycode { - class _PolyExport OpenGLES1Renderer : public Renderer { - - public: - - OpenGLES1Renderer(); - virtual ~OpenGLES1Renderer(); - - void Resize(int xRes, int yRes); - void BeginRender(); - void EndRender(); - - Cubemap *createCubemap(Texture *t0, Texture *t1, Texture *t2, Texture *t3, Texture *t4, Texture *t5); - Texture *createTexture(unsigned int width, unsigned int height, char *textureData, bool clamp, int type=Image::IMAGE_RGBA); - Texture *createFramebufferTexture(unsigned int width, unsigned int height); - void createRenderTextures(Texture **colorBuffer, Texture **depthBuffer, int width, int height); - - void enableAlphaTest(bool val); - - void createVertexBufferForMesh(Mesh *mesh); - void drawVertexBuffer(VertexBuffer *buffer); - - void bindFrameBufferTexture(Texture *texture); - void unbindFramebuffers(); - - void setOrthoMode(); - void setPerspectiveMode(); - - void enableBackfaceCulling(bool val); - void setViewportSize(int w, int h, Number fov=45.0f); - - void setLineSmooth(bool val); - - void loadIdentity(); - void setClearColor(Number r, Number g, Number b); - - void setTexture(Texture *texture); - void draw2DPolygon(Polygon *polygon); - void draw2DVertex(Vertex *vertex); - - void renderToTexture(Texture *targetTexture); - void renderZBufferToTexture(Texture *targetTexture); - void clearScreen(); - - void translate2D(Number x, Number y); - void rotate2D(Number angle); - void scale2D(Vector2 *scale); - - void setLineSize(Number lineSize); - - void setVertexColor(Number r, Number g, Number b, Number a); - - void setBlendingMode(int blendingMode); - - void enableLighting(bool enable); - void enableFog(bool enable); - void setFogProperties(int fogMode, Color color, Number density, Number startDepth, Number endDepth); - - void draw3DPolygon(Polygon *polygon); - void draw3DVertex(Vertex *vertex, Vector2 *faceUV); - void draw3DVertex2UV(Vertex *vertex, Vector2 *faceUV1, Vector2 *faceUV2); - void draw3DLine(Vector3 origin, Vector3 direction, Number length, Color color); - - virtual void setNormal(const Vector3 &normal); - - void translate3D(Vector3 *position); - void translate3D(Number x, Number y, Number z); - void scale3D(Vector3 *scale); - - Matrix4 getProjectionMatrix(); - Matrix4 getModelviewMatrix(); - void setModelviewMatrix(Matrix4 m); - void multModelviewMatrix(Matrix4 m); - - void enableDepthTest(bool val); - void drawScreenQuad(Number qx, Number qy); - - void beginRenderOperation(int meshType); - void endRenderOperation(); - - void pushMatrix(); - void popMatrix(); - - bool test2DCoordinate(Number x, Number y, Polygon *poly, const Matrix4 &matrix, bool billboardMode); - - void setFOV(Number fov); - - Vector3 Unproject(Number x, Number y); - - void clearShader(); - void applyMaterial(Material *material, ShaderBinding *localOptions, unsigned int shaderIndex); - - protected: - - GLuint defaultFramebuffer, colorRenderbuffer; - - Number nearPlane; - Number farPlane; - - GLfloat sceneProjectionMatrix[16]; - - - }; -} - diff --git a/Core/Contents/Include/PolyGLES1Texture.h b/Core/Contents/Include/PolyGLES1Texture.h deleted file mode 100644 index 81932c45c..000000000 --- a/Core/Contents/Include/PolyGLES1Texture.h +++ /dev/null @@ -1,57 +0,0 @@ -/* -Copyright (C) 2011 by Ivan Safrin - -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: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -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. -*/ - - -#pragma once -#include "PolyString.h" -#include "PolyGlobals.h" -#include "PolyTexture.h" -#include "PolyGLES1Renderer.h" - -#include -#include - -namespace Polycode { - - class _PolyExport OpenGLES1Texture : public Texture { - public: - OpenGLES1Texture(unsigned int width, unsigned int height); - OpenGLES1Texture(unsigned int width, unsigned int height, char *textureData, bool clamp, int filteringMode); - virtual ~OpenGLES1Texture(); - - void recreateFromImageData(); - - GLuint getTextureID(); - GLuint getFrameBufferID(); - - void setGLInfo(GLuint textureID, GLuint frameBufferID); - - void setTextureData(char *data); - - private: - - int filteringMode; - GLuint textureID; - GLuint frameBufferID; - }; - -} \ No newline at end of file diff --git a/Core/Contents/Include/PolyGLRenderer.h b/Core/Contents/Include/PolyGLRenderer.h index 514cefcf7..7679c7fb5 100755 --- a/Core/Contents/Include/PolyGLRenderer.h +++ b/Core/Contents/Include/PolyGLRenderer.h @@ -1,4 +1,4 @@ - + /* Copyright (C) 2011 by Ivan Safrin @@ -109,29 +109,30 @@ namespace Polycode { Cubemap *createCubemap(Texture *t0, Texture *t1, Texture *t2, Texture *t3, Texture *t4, Texture *t5); Texture *createTexture(unsigned int width, unsigned int height, char *textureData, bool clamp, bool createMipmaps, int type = Image::IMAGE_RGBA); - void destroyTexture(Texture *texture); + void destroyTexture(Texture *texture); + void destroyVertexBuffer(VertexBuffer *buffer); + Texture *createFramebufferTexture(unsigned int width, unsigned int height); void createRenderTextures(Texture **colorBuffer, Texture **depthBuffer, int width, int height, bool floatingPointBuffer); void enableAlphaTest(bool val); - void createVertexBufferForMesh(Mesh *mesh); + VertexBuffer *createVertexBufferForMesh(Mesh *mesh); void drawVertexBuffer(VertexBuffer *buffer, bool enableColorBuffer); void bindFrameBufferTexture(Texture *texture); void bindFrameBufferTextureDepth(Texture *texture); void unbindFramebuffers(); + Vector2 Project(const Matrix4 &cameraMatrix, const Matrix4 &projectionMatrix, const Polycode::Rectangle &viewport, const Vector3 &coordiante) const; + void cullFrontFaces(bool val); void pushRenderDataArray(RenderDataArray *array); - RenderDataArray *createRenderDataArrayForMesh(Mesh *mesh, int arrayType); - RenderDataArray *createRenderDataArray(int arrayType); - void setRenderArrayData(RenderDataArray *array, Number *arrayData); - void drawArrays(int drawType); + void drawArrays(int drawType, IndexDataArray *indexArray); - void setOrthoMode(Number xSize=0.0f, Number ySize=0.0f, bool centered = false); - void _setOrthoMode(Number orthoSizeX, Number orthoSizeY); - void setPerspectiveMode(); + void setProjectionOrtho(Number xSize=0.0f, Number ySize=0.0f, Number near=-256.0f, Number far=256.0f, bool centered = false); + void setProjectionMatrix(Matrix4 matrix); + void setPerspectiveDefaults(); void enableBackfaceCulling(bool val); @@ -144,19 +145,23 @@ namespace Polycode { void setTexture(Texture *texture); Image *renderScreenToImage(); - void clearScreen(); + Image *renderBufferToImage(Texture *texture); + void clearScreen(bool clearColor = true, bool clearDepth = true); void translate2D(Number x, Number y); void rotate2D(Number angle); - void scale2D(Vector2 *scale); + void scale2D(const Vector2 &scale); void enableScissor(bool val); void setScissorBox(Polycode::Rectangle box); - Vector3 projectRayFrom2DCoordinate(Number x, Number y, Matrix4 cameraMatrix, Matrix4 projectionMatrix); + Vector3 projectRayFrom2DCoordinate(Number x, Number y, const Matrix4 &cameraMatrix, const Matrix4 &projectionMatrix, const Polycode::Rectangle &viewport); + Polycode::Rectangle getViewport(); void setLineSize(Number lineSize); - + void setPointSize(Number pointSize); + void setPointSmooth(bool val); + void setVertexColor(Number r, Number g, Number b, Number a); void setBlendingMode(int blendingMode); @@ -165,20 +170,23 @@ namespace Polycode { void enableFog(bool enable); void setFogProperties(int fogMode, Color color, Number density, Number startDepth, Number endDepth); - void translate3D(Vector3 *position); + void translate3D(const Vector3 &position); void translate3D(Number x, Number y, Number z); - void scale3D(Vector3 *scale); + void scale3D(const Vector3 &scale); Matrix4 getProjectionMatrix(); Matrix4 getModelviewMatrix(); void setModelviewMatrix(Matrix4 m); - void multModelviewMatrix(Matrix4 m); + void multModelviewMatrix(Matrix4 m); + + void setWireframePolygonMode(bool val); void enableDepthTest(bool val); void enableDepthWrite(bool val); - void setClippingPlanes(Number nearPlane_, Number farPlane_); - + void setProjectionFromFrustum(Number left, Number right, Number bottom, Number top, Number near, Number far); + void setProjectionFromFoV(Number fov, Number near, Number far); + void clearBuffer(bool colorBuffer, bool depthBuffer); void drawToColorBuffer(bool val); @@ -187,19 +195,15 @@ namespace Polycode { void pushMatrix(); void popMatrix(); - Vector3 Unproject(Number x, Number y); + Vector3 Unproject(Number x, Number y, const Matrix4 &cameraMatrix, const Matrix4 &projectionMatrix, const Polycode::Rectangle &viewport); void setDepthFunction(int depthFunction); void clearShader(); - void applyMaterial(Material *material, ShaderBinding *localOptions, unsigned int shaderIndex); protected: void initOSSpecific(); - Number nearPlane; - Number farPlane; - int verticesToDraw; GLdouble sceneProjectionMatrix[16]; diff --git a/Core/Contents/Include/PolyGLSLShader.h b/Core/Contents/Include/PolyGLSLShader.h index 2049aa974..677fd4474 100755 --- a/Core/Contents/Include/PolyGLSLShader.h +++ b/Core/Contents/Include/PolyGLSLShader.h @@ -31,17 +31,6 @@ namespace Polycode { class GLSLProgram; - typedef struct { - Texture *texture; - String name; - } GLSLTextureBinding; - - typedef struct { - Cubemap *cubemap; - String name; - } GLSLCubemapBinding; - - class _PolyExport GLSLShader : public Shader { public: GLSLShader(GLSLProgram *vp, GLSLProgram *fp); @@ -61,23 +50,5 @@ namespace Polycode { void linkProgram(); void unlinkProgram(); - }; - - class _PolyExport GLSLShaderBinding : public ShaderBinding { - public: - GLSLShaderBinding(GLSLShader *shader); - virtual ~GLSLShaderBinding(); - - void clearCubemap(const String& name); - void addTexture(const String& name, Texture *texture); - void addCubemap(const String& name, Cubemap *cubemap); - void clearTexture(const String& name); - Texture *getTexture(const String& name); - Cubemap *getCubemap(const String& name); - - std::vector textures; - std::vector cubemaps; - - GLSLShader *glslShader; - }; + }; } diff --git a/Core/Contents/Include/PolyGLSLShaderModule.h b/Core/Contents/Include/PolyGLSLShaderModule.h index eabfce9c5..37a2d570a 100755 --- a/Core/Contents/Include/PolyGLSLShaderModule.h +++ b/Core/Contents/Include/PolyGLSLShaderModule.h @@ -30,6 +30,7 @@ namespace Polycode { class ProgramParam; class GLSLShader; class ShaderProgram; + class ResourcePool; class _PolyExport GLSLShaderModule : public PolycodeShaderModule { public: @@ -40,8 +41,8 @@ namespace Polycode { ShaderProgram* createProgramFromFile(const String& extension, const String& fullPath); void reloadPrograms(); String getShaderType(); - Shader *createShader(TiXmlNode *node); - Shader *createShader(String name, String vpName, String fpName); + Shader *createShader(ResourcePool *resourcePool, TiXmlNode *node); + Shader *createShader(ResourcePool *resourcePool, String name, String vpName, String fpName); bool applyShaderMaterial(Renderer *renderer, Material *material, ShaderBinding *localOptions, unsigned int shaderIndex); void clearShader(); diff --git a/Core/Contents/Include/PolyGLVertexBuffer.h b/Core/Contents/Include/PolyGLVertexBuffer.h index 9ddd7b4fb..cf0cb8cb1 100644 --- a/Core/Contents/Include/PolyGLVertexBuffer.h +++ b/Core/Contents/Include/PolyGLVertexBuffer.h @@ -42,7 +42,7 @@ namespace Polycode { class _PolyExport OpenGLVertexBuffer : public VertexBuffer { public: - OpenGLVertexBuffer(Mesh *mesh); + explicit OpenGLVertexBuffer(Mesh *mesh); virtual ~OpenGLVertexBuffer(); GLuint getVertexBufferID(); @@ -50,14 +50,21 @@ namespace Polycode { GLuint getNormalBufferID(); GLuint getColorBufferID(); GLuint getTangentBufferID(); - + GLuint getIndexBufferID(); + GLuint getBoneWeightBufferID(); + GLuint getBoneIndexBufferID(); + protected: GLuint vertexBufferID; GLuint texCoordBufferID; GLuint normalBufferID; GLuint colorBufferID; - GLuint tangentBufferID; + GLuint tangentBufferID; + GLuint indexBufferID; + + GLuint boneWeightBufferID; + GLuint boneIndexBufferID; }; } diff --git a/Core/Contents/Include/PolyGlobals.h b/Core/Contents/Include/PolyGlobals.h index 2a7aadc3e..7d3e10a3e 100755 --- a/Core/Contents/Include/PolyGlobals.h +++ b/Core/Contents/Include/PolyGlobals.h @@ -27,7 +27,11 @@ THE SOFTWARE. // Compile support for lua bindings. //#define _COMPILE_LUA +#define POLYCODE_VERSION_STRING "0.8.4" + #define COMPILE_GL_RENDERER +typedef float PolyRendererVertexType; +typedef unsigned int PolyRendererIndexType; #ifdef _WINDOWS #define WIN32_LEAN_AND_MEAN @@ -38,10 +42,14 @@ THE SOFTWARE. #pragma warning(disable:4018) #pragma warning(disable:4996) #pragma warning(disable:4309) + #ifndef NULL #define NULL 0 + #endif // Prevent windows.h includes from generating min/max macros that // clash with the templates in + #ifndef NOMINMAX #define NOMINMAX + #endif #endif @@ -78,7 +86,22 @@ THE SOFTWARE. #define PLATFORM PLATFORM_UNIX #endif +#ifdef POLYCODE_NUMBER_IS_SINGLE +typedef float Number; +#else typedef double Number; +#endif + +#ifdef _MSC_VER +#if _MSC_VER<=1700 + +#include //cmath for "round / floor" + +inline int round(Number x) { + return floor(x + 0.5); +} +#endif +#endif #define RANDOM_NUMBER ((Number)rand()/(Number)RAND_MAX) diff --git a/Core/Contents/Include/PolyHTTPFetcher.h b/Core/Contents/Include/PolyHTTPFetcher.h new file mode 100644 index 000000000..ad9e2db65 --- /dev/null +++ b/Core/Contents/Include/PolyHTTPFetcher.h @@ -0,0 +1,97 @@ +/* +Copyright (C) 2015 by Joachim Meyer + +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: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +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. +*/ + +#pragma once +#include + +#include "PolyGlobals.h" +#include "PolyThreaded.h" + +#define HTTP_VERSION "HTTP/1.1" +#define DEFAULT_USER_AGENT "Polycode HTTP Fetcher/1.0" +#define DEFAULT_PAGE_BUF_SIZE 2048 + +namespace Polycode { + + class HTTPFetcherEvent : public Event { + public: + HTTPFetcherEvent() { contentSize = 0; errorCode = 0; data = NULL; storedInFile = false; } + ~HTTPFetcherEvent(){} + + //If storedInFile: data is the file path, else: data contains all the fetched data + char* data; + //Error code: contains either the errno / WSAError code or the HTTP error code or the HTTPFetcher error code + int errorCode; + + //Has the data been saved to a file or is it shipped with this event? + bool storedInFile; + //Size of the HTTP reply + unsigned long contentSize; + + static const int EVENTBASE_SOCKETEVENT = 0x500; + static const int EVENT_HTTP_ERROR = EVENTBASE_SOCKETEVENT + 2; + static const int EVENT_HTTP_DATA_RECEIVED = EVENTBASE_SOCKETEVENT + 3; + }; + + /** + * A utility to download a file from the WWW through HTTP. It is threaded (and therefor non blocking). + * If you want to use the data you might add an EventListener for the HTTPFetcherEvent::EVENT_HTTP_DATA_RECEIVED event code. + */ + class HTTPFetcher : public Threaded { + public: + /* + * Connects to a host and fetches a file given in the param + * @param address Full path including the hostname (Domain or IP) and protocol (http://) aswell as the path to the file on the server + * @param saveToPath true if you want the file to be directly saved, false if you just want the data as char array + * @param savePath Path String where the file should be saved to + */ + HTTPFetcher(String address, bool saveToPath = false, String savePath = ""); + ~HTTPFetcher(); + + String getData(); + + /* + * Fetches a file given in the param + * @param pathToFile Path String to the new file to fetch from the same host. Without leading "/" + * @param saveToPath true if you want the file to be directly saved, false if you just want the data as char array + * @param savePath Path String where the file should be saved to + */ + void fetchFile(String pathToFile, bool saveToPath = false, String savePath = ""); + + //The received data is more or less than the HTTP header told us it should be + static const int HTTPFETCHER_ERROR_WRONG_SIZE = 0x10F00; + + bool storeInFile; + + private: + int s; + String address; + String bodyReturn; + String path; + String host; + String protocol; + String savePath; + + bool createSocket(); + void updateThread(); + }; +} diff --git a/Core/Contents/Include/PolyImage.h b/Core/Contents/Include/PolyImage.h index 5c65825f3..65b8e346c 100755 --- a/Core/Contents/Include/PolyImage.h +++ b/Core/Contents/Include/PolyImage.h @@ -25,10 +25,23 @@ THE SOFTWARE. #include "PolyColor.h" #include "PolyRectangle.h" + namespace Polycode { class String; + + #define HALF_FLOAT_MIN_BIASED_EXP_AS_SINGLE_FP_EXP 0x38000000 + #define HALF_FLOAT_MAX_BIASED_EXP_AS_SINGLE_FP_EXP 0x47800000 + #define FLOAT_MAX_BIASED_EXP (0xFF << 23) + #define HALF_FLOAT_MAX_BIASED_EXP (0x1F << 10) + + typedef uint16_t hfloat; + typedef struct POLYIGNORE { + int size; + char **tokens; + } TokenArray; + /** * An image in memory. Basic RGB or RGBA images stored in memory. Can be loaded from PNG files, created into textures and written to file. */ @@ -39,7 +52,7 @@ namespace Polycode { * Create image from file name. * @param fileName Path to image file to load. */ - Image(const String& fileName); + explicit Image(const String& fileName); /** * Create a blank image of specified size and type. @@ -80,8 +93,10 @@ namespace Polycode { * @return True if successfully loaded, false otherwise. */ bool loadImage(const String& fileName); - bool loadPNG(const String& fileName); - + + static POLYIGNORE TokenArray readTokens(char *line, const char *tokens); + static POLYIGNORE void freeTokens(TokenArray tokens); + /** * Saves the image to a file. Currently only PNG files are supported. * @param fileName Path to image file to load. @@ -104,13 +119,13 @@ namespace Polycode { * @param width Width of the image to create. * @param height Height of the image to create. */ - void createEmpty(int width, int height); + void createEmpty(int width, int height, const Color &fillColor); /** * Fills the image with the specified color values. * @param color The color to fill it with. */ - void fill(Color color); + void fill(const Color &color); /** * Sets a pixel at specified coordinates to specified color. @@ -198,18 +213,6 @@ namespace Polycode { void fastBlurVert(int blurSize); void fastBlurHor(int blurSize); - /** - * Blurs the image using gaussian blur - * @param radius Radius of the blur - * @param deviation Standard deviation of the gaussian distribution - */ - void gaussianBlur(float radius, float deviation); - float* createKernel(float radius, float deviation); - - // What are these??? I wrote them way too long ago. - void darken(Number amt, bool color, bool alpha); - void lighten(Number amt, bool color, bool alpha); - void multiply(Number amt, bool color, bool alpha); /** * Returns an area of the image buffer. The area can go outside of image bounds, in which case the pixels not within the image are zeroed out. This method allocates new memory for the returned buffer and you must free it manually. @@ -244,8 +247,6 @@ namespace Polycode { int getType() const { return imageType; } - void writeBMP(const String& fileName) const; - /** * Returns the width of the image. */ @@ -262,6 +263,9 @@ namespace Polycode { */ char *getPixels(); + /** + * Multiplies the RGB values by alpha for each pixel. + */ void premultiplyAlpha(); static const int IMAGE_RGB = 0; @@ -270,6 +274,13 @@ namespace Polycode { protected: + bool loadHDR(const String &fileName); + bool loadPNG(const String& fileName); + bool loadSTB(const String &fileName); + + + static inline hfloat convertFloatToHFloat(float f); + void setPixelType(int type); // transform coordinates from external topleft position mode diff --git a/Core/Contents/Include/PolyInputEvent.h b/Core/Contents/Include/PolyInputEvent.h index 0306fbf1a..20c254dec 100755 --- a/Core/Contents/Include/PolyInputEvent.h +++ b/Core/Contents/Include/PolyInputEvent.h @@ -29,11 +29,27 @@ THE SOFTWARE. namespace Polycode { class TouchInfo { - public: + public: + static const int TYPEBASE = 0x500; + static const int TYPE_TOUCH = TYPEBASE + 0; + static const int TYPE_PEN = TYPEBASE + 1; + + TouchInfo(); + int id; Vector2 position; + int type; }; + /* + class PointerInfo { + public: + int id; + Vector2 position; + int flag; + }; + */ + /** * Event dispatched by CoreInput. This event is dispatched by CoreInput when input happens. */ @@ -73,8 +89,7 @@ namespace Polycode { static const int EVENT_TOUCHES_BEGAN = EVENTBASE_INPUTEVENT+20; static const int EVENT_TOUCHES_MOVED = EVENTBASE_INPUTEVENT+21; static const int EVENT_TOUCHES_ENDED = EVENTBASE_INPUTEVENT+22; - - + //@} // ---------------------------------------------------------------------------------------------------------------- @@ -98,6 +113,7 @@ namespace Polycode { PolyKEY key; + wchar_t getCharCode(); int keyCode() { return key; } @@ -109,12 +125,15 @@ namespace Polycode { std::vector touches; TouchInfo touch; - + int touchType; + unsigned int joystickDeviceID; float joystickAxisValue; unsigned int joystickButton; unsigned int joystickAxis; unsigned int joystickIndex; + + Number hitDistance; protected: diff --git a/Core/Contents/Include/PolyInputKeys.h b/Core/Contents/Include/PolyInputKeys.h index c82c5cf38..8541467a1 100755 --- a/Core/Contents/Include/PolyInputKeys.h +++ b/Core/Contents/Include/PolyInputKeys.h @@ -232,9 +232,9 @@ namespace Polycode { KEY_LEFT = 276, KEY_INSERT = 277, KEY_HOME = 278, - KEY_END = 279, + KEY_END = 279, KEY_PAGEUP = 280, - KEY_PAGEDOWN = 281, + KEY_PAGEDOWN = 281, /* Function keys */ KEY_F1 = 282, diff --git a/Core/Contents/Include/PolyLabel.h b/Core/Contents/Include/PolyLabel.h index 2129b2903..1edadd238 100755 --- a/Core/Contents/Include/PolyLabel.h +++ b/Core/Contents/Include/PolyLabel.h @@ -23,6 +23,7 @@ THE SOFTWARE. #pragma once #include "PolyString.h" #include "PolyGlobals.h" +#include "PolyColor.h" #include "PolyImage.h" #include "PolyFont.h" @@ -30,6 +31,8 @@ THE SOFTWARE. #include FT_GLYPH_H #include FT_IMAGE_H +#define LCD_BLEND_GAMMA 2.2 + namespace Polycode { class Font; @@ -56,54 +59,158 @@ namespace Polycode { unsigned int rangeEnd; }; + /** + * An image that can render text into itself. This class is mostly used internally in SceneLabel, but can be used by itself to manually create text-based textures. + */ class _PolyExport Label : public Image { public: - Label(Font *font, const String& text, int size, int antiAliasMode, bool premultiplyAlpha = false); + /** + * Create a text label. + * @param font Font to use for this label. + * @param text Initial text to render. + * @param size Pixel size of the text to render. + * @param antiAliasMode Antialiasing mode. Can be ANTIALIAS_FULL, ANTIALIAS_NONE or ANTIALIAS_STRONG. + * @param premultiplyAlpha If set to true, will premultiply alpha in the label image. + * @see Font + */ + Label(Font *font, const String& text, int size, int antiAliasMode, bool premultiplyAlpha = false, const Color &backgroundColor = Color(0.0, 0.0, 0.0, 0.0), const Color &foregroundColor = Color(1.0, 1.0, 1.0, 1.0)); virtual ~Label(); + + /** + * Sets the text of the label. + * @param text Text to set. + */ void setText(const String& text); + + /** + * Returns the current text of the label. + * @return Current text. + */ const String& getText() const; + /** + * Returns the pixel width for the specified string based on the current label font and size settings. + * @param text Text to return width for. + * @return Pixel width of specified text. + */ int getTextWidthForString(const String& text); + + /** + * Returns the pixel height for the specified string based on the current label font and size settings. + * @param text Text to return height for. + * @return Pixel height of specified text. + */ int getTextHeightForString(const String& text); - void computeStringBbox(GlyphData *glyphData, FT_BBox *abbox); - void precacheGlyphs(String text, GlyphData *glyphData); - - void renderGlyphs(GlyphData *glyphData); - - void drawGlyphBitmap(FT_Bitmap *bitmap, unsigned int x, unsigned int y, Color glyphColor); - + /** + * Returns the width of the current text. + * @return Width of the current text. + */ Number getTextWidth() const; + + /** + * Returns the height of the current text. + * @return Height of the current text. + */ Number getTextHeight() const; - void clearColors(); + /** + * Sets the color for a range of characters in the label. The colors are only applied upon the next call to setText, not the currently rendered text. This call appends the color range to a list of color ranges, so if you are calling this multiple times for the same ranges, you must call clearColors. + * @param color The color to set for the specified range. + * @param rangeStart Starting index of the specified range. + * @param rangeEnd Ending index of the specified range. + * @see clearColors + */ void setColorForRange(Color color, unsigned int rangeStart, unsigned int rangeEnd); + + /** + * Clears the current label colors. + * @see setColorForRange + */ + void clearColors(); + /** + * Returns the text color for specified character index. + */ Color getColorForIndex(unsigned int index); + + /** + * Returns the premultiply alpha setting. + */ + bool getPremultiplyAlpha() const; + + /** + * If set to true, will premultiply alpha when text is set to the label. + */ + void setPremultiplyAlpha(bool val); + /** + * Sets the Font used to render text in the label. + * @see Font + */ void setFont(Font *newFont); + + /** + * Returns the Font currently used to render text in the label. + * @see Font + */ Font *getFont() const; + /** + * Sets the vertical pixel size of text rendered in the label. + */ void setSize(int newSize); + + /** + * Return the current vertical pixel size of text rendered in the label. + */ unsigned int getSize() const; - int getAntialiasMode() const; + /** + * Returns the current antialasing mode. + */ + int getAntialiasMode() const; + + /** + * Sets the antialiasing mode used to render text. + * @param newMode Antialiasing mode. Can be ANTIALIAS_FULL, ANTIALIAS_NONE or ANTIALIAS_STRONG. + */ void setAntialiasMode(int newMode); - - bool optionsChanged(); static const int ANTIALIAS_FULL = 0; static const int ANTIALIAS_NONE = 1; static const int ANTIALIAS_STRONG = 2; - + static const int ANTIALIAS_LCD = 3; + static const int ANTIALIAS_LCD_HINT = 4; + static const int ANTIALIAS_FULL_HINT = 5; + /** + * Returns the pixel distance from top of image to the baseline of the rendered text. + */ int getBaselineAdjust(); + + void setBackgroundColor(const Color &color); + void setForegroundColor(const Color &color); + void setColors(const Color &backgroundColor, const Color &foregroundColor); + + Color getBackgroundColor(); + Color getForegroundColor(); + + bool optionsChanged(); protected: + + Color backgroundColor; + Color foregroundColor; + + void computeStringBbox(GlyphData *glyphData, FT_BBox *abbox); + void precacheGlyphs(String text, GlyphData *glyphData); + void renderGlyphs(GlyphData *glyphData); + void drawGlyphBitmap(FT_Bitmap *bitmap, unsigned int x, unsigned int y, const Color &glyphColor); bool _optionsChanged; GlyphData labelData; - + std::vector colorRanges; int baseLineOffset; diff --git a/Core/Contents/Include/PolyLogger.h b/Core/Contents/Include/PolyLogger.h index 8c84b5861..bbaa121a8 100755 --- a/Core/Contents/Include/PolyLogger.h +++ b/Core/Contents/Include/PolyLogger.h @@ -34,15 +34,60 @@ namespace Polycode { String message; }; - + + /** + * Logs information to the console, should only be called through Logger:: + */ class _PolyExport Logger : public EventDispatcher { public: + /** + * Default constructor + */ Logger(); virtual ~Logger(); + /** + * Dispatches LoggerEvent and calls Logger::log() + * @param message String to log + */ void logBroadcast(String message); + /** + * Logs information to the console or debug window of VS (only available if compiled as debug) + * @param format c-strings to log, put the params into the first using the formatting of printf (reference: http://www.cplusplus.com/reference/cstdio/printf/) + */ static void log(const char *format, ...); + + /** + * Logs information through wcout + * @param str The c-string to log + */ static void logw(const char *str); + + void setLogToFile(bool val); + bool getLogToFile(); + + /** + * @return The file that is logged to + */ + FILE *getLogFile(); + + /** + * Sets the file where the Logger should log to + * @param f A pointer to a opened FILE + */ + void setLogFile(FILE *f); + + /** + * @return The logger instance + */ + static Logger *getInstance(); + + protected: + FILE *logFile; + bool logToFile; + + private: + static Logger *overrideInstance; }; } diff --git a/Core/Contents/Include/PolyMaterial.h b/Core/Contents/Include/PolyMaterial.h index 252b82639..193c6c5c2 100755 --- a/Core/Contents/Include/PolyMaterial.h +++ b/Core/Contents/Include/PolyMaterial.h @@ -35,7 +35,7 @@ namespace Polycode { class _PolyExport Material : public Resource { public: - Material(const String& name); + explicit Material(const String& name); virtual ~Material(); void addShader(Shader *shader,ShaderBinding *shaderBinding); @@ -67,7 +67,8 @@ namespace Polycode { void *shaderModule; int blendingMode; - + + bool wireframe; bool screenMaterial; protected: diff --git a/Core/Contents/Include/PolyMaterialManager.h b/Core/Contents/Include/PolyMaterialManager.h index 1bb6f432e..e2e734139 100755 --- a/Core/Contents/Include/PolyMaterialManager.h +++ b/Core/Contents/Include/PolyMaterialManager.h @@ -24,6 +24,7 @@ THE SOFTWARE. #include "PolyGlobals.h" #include "PolyImage.h" #include "PolyObject.h" +#include "PolyShader.h" #include class TiXmlNode; @@ -38,6 +39,7 @@ namespace Polycode { class Shader; class String; class ShaderProgram; + class ResourcePool; /** * Manages loading and reloading of materials, textures and shaders. This class should be only accessed from the CoreServices singleton. @@ -46,8 +48,6 @@ namespace Polycode { public: MaterialManager(); ~MaterialManager(); - - void Update(int elapsed); /** * Creates a new framebuffer texture. @@ -56,53 +56,39 @@ namespace Polycode { Texture *createTexture(int width, int height, char *imageData, bool clamp=false, bool createMipmaps = true, int type=Image::IMAGE_RGBA); Texture *createNewTexture(int width, int height, bool clamp=false, bool createMipmaps = true, int type=Image::IMAGE_RGBA); Texture *createTextureFromImage(Image *image, bool clamp=false, bool createMipmaps = true); - Texture *createTextureFromFile(const String& fileName, bool clamp=false, bool createMipmaps = true); - void deleteTexture(Texture *texture); - - void reloadTextures(); + Texture *createTextureFromFile(const String& fileName, bool clamp=false, bool createMipmaps = true, ResourcePool *resourcePool = NULL); - void reloadProgramsAndTextures(); - void reloadPrograms(); - void addShaderModule(PolycodeShaderModule *module); //SceneRenderTexture *createRenderTexture(Scene *targetScene, Camera *targetCamera, int renderWidth,int renderHeight); - Texture *getTextureByResourcePath(const String& resourcePath) const; ShaderProgram *createProgramFromFile(String programPath); + void loadMaterialLibraryIntoPool(ResourcePool *pool, const String &materialFile); + // cubemaps Cubemap *cubemapFromXMLNode(TiXmlNode *node); // materials - Material *materialFromXMLNode(TiXmlNode *node); + Material *materialFromXMLNode(ResourcePool *resourcePool, TiXmlNode *node); - Material *createMaterial(String materialName, String shaderName); + Material *createMaterial(ResourcePool *resourcePool, String materialName, String shaderName); - Shader *setShaderFromXMLNode(TiXmlNode *node); - Shader *createShaderFromXMLNode(TiXmlNode *node); - Shader *createShader(String shaderType, String name, String vpName, String fpName, bool screenShader); + Shader *setShaderFromXMLNode(ResourcePool *resourcePool, TiXmlNode *node); + Shader *createShaderFromXMLNode(ResourcePool *resourcePool, TiXmlNode *node); + Shader *createShader(ResourcePool *resourcePool, String shaderType, String name, String vpName, String fpName, bool screenShader); - std::vector loadMaterialsFromFile(String fileName); - std::vector loadShadersFromFile(String fileName); - std::vector loadCubemapsFromFile(String fileName); - - void addMaterial(Material *material); - void addShader(Shader *shader); - - unsigned int getNumShaders(); - Shader *getShaderByIndex(unsigned int index); + std::vector loadMaterialsFromFile(ResourcePool *resourcePool, const String &fileName); + std::vector loadShadersFromFile(ResourcePool *resourcePool, String fileName); + std::vector loadCubemapsFromFile(String fileName); bool premultiplyAlphaOnLoad; bool clampDefault; bool mipmapsDefault; + bool keepTextureData; private: - std::vector textures; - std::vector materials; - std::vector shaders; - std::vector shaderModules; }; }; diff --git a/Core/Contents/Include/PolyMatrix4.h b/Core/Contents/Include/PolyMatrix4.h index 4d62da060..ab094e1cc 100755 --- a/Core/Contents/Include/PolyMatrix4.h +++ b/Core/Contents/Include/PolyMatrix4.h @@ -108,18 +108,37 @@ namespace Polycode { return pos; } + inline Vector3 multiplyWithPerspective(const Vector3 &v2) const + { + Number divisor = v2.x*m[0][3] + v2.y*m[1][3] + v2.z*m[2][3] + m[3][3]; + return (*this * v2) / divisor; + } + // ---------------------------------------------------------------------------------------------------------------- /** @name Operators * Available vector operators. */ //@{ - + + inline Matrix4 operator * (Number n) const { + return Matrix4( + n*m[0][0], n*m[0][1], n*m[0][2], n*m[0][3], + n*m[1][0], n*m[1][1], n*m[1][2], n*m[1][3], + n*m[2][0], n*m[2][1], n*m[2][2], n*m[2][3], + n*m[3][0], n*m[3][1], n*m[3][2], n*m[3][3]); + } + + inline Vector3 multVector( const Vector3 &v2 ) const + { + return Vector3(v2.x*m[0][0] + v2.y*m[1][0] + v2.z*m[2][0] + m[3][0], + v2.x*m[0][1] + v2.y*m[1][1] + v2.z*m[2][1] + m[3][1], + v2.x*m[0][2] + v2.y*m[1][2] + v2.z*m[2][2] + m[3][2]); + } + inline Vector3 operator * ( const Vector3 &v2 ) const { - return Vector3(v2.x*m[0][0] + v2.y*m[1][0] + v2.z*m[2][0] + m[3][0], - v2.x*m[0][1] + v2.y*m[1][1] + v2.z*m[2][1] + m[3][1], - v2.x*m[0][2] + v2.y*m[1][2] + v2.z*m[2][2] + m[3][2]); + return multVector(v2); } inline Number* operator [] ( int row ) { return m[row];} diff --git a/Core/Contents/Include/PolyMesh.h b/Core/Contents/Include/PolyMesh.h index 98d31bc60..ad5eb1a68 100755 --- a/Core/Contents/Include/PolyMesh.h +++ b/Core/Contents/Include/PolyMesh.h @@ -22,20 +22,17 @@ THE SOFTWARE. #pragma once #include "PolyGlobals.h" -#include "PolyVertex.h" -#include "PolyPolygon.h" +#include "PolyRenderDataArray.h" +#include "PolyColor.h" +#include "PolyVector3.h" +#include "PolyVector2.h" +#include class OSFILE; namespace Polycode { class String; - - class _PolyExport VertexSorter : public PolyBase { - public: - Vertex *target; - bool operator() (Vertex *v1,Vertex *v2) { return (v1->distance(*target)distance(*target));} - }; class _PolyExport VertexBuffer : public PolyBase { public: @@ -43,60 +40,21 @@ namespace Polycode { virtual ~VertexBuffer(){} int getVertexCount() const { return vertexCount;} + int getIndexCount() const { return indexCount;} int verticesPerFace; int meshType; protected: - int vertexCount; + int vertexCount; + int indexCount; }; - - /** - * Render data array. - */ - class _PolyExport RenderDataArray : public PolyBase { - public: - int arrayType; - int stride; - int size; - void *arrayPtr; - void *rendererData; - int count; - - /** - * Vertex position array. - */ - static const int VERTEX_DATA_ARRAY = 0; - - /** - * Vertex color array. - */ - static const int COLOR_DATA_ARRAY = 1; - - /** - * Vertex normal array. - */ - static const int NORMAL_DATA_ARRAY = 2; - - /** - * Vertex texture coordinate array. - */ - static const int TEXCOORD_DATA_ARRAY = 3; - - /** - * Tangent vector array. - */ - static const int TANGENT_DATA_ARRAY = 4; - - - }; - typedef struct { float x; float y; float z; - float w; + float w; } Vector4_struct; typedef struct { @@ -111,7 +69,7 @@ namespace Polycode { } Vector2_struct; /** - * A polygonal mesh. The mesh is assembled from Polygon instances, which in turn contain Vertex instances. This structure is provided for convenience and when the mesh is rendered, it is cached into vertex arrays with no notions of separate polygons. When data in the mesh changes, arrayDirtyMap must be set to true for the appropriate array types (color, position, normal, etc). Available types are defined in RenderDataArray. + * A mesh comprised of vertices. When data in the mesh changes, arrayDirtyMap must be set to true for the appropriate array types (color, position, normal, etc). Available types are defined in RenderDataArray. */ class _PolyExport Mesh : public PolyBase { public: @@ -121,13 +79,13 @@ namespace Polycode { * Construct with an empty mesh of specified type. * @param meshType Type of mesh. Possible values are: Mesh::QUAD_MESH, Mesh::TRI_MESH, Mesh::TRIFAN_MESH, Mesh::TRISTRIP_MESH, Mesh::LINE_MESH, Mesh::POINT_MESH. */ - Mesh(int meshType); + explicit Mesh(int meshType); /** * Construct from a mesh loaded from a file. * @param fileName Path to mesh file. */ - Mesh(const String& fileName); + explicit Mesh(const String& fileName); /** * Construct from a mesh loaded from a file. @@ -137,12 +95,6 @@ namespace Polycode { virtual ~Mesh(); - /** - * Adds a polygon to the mesh. - * @param newPolygon Polygon to add. - */ - void addPolygon(Polygon *newPolygon); - /** * Loads a mesh from a file. * @param fileName Path to mesh file. @@ -159,16 +111,13 @@ namespace Polycode { * Saves mesh to a file. * @param fileName Path to file to save to. */ - void saveToFile(const String& fileName); + void saveToFile(const String& fileName, bool writeNormals = true, bool writeTangents = true, bool writeColors = true, bool writeBoneWeights = true, bool writeUVs = true, bool writeSecondaryUVs = false); void loadFromFile(OSFILE *inFile); - void saveToFile(OSFILE *outFile); + + + void saveToFile(OSFILE *outFile, bool writeNormals = true, bool writeTangents = true, bool writeColors = true, bool writeBoneWeights = true, bool writeUVs = true, bool writeSecondaryUVs = false); - /** - * Returns the number of polygons in the mesh. - * @return Number of polygons in the mesh. - */ - unsigned int getPolygonCount(); /** * Returns the total vertex count in the mesh. @@ -176,26 +125,36 @@ namespace Polycode { */ unsigned int getVertexCount(); - /** - * Returns a polygon at specified index. - * @param index Index of polygon. - * @return Polygon at index. - */ - Polygon *getPolygon(unsigned int index); /** * Creates a plane mesh of specified size. * @param w Width of plane. * @param h Depth of plane. */ - void createPlane(Number w, Number h); + void createPlane(Number w, Number h, Number tilingValue = 1.0); /** * Creates a vertical plane mesh of specified size. * @param w Width of plane. * @param h Depth of plane. */ - void createVPlane(Number w, Number h); + void createVPlane(Number w, Number h, Number tilingValue = 1.0); + + /** + * Creates a 2D circle. + * @param w Width of circle. + * @param h Height of plane. + * @param numSegments Number of segments + */ + void createCircle(Number w, Number h, unsigned int numSegments, Number tilingValue = 1.0); + + /** + * Creates a 2D circle with normals pointing outwards from vertices. + * @param w Width of circle. + * @param h Height of plane. + * @param numSegments Number of segments + */ + void createLineCircle(Number w, Number h, unsigned int numSegments, Number tilingValue = 1.0); /** * Creates a torus. @@ -204,7 +163,7 @@ namespace Polycode { * @param rSegments Number of radial segments. * @param tSegments Number of tube segments. */ - void createTorus(Number radius, Number tubeRadius, int rSegments, int tSegments); + void createTorus(Number radius, Number tubeRadius, int segmentsW, int segmentsH, Number tilingValue = 1.0); /** * Creates a cube mesh of specified size. @@ -212,7 +171,7 @@ namespace Polycode { * @param d Depth of cube. * @param h Height of cube. */ - void createBox(Number w, Number d, Number h); + void createBox(Number w, Number d, Number h, Number tilingValue = 1.0); /** * Creates a sphere mesh of specified size. @@ -220,7 +179,21 @@ namespace Polycode { * @param numRings Number of rings. * @param numSegments Number of segments. */ - void createSphere(Number radius, int numRings, int numSegments); + void createSphere(Number radius, int numRings, int numSegments, Number tilingValue = 1.0); + + /** + * Creates an icosphere of specified radius + * @param radius Radius of sphere. + * @param subdivisions 0 means you get an icosahedron, don't recommend ever going above about 4 or 5 as they get really big + */ + void createIcosphere(Number radius, int subdivisions); + + /** + * Creates an octosphere of specified radius + * @param radius Radius of sphere. + * @param subdivisions 0 means you get an octagon, don't recommend ever going too high as they get really big + */ + void createOctosphere(Number radius, int subdivisions); /** * Creates a cylinder mesh. @@ -229,7 +202,7 @@ namespace Polycode { * @param numSegments Number of segments. * @param capped Create the end caps. */ - void createCylinder(Number height, Number radius, int numSegments, bool capped=true); + void createCylinder(Number height, Number radius, int numSegments, bool capped = true, Number tilingValue = 1.0); /** * Creates a cone mesh. @@ -237,31 +210,45 @@ namespace Polycode { * @param radius Radius of the cone. * @param numSegments Number of segments. */ - void createCone(Number height, Number radius, int numSegments); + void createCone(Number height, Number radius, int numSegments, Number tilingValue = 1.0); /** * Recenters the mesh with all vertices being as equidistant from origin as possible. */ Vector3 recenterMesh(); + + void setVertexAtOffset(unsigned int offset, Number x, Number y, Number z); + + void addVertexWithUVAndNormal(Number x, Number y, Number z, Number u, Number v, Number nx, Number ny, Number nz); - /** - * Toggles the mesh between using vertex or polygon normals. - * @param val If true, the mesh will use vertex normals, otherwise it will use the polygon normals. - */ - void useVertexNormals(bool val); - - /** - * Sets the vertex buffer for the mesh. - * @param buffer New vertex buffer for mesh. - */ - void setVertexBuffer(VertexBuffer *buffer); - - /** - * Returns the vertex buffer for the mesh. - * @return The vertex buffer for this mesh. - */ - VertexBuffer *getVertexBuffer(); + void addTexCoord(Number u, Number v); + void addTexCoord2(Number u, Number v); + + void addTangent(Number x, Number y, Number z); + + void addVertexWithUV(Number x, Number y, Number z, Number u, Number v); + + void addVertex(Number x, Number y, Number z); + + void addNormal(Number nx, Number ny, Number nz); + void addNormal(const Vector3 &n); + + void addBoneAssignments(Number b1Weight, unsigned int b1Index, Number b2Weight, unsigned int b2Index, Number b3Weight, unsigned int b3Index, Number b4Weight, unsigned int b4Index); + + void addColor(Number r, Number g, Number b, Number a); + void addColor(const Color &color); + + + Vector3 getVertexPosition(unsigned int vertexOffset); + + Vector3 getVertexPositionAtIndex(unsigned int index); + + Vector2 getVertexTexCoord(unsigned int vertexOffset); + + Vector2 getVertexTexCoordAtIndex(unsigned int index); + + Mesh *Copy() const; /** * Returns the radius of the mesh (furthest vertex away from origin). @@ -274,15 +261,13 @@ namespace Polycode { * @param smooth If true, will use smooth normals. * @param smoothAngle If smooth, this parameter sets the angle tolerance for the approximation function. */ - void calculateNormals(bool smooth=true, Number smoothAngle=90.0); + void calculateNormals(); /** * Recalculates the tangent space vector for all vertices. */ void calculateTangents(); - std::vector getConnectedFaces(Vertex *v); - /** * Returns the mesh type. */ @@ -294,19 +279,19 @@ namespace Polycode { */ void setMeshType(int newType); - void dirtyArray(unsigned int arrayIndex); - void dirtyArrays(); + inline unsigned int getIndexGroupSize() { + switch (meshType) { + case QUAD_MESH: return 4; + case TRI_MESH: return 3; + case LINE_MESH: return 2; + default: return 1; + } + } /** * Calculates the mesh bounding box. */ Vector3 calculateBBox(); - - /** - * Checks if the mesh has a vertex buffer. - * @param True if the mesh has a vertex buffer, false if not. - */ - bool hasVertexBuffer() { return meshHasVertexBuffer; } /** * Quad based mesh. @@ -342,31 +327,64 @@ namespace Polycode { * Line loop based mesh. */ static const int LINE_LOOP_MESH = 7; - - - /** - * Render array dirty map. If any of these are flagged as dirty, the renderer will rebuild them from the mesh data. See RenderDataArray for types of render arrays. - * @see RenderDataArray - */ - bool arrayDirtyMap[16]; - - /** - * Render arrays. See RenderDataArray for types of render arrays. - * @see RenderDataArray - */ - RenderDataArray *renderDataArrays[16]; + + /** * If set to true, the renderer will use the vertex colors instead of entity color transform to render this mesh. */ bool useVertexColors; - - - protected: - - VertexBuffer *vertexBuffer; - bool meshHasVertexBuffer; - int meshType; - std::vector polygons; + bool indexedMesh; + + void addIndexedFace(unsigned int i1, unsigned int i2); + void addIndexedFace(unsigned int i1, unsigned int i2, unsigned int i3); + void addIndexedFace(unsigned int i1, unsigned int i2, unsigned int i3, unsigned int i4); + void addIndex(unsigned int index); + + /** Removes a range of vertices starting at beginRemoveVertex. vertexRemovalCount should be a multiple of the num + * if you want to keep your mesh data clean. If this is an indexedMesh, will also remove any faces that reference + * @param beginRemoveVertex First element of the vertex array to remove + * @param vertexRemovalCount Number of elements to remove from the vertex array */ + void removeVertexRange(unsigned int beginRemoveVertex, int vertexRemovalCount = 3); + + /** Removes a face from the mesh. Face is defined as a quad for QUAD_MESH, a triangle for TRI_MESH, a line for LI + * In indexedMesh mode this may result in orphaned vertices. + * @param faceIndex The 0-indexed face of the mesh (and NOT the index into the indices array!) */ + void removeFace(unsigned int faceIndex); + + /** For indexedMesh only, removes any unused vertices from the mesh. */ + int removeUnusedVertices(); + + unsigned int getIndexCount(); + + void subdivideToRadius(Number radius, int subdivisions); + + static Vector3 calculateFaceTangent(const Vector3 &v1, const Vector3 &v2, const Vector3 &v3, const Vector2 &texCoord1, const Vector2 &texCoord2, const Vector2 &texCoord3); + + void saveAsOBJ(const String fileName); + + void normalizeBoneWeights(); + + VertexDataArray vertexPositionArray; + VertexDataArray vertexColorArray; + VertexDataArray vertexNormalArray; + VertexDataArray vertexTexCoordArray; + VertexDataArray vertexTexCoord2Array; + VertexDataArray vertexTangentArray; + + VertexDataArray vertexBoneWeightArray; + VertexDataArray vertexBoneIndexArray; + + IndexDataArray indexArray; + + protected: + + void loadFromFileV2(OSFILE *inFile); + void loadFromFileLegacyV1(OSFILE *inFile); + + void writeVertexBlock(VertexDataArray *array, OSFILE *outFile); + void writeIndexBlock(IndexDataArray *array, OSFILE *outFile); + int meshType; + }; } diff --git a/Core/Contents/Include/PolyModule.h b/Core/Contents/Include/PolyModule.h index a458a3b55..29f5ef943 100644 --- a/Core/Contents/Include/PolyModule.h +++ b/Core/Contents/Include/PolyModule.h @@ -21,6 +21,7 @@ namespace Polycode { class ShaderBinding; class Resource; class ShaderProgram; + class ResourcePool; class _PolyExport PolycodeModule : public PolyBase { public: @@ -49,8 +50,8 @@ namespace Polycode { virtual bool acceptsExtension(const String& extension) = 0; virtual ShaderProgram* createProgramFromFile(const String& extension, const String& fullPath) = 0; virtual String getShaderType() = 0; - virtual Shader *createShader(TiXmlNode *node) = 0; - virtual Shader *createShader(String name, String vpName, String fpName) = 0; + virtual Shader *createShader(ResourcePool *resourcePool, TiXmlNode *node) = 0; + virtual Shader *createShader(ResourcePool *resourcePool, String name, String vpName, String fpName) = 0; virtual bool applyShaderMaterial(Renderer *renderer, Material *material, ShaderBinding *localOptions, unsigned int shaderIndex) = 0; bool hasShader(Shader *shader) { for(int i=0; i < shaders.size(); i++) { if(shaders[i] == shader){ return true; } } return false; } diff --git a/Core/Contents/Include/PolyObject.h b/Core/Contents/Include/PolyObject.h index 148ef9823..14312b0d9 100644 --- a/Core/Contents/Include/PolyObject.h +++ b/Core/Contents/Include/PolyObject.h @@ -38,8 +38,10 @@ namespace Polycode { /** * Default constructor */ - ObjectEntry() { type = UNKNOWN_ENTRY; length = 0; } - + ObjectEntry(); + + ~ObjectEntry(); + /** * Type of entry. Possible values are (FLOAT_ENTRY, INT_ENTRY, BOOL_ENTRY, ARRAY_ENTRY, STRING_ENTRY, CONTAINER_ENTRY). */ diff --git a/Core/Contents/Include/PolyParticle.h b/Core/Contents/Include/PolyParticle.h deleted file mode 100755 index bd52f63f1..000000000 --- a/Core/Contents/Include/PolyParticle.h +++ /dev/null @@ -1,60 +0,0 @@ -/* -Copyright (C) 2011 by Ivan Safrin - -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: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -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. -*/ - -#pragma once -#include "PolyGlobals.h" -#include "PolyVector3.h" - -namespace Polycode { - - class Entity; - class Material; - class Mesh; - class Texture; - - class _PolyExport Particle : public PolyBase { - public: - Particle(int particleType, bool isScreenParticle, Material *material, Texture *texture, Mesh *particleMesh); - ~Particle(); - void Reset(bool continuious); - - void createSceneParticle(int particleType, Material *material, Mesh *particleMesh); - void createScreenParticle(int particleType, Texture *texture, Mesh *particleMesh); - - Entity *particleBody; - - Vector3 velVector; - Vector3 dirVector; - Vector3 deviation; - Number life; - Number lifespan; - Number brightnessDeviation; - Number perlinPosX; - Number perlinPosY; - Number perlinPosZ; - - static Mesh* billboardMesh; - - static const int BILLBOARD_PARTICLE = 0; - static const int MESH_PARTICLE = 1; - }; -} diff --git a/Core/Contents/Include/PolyParticleEmitter.h b/Core/Contents/Include/PolyParticleEmitter.h index e088ba323..662e6a178 100755 --- a/Core/Contents/Include/PolyParticleEmitter.h +++ b/Core/Contents/Include/PolyParticleEmitter.h @@ -22,344 +22,163 @@ THE SOFTWARE. #pragma once #include "PolyGlobals.h" -#include "PolyString.h" -#include "PolyVector3.h" -#include "PolyMatrix4.h" +#include "PolySceneMesh.h" +#include "PolyCore.h" +#include "PolyPerlin.h" #include "PolyBezierCurve.h" -#include "PolySceneEntity.h" -#include "PolyScreenEntity.h" namespace Polycode { - - class Entity; - class Material; - class Mesh; - class Particle; - class Perlin; - class Scene; - class SceneMesh; - class Screen; - class ScreenMesh; - class Texture; - class Timer; - - /** - * Particle emitter base. - */ - class _PolyExport ParticleEmitter { - public: - ParticleEmitter(const String& imageFile, Mesh *particleMesh, int particleType, int emitterType, Number lifespan, unsigned int numParticles, Vector3 direction, Vector3 gravity, Vector3 deviation, Vector3 emitterRadius); - virtual ~ParticleEmitter(); - - virtual void dispatchTriggerCompleteEvent(); - - void createParticles(); - - /** - * Sets the speed at which particles rotate - * @param speed New rotation speed. - */ - void setRotationSpeed(Number speed); - - /** - * Sets the blending mode used for the particles. See documentation for the Entity for information on blending modes. - * @param mode New blending mode. - */ - void setParticleBlendingMode(int mode); - - int getParticleBlendingMode() const; - - /** - * Turns depth write on and off for particles. - */ - void setDepthWrite(bool val); - - /** - * Turns depth test on and off for particles. - */ - void setDepthTest(bool val); - - /** - * Turns alpha testing on and off for particles. - */ - void setAlphaTest(bool val); - - /** - * Enables perlin noise movement for particles. - */ - void enablePerlin(bool val); - - /** - * Sets visibility of all particles in the system - */ - void setParticleVisibility(bool val); - - /** - * Enables perlin noise movement size. - */ - void setPerlinModSize(Number size); - - /** - * Enables or disables billboard mode for particles. - */ - void setBillboardMode(bool mode); - - /** - * Enables or disables the emitter - */ - void enableEmitter(bool val); - - /** - * Returns true if the emitter is enabled, false otherwise. - */ - bool emitterEnabled(); - - /** - * Sets the emitter radius on all 3 axises. - */ - void setEmitterRadius(Vector3 rad); - - /** - * If set to true, will release all particles at once. - */ - void setAllAtOnce(bool val); - - - unsigned int getNumParticles() const; - - Particle *getParticleAtIndex(unsigned int index) const; - - /** - * If emitter mode is TRIGGERED_EMITTER, calling this method will trigger particle emission. - */ - - void resetAll(); - - void Trigger(); - - void resetParticle(Particle *particle); - - /** - * Changes the particle count in the emitter. - */ - void setParticleCount(int count); - - virtual Vector3 getParticleCompoundScale(); - virtual void addParticleBody(Entity *particleBody); - virtual Matrix4 getBaseMatrix(); - - /** - * Particle movement speed multiplier - */ - Number particleSpeedMod; - - /** - * Particle brightness deviation - */ - Number brightnessDeviation; - - void updateEmitter(); - - /** - * Continuous emitter setting. - */ - static const int CONTINUOUS_EMITTER = 0; - - /** - * Triggered emitter setting. - */ - static const int TRIGGERED_EMITTER = 1; - - /** - * Particle direction deviation - */ - Vector3 deviation; - /** - * Particle direction and emission strength vector - */ - Vector3 dirVector; - - /** - * Particle gravity strength vector - */ - Vector3 gravVector; - - /** - * Lifespan of particles. - */ - Number lifespan; - - /** - * If set to true, particles' rotation will follow their movement. - */ - bool rotationFollowsPath; - - /** - * Bezier curve that controls the scale of the particles. - */ - BezierCurve scaleCurve; - - /** - * Bezier curve that controls the red component of particles' color. - */ - BezierCurve colorCurveR; - /** - * Bezier curve that controls the green component of particles' color. - */ - BezierCurve colorCurveG; - /** - * Bezier curve that controls the blue component of particles' color. - */ - BezierCurve colorCurveB; - /** - * Bezier curve that controls the alpha component of particles' color. - */ - BezierCurve colorCurveA; - - /** - * If set to true, will use the color curves to control particle color. False by default. - */ - bool useColorCurves; - - /** - * If set to true, will use the scale curve to control particle scale. False by default. - */ - bool useScaleCurves; - - Number particleSize; - - Texture *getParticleTexture() const; - - void setParticleTexture(Texture *texture); - - Vector3 emitterRadius; - - Number perlinModSize; - - bool perlinEnabled; - - Number rotationSpeed; - - int emitterType; - - bool getIgnoreParentMatrix() const; - void setIgnoreParentMatrix(bool val); - - protected: - - bool ignoreParentMatrix; - - int blendingMode; - - bool isScreenEmitter; - Mesh *pMesh; - - bool allAtOnce; - int particleType; - Material *particleMaterial; - Texture *particleTexture; - - String textureFile; - - bool isEmitterEnabled; - - - Perlin *motionPerlin; - - Number numParticles; - std::vector particles; - - Number emitSpeed; - Timer *timer; - }; - - /** - * 3D particle emitter. - */ - class _PolyExport SceneParticleEmitter : public SceneEntity, public ParticleEmitter { - public: - /** - * Constructor. - * @param materialName Name of the material to use for particles. - * @param particleParentScene Scene to create particles in. - * @param particleType Type of particles to create. Can be Particle::BILLBOARD_PARTICLE or Particle::MESH_PARTICLE - * @param emitterType Type of emitter to create. Can be ParticleEmitter::CONTINUOUS_EMITTER or ParticleEmitter::TRIGGERED_EMITTER - * @param lifespan Lifetime of particles in seconds. - * @param numParticles Total number of particles to create. - * @param direction Direction of the emitter, length of this vector controls emitter strength - * @param gravity Gravity direction and strength - * @param deviation Emitter deviation on each axis - * @param particleMesh If particle type is Particle::MESH_PARTICLE, this must be set to the mesh to use for each particle - * @param emitter If this is specified, particles will be emitted from this meshe's vertices. - */ - SceneParticleEmitter(const String& materialName, int particleType, int emitterType, Number lifespan, unsigned int numParticles, Vector3 direction, Vector3 gravity, Vector3 deviation, Vector3 emitterRadius, Mesh *particleMesh = NULL, SceneMesh *emitter = NULL); - virtual ~SceneParticleEmitter(); - - /** - * Returns the emitter (helper method for LUA). - */ - ParticleEmitter *getEmitter() { return this; } - - void respawnSceneParticles(); - void addParticleBody(Entity *particleBody); - Matrix4 getBaseMatrix(); - void Update(); - - Vector3 getParticleCompoundScale(); - - void dispatchTriggerCompleteEvent(); - - /** - * Continuous emitter setting. - */ - static const int CONTINUOUS_EMITTER = 0; - - /** - * Triggered emitter setting. - */ - static const int TRIGGERED_EMITTER = 1; - - - protected: - SceneMesh *emitterMesh; - }; - - /** - * 3D particle emitter. - */ - class _PolyExport ScreenParticleEmitter : public ScreenEntity, public ParticleEmitter { - public: - ScreenParticleEmitter(const String& imageFile, int particleType, int emitterType, Number lifespan, unsigned int numParticles, Vector3 direction, Vector3 gravity, Vector3 deviation, Vector3 emitterRadius, Mesh *particleMesh = NULL, ScreenMesh *emitter = NULL); - virtual ~ScreenParticleEmitter(); - - virtual Entity *Clone(bool deepClone, bool ignoreEditorOnly) const; - virtual void applyClone(Entity *clone, bool deepClone, bool ignoreEditorOnly) const; - - /** - * Returns the emitter (helper method for LUA). - */ - ParticleEmitter *getEmitter() { return this; } - - void dispatchTriggerCompleteEvent(); - - void addParticleBody(Entity *particleBody); - Matrix4 getBaseMatrix(); - void Update(); - - Vector3 getParticleCompoundScale(); - - /** - * Continuous emitter setting. - */ - static const int CONTINUOUS_EMITTER = 0; - - /** - * Triggered emitter setting. - */ - static const int TRIGGERED_EMITTER = 1; - - - protected: - ScreenMesh *emitterMesh; - }; -} + + class SceneParticle { + public: + Number lifetime; + Vector3 position; + Vector3 velocity; + Vector3 perlinPos; + Vector3 rotation; + Number brightnessDeviation; + Number scale; + Color color; + int varianceIndex; + }; + + class SceneParticleEmitter : public SceneMesh { + public: + SceneParticleEmitter(unsigned int particleCount, Number lifetime, Number speed); + virtual ~SceneParticleEmitter(); + + void setParticleCount(unsigned int newParticleCount); + unsigned int getParticleCount() const; + + void setParticleLifetime(Number lifetime); + Number getParticleLifetime() const; + + void setDirectionDeviation(const Vector3 &newDeviation); + Vector3 getDirectionDeviation() const; + + void setEmitterSize(const Vector3 &newSize); + Vector3 getEmitterSize() const; + + void setGravity(const Vector3 &newGravity); + Vector3 getGravity() const; + + void fixedUpdate(); + void Render(); + + void updateParticles(); + void rebuildParticles(); + + void triggerParticles(bool allAtOnce); + + void enableParticleSystem(bool val); + + void setUseFloorPlane(bool val); + void setFloorPlaneOffset(Number floorPlaneOffset); + void setFloorDamping(Number floorDamping); + + void setParticlesInWorldSpace(bool val); + bool getParticlesInWorldSpace() const; + + void setPerlinEnabled(bool val); + bool getPerlinEnabled() const; + + Number getParticleSpeed() const; + void setParticleSpeed(Number speed); + + void setPerlinValue(const Vector3 &perlinValue); + Vector3 getPerlinValue() const; + + void setParticleType(unsigned int particleType); + unsigned int getParticleType() const; + + void setParticleSize(Number particleSize); + Number getParticleSize() const; + + void setParticleRotationSpeed(const Vector3 &rotationSpeed); + Vector3 getParticleRotationSpeed() const; + + void setParticleDirection(const Vector3 &direction); + Vector3 getParticleDirection() const; + + void setLoopParticles(bool val); + bool getLoopParticles() const; + + static const int PARTICLE_TYPE_POINT = 0; + static const int PARTICLE_TYPE_QUAD = 1; + static const int PARTICLE_TYPE_MESH = 2; + + bool useScaleCurve; + + /** + * Bezier curve that controls the scale of the particles. + */ + BezierCurve scaleCurve; + + bool useColorCurves; + + /** + * Bezier curve that controls the red component of particles' color. + */ + BezierCurve colorCurveR; + /** + * Bezier curve that controls the green component of particles' color. + */ + BezierCurve colorCurveG; + /** + * Bezier curve that controls the blue component of particles' color. + */ + BezierCurve colorCurveB; + /** + * Bezier curve that controls the alpha component of particles' color. + */ + BezierCurve colorCurveA; + + + Color colorDeviation; + + void addSourceMesh(Mesh *mesh); + int getNumSourceMeshes(); + Mesh *getSourcesMeshAtIndex(int index); + void removeSourceMeshAtIndex(int index); + + void positionParticle(unsigned int index); + + virtual Entity *Clone(bool deepClone, bool ignoreEditorOnly) const; + virtual void applyClone(Entity *clone, bool deepClone, bool ignoreEditorOnly) const; + + protected: + + std::vector sourceMeshes; + void resetParticle(unsigned int index); + + bool systemEnabled; + Core *core; + unsigned int particleCount; + std::vector particles; + Number particleSpeed; + Number lifetime; + + Vector3 directionVector; + Vector3 directionDeviation; + Vector3 emitterSize; + Vector3 gravity; + + Matrix4 systemTrasnformMatrix; + bool useFloorPlane; + bool particlesInWorldSpace; + bool perlinEnabled; + Vector3 perlinValue; + Perlin *motionPerlin; + Number particleSize; + Vector3 particleRotationSpeed; + + Number floorPlaneOffset; + Number floorDamping; + + bool loopParticles; + + unsigned int particleType; + Quaternion q; + }; + +} \ No newline at end of file diff --git a/Core/Contents/Include/PolyPeer.h b/Core/Contents/Include/PolyPeer.h index fbd7c7324..b3160899c 100755 --- a/Core/Contents/Include/PolyPeer.h +++ b/Core/Contents/Include/PolyPeer.h @@ -28,7 +28,7 @@ THE SOFTWARE. #include "PolySocket.h" #include - +#include namespace Polycode { @@ -38,7 +38,6 @@ namespace Polycode { unsigned int headerHash; unsigned int sequence; unsigned int ack; - unsigned short reliableID; unsigned int ackBitfield; unsigned short size; unsigned short type; @@ -56,17 +55,16 @@ namespace Polycode { class _PolyExport PeerConnection { public: - PeerConnection() { localSequence = 0; remoteSequence = 0; reliableID = 1;} - ~PeerConnection(){} - - void ackPackets(unsigned int ack); + PeerConnection(); + ~PeerConnection(); + void ackPacketsWithBitfield(unsigned int ack, unsigned int ackBitfield); + void ackPackets(unsigned int ack); unsigned int localSequence; unsigned int remoteSequence; - unsigned int reliableID; std::vector reliablePacketQueue; - std::vector recentReliableIDs; + std::deque receivedPacketQueue; Address address; }; @@ -151,6 +149,8 @@ namespace Polycode { PeerConnection *addPeerConnection(const Address &address); void removePeerConnection(PeerConnection* connection); + void setReliableRetransmissionInterval(int interval); + void updateReliableDataQueue(); virtual void updatePeer(){} @@ -158,6 +158,8 @@ namespace Polycode { protected: + int reliableRetransmissionInverval; + Timer *updateTimer; std::vector peerConnections; Socket *socket; diff --git a/Core/Contents/Include/PolyPolygon.h b/Core/Contents/Include/PolyPolygon.h deleted file mode 100755 index acaa9e410..000000000 --- a/Core/Contents/Include/PolyPolygon.h +++ /dev/null @@ -1,139 +0,0 @@ -/* -Copyright (C) 2011 by Ivan Safrin - -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: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -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. -*/ - -#pragma once -#include "PolyGlobals.h" -#include "PolyVector3.h" -#include "PolyRectangle.h" -#include - -namespace Polycode { - - class Vertex; - - /** - * A polygon structure. - */ - class _PolyExport Polygon : public PolyBase { - - public: - /** - * Default constructor. - */ - Polygon(); - virtual ~Polygon(); - - /** - * Returns the number of vertices in the polygon. - * @return Number of vertices in the polygon. - */ - unsigned int getVertexCount(); - - /** - * Returns the vertex at specified index. - * @return Vertex at specified index. - */ - Vertex *getVertex(unsigned int index); - - /** - * Adds a new vertex with the specified position coordinates and texture coordinates. - * @param x X coordinate of new vertex. - * @param y Y coordinate of new vertex. - * @param z Z coordinate of new vertex. - * @param u Horizontal texture coordinate. - * @param v Vertical texture coordinate. - * @return Newly added vertex. - */ - Vertex *addVertex(Number x, Number y, Number z, Number u, Number v); - - /** - * Adds a new vertex with the specified position coordinates. - * @param x X coordinate of new vertex. - * @param y Y coordinate of new vertex. - * @param z Z coordinate of new vertex. - * @return Newly added vertex. - */ - Vertex *addVertex(Number x, Number y, Number z); - - - /** - * Adds a new vertex. - * @param vertex New vertex. - */ - void addVertex(Vertex *vertex); - - /** - * Removes and deletes the vertex at specified index. - * @param index to remove vertex at. - */ - void removeVertex(int index); - - /** - * Calculates the average normal for the vertices. - */ - void calculateNormal(); - - /** - * Calculates the tangent space vector for the vertices. - */ - void calculateTangent(); - - /** - * Returns the face normal. - * @return Face normal. - */ - Vector3 getFaceNormal(); - - /** - * Returns the face tangent vector. - * @return Face tangent vector. - */ - Vector3 getFaceTangent(); - - - Rectangle getBounds2D(); - - /** - * Sets the polygon normal. - * @param normal The new normal. - */ - void setNormal(Vector3 normal); - - /** - * If true, will use vertex normals, if false will use the polygon normal. - */ - bool useVertexNormals; - - /** - * Flips the texture coordinate vertically. - */ - void flipUVY(); - - protected: - - unsigned int vertexCount; - std::vector vertices; - Vector3 normal; - Vector3 tangent; - }; - -} diff --git a/Core/Contents/Include/PolyQuaternion.h b/Core/Contents/Include/PolyQuaternion.h index 2a64b60b8..9ad2da650 100755 --- a/Core/Contents/Include/PolyQuaternion.h +++ b/Core/Contents/Include/PolyQuaternion.h @@ -239,12 +239,8 @@ namespace Polycode { z = fSin*rkAxis.z; } - void toEulerAngles (Vector3& eulerAngles) { - // See http://en.wikipedia.org/wiki/Conversion_between_quaternions_and_Euler_angles - // q0 = w, q1 = x, q2 = y, q3 = z - eulerAngles.x = atan2( 2 * ( w * x + y * z), 1 - 2 * (x * x + y * y) ); - eulerAngles.y = asin(2 * ( w * y - z * x)); - eulerAngles.z = atan2( 2 * ( w * z + x * y), 1 - 2 * (y * y + z * z) ); + Vector3 toEulerAngles () const { + return Vector3(atan2( 2 * ( w * x + y * z), 1 - 2 * (x * x + y * y)), asin(2 * ( w * y - z * x)), atan2( 2 * ( w * z + x * y), 1 - 2 * (y * y + z * z) )); } //----------------------------------------------------------------------- diff --git a/Core/Contents/Include/PolyQuaternionCurve.h b/Core/Contents/Include/PolyQuaternionCurve.h index 33251cc4c..9705e4680 100755 --- a/Core/Contents/Include/PolyQuaternionCurve.h +++ b/Core/Contents/Include/PolyQuaternionCurve.h @@ -33,7 +33,8 @@ namespace Polycode { public: Quaternion q1; Quaternion q2; - Quaternion q3; + Quaternion q3; + Number time; }; class _PolyExport QuaternionCurve : public PolyBase { @@ -45,12 +46,9 @@ namespace Polycode { Quaternion interpolate(unsigned int fromIndex, Number t, bool useShortestPath); void generatePointsFromCurves(BezierCurve *wCurve, BezierCurve *xCurve, BezierCurve *yCurve, BezierCurve *zCurve); - void recalcTangents(); protected: std::vector tPoints; - std::vector points; - std::vector tangents; }; } diff --git a/Core/Contents/Include/PolyRay.h b/Core/Contents/Include/PolyRay.h new file mode 100644 index 000000000..7e7ed4ecc --- /dev/null +++ b/Core/Contents/Include/PolyRay.h @@ -0,0 +1,63 @@ +/* + Copyright (C) 2013 by Ivan Safrin + + 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: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + 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. + */ + +#pragma once +#include "PolyGlobals.h" +#include "PolyVector3.h" +#include "PolyMatrix4.h" + +namespace Polycode { + + /** + * Ray class. + */ + class _PolyExport Ray : public PolyBase { + public: + Ray(); + Ray(const Vector3 &origin, const Vector3 &direction); + + Number boxIntersect(const Vector3 &box, const Matrix4 &transformMatrix, float near = 0.0, float far = 9999.0) const; + + Vector3 planeIntersectPoint(const Vector3 &planeNormal, Number planeDistance) const; + Vector3 planeIntersectPoint(const Vector3 &planeNormal, const Vector3 &planePosition) const; + Ray tranformByMatrix(const Matrix4& matrix) const; + + /** + * finds the two closest point on the ray to an arbitrary point space. + */ + Vector3 closestPointOnRay(const Vector3 &point) const; + + /** + * finds the two closest points between two rays, returns false if they're parallel. + */ + bool closestPointsBetween(const Ray &ray2, Vector3 *point1, Vector3 *point2); + + bool polygonIntersect(const Vector3 &v1, const Vector3 &v2, const Vector3 &v3) const; + + Vector3 origin; + Vector3 direction; + + Vector3 inv_direction; + int sign[3]; + }; + +} \ No newline at end of file diff --git a/Core/Contents/Include/PolyRenderDataArray.h b/Core/Contents/Include/PolyRenderDataArray.h new file mode 100755 index 000000000..78ac500aa --- /dev/null +++ b/Core/Contents/Include/PolyRenderDataArray.h @@ -0,0 +1,104 @@ +/* + Copyright (C) 2014 by Ivan Safrin + + 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: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + 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. + */ + +#pragma once +#include "PolyGlobals.h" +#include + +namespace Polycode { + + class RenderDataArray : public PolyBase { + public: + + RenderDataArray(unsigned int type); + unsigned int type; + virtual void *getArrayData(); + virtual unsigned int getDataSize(); + + /** + * Vertex position array. + */ + static const int VERTEX_DATA_ARRAY = 0; + + /** + * Vertex color array. + */ + static const int COLOR_DATA_ARRAY = 1; + + /** + * Vertex normal array. + */ + static const int NORMAL_DATA_ARRAY = 2; + + /** + * Vertex texture coordinate array. + */ + static const int TEXCOORD_DATA_ARRAY = 3; + + /** + * Tangent array. + */ + static const int TANGENT_DATA_ARRAY = 4; + + /** + * Bone weight array. + */ + static const int BONE_WEIGHT_DATA_ARRAY = 5; + + /** + * Bone weight array. + */ + static const int BONE_INDEX_DATA_ARRAY = 6; + + /** + * Index data array. + */ + static const int INDEX_DATA_ARRAY = 7; + + /** + * Secondary texture coordinate array. + */ + static const int TEXCOORD2_DATA_ARRAY = 8; + + }; + + class VertexDataArray : public RenderDataArray { + public: + VertexDataArray(unsigned int type) : RenderDataArray(type) { + } + + std::vector data; + virtual void *getArrayData(); + virtual unsigned int getDataSize(); + }; + + class IndexDataArray : public RenderDataArray { + public: + IndexDataArray(unsigned int type) : RenderDataArray(type) { + } + + std::vector data; + virtual void *getArrayData(); + virtual unsigned int getDataSize(); + }; + +} \ No newline at end of file diff --git a/Core/Contents/Include/PolyRenderer.h b/Core/Contents/Include/PolyRenderer.h index 113b699d4..768acb579 100755 --- a/Core/Contents/Include/PolyRenderer.h +++ b/Core/Contents/Include/PolyRenderer.h @@ -28,6 +28,7 @@ THE SOFTWARE. #include "PolyShader.h" #include "PolyImage.h" #include "PolyRectangle.h" +#include namespace Polycode { @@ -38,6 +39,7 @@ namespace Polycode { class PolycodeShaderModule; class Polygon; class RenderDataArray; + class IndexDataArray; class ShaderBinding; class Texture; class VertexBuffer; @@ -64,12 +66,11 @@ namespace Polycode { class _PolyExport LightSorter : public PolyBase { public: Vector3 basePosition; - Matrix4 cameraMatrix; - bool operator() (LightInfo i,LightInfo j) { + bool operator() (LightInfo i,LightInfo j) { if(i.lightImportance > j.lightImportance) return true; if(i.lightImportance == j.lightImportance) - return ((cameraMatrix*i.position).distance(basePosition)<(cameraMatrix*j.position).distance(basePosition)); + return i.position.distance(basePosition) < j.position.distance(basePosition); return false; } }; @@ -82,8 +83,6 @@ namespace Polycode { * * The renderer should only be accessed from the CoreServices singleton. Renderer operations should only be called from within Render methods of entities so that they can be properly managed. * - * @see http://www.glprogramming.com/red/ - * @see http://nehe.gamedev.net/tutorial/lessons_01__05/22004/ */ class _PolyExport Renderer : public PolyBase { public: @@ -94,30 +93,33 @@ namespace Polycode { virtual void Resize(int xRes, int yRes) = 0; - virtual void BeginRender() = 0; - virtual void EndRender() = 0; + virtual void BeginRender(); + virtual void EndRender(); virtual Cubemap *createCubemap(Texture *t0, Texture *t1, Texture *t2, Texture *t3, Texture *t4, Texture *t5) = 0; virtual Texture *createTexture(unsigned int width, unsigned int height, char *textureData, bool clamp, bool createMipmaps, int type=Image::IMAGE_RGBA) = 0; virtual void destroyTexture(Texture *texture) = 0; + virtual void destroyVertexBuffer(VertexBuffer *buffer) = 0; + virtual void createRenderTextures(Texture **colorBuffer, Texture **depthBuffer, int width, int height, bool floatingPointBuffer) = 0; virtual Texture *createFramebufferTexture(unsigned int width, unsigned int height) = 0; - virtual void bindFrameBufferTexture(Texture *texture) = 0; - virtual void bindFrameBufferTextureDepth(Texture *texture) = 0; - virtual void unbindFramebuffers() = 0; + virtual void bindFrameBufferTexture(Texture *texture); + virtual void bindFrameBufferTextureDepth(Texture *texture); + virtual void unbindFramebuffers(); virtual Image *renderScreenToImage() = 0; + virtual Image *renderBufferToImage(Texture *texture) = 0; - void setFOV(Number fov); void setViewportSize(int w, int h); - void setViewportSizeAndFOV(int w, int h, Number fov); virtual void resetViewport() = 0; + + virtual Polycode::Rectangle getViewport() = 0; virtual void loadIdentity() = 0; - virtual void setOrthoMode(Number xSize=0.0f, Number ySize=0.0f, bool centered = false) = 0; - virtual void _setOrthoMode(Number orthoSizeX, Number orthoSizeY) = 0; - virtual void setPerspectiveMode() = 0; + virtual void setProjectionOrtho(Number xSize=0.0f, Number ySize=0.0f, Number near=-256.0f, Number far=256.0f, bool centered = false) = 0; + virtual void setPerspectiveDefaults() = 0; + virtual void setProjectionMatrix(Matrix4 matrix) = 0; virtual void setTexture(Texture *texture) = 0; virtual void enableBackfaceCulling(bool val) = 0; @@ -127,33 +129,30 @@ namespace Polycode { virtual void setAmbientColor(Number r, Number g, Number b); - virtual void clearScreen() = 0; + virtual void clearScreen(bool clearColor = true, bool clearDepth = true) = 0; virtual void translate2D(Number x, Number y) = 0; virtual void rotate2D(Number angle) = 0; - virtual void scale2D(Vector2 *scale) = 0; + virtual void scale2D(const Vector2 &scale) = 0; virtual void setVertexColor(Number r, Number g, Number b, Number a) = 0; - - void pushDataArrayForMesh(Mesh *mesh, int arrayType); - + virtual void pushRenderDataArray(RenderDataArray *array) = 0; - virtual RenderDataArray *createRenderDataArrayForMesh(Mesh *mesh, int arrayType) = 0; - virtual RenderDataArray *createRenderDataArray(int arrayType) = 0; - virtual void setRenderArrayData(RenderDataArray *array, Number *arrayData) = 0; - virtual void drawArrays(int drawType) = 0; + virtual void drawArrays(int drawType, IndexDataArray *indexArray) = 0; - virtual void translate3D(Vector3 *position) = 0; + virtual void translate3D(const Vector3 &position) = 0; virtual void translate3D(Number x, Number y, Number z) = 0; - virtual void scale3D(Vector3 *scale) = 0; + virtual void scale3D(const Vector3 &scale) = 0; virtual void pushMatrix() = 0; virtual void popMatrix() = 0; virtual void setLineSmooth(bool val) = 0; virtual void setLineSize(Number lineSize) = 0; - + virtual void setPointSize(Number pointSize) = 0; + virtual void setPointSmooth(bool val) = 0; + virtual void enableLighting(bool enable) = 0; virtual void enableFog(bool enable) = 0; @@ -161,45 +160,39 @@ namespace Polycode { virtual void multModelviewMatrix(Matrix4 m) = 0; virtual void setModelviewMatrix(Matrix4 m) = 0; - - void setCurrentModelMatrix(Matrix4 m) { currentModelMatrix = m; } - Matrix4 getCurrentModelMatrix() { return currentModelMatrix; } - + virtual void setBlendingMode(int blendingMode) = 0; - virtual void applyMaterial(Material *material, ShaderBinding *localOptions, unsigned int shaderIndex) = 0; + virtual void applyMaterial(Material *material, ShaderBinding *localOptions, unsigned int shaderIndex, bool forceMaterial); virtual void clearShader() = 0; virtual void setDepthFunction(int depthFunction) = 0; - virtual void createVertexBufferForMesh(Mesh *mesh) = 0; + virtual VertexBuffer *createVertexBufferForMesh(Mesh *mesh) = 0; virtual void drawVertexBuffer(VertexBuffer *buffer, bool enableColorBuffer) = 0; - void setRenderMode(int newRenderMode); - int getRenderMode(); - virtual void enableDepthTest(bool val) = 0; virtual void enableDepthWrite(bool val) = 0; + virtual void setWireframePolygonMode(bool val) = 0; void billboardMatrix(); void billboardMatrixWithScale(Vector3 scale); void setTextureFilteringMode(int mode); - + /** - * Set the near and far clipping planes for the visible frustum. + * Set the frustum clipping planes. * * Please check the supplied external links for more information * about the problems of a high farPlane/nearPlane setting. * - * @param nearPlane The new near clipping plane. - * @param farPlane The new far clipping plane. - * - * @see http://en.wikipedia.org/wiki/Viewing_frustum * @see http://www.opengl.org/sdk/docs/man2/xhtml/glFrustum.xml */ - virtual void setClippingPlanes(Number nearPlane, Number farPlane) = 0; - + virtual void setProjectionFromFrustum(Number left, Number right, Number bottom, Number top, Number front, Number back) = 0; + + + virtual void setProjectionFromFoV(Number fov, Number near, Number far) = 0; + /** * Enable/disable alpha tests. * @@ -217,7 +210,6 @@ namespace Polycode { const Matrix4& getCameraMatrix() const; void setCameraMatrix(const Matrix4& matrix); - void setCameraPosition(Vector3 pos); virtual void drawScreenQuad(Number qx, Number qy) = 0; @@ -237,13 +229,13 @@ namespace Polycode { virtual void cullFrontFaces(bool val) = 0; void clearLights(); - void addLight(int lightImportance, Vector3 position, Vector3 direction, int type, Color color, Color specularColor, Number constantAttenuation, Number linearAttenuation, Number quadraticAttenuation, Number intensity, Number spotlightCutoff, Number spotlightExponent, bool shadowsEnabled, Matrix4 *textureMatrix, Texture *shadowMapTexture); + void addLight(int lightImportance, const Vector3 &position, const Vector3 &direction, int type, const Color &color, const Color &specularColor, Number constantAttenuation, Number linearAttenuation, Number quadraticAttenuation, Number intensity, Number spotlightCutoff, Number spotlightExponent, bool shadowsEnabled, Matrix4 *textureMatrix, Texture *shadowMapTexture); void setExposureLevel(Number level); - bool rayTriangleIntersect(Vector3 ray_origin, Vector3 ray_direction, Vector3 vert0, Vector3 vert1, Vector3 vert2, Vector3 *hitPoint); + virtual Vector3 projectRayFrom2DCoordinate(Number x, Number y, const Matrix4 &cameraMatrix, const Matrix4 &projectionMatrix, const Polycode::Rectangle &viewport) = 0; - virtual Vector3 projectRayFrom2DCoordinate(Number x, Number y, Matrix4 cameraMatrix, Matrix4 projectionMatrix) = 0; + virtual Vector2 Project(const Matrix4 &cameraMatrix, const Matrix4 &projectionMatrix, const Polycode::Rectangle &viewport, const Vector3 &coordiante) const = 0; void enableShaders(bool flag); @@ -256,20 +248,16 @@ namespace Polycode { void setRendererShaderParams(Shader *shader, ShaderBinding *binding); void addShaderModule(PolycodeShaderModule *module); - - virtual bool test2DCoordinateInPolygon(Number x, Number y, Matrix4 cameraMatrix, Matrix4 projectionMatrix, Polygon *poly, const Matrix4 &matrix, bool ortho, bool testBackfacing, bool billboardMode, bool reverseDirection = false, Matrix4 *adjustMatrix = NULL); - + virtual Matrix4 getProjectionMatrix() = 0; virtual Matrix4 getModelviewMatrix() = 0; - - static const int RENDER_MODE_NORMAL = 0; - static const int RENDER_MODE_WIREFRAME = 1; - - static const int BLEND_MODE_NORMAL = 0; - static const int BLEND_MODE_LIGHTEN = 1; - static const int BLEND_MODE_COLOR = 2; - static const int BLEND_MODE_PREMULTIPLIED = 3; - static const int BLEND_MODE_MULTIPLY = 4; + + static const int BLEND_MODE_NONE = 0; + static const int BLEND_MODE_NORMAL = 1; + static const int BLEND_MODE_LIGHTEN = 2; + static const int BLEND_MODE_COLOR = 3; + static const int BLEND_MODE_PREMULTIPLIED = 4; + static const int BLEND_MODE_MULTIPLY = 5; static const int FOG_LINEAR = 0; static const int FOG_EXP = 1; @@ -281,10 +269,8 @@ namespace Polycode { static const int TEX_FILTERING_NEAREST = 0; static const int TEX_FILTERING_LINEAR = 1; -// void addShadowMap(Texture *texture); -// vector getShadowMapTextures(){ return shadowMapTextures; }; - virtual Vector3 Unproject(Number x, Number y) = 0; + virtual Vector3 Unproject(Number x, Number y, const Matrix4 &cameraMatrix, const Matrix4 &projectionMatrix, const Polycode::Rectangle &viewport) = 0; Color ambientColor; Color clearColor; @@ -293,26 +279,53 @@ namespace Polycode { void sortLights(); - int getNumAreaLights() { return numAreaLights; } + int getNumPointLights() { return numPointLights; } int getNumSpotLights() { return numSpotLights; } int getNumLights() { return numLights; } - std::vector getAreaLights() { return areaLights; } + std::vector getPointLights() { return pointLights; } std::vector getSpotLights() { return spotLights; } bool doClearBuffer; bool blendNormalAsPremultiplied; - + Number alphaTestValue; + + void setBackingResolutionScale(Number xScale, Number yScale); + + Number getBackingResolutionScaleX(); + Number getBackingResolutionScaleY(); + + void setOverrideMaterial(Material *material); + + void pushVertexColor(); + void popVertexColor(); + void loadVertexColorIdentity(); + void multiplyVertexColor(const Color &color); + + void setRenderToGlobalFramebuffer(bool val); + bool getRenderToGlobalFramebuffer() const; + + Texture *getGlobalColorFramebuffer() const; + Texture *getGlobalDepthFramebuffer() const; + protected: virtual void initOSSpecific() {}; - + + Number backingResolutionScaleX; + Number backingResolutionScaleY; + + std::stack vertexColorStack; + Color currentVertexColor; + + std::stack framebufferStackColor; + std::stack framebufferStackDepth; + bool scissorEnabled; Polycode::Rectangle scissorBox; Number anisotropy; - Matrix4 currentModelMatrix; LightSorter sorter; Number viewportWidth; @@ -326,29 +339,28 @@ namespace Polycode { Material *currentMaterial; int textureFilteringMode; - int renderMode; Matrix4 cameraMatrix; - + Material *overrideMaterial; + PolycodeShaderModule* currentShaderModule; std::vector shaderModules; + + bool renderToGlobalFramebuffer; + Texture *globalColorFramebuffer; + Texture *globalDepthFramebuffer; std::vector lights; - std::vector areaLights; + std::vector pointLights; std::vector spotLights; int numLights; - int numAreaLights; + int numPointLights; int numSpotLights; bool shadersEnabled; Number fov; - - Number orthoSizeX; - Number orthoSizeY; bool lightingEnabled; - - bool orthoMode; int xRes; int yRes; diff --git a/Core/Contents/Include/PolyResource.h b/Core/Contents/Include/PolyResource.h index ea0c70283..69596fa59 100755 --- a/Core/Contents/Include/PolyResource.h +++ b/Core/Contents/Include/PolyResource.h @@ -57,10 +57,13 @@ namespace Polycode { static const int RESOURCE_PROGRAM = 3; static const int RESOURCE_MESH = 5; static const int RESOURCE_CUBEMAP = 6; - static const int RESOURCE_SCREEN_SPRITE = 7; - static const int RESOURCE_SCREEN_ENTITY_INSTANCE = 8; + static const int RESOURCE_SPRITE = 7; + static const int RESOURCE_ENTITY_INSTANCE = 8; bool reloadOnFileModify; + + static bool defaultReloadOnFileModify; + time_t resourceFileTime; //@} diff --git a/Core/Contents/Include/PolyResourceManager.h b/Core/Contents/Include/PolyResourceManager.h index 40dff9b68..0fb5b6e4c 100755 --- a/Core/Contents/Include/PolyResourceManager.h +++ b/Core/Contents/Include/PolyResourceManager.h @@ -23,6 +23,7 @@ THE SOFTWARE. #pragma once #include "PolyGlobals.h" +#include "PolyEventDispatcher.h" #include #define RESOURCE_CHECK_INTERVAL 2000 @@ -32,33 +33,56 @@ namespace Polycode { class Resource; class PolycodeShaderModule; class String; + + class _PolyExport ResourcePool : public EventDispatcher { + public: + ResourcePool(const String &name, ResourcePool *fallbackPool); + ~ResourcePool(); + + void setFallbackPool(ResourcePool *pool); + + void addResource(Resource *resource); + void removeResource(Resource *resource); + bool hasResource(Resource *resource); + + Resource *getResource(int resourceType, const String& resourceName) const; + + String getName(); + void setName(const String &name); + + Resource *getResourceByPath(const String& resourcePath) const; + void Update(int elapsed); + + std::vector getResources(int resourceType); + + void checkForChangedFiles(); + + bool reloadResourcesOnModify; + bool dispatchChangeEvents; + + int resourceSubscribers; + bool deleteOnUnsubscribe; + + static bool defaultReloadResourcesOnModify; + + private: + + ResourcePool *fallbackPool; + String name; + int ticksSinceCheck; + std::vector resources; + + }; /** * Manages loading and unloading of resources from directories and archives. Should only be accessed via the CoreServices singleton. */ - class _PolyExport ResourceManager : public PolyBase { + class _PolyExport ResourceManager : public EventDispatcher { public: ResourceManager(); ~ResourceManager(); - /** - * Adds a new resource. - * @param resource Resource to add. - */ - void addResource(Resource *resource); - /** - * Removes a resource. - * @param resource Resource to resource. - */ - void removeResource(Resource *resource); - - - /** - * Returns true if the following resource has been adde to the resource manager. - * @param resource Resource to check. - */ - bool hasResource(Resource *resource); /** * Loads resources from a directory. * @param dirPath Path to directory to load resources from. @@ -77,43 +101,33 @@ namespace Polycode { void removeArchive(const String& path); - bool readFile(const String& fileName) { return false;} - - void parseTextures(const String& dirPath, bool recursive, const String& basePath); - void parseMaterials(const String& dirPath, bool recursive); - void parseShaders(const String& dirPath, bool recursive); - void parsePrograms(const String& dirPath, bool recursive); - void parseCubemaps(const String& dirPath, bool recursive); - void parseOthers(const String& dirPath, bool recursive); - - /** - * Request a loaded resource. You need to manually cast it to its subclass based on its type. - * @param resourceType Type of resource. See Resource for available resource types. - * @param resourceName Name of the resource to request. - */ - Resource *getResource(int resourceType, const String& resourceName) const; - - Resource *getResourceByPath(const String& resourcePath) const; - - - /** - * Request a full set of loaded resources. You need to manually cast them to their subclasses based on their type. - * @param resourceType Type of resource. See Resource for available resource types. - */ - std::vector getResources(int resourceType); - - void addShaderModule(PolycodeShaderModule *module); - - void checkForChangedFiles(); + void parseTexturesIntoPool(ResourcePool *pool, const String& dirPath, bool recursive, const String& basePath); + void parseMaterialsIntoPool(ResourcePool *pool, const String& dirPath, bool recursive); + void parseShadersIntoPool(ResourcePool *pool, const String& dirPath, bool recursive); + void parseProgramsIntoPool(ResourcePool *pool, const String& dirPath, bool recursive); + void parseCubemapsIntoPool(ResourcePool *pool, const String& dirPath, bool recursive); + void parseOtherIntoPool(ResourcePool *pool, const String& dirPath, bool recursive); + ResourcePool *getGlobalPool(); + + ResourcePool *getResourcePoolByName(const String &name); + + void addResourcePool(ResourcePool *pool); + void removeResourcePool(ResourcePool *pool); + + std::vector getResources(int resourceType); + + void removeResource(Resource *resource); + + void subscribeToResourcePool(ResourcePool *pool); + void unsubscibeFromResourcePool(ResourcePool *pool); + void Update(int elapsed); - - bool reloadResourcesOnModify; + void handleEvent(Event *event); private: - int ticksSinceCheck; - std::vector resources; - std::vector shaderModules; + ResourcePool *globalPool; + std::vector pools; }; } diff --git a/Core/Contents/Include/PolySDLCore.h b/Core/Contents/Include/PolySDLCore.h index 8d2e64022..e22baf8d9 100644 --- a/Core/Contents/Include/PolySDLCore.h +++ b/Core/Contents/Include/PolySDLCore.h @@ -43,15 +43,15 @@ namespace Polycode { public: - SDLCore(PolycodeView *view, int xRes, int yRes, bool fullScreen, bool vSync, int aaLevel, int anisotropyLevel, int frameRate, int monitorIndex=-1); + SDLCore(PolycodeView *view, int xRes, int yRes, bool fullScreen, bool vSync, int aaLevel, int anisotropyLevel, int frameRate, int monitorIndex=-1, bool retinaSupport=false); ~SDLCore(); void enableMouse(bool newval); void captureMouse(bool); unsigned int getTicks(); - bool Update(); + bool systemUpdate(); void Render(); - void setVideoMode(int xRes, int yRes, bool fullScreen, bool vSync, int aaLevel, int anisotropyLevel); + void setVideoMode(int xRes, int yRes, bool fullScreen, bool vSync, int aaLevel, int anisotropyLevel, bool retinaSupport = true); void createThread(Threaded *target); std::vector getVideoModes(); @@ -68,6 +68,7 @@ namespace Polycode { void removeDiskItem(const String& itemPath); String openFolderPicker(); std::vector openFilePicker(std::vector extensions, bool allowMultiple); + String saveFilePicker(std::vector extensions); void resizeTo(int xRes, int yRes); String executeExternalCommand(String command, String args, String inDirectory=""); diff --git a/Core/Contents/Include/PolyScene.h b/Core/Contents/Include/PolyScene.h index 3bb3900cd..f7e7fd47d 100755 --- a/Core/Contents/Include/PolyScene.h +++ b/Core/Contents/Include/PolyScene.h @@ -25,6 +25,8 @@ THE SOFTWARE. #include "PolyString.h" #include "PolyColor.h" #include "PolyVector3.h" +#include "PolyEntity.h" +#include "PolyCore.h" #include "PolyEventDispatcher.h" #include @@ -34,44 +36,47 @@ class OSFILE; namespace Polycode { class Camera; - class SceneEntity; + class Entity; class SceneLight; - class SceneMesh; + class Mesh; /** - * 3D rendering container. The Scene class is the main container for all 3D rendering in Polycode. Scenes are automatically rendered and need only be instantiated to immediately add themselves to the rendering pipeline. A Scene is created with a camera automatically. + * Rendering container. The Scene class is the main container for all rendering in Polycode. Scenes are automatically rendered and need only be instantiated to immediately add themselves to the rendering pipeline. A Scene is created with a camera automatically. */ class _PolyExport Scene : public EventDispatcher { public: - - /** - * Default constructor. - */ - Scene(); + /** * Default constructor with options. + * @param sceneType Type of scene to create. Can be Scene::SCENE_2D, Scene::SCENE_3D or Scene::SCENE_2D_TOPLEFT * @param virtualScene If this flag is set to true, the scene is not rendered to the screen. Use this if you want to render the scene only to a texture. */ - Scene(bool virtualScene); + Scene(int sceneType, bool virtualScene = false); + + /** + * Default constructor. Defaults to type Scene::SCENE_3D + */ + Scene(); + virtual ~Scene(); /** - * Adds a new SceneEntity to the scene + * Adds a new Entity to the scene * @param entity New entity to add. */ - void addEntity(SceneEntity *entity); + void addEntity(Entity *entity); /** - * Adds a new SceneEntity to the scene + * Adds a new Entity to the scene * @param entity New entity to add. */ - void addChild(SceneEntity *entity); + void addChild(Entity *entity); /** - * Removes a SceneEntity from the scene + * Removes a Entity from the scene * @param entity New entity to remove. */ - virtual void removeEntity(SceneEntity *entity); + virtual void removeEntity(Entity *entity); /** * Returns the scene's default camera. @@ -112,7 +117,10 @@ namespace Polycode { * @param endDepth Ending depth of the fog. */ void setFogProperties(int fogMode, Color color, Number density, Number startDepth, Number endDepth); - + + void setSceneType(int newType); + + virtual void fixedUpdate(); virtual void Update(); void setVirtual(bool val); bool isVirtual(); @@ -120,19 +128,14 @@ namespace Polycode { bool isEnabled(); void setEnabled(bool enabled); - int getNumEntities() { return (int)entities.size(); } - SceneEntity *getEntity(int index) { return entities[index]; } - - /** - * Returns the entity at the specified screen position. This is currently very slow and not super reliable. - * @param x X position. - * @param y Y position. - * @return Entity at specified screen position. - */ - SceneEntity *getEntityAtScreenPosition(Number x, Number y); - void Render(Camera *targetCamera = NULL); void RenderDepthOnly(Camera *targetCamera); + + void setOverrideMaterial(Material *material); + + void handleEvent(Event *event); + + Ray projectRayFromCameraAndViewportCoordinate(Camera *camera, Vector2 coordinate); /** * Adds a light to the scene. @@ -144,18 +147,10 @@ namespace Polycode { * Removes a light from the scene. * @param light Light to remove from the scene. */ - void removeLight(SceneLight *light); - - SceneLight *getNearestLight(Vector3 pos); + void removeLight(SceneLight *light); int getNumLights(); SceneLight *getLight(int index); - - static const unsigned int ENTITY_MESH = 0; - static const unsigned int ENTITY_LIGHT = 1; - static const unsigned int ENTITY_CAMERA = 2; - static const unsigned int ENTITY_ENTITY = 3; - static const unsigned int ENTITY_COLLMESH = 4; /** * Scene clear color @@ -163,10 +158,17 @@ namespace Polycode { Color clearColor; /** - * If set to true, the renderer will use the scene's clear color when rendering the scene. + * If set to true, the renderer will clear the screen prior to rendering the scene + * @default true */ bool useClearColor; + /** + * If set to true, the renderer will clear the depth buffer prior to rendering the scene. + * @default true + */ + bool useClearDepth; + /** * Ambient color, passed to lighting shaders */ @@ -187,17 +189,39 @@ namespace Polycode { */ bool ownsChildren; + static const int SCENE_3D = 0; + static const int SCENE_2D = 1; + static const int SCENE_2D_TOPLEFT = 2; + + Entity rootEntity; + + Polycode::Rectangle sceneMouseRect; + bool remapMouse; + + bool constrainPickingToViewport; + + void doVisibilityChecking(bool val); + bool doesVisibilityChecking(); + protected: + void initScene(int sceneType, bool virtualScene); + void setEntityVisibility(Entity *entity, Camera *camera); + void setEntityVisibilityBool(Entity *entity, bool val); + bool hasLightmaps; + bool _doVisibilityChecking; - std::vector lights; - + Renderer *renderer; + std::vector lights; bool isSceneVirtual; Camera *defaultCamera; Camera *activeCamera; - std::vector entities; + + Material *overrideMaterial; + + Core *core; bool lightingEnabled; bool fogEnabled; @@ -206,5 +230,7 @@ namespace Polycode { Number fogStartDepth; Number fogEndDepth; + int sceneType; + }; } diff --git a/Core/Contents/Include/PolySceneEntity.h b/Core/Contents/Include/PolySceneEntity.h deleted file mode 100755 index 4b0b29d1c..000000000 --- a/Core/Contents/Include/PolySceneEntity.h +++ /dev/null @@ -1,56 +0,0 @@ -/* -Copyright (C) 2011 by Ivan Safrin - -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: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -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. -*/ - -#pragma once -#include "PolyEntity.h" -#include "PolyEventDispatcher.h" - -namespace Polycode { - - /** - * 3D base entity. SceneEntities are the base class for all 3D entities in Polycode. A thin wrapper around Entity, it inherits most of its functionality. - @see Entity - */ - class _PolyExport SceneEntity : public Entity { - public: - SceneEntity(); - virtual ~SceneEntity(); - - /** - * Test mouse collision on the scene entity at a specified screen point. Each SceneEntity subclass must implement this if it wants to support this feature. - * @param x X position on screen. - * @param y Y position on screen. - * @return True if the entity is at the specified screen coordinate. - */ - virtual bool testMouseCollision(Number x, Number y) { return false;} - - /** - * If set to true, will cast shadows (Defaults to true). - */ - bool castShadows; - - int collisionShapeType; - - protected: - - }; -} diff --git a/Core/Contents/Include/PolySceneEntityInstance.h b/Core/Contents/Include/PolySceneEntityInstance.h new file mode 100644 index 000000000..c0d49260c --- /dev/null +++ b/Core/Contents/Include/PolySceneEntityInstance.h @@ -0,0 +1,127 @@ +/* +Copyright (C) 2013 by Ivan Safrin + +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: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +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. +*/ + + +#pragma once +#include "PolyGlobals.h" +#include "PolyEntity.h" +#include "PolyObject.h" +#include "PolyParticleEmitter.h" +#include "PolyScenePrimitive.h" +#include "PolyResource.h" +#include "PolySceneSprite.h" +#include "PolyBezierCurve.h" +#include "PolySceneLine.h" +#include "PolyScene.h" +#include "PolySound.h" + +namespace Polycode { + +class SceneEntityInstanceResourceEntry; + class SceneEntityInstanceLayer; + +class SceneEntityInstance : public Entity { + public: + + SceneEntityInstance(Scene *parentScene, const String& fileName, ResourcePool *loadIntoPool = NULL); + explicit SceneEntityInstance(Scene *parentScene); + + static SceneEntityInstance *BlankSceneEntityInstance(Scene *parentScene); + + virtual ~SceneEntityInstance(); + + virtual Entity *Clone(bool deepClone, bool ignoreEditorOnly) const; + virtual void applyClone(Entity *clone, bool deepClone, bool ignoreEditorOnly) const; + + void reloadEntityInstance(); + + void clearInstance(); + + void parseObjectIntoCurve(ObjectEntry *entry, BezierCurve *curve); + Entity *loadObjectEntryIntoEntity(ObjectEntry *entry, Entity *targetEntity = NULL, int entityFileVersion = 1); + bool loadFromFile(const String& fileName); + void applySceneMesh(ObjectEntry *entry, SceneMesh *sceneMesh); + + void linkResourcePool(ResourcePool *pool); + unsigned int getNumLinkedResourePools(); + ResourcePool *getLinkedResourcePoolAtIndex(unsigned int index); + + void unlinkResourcePool(ResourcePool *pool); + + SceneEntityInstanceResourceEntry *getResourceEntry(); + + ResourcePool *getTopLevelResourcePool(); + + bool hasLayerID(unsigned char layerID) const; + unsigned int getNumLayers() const; + SceneEntityInstanceLayer *getLayerAtIndex(unsigned int index) const; + void removeLayer(SceneEntityInstanceLayer *layer); + + + SceneEntityInstanceLayer *createNewLayer(String name); + + String getFileName() const; + bool cloneUsingReload; + + String fileName; + + protected: + + std::vector layers; + + void rebuildResourceLinks(); + + ResourcePool *loadIntoPool; + ResourcePool *topLevelResourcePool; + std::vector resourcePools; + Scene *parentScene; + SceneEntityInstanceResourceEntry *resourceEntry; + +}; + +class SceneEntityInstanceLayer { + public: + SceneEntityInstanceLayer(SceneEntityInstance *instance,String name); + + void setLayerVisibility(bool val); + + String name; + unsigned char layerID; + bool visible; + SceneEntityInstance *instance; +}; + + +class SceneEntityInstanceResourceEntry : public Resource { + public: + SceneEntityInstanceResourceEntry(SceneEntityInstance *instance); + virtual ~SceneEntityInstanceResourceEntry(); + + SceneEntityInstance *getInstance(); + void reloadResource(); + + protected: + SceneEntityInstance* instance; +}; + + +} \ No newline at end of file diff --git a/Core/Contents/Include/PolySceneImage.h b/Core/Contents/Include/PolySceneImage.h new file mode 100644 index 000000000..6f2353f1f --- /dev/null +++ b/Core/Contents/Include/PolySceneImage.h @@ -0,0 +1,96 @@ +/* +Copyright (C) 2011 by Ivan Safrin + +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: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +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. +*/ + +#pragma once +#include "PolyGlobals.h" +#include "PolyImage.h" +#include "PolyScenePrimitive.h" + +namespace Polycode { + + /** + * 2D screen image display. This ScreenEntity can load and display and image. + */ + class _PolyExport SceneImage : public ScenePrimitive { + public: + /** + * Create screen image from file. + * @param fileName + */ + explicit SceneImage(const String& fileName); + + /** + * Create screen image from Image. + * @param image Image to create from. + */ + explicit SceneImage(Image *image); + + /** + * Create screen image from Texture. + * @param texture Texture to create from. + */ + explicit SceneImage(Texture *texture); + + /** + * Create screen image from Image. + * @param image Image to create from. + */ + static SceneImage* SceneImageWithImage(Image *image); + + /** + * Create screen image from Texture. + * @param texture Texture to create from. + */ + static SceneImage* SceneImageWithTexture(Texture *texture); + + virtual ~SceneImage(); + + virtual Entity *Clone(bool deepClone, bool ignoreEditorOnly) const; + virtual void applyClone(Entity *clone, bool deepClone, bool ignoreEditorOnly) const; + + /** + * Changes which part of the image is displayed. + * @param x X position of the display rectangle. + * @param y Y position of the display rectangle. + * @param width Width of the display rectangle. + * @param height Height of the display rectangle. + */ + void setImageCoordinates(Number x, Number y, Number width, Number height, Number realWidth=-1, Number realHeight=-1); + + /** + * Returns the image width. + */ + Number getImageWidth() const; + + /** + * Returns the image height. + */ + Number getImageHeight() const; + + protected: + + Number imageWidth; + Number imageHeight; + + }; + +} \ No newline at end of file diff --git a/Core/Contents/Include/PolySceneLabel.h b/Core/Contents/Include/PolySceneLabel.h index 1c0cdb2a9..67728524c 100755 --- a/Core/Contents/Include/PolySceneLabel.h +++ b/Core/Contents/Include/PolySceneLabel.h @@ -38,15 +38,18 @@ namespace Polycode { class _PolyExport SceneLabel : public ScenePrimitive { public: - /** - * Constructor. - * @param fontName Name of a registered font to use. @see FontManager for info on how to register fonts. - * @param text Text to display. - * @param size Size in pixels. - * @param scale Scale to multiply pixel size by for the actual world size of the label. - * @param Anti-aliasing mode. Can be Label::ANTIALIAS_FULL or Label::ANTIALIAS_NONE. - */ - SceneLabel(const String& fontName, const String& text, int size, Number scale, int amode, bool premultiplyAlpha = false); + + SceneLabel(const String& text, int size, const String& fontName = "sans", int amode = 0, Number actualHeight = 0.0, bool premultiplyAlpha = false, const Color &backgroundColor = Color(0.0, 0.0, 0.0, 0.0), const Color &foregroundColor = Color(1.0, 1.0, 1.0, 1.0)); + + String getText(); + + void setLabelActualHeight(Number actualHeight); + Number getLabelActualHeight(); + + void Render(); + + int getTextWidthForString(String text); + virtual ~SceneLabel(); /** @@ -54,14 +57,26 @@ namespace Polycode { * @param newText New text to display. */ void setText(const String& newText); + + virtual Entity *Clone(bool deepClone, bool ignoreEditorOnly) const; + virtual void applyClone(Entity *clone, bool deepClone, bool ignoreEditorOnly) const; + void updateFromLabel(); + Label *getLabel(); - protected: + bool positionAtBaseline; - void updateFromLabel(); + static Vector3 defaultAnchor; + static bool defaultPositionAtBaseline; + static bool defaultSnapToPixels; + static bool createMipmapsForLabels; + + protected: - Number scale; + + Number actualHeight; + Number labelScale; Label *label; }; } diff --git a/Core/Contents/Include/PolySceneLight.h b/Core/Contents/Include/PolySceneLight.h index 9bf4582a0..16335876b 100755 --- a/Core/Contents/Include/PolySceneLight.h +++ b/Core/Contents/Include/PolySceneLight.h @@ -23,7 +23,7 @@ THE SOFTWARE. #pragma once #include "PolyGlobals.h" -#include "PolySceneEntity.h" +#include "PolyEntity.h" namespace Polycode { @@ -34,14 +34,14 @@ namespace Polycode { // class ScenePrimitive; /** - * 3D light source. Lights can be area or spot lights and can be set to different colors. + * 3D light source. Lights can be point or spot lights and can be set to different colors. */ - class _PolyExport SceneLight : public SceneEntity { + class _PolyExport SceneLight : public Entity { public: /** * Constructs a light with parameters. - * @param type Type of light to create. Can be SceneLight::AREA_LIGHT or SceneLight::SPOT_LIGHT + * @param type Type of light to create. Can be SceneLight::POINT_LIGHT or SceneLight::SPOT_LIGHT * @param parentScene Scene to light. * @param intensity Light color intensity * @param constantAttenuation Constant falloff attenuation value @@ -86,7 +86,7 @@ namespace Polycode { const Matrix4& getLightViewMatrix() const; - static const int AREA_LIGHT = 0; + static const int POINT_LIGHT = 0; static const int SPOT_LIGHT = 1; Texture *getZBufferTexture() const; @@ -144,20 +144,18 @@ namespace Polycode { */ void setSpotlightProperties(Number spotlightCutoff, Number spotlightExponent) { this->spotlightCutoff = spotlightCutoff; - Number cosVal = cos(spotlightCutoff*(PI/180.0)); - this->spotlightExponent = cosVal - (0.02*spotlightExponent); + this->spotlightExponent = spotlightExponent; } Number getSpotlightCutoff() const { return spotlightCutoff; } Number getSpotlightExponent() const { return spotlightExponent; } - /** * If this is called with 'true', the light will generate a shadow map. * @param val If set to true, enables this light to cast shadows. * @param resolution Resolution of the shadow map. (defaults to 256x256). */ - void enableShadows(bool val, Number resolution=256); + void enableShadows(bool val, unsigned int resolution=256); /** * This sets the shadow map field of view. The larger the field of view, the more of the scene it encompasses, but the more quality it loses. @@ -165,6 +163,13 @@ namespace Polycode { */ void setShadowMapFOV(Number fov); + /** + * Returns the light's shadow map field of view. + */ + Number getShadowMapFOV() const; + + unsigned int getShadowMapResolution() const; + /** * Returns true if shadows are enabled. */ @@ -174,17 +179,20 @@ namespace Polycode { * Returns the light type. */ int getLightType() const { return type; } - - /** - * If set to true, draws a wireframe primitive visualizing the light. - */ - void enableDebugDraw(bool val); void setLightImportance(int newImportance); int getLightImportance() const; + + void setLightType(int lightType); - SceneEntity *lightShape; - + virtual Entity *Clone(bool deepClone, bool ignoreEditorOnly) const; + virtual void applyClone(Entity *clone, bool deepClone, bool ignoreEditorOnly) const; + + Scene *getParentScene() const; + void setParentScene(Scene *scene); + + Camera *getSpotlightCamera(); + protected: Number spotlightExponent; @@ -206,7 +214,7 @@ namespace Polycode { Matrix4 lightViewMatrix; - Number shadowMapRes; + unsigned int shadowMapRes; Number shadowMapFOV; bool shadowsEnabled; diff --git a/Core/Contents/Include/PolySceneLine.h b/Core/Contents/Include/PolySceneLine.h index 967f40dd9..75547785e 100755 --- a/Core/Contents/Include/PolySceneLine.h +++ b/Core/Contents/Include/PolySceneLine.h @@ -24,23 +24,86 @@ THE SOFTWARE. #include "PolyString.h" #include "PolyGlobals.h" #include "PolySceneMesh.h" +#include "PolyBezierCurve.h" #include "PolyCoreServices.h" #include "PolyMesh.h" namespace Polycode { + /** + * BezierCurve scene rendering/placement class. You can use this class to place a bezier curve in scene space for use as animation tracks or rendering. + */ + class _PolyExport SceneCurve : public SceneMesh { + public: + + /* + * Create empty scene curve. + */ + SceneCurve(); + + /* + * Create scene curve with an existing curve. + * @param curve Existing curve to use. + */ + SceneCurve(BezierCurve *curve); + + /* + * Create scene curve with an existing curve. + * @param curve Existing curve to use. + */ + static SceneCurve *SceneCurveWithCurve(BezierCurve *curve); + + /* + * Return a point along the curve in world space. + * @param t Number value from 0.0 to 1.0 along the curve + * @return A Vector3 point along the curve in world space. + */ + Vector3 getWorldPointAt(Number t); + + + virtual ~SceneCurve(); + void Update(); + + + virtual Entity *Clone(bool deepClone, bool ignoreEditorOnly) const; + virtual void applyClone(Entity *clone, bool deepClone, bool ignoreEditorOnly) const; + + /* + * Return the actual bezier curve class. + * @return The bezier curve used in this scene curve. + */ + BezierCurve *getCurve(); + + /* + * If set to true, renders the curve on every frame (defaults to true). + */ + bool renderCurve; + + /* + * Number of vertices to use in rendering the curve. + */ + int curveResolution; + + protected: + + BezierCurve *curve; + }; + /** * 3D line class. Can connect two SceneEntity classes with a line. */ class _PolyExport SceneLine : public SceneMesh { public: - /** - * Constructs the line with two taraget entities. - * @param ent1 Starting entity. - * @param ent2 Ending entity. - */ - SceneLine(SceneEntity *ent1, SceneEntity *ent2); - + + + /** + * Constructs the line with two taraget entities. + * @param ent1 Starting entity. + * @param ent2 Ending entity. + */ + SceneLine(Entity *ent1, Entity *ent2); + + /** * Constructs the line with two taraget positions. * @param start Starting position. @@ -69,8 +132,8 @@ namespace Polycode { Vector3 start; Vector3 end; - SceneEntity *ent1; - SceneEntity *ent2; + Entity *ent1; + Entity *ent2; }; } \ No newline at end of file diff --git a/Core/Contents/Include/PolySceneManager.h b/Core/Contents/Include/PolySceneManager.h index 93a8b2857..0f934508d 100755 --- a/Core/Contents/Include/PolySceneManager.h +++ b/Core/Contents/Include/PolySceneManager.h @@ -29,21 +29,34 @@ namespace Polycode { class Scene; class SceneRenderTexture; + class Renderer; + /** + * This class manages all rendered scenes in Polycode. + */ class _PolyExport SceneManager : public PolyBase { public: SceneManager(); ~SceneManager(); + /** + * Adds a scene to the render loop. Scenes automatically add themselves to the manager on creation, so there's no need to call this manually unless you remove a scene yourself. + */ void addScene(Scene *newScene); + + /** + * Removes scene from the render loop (does not delete the scene). + */ + void removeScene(Scene *scene); + + // Polycode internal void Update(); + void fixedUpdate(); void Render(); - void renderVirtual(); - - void removeScene(Scene *scene); void registerRenderTexture(SceneRenderTexture *renderTexture); void unregisterRenderTexture(SceneRenderTexture *renderTexture); + void setRenderer(Renderer *renderer); private: @@ -51,7 +64,8 @@ namespace Polycode { std::vector scenes; std::vector renderTextures; - + + Renderer *renderer; }; } diff --git a/Core/Contents/Include/PolySceneMesh.h b/Core/Contents/Include/PolySceneMesh.h index cfeeb5050..e58c186e2 100755 --- a/Core/Contents/Include/PolySceneMesh.h +++ b/Core/Contents/Include/PolySceneMesh.h @@ -22,8 +22,10 @@ THE SOFTWARE. #pragma once #include "PolyGlobals.h" -#include "PolySceneEntity.h" +#include "PolyEntity.h" +#include "PolyMesh.h" #include "PolyShader.h" +#include "PolyRenderDataArray.h" namespace Polycode { @@ -31,29 +33,31 @@ namespace Polycode { class Mesh; class Texture; class Skeleton; + class Image; + class ResourcePool; /** * 3D polygonal mesh instance. The SceneMesh is the base for all polygonal 3d geometry. It can have simple textures or complex materials applied to it. */ - class _PolyExport SceneMesh : public SceneEntity { + class _PolyExport SceneMesh : public Entity { public: /** * Construct a scene mesh from a mesh file. * @param fileName Path to mesh file to load. */ - SceneMesh(const String& fileName); + explicit SceneMesh(const String& fileName); /** * Construct an empty scene mesh with the specified type. * @param meshType Mesh type to create. See Mesh for possible values. */ - SceneMesh(int meshType); + explicit SceneMesh(int meshType); /** * Construct scene mesh from an existing Mesh instance. */ - SceneMesh(Mesh *mesh); + explicit SceneMesh(Mesh *mesh); /** * Construct scene mesh from an existing Mesh instance. @@ -70,6 +74,9 @@ namespace Polycode { void Render(); + /** + * Returns the local material binding options for this mesh. + */ ShaderBinding *getLocalShaderOptions(); /** @@ -80,7 +87,7 @@ namespace Polycode { /** * Returns the texture applied. */ - Texture *getTexture(); + Texture *getTexture() const; /** * Returns the material applied. @@ -93,12 +100,14 @@ namespace Polycode { * @param clamp If true, clamps the texture to edges. See Texture for details on that. */ void loadTexture(const String& fileName); + + void loadTextureFromImage(Image *image); /** * Loads a skeleton from a file and applies it to the scene mesh. * @param fileName Filename to load the skeleton from. */ - void loadSkeleton(const String& fileName); + Skeleton *loadSkeleton(const String& fileName); /** * Sets the texture from an existing Texture instance. @@ -121,7 +130,7 @@ namespace Polycode { * Set material by name. You can create materials in material files and name them there, then use this to set a material by name to a scene mesh. * @param materialName Name of material to apply. */ - void setMaterialByName(const String& materialName); + void setMaterialByName(const String& materialName, ResourcePool *resourcePool = NULL); /** * Set the mesh this scene mesh renders. @@ -146,17 +155,49 @@ namespace Polycode { * If this is set to true, the mesh will be cached to a hardware vertex buffer if those are available. This can dramatically speed up rendering. */ void cacheToVertexBuffer(bool cache); - - unsigned int lightmapIndex; - bool showVertexNormals; - - + /** + * Sets the line width for line-based meshes. + */ + void setLineWidth(Number newWidth); + + /** + * If this mesh was loaded form file, returns the filename of the loaded mesh. + */ + String getFilename(); + + /** + * Sets the filename path of the mesh. + */ + void setFilename(String fileName); + + /** + * Loads mesh from file. Deletes current mesh if ownsMesh is set to true. + */ + void loadFromFile(String fileName); + + /** + * Line width for line-based meshes. + */ Number lineWidth; + + /** + * If set to true, will antialias the lines in a line-based mesh. Defaults to false. + */ bool lineSmooth; + /** + * Point size for point-based meshes. + */ + Number pointSize; + + /** + * If setto true, will antialias points in a point-based mesh. Defaults to false. + */ + bool pointSmooth; + /** - * If true, will delete its Mesh upon destruction. (defaults to true) + * If true, will delete its Mesh upon destruction or mesh loading. (defaults to true) */ bool ownsMesh; @@ -164,14 +205,64 @@ namespace Polycode { * If true, will delete its Skeleton upon destruction. (defaults to true) */ bool ownsSkeleton; - + + /** + * If set to true, will render the mesh wireframe on top of the mesh using wireFrameColor. + * @see wireFrameColor + */ + bool overlayWireframe; + + /* + * If overlayWireframe is set to true, defines the color of the mesh wireframe. + */ + Color wireFrameColor; + + /** + * If set to true, will check against actual geometry polygons on ray hit detection. Defaults to false. + */ + bool useGeometryHitDetection; + + bool customHitDetection(const Ray &ray); + + /** + * The Renderer has an ability to set an override material that is set for all rendered entities. If forceMaterial is set to true, this entity will always use its assigned material, even if an override material is set. + */ + bool forceMaterial; + + virtual Entity *Clone(bool deepClone, bool ignoreEditorOnly) const; + virtual void applyClone(Entity *clone, bool deepClone, bool ignoreEditorOnly) const; + + + /** + * Normally, translucent textures do not affect the depth buffer, but if this flag is set to true, this entity's alpha channel is written to the depth buffer at a preset threshold. This flag is set to false by default. + */ + bool alphaTest; + + /** + * If this flag is set to false, backface culling is disabled when rendering this entity, rendering both sides of each face. Set to true by default. + */ + bool backfaceCulled; + + bool sendBoneMatricesToMaterial; + protected: bool useVertexBuffer; + VertexBuffer *vertexBuffer; + Mesh *mesh; Texture *texture; Material *material; Skeleton *skeleton; ShaderBinding *localShaderOptions; + String fileName; + + std::vector materialBoneMatrices; + + VertexDataArray skeletalVertexPositions; + VertexDataArray skeletalVertexNormals; + + + }; } diff --git a/Core/Contents/Include/PolyScenePrimitive.h b/Core/Contents/Include/PolyScenePrimitive.h index 2c3a6489e..78b2803cd 100755 --- a/Core/Contents/Include/PolyScenePrimitive.h +++ b/Core/Contents/Include/PolyScenePrimitive.h @@ -23,6 +23,7 @@ THE SOFTWARE. #pragma once #include "PolyGlobals.h" #include "PolySceneMesh.h" +#include "PolyMesh.h" namespace Polycode { @@ -42,77 +43,137 @@ namespace Polycode { * @param v4 See the constant primitive types for values for these parameters * @param v5 See the constant primitive types for values for these parameters */ - ScenePrimitive(int type, Number v1=1.0f, Number v2=1.0f, Number v3=1.0f,Number v4=0.0f,Number v5=0.0f); + ScenePrimitive(int type, Number v1=1.0f, Number v2=1.0f, Number v3=1.0f,Number v4=1.0f,Number v5=1.0f); virtual ~ScenePrimitive(); + void setPrimitiveOptions(int type, Number v1=1.0f, Number v2=1.0f, Number v3=1.0f,Number v4=1.0f,Number v5=1.0f); + + void recreatePrimitive(); + /** * A cube. * v1 - X size * v2 - Y size - * v3 - Z size + * v3 - Z size + * v4 - # of tiles */ static const int TYPE_BOX = 0; /** * A horizontal plane. * v1 - X size - * v2 - Z size + * v2 - Z size + * v3 - # of tiles */ static const int TYPE_PLANE = 1; - /** - * A sphere. - * v1 - Sphere radius - * v2 - Lat segments - * v3 - Long segments - */ - static const int TYPE_SPHERE = 2; - + /** + * A vertical plane. + * v1 - X size + * v2 - Y size + * v3 - # of tiles + */ + static const int TYPE_VPLANE = 2; + /** * A cylinder. * v1 - Cylinder length * v2 - Cylinder radius * v3 - Number of segments. + * v4 - # of tiles */ static const int TYPE_CYLINDER = 3; - /** - * A cone. - * v1 - Cone length. - * v2 - Cone raidus. - * v3 - Number of segments. - */ - static const int TYPE_CONE = 4; + /** + * A cylinder. + * v1 - Cylinder length + * v2 - Cylinder radius + * v3 - Number of segments. + * v4 - # of tiles + */ + static const int TYPE_UNCAPPED_CYLINDER = 4; - /** - * A vertical plane. - * v1 - X size - * v2 - Y size - */ - static const int TYPE_VPLANE = 5; + /** + * A sphere. + * v1 - Sphere radius + * v2 - Lat segments + * v3 - Long segments + * v4 - # of tiles + */ + static const int TYPE_SPHERE = 5; /** * A torus. * v1 - Torus radius. * v2 - Pipe radius. * v3 - Number of ring segments. - * v4- Number of pipe segments. + * v4 - Number of pipe segments. + * v5 - # of tiles */ static const int TYPE_TORUS = 6; - + + /** + * A cone. + * v1 - Cone length. + * v2 - Cone raidus. + * v3 - Number of segments. + * v4 - # of tiles + */ + static const int TYPE_CONE = 7; /** - * A cylinder. - * v1 - Cylinder length - * v2 - Cylinder radius - * v3 - Number of segments. + * A 2D circle. + * v1 - X size + * v2 - Y size + * v3 - Number of segments + * v4 - # of tiles */ - static const int TYPE_UNCAPPED_CYLINDER = 7; - + static const int TYPE_CIRCLE = 8; + + /** + * An ico sphere. + * v1 - Sphere radius + * v2 - number of subdivisions + */ + static const int TYPE_ICOSPHERE = 9; + + /** + * An ico sphere. + * v1 - Sphere radius + * v2 - number of subdivisions + */ + static const int TYPE_OCTOSPHERE = 10; + + /** + * A 2D line circle. + * v1 - X size + * v2 - Y size + * v3 - Number of segments + * v4 - # of tiles + */ + static const int TYPE_LINE_CIRCLE = 11; + + int getPrimitiveType() const; + + Number getPrimitiveParameter1() const; + Number getPrimitiveParameter2() const; + Number getPrimitiveParameter3() const; + Number getPrimitiveParameter4() const; + Number getPrimitiveParameter5() const; + + virtual Entity *Clone(bool deepClone, bool ignoreEditorOnly) const; + virtual void applyClone(Entity *clone, bool deepClone, bool ignoreEditorOnly) const; protected: - + + int type; + Number v1; + Number v2; + Number v3; + Number v4; + Number v5; + }; } diff --git a/Core/Contents/Include/PolySceneRenderTexture.h b/Core/Contents/Include/PolySceneRenderTexture.h index 99b56dbd7..61fd6a979 100755 --- a/Core/Contents/Include/PolySceneRenderTexture.h +++ b/Core/Contents/Include/PolySceneRenderTexture.h @@ -28,6 +28,8 @@ namespace Polycode { class Scene; class Camera; class Texture; + class Renderer; + class Image; /** * Renders scenes to texture. This class automatically renders a scene to a texture every frame that you can use to texture anything else. You can set a scene to virtual (see Scene for details) to only render a scene to a texture if you need to. This class automatically adds itself to the render cycle, so you do not need to do anything manual every frame. @@ -51,7 +53,11 @@ namespace Polycode { Texture *getTargetTexture(); Texture *getFilterColorBufferTexture(); - Texture *getFilterZBufferTexture(); + Texture *getFilterZBufferTexture(); + + void Render(); + + Image *saveToImage(); void resizeRenderTexture(int newWidth, int newHeight); /** @@ -63,8 +69,12 @@ namespace Polycode { * Returns the target camera. */ Camera *getTargetCamera(); - + + bool enabled; + protected: + + Renderer *renderer; Texture *filterColorBufferTexture; Texture *filterZBufferTexture; diff --git a/Core/Contents/Include/PolySceneSound.h b/Core/Contents/Include/PolySceneSound.h index ae71eae5e..0d23b8b36 100644 --- a/Core/Contents/Include/PolySceneSound.h +++ b/Core/Contents/Include/PolySceneSound.h @@ -22,7 +22,7 @@ THE SOFTWARE. #pragma once #include "PolyGlobals.h" -#include "PolySceneEntity.h" +#include "PolyEntity.h" namespace Polycode { @@ -31,7 +31,7 @@ namespace Polycode { /** * Creates a positional 3D sound listener. There can be only one listener active at any one time. */ - class _PolyExport SceneSoundListener : public SceneEntity { + class _PolyExport SceneSoundListener : public Entity { public: SceneSoundListener(); virtual ~SceneSoundListener(); @@ -42,19 +42,29 @@ namespace Polycode { /** * Creates a positional 3D sound. */ - class _PolyExport SceneSound : public SceneEntity { + class _PolyExport SceneSound : public Entity { public: SceneSound(const String& fileName, Number referenceDistance, Number maxDistance, bool directionalSound = false); virtual ~SceneSound(); void Update(); + + virtual Entity *Clone(bool deepClone, bool ignoreEditorOnly) const; + virtual void applyClone(Entity *clone, bool deepClone, bool ignoreEditorOnly) const; + bool isDirectionalSound() const; + void setDirectionalSound(bool val); + /** * Returns the sound object associated with this positional sound. */ Sound *getSound(); + + void setLoopOnLoad(bool val); + bool getLoopOnLoad(); protected: + bool loopOnLoad; bool directionalSound; Sound *sound; }; diff --git a/Core/Contents/Include/PolySceneSprite.h b/Core/Contents/Include/PolySceneSprite.h new file mode 100644 index 000000000..1e48a2590 --- /dev/null +++ b/Core/Contents/Include/PolySceneSprite.h @@ -0,0 +1,216 @@ +/* +Copyright (C) 2013 by Ivan Safrin + +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: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +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. +*/ + +#pragma once +#include "PolyGlobals.h" +#include "PolyScenePrimitive.h" +#include "PolyCore.h" +#include "PolyResourceManager.h" +#include + +namespace Polycode { + + + class SpriteFrame { + public: + Polycode::Rectangle coordinates; + Vector2 anchorPoint; + unsigned int frameID; + }; + + class SpriteSet; + + class SpriteState { + public: + SpriteState(SpriteSet *spriteSet, String name); + + void setName(String name); + String getName() const; + + void POLYIGNORE appendFrames(std::vector newFrameIDs); + + unsigned int getNumFrameIDs(); + unsigned int getFrameIDAtIndex(unsigned int index); + + Mesh *getMeshForFrameIndex(unsigned int index); + + void insertFrame(unsigned int index, unsigned int frameID); + + void POLYIGNORE setNewFrameIDs(std::vector newIDs); + + void removeFrameByIndex(unsigned int frameIndex); + void POLYIGNORE removeFrameIndices(std::vector indices); + void clearFrames(); + + void setPixelsPerUnit(Number ppu); + Number getPixelsPerUnit(); + + void rebuildStateMeshes(); + + void setStateFPS(Number fps); + Number getStateFPS(); + + Vector3 getLargestFrameBoundingBox() const; + + void setBoundingBox(Vector2 boundingBox); + Vector2 getBoundingBox(); + + Vector2 getSpriteOffset(); + void setSpriteOffset(const Vector2 &offset); + + protected: + + Vector3 largestFrameBoundingBox; + Vector2 boundingBox; + Vector2 spriteOffset; + Number pixelsPerUnit; + Number stateFPS; + SpriteSet *spriteSet; + String name; + std::vector frameIDs; + std::vector frameMeshes; + }; + + class SpriteSet; + + class Sprite : public Resource { + public: + Sprite(String name); + ~Sprite(); + + String getName(); + void setName(String name); + + void addSpriteState(SpriteState *state); + void removeSpriteState(SpriteState *state); + + unsigned int getNumStates(); + SpriteState *getState(unsigned int index); + SpriteState *getStateByName(const String &name); + + void setParentSpritSet(SpriteSet *spriteSet); + SpriteSet *getParentSpriteSet(); + + + protected: + String name; + SpriteSet *parentSpriteSet; + std::vector states; + }; + + class SpriteSet : public ResourcePool { + public: + SpriteSet(const String &fileName, ResourcePool *parentPool = CoreServices::getInstance()->getResourceManager()->getGlobalPool()); + ~SpriteSet(); + + void setTexture(Texture *texture); + Texture *getTexture(); + Texture *loadTexture(String imageFileName); + + void addSpriteEntry(Sprite *newEntry); + unsigned int getNumSpriteEntries() const; + Sprite *getSpriteEntry(unsigned int index) const; + void removeSprite(Sprite *sprite); + + void loadSpriteSet(String fileName); + + // frame manipulation + void addSpriteFrame(const SpriteFrame &frame, bool assignID = true); + unsigned int getNumFrames() const; + SpriteFrame getSpriteFrame(unsigned int index) const; + + SpriteFrame getSpriteFrameByID(unsigned int frameID) const; + void removeFrameByID(unsigned int frameID); + + void setSpriteFrame(const SpriteFrame &frame); + + void clearFrames(); + + // automatic frame generation + void createGridFrames(unsigned int xCount, unsigned int yCount, const Vector2 &defaultAnchor); + void createFramesFromIslands(unsigned int minDistance, const Vector2 &defaultAnchor); + + Sprite *getSpriteByName(String spriteName); + + protected: + + unsigned int nextFrameIDIndex; + Texture *spriteTexture; + std::vector frames; + std::vector sprites; + }; + + class SceneSprite : public SceneMesh { + public: + SceneSprite(SpriteSet *spriteSet); + ~SceneSprite(); + + Entity *Clone(bool deepClone, bool ignoreEditorOnly) const; + void applyClone(Entity *clone, bool deepClone, bool ignoreEditorOnly) const; + + SpriteSet *getSpriteSet(); + Sprite *getCurrentSprite(); + + void handleEvent(Event *event); + + void setSpriteSet(SpriteSet *spriteSet); + void setSpriteByName(String spriteName); + + void setCurrentFrame(unsigned int frameIndex); + unsigned int getCurrentFrame(); + void Update(); + void Render(); + + Vector3 getSpriteBoundingBox() const; + + void setPaused(bool val); + bool isPaused(); + + void setSprite(Sprite *spriteEntry); + + void setSpriteState(SpriteState *spriteState, unsigned int startingFrame, bool playOnce); + + void setSpriteStateByName(String name, unsigned int startingFrame, bool playOnce); + + SpriteState *getCurrentSpriteState(); + + + bool getStartOnRandomFrame(); + void setStartOnRandomFrame(bool val); + + protected: + + Vector3 spriteBoundingBox; + bool startOnRandomFrame; + bool playOnce; + bool paused; + Core *core; + unsigned int currentFrame; + Mesh *defaultMesh; + Sprite *currentSprite; + SpriteState *currentSpriteState; + SpriteSet *spriteSet; + Number spriteTimer; + Number spriteTimerVal; + + }; +} diff --git a/Core/Contents/Include/PolyScreen.h b/Core/Contents/Include/PolyScreen.h deleted file mode 100755 index 13b286c44..000000000 --- a/Core/Contents/Include/PolyScreen.h +++ /dev/null @@ -1,154 +0,0 @@ -/* -Copyright (C) 2011 by Ivan Safrin - -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: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -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. -*/ - - -#pragma once -#include "PolyGlobals.h" -#include "PolyVector2.h" -#include "PolyEventDispatcher.h" -#include "PolyScreenEntity.h" -#include - -namespace Polycode { - - class InputEvent; - class Renderer; - class Material; - class Texture; - class ShaderBinding; - - /** - * 2D rendering base. The Screen is the container for all 2D rendering in Polycode. Screens are automatically rendered and need only be instantiated to immediately add themselves to the rendering pipeline. Each screen has a root entity. - */ - class _PolyExport Screen : public EventDispatcher { - public: - - /** - * Default constructor. - */ - Screen(); - virtual ~Screen(); - - /** - * Adds a ScreenEntity to the 2d rendering pipeline. - * @param newEntity Entity to add. - */ - void addChild(ScreenEntity *newEntity); - - /** - * Adds a ScreenEntity to the 2d rendering pipeline. - * @param newEntity Entity to add. - */ - void addEntity(ScreenEntity *newEntity); - - /** - * Removes a ScreenEntity from the screen's root entity - * @param entityToRemove Entity to remove. - */ - virtual void removeChild(ScreenEntity *entityToRemove); - - /** - * Sets the screen's offset. You can also translate the root entity to do the same thing. - * @param x New x offset. - * @param y New y offset. - */ - void setScreenOffset(Number x, Number y); - - virtual void Shutdown(); - virtual void Update(); - - void Render(); - void setRenderer(Renderer *renderer); - - /** - * Changes the screen's coordinate system. By default, screens' dimensions are in pixels. To accommodate changing resolutions without changing the dimensions of a screen's content, you can call this method to make it use normalized coordinates. - * @param newVal If true, the screen will use normalized coordinates, if false, it will use pixel coordinates. - * @param yCoordinateSize The normalized size of the screen vertically. The horizontal size will be calculated based on the resolution. - */ - void setNormalizedCoordinates(bool newVal, Number yCoordinateSize = 1.0f); - - /** - * Sets the shader material to use for post processing on this screen. - * @param shaderName Name of the shader material to use. - */ - void setScreenShader(const String& shaderName); - - /** - * Removes the current screen shader for this screen. - */ - void clearScreenShader(); - - void handleInputEvent(InputEvent *inputEvent); - - /** - * Returns true if the screen has a shader applied to it. - */ - bool hasFilterShader() const; - void drawFilter(); - - bool usesNormalizedCoordinates() const { return useNormalizedCoordinates; } - Number getYCoordinateSize() const { return yCoordinateSize; } - - /** - * If set to false, the screen will not be rendered or updated. - */ - bool enabled; - - /** - * Returns the local shader options for the camera post processing material. - */ - const std::vector& getLocalShaderOptions() const { return localShaderOptions; } - - /** - * Returns the shader material applied to the camera. - */ - Material *getScreenShaderMaterial() const { return filterShaderMaterial; } - - /** - * If set to true, will process touch events as mouse clicks. Defaults to false. - */ - bool processTouchEventsAsMouse; - - /** - * If ownsChildren is set to true, the scene will delete its children upon destruction (defaults to false). - */ - bool ownsChildren; - - - ScreenEntity rootEntity; - - protected: - - Vector2 offset; - - bool useNormalizedCoordinates; - Number yCoordinateSize; - - Renderer *renderer; - ScreenEntity *focusChild; - - Material *filterShaderMaterial; - Texture *originalSceneTexture; - std::vector localShaderOptions; - bool _hasFilterShader; - }; -} diff --git a/Core/Contents/Include/PolyScreenEntity.h b/Core/Contents/Include/PolyScreenEntity.h deleted file mode 100755 index 7f79e2e04..000000000 --- a/Core/Contents/Include/PolyScreenEntity.h +++ /dev/null @@ -1,343 +0,0 @@ -/* -Copyright (C) 2011 by Ivan Safrin - -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: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -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. -*/ - - -#pragma once -#include "PolyGlobals.h" -#include "PolyVector2.h" -#include "PolyMatrix4.h" -#include "PolyRectangle.h" -#include "PolyInputKeys.h" -#include "PolyEntity.h" -#include "PolyObject.h" -#include "PolyEventDispatcher.h" - -namespace Polycode { - - class _PolyExport MouseEventResult { - public: - bool hit; - bool blocked; - }; - -/** -* 2D Entity base. -* -* The ScreenEntity is the base class for all 2D elements in Polycode. They can be added to a screen or to other ScreenEntities and are rendered automatically. If you want to create custom screen objects, subclass this. ScreenEntity subclasses Entity, which use 3d positioning and tranformation, but provides some 2d-only versions of the transformation functions for convenience. -*/ -class _PolyExport ScreenEntity : public Entity { - - public: - using Entity::setPosition; - using Entity::setScale; - - ScreenEntity(); - virtual ~ScreenEntity(); - - virtual Entity *Clone(bool deepClone, bool ignoreEditorOnly) const; - virtual void applyClone(Entity *clone, bool deepClone, bool ignoreEditorOnly) const; - - void addEntity(Entity *newChild); - - /** - * Set 2d position. - * @param x Horizontal position. - * @param y Vertical position. - */ - void setPosition(Number x, Number y); - - /** - * Set 2d position. - * @param v New 2D position vector. - */ - void setPosition(const Vector2 &v); - - - /** - * Set 2d scale. - * @param x Horizontal scale. - * @param y Vertical scale. - */ - void setScale(Number x, Number y); - - /** - * Set 2d scale. - * @param v New 2D scale vector. - */ - void setScale(const Vector2 &v); - - - /** - * Set 2d rotation. - * @param rotation New rotation value in degrees. - */ - void setRotation(Number rotation); - - /** - * Returns current rotation. - * @return Current rotation value. - */ - Number getRotation() const; - - MouseEventResult _onMouseDown(Number x, Number y, int mouseButton, int timestamp); - MouseEventResult _onMouseUp(Number x, Number y, int mouseButton, int timestamp); - MouseEventResult _onMouseMove(Number x, Number y, int timestamp); - MouseEventResult _onMouseWheelUp(Number x, Number y, int timestamp); - MouseEventResult _onMouseWheelDown(Number x, Number y, int timestamp); - - virtual void onMouseDown(Number x, Number y){} - virtual void onMouseUp(Number x, Number y){} - virtual void onMouseMove(Number x, Number y){} - virtual void onMouseWheelUp(Number x, Number y) {} - virtual void onMouseWheelDown(Number x, Number y) {} - - void _onKeyDown(PolyKEY key, wchar_t charCode); - void _onKeyUp(PolyKEY key, wchar_t charCode); - - Matrix4 getScreenConcatenatedMatrix() const; - - virtual void onKeyDown(PolyKEY key, wchar_t charCode){} - virtual void onKeyUp(PolyKEY key, wchar_t charCode){} - - bool hitTest(Number x, Number y) const; - bool hitTest(Vector2 v) const; - - Matrix4 buildPositionMatrix(); - void adjustMatrixForChildren(); - - /** - * Returns the width of the screen entity. - * @return Height of the screen entity. - */ - Number getWidth() const; - - /** - * Returns the height of the screen entity. - */ - Number getHeight() const; - - /** - * Sets the width of the screen entity. - * @param w New height value. - */ - void setWidth(Number w) { width = w; hit.w = w; hit.x = -w/2; } - - /** - * Sets the height of the screen entity. - * @param h New height value. - */ - void setHeight(Number h) { height = h; hit.h = h; hit.y = -h/2; } - - virtual void onGainFocus(){} - virtual void onLoseFocus(){} - - void startDrag(Number xOffset, Number yOffset); - void stopDrag(); - - void setBlendingMode(int newBlendingMode); - - /** - * Changes the positioning mode of the screen entity. - - If the positioning mode is ScreenEntity::POSITION_TOPLEFT, the entity is translated by half its width and half its height when it's rendered, making all other transformations relative to its top-left corner instead of the center. - If the mode is ScreenEntity::POSITION_CENTER, the entity is rendered as is. - Set to POSITION_CENTER by default. - @param newPositionMode The new positioning mode. - */ - void setPositionMode(int newPositionMode); - - /** - * Get the position mode. - * @see setPositionMode() - */ - int getPositionMode() const; - - /** - * Set a rectangle to which dragging will be restricted. - * - * Dragging the item past the given rectangle will snap it - * back to the edge of the rectangle. - * - * @param rect The rectangle to restrict dragging to. Uses the - * same positioning mode as `this->position`. - */ - void setDragLimits(Rectangle rect); - - /** - * Disable restricting where `this` can be dragged. - * @see setDragLimits() - */ - void clearDragLimits(); - - /** - * Set options for `this` and all children recursively. - * - * @param snapToPixels Whether to snap entity positions to pixels before rendering. - */ - void setDefaultScreenOptions(bool snapToPixels); - - /* - * Make `child` take focus, which will cause certain events - * to be sent to it. - * - * Note that this is a global setting. Only one Entity per - * running instance can take focus. - */ - void focusChild(ScreenEntity *child); - - /* - * Make the next child in `this->children`, after the currently - * focused child entity take focus. - * - * Does nothing if no child of `this` has focus. - */ - void focusNextChild(); - - /** - * Same semantics as getPosition(), but returns only the x and y coordinates. - * - * @see getPosition() - * @see getScreenPosition() - */ - Vector2 getPosition2D() const; - - /** - * Get the position of ScreenEntity in relation to the top-left - * corner of the Polycode screen it's on. - * - * This is unlike getPosition(), which will return the position - * of an Entity in relation to its parent Entity. - * - * @see getPosition() - */ - Vector2 getScreenPosition() const; - - /** - * Same semantics as getScale(), but returns only the x and y scale. - * - * @see getScale() - */ - Vector2 getScale2D() const; - - /** - * Positioning mode in which you specify an entity's topleft corner - * as coordinate for placement. - */ - static const int POSITION_TOPLEFT = 0; - - /** - * Positioning mode in which you specify an entity's center as - * coordinate for placement. - */ - static const int POSITION_CENTER = 1; - - bool isFocusable() const; - - bool hasFocus; - - /** - * Does the same as getEntityByID, but casts to ScreenEntity. - * - * Note: Make sure only entities of type ScreenEntity have the tag you're - * querying, or otherwise you will be treating an Entity as ScreenEntity. - */ - ScreenEntity *getScreenEntityById(String id, bool recursive) const; - - /** - * Does the same as getEntitiesByID, but casts each result to ScreenEntity. - * - * Note: Make sure only entities of type ScreenEntity have the tag you're - * querying, or otherwise you will be treating an Entity as ScreenEntity. - */ - std::vector getScreenEntitiesByTag(String tag, bool recursive) const; - - /** - * If set to true, will block mouse events for underlaying entities. - * (NOTE: processInputEvents must be set to true) - */ - bool blockMouseInput; - - /** - * If this option is true, the screen entity's positions will be roudnded to whole pixels. This only works if the screen is using pixel coordinates. - */ - bool snapToPixels; - bool processInputEvents; - - /** - * Get the hitbox of this ScreenEntity. - * @see setHitBox() - */ - Rectangle getHitbox() const; - - - void setHitbox(Number width, Number height); - - /** - * Set the hitbox of this ScreenEntity. - * - * The hitbox determines the "physical" dimensions of the entity. - * - * For instance, when it is checked whether you clicked an entity, - * it is checked whether the mouse was pointing inside the hitbox. - * - * The hitbox is measured from the entity's center, so a hitbox covering - * a square would be `(-square.w/2, -square.h/2, square.w, square.h)` - * - * @param left The left of the hitbox, as measured from the entity's center. - * @param top The top of the hitbox, as measured from the entity's center. - * @param width The width of the hitbox. - * @param height The height of the hitbox. - */ - void setHitbox(Number width, Number height, Number left, Number top); - - Number width; - Number height; - - /** - * Get the drag status (true if currently being dragged) of the entity. - */ - bool isDragged(); - - protected: - - bool focusable; - bool focusChildren; - - bool dragged; - Number dragOffsetX; - Number dragOffsetY; - - bool mouseOver; - - Rectangle hit; - - Number xmouse; - Number ymouse; - - int positionMode; - Rectangle *dragLimits; - - int lastClickTicks; - -}; - -} diff --git a/Core/Contents/Include/PolyScreenEntityInstance.h b/Core/Contents/Include/PolyScreenEntityInstance.h deleted file mode 100755 index a22429a19..000000000 --- a/Core/Contents/Include/PolyScreenEntityInstance.h +++ /dev/null @@ -1,91 +0,0 @@ -/* -Copyright (C) 2011 by Ivan Safrin - -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: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -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. -*/ - - -#pragma once -#include "PolyGlobals.h" -#include "PolyScreenEntity.h" -#include "PolyObject.h" -#include "PolyScreenShape.h" -#include "PolyScreenImage.h" -#include "PolyScreenLabel.h" -#include "PolyScreenSound.h" -#include "PolyScreenSprite.h" -#include "PolyParticleEmitter.h" -#include "PolyParticle.h" -#include "PolyResource.h" -#include "PolySound.h" - -namespace Polycode { - -class ScreenEntityInstanceResourceEntry; - -class ScreenEntityInstance : public ScreenEntity { - public: - ScreenEntityInstance(const String& fileName); - ScreenEntityInstance(); - - static ScreenEntityInstance *BlankScreenEntityInstance(); - - virtual ~ScreenEntityInstance(); - - virtual Entity *Clone(bool deepClone, bool ignoreEditorOnly) const; - virtual void applyClone(Entity *clone, bool deepClone, bool ignoreEditorOnly) const; - - void reloadEntityInstance(); - - void clearInstance(); - - void parseObjectIntoCurve(ObjectEntry *entry, BezierCurve *curve); - void applyScreenShape(ObjectEntry *entry, ScreenShape *shape); - ScreenEntity *loadObjectEntryIntoEntity(ObjectEntry *entry, ScreenEntity *targetEntity = NULL); - bool loadFromFile(const String& fileName); - - - ScreenEntityInstanceResourceEntry *getResourceEntry(); - - String getFileName() const; - - bool cloneUsingReload; - - String fileName; - - protected: - - ScreenEntityInstanceResourceEntry *resourceEntry; - -}; - -class ScreenEntityInstanceResourceEntry : public Resource { - public: - ScreenEntityInstanceResourceEntry(ScreenEntityInstance *instance); - virtual ~ScreenEntityInstanceResourceEntry(); - - ScreenEntityInstance *getInstance(); - void reloadResource(); - - protected: - ScreenEntityInstance* instance; -}; - - -} diff --git a/Core/Contents/Include/PolyScreenEvent.h b/Core/Contents/Include/PolyScreenEvent.h deleted file mode 100755 index 0f1185a19..000000000 --- a/Core/Contents/Include/PolyScreenEvent.h +++ /dev/null @@ -1,44 +0,0 @@ -/* -Copyright (C) 2011 by Ivan Safrin - -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: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -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. -*/ - - -#pragma once -#include "PolyString.h" -#include "PolyGlobals.h" -#include "PolyEvent.h" - -namespace Polycode { - - class _PolyExport ScreenEvent : public Event { - public: - ScreenEvent(); - virtual ~ScreenEvent(); - - static const int ENTITY_MOVE_TOP = 0; - static const int ENTITY_MOVE_BOTTOM = 1; - static const int ENTITY_MOVE_UP = 2; - static const int ENTITY_MOVE_DOWN = 3; - - protected: - - }; -} \ No newline at end of file diff --git a/Core/Contents/Include/PolyScreenImage.h b/Core/Contents/Include/PolyScreenImage.h deleted file mode 100755 index 527de283c..000000000 --- a/Core/Contents/Include/PolyScreenImage.h +++ /dev/null @@ -1,95 +0,0 @@ -/* -Copyright (C) 2011 by Ivan Safrin - -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: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -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. -*/ - -#pragma once -#include "PolyGlobals.h" -#include "PolyScreenShape.h" - -namespace Polycode { - - /** - * 2D screen image display. This ScreenEntity can load and display and image. - */ - class _PolyExport ScreenImage : public ScreenShape { - public: - /** - * Create screen image from file. - * @param fileName - */ - ScreenImage(const String& fileName); - - /** - * Create screen image from Image. - * @param image Image to create from. - */ - ScreenImage(Image *image); - - /** - * Create screen image from Texture. - * @param texture Texture to create from. - */ - ScreenImage(Texture *texture); - - /** - * Create screen image from Image. - * @param image Image to create from. - */ - static ScreenImage* ScreenImageWithImage(Image *image); - - /** - * Create screen image from Texture. - * @param texture Texture to create from. - */ - static ScreenImage* ScreenImageWithTexture(Texture *texture); - - virtual ~ScreenImage(); - - virtual Entity *Clone(bool deepClone, bool ignoreEditorOnly) const; - virtual void applyClone(Entity *clone, bool deepClone, bool ignoreEditorOnly) const; - - /** - * Changes which part of the image is displayed. - * @param x X position of the display rectangle. - * @param y Y position of the display rectangle. - * @param width Width of the display rectangle. - * @param height Height of the display rectangle. - */ - void setImageCoordinates(Number x, Number y, Number width, Number height); - - /** - * Returns the image width. - */ - Number getImageWidth() const; - - /** - * Returns the image height. - */ - Number getImageHeight() const; - - protected: - - Number imageWidth; - Number imageHeight; - - }; - -} diff --git a/Core/Contents/Include/PolyScreenLabel.h b/Core/Contents/Include/PolyScreenLabel.h deleted file mode 100755 index 6c5067ac6..000000000 --- a/Core/Contents/Include/PolyScreenLabel.h +++ /dev/null @@ -1,73 +0,0 @@ -/* -Copyright (C) 2011 by Ivan Safrin - -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: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -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. -*/ - -#pragma once -#include "PolyGlobals.h" -#include "PolyScreenShape.h" - -namespace Polycode { - - class Label; - class ScreenImage; - - /** - * 2D screen label display. Displays 2d text in a specified font. - */ - class _PolyExport ScreenLabel : public ScreenShape { - public: - - /** - * Constructor. - * @param fontName Name of a registered font to use. @see FontManager for info on how to register fonts. - * @param text Text to display. - * @param size Size in pixels. - * @param Anti-aliasing mode. - */ - ScreenLabel(const String& text, int size, const String& fontName = "sans", int amode = 0, bool premultiplyAlpha = false); - virtual ~ScreenLabel(); - - virtual Entity *Clone(bool deepClone, bool ignoreEditorOnly) const; - virtual void applyClone(Entity *clone, bool deepClone, bool ignoreEditorOnly) const; - - /** - * Sets a new text to the screen label. - * @param newText Text to set. - */ - void setText(const String& newText); - - /** - * Returns the label's text as a string. - * @return The label's text. - */ - const String& getText() const; - - Label *getLabel() const; - - void Render(); - bool positionAtBaseline; - - protected: - - void updateTexture(); - Label *label; - }; -} diff --git a/Core/Contents/Include/PolyScreenLine.h b/Core/Contents/Include/PolyScreenLine.h deleted file mode 100755 index 4ba73ef55..000000000 --- a/Core/Contents/Include/PolyScreenLine.h +++ /dev/null @@ -1,83 +0,0 @@ -/* -Copyright (C) 2011 by Ivan Safrin - -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: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -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. -*/ - -#pragma once -#include "PolyGlobals.h" -#include "PolyScreenMesh.h" - -namespace Polycode { - - class Vertex; - - /** - * A 2D line between two points or two ScreenEntity instances. - */ - class _PolyExport ScreenLine : public ScreenMesh { - public: - /** - * Create a line between two points. - * @start Starting point. - * @end Enfing point. - */ - ScreenLine(Vector2 start, Vector2 end); - - /** - * Create a line between two entities. It's automatically updated every frame to follow the entities. - * @target1 Starting target. - * @target2 Ending target. - */ - ScreenLine(ScreenEntity* target1, ScreenEntity* target2); - virtual ~ScreenLine(); - - void setStart(Vector2 point); - void setEnd(Vector2 point); - - /** - * Create a line between two entities. It's automatically updated every frame to follow the entities. - * @target1 Starting target. - * @target2 Ending target. - */ - static ScreenLine *ScreenLineBetweenEntities(ScreenEntity* target1, ScreenEntity* target2); - - void Update(); - void Render(); - - /** - * Sets the line width. - * @param width New line width. - */ - void setLineWidth(Number width); - - protected: - - void initMesh(); - - Number lineWidth; - - Vertex *startVertex; - Vertex *endVertex; - - ScreenEntity *target1; - ScreenEntity *target2; - - }; -} diff --git a/Core/Contents/Include/PolyScreenManager.h b/Core/Contents/Include/PolyScreenManager.h deleted file mode 100755 index 1646ee65b..000000000 --- a/Core/Contents/Include/PolyScreenManager.h +++ /dev/null @@ -1,63 +0,0 @@ -/* -Copyright (C) 2011 by Ivan Safrin - -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: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -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. -*/ - -#pragma once -#include "PolyGlobals.h" -#include "PolyEventDispatcher.h" -#include - -namespace Polycode { - - class Screen; - - /** - * 2D Screen manager. Must be accessed via CoreServices. Screens are automatically added to the manager when they are created, so there is no need to manually add them. - */ - class _PolyExport ScreenManager : public EventDispatcher { - public: - ScreenManager(); - ~ScreenManager(); - - /** - * Removes a screen from the manager, taking it out of the render loop. - * @param screen Screen to remove. - */ - void removeScreen(Screen *screen); - void addScreen(Screen* screen); - void Update(); - void Render(); - - void handleEvent(Event *event); - - - /** - * If set to true, will draw Screens before Scenes (defaults to false). - */ - bool drawScreensFirst; - - private: - - std::vector screens; - - }; - -} diff --git a/Core/Contents/Include/PolyScreenMesh.h b/Core/Contents/Include/PolyScreenMesh.h deleted file mode 100755 index 3225948fe..000000000 --- a/Core/Contents/Include/PolyScreenMesh.h +++ /dev/null @@ -1,148 +0,0 @@ -/* -Copyright (C) 2011 by Ivan Safrin - -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: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -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. -*/ - -#pragma once -#include "PolyGlobals.h" -#include "PolyScreenEntity.h" -#include "PolyMesh.h" -#include "PolyMaterial.h" - -namespace Polycode { - - class Image; - class Texture; - - /** - * 2D Mesh. ScreenMesh is the base for most geometry-based screen entities. It's based aroudn a Mesh instance, like its 3D counterpart (SceneMesh), but currently has fewer options. - * @see Mesh - */ - class _PolyExport ScreenMesh : public ScreenEntity { - public: - - /** - * Creates the screen mesh and loads a mesh from a file name. - */ - ScreenMesh(const String& fileName); - - /** - * Creates the screen mesh from existing Mesh. - */ - ScreenMesh(Mesh *mesh); - - /** - * Create an empty screen mesh of specified type. See Mesh for available mesh types. - */ - ScreenMesh(int meshType); - - /** - * Create an empty screen mesh of specified type. See Mesh for available mesh types. - */ - static ScreenMesh *ScreenMeshWithType(int meshType); - - /** - * Creates the screen mesh from existing Mesh. - */ - static ScreenMesh *ScreenMeshWithMesh(Mesh *mesh); - - virtual ~ScreenMesh(); - - void Render(); - - /** - * Returns the mesh for this screen mesh. - * @return The mesh. - */ - Mesh *getMesh() const; - - /** - * Returns the texture associated with the mesh. - */ - Texture *getTexture() const; - - /** - * Loads a texture from an image file. - * @param fileName Path to the image file. - */ - void loadTexture(const String& fileName); - - /** - * Loads a texture from an image instance. - * @param image Image instance. - */ - void loadTexture(Image *image); - - /** - * Applies a texture - * @param texture to apply. - */ - void setTexture(Texture *texture); - - /** - * Set material from existing Material instance. - * @param material Material to apply. - */ - void setMaterial(Material *material); - - /** - * Set material by name. You can create materials in material files and name them there, then use this to set a material by name to a scene mesh. - * @param materialName Name of material to apply. - */ - void setMaterialByName(const String& materialName); - - /** - * Clears the currently applied material - */ - void clearMaterial(); - - /** - * Returns the material applied. - */ - Material *getMaterial(); - - - ShaderBinding *getLocalShaderOptions(); - - /** - * If this is set to true, the lines in wireframe meshes will be anti-aliased if the support is available in the renderer. - */ - bool lineSmooth; - - Number lineWidth; - - /** - * If true, will delete its Mesh upon destruction. (defaults to true) - */ - bool ownsMesh; - - /** - * Updates hit.width, hit.height to coordinates of mesh. - */ - void updateHitBox(); - - protected: - - Material *material; - ShaderBinding *localShaderOptions; - Mesh *mesh; - Texture *texture; - }; -} diff --git a/Core/Contents/Include/PolyScreenShape.h b/Core/Contents/Include/PolyScreenShape.h deleted file mode 100755 index d45f34fd0..000000000 --- a/Core/Contents/Include/PolyScreenShape.h +++ /dev/null @@ -1,132 +0,0 @@ -/* -Copyright (C) 2011 by Ivan Safrin - -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: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -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. -*/ - -#pragma once -#include "PolyGlobals.h" -#include "PolyScreenMesh.h" - -namespace Polycode { - - class Polygon; - - /** - * 2D primitive. Screen shape can create 2d shapes (Currently only rectangles and circles). - */ - class _PolyExport ScreenShape : public ScreenMesh { - public: - - /** - * Create a new shape of specified type and size/options. - * @param shapeType Type of shape to create. Currently the only options are ScreenShape::SHAPE_RECT and ScreenShape::SHAPE_CIRCLE. - * @param option1 Width option. - * @param option2 Height option. - * @param option3 Number of vertices for the the circle (defaults to 360). Unused for rectangle. - * @param option4 Reserved. - */ - ScreenShape(int shapeType, Number option1=0, Number option2=0, Number option3=0, Number option4=0); - - virtual Entity *Clone(bool deepClone, bool ignoreEditorOnly) const; - virtual void applyClone(Entity *clone, bool deepClone, bool ignoreEditorOnly) const; - - virtual ~ScreenShape(); - void Render(); - - /** - * Sets the color of the shape stroke if it's enabled. - * @param r Red value 0-1. - * @param g Green value 0-1 - * @param b Blue value 0-1 - * @param a Alpha value 0-1 - * @see strokeEnabled - */ - void setStrokeColor(Number r, Number g, Number b, Number a); - - /** - * Sets the width of the shape stroke if it's enabled. - * @param width New stroke width. - * @see strokeEnabled - */ - void setStrokeWidth(Number width); - - /** - * Colors the shape with a gradient. Radial for circles, linear for rectangles. - * @param width New stroke width. - * @param r1 Red value of the first gradient color 0-1. - * @param g1 Green value of the first gradient color 0-1 - * @param b1 Blue value of the first gradient color 0-1 - * @param a1 Alpha value of the first gradient color 0-1 - * @param r2 Red value of the second gradient color 0-1. - * @param g2 Green value of the second gradient color 0-1 - * @param b2 Blue value of the second gradient color 0-1 - * @param a2 Alpha value of the second gradient color 0-1 - * @see strokeEnabled - */ - void setGradient(Number r1, Number g1, Number b1, Number a1, Number r2, Number g2, Number b2, Number a2); - - /** - * Removes the gradient from the shape. - */ - void clearGradient(); - - int getShapeType() const; - - void setShapeType(unsigned int type); - - void setShapeSize(Number newWidth, Number newHeight); - - void buildShapeMesh(); - - /** - * Assignment operator - */ - void operator=(const ScreenShape& copy); - - static const int SHAPE_RECT = 1; - static const int SHAPE_CIRCLE = 2; - static const int SHAPE_CUSTOM = 4; - - /** - * If set to true, the shape will be drawn over with a stroke. - */ - bool strokeEnabled; - - /** - * Color of the shape stroke. - */ - Color strokeColor; - - /** - * Width of the shape stroke. - */ - Number strokeWidth; - - protected: - - Number option1; - Number option2; - Number option3; - Number option4; - - int shapeType; - - }; -} diff --git a/Core/Contents/Include/PolyScreenSound.h b/Core/Contents/Include/PolyScreenSound.h deleted file mode 100644 index 4674ca72e..000000000 --- a/Core/Contents/Include/PolyScreenSound.h +++ /dev/null @@ -1,65 +0,0 @@ -/* -Copyright (C) 2011 by Ivan Safrin - -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: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -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. -*/ - -#pragma once -#include "PolyGlobals.h" -#include "PolyScreenEntity.h" - -namespace Polycode { - - class Sound; - - /** - * Creates a positional 2D sound listener. There can be only one listener active at any one time. - */ - class _PolyExport ScreenSoundListener : public ScreenEntity { - public: - ScreenSoundListener(); - virtual ~ScreenSoundListener(); - void Update(); - }; - - - /** - * Creates a positional 2D sound. - */ - class _PolyExport ScreenSound : public ScreenEntity { - public: - ScreenSound(const String& fileName, Number referenceDistance, Number maxDistance); - virtual ~ScreenSound(); - - virtual Entity *Clone(bool deepClone, bool ignoreEditorOnly) const; - virtual void applyClone(Entity *clone, bool deepClone, bool ignoreEditorOnly) const; - - void Update(); - - /** - * Returns the sound object associated with this positional sound. - */ - Sound *getSound() const; - - protected: - - Sound *sound; - }; - -} diff --git a/Core/Contents/Include/PolyScreenSprite.h b/Core/Contents/Include/PolyScreenSprite.h deleted file mode 100644 index 97c584111..000000000 --- a/Core/Contents/Include/PolyScreenSprite.h +++ /dev/null @@ -1,169 +0,0 @@ -/* -Copyright (C) 2011 by Ivan Safrin - -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: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -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. -*/ - -#pragma once -#include "PolyGlobals.h" -#include "PolyScreenShape.h" -#include - -namespace Polycode { - -class ScreenSpriteResourceEntry; - -class _PolyExport SpriteAnimation { - public: - Number speed; - String name; - String frames; - int numFrames; - - int numFramesX; - int numFramesY; - Number spriteUVWidth; - Number spriteUVHeight; - - void setOffsetsFromFrameString(const String& frames); - - std::vector framesOffsets; -}; - -/** -* Animated 2D image sprite. This screen entity can load spritesheet images and play back animations. -*/ -class _PolyExport ScreenSprite : public ScreenShape -{ - public: - - /** - * Create a sprite from a sprite file format - * @param fileName Sprite file to load - */ - ScreenSprite(const String& fileName); - - /** - * Create a sprite from a spritesheet image of specified size. - * @param fileName Image file to load spritesheet from. - * @param spriteWidth Pixel width of each sprite cell. - * @param spriteWidth Pixel height of each sprite cell. - */ - ScreenSprite(const String& fileName, Number spriteWidth, Number spriteHeight); - - /** - * Create a sprite from a spritesheet image of specified size. - * @param fileName Image file to load spritesheet from. - * @param spriteWidth Pixel width of each sprite cell. - * @param spriteWidth Pixel height of each sprite cell. - */ - static ScreenSprite* ScreenSpriteFromImageFile(const String& fileName, Number spriteWidth, Number spriteHeight); - - virtual ~ScreenSprite(); - - virtual Entity *Clone(bool deepClone, bool ignoreEditorOnly) const; - virtual void applyClone(Entity *clone, bool deepClone, bool ignoreEditorOnly) const; - - /** - * Adds a new animation to the sprite. Animations are added by specifying a list of frame indexes and then can be played back by the specified name. - * @param name Name of the new animation. - * @param frames A comma separated list of frames indexes to include in the animation. - * @speed Speed at which to play back the animation. - * @return Returns newly added animation - */ - SpriteAnimation *addAnimation(const String& name, const String& frames, Number speed); - - /** - * Shows a specific frame of the current animation. - * @param frameIndex Frame index of the frame to show. - */ - void showFrame(unsigned int frameIndex); - - /** - * Play back a previously created animation by name. - * @param name Name of the animation to play. - * @param startFrame Starting frame for playback. - * @param once If true, only plays once, otherwise loops. - */ - void playAnimation(const String& name, int startFrame, bool once); - void Update(); - - void setSpriteSize(const Number spriteWidth, const Number spriteHeight); - - Vector2 getSpriteSize(); - - String getFileName() const; - - void recalculateSpriteDimensions(); - - bool loadFromFile(const String& fileName); - - void reloadSprite(); - - /** - * Pauses or unpauses the current sprite animation. - * @param val If true, pauses the current animation, if false, resumes playing it. - */ - void Pause(bool val); - - unsigned int getNumAnimations(); - SpriteAnimation *getAnimationAtIndex(unsigned int index); - - SpriteAnimation *getCurrentAnimation(); - unsigned int getCurrentAnimationFrame(); - bool isCurrentAnimationFinished(); - - void updateSprite(); - - ScreenSpriteResourceEntry *getResourceEntry(); - - protected: - - String fileName; - - bool paused; - - Number spriteWidth; - Number spriteHeight; - - bool playingOnce; - Number lastTick; - - ScreenSpriteResourceEntry *resourceEntry; - - Number spriteUVWidth; - Number spriteUVHeight; - int currentFrame; - SpriteAnimation *currentAnimation; - - std::vector animations; -}; - -class ScreenSpriteResourceEntry : public Resource { - public: - ScreenSpriteResourceEntry(ScreenSprite *sprite); - virtual ~ScreenSpriteResourceEntry(); - ScreenSprite *getSprite(); - void reloadResource(); - - protected: - ScreenSprite* sprite; -}; - -} diff --git a/Core/Contents/Include/PolyServer.h b/Core/Contents/Include/PolyServer.h index 8407319af..49bc4f61b 100755 --- a/Core/Contents/Include/PolyServer.h +++ b/Core/Contents/Include/PolyServer.h @@ -54,8 +54,9 @@ namespace Polycode { ServerClientEvent *handlePacket(Packet *packet); - unsigned int clientID; + unsigned int clientID; PeerConnection *connection; + bool clientReady; }; class _PolyExport ServerEvent : public Event { diff --git a/Core/Contents/Include/PolyShader.h b/Core/Contents/Include/PolyShader.h index ece5da3a4..587b3519f 100755 --- a/Core/Contents/Include/PolyShader.h +++ b/Core/Contents/Include/PolyShader.h @@ -26,6 +26,7 @@ THE SOFTWARE. #include "PolyColor.h" #include "PolyVector2.h" #include "PolyVector3.h" +#include "PolyMatrix4.h" #include "PolyResource.h" #include @@ -49,11 +50,21 @@ namespace Polycode { static const int PARAM_VECTOR3 = 3; static const int PARAM_COLOR = 4; static const int PARAM_MATRIX = 5; - }; + }; + + typedef struct { + Texture *texture; + String name; + } TextureBinding; + + typedef struct { + Cubemap *cubemap; + String name; + } CubemapBinding; class _PolyExport ShaderProgram : public Resource { public: - ShaderProgram(int type); + explicit ShaderProgram(int type); virtual ~ShaderProgram(); virtual void reloadProgram() {} @@ -69,15 +80,15 @@ namespace Polycode { class _PolyExport Shader : public Resource { public: - Shader(int type); + explicit Shader(int type); virtual ~Shader(); int getType() const; void setName(const String& name); const String& getName() const; - virtual ShaderBinding *createBinding() = 0; - virtual void reload() {} + ShaderBinding *createBinding(); + virtual void reload() {} int getExpectedParamType(String name); @@ -88,7 +99,7 @@ namespace Polycode { static const int MODULE_SHADER = 1; int numSpotLights; - int numAreaLights; + int numPointLights; std::vector expectedTextures; std::vector expectedCubemaps; @@ -124,19 +135,31 @@ namespace Polycode { }; class LocalShaderParam : public PolyBase { - public: + public: + + LocalShaderParam(); + ~LocalShaderParam(); + LocalShaderParam *Copy(); + String name; void *data; + int type; + bool ownsPointer; + unsigned int arraySize; - // Convenience getters/setters for Lua users - Number getNumber() { return *((Number *)data); } - Vector2 getVector2() { return *((Vector2 *)data); } - Vector3 getVector3() { return *((Vector3 *)data); } - Color getColor() { return *((Color *)data); } - void setNumber(Number x) { memcpy(data, &x, sizeof(x)); } - void setVector2(Vector2 x) { memcpy(data, &x, sizeof(x)); } - void setVector3(Vector3 x) { memcpy(data, &x, sizeof(x)); } - void setColor(Color x) { static_cast(data)->setColor(&x); } + // Convenience getters/setters for Lua users + Number getNumber(); + Vector2 getVector2(); + Vector3 getVector3(); + Matrix4 getMatrix4(); + Color getColor(); + void setNumber(Number x); + void setVector2(Vector2 x); + void setVector3(Vector3 x); + void setMatrix4(Matrix4 x); + void setColor(Color x); + + void setParamValueFromString(int type, String pvalue); }; class RenderTargetBinding : public PolyBase { @@ -155,15 +178,18 @@ namespace Polycode { public: ShaderBinding(Shader *shader); virtual ~ShaderBinding(); - - virtual Texture *getTexture(const String& name){ return NULL;}; - virtual Cubemap *getCubemap(const String& name){ return NULL;}; - virtual void clearTexture(const String& name){}; - virtual void clearCubemap(const String& name){}; - virtual void addTexture(const String& name, Texture *texture) {}; + + void copyTo(ShaderBinding *targetBinding); + + Texture *getTexture(const String& name); + Cubemap *getCubemap(const String& name); + void clearTexture(const String& name); + void clearCubemap(const String& name); + void addTexture(const String& name, Texture *texture); + void addCubemap(const String& name, Cubemap *cubemap); + LocalShaderParam *addParam(int type, const String& name); - virtual void addCubemap(const String& name, Cubemap *cubemap) {}; - + LocalShaderParam *addParamPointer(int type, const String& name, void *ptr); unsigned int getNumLocalParams(); LocalShaderParam *getLocalParam(unsigned int index); LocalShaderParam *getLocalParamByName(const String& name); @@ -185,8 +211,9 @@ namespace Polycode { unsigned int getNumOutTargetBindings(); RenderTargetBinding *getOutTargetBinding(unsigned int index); - - LocalShaderParam *addLocalParam(const String& name, void *ptr); + + std::vector textures; + std::vector cubemaps; Shader* shader; std::vector localParams; diff --git a/Core/Contents/Include/PolySkeleton.h b/Core/Contents/Include/PolySkeleton.h index 640ae256c..ece65507f 100755 --- a/Core/Contents/Include/PolySkeleton.h +++ b/Core/Contents/Include/PolySkeleton.h @@ -27,7 +27,8 @@ THE SOFTWARE. #include "PolyColor.h" #include "PolyVector3.h" #include "PolyQuaternion.h" -#include "PolySceneEntity.h" +#include "PolyQuaternionCurve.h" +#include "PolyEntity.h" #include namespace Polycode { @@ -41,9 +42,11 @@ namespace Polycode { public: BoneTrack(Bone *bone, Number length); ~BoneTrack(); + void Play(bool once=false); void Stop(); - void Update(); + void Update(Number elapsed); + void Reset(); void setSpeed(Number speed); @@ -58,32 +61,21 @@ namespace Polycode { BezierCurve *LocY; BezierCurve *LocZ; - Vector3 LocXVec; - Vector3 LocYVec; - Vector3 LocZVec; - - Vector3 ScaleXVec; - Vector3 ScaleYVec; - Vector3 ScaleZVec; - - + Vector3 position; + Vector3 scale; Quaternion boneQuat; - QuaternionTween *quatTween; - - Vector3 QuatWVec; - Vector3 QuatXVec; - Vector3 QuatYVec; - Vector3 QuatZVec; - + QuaternionCurve *quatCurve; + + Number weight; protected: Number length; - - bool initialized; - + Number speed; + bool paused; + Number time; Bone *targetBone; - std::vector pathTweens; + bool playOnce; }; @@ -115,6 +107,9 @@ namespace Polycode { * Stops the animation. */ void Stop(); + + void Reset(); + void Update(); /** @@ -123,8 +118,15 @@ namespace Polycode { */ void setSpeed(Number speed); + void setWeight(Number newWeight); + Number getWeight() const; + + bool isPlaying() const; + protected: + Number weight; + bool playing; String name; Number duration; std::vector boneTracks; @@ -133,7 +135,7 @@ namespace Polycode { /** * 3D skeleton. Skeletons are applied to scene meshes and can be animated with loaded animations. */ - class _PolyExport Skeleton : public SceneEntity { + class _PolyExport Skeleton : public Entity { public: /** @@ -165,10 +167,18 @@ namespace Polycode { * @param animName Name of animation to play. * @param once If true, will only play the animation once. */ - void playAnimation(const String& animName, bool once = false); - - void playAnimationByIndex(int index, bool once = false); + void playAnimationByName(const String& animName, Number weight = 1.0, bool once = false, bool restartIfPlaying = false); + + void playAnimation(SkeletonAnimation *animation, Number weight = 1.0, bool once = false, bool restartIfPlaying = false); + + void setBaseAnimationByName(const String &animName); + void setBaseAnimation(SkeletonAnimation *animation); + + void stopAllAnimations(); + + SkeletonAnimation *getBaseAnimation(); + /** * Loads in a new animation from a file and adds it to the skeleton. * @param name Name of the new animation. @@ -181,6 +191,11 @@ namespace Polycode { * @param Name of animation to return. */ SkeletonAnimation *getAnimation(const String& name) const; + + + void stopAnimationByName(const String &name); + void stopAnimation(SkeletonAnimation *animation); + void Update(); /** @@ -194,16 +209,7 @@ namespace Polycode { * @param val If true, bones will be rendered, if false, they will not. */ void bonesVisible(bool val); - - /** - * Enables labels with bone names to be rendered. See SceneLabel for details on the parameters. - * @param labelFont Font to use - * @param size Size of font. - * @param scale Scale of font. - * @param labelColor Color of the label. - */ - void enableBoneLabels(const String& labelFont, Number size, Number scale, Color labelColor); - + /** * Returns the number of bones in the skeleton */ @@ -213,18 +219,19 @@ namespace Polycode { * Returns a bone at the specified index. * @param index Bone index. */ - Bone *getBone(int index) const; - - /** - * Returns the current animation. - */ - SkeletonAnimation *getCurrentAnimation() const { return currentAnimation; } + Bone *getBone(unsigned int index) const; + void addBone(Bone *bone); + void removeBone(Bone *bone); + + unsigned int getBoneIndexByBone(Bone *bone); + protected: - SceneEntity *bonesEntity; + Entity *bonesEntity; - SkeletonAnimation *currentAnimation; + SkeletonAnimation *baseAnimation; + std::vector playingAnimations; std::vector bones; std::vector animations; }; diff --git a/Core/Contents/Include/PolySound.h b/Core/Contents/Include/PolySound.h index f151745be..a2ab3fa95 100755 --- a/Core/Contents/Include/PolySound.h +++ b/Core/Contents/Include/PolySound.h @@ -24,6 +24,7 @@ #include "PolyGlobals.h" #include "PolyVector3.h" #include "PolyString.h" +#include "PolyCoreServices.h" #if defined(__APPLE__) && defined(__MACH__) #include @@ -42,11 +43,31 @@ #define ALOtherErrorStr "AL error: unknown error" #define BUFFER_SIZE 32768 +#define STREAMING_BUFFER_COUNT 4 +#define STREAMING_BUFFER_SIZE 4096 namespace Polycode { class String; - + + class AudioStreamingSource { + public: + AudioStreamingSource(unsigned int channels, unsigned int bps, unsigned int freq); + + POLYIGNORE virtual unsigned int streamData(char *buffer, unsigned int size); + + unsigned int getNumChannels(); + unsigned int getBitsPerSample(); + unsigned int getFrequency(); + + protected: + + unsigned int channels; + unsigned int bps; + unsigned int freq; + + }; + /** * Loads and plays a sound. This class can load and play an OGG or WAV sound file. */ @@ -57,11 +78,13 @@ namespace Polycode { * Constructor. * @param fileName Path to an OGG or WAV file to load. */ - Sound(const String& fileName); - Sound(const char *data, int size, int channels = 1, ALsizei freq = 44100, int bps = 16); + Sound(const String& fileName, bool generateFloatBuffer = false); + Sound(int size, const char *data, int channels = 1, ALsizei freq = 44100, int bps = 16, bool generateFloatBuffer = false); + Sound(AudioStreamingSource *streamingSource); + virtual ~Sound(); - void loadFile(String fileName); + void loadFile(String fileName, bool generateFloatBuffer); void reloadProperties(); @@ -137,9 +160,9 @@ namespace Polycode { Number getReferenceDistance(); Number getMaxDistance(); - ALuint loadBytes(const char *data, int size, int channels = 1, ALsizei freq = 44100, int bps = 16); - ALuint loadWAV(const String& fileName); - ALuint loadOGG(const String& fileName); + ALuint loadBytes(const char *data, int size, int channels = 1, ALsizei freq = 44100, int bps = 16, bool generateFloatBuffer = false); + ALuint loadWAV(const String& fileName, bool generateFloatBuffer); + ALuint loadOGG(const String& fileName, bool generateFloatBuffer); ALuint GenSource(ALuint buffer); ALuint GenSource(); @@ -149,12 +172,21 @@ namespace Polycode { void soundCheck(bool result, const String& err); static unsigned long readByte32(const unsigned char buffer[4]); static unsigned short readByte16(const unsigned char buffer[2]); + + void updateStream(); + + POLYIGNORE std::vector *getFloatBuffer(); protected: + + bool updateALBuffer(ALuint buffer); Number referenceDistance; Number maxDistance; - + + bool streamingSound; + AudioStreamingSource *streamingSource; + Number pitch; Number volume; @@ -166,6 +198,10 @@ namespace Polycode { ALuint buffer; // Kept around only for deletion purposes ALuint soundSource; int sampleLength; + + ALuint streamingSources; + ALuint streamingBuffers[STREAMING_BUFFER_COUNT]; + std::vector floatBuffer; }; } diff --git a/Core/Contents/Include/PolySoundManager.h b/Core/Contents/Include/PolySoundManager.h index 4e42c99d9..694382f4a 100755 --- a/Core/Contents/Include/PolySoundManager.h +++ b/Core/Contents/Include/PolySoundManager.h @@ -23,6 +23,7 @@ #pragma once #include "PolyGlobals.h" #include "PolyVector3.h" +#include "PolySound.h" #if defined(__APPLE__) && defined(__MACH__) #include @@ -37,6 +38,7 @@ namespace Polycode { /** * Controls global sound settings. */ + class _PolyExport SoundManager : public PolyBase{ public: SoundManager(); @@ -45,16 +47,28 @@ namespace Polycode { void setListenerPosition(Vector3 position); void setListenerOrientation(Vector3 orientation, Vector3 upVector); void initAL(); + + bool recordSound(unsigned int rate, unsigned int sampleSize); + Sound *stopRecording(bool generateFloatBuffer = false); + + void Update(); /** * Sets the global sound volume. */ void setGlobalVolume(Number globalVolume); - + + void registerStreamingSound(Sound *sound); + void unregisterStreamingSound(Sound *sound); protected: + std::vector streamingSounds; ALCdevice* device; + ALCdevice* captureDevice; + ALbyte *recordingBuffer; + int recordingBufferSize; + int recordingBufferRate; ALCcontext* context; }; } diff --git a/Core/Contents/Include/PolyString.h b/Core/Contents/Include/PolyString.h index 65a8462b5..11d8ba96b 100644 --- a/Core/Contents/Include/PolyString.h +++ b/Core/Contents/Include/PolyString.h @@ -175,6 +175,10 @@ namespace Polycode { * @return A string converted from the Number. */ static String NumberToString(Number value, int precision = 2); + + + Number toNumber(); + int toInteger(); /** * Convert an integer to a String. diff --git a/Core/Contents/Include/PolyTextMesh.h b/Core/Contents/Include/PolyTextMesh.h new file mode 100644 index 000000000..deba4b1ad --- /dev/null +++ b/Core/Contents/Include/PolyTextMesh.h @@ -0,0 +1,25 @@ + +#pragma once +#include "PolyGlobals.h" +#include "PolyEntity.h" +#include "PolyMesh.h" +#include "PolyString.h" + +namespace Polycode { + + class FontGlyphSheet; + + class _PolyExport TextMesh : public Mesh { + public: + TextMesh(FontGlyphSheet* font, const String& text); + + void setFont(FontGlyphSheet* font); + void setText(const String& text); + + void rebuild(); + + private: + String text; + FontGlyphSheet* font; + }; +} diff --git a/Core/Contents/Include/PolyTexture.h b/Core/Contents/Include/PolyTexture.h index 740aa25d9..2e216bc7a 100755 --- a/Core/Contents/Include/PolyTexture.h +++ b/Core/Contents/Include/PolyTexture.h @@ -53,6 +53,9 @@ namespace Polycode { int getWidth() const; int getHeight() const; + + void setCreateMipmaps(bool createMipmapsIn) { createMipmaps = createMipmapsIn; } + bool getCreateMipmaps() const { return createMipmaps; } bool clamp; char *textureData; diff --git a/Core/Contents/Include/PolyTween.h b/Core/Contents/Include/PolyTween.h index 44bfdc69d..1c92ab4c9 100755 --- a/Core/Contents/Include/PolyTween.h +++ b/Core/Contents/Include/PolyTween.h @@ -50,10 +50,12 @@ namespace Polycode { Tween(Number *target, int easeType, Number startVal, Number endVal, Number time, bool repeat=false, bool deleteOnComplete=false, Number waitTime = 0.0); virtual ~Tween(); - void handleEvent(Event *event); + void updateTween(Number elapsed); Number interpolateTween(); virtual void updateCustomTween() {} void doOnComplete(); + + Number *getTarget(); /** * Pauses and resumes the tween. @@ -102,6 +104,7 @@ namespace Polycode { */ void setSpeed(Number speed); + bool paused; protected: @@ -116,7 +119,6 @@ namespace Polycode { Number *targetVal; Number localTargetVal; Number tweenTime; - Timer *tweenTimer; }; /** diff --git a/Core/Contents/Include/PolyTweenManager.h b/Core/Contents/Include/PolyTweenManager.h index 2ba4759d4..88bcf3569 100755 --- a/Core/Contents/Include/PolyTweenManager.h +++ b/Core/Contents/Include/PolyTweenManager.h @@ -23,6 +23,7 @@ THE SOFTWARE. #pragma once #include "PolyGlobals.h" #include +#include "PolyCore.h" namespace Polycode { @@ -33,10 +34,14 @@ namespace Polycode { TweenManager(); ~TweenManager(); void addTween(Tween *tween); - void removeTween(Tween *tween); - void Update(); + void Update(Number elapsed); + void removeTween(Tween *tween); + void removeTweensForTarget(Number *target); private: + + std::vector tweensToAdd; std::vector tweens; + }; } diff --git a/Core/Contents/Include/PolyVector2.h b/Core/Contents/Include/PolyVector2.h index 2a345e468..5e00b3c93 100755 --- a/Core/Contents/Include/PolyVector2.h +++ b/Core/Contents/Include/PolyVector2.h @@ -76,6 +76,10 @@ namespace Polycode { inline Vector2 operator * (const Number val) const { return Vector2(x * val, y * val); } + + inline Vector2 operator * (const Vector2 &v2) const { + return Vector2(x * v2.x, y * v2.y); + } inline Vector2 operator / (const Number val) const { assert( val != 0.0 ); @@ -104,10 +108,13 @@ namespace Polycode { return Vector2(x + v2.x, y + v2.y); } + inline Vector2 operator - () { + return Vector2(-x, -y); + } inline bool operator == ( const Vector2& v2) { return (v2.x == x && v2.y == y); - } + } inline bool operator != ( const Vector2& v2) { return (v2.x != x || v2.y != y); diff --git a/Core/Contents/Include/PolyVector3.h b/Core/Contents/Include/PolyVector3.h index d8857f3d2..08a47b238 100755 --- a/Core/Contents/Include/PolyVector3.h +++ b/Core/Contents/Include/PolyVector3.h @@ -54,7 +54,7 @@ namespace Polycode { * Default constructor. */ Vector3(); - ~Vector3(); + virtual ~Vector3(); /** * Sets the vector from x,y,z coordinates. @@ -88,6 +88,11 @@ namespace Polycode { return Vector3(x * val, y * val, z * val); } + inline Vector3 operator * (const Vector3 &v2) const { + return Vector3(x * v2.x, y * v2.y, z * v2.z); + } + + inline Vector3 operator / (const Number val) const { assert( val != 0.0 ); return operator*(1/val); @@ -116,8 +121,11 @@ namespace Polycode { inline Vector3 operator + ( const Vector3& v2 ) const { return Vector3(x + v2.x, y + v2.y, z + v2.z); - } - + } + + inline Vector3 operator - () { + return Vector3(-x, -y, -z); + } inline bool operator == ( const Vector3& v2) { return (v2.x == x && v2.y == y && v2.z == z); @@ -151,6 +159,24 @@ namespace Polycode { inline Number length () const { return sqrtf( x * x + y * y + z * z ); } + + /** + * Returns square of the length of the vector. + * Cheaper to execute than length(), for use when you're just e.g. comparing vector lengths. + * @return Square length of the vector. + */ + inline Number lengthSquared() const { + return dot(*this); + } + + inline Vector3 setLength(const Number newLength) { + Number oldLength = length(); + if(oldLength != 0 && newLength != oldLength) { + (*this) = (*this) * (newLength / oldLength); + } + return (*this); + + } /** * Returns the dot product with another vector. diff --git a/Core/Contents/Include/PolyVector4.h b/Core/Contents/Include/PolyVector4.h new file mode 100755 index 000000000..a6d5b94df --- /dev/null +++ b/Core/Contents/Include/PolyVector4.h @@ -0,0 +1,168 @@ +/* + Copyright (C) 2011 by Ivan Safrin + + 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: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + 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. + */ + +#pragma once +#include "PolyGlobals.h" +#include + +//#ifdef _WINDOWS + #include +//#endif + +namespace Polycode { + + /** + * 4D Vector class. + */ + class _PolyExport Vector4 : public PolyBase { + public: + + /** + * Create from x,y,z coordinates. + * @param x X coordinate. + * @param y Y coordinate. + * @param z Z coordinate. + * @param w W coordinate. + */ + Vector4(Number x,Number y,Number z, Number w); + + /** + * Create from single value for all coordinates + * @param val Value for all coordinates + */ + Vector4(Number val); + + /** + * Default constructor. + */ + Vector4(); + virtual ~Vector4(); + + /** + * Sets the vector from x,y,z coordinates. + * @param x X coordinate. + * @param y Y coordinate. + * @param z Z coordinate. + * @param w W coordinate + */ + void set(Number x, Number y, Number z, Number w); + + inline Vector4 operator - ( const Vector4& v2 ) const { + return Vector4(x - v2.x, y - v2.y, z - v2.z, w - v2.w); + } + + + // ---------------------------------------------------------------------------------------------------------------- + /** @name Operators + * Available vector operators. + */ + //@{ + + + inline Vector4 operator * (const Number val) const { + return Vector4(x * val, y * val, z * val, w * val); + } + + inline Vector4 operator * (const Vector4 &v2) const { + return Vector4(x * v2.x, y * v2.y, z * v2.z, w * v2.w); + } + + + inline Vector4 operator / (const Number val) const { + assert( val != 0.0 ); + return operator*(1/val); + } + + inline Vector4& operator = ( const Vector4& v2) { + x = v2.x; + y = v2.y; + z = v2.z; + w = v2.w; + return *this; + } + + inline Vector4& operator += ( const Vector4& v2) { + x += v2.x; + y += v2.y; + z += v2.z; + w += v2.w; + return *this; + } + + inline Vector4& operator -= ( const Vector4& v2) { + x -= v2.x; + y -= v2.y; + z -= v2.z; + w -= v2.w; + return *this; + } + + inline Vector4 operator + ( const Vector4& v2 ) const { + return Vector4(x + v2.x, y + v2.y, z + v2.z, w + v2.w); + } + + inline Vector4 operator - () { + return Vector4(-x, -y, -z, -w); + } + + inline bool operator == ( const Vector4& v2) { + return (v2.x == x && v2.y == y && v2.z == z && v2.w == w); + } + + inline bool operator != ( const Vector4& v2) { + return (v2.x != x || v2.y != y || v2.z != z || v2.w != w); + } + + //@} + // ---------------------------------------------------------------------------------------------------------------- + + + /** + * Returns the dot product with another vector. + * @return Dor product with the vector. + */ + inline Number dot(const Vector4 &u) const { + return x * u.x + y * u.y + z * u.z + w * u.w; + } + + /** + * X coordinate. + */ + Number x; + + /** + * Y coordinate. + */ + Number y; + + /** + * Z coordinate. + */ + Number z; + + /** + * W coordinate. + */ + Number w; + + }; +} diff --git a/Core/Contents/Include/PolyVertex.h b/Core/Contents/Include/PolyVertex.h deleted file mode 100755 index 4b8353614..000000000 --- a/Core/Contents/Include/PolyVertex.h +++ /dev/null @@ -1,196 +0,0 @@ -/* - Copyright (C) 2011 by Ivan Safrin - - 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: - - The above copyright notice and this permission notice shall be included in - all copies or substantial portions of the Software. - - 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. -*/ - -#pragma once -#include "PolyGlobals.h" -#include "PolyVector3.h" -#include "PolyVector2.h" -#include "PolyColor.h" -#include - -namespace Polycode { - - class Bone; - - /** - * Bone assignment. - */ - class _PolyExport BoneAssignment { - public: - BoneAssignment(){ - bone = NULL; - } - /** - * Id of the bone assigned. - */ - unsigned int boneID; - - /** - * Assignment weight. - */ - Number weight; - - /** - * Assigned bone. - */ - Bone *bone; - }; - - /** - * A mesh vertex. - */ - class _PolyExport Vertex : public Vector3 { - public: - - /** - * Default constructor. - */ - Vertex(); - - /** - * Initialize with position. - * @param pos_x Position x. - * @param pos_y Position y. - * @param pos_z Position z. - */ - Vertex(Number x, Number y, Number z); - - /** - * Initialize with position and normal. - * @param pos_x Position x. - * @param pos_y Position y. - * @param pos_z Position z. - * @param nor_x Normal x. - * @param nor_y Normal y. - * @param nor_z Normal z. - */ - Vertex(Number pos_x, Number pos_y, Number pos_z, Number nor_x, Number nor_y, Number nor_z); - - /** - * Initialize with position and normal and texture coordinates. - * @param pos_x Position x. - * @param pos_y Position y. - * @param pos_z Position z. - * @param nor_x Normal x. - * @param nor_y Normal y. - * @param nor_z Normal z. - * @param u Horizontal texture coordinate. - * @param v Vertical texture coordinate. - */ - Vertex(Number pos_x, Number pos_y, Number pos_z, Number nor_x, Number nor_y, Number nor_z, Number u, Number v); - - /** - * Initialize with position and texture coordinates. - * @param pos_x Position x. - * @param pos_y Position y. - * @param pos_z Position z. - * @param u Horizontal texture coordinate. - * @param v Vertical texture coordinate. - */ - Vertex(Number x, Number y, Number z, Number u, Number v); - - virtual ~Vertex(); - - /** - * Assign a bone to the vertex by bone id. - * @param boneID The bone id. - * @param boneWeight Normalized weight of the bone assignment. - */ - void addBoneAssignment(unsigned int boneID, Number boneWeight); - - /** - * Get total number of bone assignments. - * @return Number of bone assignments. - */ - int getNumBoneAssignments(); - - /** - * Get bone assignment at index. - * @param Index of bone assignment. - * @return Bone assignment at index. - */ - BoneAssignment *getBoneAssignment(unsigned int index); - - /** - * Normalizes all current weight assignments. - */ - void normalizeWeights(); - - /** - * Returns the texture coordinates. - * @return Texture coordinates. - */ - Vector2 getTexCoord(); - - /** - * Sets the texture coordinates. - * @param u New horizontal texture coordinate. - * @param v New vertical texture coordinate. - */ - void setTexCoord(Number u, Number v); - - /** - * Sets the normal - * @param x Normal x. - * @param y Normal y. - * @param z Normal z. - */ - void setNormal(Number x, Number y, Number z); - - /** - * Rest normal. - */ - Vector3 restNormal; - - /** - * Rest position. - */ - Vector3 restPosition; - - /** - * Vertex normal. - */ - Vector3 normal; - - /** - * Vertex tangent. - */ - Vector3 tangent; - - /** - * Vertex color. - */ - Color vertexColor; - - /** - * Texture coordinates - */ - Vector2 texCoord; - - bool useVertexColor; - - protected: - - std::vector boneAssignments; - - }; -} diff --git a/Core/Contents/Include/PolyWinCore.h b/Core/Contents/Include/PolyWinCore.h index f1e1a3802..65c30ab5c 100644 --- a/Core/Contents/Include/PolyWinCore.h +++ b/Core/Contents/Include/PolyWinCore.h @@ -95,8 +95,12 @@ #define EXTENDED_KEYMASK (1<<24) +#define NO_TOUCH_API +#define NO_PEN_API + #ifdef _MINGW #define NO_TOUCH_API 1 +#define NO_PEN_API 1 #endif #define POLYCODE_CORE Win32Core @@ -116,6 +120,7 @@ namespace Polycode { int mouseY; TouchInfo touch; std::vector touches; + int touchType; PolyKEY keyCode; wchar_t unicodeChar; char mouseButton; @@ -175,14 +180,14 @@ class Gamepad_devicePrivate { public: - Win32Core(PolycodeViewBase *view, int xRes, int yRes, bool fullScreen, bool vSync, int aaLevel, int anisotropyLevel, int frameRate, int monitorIndex = -1); + Win32Core(PolycodeViewBase *view, int xRes, int yRes, bool fullScreen, bool vSync, int aaLevel, int anisotropyLevel, int frameRate, int monitorIndex = -1, bool retinaSupport = false); ~Win32Core(); void enableMouse(bool newval); void captureMouse(bool newval); void warpCursor(int x, int y); unsigned int getTicks(); - bool Update(); + bool systemUpdate(); void Render(); void setVSync(bool vSyncVal); @@ -193,14 +198,17 @@ class Gamepad_devicePrivate { void handleMouseDown(int mouseCode,LPARAM lParam, WPARAM wParam); void handleMouseUp(int mouseCode,LPARAM lParam, WPARAM wParam); void handleTouchEvent(LPARAM lParam, WPARAM wParam); + void handlePointerUpdate(LPARAM lParam, WPARAM wParam); bool isMultiTouchEnabled() { return hasMultiTouch; } - void setVideoMode(int xRes, int yRes, bool fullScreen, bool vSync, int aaLevel, int anisotropyLevel); + void setVideoMode(int xRes, int yRes, bool fullScreen, bool vSync, int aaLevel, int anisotropyLevel, bool retinaSupport = true); - void initContext(bool usePixelFormat, unsigned int pixelFormat); + void initContext(int aaLevel); void destroyContext(); + void getWglFunctionPointers(); + void createThread(Threaded *target); PolyKEY mapKey(LPARAM lParam, WPARAM wParam); @@ -231,6 +239,8 @@ class Gamepad_devicePrivate { String executeExternalCommand(String command, String args, String inDirectory); std::vector openFilePicker(std::vector extensions, bool allowMultiple); + String saveFilePicker(std::vector extensions); + void createFolder(const String& folderPath); void openURL(String url); String openFolderPicker(); @@ -238,6 +248,9 @@ class Gamepad_devicePrivate { void moveDiskItem(const String& itemPath, const String& destItemPath); void removeDiskItem(const String& itemPath); + Number getBackingXRes(); + Number getBackingYRes(); + void setCursor(int cursorType); void copyStringToClipboard(const String& str); @@ -248,11 +261,13 @@ class Gamepad_devicePrivate { std::vector gamepads; HWND hWnd; + HINSTANCE hInstance; bool hasCopyDataString; String copyDataString; private: + Number scaleFactor; bool checkSpecialKeyEvents(PolyKEY key); unsigned int nextDeviceID; @@ -261,24 +276,23 @@ class Gamepad_devicePrivate { std::vector win32Events; - void initMultisample(int numSamples); - - int lastMouseX; int lastMouseY; bool isFullScreen; + bool retinaSupport; + bool resizable; HDC hDC; HGLRC hRC; - unsigned int PixelFormat; - PIXELFORMATDESCRIPTOR pfd; // frequency of the windows performance counter double pcFreq; // Tracks whether the system supports multitouch at runtime bool hasMultiTouch; + + std::vector pointerTouches; #ifndef NO_TOUCH_API // Create generic reference to any multitouch functions we need, so that we diff --git a/Core/Contents/Include/Polycode.h b/Core/Contents/Include/Polycode.h index 867e01572..7f64fd374 100755 --- a/Core/Contents/Include/Polycode.h +++ b/Core/Contents/Include/Polycode.h @@ -30,7 +30,6 @@ #include "PolyConfig.h" #include "PolyPerlin.h" #include "PolyEntity.h" -#include "PolyPolygon.h" #include "PolyEvent.h" #include "PolyEventDispatcher.h" #include "PolyEventHandler.h" @@ -42,35 +41,31 @@ #include "PolyCoreInput.h" #include "PolyInputKeys.h" #include "PolyInputEvent.h" +#include "PolyVector2.h" #include "PolyVector3.h" +#include "PolyVector4.h" #include "PolyBezierCurve.h" #include "PolyQuaternionCurve.h" #include "PolyRectangle.h" #include "PolyRenderer.h" +#include "PolyRenderDataArray.h" #include "PolyCoreServices.h" -#include "PolyScreen.h" -#include "PolyScreenEntity.h" -#include "PolyScreenLine.h" -#include "PolyScreenMesh.h" -#include "PolyScreenShape.h" #include "PolyImage.h" #include "PolyLabel.h" #include "PolyFont.h" +#include "PolyFontGlyphSheet.h" #include "PolyFontManager.h" -#include "PolyScreenImage.h" -#include "PolyScreenSprite.h" -#include "PolyScreenLabel.h" -#include "PolyScreenEntityInstance.h" #include "PolyTexture.h" #include "PolyMaterial.h" #include "PolyMesh.h" +#include "PolyTextMesh.h" #include "PolyShader.h" #include "PolyFixedShader.h" #include "PolySceneManager.h" #include "PolyCoreServices.h" #include "PolyCamera.h" #include "PolyScene.h" -#include "PolySceneEntity.h" +#include "PolyEntity.h" #include "PolySceneMesh.h" #include "PolySceneLine.h" #include "PolySceneLight.h" @@ -79,20 +74,22 @@ #include "PolyScenePrimitive.h" #include "PolySceneLabel.h" #include "PolyParticleEmitter.h" -#include "PolyParticle.h" #include "PolySceneRenderTexture.h" -#include "PolyScreenEvent.h" #include "PolyResource.h" #include "PolyThreaded.h" #include "PolySound.h" #include "PolySoundManager.h" #include "PolySceneSound.h" -#include "PolyScreenSound.h" +#include "PolySceneImage.h" #include "PolyClient.h" #include "PolyPeer.h" #include "PolyServer.h" #include "PolyServerWorld.h" #include "PolySocket.h" +#include "PolyHTTPFetcher.h" +#include "PolyRay.h" +#include "PolySceneSprite.h" +#include "PolySceneEntityInstance.h" #include "PolyGlobals.h" #ifdef _WINDOWS diff --git a/Core/Contents/Include/PolyiPhoneCore.h b/Core/Contents/Include/PolyiPhoneCore.h deleted file mode 100644 index affd0372a..000000000 --- a/Core/Contents/Include/PolyiPhoneCore.h +++ /dev/null @@ -1,88 +0,0 @@ -/* -Copyright (C) 2011 by Ivan Safrin - -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: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -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. -*/ - -#pragma once -#include "PolyString.h" -#include "PolyGlobals.h" -#include "PolyCore.h" -#include "PolyRectangle.h" -#include - -using std::vector; - -namespace Polycode { - - class _PolyExport PosixMutex : public CoreMutex { - public: - pthread_mutex_t pMutex; - }; - - class iPhoneEvent { - public: - int eventGroup; - int eventCode; - - int mouseX; - int mouseY; - - PolyKEY keyCode; - wchar_t unicodeChar; - - char mouseButton; - - static const int EVENTBASE_PLATFORMEVENT = 0x300; - static const int INPUT_EVENT = EVENTBASE_PLATFORMEVENT+0; - }; - - class _PolyExport IPhoneCore : public Core { - - public: - - IPhoneCore(int frameRate); - virtual ~IPhoneCore(); - - void enableMouse(bool newval); - unsigned int getTicks(); - bool Update(); - void setVideoMode(int xRes, int yRes, bool fullScreen, int aaLevel); - void createThread(Threaded *target); - - void lockMutex(CoreMutex *mutex); - void unlockMutex(CoreMutex *mutex); - CoreMutex *createMutex(); - - void checkEvents(); - - vector getVideoModes(); - - int lastMouseY; - int lastMouseX; - - CoreMutex *eventMutex; - - vector osxEvents; - - private: - - - }; -} \ No newline at end of file diff --git a/Core/Contents/Include/rgbe.h b/Core/Contents/Include/rgbe.h new file mode 100644 index 000000000..87929ad2a --- /dev/null +++ b/Core/Contents/Include/rgbe.h @@ -0,0 +1,51 @@ +#ifndef _H_RGBE +#define _H_RGBE +/* THIS CODE CARRIES NO GUARANTEE OF USABILITY OR FITNESS FOR ANY PURPOSE. + * WHILE THE AUTHORS HAVE TRIED TO ENSURE THE PROGRAM WORKS CORRECTLY, + * IT IS STRICTLY USE AT YOUR OWN RISK. */ + +/* utility for reading and writing Ward's rgbe image format. + See rgbe.txt file for more details. +*/ + +#include + +typedef struct { + int valid; /* indicate which fields are valid */ + char programtype[16]; /* listed at beginning of file to identify it + * after "#?". defaults to "RGBE" */ + float gamma; /* image has already been gamma corrected with + * given gamma. defaults to 1.0 (no correction) */ + float exposure; /* a value of 1.0 in an image corresponds to + * watts/steradian/m^2. + * defaults to 1.0 */ +} rgbe_header_info; + +/* flags indicating which fields in an rgbe_header_info are valid */ +#define RGBE_VALID_PROGRAMTYPE 0x01 +#define RGBE_VALID_GAMMA 0x02 +#define RGBE_VALID_EXPOSURE 0x04 + +/* return codes for rgbe routines */ +#define RGBE_RETURN_SUCCESS 0 +#define RGBE_RETURN_FAILURE -1 + +/* read or write headers */ +/* you may set rgbe_header_info to null if you want to */ +int RGBE_WriteHeader(FILE *fp, int width, int height, rgbe_header_info *info); +int RGBE_ReadHeader(FILE *fp, int *width, int *height, rgbe_header_info *info); + +/* read or write pixels */ +/* can read or write pixels in chunks of any size including single pixels*/ +int RGBE_WritePixels(FILE *fp, float *data, int numpixels); +int RGBE_ReadPixels(FILE *fp, float *data, int numpixels); + +/* read or write run length encoded files */ +/* must be called to read or write whole scanlines */ +int RGBE_WritePixels_RLE(FILE *fp, float *data, int scanline_width, + int num_scanlines); +int RGBE_ReadPixels_RLE(FILE *fp, float *data, int scanline_width, + int num_scanlines); + +#endif /* _H_RGBE */ + diff --git a/Core/Contents/Include/stb_image.h b/Core/Contents/Include/stb_image.h new file mode 100644 index 000000000..1a777855e --- /dev/null +++ b/Core/Contents/Include/stb_image.h @@ -0,0 +1,6435 @@ +/* stb_image - v2.05 - public domain image loader - http://nothings.org/stb_image.h + no warranty implied; use at your own risk + + Do this: + #define STB_IMAGE_IMPLEMENTATION + before you include this file in *one* C or C++ file to create the implementation. + + // i.e. it should look like this: + #include ... + #include ... + #include ... + #define STB_IMAGE_IMPLEMENTATION + #include "stb_image.h" + + You can #define STBI_ASSERT(x) before the #include to avoid using assert.h. + And #define STBI_MALLOC, STBI_REALLOC, and STBI_FREE to avoid using malloc,realloc,free + + + QUICK NOTES: + Primarily of interest to game developers and other people who can + avoid problematic images and only need the trivial interface + + JPEG baseline & progressive (12 bpc/arithmetic not supported, same as stock IJG lib) + PNG 1/2/4/8-bit-per-channel (16 bpc not supported) + + TGA (not sure what subset, if a subset) + BMP non-1bpp, non-RLE + PSD (composited view only, no extra channels) + + GIF (*comp always reports as 4-channel) + HDR (radiance rgbE format) + PIC (Softimage PIC) + PNM (PPM and PGM binary only) + + - decode from memory or through FILE (define STBI_NO_STDIO to remove code) + - decode from arbitrary I/O callbacks + - SIMD acceleration on x86/x64 (SSE2) and ARM (NEON) + + Full documentation under "DOCUMENTATION" below. + + + Revision 2.00 release notes: + + - Progressive JPEG is now supported. + + - PPM and PGM binary formats are now supported, thanks to Ken Miller. + + - x86 platforms now make use of SSE2 SIMD instructions for + JPEG decoding, and ARM platforms can use NEON SIMD if requested. + This work was done by Fabian "ryg" Giesen. SSE2 is used by + default, but NEON must be enabled explicitly; see docs. + + With other JPEG optimizations included in this version, we see + 2x speedup on a JPEG on an x86 machine, and a 1.5x speedup + on a JPEG on an ARM machine, relative to previous versions of this + library. The same results will not obtain for all JPGs and for all + x86/ARM machines. (Note that progressive JPEGs are significantly + slower to decode than regular JPEGs.) This doesn't mean that this + is the fastest JPEG decoder in the land; rather, it brings it + closer to parity with standard libraries. If you want the fastest + decode, look elsewhere. (See "Philosophy" section of docs below.) + + See final bullet items below for more info on SIMD. + + - Added STBI_MALLOC, STBI_REALLOC, and STBI_FREE macros for replacing + the memory allocator. Unlike other STBI libraries, these macros don't + support a context parameter, so if you need to pass a context in to + the allocator, you'll have to store it in a global or a thread-local + variable. + + - Split existing STBI_NO_HDR flag into two flags, STBI_NO_HDR and + STBI_NO_LINEAR. + STBI_NO_HDR: suppress implementation of .hdr reader format + STBI_NO_LINEAR: suppress high-dynamic-range light-linear float API + + - You can suppress implementation of any of the decoders to reduce + your code footprint by #defining one or more of the following + symbols before creating the implementation. + + STBI_NO_JPEG + STBI_NO_PNG + STBI_NO_BMP + STBI_NO_PSD + STBI_NO_TGA + STBI_NO_GIF + STBI_NO_HDR + STBI_NO_PIC + STBI_NO_PNM (.ppm and .pgm) + + - You can request *only* certain decoders and suppress all other ones + (this will be more forward-compatible, as addition of new decoders + doesn't require you to disable them explicitly): + + STBI_ONLY_JPEG + STBI_ONLY_PNG + STBI_ONLY_BMP + STBI_ONLY_PSD + STBI_ONLY_TGA + STBI_ONLY_GIF + STBI_ONLY_HDR + STBI_ONLY_PIC + STBI_ONLY_PNM (.ppm and .pgm) + + Note that you can define multiples of these, and you will get all + of them ("only x" and "only y" is interpreted to mean "only x&y"). + + - If you use STBI_NO_PNG (or _ONLY_ without PNG), and you still + want the zlib decoder to be available, #define STBI_SUPPORT_ZLIB + + - Compilation of all SIMD code can be suppressed with + #define STBI_NO_SIMD + It should not be necessary to disable SIMD unless you have issues + compiling (e.g. using an x86 compiler which doesn't support SSE + intrinsics or that doesn't support the method used to detect + SSE2 support at run-time), and even those can be reported as + bugs so I can refine the built-in compile-time checking to be + smarter. + + - The old STBI_SIMD system which allowed installing a user-defined + IDCT etc. has been removed. If you need this, don't upgrade. My + assumption is that almost nobody was doing this, and those who + were will find the built-in SIMD more satisfactory anyway. + + - RGB values computed for JPEG images are slightly different from + previous versions of stb_image. (This is due to using less + integer precision in SIMD.) The C code has been adjusted so + that the same RGB values will be computed regardless of whether + SIMD support is available, so your app should always produce + consistent results. But these results are slightly different from + previous versions. (Specifically, about 3% of available YCbCr values + will compute different RGB results from pre-1.49 versions by +-1; + most of the deviating values are one smaller in the G channel.) + + - If you must produce consistent results with previous versions of + stb_image, #define STBI_JPEG_OLD and you will get the same results + you used to; however, you will not get the SIMD speedups for + the YCbCr-to-RGB conversion step (although you should still see + significant JPEG speedup from the other changes). + + Please note that STBI_JPEG_OLD is a temporary feature; it will be + removed in future versions of the library. It is only intended for + near-term back-compatibility use. + + + Latest revision history: + 2.05 (2015-04-19) fix bug in progressive JPEG handling, fix warning + 2.04 (2015-04-15) try to re-enable SIMD on MinGW 64-bit + 2.03 (2015-04-12) additional corruption checking + stbi_set_flip_vertically_on_load + fix NEON support; fix mingw support + 2.02 (2015-01-19) fix incorrect assert, fix warning + 2.01 (2015-01-17) fix various warnings + 2.00b (2014-12-25) fix STBI_MALLOC in progressive JPEG + 2.00 (2014-12-25) optimize JPEG, including x86 SSE2 & ARM NEON SIMD + progressive JPEG + PGM/PPM support + STBI_MALLOC,STBI_REALLOC,STBI_FREE + STBI_NO_*, STBI_ONLY_* + GIF bugfix + 1.48 (2014-12-14) fix incorrectly-named assert() + 1.47 (2014-12-14) 1/2/4-bit PNG support (both grayscale and paletted) + optimize PNG + fix bug in interlaced PNG with user-specified channel count + + See end of file for full revision history. + + + ============================ Contributors ========================= + + Image formats Bug fixes & warning fixes + Sean Barrett (jpeg, png, bmp) Marc LeBlanc + Nicolas Schulz (hdr, psd) Christpher Lloyd + Jonathan Dummer (tga) Dave Moore + Jean-Marc Lienher (gif) Won Chun + Tom Seddon (pic) the Horde3D community + Thatcher Ulrich (psd) Janez Zemva + Ken Miller (pgm, ppm) Jonathan Blow + Laurent Gomila + Aruelien Pocheville + Extensions, features Ryamond Barbiero + Jetro Lauha (stbi_info) David Woo + Martin "SpartanJ" Golini (stbi_info) Martin Golini + James "moose2000" Brown (iPhone PNG) Roy Eltham + Ben "Disch" Wenger (io callbacks) Luke Graham + Omar Cornut (1/2/4-bit PNG) Thomas Ruf + Nicolas Guillemot (vertical flip) John Bartholomew + Ken Hamada + Optimizations & bugfixes Cort Stratton + Fabian "ryg" Giesen Blazej Dariusz Roszkowski + Arseny Kapoulkine Thibault Reuille + Paul Du Bois + Guillaume George + If your name should be here but Jerry Jansson + isn't, let Sean know. Hayaki Saito + Johan Duparc + Ronny Chevalier + Michal Cichon + Tero Hanninen + Sergio Gonzalez + Cass Everitt + Engin Manap + Martins Mozeiko + Joseph Thomson + Phil Jordan + +License: + This software is in the public domain. Where that dedication is not + recognized, you are granted a perpetual, irrevocable license to copy + and modify this file however you want. + +*/ + +#ifndef STBI_INCLUDE_STB_IMAGE_H +#define STBI_INCLUDE_STB_IMAGE_H + +// DOCUMENTATION +// +// Limitations: +// - no 16-bit-per-channel PNG +// - no 12-bit-per-channel JPEG +// - no JPEGs with arithmetic coding +// - no 1-bit BMP +// - GIF always returns *comp=4 +// +// Basic usage (see HDR discussion below for HDR usage): +// int x,y,n; +// unsigned char *data = stbi_load(filename, &x, &y, &n, 0); +// // ... process data if not NULL ... +// // ... x = width, y = height, n = # 8-bit components per pixel ... +// // ... replace '0' with '1'..'4' to force that many components per pixel +// // ... but 'n' will always be the number that it would have been if you said 0 +// stbi_image_free(data) +// +// Standard parameters: +// int *x -- outputs image width in pixels +// int *y -- outputs image height in pixels +// int *comp -- outputs # of image components in image file +// int req_comp -- if non-zero, # of image components requested in result +// +// The return value from an image loader is an 'unsigned char *' which points +// to the pixel data, or NULL on an allocation failure or if the image is +// corrupt or invalid. The pixel data consists of *y scanlines of *x pixels, +// with each pixel consisting of N interleaved 8-bit components; the first +// pixel pointed to is top-left-most in the image. There is no padding between +// image scanlines or between pixels, regardless of format. The number of +// components N is 'req_comp' if req_comp is non-zero, or *comp otherwise. +// If req_comp is non-zero, *comp has the number of components that _would_ +// have been output otherwise. E.g. if you set req_comp to 4, you will always +// get RGBA output, but you can check *comp to see if it's trivially opaque +// because e.g. there were only 3 channels in the source image. +// +// An output image with N components has the following components interleaved +// in this order in each pixel: +// +// N=#comp components +// 1 grey +// 2 grey, alpha +// 3 red, green, blue +// 4 red, green, blue, alpha +// +// If image loading fails for any reason, the return value will be NULL, +// and *x, *y, *comp will be unchanged. The function stbi_failure_reason() +// can be queried for an extremely brief, end-user unfriendly explanation +// of why the load failed. Define STBI_NO_FAILURE_STRINGS to avoid +// compiling these strings at all, and STBI_FAILURE_USERMSG to get slightly +// more user-friendly ones. +// +// Paletted PNG, BMP, GIF, and PIC images are automatically depalettized. +// +// =========================================================================== +// +// Philosophy +// +// stb libraries are designed with the following priorities: +// +// 1. easy to use +// 2. easy to maintain +// 3. good performance +// +// Sometimes I let "good performance" creep up in priority over "easy to maintain", +// and for best performance I may provide less-easy-to-use APIs that give higher +// performance, in addition to the easy to use ones. Nevertheless, it's important +// to keep in mind that from the standpoint of you, a client of this library, +// all you care about is #1 and #3, and stb libraries do not emphasize #3 above all. +// +// Some secondary priorities arise directly from the first two, some of which +// make more explicit reasons why performance can't be emphasized. +// +// - Portable ("ease of use") +// - Small footprint ("easy to maintain") +// - No dependencies ("ease of use") +// +// =========================================================================== +// +// I/O callbacks +// +// I/O callbacks allow you to read from arbitrary sources, like packaged +// files or some other source. Data read from callbacks are processed +// through a small internal buffer (currently 128 bytes) to try to reduce +// overhead. +// +// The three functions you must define are "read" (reads some bytes of data), +// "skip" (skips some bytes of data), "eof" (reports if the stream is at the end). +// +// =========================================================================== +// +// SIMD support +// +// The JPEG decoder will try to automatically use SIMD kernels on x86 when +// supported by the compiler. For ARM Neon support, you must explicitly +// request it. +// +// (The old do-it-yourself SIMD API is no longer supported in the current +// code.) +// +// On x86, SSE2 will automatically be used when available based on a run-time +// test; if not, the generic C versions are used as a fall-back. On ARM targets, +// the typical path is to have separate builds for NEON and non-NEON devices +// (at least this is true for iOS and Android). Therefore, the NEON support is +// toggled by a build flag: define STBI_NEON to get NEON loops. +// +// The output of the JPEG decoder is slightly different from versions where +// SIMD support was introduced (that is, for versions before 1.49). The +// difference is only +-1 in the 8-bit RGB channels, and only on a small +// fraction of pixels. You can force the pre-1.49 behavior by defining +// STBI_JPEG_OLD, but this will disable some of the SIMD decoding path +// and hence cost some performance. +// +// If for some reason you do not want to use any of SIMD code, or if +// you have issues compiling it, you can disable it entirely by +// defining STBI_NO_SIMD. +// +// =========================================================================== +// +// HDR image support (disable by defining STBI_NO_HDR) +// +// stb_image now supports loading HDR images in general, and currently +// the Radiance .HDR file format, although the support is provided +// generically. You can still load any file through the existing interface; +// if you attempt to load an HDR file, it will be automatically remapped to +// LDR, assuming gamma 2.2 and an arbitrary scale factor defaulting to 1; +// both of these constants can be reconfigured through this interface: +// +// stbi_hdr_to_ldr_gamma(2.2f); +// stbi_hdr_to_ldr_scale(1.0f); +// +// (note, do not use _inverse_ constants; stbi_image will invert them +// appropriately). +// +// Additionally, there is a new, parallel interface for loading files as +// (linear) floats to preserve the full dynamic range: +// +// float *data = stbi_loadf(filename, &x, &y, &n, 0); +// +// If you load LDR images through this interface, those images will +// be promoted to floating point values, run through the inverse of +// constants corresponding to the above: +// +// stbi_ldr_to_hdr_scale(1.0f); +// stbi_ldr_to_hdr_gamma(2.2f); +// +// Finally, given a filename (or an open file or memory block--see header +// file for details) containing image data, you can query for the "most +// appropriate" interface to use (that is, whether the image is HDR or +// not), using: +// +// stbi_is_hdr(char *filename); +// +// =========================================================================== +// +// iPhone PNG support: +// +// By default we convert iphone-formatted PNGs back to RGB, even though +// they are internally encoded differently. You can disable this conversion +// by by calling stbi_convert_iphone_png_to_rgb(0), in which case +// you will always just get the native iphone "format" through (which +// is BGR stored in RGB). +// +// Call stbi_set_unpremultiply_on_load(1) as well to force a divide per +// pixel to remove any premultiplied alpha *only* if the image file explicitly +// says there's premultiplied data (currently only happens in iPhone images, +// and only if iPhone convert-to-rgb processing is on). +// + + +#define STBI_NO_STDIO + +#ifndef STBI_NO_STDIO +#include +#endif // STBI_NO_STDIO + +#define STBI_VERSION 1 + +enum +{ + STBI_default = 0, // only used for req_comp + + STBI_grey = 1, + STBI_grey_alpha = 2, + STBI_rgb = 3, + STBI_rgb_alpha = 4 +}; + +typedef unsigned char stbi_uc; + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef STB_IMAGE_STATIC +#define STBIDEF static +#else +#define STBIDEF extern +#endif + +////////////////////////////////////////////////////////////////////////////// +// +// PRIMARY API - works on images of any type +// + +// +// load image by filename, open file, or memory buffer +// + +typedef struct +{ + int (*read) (void *user,char *data,int size); // fill 'data' with 'size' bytes. return number of bytes actually read + void (*skip) (void *user,int n); // skip the next 'n' bytes, or 'unget' the last -n bytes if negative + int (*eof) (void *user); // returns nonzero if we are at end of file/data +} stbi_io_callbacks; + +STBIDEF stbi_uc *stbi_load (char const *filename, int *x, int *y, int *comp, int req_comp); +STBIDEF stbi_uc *stbi_load_from_memory (stbi_uc const *buffer, int len , int *x, int *y, int *comp, int req_comp); +STBIDEF stbi_uc *stbi_load_from_callbacks(stbi_io_callbacks const *clbk , void *user, int *x, int *y, int *comp, int req_comp); + +#ifndef STBI_NO_STDIO +STBIDEF stbi_uc *stbi_load_from_file (FILE *f, int *x, int *y, int *comp, int req_comp); +// for stbi_load_from_file, file pointer is left pointing immediately after image +#endif + +#ifndef STBI_NO_LINEAR + STBIDEF float *stbi_loadf (char const *filename, int *x, int *y, int *comp, int req_comp); + STBIDEF float *stbi_loadf_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp); + STBIDEF float *stbi_loadf_from_callbacks (stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp); + + #ifndef STBI_NO_STDIO + STBIDEF float *stbi_loadf_from_file (FILE *f, int *x, int *y, int *comp, int req_comp); + #endif +#endif + +#ifndef STBI_NO_HDR + STBIDEF void stbi_hdr_to_ldr_gamma(float gamma); + STBIDEF void stbi_hdr_to_ldr_scale(float scale); +#endif + +#ifndef STBI_NO_LINEAR + STBIDEF void stbi_ldr_to_hdr_gamma(float gamma); + STBIDEF void stbi_ldr_to_hdr_scale(float scale); +#endif // STBI_NO_HDR + +// stbi_is_hdr is always defined, but always returns false if STBI_NO_HDR +STBIDEF int stbi_is_hdr_from_callbacks(stbi_io_callbacks const *clbk, void *user); +STBIDEF int stbi_is_hdr_from_memory(stbi_uc const *buffer, int len); +#ifndef STBI_NO_STDIO +STBIDEF int stbi_is_hdr (char const *filename); +STBIDEF int stbi_is_hdr_from_file(FILE *f); +#endif // STBI_NO_STDIO + + +// get a VERY brief reason for failure +// NOT THREADSAFE +STBIDEF const char *stbi_failure_reason (void); + +// free the loaded image -- this is just free() +STBIDEF void stbi_image_free (void *retval_from_stbi_load); + +// get image dimensions & components without fully decoding +STBIDEF int stbi_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp); +STBIDEF int stbi_info_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp); + +#ifndef STBI_NO_STDIO +STBIDEF int stbi_info (char const *filename, int *x, int *y, int *comp); +STBIDEF int stbi_info_from_file (FILE *f, int *x, int *y, int *comp); + +#endif + + + +// for image formats that explicitly notate that they have premultiplied alpha, +// we just return the colors as stored in the file. set this flag to force +// unpremultiplication. results are undefined if the unpremultiply overflow. +STBIDEF void stbi_set_unpremultiply_on_load(int flag_true_if_should_unpremultiply); + +// indicate whether we should process iphone images back to canonical format, +// or just pass them through "as-is" +STBIDEF void stbi_convert_iphone_png_to_rgb(int flag_true_if_should_convert); + +// flip the image vertically, so the first pixel in the output array is the bottom left +STBIDEF void stbi_set_flip_vertically_on_load(int flag_true_if_should_flip); + +// ZLIB client - used by PNG, available for other purposes + +STBIDEF char *stbi_zlib_decode_malloc_guesssize(const char *buffer, int len, int initial_size, int *outlen); +STBIDEF char *stbi_zlib_decode_malloc_guesssize_headerflag(const char *buffer, int len, int initial_size, int *outlen, int parse_header); +STBIDEF char *stbi_zlib_decode_malloc(const char *buffer, int len, int *outlen); +STBIDEF int stbi_zlib_decode_buffer(char *obuffer, int olen, const char *ibuffer, int ilen); + +STBIDEF char *stbi_zlib_decode_noheader_malloc(const char *buffer, int len, int *outlen); +STBIDEF int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const char *ibuffer, int ilen); + + +#ifdef __cplusplus +} +#endif + +// +// +//// end header file ///////////////////////////////////////////////////// +#endif // STBI_INCLUDE_STB_IMAGE_H + +#ifdef STB_IMAGE_IMPLEMENTATION + +#if defined(STBI_ONLY_JPEG) || defined(STBI_ONLY_PNG) || defined(STBI_ONLY_BMP) \ + || defined(STBI_ONLY_TGA) || defined(STBI_ONLY_GIF) || defined(STBI_ONLY_PSD) \ + || defined(STBI_ONLY_HDR) || defined(STBI_ONLY_PIC) || defined(STBI_ONLY_PNM) \ + || defined(STBI_ONLY_ZLIB) + #ifndef STBI_ONLY_JPEG + #define STBI_NO_JPEG + #endif + #ifndef STBI_ONLY_PNG + #define STBI_NO_PNG + #endif + #ifndef STBI_ONLY_BMP + #define STBI_NO_BMP + #endif + #ifndef STBI_ONLY_PSD + #define STBI_NO_PSD + #endif + #ifndef STBI_ONLY_TGA + #define STBI_NO_TGA + #endif + #ifndef STBI_ONLY_GIF + #define STBI_NO_GIF + #endif + #ifndef STBI_ONLY_HDR + #define STBI_NO_HDR + #endif + #ifndef STBI_ONLY_PIC + #define STBI_NO_PIC + #endif + #ifndef STBI_ONLY_PNM + #define STBI_NO_PNM + #endif +#endif + +#if defined(STBI_NO_PNG) && !defined(STBI_SUPPORT_ZLIB) && !defined(STBI_NO_ZLIB) +#define STBI_NO_ZLIB +#endif + + +#include +#include // ptrdiff_t on osx +#include +#include + +#if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR) +#include // ldexp +#endif + +#ifndef STBI_NO_STDIO +#include +#endif + +#ifndef STBI_ASSERT +#include +#define STBI_ASSERT(x) assert(x) +#endif + + +#ifndef _MSC_VER + #ifdef __cplusplus + #define stbi_inline inline + #else + #define stbi_inline + #endif +#else + #define stbi_inline __forceinline +#endif + + +#ifdef _MSC_VER +typedef unsigned short stbi__uint16; +typedef signed short stbi__int16; +typedef unsigned int stbi__uint32; +typedef signed int stbi__int32; +#else +#include +typedef uint16_t stbi__uint16; +typedef int16_t stbi__int16; +typedef uint32_t stbi__uint32; +typedef int32_t stbi__int32; +#endif + +// should produce compiler error if size is wrong +typedef unsigned char validate_uint32[sizeof(stbi__uint32)==4 ? 1 : -1]; + +#ifdef _MSC_VER +#define STBI_NOTUSED(v) (void)(v) +#else +#define STBI_NOTUSED(v) (void)sizeof(v) +#endif + +#ifdef _MSC_VER +#define STBI_HAS_LROTL +#endif + +#ifdef STBI_HAS_LROTL + #define stbi_lrot(x,y) _lrotl(x,y) +#else + #define stbi_lrot(x,y) (((x) << (y)) | ((x) >> (32 - (y)))) +#endif + +#if defined(STBI_MALLOC) && defined(STBI_FREE) && defined(STBI_REALLOC) +// ok +#elif !defined(STBI_MALLOC) && !defined(STBI_FREE) && !defined(STBI_REALLOC) +// ok +#else +#error "Must define all or none of STBI_MALLOC, STBI_FREE, and STBI_REALLOC." +#endif + +#ifndef STBI_MALLOC +#define STBI_MALLOC(sz) malloc(sz) +#define STBI_REALLOC(p,sz) realloc(p,sz) +#define STBI_FREE(p) free(p) +#endif + +// x86/x64 detection +#if defined(__x86_64__) || defined(_M_X64) +#define STBI__X64_TARGET +#elif defined(__i386) || defined(_M_IX86) +#define STBI__X86_TARGET +#endif + +#if defined(__GNUC__) && (defined(STBI__X86_TARGET) || defined(STBI__X64_TARGET)) && !defined(__SSE2__) && !defined(STBI_NO_SIMD) +// NOTE: not clear do we actually need this for the 64-bit path? +// gcc doesn't support sse2 intrinsics unless you compile with -msse2, +// (but compiling with -msse2 allows the compiler to use SSE2 everywhere; +// this is just broken and gcc are jerks for not fixing it properly +// http://www.virtualdub.org/blog/pivot/entry.php?id=363 ) +#define STBI_NO_SIMD +#endif + +#if defined(__MINGW32__) && defined(STBI__X86_TARGET) && !defined(STBI_MINGW_ENABLE_SSE2) && !defined(STBI_NO_SIMD) +// Note that __MINGW32__ doesn't actually mean 32-bit, so we have to avoid STBI__X64_TARGET +// +// 32-bit MinGW wants ESP to be 16-byte aligned, but this is not in the +// Windows ABI and VC++ as well as Windows DLLs don't maintain that invariant. +// As a result, enabling SSE2 on 32-bit MinGW is dangerous when not +// simultaneously enabling "-mstackrealign". +// +// See https://github.com/nothings/stb/issues/81 for more information. +// +// So default to no SSE2 on 32-bit MinGW. If you've read this far and added +// -mstackrealign to your build settings, feel free to #define STBI_MINGW_ENABLE_SSE2. +#define STBI_NO_SIMD +#endif + +#if !defined(STBI_NO_SIMD) && defined(STBI__X86_TARGET) +#define STBI_SSE2 +#include + +#ifdef _MSC_VER + +#if _MSC_VER >= 1400 // not VC6 +#include // __cpuid +static int stbi__cpuid3(void) +{ + int info[4]; + __cpuid(info,1); + return info[3]; +} +#else +static int stbi__cpuid3(void) +{ + int res; + __asm { + mov eax,1 + cpuid + mov res,edx + } + return res; +} +#endif + +#define STBI_SIMD_ALIGN(type, name) __declspec(align(16)) type name + +static int stbi__sse2_available() +{ + int info3 = stbi__cpuid3(); + return ((info3 >> 26) & 1) != 0; +} +#else // assume GCC-style if not VC++ +#define STBI_SIMD_ALIGN(type, name) type name __attribute__((aligned(16))) + +static int stbi__sse2_available() +{ +#if defined(__GNUC__) && (__GNUC__ * 100 + __GNUC_MINOR__) >= 408 // GCC 4.8 or later + // GCC 4.8+ has a nice way to do this + return __builtin_cpu_supports("sse2"); +#else + // portable way to do this, preferably without using GCC inline ASM? + // just bail for now. + return 0; +#endif +} +#endif +#endif + +// ARM NEON +#if defined(STBI_NO_SIMD) && defined(STBI_NEON) +#undef STBI_NEON +#endif + +#ifdef STBI_NEON +#include +// assume GCC or Clang on ARM targets +#define STBI_SIMD_ALIGN(type, name) type name __attribute__((aligned(16))) +#endif + +#ifndef STBI_SIMD_ALIGN +#define STBI_SIMD_ALIGN(type, name) type name +#endif + +/////////////////////////////////////////////// +// +// stbi__context struct and start_xxx functions + +// stbi__context structure is our basic context used by all images, so it +// contains all the IO context, plus some basic image information +typedef struct +{ + stbi__uint32 img_x, img_y; + int img_n, img_out_n; + + stbi_io_callbacks io; + void *io_user_data; + + int read_from_callbacks; + int buflen; + stbi_uc buffer_start[128]; + + stbi_uc *img_buffer, *img_buffer_end; + stbi_uc *img_buffer_original; +} stbi__context; + + +static void stbi__refill_buffer(stbi__context *s); + +// initialize a memory-decode context +static void stbi__start_mem(stbi__context *s, stbi_uc const *buffer, int len) +{ + s->io.read = NULL; + s->read_from_callbacks = 0; + s->img_buffer = s->img_buffer_original = (stbi_uc *) buffer; + s->img_buffer_end = (stbi_uc *) buffer+len; +} + +// initialize a callback-based context +static void stbi__start_callbacks(stbi__context *s, stbi_io_callbacks *c, void *user) +{ + s->io = *c; + s->io_user_data = user; + s->buflen = sizeof(s->buffer_start); + s->read_from_callbacks = 1; + s->img_buffer_original = s->buffer_start; + stbi__refill_buffer(s); +} + +#ifndef STBI_NO_STDIO + +static int stbi__stdio_read(void *user, char *data, int size) +{ + return (int) fread(data,1,size,(FILE*) user); +} + +static void stbi__stdio_skip(void *user, int n) +{ + fseek((FILE*) user, n, SEEK_CUR); +} + +static int stbi__stdio_eof(void *user) +{ + return feof((FILE*) user); +} + +static stbi_io_callbacks stbi__stdio_callbacks = +{ + stbi__stdio_read, + stbi__stdio_skip, + stbi__stdio_eof, +}; + +static void stbi__start_file(stbi__context *s, FILE *f) +{ + stbi__start_callbacks(s, &stbi__stdio_callbacks, (void *) f); +} + +//static void stop_file(stbi__context *s) { } + +#endif // !STBI_NO_STDIO + +static void stbi__rewind(stbi__context *s) +{ + // conceptually rewind SHOULD rewind to the beginning of the stream, + // but we just rewind to the beginning of the initial buffer, because + // we only use it after doing 'test', which only ever looks at at most 92 bytes + s->img_buffer = s->img_buffer_original; +} + +#ifndef STBI_NO_JPEG +static int stbi__jpeg_test(stbi__context *s); +static stbi_uc *stbi__jpeg_load(stbi__context *s, int *x, int *y, int *comp, int req_comp); +static int stbi__jpeg_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_PNG +static int stbi__png_test(stbi__context *s); +static stbi_uc *stbi__png_load(stbi__context *s, int *x, int *y, int *comp, int req_comp); +static int stbi__png_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_BMP +static int stbi__bmp_test(stbi__context *s); +static stbi_uc *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req_comp); +static int stbi__bmp_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_TGA +static int stbi__tga_test(stbi__context *s); +static stbi_uc *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int req_comp); +static int stbi__tga_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_PSD +static int stbi__psd_test(stbi__context *s); +static stbi_uc *stbi__psd_load(stbi__context *s, int *x, int *y, int *comp, int req_comp); +static int stbi__psd_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_HDR +static int stbi__hdr_test(stbi__context *s); +static float *stbi__hdr_load(stbi__context *s, int *x, int *y, int *comp, int req_comp); +static int stbi__hdr_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_PIC +static int stbi__pic_test(stbi__context *s); +static stbi_uc *stbi__pic_load(stbi__context *s, int *x, int *y, int *comp, int req_comp); +static int stbi__pic_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_GIF +static int stbi__gif_test(stbi__context *s); +static stbi_uc *stbi__gif_load(stbi__context *s, int *x, int *y, int *comp, int req_comp); +static int stbi__gif_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_PNM +static int stbi__pnm_test(stbi__context *s); +static stbi_uc *stbi__pnm_load(stbi__context *s, int *x, int *y, int *comp, int req_comp); +static int stbi__pnm_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +// this is not threadsafe +static const char *stbi__g_failure_reason; + +STBIDEF const char *stbi_failure_reason(void) +{ + return stbi__g_failure_reason; +} + +static int stbi__err(const char *str) +{ + stbi__g_failure_reason = str; + return 0; +} + +static void *stbi__malloc(size_t size) +{ + return STBI_MALLOC(size); +} + +// stbi__err - error +// stbi__errpf - error returning pointer to float +// stbi__errpuc - error returning pointer to unsigned char + +#ifdef STBI_NO_FAILURE_STRINGS + #define stbi__err(x,y) 0 +#elif defined(STBI_FAILURE_USERMSG) + #define stbi__err(x,y) stbi__err(y) +#else + #define stbi__err(x,y) stbi__err(x) +#endif + +#define stbi__errpf(x,y) ((float *) (stbi__err(x,y)?NULL:NULL)) +#define stbi__errpuc(x,y) ((unsigned char *) (stbi__err(x,y)?NULL:NULL)) + +STBIDEF void stbi_image_free(void *retval_from_stbi_load) +{ + STBI_FREE(retval_from_stbi_load); +} + +#ifndef STBI_NO_LINEAR +static float *stbi__ldr_to_hdr(stbi_uc *data, int x, int y, int comp); +#endif + +#ifndef STBI_NO_HDR +static stbi_uc *stbi__hdr_to_ldr(float *data, int x, int y, int comp); +#endif + +static int stbi__vertically_flip_on_load = 1; + +STBIDEF void stbi_set_flip_vertically_on_load(int flag_true_if_should_flip) +{ + stbi__vertically_flip_on_load = flag_true_if_should_flip; +} + +static unsigned char *stbi__load_main(stbi__context *s, int *x, int *y, int *comp, int req_comp) +{ + #ifndef STBI_NO_JPEG + if (stbi__jpeg_test(s)) return stbi__jpeg_load(s,x,y,comp,req_comp); + #endif + #ifndef STBI_NO_PNG + if (stbi__png_test(s)) return stbi__png_load(s,x,y,comp,req_comp); + #endif + #ifndef STBI_NO_BMP + if (stbi__bmp_test(s)) return stbi__bmp_load(s,x,y,comp,req_comp); + #endif + #ifndef STBI_NO_GIF + if (stbi__gif_test(s)) return stbi__gif_load(s,x,y,comp,req_comp); + #endif + #ifndef STBI_NO_PSD + if (stbi__psd_test(s)) return stbi__psd_load(s,x,y,comp,req_comp); + #endif + #ifndef STBI_NO_PIC + if (stbi__pic_test(s)) return stbi__pic_load(s,x,y,comp,req_comp); + #endif + #ifndef STBI_NO_PNM + if (stbi__pnm_test(s)) return stbi__pnm_load(s,x,y,comp,req_comp); + #endif + + #ifndef STBI_NO_HDR + if (stbi__hdr_test(s)) { + float *hdr = stbi__hdr_load(s, x,y,comp,req_comp); + return stbi__hdr_to_ldr(hdr, *x, *y, req_comp ? req_comp : *comp); + } + #endif + + #ifndef STBI_NO_TGA + // test tga last because it's a crappy test! + if (stbi__tga_test(s)) + return stbi__tga_load(s,x,y,comp,req_comp); + #endif + + return stbi__errpuc("unknown image type", "Image not of any known type, or corrupt"); +} + +static unsigned char *stbi__load_flip(stbi__context *s, int *x, int *y, int *comp, int req_comp) +{ + unsigned char *result = stbi__load_main(s, x, y, comp, req_comp); + + if (stbi__vertically_flip_on_load && result != NULL) { + int w = *x, h = *y; + int depth = req_comp ? req_comp : *comp; + int row,col,z; + stbi_uc temp; + + // @OPTIMIZE: use a bigger temp buffer and memcpy multiple pixels at once + for (row = 0; row < (h>>1); row++) { + for (col = 0; col < w; col++) { + for (z = 0; z < depth; z++) { + temp = result[(row * w + col) * depth + z]; + result[(row * w + col) * depth + z] = result[((h - row - 1) * w + col) * depth + z]; + result[((h - row - 1) * w + col) * depth + z] = temp; + } + } + } + } + + return result; +} + +static void stbi__float_postprocess(float *result, int *x, int *y, int *comp, int req_comp) +{ + if (stbi__vertically_flip_on_load && result != NULL) { + int w = *x, h = *y; + int depth = req_comp ? req_comp : *comp; + int row,col,z; + float temp; + + // @OPTIMIZE: use a bigger temp buffer and memcpy multiple pixels at once + for (row = 0; row < (h>>1); row++) { + for (col = 0; col < w; col++) { + for (z = 0; z < depth; z++) { + temp = result[(row * w + col) * depth + z]; + result[(row * w + col) * depth + z] = result[((h - row - 1) * w + col) * depth + z]; + result[((h - row - 1) * w + col) * depth + z] = temp; + } + } + } + } +} + + +#ifndef STBI_NO_STDIO + +static FILE *stbi__fopen(char const *filename, char const *mode) +{ + FILE *f; +#if defined(_MSC_VER) && _MSC_VER >= 1400 + if (0 != fopen_s(&f, filename, mode)) + f=0; +#else + f = fopen(filename, mode); +#endif + return f; +} + + +STBIDEF stbi_uc *stbi_load(char const *filename, int *x, int *y, int *comp, int req_comp) +{ + FILE *f = stbi__fopen(filename, "rb"); + unsigned char *result; + if (!f) return stbi__errpuc("can't fopen", "Unable to open file"); + result = stbi_load_from_file(f,x,y,comp,req_comp); + fclose(f); + return result; +} + +STBIDEF stbi_uc *stbi_load_from_file(FILE *f, int *x, int *y, int *comp, int req_comp) +{ + unsigned char *result; + stbi__context s; + stbi__start_file(&s,f); + result = stbi__load_flip(&s,x,y,comp,req_comp); + if (result) { + // need to 'unget' all the characters in the IO buffer + fseek(f, - (int) (s.img_buffer_end - s.img_buffer), SEEK_CUR); + } + return result; +} +#endif //!STBI_NO_STDIO + +STBIDEF stbi_uc *stbi_load_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp) +{ + stbi__context s; + stbi__start_mem(&s,buffer,len); + return stbi__load_flip(&s,x,y,comp,req_comp); +} + +STBIDEF stbi_uc *stbi_load_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp) +{ + stbi__context s; + stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user); + return stbi__load_flip(&s,x,y,comp,req_comp); +} + +#ifndef STBI_NO_LINEAR +static float *stbi__loadf_main(stbi__context *s, int *x, int *y, int *comp, int req_comp) +{ + unsigned char *data; + #ifndef STBI_NO_HDR + if (stbi__hdr_test(s)) { + float *hdr_data = stbi__hdr_load(s,x,y,comp,req_comp); + if (hdr_data) + stbi__float_postprocess(hdr_data,x,y,comp,req_comp); + return hdr_data; + } + #endif + data = stbi__load_flip(s, x, y, comp, req_comp); + if (data) + return stbi__ldr_to_hdr(data, *x, *y, req_comp ? req_comp : *comp); + return stbi__errpf("unknown image type", "Image not of any known type, or corrupt"); +} + +STBIDEF float *stbi_loadf_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp) +{ + stbi__context s; + stbi__start_mem(&s,buffer,len); + return stbi__loadf_main(&s,x,y,comp,req_comp); +} + +STBIDEF float *stbi_loadf_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp) +{ + stbi__context s; + stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user); + return stbi__loadf_main(&s,x,y,comp,req_comp); +} + +#ifndef STBI_NO_STDIO +STBIDEF float *stbi_loadf(char const *filename, int *x, int *y, int *comp, int req_comp) +{ + float *result; + FILE *f = stbi__fopen(filename, "rb"); + if (!f) return stbi__errpf("can't fopen", "Unable to open file"); + result = stbi_loadf_from_file(f,x,y,comp,req_comp); + fclose(f); + return result; +} + +STBIDEF float *stbi_loadf_from_file(FILE *f, int *x, int *y, int *comp, int req_comp) +{ + stbi__context s; + stbi__start_file(&s,f); + return stbi__loadf_main(&s,x,y,comp,req_comp); +} +#endif // !STBI_NO_STDIO + +#endif // !STBI_NO_LINEAR + +// these is-hdr-or-not is defined independent of whether STBI_NO_LINEAR is +// defined, for API simplicity; if STBI_NO_LINEAR is defined, it always +// reports false! + +STBIDEF int stbi_is_hdr_from_memory(stbi_uc const *buffer, int len) +{ + #ifndef STBI_NO_HDR + stbi__context s; + stbi__start_mem(&s,buffer,len); + return stbi__hdr_test(&s); + #else + STBI_NOTUSED(buffer); + STBI_NOTUSED(len); + return 0; + #endif +} + +#ifndef STBI_NO_STDIO +STBIDEF int stbi_is_hdr (char const *filename) +{ + FILE *f = stbi__fopen(filename, "rb"); + int result=0; + if (f) { + result = stbi_is_hdr_from_file(f); + fclose(f); + } + return result; +} + +STBIDEF int stbi_is_hdr_from_file(FILE *f) +{ + #ifndef STBI_NO_HDR + stbi__context s; + stbi__start_file(&s,f); + return stbi__hdr_test(&s); + #else + return 0; + #endif +} +#endif // !STBI_NO_STDIO + +STBIDEF int stbi_is_hdr_from_callbacks(stbi_io_callbacks const *clbk, void *user) +{ + #ifndef STBI_NO_HDR + stbi__context s; + stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user); + return stbi__hdr_test(&s); + #else + return 0; + #endif +} + +static float stbi__h2l_gamma_i=1.0f/2.2f, stbi__h2l_scale_i=1.0f; +static float stbi__l2h_gamma=2.2f, stbi__l2h_scale=1.0f; + +#ifndef STBI_NO_LINEAR +STBIDEF void stbi_ldr_to_hdr_gamma(float gamma) { stbi__l2h_gamma = gamma; } +STBIDEF void stbi_ldr_to_hdr_scale(float scale) { stbi__l2h_scale = scale; } +#endif + +STBIDEF void stbi_hdr_to_ldr_gamma(float gamma) { stbi__h2l_gamma_i = 1/gamma; } +STBIDEF void stbi_hdr_to_ldr_scale(float scale) { stbi__h2l_scale_i = 1/scale; } + + +////////////////////////////////////////////////////////////////////////////// +// +// Common code used by all image loaders +// + +enum +{ + STBI__SCAN_load=0, + STBI__SCAN_type, + STBI__SCAN_header +}; + +static void stbi__refill_buffer(stbi__context *s) +{ + int n = (s->io.read)(s->io_user_data,(char*)s->buffer_start,s->buflen); + if (n == 0) { + // at end of file, treat same as if from memory, but need to handle case + // where s->img_buffer isn't pointing to safe memory, e.g. 0-byte file + s->read_from_callbacks = 0; + s->img_buffer = s->buffer_start; + s->img_buffer_end = s->buffer_start+1; + *s->img_buffer = 0; + } else { + s->img_buffer = s->buffer_start; + s->img_buffer_end = s->buffer_start + n; + } +} + +stbi_inline static stbi_uc stbi__get8(stbi__context *s) +{ + if (s->img_buffer < s->img_buffer_end) + return *s->img_buffer++; + if (s->read_from_callbacks) { + stbi__refill_buffer(s); + return *s->img_buffer++; + } + return 0; +} + +stbi_inline static int stbi__at_eof(stbi__context *s) +{ + if (s->io.read) { + if (!(s->io.eof)(s->io_user_data)) return 0; + // if feof() is true, check if buffer = end + // special case: we've only got the special 0 character at the end + if (s->read_from_callbacks == 0) return 1; + } + + return s->img_buffer >= s->img_buffer_end; +} + +static void stbi__skip(stbi__context *s, int n) +{ + if (n < 0) { + s->img_buffer = s->img_buffer_end; + return; + } + if (s->io.read) { + int blen = (int) (s->img_buffer_end - s->img_buffer); + if (blen < n) { + s->img_buffer = s->img_buffer_end; + (s->io.skip)(s->io_user_data, n - blen); + return; + } + } + s->img_buffer += n; +} + +static int stbi__getn(stbi__context *s, stbi_uc *buffer, int n) +{ + if (s->io.read) { + int blen = (int) (s->img_buffer_end - s->img_buffer); + if (blen < n) { + int res, count; + + memcpy(buffer, s->img_buffer, blen); + + count = (s->io.read)(s->io_user_data, (char*) buffer + blen, n - blen); + res = (count == (n-blen)); + s->img_buffer = s->img_buffer_end; + return res; + } + } + + if (s->img_buffer+n <= s->img_buffer_end) { + memcpy(buffer, s->img_buffer, n); + s->img_buffer += n; + return 1; + } else + return 0; +} + +static int stbi__get16be(stbi__context *s) +{ + int z = stbi__get8(s); + return (z << 8) + stbi__get8(s); +} + +static stbi__uint32 stbi__get32be(stbi__context *s) +{ + stbi__uint32 z = stbi__get16be(s); + return (z << 16) + stbi__get16be(s); +} + +static int stbi__get16le(stbi__context *s) +{ + int z = stbi__get8(s); + return z + (stbi__get8(s) << 8); +} + +static stbi__uint32 stbi__get32le(stbi__context *s) +{ + stbi__uint32 z = stbi__get16le(s); + return z + (stbi__get16le(s) << 16); +} + +#define STBI__BYTECAST(x) ((stbi_uc) ((x) & 255)) // truncate int to byte without warnings + + +////////////////////////////////////////////////////////////////////////////// +// +// generic converter from built-in img_n to req_comp +// individual types do this automatically as much as possible (e.g. jpeg +// does all cases internally since it needs to colorspace convert anyway, +// and it never has alpha, so very few cases ). png can automatically +// interleave an alpha=255 channel, but falls back to this for other cases +// +// assume data buffer is malloced, so malloc a new one and free that one +// only failure mode is malloc failing + +static stbi_uc stbi__compute_y(int r, int g, int b) +{ + return (stbi_uc) (((r*77) + (g*150) + (29*b)) >> 8); +} + +static unsigned char *stbi__convert_format(unsigned char *data, int img_n, int req_comp, unsigned int x, unsigned int y) +{ + int i,j; + unsigned char *good; + + if (req_comp == img_n) return data; + STBI_ASSERT(req_comp >= 1 && req_comp <= 4); + + good = (unsigned char *) stbi__malloc(req_comp * x * y); + if (good == NULL) { + STBI_FREE(data); + return stbi__errpuc("outofmem", "Out of memory"); + } + + for (j=0; j < (int) y; ++j) { + unsigned char *src = data + j * x * img_n ; + unsigned char *dest = good + j * x * req_comp; + + #define COMBO(a,b) ((a)*8+(b)) + #define CASE(a,b) case COMBO(a,b): for(i=x-1; i >= 0; --i, src += a, dest += b) + // convert source image with img_n components to one with req_comp components; + // avoid switch per pixel, so use switch per scanline and massive macros + switch (COMBO(img_n, req_comp)) { + CASE(1,2) dest[0]=src[0], dest[1]=255; break; + CASE(1,3) dest[0]=dest[1]=dest[2]=src[0]; break; + CASE(1,4) dest[0]=dest[1]=dest[2]=src[0], dest[3]=255; break; + CASE(2,1) dest[0]=src[0]; break; + CASE(2,3) dest[0]=dest[1]=dest[2]=src[0]; break; + CASE(2,4) dest[0]=dest[1]=dest[2]=src[0], dest[3]=src[1]; break; + CASE(3,4) dest[0]=src[0],dest[1]=src[1],dest[2]=src[2],dest[3]=255; break; + CASE(3,1) dest[0]=stbi__compute_y(src[0],src[1],src[2]); break; + CASE(3,2) dest[0]=stbi__compute_y(src[0],src[1],src[2]), dest[1] = 255; break; + CASE(4,1) dest[0]=stbi__compute_y(src[0],src[1],src[2]); break; + CASE(4,2) dest[0]=stbi__compute_y(src[0],src[1],src[2]), dest[1] = src[3]; break; + CASE(4,3) dest[0]=src[0],dest[1]=src[1],dest[2]=src[2]; break; + default: STBI_ASSERT(0); + } + #undef CASE + } + + STBI_FREE(data); + return good; +} + +#ifndef STBI_NO_LINEAR +static float *stbi__ldr_to_hdr(stbi_uc *data, int x, int y, int comp) +{ + int i,k,n; + float *output = (float *) stbi__malloc(x * y * comp * sizeof(float)); + if (output == NULL) { STBI_FREE(data); return stbi__errpf("outofmem", "Out of memory"); } + // compute number of non-alpha components + if (comp & 1) n = comp; else n = comp-1; + for (i=0; i < x*y; ++i) { + for (k=0; k < n; ++k) { + output[i*comp + k] = (float) (pow(data[i*comp+k]/255.0f, stbi__l2h_gamma) * stbi__l2h_scale); + } + if (k < comp) output[i*comp + k] = data[i*comp+k]/255.0f; + } + STBI_FREE(data); + return output; +} +#endif + +#ifndef STBI_NO_HDR +#define stbi__float2int(x) ((int) (x)) +static stbi_uc *stbi__hdr_to_ldr(float *data, int x, int y, int comp) +{ + int i,k,n; + stbi_uc *output = (stbi_uc *) stbi__malloc(x * y * comp); + if (output == NULL) { STBI_FREE(data); return stbi__errpuc("outofmem", "Out of memory"); } + // compute number of non-alpha components + if (comp & 1) n = comp; else n = comp-1; + for (i=0; i < x*y; ++i) { + for (k=0; k < n; ++k) { + float z = (float) pow(data[i*comp+k]*stbi__h2l_scale_i, stbi__h2l_gamma_i) * 255 + 0.5f; + if (z < 0) z = 0; + if (z > 255) z = 255; + output[i*comp + k] = (stbi_uc) stbi__float2int(z); + } + if (k < comp) { + float z = data[i*comp+k] * 255 + 0.5f; + if (z < 0) z = 0; + if (z > 255) z = 255; + output[i*comp + k] = (stbi_uc) stbi__float2int(z); + } + } + STBI_FREE(data); + return output; +} +#endif + +////////////////////////////////////////////////////////////////////////////// +// +// "baseline" JPEG/JFIF decoder +// +// simple implementation +// - doesn't support delayed output of y-dimension +// - simple interface (only one output format: 8-bit interleaved RGB) +// - doesn't try to recover corrupt jpegs +// - doesn't allow partial loading, loading multiple at once +// - still fast on x86 (copying globals into locals doesn't help x86) +// - allocates lots of intermediate memory (full size of all components) +// - non-interleaved case requires this anyway +// - allows good upsampling (see next) +// high-quality +// - upsampled channels are bilinearly interpolated, even across blocks +// - quality integer IDCT derived from IJG's 'slow' +// performance +// - fast huffman; reasonable integer IDCT +// - some SIMD kernels for common paths on targets with SSE2/NEON +// - uses a lot of intermediate memory, could cache poorly + +#ifndef STBI_NO_JPEG + +// huffman decoding acceleration +#define FAST_BITS 9 // larger handles more cases; smaller stomps less cache + +typedef struct +{ + stbi_uc fast[1 << FAST_BITS]; + // weirdly, repacking this into AoS is a 10% speed loss, instead of a win + stbi__uint16 code[256]; + stbi_uc values[256]; + stbi_uc size[257]; + unsigned int maxcode[18]; + int delta[17]; // old 'firstsymbol' - old 'firstcode' +} stbi__huffman; + +typedef struct +{ + stbi__context *s; + stbi__huffman huff_dc[4]; + stbi__huffman huff_ac[4]; + stbi_uc dequant[4][64]; + stbi__int16 fast_ac[4][1 << FAST_BITS]; + +// sizes for components, interleaved MCUs + int img_h_max, img_v_max; + int img_mcu_x, img_mcu_y; + int img_mcu_w, img_mcu_h; + +// definition of jpeg image component + struct + { + int id; + int h,v; + int tq; + int hd,ha; + int dc_pred; + + int x,y,w2,h2; + stbi_uc *data; + void *raw_data, *raw_coeff; + stbi_uc *linebuf; + short *coeff; // progressive only + int coeff_w, coeff_h; // number of 8x8 coefficient blocks + } img_comp[4]; + + stbi__uint32 code_buffer; // jpeg entropy-coded buffer + int code_bits; // number of valid bits + unsigned char marker; // marker seen while filling entropy buffer + int nomore; // flag if we saw a marker so must stop + + int progressive; + int spec_start; + int spec_end; + int succ_high; + int succ_low; + int eob_run; + + int scan_n, order[4]; + int restart_interval, todo; + +// kernels + void (*idct_block_kernel)(stbi_uc *out, int out_stride, short data[64]); + void (*YCbCr_to_RGB_kernel)(stbi_uc *out, const stbi_uc *y, const stbi_uc *pcb, const stbi_uc *pcr, int count, int step); + stbi_uc *(*resample_row_hv_2_kernel)(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs); +} stbi__jpeg; + +static int stbi__build_huffman(stbi__huffman *h, int *count) +{ + int i,j,k=0,code; + // build size list for each symbol (from JPEG spec) + for (i=0; i < 16; ++i) + for (j=0; j < count[i]; ++j) + h->size[k++] = (stbi_uc) (i+1); + h->size[k] = 0; + + // compute actual symbols (from jpeg spec) + code = 0; + k = 0; + for(j=1; j <= 16; ++j) { + // compute delta to add to code to compute symbol id + h->delta[j] = k - code; + if (h->size[k] == j) { + while (h->size[k] == j) + h->code[k++] = (stbi__uint16) (code++); + if (code-1 >= (1 << j)) return stbi__err("bad code lengths","Corrupt JPEG"); + } + // compute largest code + 1 for this size, preshifted as needed later + h->maxcode[j] = code << (16-j); + code <<= 1; + } + h->maxcode[j] = 0xffffffff; + + // build non-spec acceleration table; 255 is flag for not-accelerated + memset(h->fast, 255, 1 << FAST_BITS); + for (i=0; i < k; ++i) { + int s = h->size[i]; + if (s <= FAST_BITS) { + int c = h->code[i] << (FAST_BITS-s); + int m = 1 << (FAST_BITS-s); + for (j=0; j < m; ++j) { + h->fast[c+j] = (stbi_uc) i; + } + } + } + return 1; +} + +// build a table that decodes both magnitude and value of small ACs in +// one go. +static void stbi__build_fast_ac(stbi__int16 *fast_ac, stbi__huffman *h) +{ + int i; + for (i=0; i < (1 << FAST_BITS); ++i) { + stbi_uc fast = h->fast[i]; + fast_ac[i] = 0; + if (fast < 255) { + int rs = h->values[fast]; + int run = (rs >> 4) & 15; + int magbits = rs & 15; + int len = h->size[fast]; + + if (magbits && len + magbits <= FAST_BITS) { + // magnitude code followed by receive_extend code + int k = ((i << len) & ((1 << FAST_BITS) - 1)) >> (FAST_BITS - magbits); + int m = 1 << (magbits - 1); + if (k < m) k += (-1 << magbits) + 1; + // if the result is small enough, we can fit it in fast_ac table + if (k >= -128 && k <= 127) + fast_ac[i] = (stbi__int16) ((k << 8) + (run << 4) + (len + magbits)); + } + } + } +} + +static void stbi__grow_buffer_unsafe(stbi__jpeg *j) +{ + do { + int b = j->nomore ? 0 : stbi__get8(j->s); + if (b == 0xff) { + int c = stbi__get8(j->s); + if (c != 0) { + j->marker = (unsigned char) c; + j->nomore = 1; + return; + } + } + j->code_buffer |= b << (24 - j->code_bits); + j->code_bits += 8; + } while (j->code_bits <= 24); +} + +// (1 << n) - 1 +static stbi__uint32 stbi__bmask[17]={0,1,3,7,15,31,63,127,255,511,1023,2047,4095,8191,16383,32767,65535}; + +// decode a jpeg huffman value from the bitstream +stbi_inline static int stbi__jpeg_huff_decode(stbi__jpeg *j, stbi__huffman *h) +{ + unsigned int temp; + int c,k; + + if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); + + // look at the top FAST_BITS and determine what symbol ID it is, + // if the code is <= FAST_BITS + c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS)-1); + k = h->fast[c]; + if (k < 255) { + int s = h->size[k]; + if (s > j->code_bits) + return -1; + j->code_buffer <<= s; + j->code_bits -= s; + return h->values[k]; + } + + // naive test is to shift the code_buffer down so k bits are + // valid, then test against maxcode. To speed this up, we've + // preshifted maxcode left so that it has (16-k) 0s at the + // end; in other words, regardless of the number of bits, it + // wants to be compared against something shifted to have 16; + // that way we don't need to shift inside the loop. + temp = j->code_buffer >> 16; + for (k=FAST_BITS+1 ; ; ++k) + if (temp < h->maxcode[k]) + break; + if (k == 17) { + // error! code not found + j->code_bits -= 16; + return -1; + } + + if (k > j->code_bits) + return -1; + + // convert the huffman code to the symbol id + c = ((j->code_buffer >> (32 - k)) & stbi__bmask[k]) + h->delta[k]; + STBI_ASSERT((((j->code_buffer) >> (32 - h->size[c])) & stbi__bmask[h->size[c]]) == h->code[c]); + + // convert the id to a symbol + j->code_bits -= k; + j->code_buffer <<= k; + return h->values[c]; +} + +// bias[n] = (-1<code_bits < n) stbi__grow_buffer_unsafe(j); + + sgn = (stbi__int32)j->code_buffer >> 31; // sign bit is always in MSB + k = stbi_lrot(j->code_buffer, n); + STBI_ASSERT(n >= 0 && n < (int) (sizeof(stbi__bmask)/sizeof(*stbi__bmask))); + j->code_buffer = k & ~stbi__bmask[n]; + k &= stbi__bmask[n]; + j->code_bits -= n; + return k + (stbi__jbias[n] & ~sgn); +} + +// get some unsigned bits +stbi_inline static int stbi__jpeg_get_bits(stbi__jpeg *j, int n) +{ + unsigned int k; + if (j->code_bits < n) stbi__grow_buffer_unsafe(j); + k = stbi_lrot(j->code_buffer, n); + j->code_buffer = k & ~stbi__bmask[n]; + k &= stbi__bmask[n]; + j->code_bits -= n; + return k; +} + +stbi_inline static int stbi__jpeg_get_bit(stbi__jpeg *j) +{ + unsigned int k; + if (j->code_bits < 1) stbi__grow_buffer_unsafe(j); + k = j->code_buffer; + j->code_buffer <<= 1; + --j->code_bits; + return k & 0x80000000; +} + +// given a value that's at position X in the zigzag stream, +// where does it appear in the 8x8 matrix coded as row-major? +static stbi_uc stbi__jpeg_dezigzag[64+15] = +{ + 0, 1, 8, 16, 9, 2, 3, 10, + 17, 24, 32, 25, 18, 11, 4, 5, + 12, 19, 26, 33, 40, 48, 41, 34, + 27, 20, 13, 6, 7, 14, 21, 28, + 35, 42, 49, 56, 57, 50, 43, 36, + 29, 22, 15, 23, 30, 37, 44, 51, + 58, 59, 52, 45, 38, 31, 39, 46, + 53, 60, 61, 54, 47, 55, 62, 63, + // let corrupt input sample past end + 63, 63, 63, 63, 63, 63, 63, 63, + 63, 63, 63, 63, 63, 63, 63 +}; + +// decode one 64-entry block-- +static int stbi__jpeg_decode_block(stbi__jpeg *j, short data[64], stbi__huffman *hdc, stbi__huffman *hac, stbi__int16 *fac, int b, stbi_uc *dequant) +{ + int diff,dc,k; + int t; + + if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); + t = stbi__jpeg_huff_decode(j, hdc); + if (t < 0) return stbi__err("bad huffman code","Corrupt JPEG"); + + // 0 all the ac values now so we can do it 32-bits at a time + memset(data,0,64*sizeof(data[0])); + + diff = t ? stbi__extend_receive(j, t) : 0; + dc = j->img_comp[b].dc_pred + diff; + j->img_comp[b].dc_pred = dc; + data[0] = (short) (dc * dequant[0]); + + // decode AC components, see JPEG spec + k = 1; + do { + unsigned int zig; + int c,r,s; + if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); + c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS)-1); + r = fac[c]; + if (r) { // fast-AC path + k += (r >> 4) & 15; // run + s = r & 15; // combined length + j->code_buffer <<= s; + j->code_bits -= s; + // decode into unzigzag'd location + zig = stbi__jpeg_dezigzag[k++]; + data[zig] = (short) ((r >> 8) * dequant[zig]); + } else { + int rs = stbi__jpeg_huff_decode(j, hac); + if (rs < 0) return stbi__err("bad huffman code","Corrupt JPEG"); + s = rs & 15; + r = rs >> 4; + if (s == 0) { + if (rs != 0xf0) break; // end block + k += 16; + } else { + k += r; + // decode into unzigzag'd location + zig = stbi__jpeg_dezigzag[k++]; + data[zig] = (short) (stbi__extend_receive(j,s) * dequant[zig]); + } + } + } while (k < 64); + return 1; +} + +static int stbi__jpeg_decode_block_prog_dc(stbi__jpeg *j, short data[64], stbi__huffman *hdc, int b) +{ + int diff,dc; + int t; + if (j->spec_end != 0) return stbi__err("can't merge dc and ac", "Corrupt JPEG"); + + if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); + + if (j->succ_high == 0) { + // first scan for DC coefficient, must be first + memset(data,0,64*sizeof(data[0])); // 0 all the ac values now + t = stbi__jpeg_huff_decode(j, hdc); + diff = t ? stbi__extend_receive(j, t) : 0; + + dc = j->img_comp[b].dc_pred + diff; + j->img_comp[b].dc_pred = dc; + data[0] = (short) (dc << j->succ_low); + } else { + // refinement scan for DC coefficient + if (stbi__jpeg_get_bit(j)) + data[0] += (short) (1 << j->succ_low); + } + return 1; +} + +// @OPTIMIZE: store non-zigzagged during the decode passes, +// and only de-zigzag when dequantizing +static int stbi__jpeg_decode_block_prog_ac(stbi__jpeg *j, short data[64], stbi__huffman *hac, stbi__int16 *fac) +{ + int k; + if (j->spec_start == 0) return stbi__err("can't merge dc and ac", "Corrupt JPEG"); + + if (j->succ_high == 0) { + int shift = j->succ_low; + + if (j->eob_run) { + --j->eob_run; + return 1; + } + + k = j->spec_start; + do { + unsigned int zig; + int c,r,s; + if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); + c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS)-1); + r = fac[c]; + if (r) { // fast-AC path + k += (r >> 4) & 15; // run + s = r & 15; // combined length + j->code_buffer <<= s; + j->code_bits -= s; + zig = stbi__jpeg_dezigzag[k++]; + data[zig] = (short) ((r >> 8) << shift); + } else { + int rs = stbi__jpeg_huff_decode(j, hac); + if (rs < 0) return stbi__err("bad huffman code","Corrupt JPEG"); + s = rs & 15; + r = rs >> 4; + if (s == 0) { + if (r < 15) { + j->eob_run = (1 << r); + if (r) + j->eob_run += stbi__jpeg_get_bits(j, r); + --j->eob_run; + break; + } + k += 16; + } else { + k += r; + zig = stbi__jpeg_dezigzag[k++]; + data[zig] = (short) (stbi__extend_receive(j,s) << shift); + } + } + } while (k <= j->spec_end); + } else { + // refinement scan for these AC coefficients + + short bit = (short) (1 << j->succ_low); + + if (j->eob_run) { + --j->eob_run; + for (k = j->spec_start; k <= j->spec_end; ++k) { + short *p = &data[stbi__jpeg_dezigzag[k]]; + if (*p != 0) + if (stbi__jpeg_get_bit(j)) + if ((*p & bit)==0) { + if (*p > 0) + *p += bit; + else + *p -= bit; + } + } + } else { + k = j->spec_start; + do { + int r,s; + int rs = stbi__jpeg_huff_decode(j, hac); // @OPTIMIZE see if we can use the fast path here, advance-by-r is so slow, eh + if (rs < 0) return stbi__err("bad huffman code","Corrupt JPEG"); + s = rs & 15; + r = rs >> 4; + if (s == 0) { + if (r < 15) { + j->eob_run = (1 << r) - 1; + if (r) + j->eob_run += stbi__jpeg_get_bits(j, r); + r = 64; // force end of block + } else { + // r=15 s=0 should write 16 0s, so we just do + // a run of 15 0s and then write s (which is 0), + // so we don't have to do anything special here + } + } else { + if (s != 1) return stbi__err("bad huffman code", "Corrupt JPEG"); + // sign bit + if (stbi__jpeg_get_bit(j)) + s = bit; + else + s = -bit; + } + + // advance by r + while (k <= j->spec_end) { + short *p = &data[stbi__jpeg_dezigzag[k++]]; + if (*p != 0) { + if (stbi__jpeg_get_bit(j)) + if ((*p & bit)==0) { + if (*p > 0) + *p += bit; + else + *p -= bit; + } + } else { + if (r == 0) { + *p = (short) s; + break; + } + --r; + } + } + } while (k <= j->spec_end); + } + } + return 1; +} + +// take a -128..127 value and stbi__clamp it and convert to 0..255 +stbi_inline static stbi_uc stbi__clamp(int x) +{ + // trick to use a single test to catch both cases + if ((unsigned int) x > 255) { + if (x < 0) return 0; + if (x > 255) return 255; + } + return (stbi_uc) x; +} + +#define stbi__f2f(x) ((int) (((x) * 4096 + 0.5))) +#define stbi__fsh(x) ((x) << 12) + +// derived from jidctint -- DCT_ISLOW +#define STBI__IDCT_1D(s0,s1,s2,s3,s4,s5,s6,s7) \ + int t0,t1,t2,t3,p1,p2,p3,p4,p5,x0,x1,x2,x3; \ + p2 = s2; \ + p3 = s6; \ + p1 = (p2+p3) * stbi__f2f(0.5411961f); \ + t2 = p1 + p3*stbi__f2f(-1.847759065f); \ + t3 = p1 + p2*stbi__f2f( 0.765366865f); \ + p2 = s0; \ + p3 = s4; \ + t0 = stbi__fsh(p2+p3); \ + t1 = stbi__fsh(p2-p3); \ + x0 = t0+t3; \ + x3 = t0-t3; \ + x1 = t1+t2; \ + x2 = t1-t2; \ + t0 = s7; \ + t1 = s5; \ + t2 = s3; \ + t3 = s1; \ + p3 = t0+t2; \ + p4 = t1+t3; \ + p1 = t0+t3; \ + p2 = t1+t2; \ + p5 = (p3+p4)*stbi__f2f( 1.175875602f); \ + t0 = t0*stbi__f2f( 0.298631336f); \ + t1 = t1*stbi__f2f( 2.053119869f); \ + t2 = t2*stbi__f2f( 3.072711026f); \ + t3 = t3*stbi__f2f( 1.501321110f); \ + p1 = p5 + p1*stbi__f2f(-0.899976223f); \ + p2 = p5 + p2*stbi__f2f(-2.562915447f); \ + p3 = p3*stbi__f2f(-1.961570560f); \ + p4 = p4*stbi__f2f(-0.390180644f); \ + t3 += p1+p4; \ + t2 += p2+p3; \ + t1 += p2+p4; \ + t0 += p1+p3; + +static void stbi__idct_block(stbi_uc *out, int out_stride, short data[64]) +{ + int i,val[64],*v=val; + stbi_uc *o; + short *d = data; + + // columns + for (i=0; i < 8; ++i,++d, ++v) { + // if all zeroes, shortcut -- this avoids dequantizing 0s and IDCTing + if (d[ 8]==0 && d[16]==0 && d[24]==0 && d[32]==0 + && d[40]==0 && d[48]==0 && d[56]==0) { + // no shortcut 0 seconds + // (1|2|3|4|5|6|7)==0 0 seconds + // all separate -0.047 seconds + // 1 && 2|3 && 4|5 && 6|7: -0.047 seconds + int dcterm = d[0] << 2; + v[0] = v[8] = v[16] = v[24] = v[32] = v[40] = v[48] = v[56] = dcterm; + } else { + STBI__IDCT_1D(d[ 0],d[ 8],d[16],d[24],d[32],d[40],d[48],d[56]) + // constants scaled things up by 1<<12; let's bring them back + // down, but keep 2 extra bits of precision + x0 += 512; x1 += 512; x2 += 512; x3 += 512; + v[ 0] = (x0+t3) >> 10; + v[56] = (x0-t3) >> 10; + v[ 8] = (x1+t2) >> 10; + v[48] = (x1-t2) >> 10; + v[16] = (x2+t1) >> 10; + v[40] = (x2-t1) >> 10; + v[24] = (x3+t0) >> 10; + v[32] = (x3-t0) >> 10; + } + } + + for (i=0, v=val, o=out; i < 8; ++i,v+=8,o+=out_stride) { + // no fast case since the first 1D IDCT spread components out + STBI__IDCT_1D(v[0],v[1],v[2],v[3],v[4],v[5],v[6],v[7]) + // constants scaled things up by 1<<12, plus we had 1<<2 from first + // loop, plus horizontal and vertical each scale by sqrt(8) so together + // we've got an extra 1<<3, so 1<<17 total we need to remove. + // so we want to round that, which means adding 0.5 * 1<<17, + // aka 65536. Also, we'll end up with -128 to 127 that we want + // to encode as 0..255 by adding 128, so we'll add that before the shift + x0 += 65536 + (128<<17); + x1 += 65536 + (128<<17); + x2 += 65536 + (128<<17); + x3 += 65536 + (128<<17); + // tried computing the shifts into temps, or'ing the temps to see + // if any were out of range, but that was slower + o[0] = stbi__clamp((x0+t3) >> 17); + o[7] = stbi__clamp((x0-t3) >> 17); + o[1] = stbi__clamp((x1+t2) >> 17); + o[6] = stbi__clamp((x1-t2) >> 17); + o[2] = stbi__clamp((x2+t1) >> 17); + o[5] = stbi__clamp((x2-t1) >> 17); + o[3] = stbi__clamp((x3+t0) >> 17); + o[4] = stbi__clamp((x3-t0) >> 17); + } +} + +#ifdef STBI_SSE2 +// sse2 integer IDCT. not the fastest possible implementation but it +// produces bit-identical results to the generic C version so it's +// fully "transparent". +static void stbi__idct_simd(stbi_uc *out, int out_stride, short data[64]) +{ + // This is constructed to match our regular (generic) integer IDCT exactly. + __m128i row0, row1, row2, row3, row4, row5, row6, row7; + __m128i tmp; + + // dot product constant: even elems=x, odd elems=y + #define dct_const(x,y) _mm_setr_epi16((x),(y),(x),(y),(x),(y),(x),(y)) + + // out(0) = c0[even]*x + c0[odd]*y (c0, x, y 16-bit, out 32-bit) + // out(1) = c1[even]*x + c1[odd]*y + #define dct_rot(out0,out1, x,y,c0,c1) \ + __m128i c0##lo = _mm_unpacklo_epi16((x),(y)); \ + __m128i c0##hi = _mm_unpackhi_epi16((x),(y)); \ + __m128i out0##_l = _mm_madd_epi16(c0##lo, c0); \ + __m128i out0##_h = _mm_madd_epi16(c0##hi, c0); \ + __m128i out1##_l = _mm_madd_epi16(c0##lo, c1); \ + __m128i out1##_h = _mm_madd_epi16(c0##hi, c1) + + // out = in << 12 (in 16-bit, out 32-bit) + #define dct_widen(out, in) \ + __m128i out##_l = _mm_srai_epi32(_mm_unpacklo_epi16(_mm_setzero_si128(), (in)), 4); \ + __m128i out##_h = _mm_srai_epi32(_mm_unpackhi_epi16(_mm_setzero_si128(), (in)), 4) + + // wide add + #define dct_wadd(out, a, b) \ + __m128i out##_l = _mm_add_epi32(a##_l, b##_l); \ + __m128i out##_h = _mm_add_epi32(a##_h, b##_h) + + // wide sub + #define dct_wsub(out, a, b) \ + __m128i out##_l = _mm_sub_epi32(a##_l, b##_l); \ + __m128i out##_h = _mm_sub_epi32(a##_h, b##_h) + + // butterfly a/b, add bias, then shift by "s" and pack + #define dct_bfly32o(out0, out1, a,b,bias,s) \ + { \ + __m128i abiased_l = _mm_add_epi32(a##_l, bias); \ + __m128i abiased_h = _mm_add_epi32(a##_h, bias); \ + dct_wadd(sum, abiased, b); \ + dct_wsub(dif, abiased, b); \ + out0 = _mm_packs_epi32(_mm_srai_epi32(sum_l, s), _mm_srai_epi32(sum_h, s)); \ + out1 = _mm_packs_epi32(_mm_srai_epi32(dif_l, s), _mm_srai_epi32(dif_h, s)); \ + } + + // 8-bit interleave step (for transposes) + #define dct_interleave8(a, b) \ + tmp = a; \ + a = _mm_unpacklo_epi8(a, b); \ + b = _mm_unpackhi_epi8(tmp, b) + + // 16-bit interleave step (for transposes) + #define dct_interleave16(a, b) \ + tmp = a; \ + a = _mm_unpacklo_epi16(a, b); \ + b = _mm_unpackhi_epi16(tmp, b) + + #define dct_pass(bias,shift) \ + { \ + /* even part */ \ + dct_rot(t2e,t3e, row2,row6, rot0_0,rot0_1); \ + __m128i sum04 = _mm_add_epi16(row0, row4); \ + __m128i dif04 = _mm_sub_epi16(row0, row4); \ + dct_widen(t0e, sum04); \ + dct_widen(t1e, dif04); \ + dct_wadd(x0, t0e, t3e); \ + dct_wsub(x3, t0e, t3e); \ + dct_wadd(x1, t1e, t2e); \ + dct_wsub(x2, t1e, t2e); \ + /* odd part */ \ + dct_rot(y0o,y2o, row7,row3, rot2_0,rot2_1); \ + dct_rot(y1o,y3o, row5,row1, rot3_0,rot3_1); \ + __m128i sum17 = _mm_add_epi16(row1, row7); \ + __m128i sum35 = _mm_add_epi16(row3, row5); \ + dct_rot(y4o,y5o, sum17,sum35, rot1_0,rot1_1); \ + dct_wadd(x4, y0o, y4o); \ + dct_wadd(x5, y1o, y5o); \ + dct_wadd(x6, y2o, y5o); \ + dct_wadd(x7, y3o, y4o); \ + dct_bfly32o(row0,row7, x0,x7,bias,shift); \ + dct_bfly32o(row1,row6, x1,x6,bias,shift); \ + dct_bfly32o(row2,row5, x2,x5,bias,shift); \ + dct_bfly32o(row3,row4, x3,x4,bias,shift); \ + } + + __m128i rot0_0 = dct_const(stbi__f2f(0.5411961f), stbi__f2f(0.5411961f) + stbi__f2f(-1.847759065f)); + __m128i rot0_1 = dct_const(stbi__f2f(0.5411961f) + stbi__f2f( 0.765366865f), stbi__f2f(0.5411961f)); + __m128i rot1_0 = dct_const(stbi__f2f(1.175875602f) + stbi__f2f(-0.899976223f), stbi__f2f(1.175875602f)); + __m128i rot1_1 = dct_const(stbi__f2f(1.175875602f), stbi__f2f(1.175875602f) + stbi__f2f(-2.562915447f)); + __m128i rot2_0 = dct_const(stbi__f2f(-1.961570560f) + stbi__f2f( 0.298631336f), stbi__f2f(-1.961570560f)); + __m128i rot2_1 = dct_const(stbi__f2f(-1.961570560f), stbi__f2f(-1.961570560f) + stbi__f2f( 3.072711026f)); + __m128i rot3_0 = dct_const(stbi__f2f(-0.390180644f) + stbi__f2f( 2.053119869f), stbi__f2f(-0.390180644f)); + __m128i rot3_1 = dct_const(stbi__f2f(-0.390180644f), stbi__f2f(-0.390180644f) + stbi__f2f( 1.501321110f)); + + // rounding biases in column/row passes, see stbi__idct_block for explanation. + __m128i bias_0 = _mm_set1_epi32(512); + __m128i bias_1 = _mm_set1_epi32(65536 + (128<<17)); + + // load + row0 = _mm_load_si128((const __m128i *) (data + 0*8)); + row1 = _mm_load_si128((const __m128i *) (data + 1*8)); + row2 = _mm_load_si128((const __m128i *) (data + 2*8)); + row3 = _mm_load_si128((const __m128i *) (data + 3*8)); + row4 = _mm_load_si128((const __m128i *) (data + 4*8)); + row5 = _mm_load_si128((const __m128i *) (data + 5*8)); + row6 = _mm_load_si128((const __m128i *) (data + 6*8)); + row7 = _mm_load_si128((const __m128i *) (data + 7*8)); + + // column pass + dct_pass(bias_0, 10); + + { + // 16bit 8x8 transpose pass 1 + dct_interleave16(row0, row4); + dct_interleave16(row1, row5); + dct_interleave16(row2, row6); + dct_interleave16(row3, row7); + + // transpose pass 2 + dct_interleave16(row0, row2); + dct_interleave16(row1, row3); + dct_interleave16(row4, row6); + dct_interleave16(row5, row7); + + // transpose pass 3 + dct_interleave16(row0, row1); + dct_interleave16(row2, row3); + dct_interleave16(row4, row5); + dct_interleave16(row6, row7); + } + + // row pass + dct_pass(bias_1, 17); + + { + // pack + __m128i p0 = _mm_packus_epi16(row0, row1); // a0a1a2a3...a7b0b1b2b3...b7 + __m128i p1 = _mm_packus_epi16(row2, row3); + __m128i p2 = _mm_packus_epi16(row4, row5); + __m128i p3 = _mm_packus_epi16(row6, row7); + + // 8bit 8x8 transpose pass 1 + dct_interleave8(p0, p2); // a0e0a1e1... + dct_interleave8(p1, p3); // c0g0c1g1... + + // transpose pass 2 + dct_interleave8(p0, p1); // a0c0e0g0... + dct_interleave8(p2, p3); // b0d0f0h0... + + // transpose pass 3 + dct_interleave8(p0, p2); // a0b0c0d0... + dct_interleave8(p1, p3); // a4b4c4d4... + + // store + _mm_storel_epi64((__m128i *) out, p0); out += out_stride; + _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p0, 0x4e)); out += out_stride; + _mm_storel_epi64((__m128i *) out, p2); out += out_stride; + _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p2, 0x4e)); out += out_stride; + _mm_storel_epi64((__m128i *) out, p1); out += out_stride; + _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p1, 0x4e)); out += out_stride; + _mm_storel_epi64((__m128i *) out, p3); out += out_stride; + _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p3, 0x4e)); + } + +#undef dct_const +#undef dct_rot +#undef dct_widen +#undef dct_wadd +#undef dct_wsub +#undef dct_bfly32o +#undef dct_interleave8 +#undef dct_interleave16 +#undef dct_pass +} + +#endif // STBI_SSE2 + +#ifdef STBI_NEON + +// NEON integer IDCT. should produce bit-identical +// results to the generic C version. +static void stbi__idct_simd(stbi_uc *out, int out_stride, short data[64]) +{ + int16x8_t row0, row1, row2, row3, row4, row5, row6, row7; + + int16x4_t rot0_0 = vdup_n_s16(stbi__f2f(0.5411961f)); + int16x4_t rot0_1 = vdup_n_s16(stbi__f2f(-1.847759065f)); + int16x4_t rot0_2 = vdup_n_s16(stbi__f2f( 0.765366865f)); + int16x4_t rot1_0 = vdup_n_s16(stbi__f2f( 1.175875602f)); + int16x4_t rot1_1 = vdup_n_s16(stbi__f2f(-0.899976223f)); + int16x4_t rot1_2 = vdup_n_s16(stbi__f2f(-2.562915447f)); + int16x4_t rot2_0 = vdup_n_s16(stbi__f2f(-1.961570560f)); + int16x4_t rot2_1 = vdup_n_s16(stbi__f2f(-0.390180644f)); + int16x4_t rot3_0 = vdup_n_s16(stbi__f2f( 0.298631336f)); + int16x4_t rot3_1 = vdup_n_s16(stbi__f2f( 2.053119869f)); + int16x4_t rot3_2 = vdup_n_s16(stbi__f2f( 3.072711026f)); + int16x4_t rot3_3 = vdup_n_s16(stbi__f2f( 1.501321110f)); + +#define dct_long_mul(out, inq, coeff) \ + int32x4_t out##_l = vmull_s16(vget_low_s16(inq), coeff); \ + int32x4_t out##_h = vmull_s16(vget_high_s16(inq), coeff) + +#define dct_long_mac(out, acc, inq, coeff) \ + int32x4_t out##_l = vmlal_s16(acc##_l, vget_low_s16(inq), coeff); \ + int32x4_t out##_h = vmlal_s16(acc##_h, vget_high_s16(inq), coeff) + +#define dct_widen(out, inq) \ + int32x4_t out##_l = vshll_n_s16(vget_low_s16(inq), 12); \ + int32x4_t out##_h = vshll_n_s16(vget_high_s16(inq), 12) + +// wide add +#define dct_wadd(out, a, b) \ + int32x4_t out##_l = vaddq_s32(a##_l, b##_l); \ + int32x4_t out##_h = vaddq_s32(a##_h, b##_h) + +// wide sub +#define dct_wsub(out, a, b) \ + int32x4_t out##_l = vsubq_s32(a##_l, b##_l); \ + int32x4_t out##_h = vsubq_s32(a##_h, b##_h) + +// butterfly a/b, then shift using "shiftop" by "s" and pack +#define dct_bfly32o(out0,out1, a,b,shiftop,s) \ + { \ + dct_wadd(sum, a, b); \ + dct_wsub(dif, a, b); \ + out0 = vcombine_s16(shiftop(sum_l, s), shiftop(sum_h, s)); \ + out1 = vcombine_s16(shiftop(dif_l, s), shiftop(dif_h, s)); \ + } + +#define dct_pass(shiftop, shift) \ + { \ + /* even part */ \ + int16x8_t sum26 = vaddq_s16(row2, row6); \ + dct_long_mul(p1e, sum26, rot0_0); \ + dct_long_mac(t2e, p1e, row6, rot0_1); \ + dct_long_mac(t3e, p1e, row2, rot0_2); \ + int16x8_t sum04 = vaddq_s16(row0, row4); \ + int16x8_t dif04 = vsubq_s16(row0, row4); \ + dct_widen(t0e, sum04); \ + dct_widen(t1e, dif04); \ + dct_wadd(x0, t0e, t3e); \ + dct_wsub(x3, t0e, t3e); \ + dct_wadd(x1, t1e, t2e); \ + dct_wsub(x2, t1e, t2e); \ + /* odd part */ \ + int16x8_t sum15 = vaddq_s16(row1, row5); \ + int16x8_t sum17 = vaddq_s16(row1, row7); \ + int16x8_t sum35 = vaddq_s16(row3, row5); \ + int16x8_t sum37 = vaddq_s16(row3, row7); \ + int16x8_t sumodd = vaddq_s16(sum17, sum35); \ + dct_long_mul(p5o, sumodd, rot1_0); \ + dct_long_mac(p1o, p5o, sum17, rot1_1); \ + dct_long_mac(p2o, p5o, sum35, rot1_2); \ + dct_long_mul(p3o, sum37, rot2_0); \ + dct_long_mul(p4o, sum15, rot2_1); \ + dct_wadd(sump13o, p1o, p3o); \ + dct_wadd(sump24o, p2o, p4o); \ + dct_wadd(sump23o, p2o, p3o); \ + dct_wadd(sump14o, p1o, p4o); \ + dct_long_mac(x4, sump13o, row7, rot3_0); \ + dct_long_mac(x5, sump24o, row5, rot3_1); \ + dct_long_mac(x6, sump23o, row3, rot3_2); \ + dct_long_mac(x7, sump14o, row1, rot3_3); \ + dct_bfly32o(row0,row7, x0,x7,shiftop,shift); \ + dct_bfly32o(row1,row6, x1,x6,shiftop,shift); \ + dct_bfly32o(row2,row5, x2,x5,shiftop,shift); \ + dct_bfly32o(row3,row4, x3,x4,shiftop,shift); \ + } + + // load + row0 = vld1q_s16(data + 0*8); + row1 = vld1q_s16(data + 1*8); + row2 = vld1q_s16(data + 2*8); + row3 = vld1q_s16(data + 3*8); + row4 = vld1q_s16(data + 4*8); + row5 = vld1q_s16(data + 5*8); + row6 = vld1q_s16(data + 6*8); + row7 = vld1q_s16(data + 7*8); + + // add DC bias + row0 = vaddq_s16(row0, vsetq_lane_s16(1024, vdupq_n_s16(0), 0)); + + // column pass + dct_pass(vrshrn_n_s32, 10); + + // 16bit 8x8 transpose + { +// these three map to a single VTRN.16, VTRN.32, and VSWP, respectively. +// whether compilers actually get this is another story, sadly. +#define dct_trn16(x, y) { int16x8x2_t t = vtrnq_s16(x, y); x = t.val[0]; y = t.val[1]; } +#define dct_trn32(x, y) { int32x4x2_t t = vtrnq_s32(vreinterpretq_s32_s16(x), vreinterpretq_s32_s16(y)); x = vreinterpretq_s16_s32(t.val[0]); y = vreinterpretq_s16_s32(t.val[1]); } +#define dct_trn64(x, y) { int16x8_t x0 = x; int16x8_t y0 = y; x = vcombine_s16(vget_low_s16(x0), vget_low_s16(y0)); y = vcombine_s16(vget_high_s16(x0), vget_high_s16(y0)); } + + // pass 1 + dct_trn16(row0, row1); // a0b0a2b2a4b4a6b6 + dct_trn16(row2, row3); + dct_trn16(row4, row5); + dct_trn16(row6, row7); + + // pass 2 + dct_trn32(row0, row2); // a0b0c0d0a4b4c4d4 + dct_trn32(row1, row3); + dct_trn32(row4, row6); + dct_trn32(row5, row7); + + // pass 3 + dct_trn64(row0, row4); // a0b0c0d0e0f0g0h0 + dct_trn64(row1, row5); + dct_trn64(row2, row6); + dct_trn64(row3, row7); + +#undef dct_trn16 +#undef dct_trn32 +#undef dct_trn64 + } + + // row pass + // vrshrn_n_s32 only supports shifts up to 16, we need + // 17. so do a non-rounding shift of 16 first then follow + // up with a rounding shift by 1. + dct_pass(vshrn_n_s32, 16); + + { + // pack and round + uint8x8_t p0 = vqrshrun_n_s16(row0, 1); + uint8x8_t p1 = vqrshrun_n_s16(row1, 1); + uint8x8_t p2 = vqrshrun_n_s16(row2, 1); + uint8x8_t p3 = vqrshrun_n_s16(row3, 1); + uint8x8_t p4 = vqrshrun_n_s16(row4, 1); + uint8x8_t p5 = vqrshrun_n_s16(row5, 1); + uint8x8_t p6 = vqrshrun_n_s16(row6, 1); + uint8x8_t p7 = vqrshrun_n_s16(row7, 1); + + // again, these can translate into one instruction, but often don't. +#define dct_trn8_8(x, y) { uint8x8x2_t t = vtrn_u8(x, y); x = t.val[0]; y = t.val[1]; } +#define dct_trn8_16(x, y) { uint16x4x2_t t = vtrn_u16(vreinterpret_u16_u8(x), vreinterpret_u16_u8(y)); x = vreinterpret_u8_u16(t.val[0]); y = vreinterpret_u8_u16(t.val[1]); } +#define dct_trn8_32(x, y) { uint32x2x2_t t = vtrn_u32(vreinterpret_u32_u8(x), vreinterpret_u32_u8(y)); x = vreinterpret_u8_u32(t.val[0]); y = vreinterpret_u8_u32(t.val[1]); } + + // sadly can't use interleaved stores here since we only write + // 8 bytes to each scan line! + + // 8x8 8-bit transpose pass 1 + dct_trn8_8(p0, p1); + dct_trn8_8(p2, p3); + dct_trn8_8(p4, p5); + dct_trn8_8(p6, p7); + + // pass 2 + dct_trn8_16(p0, p2); + dct_trn8_16(p1, p3); + dct_trn8_16(p4, p6); + dct_trn8_16(p5, p7); + + // pass 3 + dct_trn8_32(p0, p4); + dct_trn8_32(p1, p5); + dct_trn8_32(p2, p6); + dct_trn8_32(p3, p7); + + // store + vst1_u8(out, p0); out += out_stride; + vst1_u8(out, p1); out += out_stride; + vst1_u8(out, p2); out += out_stride; + vst1_u8(out, p3); out += out_stride; + vst1_u8(out, p4); out += out_stride; + vst1_u8(out, p5); out += out_stride; + vst1_u8(out, p6); out += out_stride; + vst1_u8(out, p7); + +#undef dct_trn8_8 +#undef dct_trn8_16 +#undef dct_trn8_32 + } + +#undef dct_long_mul +#undef dct_long_mac +#undef dct_widen +#undef dct_wadd +#undef dct_wsub +#undef dct_bfly32o +#undef dct_pass +} + +#endif // STBI_NEON + +#define STBI__MARKER_none 0xff +// if there's a pending marker from the entropy stream, return that +// otherwise, fetch from the stream and get a marker. if there's no +// marker, return 0xff, which is never a valid marker value +static stbi_uc stbi__get_marker(stbi__jpeg *j) +{ + stbi_uc x; + if (j->marker != STBI__MARKER_none) { x = j->marker; j->marker = STBI__MARKER_none; return x; } + x = stbi__get8(j->s); + if (x != 0xff) return STBI__MARKER_none; + while (x == 0xff) + x = stbi__get8(j->s); + return x; +} + +// in each scan, we'll have scan_n components, and the order +// of the components is specified by order[] +#define STBI__RESTART(x) ((x) >= 0xd0 && (x) <= 0xd7) + +// after a restart interval, stbi__jpeg_reset the entropy decoder and +// the dc prediction +static void stbi__jpeg_reset(stbi__jpeg *j) +{ + j->code_bits = 0; + j->code_buffer = 0; + j->nomore = 0; + j->img_comp[0].dc_pred = j->img_comp[1].dc_pred = j->img_comp[2].dc_pred = 0; + j->marker = STBI__MARKER_none; + j->todo = j->restart_interval ? j->restart_interval : 0x7fffffff; + j->eob_run = 0; + // no more than 1<<31 MCUs if no restart_interal? that's plenty safe, + // since we don't even allow 1<<30 pixels +} + +static int stbi__parse_entropy_coded_data(stbi__jpeg *z) +{ + stbi__jpeg_reset(z); + if (!z->progressive) { + if (z->scan_n == 1) { + int i,j; + STBI_SIMD_ALIGN(short, data[64]); + int n = z->order[0]; + // non-interleaved data, we just need to process one block at a time, + // in trivial scanline order + // number of blocks to do just depends on how many actual "pixels" this + // component has, independent of interleaved MCU blocking and such + int w = (z->img_comp[n].x+7) >> 3; + int h = (z->img_comp[n].y+7) >> 3; + for (j=0; j < h; ++j) { + for (i=0; i < w; ++i) { + int ha = z->img_comp[n].ha; + if (!stbi__jpeg_decode_block(z, data, z->huff_dc+z->img_comp[n].hd, z->huff_ac+ha, z->fast_ac[ha], n, z->dequant[z->img_comp[n].tq])) return 0; + z->idct_block_kernel(z->img_comp[n].data+z->img_comp[n].w2*j*8+i*8, z->img_comp[n].w2, data); + // every data block is an MCU, so countdown the restart interval + if (--z->todo <= 0) { + if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); + // if it's NOT a restart, then just bail, so we get corrupt data + // rather than no data + if (!STBI__RESTART(z->marker)) return 1; + stbi__jpeg_reset(z); + } + } + } + return 1; + } else { // interleaved + int i,j,k,x,y; + STBI_SIMD_ALIGN(short, data[64]); + for (j=0; j < z->img_mcu_y; ++j) { + for (i=0; i < z->img_mcu_x; ++i) { + // scan an interleaved mcu... process scan_n components in order + for (k=0; k < z->scan_n; ++k) { + int n = z->order[k]; + // scan out an mcu's worth of this component; that's just determined + // by the basic H and V specified for the component + for (y=0; y < z->img_comp[n].v; ++y) { + for (x=0; x < z->img_comp[n].h; ++x) { + int x2 = (i*z->img_comp[n].h + x)*8; + int y2 = (j*z->img_comp[n].v + y)*8; + int ha = z->img_comp[n].ha; + if (!stbi__jpeg_decode_block(z, data, z->huff_dc+z->img_comp[n].hd, z->huff_ac+ha, z->fast_ac[ha], n, z->dequant[z->img_comp[n].tq])) return 0; + z->idct_block_kernel(z->img_comp[n].data+z->img_comp[n].w2*y2+x2, z->img_comp[n].w2, data); + } + } + } + // after all interleaved components, that's an interleaved MCU, + // so now count down the restart interval + if (--z->todo <= 0) { + if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); + if (!STBI__RESTART(z->marker)) return 1; + stbi__jpeg_reset(z); + } + } + } + return 1; + } + } else { + if (z->scan_n == 1) { + int i,j; + int n = z->order[0]; + // non-interleaved data, we just need to process one block at a time, + // in trivial scanline order + // number of blocks to do just depends on how many actual "pixels" this + // component has, independent of interleaved MCU blocking and such + int w = (z->img_comp[n].x+7) >> 3; + int h = (z->img_comp[n].y+7) >> 3; + for (j=0; j < h; ++j) { + for (i=0; i < w; ++i) { + short *data = z->img_comp[n].coeff + 64 * (i + j * z->img_comp[n].coeff_w); + if (z->spec_start == 0) { + if (!stbi__jpeg_decode_block_prog_dc(z, data, &z->huff_dc[z->img_comp[n].hd], n)) + return 0; + } else { + int ha = z->img_comp[n].ha; + if (!stbi__jpeg_decode_block_prog_ac(z, data, &z->huff_ac[ha], z->fast_ac[ha])) + return 0; + } + // every data block is an MCU, so countdown the restart interval + if (--z->todo <= 0) { + if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); + if (!STBI__RESTART(z->marker)) return 1; + stbi__jpeg_reset(z); + } + } + } + return 1; + } else { // interleaved + int i,j,k,x,y; + for (j=0; j < z->img_mcu_y; ++j) { + for (i=0; i < z->img_mcu_x; ++i) { + // scan an interleaved mcu... process scan_n components in order + for (k=0; k < z->scan_n; ++k) { + int n = z->order[k]; + // scan out an mcu's worth of this component; that's just determined + // by the basic H and V specified for the component + for (y=0; y < z->img_comp[n].v; ++y) { + for (x=0; x < z->img_comp[n].h; ++x) { + int x2 = (i*z->img_comp[n].h + x); + int y2 = (j*z->img_comp[n].v + y); + short *data = z->img_comp[n].coeff + 64 * (x2 + y2 * z->img_comp[n].coeff_w); + if (!stbi__jpeg_decode_block_prog_dc(z, data, &z->huff_dc[z->img_comp[n].hd], n)) + return 0; + } + } + } + // after all interleaved components, that's an interleaved MCU, + // so now count down the restart interval + if (--z->todo <= 0) { + if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); + if (!STBI__RESTART(z->marker)) return 1; + stbi__jpeg_reset(z); + } + } + } + return 1; + } + } +} + +static void stbi__jpeg_dequantize(short *data, stbi_uc *dequant) +{ + int i; + for (i=0; i < 64; ++i) + data[i] *= dequant[i]; +} + +static void stbi__jpeg_finish(stbi__jpeg *z) +{ + if (z->progressive) { + // dequantize and idct the data + int i,j,n; + for (n=0; n < z->s->img_n; ++n) { + int w = (z->img_comp[n].x+7) >> 3; + int h = (z->img_comp[n].y+7) >> 3; + for (j=0; j < h; ++j) { + for (i=0; i < w; ++i) { + short *data = z->img_comp[n].coeff + 64 * (i + j * z->img_comp[n].coeff_w); + stbi__jpeg_dequantize(data, z->dequant[z->img_comp[n].tq]); + z->idct_block_kernel(z->img_comp[n].data+z->img_comp[n].w2*j*8+i*8, z->img_comp[n].w2, data); + } + } + } + } +} + +static int stbi__process_marker(stbi__jpeg *z, int m) +{ + int L; + switch (m) { + case STBI__MARKER_none: // no marker found + return stbi__err("expected marker","Corrupt JPEG"); + + case 0xDD: // DRI - specify restart interval + if (stbi__get16be(z->s) != 4) return stbi__err("bad DRI len","Corrupt JPEG"); + z->restart_interval = stbi__get16be(z->s); + return 1; + + case 0xDB: // DQT - define quantization table + L = stbi__get16be(z->s)-2; + while (L > 0) { + int q = stbi__get8(z->s); + int p = q >> 4; + int t = q & 15,i; + if (p != 0) return stbi__err("bad DQT type","Corrupt JPEG"); + if (t > 3) return stbi__err("bad DQT table","Corrupt JPEG"); + for (i=0; i < 64; ++i) + z->dequant[t][stbi__jpeg_dezigzag[i]] = stbi__get8(z->s); + L -= 65; + } + return L==0; + + case 0xC4: // DHT - define huffman table + L = stbi__get16be(z->s)-2; + while (L > 0) { + stbi_uc *v; + int sizes[16],i,n=0; + int q = stbi__get8(z->s); + int tc = q >> 4; + int th = q & 15; + if (tc > 1 || th > 3) return stbi__err("bad DHT header","Corrupt JPEG"); + for (i=0; i < 16; ++i) { + sizes[i] = stbi__get8(z->s); + n += sizes[i]; + } + L -= 17; + if (tc == 0) { + if (!stbi__build_huffman(z->huff_dc+th, sizes)) return 0; + v = z->huff_dc[th].values; + } else { + if (!stbi__build_huffman(z->huff_ac+th, sizes)) return 0; + v = z->huff_ac[th].values; + } + for (i=0; i < n; ++i) + v[i] = stbi__get8(z->s); + if (tc != 0) + stbi__build_fast_ac(z->fast_ac[th], z->huff_ac + th); + L -= n; + } + return L==0; + } + // check for comment block or APP blocks + if ((m >= 0xE0 && m <= 0xEF) || m == 0xFE) { + stbi__skip(z->s, stbi__get16be(z->s)-2); + return 1; + } + return 0; +} + +// after we see SOS +static int stbi__process_scan_header(stbi__jpeg *z) +{ + int i; + int Ls = stbi__get16be(z->s); + z->scan_n = stbi__get8(z->s); + if (z->scan_n < 1 || z->scan_n > 4 || z->scan_n > (int) z->s->img_n) return stbi__err("bad SOS component count","Corrupt JPEG"); + if (Ls != 6+2*z->scan_n) return stbi__err("bad SOS len","Corrupt JPEG"); + for (i=0; i < z->scan_n; ++i) { + int id = stbi__get8(z->s), which; + int q = stbi__get8(z->s); + for (which = 0; which < z->s->img_n; ++which) + if (z->img_comp[which].id == id) + break; + if (which == z->s->img_n) return 0; // no match + z->img_comp[which].hd = q >> 4; if (z->img_comp[which].hd > 3) return stbi__err("bad DC huff","Corrupt JPEG"); + z->img_comp[which].ha = q & 15; if (z->img_comp[which].ha > 3) return stbi__err("bad AC huff","Corrupt JPEG"); + z->order[i] = which; + } + + { + int aa; + z->spec_start = stbi__get8(z->s); + z->spec_end = stbi__get8(z->s); // should be 63, but might be 0 + aa = stbi__get8(z->s); + z->succ_high = (aa >> 4); + z->succ_low = (aa & 15); + if (z->progressive) { + if (z->spec_start > 63 || z->spec_end > 63 || z->spec_start > z->spec_end || z->succ_high > 13 || z->succ_low > 13) + return stbi__err("bad SOS", "Corrupt JPEG"); + } else { + if (z->spec_start != 0) return stbi__err("bad SOS","Corrupt JPEG"); + if (z->succ_high != 0 || z->succ_low != 0) return stbi__err("bad SOS","Corrupt JPEG"); + z->spec_end = 63; + } + } + + return 1; +} + +static int stbi__process_frame_header(stbi__jpeg *z, int scan) +{ + stbi__context *s = z->s; + int Lf,p,i,q, h_max=1,v_max=1,c; + Lf = stbi__get16be(s); if (Lf < 11) return stbi__err("bad SOF len","Corrupt JPEG"); // JPEG + p = stbi__get8(s); if (p != 8) return stbi__err("only 8-bit","JPEG format not supported: 8-bit only"); // JPEG baseline + s->img_y = stbi__get16be(s); if (s->img_y == 0) return stbi__err("no header height", "JPEG format not supported: delayed height"); // Legal, but we don't handle it--but neither does IJG + s->img_x = stbi__get16be(s); if (s->img_x == 0) return stbi__err("0 width","Corrupt JPEG"); // JPEG requires + c = stbi__get8(s); + if (c != 3 && c != 1) return stbi__err("bad component count","Corrupt JPEG"); // JFIF requires + s->img_n = c; + for (i=0; i < c; ++i) { + z->img_comp[i].data = NULL; + z->img_comp[i].linebuf = NULL; + } + + if (Lf != 8+3*s->img_n) return stbi__err("bad SOF len","Corrupt JPEG"); + + for (i=0; i < s->img_n; ++i) { + z->img_comp[i].id = stbi__get8(s); + if (z->img_comp[i].id != i+1) // JFIF requires + if (z->img_comp[i].id != i) // some version of jpegtran outputs non-JFIF-compliant files! + return stbi__err("bad component ID","Corrupt JPEG"); + q = stbi__get8(s); + z->img_comp[i].h = (q >> 4); if (!z->img_comp[i].h || z->img_comp[i].h > 4) return stbi__err("bad H","Corrupt JPEG"); + z->img_comp[i].v = q & 15; if (!z->img_comp[i].v || z->img_comp[i].v > 4) return stbi__err("bad V","Corrupt JPEG"); + z->img_comp[i].tq = stbi__get8(s); if (z->img_comp[i].tq > 3) return stbi__err("bad TQ","Corrupt JPEG"); + } + + if (scan != STBI__SCAN_load) return 1; + + if ((1 << 30) / s->img_x / s->img_n < s->img_y) return stbi__err("too large", "Image too large to decode"); + + for (i=0; i < s->img_n; ++i) { + if (z->img_comp[i].h > h_max) h_max = z->img_comp[i].h; + if (z->img_comp[i].v > v_max) v_max = z->img_comp[i].v; + } + + // compute interleaved mcu info + z->img_h_max = h_max; + z->img_v_max = v_max; + z->img_mcu_w = h_max * 8; + z->img_mcu_h = v_max * 8; + z->img_mcu_x = (s->img_x + z->img_mcu_w-1) / z->img_mcu_w; + z->img_mcu_y = (s->img_y + z->img_mcu_h-1) / z->img_mcu_h; + + for (i=0; i < s->img_n; ++i) { + // number of effective pixels (e.g. for non-interleaved MCU) + z->img_comp[i].x = (s->img_x * z->img_comp[i].h + h_max-1) / h_max; + z->img_comp[i].y = (s->img_y * z->img_comp[i].v + v_max-1) / v_max; + // to simplify generation, we'll allocate enough memory to decode + // the bogus oversized data from using interleaved MCUs and their + // big blocks (e.g. a 16x16 iMCU on an image of width 33); we won't + // discard the extra data until colorspace conversion + z->img_comp[i].w2 = z->img_mcu_x * z->img_comp[i].h * 8; + z->img_comp[i].h2 = z->img_mcu_y * z->img_comp[i].v * 8; + z->img_comp[i].raw_data = stbi__malloc(z->img_comp[i].w2 * z->img_comp[i].h2+15); + + if (z->img_comp[i].raw_data == NULL) { + for(--i; i >= 0; --i) { + STBI_FREE(z->img_comp[i].raw_data); + z->img_comp[i].data = NULL; + } + return stbi__err("outofmem", "Out of memory"); + } + // align blocks for idct using mmx/sse + z->img_comp[i].data = (stbi_uc*) (((size_t) z->img_comp[i].raw_data + 15) & ~15); + z->img_comp[i].linebuf = NULL; + if (z->progressive) { + z->img_comp[i].coeff_w = (z->img_comp[i].w2 + 7) >> 3; + z->img_comp[i].coeff_h = (z->img_comp[i].h2 + 7) >> 3; + z->img_comp[i].raw_coeff = STBI_MALLOC(z->img_comp[i].coeff_w * z->img_comp[i].coeff_h * 64 * sizeof(short) + 15); + z->img_comp[i].coeff = (short*) (((size_t) z->img_comp[i].raw_coeff + 15) & ~15); + } else { + z->img_comp[i].coeff = 0; + z->img_comp[i].raw_coeff = 0; + } + } + + return 1; +} + +// use comparisons since in some cases we handle more than one case (e.g. SOF) +#define stbi__DNL(x) ((x) == 0xdc) +#define stbi__SOI(x) ((x) == 0xd8) +#define stbi__EOI(x) ((x) == 0xd9) +#define stbi__SOF(x) ((x) == 0xc0 || (x) == 0xc1 || (x) == 0xc2) +#define stbi__SOS(x) ((x) == 0xda) + +#define stbi__SOF_progressive(x) ((x) == 0xc2) + +static int stbi__decode_jpeg_header(stbi__jpeg *z, int scan) +{ + int m; + z->marker = STBI__MARKER_none; // initialize cached marker to empty + m = stbi__get_marker(z); + if (!stbi__SOI(m)) return stbi__err("no SOI","Corrupt JPEG"); + if (scan == STBI__SCAN_type) return 1; + m = stbi__get_marker(z); + while (!stbi__SOF(m)) { + if (!stbi__process_marker(z,m)) return 0; + m = stbi__get_marker(z); + while (m == STBI__MARKER_none) { + // some files have extra padding after their blocks, so ok, we'll scan + if (stbi__at_eof(z->s)) return stbi__err("no SOF", "Corrupt JPEG"); + m = stbi__get_marker(z); + } + } + z->progressive = stbi__SOF_progressive(m); + if (!stbi__process_frame_header(z, scan)) return 0; + return 1; +} + +// decode image to YCbCr format +static int stbi__decode_jpeg_image(stbi__jpeg *j) +{ + int m; + for (m = 0; m < 4; m++) { + j->img_comp[m].raw_data = NULL; + j->img_comp[m].raw_coeff = NULL; + } + j->restart_interval = 0; + if (!stbi__decode_jpeg_header(j, STBI__SCAN_load)) return 0; + m = stbi__get_marker(j); + while (!stbi__EOI(m)) { + if (stbi__SOS(m)) { + if (!stbi__process_scan_header(j)) return 0; + if (!stbi__parse_entropy_coded_data(j)) return 0; + if (j->marker == STBI__MARKER_none ) { + // handle 0s at the end of image data from IP Kamera 9060 + while (!stbi__at_eof(j->s)) { + int x = stbi__get8(j->s); + if (x == 255) { + j->marker = stbi__get8(j->s); + break; + } else if (x != 0) { + return stbi__err("junk before marker", "Corrupt JPEG"); + } + } + // if we reach eof without hitting a marker, stbi__get_marker() below will fail and we'll eventually return 0 + } + } else { + if (!stbi__process_marker(j, m)) return 0; + } + m = stbi__get_marker(j); + } + if (j->progressive) + stbi__jpeg_finish(j); + return 1; +} + +// static jfif-centered resampling (across block boundaries) + +typedef stbi_uc *(*resample_row_func)(stbi_uc *out, stbi_uc *in0, stbi_uc *in1, + int w, int hs); + +#define stbi__div4(x) ((stbi_uc) ((x) >> 2)) + +static stbi_uc *resample_row_1(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) +{ + STBI_NOTUSED(out); + STBI_NOTUSED(in_far); + STBI_NOTUSED(w); + STBI_NOTUSED(hs); + return in_near; +} + +static stbi_uc* stbi__resample_row_v_2(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) +{ + // need to generate two samples vertically for every one in input + int i; + STBI_NOTUSED(hs); + for (i=0; i < w; ++i) + out[i] = stbi__div4(3*in_near[i] + in_far[i] + 2); + return out; +} + +static stbi_uc* stbi__resample_row_h_2(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) +{ + // need to generate two samples horizontally for every one in input + int i; + stbi_uc *input = in_near; + + if (w == 1) { + // if only one sample, can't do any interpolation + out[0] = out[1] = input[0]; + return out; + } + + out[0] = input[0]; + out[1] = stbi__div4(input[0]*3 + input[1] + 2); + for (i=1; i < w-1; ++i) { + int n = 3*input[i]+2; + out[i*2+0] = stbi__div4(n+input[i-1]); + out[i*2+1] = stbi__div4(n+input[i+1]); + } + out[i*2+0] = stbi__div4(input[w-2]*3 + input[w-1] + 2); + out[i*2+1] = input[w-1]; + + STBI_NOTUSED(in_far); + STBI_NOTUSED(hs); + + return out; +} + +#define stbi__div16(x) ((stbi_uc) ((x) >> 4)) + +static stbi_uc *stbi__resample_row_hv_2(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) +{ + // need to generate 2x2 samples for every one in input + int i,t0,t1; + if (w == 1) { + out[0] = out[1] = stbi__div4(3*in_near[0] + in_far[0] + 2); + return out; + } + + t1 = 3*in_near[0] + in_far[0]; + out[0] = stbi__div4(t1+2); + for (i=1; i < w; ++i) { + t0 = t1; + t1 = 3*in_near[i]+in_far[i]; + out[i*2-1] = stbi__div16(3*t0 + t1 + 8); + out[i*2 ] = stbi__div16(3*t1 + t0 + 8); + } + out[w*2-1] = stbi__div4(t1+2); + + STBI_NOTUSED(hs); + + return out; +} + +#if defined(STBI_SSE2) || defined(STBI_NEON) +static stbi_uc *stbi__resample_row_hv_2_simd(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) +{ + // need to generate 2x2 samples for every one in input + int i=0,t0,t1; + + if (w == 1) { + out[0] = out[1] = stbi__div4(3*in_near[0] + in_far[0] + 2); + return out; + } + + t1 = 3*in_near[0] + in_far[0]; + // process groups of 8 pixels for as long as we can. + // note we can't handle the last pixel in a row in this loop + // because we need to handle the filter boundary conditions. + for (; i < ((w-1) & ~7); i += 8) { +#if defined(STBI_SSE2) + // load and perform the vertical filtering pass + // this uses 3*x + y = 4*x + (y - x) + __m128i zero = _mm_setzero_si128(); + __m128i farb = _mm_loadl_epi64((__m128i *) (in_far + i)); + __m128i nearb = _mm_loadl_epi64((__m128i *) (in_near + i)); + __m128i farw = _mm_unpacklo_epi8(farb, zero); + __m128i nearw = _mm_unpacklo_epi8(nearb, zero); + __m128i diff = _mm_sub_epi16(farw, nearw); + __m128i nears = _mm_slli_epi16(nearw, 2); + __m128i curr = _mm_add_epi16(nears, diff); // current row + + // horizontal filter works the same based on shifted vers of current + // row. "prev" is current row shifted right by 1 pixel; we need to + // insert the previous pixel value (from t1). + // "next" is current row shifted left by 1 pixel, with first pixel + // of next block of 8 pixels added in. + __m128i prv0 = _mm_slli_si128(curr, 2); + __m128i nxt0 = _mm_srli_si128(curr, 2); + __m128i prev = _mm_insert_epi16(prv0, t1, 0); + __m128i next = _mm_insert_epi16(nxt0, 3*in_near[i+8] + in_far[i+8], 7); + + // horizontal filter, polyphase implementation since it's convenient: + // even pixels = 3*cur + prev = cur*4 + (prev - cur) + // odd pixels = 3*cur + next = cur*4 + (next - cur) + // note the shared term. + __m128i bias = _mm_set1_epi16(8); + __m128i curs = _mm_slli_epi16(curr, 2); + __m128i prvd = _mm_sub_epi16(prev, curr); + __m128i nxtd = _mm_sub_epi16(next, curr); + __m128i curb = _mm_add_epi16(curs, bias); + __m128i even = _mm_add_epi16(prvd, curb); + __m128i odd = _mm_add_epi16(nxtd, curb); + + // interleave even and odd pixels, then undo scaling. + __m128i int0 = _mm_unpacklo_epi16(even, odd); + __m128i int1 = _mm_unpackhi_epi16(even, odd); + __m128i de0 = _mm_srli_epi16(int0, 4); + __m128i de1 = _mm_srli_epi16(int1, 4); + + // pack and write output + __m128i outv = _mm_packus_epi16(de0, de1); + _mm_storeu_si128((__m128i *) (out + i*2), outv); +#elif defined(STBI_NEON) + // load and perform the vertical filtering pass + // this uses 3*x + y = 4*x + (y - x) + uint8x8_t farb = vld1_u8(in_far + i); + uint8x8_t nearb = vld1_u8(in_near + i); + int16x8_t diff = vreinterpretq_s16_u16(vsubl_u8(farb, nearb)); + int16x8_t nears = vreinterpretq_s16_u16(vshll_n_u8(nearb, 2)); + int16x8_t curr = vaddq_s16(nears, diff); // current row + + // horizontal filter works the same based on shifted vers of current + // row. "prev" is current row shifted right by 1 pixel; we need to + // insert the previous pixel value (from t1). + // "next" is current row shifted left by 1 pixel, with first pixel + // of next block of 8 pixels added in. + int16x8_t prv0 = vextq_s16(curr, curr, 7); + int16x8_t nxt0 = vextq_s16(curr, curr, 1); + int16x8_t prev = vsetq_lane_s16(t1, prv0, 0); + int16x8_t next = vsetq_lane_s16(3*in_near[i+8] + in_far[i+8], nxt0, 7); + + // horizontal filter, polyphase implementation since it's convenient: + // even pixels = 3*cur + prev = cur*4 + (prev - cur) + // odd pixels = 3*cur + next = cur*4 + (next - cur) + // note the shared term. + int16x8_t curs = vshlq_n_s16(curr, 2); + int16x8_t prvd = vsubq_s16(prev, curr); + int16x8_t nxtd = vsubq_s16(next, curr); + int16x8_t even = vaddq_s16(curs, prvd); + int16x8_t odd = vaddq_s16(curs, nxtd); + + // undo scaling and round, then store with even/odd phases interleaved + uint8x8x2_t o; + o.val[0] = vqrshrun_n_s16(even, 4); + o.val[1] = vqrshrun_n_s16(odd, 4); + vst2_u8(out + i*2, o); +#endif + + // "previous" value for next iter + t1 = 3*in_near[i+7] + in_far[i+7]; + } + + t0 = t1; + t1 = 3*in_near[i] + in_far[i]; + out[i*2] = stbi__div16(3*t1 + t0 + 8); + + for (++i; i < w; ++i) { + t0 = t1; + t1 = 3*in_near[i]+in_far[i]; + out[i*2-1] = stbi__div16(3*t0 + t1 + 8); + out[i*2 ] = stbi__div16(3*t1 + t0 + 8); + } + out[w*2-1] = stbi__div4(t1+2); + + STBI_NOTUSED(hs); + + return out; +} +#endif + +static stbi_uc *stbi__resample_row_generic(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) +{ + // resample with nearest-neighbor + int i,j; + STBI_NOTUSED(in_far); + for (i=0; i < w; ++i) + for (j=0; j < hs; ++j) + out[i*hs+j] = in_near[i]; + return out; +} + +#ifdef STBI_JPEG_OLD +// this is the same YCbCr-to-RGB calculation that stb_image has used +// historically before the algorithm changes in 1.49 +#define float2fixed(x) ((int) ((x) * 65536 + 0.5)) +static void stbi__YCbCr_to_RGB_row(stbi_uc *out, const stbi_uc *y, const stbi_uc *pcb, const stbi_uc *pcr, int count, int step) +{ + int i; + for (i=0; i < count; ++i) { + int y_fixed = (y[i] << 16) + 32768; // rounding + int r,g,b; + int cr = pcr[i] - 128; + int cb = pcb[i] - 128; + r = y_fixed + cr*float2fixed(1.40200f); + g = y_fixed - cr*float2fixed(0.71414f) - cb*float2fixed(0.34414f); + b = y_fixed + cb*float2fixed(1.77200f); + r >>= 16; + g >>= 16; + b >>= 16; + if ((unsigned) r > 255) { if (r < 0) r = 0; else r = 255; } + if ((unsigned) g > 255) { if (g < 0) g = 0; else g = 255; } + if ((unsigned) b > 255) { if (b < 0) b = 0; else b = 255; } + out[0] = (stbi_uc)r; + out[1] = (stbi_uc)g; + out[2] = (stbi_uc)b; + out[3] = 255; + out += step; + } +} +#else +// this is a reduced-precision calculation of YCbCr-to-RGB introduced +// to make sure the code produces the same results in both SIMD and scalar +#define float2fixed(x) (((int) ((x) * 4096.0f + 0.5f)) << 8) +static void stbi__YCbCr_to_RGB_row(stbi_uc *out, const stbi_uc *y, const stbi_uc *pcb, const stbi_uc *pcr, int count, int step) +{ + int i; + for (i=0; i < count; ++i) { + int y_fixed = (y[i] << 20) + (1<<19); // rounding + int r,g,b; + int cr = pcr[i] - 128; + int cb = pcb[i] - 128; + r = y_fixed + cr* float2fixed(1.40200f); + g = y_fixed + (cr*-float2fixed(0.71414f)) + ((cb*-float2fixed(0.34414f)) & 0xffff0000); + b = y_fixed + cb* float2fixed(1.77200f); + r >>= 20; + g >>= 20; + b >>= 20; + if ((unsigned) r > 255) { if (r < 0) r = 0; else r = 255; } + if ((unsigned) g > 255) { if (g < 0) g = 0; else g = 255; } + if ((unsigned) b > 255) { if (b < 0) b = 0; else b = 255; } + out[0] = (stbi_uc)r; + out[1] = (stbi_uc)g; + out[2] = (stbi_uc)b; + out[3] = 255; + out += step; + } +} +#endif + +#if defined(STBI_SSE2) || defined(STBI_NEON) +static void stbi__YCbCr_to_RGB_simd(stbi_uc *out, stbi_uc const *y, stbi_uc const *pcb, stbi_uc const *pcr, int count, int step) +{ + int i = 0; + +#ifdef STBI_SSE2 + // step == 3 is pretty ugly on the final interleave, and i'm not convinced + // it's useful in practice (you wouldn't use it for textures, for example). + // so just accelerate step == 4 case. + if (step == 4) { + // this is a fairly straightforward implementation and not super-optimized. + __m128i signflip = _mm_set1_epi8(-0x80); + __m128i cr_const0 = _mm_set1_epi16( (short) ( 1.40200f*4096.0f+0.5f)); + __m128i cr_const1 = _mm_set1_epi16( - (short) ( 0.71414f*4096.0f+0.5f)); + __m128i cb_const0 = _mm_set1_epi16( - (short) ( 0.34414f*4096.0f+0.5f)); + __m128i cb_const1 = _mm_set1_epi16( (short) ( 1.77200f*4096.0f+0.5f)); + __m128i y_bias = _mm_set1_epi8((char) (unsigned char) 128); + __m128i xw = _mm_set1_epi16(255); // alpha channel + + for (; i+7 < count; i += 8) { + // load + __m128i y_bytes = _mm_loadl_epi64((__m128i *) (y+i)); + __m128i cr_bytes = _mm_loadl_epi64((__m128i *) (pcr+i)); + __m128i cb_bytes = _mm_loadl_epi64((__m128i *) (pcb+i)); + __m128i cr_biased = _mm_xor_si128(cr_bytes, signflip); // -128 + __m128i cb_biased = _mm_xor_si128(cb_bytes, signflip); // -128 + + // unpack to short (and left-shift cr, cb by 8) + __m128i yw = _mm_unpacklo_epi8(y_bias, y_bytes); + __m128i crw = _mm_unpacklo_epi8(_mm_setzero_si128(), cr_biased); + __m128i cbw = _mm_unpacklo_epi8(_mm_setzero_si128(), cb_biased); + + // color transform + __m128i yws = _mm_srli_epi16(yw, 4); + __m128i cr0 = _mm_mulhi_epi16(cr_const0, crw); + __m128i cb0 = _mm_mulhi_epi16(cb_const0, cbw); + __m128i cb1 = _mm_mulhi_epi16(cbw, cb_const1); + __m128i cr1 = _mm_mulhi_epi16(crw, cr_const1); + __m128i rws = _mm_add_epi16(cr0, yws); + __m128i gwt = _mm_add_epi16(cb0, yws); + __m128i bws = _mm_add_epi16(yws, cb1); + __m128i gws = _mm_add_epi16(gwt, cr1); + + // descale + __m128i rw = _mm_srai_epi16(rws, 4); + __m128i bw = _mm_srai_epi16(bws, 4); + __m128i gw = _mm_srai_epi16(gws, 4); + + // back to byte, set up for transpose + __m128i brb = _mm_packus_epi16(rw, bw); + __m128i gxb = _mm_packus_epi16(gw, xw); + + // transpose to interleave channels + __m128i t0 = _mm_unpacklo_epi8(brb, gxb); + __m128i t1 = _mm_unpackhi_epi8(brb, gxb); + __m128i o0 = _mm_unpacklo_epi16(t0, t1); + __m128i o1 = _mm_unpackhi_epi16(t0, t1); + + // store + _mm_storeu_si128((__m128i *) (out + 0), o0); + _mm_storeu_si128((__m128i *) (out + 16), o1); + out += 32; + } + } +#endif + +#ifdef STBI_NEON + // in this version, step=3 support would be easy to add. but is there demand? + if (step == 4) { + // this is a fairly straightforward implementation and not super-optimized. + uint8x8_t signflip = vdup_n_u8(0x80); + int16x8_t cr_const0 = vdupq_n_s16( (short) ( 1.40200f*4096.0f+0.5f)); + int16x8_t cr_const1 = vdupq_n_s16( - (short) ( 0.71414f*4096.0f+0.5f)); + int16x8_t cb_const0 = vdupq_n_s16( - (short) ( 0.34414f*4096.0f+0.5f)); + int16x8_t cb_const1 = vdupq_n_s16( (short) ( 1.77200f*4096.0f+0.5f)); + + for (; i+7 < count; i += 8) { + // load + uint8x8_t y_bytes = vld1_u8(y + i); + uint8x8_t cr_bytes = vld1_u8(pcr + i); + uint8x8_t cb_bytes = vld1_u8(pcb + i); + int8x8_t cr_biased = vreinterpret_s8_u8(vsub_u8(cr_bytes, signflip)); + int8x8_t cb_biased = vreinterpret_s8_u8(vsub_u8(cb_bytes, signflip)); + + // expand to s16 + int16x8_t yws = vreinterpretq_s16_u16(vshll_n_u8(y_bytes, 4)); + int16x8_t crw = vshll_n_s8(cr_biased, 7); + int16x8_t cbw = vshll_n_s8(cb_biased, 7); + + // color transform + int16x8_t cr0 = vqdmulhq_s16(crw, cr_const0); + int16x8_t cb0 = vqdmulhq_s16(cbw, cb_const0); + int16x8_t cr1 = vqdmulhq_s16(crw, cr_const1); + int16x8_t cb1 = vqdmulhq_s16(cbw, cb_const1); + int16x8_t rws = vaddq_s16(yws, cr0); + int16x8_t gws = vaddq_s16(vaddq_s16(yws, cb0), cr1); + int16x8_t bws = vaddq_s16(yws, cb1); + + // undo scaling, round, convert to byte + uint8x8x4_t o; + o.val[0] = vqrshrun_n_s16(rws, 4); + o.val[1] = vqrshrun_n_s16(gws, 4); + o.val[2] = vqrshrun_n_s16(bws, 4); + o.val[3] = vdup_n_u8(255); + + // store, interleaving r/g/b/a + vst4_u8(out, o); + out += 8*4; + } + } +#endif + + for (; i < count; ++i) { + int y_fixed = (y[i] << 20) + (1<<19); // rounding + int r,g,b; + int cr = pcr[i] - 128; + int cb = pcb[i] - 128; + r = y_fixed + cr* float2fixed(1.40200f); + g = y_fixed + cr*-float2fixed(0.71414f) + ((cb*-float2fixed(0.34414f)) & 0xffff0000); + b = y_fixed + cb* float2fixed(1.77200f); + r >>= 20; + g >>= 20; + b >>= 20; + if ((unsigned) r > 255) { if (r < 0) r = 0; else r = 255; } + if ((unsigned) g > 255) { if (g < 0) g = 0; else g = 255; } + if ((unsigned) b > 255) { if (b < 0) b = 0; else b = 255; } + out[0] = (stbi_uc)r; + out[1] = (stbi_uc)g; + out[2] = (stbi_uc)b; + out[3] = 255; + out += step; + } +} +#endif + +// set up the kernels +static void stbi__setup_jpeg(stbi__jpeg *j) +{ + j->idct_block_kernel = stbi__idct_block; + j->YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_row; + j->resample_row_hv_2_kernel = stbi__resample_row_hv_2; + +#ifdef STBI_SSE2 + if (stbi__sse2_available()) { + j->idct_block_kernel = stbi__idct_simd; + #ifndef STBI_JPEG_OLD + j->YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_simd; + #endif + j->resample_row_hv_2_kernel = stbi__resample_row_hv_2_simd; + } +#endif + +#ifdef STBI_NEON + j->idct_block_kernel = stbi__idct_simd; + #ifndef STBI_JPEG_OLD + j->YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_simd; + #endif + j->resample_row_hv_2_kernel = stbi__resample_row_hv_2_simd; +#endif +} + +// clean up the temporary component buffers +static void stbi__cleanup_jpeg(stbi__jpeg *j) +{ + int i; + for (i=0; i < j->s->img_n; ++i) { + if (j->img_comp[i].raw_data) { + STBI_FREE(j->img_comp[i].raw_data); + j->img_comp[i].raw_data = NULL; + j->img_comp[i].data = NULL; + } + if (j->img_comp[i].raw_coeff) { + STBI_FREE(j->img_comp[i].raw_coeff); + j->img_comp[i].raw_coeff = 0; + j->img_comp[i].coeff = 0; + } + if (j->img_comp[i].linebuf) { + STBI_FREE(j->img_comp[i].linebuf); + j->img_comp[i].linebuf = NULL; + } + } +} + +typedef struct +{ + resample_row_func resample; + stbi_uc *line0,*line1; + int hs,vs; // expansion factor in each axis + int w_lores; // horizontal pixels pre-expansion + int ystep; // how far through vertical expansion we are + int ypos; // which pre-expansion row we're on +} stbi__resample; + +static stbi_uc *load_jpeg_image(stbi__jpeg *z, int *out_x, int *out_y, int *comp, int req_comp) +{ + int n, decode_n; + z->s->img_n = 0; // make stbi__cleanup_jpeg safe + + // validate req_comp + if (req_comp < 0 || req_comp > 4) return stbi__errpuc("bad req_comp", "Internal error"); + + // load a jpeg image from whichever source, but leave in YCbCr format + if (!stbi__decode_jpeg_image(z)) { stbi__cleanup_jpeg(z); return NULL; } + + // determine actual number of components to generate + n = req_comp ? req_comp : z->s->img_n; + + if (z->s->img_n == 3 && n < 3) + decode_n = 1; + else + decode_n = z->s->img_n; + + // resample and color-convert + { + int k; + unsigned int i,j; + stbi_uc *output; + stbi_uc *coutput[4]; + + stbi__resample res_comp[4]; + + for (k=0; k < decode_n; ++k) { + stbi__resample *r = &res_comp[k]; + + // allocate line buffer big enough for upsampling off the edges + // with upsample factor of 4 + z->img_comp[k].linebuf = (stbi_uc *) stbi__malloc(z->s->img_x + 3); + if (!z->img_comp[k].linebuf) { stbi__cleanup_jpeg(z); return stbi__errpuc("outofmem", "Out of memory"); } + + r->hs = z->img_h_max / z->img_comp[k].h; + r->vs = z->img_v_max / z->img_comp[k].v; + r->ystep = r->vs >> 1; + r->w_lores = (z->s->img_x + r->hs-1) / r->hs; + r->ypos = 0; + r->line0 = r->line1 = z->img_comp[k].data; + + if (r->hs == 1 && r->vs == 1) r->resample = resample_row_1; + else if (r->hs == 1 && r->vs == 2) r->resample = stbi__resample_row_v_2; + else if (r->hs == 2 && r->vs == 1) r->resample = stbi__resample_row_h_2; + else if (r->hs == 2 && r->vs == 2) r->resample = z->resample_row_hv_2_kernel; + else r->resample = stbi__resample_row_generic; + } + + // can't error after this so, this is safe + output = (stbi_uc *) stbi__malloc(n * z->s->img_x * z->s->img_y + 1); + if (!output) { stbi__cleanup_jpeg(z); return stbi__errpuc("outofmem", "Out of memory"); } + + // now go ahead and resample + for (j=0; j < z->s->img_y; ++j) { + stbi_uc *out = output + n * z->s->img_x * j; + for (k=0; k < decode_n; ++k) { + stbi__resample *r = &res_comp[k]; + int y_bot = r->ystep >= (r->vs >> 1); + coutput[k] = r->resample(z->img_comp[k].linebuf, + y_bot ? r->line1 : r->line0, + y_bot ? r->line0 : r->line1, + r->w_lores, r->hs); + if (++r->ystep >= r->vs) { + r->ystep = 0; + r->line0 = r->line1; + if (++r->ypos < z->img_comp[k].y) + r->line1 += z->img_comp[k].w2; + } + } + if (n >= 3) { + stbi_uc *y = coutput[0]; + if (z->s->img_n == 3) { + z->YCbCr_to_RGB_kernel(out, y, coutput[1], coutput[2], z->s->img_x, n); + } else + for (i=0; i < z->s->img_x; ++i) { + out[0] = out[1] = out[2] = y[i]; + out[3] = 255; // not used if n==3 + out += n; + } + } else { + stbi_uc *y = coutput[0]; + if (n == 1) + for (i=0; i < z->s->img_x; ++i) out[i] = y[i]; + else + for (i=0; i < z->s->img_x; ++i) *out++ = y[i], *out++ = 255; + } + } + stbi__cleanup_jpeg(z); + *out_x = z->s->img_x; + *out_y = z->s->img_y; + if (comp) *comp = z->s->img_n; // report original components, not output + return output; + } +} + +static unsigned char *stbi__jpeg_load(stbi__context *s, int *x, int *y, int *comp, int req_comp) +{ + stbi__jpeg j; + j.s = s; + stbi__setup_jpeg(&j); + return load_jpeg_image(&j, x,y,comp,req_comp); +} + +static int stbi__jpeg_test(stbi__context *s) +{ + int r; + stbi__jpeg j; + j.s = s; + stbi__setup_jpeg(&j); + r = stbi__decode_jpeg_header(&j, STBI__SCAN_type); + stbi__rewind(s); + return r; +} + +static int stbi__jpeg_info_raw(stbi__jpeg *j, int *x, int *y, int *comp) +{ + if (!stbi__decode_jpeg_header(j, STBI__SCAN_header)) { + stbi__rewind( j->s ); + return 0; + } + if (x) *x = j->s->img_x; + if (y) *y = j->s->img_y; + if (comp) *comp = j->s->img_n; + return 1; +} + +static int stbi__jpeg_info(stbi__context *s, int *x, int *y, int *comp) +{ + stbi__jpeg j; + j.s = s; + return stbi__jpeg_info_raw(&j, x, y, comp); +} +#endif + +// public domain zlib decode v0.2 Sean Barrett 2006-11-18 +// simple implementation +// - all input must be provided in an upfront buffer +// - all output is written to a single output buffer (can malloc/realloc) +// performance +// - fast huffman + +#ifndef STBI_NO_ZLIB + +// fast-way is faster to check than jpeg huffman, but slow way is slower +#define STBI__ZFAST_BITS 9 // accelerate all cases in default tables +#define STBI__ZFAST_MASK ((1 << STBI__ZFAST_BITS) - 1) + +// zlib-style huffman encoding +// (jpegs packs from left, zlib from right, so can't share code) +typedef struct +{ + stbi__uint16 fast[1 << STBI__ZFAST_BITS]; + stbi__uint16 firstcode[16]; + int maxcode[17]; + stbi__uint16 firstsymbol[16]; + stbi_uc size[288]; + stbi__uint16 value[288]; +} stbi__zhuffman; + +stbi_inline static int stbi__bitreverse16(int n) +{ + n = ((n & 0xAAAA) >> 1) | ((n & 0x5555) << 1); + n = ((n & 0xCCCC) >> 2) | ((n & 0x3333) << 2); + n = ((n & 0xF0F0) >> 4) | ((n & 0x0F0F) << 4); + n = ((n & 0xFF00) >> 8) | ((n & 0x00FF) << 8); + return n; +} + +stbi_inline static int stbi__bit_reverse(int v, int bits) +{ + STBI_ASSERT(bits <= 16); + // to bit reverse n bits, reverse 16 and shift + // e.g. 11 bits, bit reverse and shift away 5 + return stbi__bitreverse16(v) >> (16-bits); +} + +static int stbi__zbuild_huffman(stbi__zhuffman *z, stbi_uc *sizelist, int num) +{ + int i,k=0; + int code, next_code[16], sizes[17]; + + // DEFLATE spec for generating codes + memset(sizes, 0, sizeof(sizes)); + memset(z->fast, 0, sizeof(z->fast)); + for (i=0; i < num; ++i) + ++sizes[sizelist[i]]; + sizes[0] = 0; + for (i=1; i < 16; ++i) + if (sizes[i] > (1 << i)) + return stbi__err("bad sizes", "Corrupt PNG"); + code = 0; + for (i=1; i < 16; ++i) { + next_code[i] = code; + z->firstcode[i] = (stbi__uint16) code; + z->firstsymbol[i] = (stbi__uint16) k; + code = (code + sizes[i]); + if (sizes[i]) + if (code-1 >= (1 << i)) return stbi__err("bad codelengths","Corrupt PNG"); + z->maxcode[i] = code << (16-i); // preshift for inner loop + code <<= 1; + k += sizes[i]; + } + z->maxcode[16] = 0x10000; // sentinel + for (i=0; i < num; ++i) { + int s = sizelist[i]; + if (s) { + int c = next_code[s] - z->firstcode[s] + z->firstsymbol[s]; + stbi__uint16 fastv = (stbi__uint16) ((s << 9) | i); + z->size [c] = (stbi_uc ) s; + z->value[c] = (stbi__uint16) i; + if (s <= STBI__ZFAST_BITS) { + int k = stbi__bit_reverse(next_code[s],s); + while (k < (1 << STBI__ZFAST_BITS)) { + z->fast[k] = fastv; + k += (1 << s); + } + } + ++next_code[s]; + } + } + return 1; +} + +// zlib-from-memory implementation for PNG reading +// because PNG allows splitting the zlib stream arbitrarily, +// and it's annoying structurally to have PNG call ZLIB call PNG, +// we require PNG read all the IDATs and combine them into a single +// memory buffer + +typedef struct +{ + stbi_uc *zbuffer, *zbuffer_end; + int num_bits; + stbi__uint32 code_buffer; + + char *zout; + char *zout_start; + char *zout_end; + int z_expandable; + + stbi__zhuffman z_length, z_distance; +} stbi__zbuf; + +stbi_inline static stbi_uc stbi__zget8(stbi__zbuf *z) +{ + if (z->zbuffer >= z->zbuffer_end) return 0; + return *z->zbuffer++; +} + +static void stbi__fill_bits(stbi__zbuf *z) +{ + do { + STBI_ASSERT(z->code_buffer < (1U << z->num_bits)); + z->code_buffer |= stbi__zget8(z) << z->num_bits; + z->num_bits += 8; + } while (z->num_bits <= 24); +} + +stbi_inline static unsigned int stbi__zreceive(stbi__zbuf *z, int n) +{ + unsigned int k; + if (z->num_bits < n) stbi__fill_bits(z); + k = z->code_buffer & ((1 << n) - 1); + z->code_buffer >>= n; + z->num_bits -= n; + return k; +} + +static int stbi__zhuffman_decode_slowpath(stbi__zbuf *a, stbi__zhuffman *z) +{ + int b,s,k; + // not resolved by fast table, so compute it the slow way + // use jpeg approach, which requires MSbits at top + k = stbi__bit_reverse(a->code_buffer, 16); + for (s=STBI__ZFAST_BITS+1; ; ++s) + if (k < z->maxcode[s]) + break; + if (s == 16) return -1; // invalid code! + // code size is s, so: + b = (k >> (16-s)) - z->firstcode[s] + z->firstsymbol[s]; + STBI_ASSERT(z->size[b] == s); + a->code_buffer >>= s; + a->num_bits -= s; + return z->value[b]; +} + +stbi_inline static int stbi__zhuffman_decode(stbi__zbuf *a, stbi__zhuffman *z) +{ + int b,s; + if (a->num_bits < 16) stbi__fill_bits(a); + b = z->fast[a->code_buffer & STBI__ZFAST_MASK]; + if (b) { + s = b >> 9; + a->code_buffer >>= s; + a->num_bits -= s; + return b & 511; + } + return stbi__zhuffman_decode_slowpath(a, z); +} + +static int stbi__zexpand(stbi__zbuf *z, char *zout, int n) // need to make room for n bytes +{ + char *q; + int cur, limit; + z->zout = zout; + if (!z->z_expandable) return stbi__err("output buffer limit","Corrupt PNG"); + cur = (int) (z->zout - z->zout_start); + limit = (int) (z->zout_end - z->zout_start); + while (cur + n > limit) + limit *= 2; + q = (char *) STBI_REALLOC(z->zout_start, limit); + if (q == NULL) return stbi__err("outofmem", "Out of memory"); + z->zout_start = q; + z->zout = q + cur; + z->zout_end = q + limit; + return 1; +} + +static int stbi__zlength_base[31] = { + 3,4,5,6,7,8,9,10,11,13, + 15,17,19,23,27,31,35,43,51,59, + 67,83,99,115,131,163,195,227,258,0,0 }; + +static int stbi__zlength_extra[31]= +{ 0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0,0,0 }; + +static int stbi__zdist_base[32] = { 1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193, +257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577,0,0}; + +static int stbi__zdist_extra[32] = +{ 0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13}; + +static int stbi__parse_huffman_block(stbi__zbuf *a) +{ + char *zout = a->zout; + for(;;) { + int z = stbi__zhuffman_decode(a, &a->z_length); + if (z < 256) { + if (z < 0) return stbi__err("bad huffman code","Corrupt PNG"); // error in huffman codes + if (zout >= a->zout_end) { + if (!stbi__zexpand(a, zout, 1)) return 0; + zout = a->zout; + } + *zout++ = (char) z; + } else { + stbi_uc *p; + int len,dist; + if (z == 256) { + a->zout = zout; + return 1; + } + z -= 257; + len = stbi__zlength_base[z]; + if (stbi__zlength_extra[z]) len += stbi__zreceive(a, stbi__zlength_extra[z]); + z = stbi__zhuffman_decode(a, &a->z_distance); + if (z < 0) return stbi__err("bad huffman code","Corrupt PNG"); + dist = stbi__zdist_base[z]; + if (stbi__zdist_extra[z]) dist += stbi__zreceive(a, stbi__zdist_extra[z]); + if (zout - a->zout_start < dist) return stbi__err("bad dist","Corrupt PNG"); + if (zout + len > a->zout_end) { + if (!stbi__zexpand(a, zout, len)) return 0; + zout = a->zout; + } + p = (stbi_uc *) (zout - dist); + if (dist == 1) { // run of one byte; common in images. + stbi_uc v = *p; + if (len) { do *zout++ = v; while (--len); } + } else { + if (len) { do *zout++ = *p++; while (--len); } + } + } + } +} + +static int stbi__compute_huffman_codes(stbi__zbuf *a) +{ + static stbi_uc length_dezigzag[19] = { 16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15 }; + stbi__zhuffman z_codelength; + stbi_uc lencodes[286+32+137];//padding for maximum single op + stbi_uc codelength_sizes[19]; + int i,n; + + int hlit = stbi__zreceive(a,5) + 257; + int hdist = stbi__zreceive(a,5) + 1; + int hclen = stbi__zreceive(a,4) + 4; + + memset(codelength_sizes, 0, sizeof(codelength_sizes)); + for (i=0; i < hclen; ++i) { + int s = stbi__zreceive(a,3); + codelength_sizes[length_dezigzag[i]] = (stbi_uc) s; + } + if (!stbi__zbuild_huffman(&z_codelength, codelength_sizes, 19)) return 0; + + n = 0; + while (n < hlit + hdist) { + int c = stbi__zhuffman_decode(a, &z_codelength); + if (c < 0 || c >= 19) return stbi__err("bad codelengths", "Corrupt PNG"); + if (c < 16) + lencodes[n++] = (stbi_uc) c; + else if (c == 16) { + c = stbi__zreceive(a,2)+3; + memset(lencodes+n, lencodes[n-1], c); + n += c; + } else if (c == 17) { + c = stbi__zreceive(a,3)+3; + memset(lencodes+n, 0, c); + n += c; + } else { + STBI_ASSERT(c == 18); + c = stbi__zreceive(a,7)+11; + memset(lencodes+n, 0, c); + n += c; + } + } + if (n != hlit+hdist) return stbi__err("bad codelengths","Corrupt PNG"); + if (!stbi__zbuild_huffman(&a->z_length, lencodes, hlit)) return 0; + if (!stbi__zbuild_huffman(&a->z_distance, lencodes+hlit, hdist)) return 0; + return 1; +} + +static int stbi__parse_uncomperssed_block(stbi__zbuf *a) +{ + stbi_uc header[4]; + int len,nlen,k; + if (a->num_bits & 7) + stbi__zreceive(a, a->num_bits & 7); // discard + // drain the bit-packed data into header + k = 0; + while (a->num_bits > 0) { + header[k++] = (stbi_uc) (a->code_buffer & 255); // suppress MSVC run-time check + a->code_buffer >>= 8; + a->num_bits -= 8; + } + STBI_ASSERT(a->num_bits == 0); + // now fill header the normal way + while (k < 4) + header[k++] = stbi__zget8(a); + len = header[1] * 256 + header[0]; + nlen = header[3] * 256 + header[2]; + if (nlen != (len ^ 0xffff)) return stbi__err("zlib corrupt","Corrupt PNG"); + if (a->zbuffer + len > a->zbuffer_end) return stbi__err("read past buffer","Corrupt PNG"); + if (a->zout + len > a->zout_end) + if (!stbi__zexpand(a, a->zout, len)) return 0; + memcpy(a->zout, a->zbuffer, len); + a->zbuffer += len; + a->zout += len; + return 1; +} + +static int stbi__parse_zlib_header(stbi__zbuf *a) +{ + int cmf = stbi__zget8(a); + int cm = cmf & 15; + /* int cinfo = cmf >> 4; */ + int flg = stbi__zget8(a); + if ((cmf*256+flg) % 31 != 0) return stbi__err("bad zlib header","Corrupt PNG"); // zlib spec + if (flg & 32) return stbi__err("no preset dict","Corrupt PNG"); // preset dictionary not allowed in png + if (cm != 8) return stbi__err("bad compression","Corrupt PNG"); // DEFLATE required for png + // window = 1 << (8 + cinfo)... but who cares, we fully buffer output + return 1; +} + +// @TODO: should statically initialize these for optimal thread safety +static stbi_uc stbi__zdefault_length[288], stbi__zdefault_distance[32]; +static void stbi__init_zdefaults(void) +{ + int i; // use <= to match clearly with spec + for (i=0; i <= 143; ++i) stbi__zdefault_length[i] = 8; + for ( ; i <= 255; ++i) stbi__zdefault_length[i] = 9; + for ( ; i <= 279; ++i) stbi__zdefault_length[i] = 7; + for ( ; i <= 287; ++i) stbi__zdefault_length[i] = 8; + + for (i=0; i <= 31; ++i) stbi__zdefault_distance[i] = 5; +} + +static int stbi__parse_zlib(stbi__zbuf *a, int parse_header) +{ + int final, type; + if (parse_header) + if (!stbi__parse_zlib_header(a)) return 0; + a->num_bits = 0; + a->code_buffer = 0; + do { + final = stbi__zreceive(a,1); + type = stbi__zreceive(a,2); + if (type == 0) { + if (!stbi__parse_uncomperssed_block(a)) return 0; + } else if (type == 3) { + return 0; + } else { + if (type == 1) { + // use fixed code lengths + if (!stbi__zdefault_distance[31]) stbi__init_zdefaults(); + if (!stbi__zbuild_huffman(&a->z_length , stbi__zdefault_length , 288)) return 0; + if (!stbi__zbuild_huffman(&a->z_distance, stbi__zdefault_distance, 32)) return 0; + } else { + if (!stbi__compute_huffman_codes(a)) return 0; + } + if (!stbi__parse_huffman_block(a)) return 0; + } + } while (!final); + return 1; +} + +static int stbi__do_zlib(stbi__zbuf *a, char *obuf, int olen, int exp, int parse_header) +{ + a->zout_start = obuf; + a->zout = obuf; + a->zout_end = obuf + olen; + a->z_expandable = exp; + + return stbi__parse_zlib(a, parse_header); +} + +STBIDEF char *stbi_zlib_decode_malloc_guesssize(const char *buffer, int len, int initial_size, int *outlen) +{ + stbi__zbuf a; + char *p = (char *) stbi__malloc(initial_size); + if (p == NULL) return NULL; + a.zbuffer = (stbi_uc *) buffer; + a.zbuffer_end = (stbi_uc *) buffer + len; + if (stbi__do_zlib(&a, p, initial_size, 1, 1)) { + if (outlen) *outlen = (int) (a.zout - a.zout_start); + return a.zout_start; + } else { + STBI_FREE(a.zout_start); + return NULL; + } +} + +STBIDEF char *stbi_zlib_decode_malloc(char const *buffer, int len, int *outlen) +{ + return stbi_zlib_decode_malloc_guesssize(buffer, len, 16384, outlen); +} + +STBIDEF char *stbi_zlib_decode_malloc_guesssize_headerflag(const char *buffer, int len, int initial_size, int *outlen, int parse_header) +{ + stbi__zbuf a; + char *p = (char *) stbi__malloc(initial_size); + if (p == NULL) return NULL; + a.zbuffer = (stbi_uc *) buffer; + a.zbuffer_end = (stbi_uc *) buffer + len; + if (stbi__do_zlib(&a, p, initial_size, 1, parse_header)) { + if (outlen) *outlen = (int) (a.zout - a.zout_start); + return a.zout_start; + } else { + STBI_FREE(a.zout_start); + return NULL; + } +} + +STBIDEF int stbi_zlib_decode_buffer(char *obuffer, int olen, char const *ibuffer, int ilen) +{ + stbi__zbuf a; + a.zbuffer = (stbi_uc *) ibuffer; + a.zbuffer_end = (stbi_uc *) ibuffer + ilen; + if (stbi__do_zlib(&a, obuffer, olen, 0, 1)) + return (int) (a.zout - a.zout_start); + else + return -1; +} + +STBIDEF char *stbi_zlib_decode_noheader_malloc(char const *buffer, int len, int *outlen) +{ + stbi__zbuf a; + char *p = (char *) stbi__malloc(16384); + if (p == NULL) return NULL; + a.zbuffer = (stbi_uc *) buffer; + a.zbuffer_end = (stbi_uc *) buffer+len; + if (stbi__do_zlib(&a, p, 16384, 1, 0)) { + if (outlen) *outlen = (int) (a.zout - a.zout_start); + return a.zout_start; + } else { + STBI_FREE(a.zout_start); + return NULL; + } +} + +STBIDEF int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const char *ibuffer, int ilen) +{ + stbi__zbuf a; + a.zbuffer = (stbi_uc *) ibuffer; + a.zbuffer_end = (stbi_uc *) ibuffer + ilen; + if (stbi__do_zlib(&a, obuffer, olen, 0, 0)) + return (int) (a.zout - a.zout_start); + else + return -1; +} +#endif + +// public domain "baseline" PNG decoder v0.10 Sean Barrett 2006-11-18 +// simple implementation +// - only 8-bit samples +// - no CRC checking +// - allocates lots of intermediate memory +// - avoids problem of streaming data between subsystems +// - avoids explicit window management +// performance +// - uses stb_zlib, a PD zlib implementation with fast huffman decoding + +#ifndef STBI_NO_PNG +typedef struct +{ + stbi__uint32 length; + stbi__uint32 type; +} stbi__pngchunk; + +static stbi__pngchunk stbi__get_chunk_header(stbi__context *s) +{ + stbi__pngchunk c; + c.length = stbi__get32be(s); + c.type = stbi__get32be(s); + return c; +} + +static int stbi__check_png_header(stbi__context *s) +{ + static stbi_uc png_sig[8] = { 137,80,78,71,13,10,26,10 }; + int i; + for (i=0; i < 8; ++i) + if (stbi__get8(s) != png_sig[i]) return stbi__err("bad png sig","Not a PNG"); + return 1; +} + +typedef struct +{ + stbi__context *s; + stbi_uc *idata, *expanded, *out; +} stbi__png; + + +enum { + STBI__F_none=0, + STBI__F_sub=1, + STBI__F_up=2, + STBI__F_avg=3, + STBI__F_paeth=4, + // synthetic filters used for first scanline to avoid needing a dummy row of 0s + STBI__F_avg_first, + STBI__F_paeth_first +}; + +static stbi_uc first_row_filter[5] = +{ + STBI__F_none, + STBI__F_sub, + STBI__F_none, + STBI__F_avg_first, + STBI__F_paeth_first +}; + +static int stbi__paeth(int a, int b, int c) +{ + int p = a + b - c; + int pa = abs(p-a); + int pb = abs(p-b); + int pc = abs(p-c); + if (pa <= pb && pa <= pc) return a; + if (pb <= pc) return b; + return c; +} + +static stbi_uc stbi__depth_scale_table[9] = { 0, 0xff, 0x55, 0, 0x11, 0,0,0, 0x01 }; + +// create the png data from post-deflated data +static int stbi__create_png_image_raw(stbi__png *a, stbi_uc *raw, stbi__uint32 raw_len, int out_n, stbi__uint32 x, stbi__uint32 y, int depth, int color) +{ + stbi__context *s = a->s; + stbi__uint32 i,j,stride = x*out_n; + stbi__uint32 img_len, img_width_bytes; + int k; + int img_n = s->img_n; // copy it into a local for later + + STBI_ASSERT(out_n == s->img_n || out_n == s->img_n+1); + a->out = (stbi_uc *) stbi__malloc(x * y * out_n); // extra bytes to write off the end into + if (!a->out) return stbi__err("outofmem", "Out of memory"); + + img_width_bytes = (((img_n * x * depth) + 7) >> 3); + img_len = (img_width_bytes + 1) * y; + if (s->img_x == x && s->img_y == y) { + if (raw_len != img_len) return stbi__err("not enough pixels","Corrupt PNG"); + } else { // interlaced: + if (raw_len < img_len) return stbi__err("not enough pixels","Corrupt PNG"); + } + + for (j=0; j < y; ++j) { + stbi_uc *cur = a->out + stride*j; + stbi_uc *prior = cur - stride; + int filter = *raw++; + int filter_bytes = img_n; + int width = x; + if (filter > 4) + return stbi__err("invalid filter","Corrupt PNG"); + + if (depth < 8) { + STBI_ASSERT(img_width_bytes <= x); + cur += x*out_n - img_width_bytes; // store output to the rightmost img_len bytes, so we can decode in place + filter_bytes = 1; + width = img_width_bytes; + } + + // if first row, use special filter that doesn't sample previous row + if (j == 0) filter = first_row_filter[filter]; + + // handle first byte explicitly + for (k=0; k < filter_bytes; ++k) { + switch (filter) { + case STBI__F_none : cur[k] = raw[k]; break; + case STBI__F_sub : cur[k] = raw[k]; break; + case STBI__F_up : cur[k] = STBI__BYTECAST(raw[k] + prior[k]); break; + case STBI__F_avg : cur[k] = STBI__BYTECAST(raw[k] + (prior[k]>>1)); break; + case STBI__F_paeth : cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(0,prior[k],0)); break; + case STBI__F_avg_first : cur[k] = raw[k]; break; + case STBI__F_paeth_first: cur[k] = raw[k]; break; + } + } + + if (depth == 8) { + if (img_n != out_n) + cur[img_n] = 255; // first pixel + raw += img_n; + cur += out_n; + prior += out_n; + } else { + raw += 1; + cur += 1; + prior += 1; + } + + // this is a little gross, so that we don't switch per-pixel or per-component + if (depth < 8 || img_n == out_n) { + int nk = (width - 1)*img_n; + #define CASE(f) \ + case f: \ + for (k=0; k < nk; ++k) + switch (filter) { + // "none" filter turns into a memcpy here; make that explicit. + case STBI__F_none: memcpy(cur, raw, nk); break; + CASE(STBI__F_sub) cur[k] = STBI__BYTECAST(raw[k] + cur[k-filter_bytes]); break; + CASE(STBI__F_up) cur[k] = STBI__BYTECAST(raw[k] + prior[k]); break; + CASE(STBI__F_avg) cur[k] = STBI__BYTECAST(raw[k] + ((prior[k] + cur[k-filter_bytes])>>1)); break; + CASE(STBI__F_paeth) cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-filter_bytes],prior[k],prior[k-filter_bytes])); break; + CASE(STBI__F_avg_first) cur[k] = STBI__BYTECAST(raw[k] + (cur[k-filter_bytes] >> 1)); break; + CASE(STBI__F_paeth_first) cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-filter_bytes],0,0)); break; + } + #undef CASE + raw += nk; + } else { + STBI_ASSERT(img_n+1 == out_n); + #define CASE(f) \ + case f: \ + for (i=x-1; i >= 1; --i, cur[img_n]=255,raw+=img_n,cur+=out_n,prior+=out_n) \ + for (k=0; k < img_n; ++k) + switch (filter) { + CASE(STBI__F_none) cur[k] = raw[k]; break; + CASE(STBI__F_sub) cur[k] = STBI__BYTECAST(raw[k] + cur[k-out_n]); break; + CASE(STBI__F_up) cur[k] = STBI__BYTECAST(raw[k] + prior[k]); break; + CASE(STBI__F_avg) cur[k] = STBI__BYTECAST(raw[k] + ((prior[k] + cur[k-out_n])>>1)); break; + CASE(STBI__F_paeth) cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-out_n],prior[k],prior[k-out_n])); break; + CASE(STBI__F_avg_first) cur[k] = STBI__BYTECAST(raw[k] + (cur[k-out_n] >> 1)); break; + CASE(STBI__F_paeth_first) cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-out_n],0,0)); break; + } + #undef CASE + } + } + + // we make a separate pass to expand bits to pixels; for performance, + // this could run two scanlines behind the above code, so it won't + // intefere with filtering but will still be in the cache. + if (depth < 8) { + for (j=0; j < y; ++j) { + stbi_uc *cur = a->out + stride*j; + stbi_uc *in = a->out + stride*j + x*out_n - img_width_bytes; + // unpack 1/2/4-bit into a 8-bit buffer. allows us to keep the common 8-bit path optimal at minimal cost for 1/2/4-bit + // png guarante byte alignment, if width is not multiple of 8/4/2 we'll decode dummy trailing data that will be skipped in the later loop + stbi_uc scale = (color == 0) ? stbi__depth_scale_table[depth] : 1; // scale grayscale values to 0..255 range + + // note that the final byte might overshoot and write more data than desired. + // we can allocate enough data that this never writes out of memory, but it + // could also overwrite the next scanline. can it overwrite non-empty data + // on the next scanline? yes, consider 1-pixel-wide scanlines with 1-bit-per-pixel. + // so we need to explicitly clamp the final ones + + if (depth == 4) { + for (k=x*img_n; k >= 2; k-=2, ++in) { + *cur++ = scale * ((*in >> 4) ); + *cur++ = scale * ((*in ) & 0x0f); + } + if (k > 0) *cur++ = scale * ((*in >> 4) ); + } else if (depth == 2) { + for (k=x*img_n; k >= 4; k-=4, ++in) { + *cur++ = scale * ((*in >> 6) ); + *cur++ = scale * ((*in >> 4) & 0x03); + *cur++ = scale * ((*in >> 2) & 0x03); + *cur++ = scale * ((*in ) & 0x03); + } + if (k > 0) *cur++ = scale * ((*in >> 6) ); + if (k > 1) *cur++ = scale * ((*in >> 4) & 0x03); + if (k > 2) *cur++ = scale * ((*in >> 2) & 0x03); + } else if (depth == 1) { + for (k=x*img_n; k >= 8; k-=8, ++in) { + *cur++ = scale * ((*in >> 7) ); + *cur++ = scale * ((*in >> 6) & 0x01); + *cur++ = scale * ((*in >> 5) & 0x01); + *cur++ = scale * ((*in >> 4) & 0x01); + *cur++ = scale * ((*in >> 3) & 0x01); + *cur++ = scale * ((*in >> 2) & 0x01); + *cur++ = scale * ((*in >> 1) & 0x01); + *cur++ = scale * ((*in ) & 0x01); + } + if (k > 0) *cur++ = scale * ((*in >> 7) ); + if (k > 1) *cur++ = scale * ((*in >> 6) & 0x01); + if (k > 2) *cur++ = scale * ((*in >> 5) & 0x01); + if (k > 3) *cur++ = scale * ((*in >> 4) & 0x01); + if (k > 4) *cur++ = scale * ((*in >> 3) & 0x01); + if (k > 5) *cur++ = scale * ((*in >> 2) & 0x01); + if (k > 6) *cur++ = scale * ((*in >> 1) & 0x01); + } + if (img_n != out_n) { + // insert alpha = 255 + stbi_uc *cur = a->out + stride*j; + int i; + if (img_n == 1) { + for (i=x-1; i >= 0; --i) { + cur[i*2+1] = 255; + cur[i*2+0] = cur[i]; + } + } else { + STBI_ASSERT(img_n == 3); + for (i=x-1; i >= 0; --i) { + cur[i*4+3] = 255; + cur[i*4+2] = cur[i*3+2]; + cur[i*4+1] = cur[i*3+1]; + cur[i*4+0] = cur[i*3+0]; + } + } + } + } + } + + return 1; +} + +static int stbi__create_png_image(stbi__png *a, stbi_uc *image_data, stbi__uint32 image_data_len, int out_n, int depth, int color, int interlaced) +{ + stbi_uc *final; + int p; + if (!interlaced) + return stbi__create_png_image_raw(a, image_data, image_data_len, out_n, a->s->img_x, a->s->img_y, depth, color); + + // de-interlacing + final = (stbi_uc *) stbi__malloc(a->s->img_x * a->s->img_y * out_n); + for (p=0; p < 7; ++p) { + int xorig[] = { 0,4,0,2,0,1,0 }; + int yorig[] = { 0,0,4,0,2,0,1 }; + int xspc[] = { 8,8,4,4,2,2,1 }; + int yspc[] = { 8,8,8,4,4,2,2 }; + int i,j,x,y; + // pass1_x[4] = 0, pass1_x[5] = 1, pass1_x[12] = 1 + x = (a->s->img_x - xorig[p] + xspc[p]-1) / xspc[p]; + y = (a->s->img_y - yorig[p] + yspc[p]-1) / yspc[p]; + if (x && y) { + stbi__uint32 img_len = ((((a->s->img_n * x * depth) + 7) >> 3) + 1) * y; + if (!stbi__create_png_image_raw(a, image_data, image_data_len, out_n, x, y, depth, color)) { + STBI_FREE(final); + return 0; + } + for (j=0; j < y; ++j) { + for (i=0; i < x; ++i) { + int out_y = j*yspc[p]+yorig[p]; + int out_x = i*xspc[p]+xorig[p]; + memcpy(final + out_y*a->s->img_x*out_n + out_x*out_n, + a->out + (j*x+i)*out_n, out_n); + } + } + STBI_FREE(a->out); + image_data += img_len; + image_data_len -= img_len; + } + } + a->out = final; + + return 1; +} + +static int stbi__compute_transparency(stbi__png *z, stbi_uc tc[3], int out_n) +{ + stbi__context *s = z->s; + stbi__uint32 i, pixel_count = s->img_x * s->img_y; + stbi_uc *p = z->out; + + // compute color-based transparency, assuming we've + // already got 255 as the alpha value in the output + STBI_ASSERT(out_n == 2 || out_n == 4); + + if (out_n == 2) { + for (i=0; i < pixel_count; ++i) { + p[1] = (p[0] == tc[0] ? 0 : 255); + p += 2; + } + } else { + for (i=0; i < pixel_count; ++i) { + if (p[0] == tc[0] && p[1] == tc[1] && p[2] == tc[2]) + p[3] = 0; + p += 4; + } + } + return 1; +} + +static int stbi__expand_png_palette(stbi__png *a, stbi_uc *palette, int len, int pal_img_n) +{ + stbi__uint32 i, pixel_count = a->s->img_x * a->s->img_y; + stbi_uc *p, *temp_out, *orig = a->out; + + p = (stbi_uc *) stbi__malloc(pixel_count * pal_img_n); + if (p == NULL) return stbi__err("outofmem", "Out of memory"); + + // between here and free(out) below, exitting would leak + temp_out = p; + + if (pal_img_n == 3) { + for (i=0; i < pixel_count; ++i) { + int n = orig[i]*4; + p[0] = palette[n ]; + p[1] = palette[n+1]; + p[2] = palette[n+2]; + p += 3; + } + } else { + for (i=0; i < pixel_count; ++i) { + int n = orig[i]*4; + p[0] = palette[n ]; + p[1] = palette[n+1]; + p[2] = palette[n+2]; + p[3] = palette[n+3]; + p += 4; + } + } + STBI_FREE(a->out); + a->out = temp_out; + + STBI_NOTUSED(len); + + return 1; +} + +static int stbi__unpremultiply_on_load = 0; +static int stbi__de_iphone_flag = 0; + +STBIDEF void stbi_set_unpremultiply_on_load(int flag_true_if_should_unpremultiply) +{ + stbi__unpremultiply_on_load = flag_true_if_should_unpremultiply; +} + +STBIDEF void stbi_convert_iphone_png_to_rgb(int flag_true_if_should_convert) +{ + stbi__de_iphone_flag = flag_true_if_should_convert; +} + +static void stbi__de_iphone(stbi__png *z) +{ + stbi__context *s = z->s; + stbi__uint32 i, pixel_count = s->img_x * s->img_y; + stbi_uc *p = z->out; + + if (s->img_out_n == 3) { // convert bgr to rgb + for (i=0; i < pixel_count; ++i) { + stbi_uc t = p[0]; + p[0] = p[2]; + p[2] = t; + p += 3; + } + } else { + STBI_ASSERT(s->img_out_n == 4); + if (stbi__unpremultiply_on_load) { + // convert bgr to rgb and unpremultiply + for (i=0; i < pixel_count; ++i) { + stbi_uc a = p[3]; + stbi_uc t = p[0]; + if (a) { + p[0] = p[2] * 255 / a; + p[1] = p[1] * 255 / a; + p[2] = t * 255 / a; + } else { + p[0] = p[2]; + p[2] = t; + } + p += 4; + } + } else { + // convert bgr to rgb + for (i=0; i < pixel_count; ++i) { + stbi_uc t = p[0]; + p[0] = p[2]; + p[2] = t; + p += 4; + } + } + } +} + +#define STBI__PNG_TYPE(a,b,c,d) (((a) << 24) + ((b) << 16) + ((c) << 8) + (d)) + +static int stbi__parse_png_file(stbi__png *z, int scan, int req_comp) +{ + stbi_uc palette[1024], pal_img_n=0; + stbi_uc has_trans=0, tc[3]; + stbi__uint32 ioff=0, idata_limit=0, i, pal_len=0; + int first=1,k,interlace=0, color=0, depth=0, is_iphone=0; + stbi__context *s = z->s; + + z->expanded = NULL; + z->idata = NULL; + z->out = NULL; + + if (!stbi__check_png_header(s)) return 0; + + if (scan == STBI__SCAN_type) return 1; + + for (;;) { + stbi__pngchunk c = stbi__get_chunk_header(s); + switch (c.type) { + case STBI__PNG_TYPE('C','g','B','I'): + is_iphone = 1; + stbi__skip(s, c.length); + break; + case STBI__PNG_TYPE('I','H','D','R'): { + int comp,filter; + if (!first) return stbi__err("multiple IHDR","Corrupt PNG"); + first = 0; + if (c.length != 13) return stbi__err("bad IHDR len","Corrupt PNG"); + s->img_x = stbi__get32be(s); if (s->img_x > (1 << 24)) return stbi__err("too large","Very large image (corrupt?)"); + s->img_y = stbi__get32be(s); if (s->img_y > (1 << 24)) return stbi__err("too large","Very large image (corrupt?)"); + depth = stbi__get8(s); if (depth != 1 && depth != 2 && depth != 4 && depth != 8) return stbi__err("1/2/4/8-bit only","PNG not supported: 1/2/4/8-bit only"); + color = stbi__get8(s); if (color > 6) return stbi__err("bad ctype","Corrupt PNG"); + if (color == 3) pal_img_n = 3; else if (color & 1) return stbi__err("bad ctype","Corrupt PNG"); + comp = stbi__get8(s); if (comp) return stbi__err("bad comp method","Corrupt PNG"); + filter= stbi__get8(s); if (filter) return stbi__err("bad filter method","Corrupt PNG"); + interlace = stbi__get8(s); if (interlace>1) return stbi__err("bad interlace method","Corrupt PNG"); + if (!s->img_x || !s->img_y) return stbi__err("0-pixel image","Corrupt PNG"); + if (!pal_img_n) { + s->img_n = (color & 2 ? 3 : 1) + (color & 4 ? 1 : 0); + if ((1 << 30) / s->img_x / s->img_n < s->img_y) return stbi__err("too large", "Image too large to decode"); + if (scan == STBI__SCAN_header) return 1; + } else { + // if paletted, then pal_n is our final components, and + // img_n is # components to decompress/filter. + s->img_n = 1; + if ((1 << 30) / s->img_x / 4 < s->img_y) return stbi__err("too large","Corrupt PNG"); + // if SCAN_header, have to scan to see if we have a tRNS + } + break; + } + + case STBI__PNG_TYPE('P','L','T','E'): { + if (first) return stbi__err("first not IHDR", "Corrupt PNG"); + if (c.length > 256*3) return stbi__err("invalid PLTE","Corrupt PNG"); + pal_len = c.length / 3; + if (pal_len * 3 != c.length) return stbi__err("invalid PLTE","Corrupt PNG"); + for (i=0; i < pal_len; ++i) { + palette[i*4+0] = stbi__get8(s); + palette[i*4+1] = stbi__get8(s); + palette[i*4+2] = stbi__get8(s); + palette[i*4+3] = 255; + } + break; + } + + case STBI__PNG_TYPE('t','R','N','S'): { + if (first) return stbi__err("first not IHDR", "Corrupt PNG"); + if (z->idata) return stbi__err("tRNS after IDAT","Corrupt PNG"); + if (pal_img_n) { + if (scan == STBI__SCAN_header) { s->img_n = 4; return 1; } + if (pal_len == 0) return stbi__err("tRNS before PLTE","Corrupt PNG"); + if (c.length > pal_len) return stbi__err("bad tRNS len","Corrupt PNG"); + pal_img_n = 4; + for (i=0; i < c.length; ++i) + palette[i*4+3] = stbi__get8(s); + } else { + if (!(s->img_n & 1)) return stbi__err("tRNS with alpha","Corrupt PNG"); + if (c.length != (stbi__uint32) s->img_n*2) return stbi__err("bad tRNS len","Corrupt PNG"); + has_trans = 1; + for (k=0; k < s->img_n; ++k) + tc[k] = (stbi_uc) (stbi__get16be(s) & 255) * stbi__depth_scale_table[depth]; // non 8-bit images will be larger + } + break; + } + + case STBI__PNG_TYPE('I','D','A','T'): { + if (first) return stbi__err("first not IHDR", "Corrupt PNG"); + if (pal_img_n && !pal_len) return stbi__err("no PLTE","Corrupt PNG"); + if (scan == STBI__SCAN_header) { s->img_n = pal_img_n; return 1; } + if ((int)(ioff + c.length) < (int)ioff) return 0; + if (ioff + c.length > idata_limit) { + stbi_uc *p; + if (idata_limit == 0) idata_limit = c.length > 4096 ? c.length : 4096; + while (ioff + c.length > idata_limit) + idata_limit *= 2; + p = (stbi_uc *) STBI_REALLOC(z->idata, idata_limit); if (p == NULL) return stbi__err("outofmem", "Out of memory"); + z->idata = p; + } + if (!stbi__getn(s, z->idata+ioff,c.length)) return stbi__err("outofdata","Corrupt PNG"); + ioff += c.length; + break; + } + + case STBI__PNG_TYPE('I','E','N','D'): { + stbi__uint32 raw_len, bpl; + if (first) return stbi__err("first not IHDR", "Corrupt PNG"); + if (scan != STBI__SCAN_load) return 1; + if (z->idata == NULL) return stbi__err("no IDAT","Corrupt PNG"); + // initial guess for decoded data size to avoid unnecessary reallocs + bpl = (s->img_x * depth + 7) / 8; // bytes per line, per component + raw_len = bpl * s->img_y * s->img_n /* pixels */ + s->img_y /* filter mode per row */; + z->expanded = (stbi_uc *) stbi_zlib_decode_malloc_guesssize_headerflag((char *) z->idata, ioff, raw_len, (int *) &raw_len, !is_iphone); + if (z->expanded == NULL) return 0; // zlib should set error + STBI_FREE(z->idata); z->idata = NULL; + if ((req_comp == s->img_n+1 && req_comp != 3 && !pal_img_n) || has_trans) + s->img_out_n = s->img_n+1; + else + s->img_out_n = s->img_n; + if (!stbi__create_png_image(z, z->expanded, raw_len, s->img_out_n, depth, color, interlace)) return 0; + if (has_trans) + if (!stbi__compute_transparency(z, tc, s->img_out_n)) return 0; + if (is_iphone && stbi__de_iphone_flag && s->img_out_n > 2) + stbi__de_iphone(z); + if (pal_img_n) { + // pal_img_n == 3 or 4 + s->img_n = pal_img_n; // record the actual colors we had + s->img_out_n = pal_img_n; + if (req_comp >= 3) s->img_out_n = req_comp; + if (!stbi__expand_png_palette(z, palette, pal_len, s->img_out_n)) + return 0; + } + STBI_FREE(z->expanded); z->expanded = NULL; + return 1; + } + + default: + // if critical, fail + if (first) return stbi__err("first not IHDR", "Corrupt PNG"); + if ((c.type & (1 << 29)) == 0) { + #ifndef STBI_NO_FAILURE_STRINGS + // not threadsafe + static char invalid_chunk[] = "XXXX PNG chunk not known"; + invalid_chunk[0] = STBI__BYTECAST(c.type >> 24); + invalid_chunk[1] = STBI__BYTECAST(c.type >> 16); + invalid_chunk[2] = STBI__BYTECAST(c.type >> 8); + invalid_chunk[3] = STBI__BYTECAST(c.type >> 0); + #endif + return stbi__err(invalid_chunk, "PNG not supported: unknown PNG chunk type"); + } + stbi__skip(s, c.length); + break; + } + // end of PNG chunk, read and skip CRC + stbi__get32be(s); + } +} + +static unsigned char *stbi__do_png(stbi__png *p, int *x, int *y, int *n, int req_comp) +{ + unsigned char *result=NULL; + if (req_comp < 0 || req_comp > 4) return stbi__errpuc("bad req_comp", "Internal error"); + if (stbi__parse_png_file(p, STBI__SCAN_load, req_comp)) { + result = p->out; + p->out = NULL; + if (req_comp && req_comp != p->s->img_out_n) { + result = stbi__convert_format(result, p->s->img_out_n, req_comp, p->s->img_x, p->s->img_y); + p->s->img_out_n = req_comp; + if (result == NULL) return result; + } + *x = p->s->img_x; + *y = p->s->img_y; + if (n) *n = p->s->img_out_n; + } + STBI_FREE(p->out); p->out = NULL; + STBI_FREE(p->expanded); p->expanded = NULL; + STBI_FREE(p->idata); p->idata = NULL; + + return result; +} + +static unsigned char *stbi__png_load(stbi__context *s, int *x, int *y, int *comp, int req_comp) +{ + stbi__png p; + p.s = s; + return stbi__do_png(&p, x,y,comp,req_comp); +} + +static int stbi__png_test(stbi__context *s) +{ + int r; + r = stbi__check_png_header(s); + stbi__rewind(s); + return r; +} + +static int stbi__png_info_raw(stbi__png *p, int *x, int *y, int *comp) +{ + if (!stbi__parse_png_file(p, STBI__SCAN_header, 0)) { + stbi__rewind( p->s ); + return 0; + } + if (x) *x = p->s->img_x; + if (y) *y = p->s->img_y; + if (comp) *comp = p->s->img_n; + return 1; +} + +static int stbi__png_info(stbi__context *s, int *x, int *y, int *comp) +{ + stbi__png p; + p.s = s; + return stbi__png_info_raw(&p, x, y, comp); +} +#endif + +// Microsoft/Windows BMP image + +#ifndef STBI_NO_BMP +static int stbi__bmp_test_raw(stbi__context *s) +{ + int r; + int sz; + if (stbi__get8(s) != 'B') return 0; + if (stbi__get8(s) != 'M') return 0; + stbi__get32le(s); // discard filesize + stbi__get16le(s); // discard reserved + stbi__get16le(s); // discard reserved + stbi__get32le(s); // discard data offset + sz = stbi__get32le(s); + r = (sz == 12 || sz == 40 || sz == 56 || sz == 108 || sz == 124); + return r; +} + +static int stbi__bmp_test(stbi__context *s) +{ + int r = stbi__bmp_test_raw(s); + stbi__rewind(s); + return r; +} + + +// returns 0..31 for the highest set bit +static int stbi__high_bit(unsigned int z) +{ + int n=0; + if (z == 0) return -1; + if (z >= 0x10000) n += 16, z >>= 16; + if (z >= 0x00100) n += 8, z >>= 8; + if (z >= 0x00010) n += 4, z >>= 4; + if (z >= 0x00004) n += 2, z >>= 2; + if (z >= 0x00002) n += 1, z >>= 1; + return n; +} + +static int stbi__bitcount(unsigned int a) +{ + a = (a & 0x55555555) + ((a >> 1) & 0x55555555); // max 2 + a = (a & 0x33333333) + ((a >> 2) & 0x33333333); // max 4 + a = (a + (a >> 4)) & 0x0f0f0f0f; // max 8 per 4, now 8 bits + a = (a + (a >> 8)); // max 16 per 8 bits + a = (a + (a >> 16)); // max 32 per 8 bits + return a & 0xff; +} + +static int stbi__shiftsigned(int v, int shift, int bits) +{ + int result; + int z=0; + + if (shift < 0) v <<= -shift; + else v >>= shift; + result = v; + + z = bits; + while (z < 8) { + result += v >> z; + z += bits; + } + return result; +} + +static stbi_uc *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req_comp) +{ + stbi_uc *out; + unsigned int mr=0,mg=0,mb=0,ma=0, fake_a=0; + stbi_uc pal[256][4]; + int psize=0,i,j,compress=0,width; + int bpp, flip_vertically, pad, target, offset, hsz; + if (stbi__get8(s) != 'B' || stbi__get8(s) != 'M') return stbi__errpuc("not BMP", "Corrupt BMP"); + stbi__get32le(s); // discard filesize + stbi__get16le(s); // discard reserved + stbi__get16le(s); // discard reserved + offset = stbi__get32le(s); + hsz = stbi__get32le(s); + if (hsz != 12 && hsz != 40 && hsz != 56 && hsz != 108 && hsz != 124) return stbi__errpuc("unknown BMP", "BMP type not supported: unknown"); + if (hsz == 12) { + s->img_x = stbi__get16le(s); + s->img_y = stbi__get16le(s); + } else { + s->img_x = stbi__get32le(s); + s->img_y = stbi__get32le(s); + } + if (stbi__get16le(s) != 1) return stbi__errpuc("bad BMP", "bad BMP"); + bpp = stbi__get16le(s); + if (bpp == 1) return stbi__errpuc("monochrome", "BMP type not supported: 1-bit"); + flip_vertically = ((int) s->img_y) > 0; + s->img_y = abs((int) s->img_y); + if (hsz == 12) { + if (bpp < 24) + psize = (offset - 14 - 24) / 3; + } else { + compress = stbi__get32le(s); + if (compress == 1 || compress == 2) return stbi__errpuc("BMP RLE", "BMP type not supported: RLE"); + stbi__get32le(s); // discard sizeof + stbi__get32le(s); // discard hres + stbi__get32le(s); // discard vres + stbi__get32le(s); // discard colorsused + stbi__get32le(s); // discard max important + if (hsz == 40 || hsz == 56) { + if (hsz == 56) { + stbi__get32le(s); + stbi__get32le(s); + stbi__get32le(s); + stbi__get32le(s); + } + if (bpp == 16 || bpp == 32) { + mr = mg = mb = 0; + if (compress == 0) { + if (bpp == 32) { + mr = 0xffu << 16; + mg = 0xffu << 8; + mb = 0xffu << 0; + ma = 0xffu << 24; + fake_a = 1; // @TODO: check for cases like alpha value is all 0 and switch it to 255 + STBI_NOTUSED(fake_a); + } else { + mr = 31u << 10; + mg = 31u << 5; + mb = 31u << 0; + } + } else if (compress == 3) { + mr = stbi__get32le(s); + mg = stbi__get32le(s); + mb = stbi__get32le(s); + // not documented, but generated by photoshop and handled by mspaint + if (mr == mg && mg == mb) { + // ?!?!? + return stbi__errpuc("bad BMP", "bad BMP"); + } + } else + return stbi__errpuc("bad BMP", "bad BMP"); + } + } else { + STBI_ASSERT(hsz == 108 || hsz == 124); + mr = stbi__get32le(s); + mg = stbi__get32le(s); + mb = stbi__get32le(s); + ma = stbi__get32le(s); + stbi__get32le(s); // discard color space + for (i=0; i < 12; ++i) + stbi__get32le(s); // discard color space parameters + if (hsz == 124) { + stbi__get32le(s); // discard rendering intent + stbi__get32le(s); // discard offset of profile data + stbi__get32le(s); // discard size of profile data + stbi__get32le(s); // discard reserved + } + } + if (bpp < 16) + psize = (offset - 14 - hsz) >> 2; + } + s->img_n = ma ? 4 : 3; + if (req_comp && req_comp >= 3) // we can directly decode 3 or 4 + target = req_comp; + else + target = s->img_n; // if they want monochrome, we'll post-convert + out = (stbi_uc *) stbi__malloc(target * s->img_x * s->img_y); + if (!out) return stbi__errpuc("outofmem", "Out of memory"); + if (bpp < 16) { + int z=0; + if (psize == 0 || psize > 256) { STBI_FREE(out); return stbi__errpuc("invalid", "Corrupt BMP"); } + for (i=0; i < psize; ++i) { + pal[i][2] = stbi__get8(s); + pal[i][1] = stbi__get8(s); + pal[i][0] = stbi__get8(s); + if (hsz != 12) stbi__get8(s); + pal[i][3] = 255; + } + stbi__skip(s, offset - 14 - hsz - psize * (hsz == 12 ? 3 : 4)); + if (bpp == 4) width = (s->img_x + 1) >> 1; + else if (bpp == 8) width = s->img_x; + else { STBI_FREE(out); return stbi__errpuc("bad bpp", "Corrupt BMP"); } + pad = (-width)&3; + for (j=0; j < (int) s->img_y; ++j) { + for (i=0; i < (int) s->img_x; i += 2) { + int v=stbi__get8(s),v2=0; + if (bpp == 4) { + v2 = v & 15; + v >>= 4; + } + out[z++] = pal[v][0]; + out[z++] = pal[v][1]; + out[z++] = pal[v][2]; + if (target == 4) out[z++] = 255; + if (i+1 == (int) s->img_x) break; + v = (bpp == 8) ? stbi__get8(s) : v2; + out[z++] = pal[v][0]; + out[z++] = pal[v][1]; + out[z++] = pal[v][2]; + if (target == 4) out[z++] = 255; + } + stbi__skip(s, pad); + } + } else { + int rshift=0,gshift=0,bshift=0,ashift=0,rcount=0,gcount=0,bcount=0,acount=0; + int z = 0; + int easy=0; + stbi__skip(s, offset - 14 - hsz); + if (bpp == 24) width = 3 * s->img_x; + else if (bpp == 16) width = 2*s->img_x; + else /* bpp = 32 and pad = 0 */ width=0; + pad = (-width) & 3; + if (bpp == 24) { + easy = 1; + } else if (bpp == 32) { + if (mb == 0xff && mg == 0xff00 && mr == 0x00ff0000 && ma == 0xff000000) + easy = 2; + } + if (!easy) { + if (!mr || !mg || !mb) { STBI_FREE(out); return stbi__errpuc("bad masks", "Corrupt BMP"); } + // right shift amt to put high bit in position #7 + rshift = stbi__high_bit(mr)-7; rcount = stbi__bitcount(mr); + gshift = stbi__high_bit(mg)-7; gcount = stbi__bitcount(mg); + bshift = stbi__high_bit(mb)-7; bcount = stbi__bitcount(mb); + ashift = stbi__high_bit(ma)-7; acount = stbi__bitcount(ma); + } + for (j=0; j < (int) s->img_y; ++j) { + if (easy) { + for (i=0; i < (int) s->img_x; ++i) { + unsigned char a; + out[z+2] = stbi__get8(s); + out[z+1] = stbi__get8(s); + out[z+0] = stbi__get8(s); + z += 3; + a = (easy == 2 ? stbi__get8(s) : 255); + if (target == 4) out[z++] = a; + } + } else { + for (i=0; i < (int) s->img_x; ++i) { + stbi__uint32 v = (bpp == 16 ? (stbi__uint32) stbi__get16le(s) : stbi__get32le(s)); + int a; + out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mr, rshift, rcount)); + out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mg, gshift, gcount)); + out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mb, bshift, bcount)); + a = (ma ? stbi__shiftsigned(v & ma, ashift, acount) : 255); + if (target == 4) out[z++] = STBI__BYTECAST(a); + } + } + stbi__skip(s, pad); + } + } + if (flip_vertically) { + stbi_uc t; + for (j=0; j < (int) s->img_y>>1; ++j) { + stbi_uc *p1 = out + j *s->img_x*target; + stbi_uc *p2 = out + (s->img_y-1-j)*s->img_x*target; + for (i=0; i < (int) s->img_x*target; ++i) { + t = p1[i], p1[i] = p2[i], p2[i] = t; + } + } + } + + if (req_comp && req_comp != target) { + out = stbi__convert_format(out, target, req_comp, s->img_x, s->img_y); + if (out == NULL) return out; // stbi__convert_format frees input on failure + } + + *x = s->img_x; + *y = s->img_y; + if (comp) *comp = s->img_n; + return out; +} +#endif + +// Targa Truevision - TGA +// by Jonathan Dummer +#ifndef STBI_NO_TGA +static int stbi__tga_info(stbi__context *s, int *x, int *y, int *comp) +{ + int tga_w, tga_h, tga_comp; + int sz; + stbi__get8(s); // discard Offset + sz = stbi__get8(s); // color type + if( sz > 1 ) { + stbi__rewind(s); + return 0; // only RGB or indexed allowed + } + sz = stbi__get8(s); // image type + // only RGB or grey allowed, +/- RLE + if ((sz != 1) && (sz != 2) && (sz != 3) && (sz != 9) && (sz != 10) && (sz != 11)) return 0; + stbi__skip(s,9); + tga_w = stbi__get16le(s); + if( tga_w < 1 ) { + stbi__rewind(s); + return 0; // test width + } + tga_h = stbi__get16le(s); + if( tga_h < 1 ) { + stbi__rewind(s); + return 0; // test height + } + sz = stbi__get8(s); // bits per pixel + // only RGB or RGBA or grey allowed + if ((sz != 8) && (sz != 16) && (sz != 24) && (sz != 32)) { + stbi__rewind(s); + return 0; + } + tga_comp = sz; + if (x) *x = tga_w; + if (y) *y = tga_h; + if (comp) *comp = tga_comp / 8; + return 1; // seems to have passed everything +} + +static int stbi__tga_test(stbi__context *s) +{ + int res; + int sz; + stbi__get8(s); // discard Offset + sz = stbi__get8(s); // color type + if ( sz > 1 ) return 0; // only RGB or indexed allowed + sz = stbi__get8(s); // image type + if ( (sz != 1) && (sz != 2) && (sz != 3) && (sz != 9) && (sz != 10) && (sz != 11) ) return 0; // only RGB or grey allowed, +/- RLE + stbi__get16be(s); // discard palette start + stbi__get16be(s); // discard palette length + stbi__get8(s); // discard bits per palette color entry + stbi__get16be(s); // discard x origin + stbi__get16be(s); // discard y origin + if ( stbi__get16be(s) < 1 ) return 0; // test width + if ( stbi__get16be(s) < 1 ) return 0; // test height + sz = stbi__get8(s); // bits per pixel + if ( (sz != 8) && (sz != 16) && (sz != 24) && (sz != 32) ) + res = 0; + else + res = 1; + stbi__rewind(s); + return res; +} + +static stbi_uc *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int req_comp) +{ + // read in the TGA header stuff + int tga_offset = stbi__get8(s); + int tga_indexed = stbi__get8(s); + int tga_image_type = stbi__get8(s); + int tga_is_RLE = 0; + int tga_palette_start = stbi__get16le(s); + int tga_palette_len = stbi__get16le(s); + int tga_palette_bits = stbi__get8(s); + int tga_x_origin = stbi__get16le(s); + int tga_y_origin = stbi__get16le(s); + int tga_width = stbi__get16le(s); + int tga_height = stbi__get16le(s); + int tga_bits_per_pixel = stbi__get8(s); + int tga_comp = tga_bits_per_pixel / 8; + int tga_inverted = stbi__get8(s); + // image data + unsigned char *tga_data; + unsigned char *tga_palette = NULL; + int i, j; + unsigned char raw_data[4]; + int RLE_count = 0; + int RLE_repeating = 0; + int read_next_pixel = 1; + + // do a tiny bit of precessing + if ( tga_image_type >= 8 ) + { + tga_image_type -= 8; + tga_is_RLE = 1; + } + /* int tga_alpha_bits = tga_inverted & 15; */ + tga_inverted = 1 - ((tga_inverted >> 5) & 1); + + // error check + if ( //(tga_indexed) || + (tga_width < 1) || (tga_height < 1) || + (tga_image_type < 1) || (tga_image_type > 3) || + ((tga_bits_per_pixel != 8) && (tga_bits_per_pixel != 16) && + (tga_bits_per_pixel != 24) && (tga_bits_per_pixel != 32)) + ) + { + return NULL; // we don't report this as a bad TGA because we don't even know if it's TGA + } + + // If I'm paletted, then I'll use the number of bits from the palette + if ( tga_indexed ) + { + tga_comp = tga_palette_bits / 8; + } + + // tga info + *x = tga_width; + *y = tga_height; + if (comp) *comp = tga_comp; + + tga_data = (unsigned char*)stbi__malloc( (size_t)tga_width * tga_height * tga_comp ); + if (!tga_data) return stbi__errpuc("outofmem", "Out of memory"); + + // skip to the data's starting position (offset usually = 0) + stbi__skip(s, tga_offset ); + + if ( !tga_indexed && !tga_is_RLE) { + for (i=0; i < tga_height; ++i) { + int y = tga_inverted ? tga_height -i - 1 : i; + stbi_uc *tga_row = tga_data + y*tga_width*tga_comp; + stbi__getn(s, tga_row, tga_width * tga_comp); + } + } else { + // do I need to load a palette? + if ( tga_indexed) + { + // any data to skip? (offset usually = 0) + stbi__skip(s, tga_palette_start ); + // load the palette + tga_palette = (unsigned char*)stbi__malloc( tga_palette_len * tga_palette_bits / 8 ); + if (!tga_palette) { + STBI_FREE(tga_data); + return stbi__errpuc("outofmem", "Out of memory"); + } + if (!stbi__getn(s, tga_palette, tga_palette_len * tga_palette_bits / 8 )) { + STBI_FREE(tga_data); + STBI_FREE(tga_palette); + return stbi__errpuc("bad palette", "Corrupt TGA"); + } + } + // load the data + for (i=0; i < tga_width * tga_height; ++i) + { + // if I'm in RLE mode, do I need to get a RLE stbi__pngchunk? + if ( tga_is_RLE ) + { + if ( RLE_count == 0 ) + { + // yep, get the next byte as a RLE command + int RLE_cmd = stbi__get8(s); + RLE_count = 1 + (RLE_cmd & 127); + RLE_repeating = RLE_cmd >> 7; + read_next_pixel = 1; + } else if ( !RLE_repeating ) + { + read_next_pixel = 1; + } + } else + { + read_next_pixel = 1; + } + // OK, if I need to read a pixel, do it now + if ( read_next_pixel ) + { + // load however much data we did have + if ( tga_indexed ) + { + // read in 1 byte, then perform the lookup + int pal_idx = stbi__get8(s); + if ( pal_idx >= tga_palette_len ) + { + // invalid index + pal_idx = 0; + } + pal_idx *= tga_bits_per_pixel / 8; + for (j = 0; j*8 < tga_bits_per_pixel; ++j) + { + raw_data[j] = tga_palette[pal_idx+j]; + } + } else + { + // read in the data raw + for (j = 0; j*8 < tga_bits_per_pixel; ++j) + { + raw_data[j] = stbi__get8(s); + } + } + // clear the reading flag for the next pixel + read_next_pixel = 0; + } // end of reading a pixel + + // copy data + for (j = 0; j < tga_comp; ++j) + tga_data[i*tga_comp+j] = raw_data[j]; + + // in case we're in RLE mode, keep counting down + --RLE_count; + } + // do I need to invert the image? + if ( tga_inverted ) + { + for (j = 0; j*2 < tga_height; ++j) + { + int index1 = j * tga_width * tga_comp; + int index2 = (tga_height - 1 - j) * tga_width * tga_comp; + for (i = tga_width * tga_comp; i > 0; --i) + { + unsigned char temp = tga_data[index1]; + tga_data[index1] = tga_data[index2]; + tga_data[index2] = temp; + ++index1; + ++index2; + } + } + } + // clear my palette, if I had one + if ( tga_palette != NULL ) + { + STBI_FREE( tga_palette ); + } + } + + // swap RGB + if (tga_comp >= 3) + { + unsigned char* tga_pixel = tga_data; + for (i=0; i < tga_width * tga_height; ++i) + { + unsigned char temp = tga_pixel[0]; + tga_pixel[0] = tga_pixel[2]; + tga_pixel[2] = temp; + tga_pixel += tga_comp; + } + } + + // convert to target component count + if (req_comp && req_comp != tga_comp) + tga_data = stbi__convert_format(tga_data, tga_comp, req_comp, tga_width, tga_height); + + // the things I do to get rid of an error message, and yet keep + // Microsoft's C compilers happy... [8^( + tga_palette_start = tga_palette_len = tga_palette_bits = + tga_x_origin = tga_y_origin = 0; + // OK, done + return tga_data; +} +#endif + +// ************************************************************************************************* +// Photoshop PSD loader -- PD by Thatcher Ulrich, integration by Nicolas Schulz, tweaked by STB + +#ifndef STBI_NO_PSD +static int stbi__psd_test(stbi__context *s) +{ + int r = (stbi__get32be(s) == 0x38425053); + stbi__rewind(s); + return r; +} + +static stbi_uc *stbi__psd_load(stbi__context *s, int *x, int *y, int *comp, int req_comp) +{ + int pixelCount; + int channelCount, compression; + int channel, i, count, len; + int w,h; + stbi_uc *out; + + // Check identifier + if (stbi__get32be(s) != 0x38425053) // "8BPS" + return stbi__errpuc("not PSD", "Corrupt PSD image"); + + // Check file type version. + if (stbi__get16be(s) != 1) + return stbi__errpuc("wrong version", "Unsupported version of PSD image"); + + // Skip 6 reserved bytes. + stbi__skip(s, 6 ); + + // Read the number of channels (R, G, B, A, etc). + channelCount = stbi__get16be(s); + if (channelCount < 0 || channelCount > 16) + return stbi__errpuc("wrong channel count", "Unsupported number of channels in PSD image"); + + // Read the rows and columns of the image. + h = stbi__get32be(s); + w = stbi__get32be(s); + + // Make sure the depth is 8 bits. + if (stbi__get16be(s) != 8) + return stbi__errpuc("unsupported bit depth", "PSD bit depth is not 8 bit"); + + // Make sure the color mode is RGB. + // Valid options are: + // 0: Bitmap + // 1: Grayscale + // 2: Indexed color + // 3: RGB color + // 4: CMYK color + // 7: Multichannel + // 8: Duotone + // 9: Lab color + if (stbi__get16be(s) != 3) + return stbi__errpuc("wrong color format", "PSD is not in RGB color format"); + + // Skip the Mode Data. (It's the palette for indexed color; other info for other modes.) + stbi__skip(s,stbi__get32be(s) ); + + // Skip the image resources. (resolution, pen tool paths, etc) + stbi__skip(s, stbi__get32be(s) ); + + // Skip the reserved data. + stbi__skip(s, stbi__get32be(s) ); + + // Find out if the data is compressed. + // Known values: + // 0: no compression + // 1: RLE compressed + compression = stbi__get16be(s); + if (compression > 1) + return stbi__errpuc("bad compression", "PSD has an unknown compression format"); + + // Create the destination image. + out = (stbi_uc *) stbi__malloc(4 * w*h); + if (!out) return stbi__errpuc("outofmem", "Out of memory"); + pixelCount = w*h; + + // Initialize the data to zero. + //memset( out, 0, pixelCount * 4 ); + + // Finally, the image data. + if (compression) { + // RLE as used by .PSD and .TIFF + // Loop until you get the number of unpacked bytes you are expecting: + // Read the next source byte into n. + // If n is between 0 and 127 inclusive, copy the next n+1 bytes literally. + // Else if n is between -127 and -1 inclusive, copy the next byte -n+1 times. + // Else if n is 128, noop. + // Endloop + + // The RLE-compressed data is preceeded by a 2-byte data count for each row in the data, + // which we're going to just skip. + stbi__skip(s, h * channelCount * 2 ); + + // Read the RLE data by channel. + for (channel = 0; channel < 4; channel++) { + stbi_uc *p; + + p = out+channel; + if (channel >= channelCount) { + // Fill this channel with default data. + for (i = 0; i < pixelCount; i++) *p = (channel == 3 ? 255 : 0), p += 4; + } else { + // Read the RLE data. + count = 0; + while (count < pixelCount) { + len = stbi__get8(s); + if (len == 128) { + // No-op. + } else if (len < 128) { + // Copy next len+1 bytes literally. + len++; + count += len; + while (len) { + *p = stbi__get8(s); + p += 4; + len--; + } + } else if (len > 128) { + stbi_uc val; + // Next -len+1 bytes in the dest are replicated from next source byte. + // (Interpret len as a negative 8-bit int.) + len ^= 0x0FF; + len += 2; + val = stbi__get8(s); + count += len; + while (len) { + *p = val; + p += 4; + len--; + } + } + } + } + } + + } else { + // We're at the raw image data. It's each channel in order (Red, Green, Blue, Alpha, ...) + // where each channel consists of an 8-bit value for each pixel in the image. + + // Read the data by channel. + for (channel = 0; channel < 4; channel++) { + stbi_uc *p; + + p = out + channel; + if (channel > channelCount) { + // Fill this channel with default data. + for (i = 0; i < pixelCount; i++) *p = channel == 3 ? 255 : 0, p += 4; + } else { + // Read the data. + for (i = 0; i < pixelCount; i++) + *p = stbi__get8(s), p += 4; + } + } + } + + if (req_comp && req_comp != 4) { + out = stbi__convert_format(out, 4, req_comp, w, h); + if (out == NULL) return out; // stbi__convert_format frees input on failure + } + + if (comp) *comp = channelCount; + *y = h; + *x = w; + + return out; +} +#endif + +// ************************************************************************************************* +// Softimage PIC loader +// by Tom Seddon +// +// See http://softimage.wiki.softimage.com/index.php/INFO:_PIC_file_format +// See http://ozviz.wasp.uwa.edu.au/~pbourke/dataformats/softimagepic/ + +#ifndef STBI_NO_PIC +static int stbi__pic_is4(stbi__context *s,const char *str) +{ + int i; + for (i=0; i<4; ++i) + if (stbi__get8(s) != (stbi_uc)str[i]) + return 0; + + return 1; +} + +static int stbi__pic_test_core(stbi__context *s) +{ + int i; + + if (!stbi__pic_is4(s,"\x53\x80\xF6\x34")) + return 0; + + for(i=0;i<84;++i) + stbi__get8(s); + + if (!stbi__pic_is4(s,"PICT")) + return 0; + + return 1; +} + +typedef struct +{ + stbi_uc size,type,channel; +} stbi__pic_packet; + +static stbi_uc *stbi__readval(stbi__context *s, int channel, stbi_uc *dest) +{ + int mask=0x80, i; + + for (i=0; i<4; ++i, mask>>=1) { + if (channel & mask) { + if (stbi__at_eof(s)) return stbi__errpuc("bad file","PIC file too short"); + dest[i]=stbi__get8(s); + } + } + + return dest; +} + +static void stbi__copyval(int channel,stbi_uc *dest,const stbi_uc *src) +{ + int mask=0x80,i; + + for (i=0;i<4; ++i, mask>>=1) + if (channel&mask) + dest[i]=src[i]; +} + +static stbi_uc *stbi__pic_load_core(stbi__context *s,int width,int height,int *comp, stbi_uc *result) +{ + int act_comp=0,num_packets=0,y,chained; + stbi__pic_packet packets[10]; + + // this will (should...) cater for even some bizarre stuff like having data + // for the same channel in multiple packets. + do { + stbi__pic_packet *packet; + + if (num_packets==sizeof(packets)/sizeof(packets[0])) + return stbi__errpuc("bad format","too many packets"); + + packet = &packets[num_packets++]; + + chained = stbi__get8(s); + packet->size = stbi__get8(s); + packet->type = stbi__get8(s); + packet->channel = stbi__get8(s); + + act_comp |= packet->channel; + + if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (reading packets)"); + if (packet->size != 8) return stbi__errpuc("bad format","packet isn't 8bpp"); + } while (chained); + + *comp = (act_comp & 0x10 ? 4 : 3); // has alpha channel? + + for(y=0; ytype) { + default: + return stbi__errpuc("bad format","packet has bad compression type"); + + case 0: {//uncompressed + int x; + + for(x=0;xchannel,dest)) + return 0; + break; + } + + case 1://Pure RLE + { + int left=width, i; + + while (left>0) { + stbi_uc count,value[4]; + + count=stbi__get8(s); + if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (pure read count)"); + + if (count > left) + count = (stbi_uc) left; + + if (!stbi__readval(s,packet->channel,value)) return 0; + + for(i=0; ichannel,dest,value); + left -= count; + } + } + break; + + case 2: {//Mixed RLE + int left=width; + while (left>0) { + int count = stbi__get8(s), i; + if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (mixed read count)"); + + if (count >= 128) { // Repeated + stbi_uc value[4]; + int i; + + if (count==128) + count = stbi__get16be(s); + else + count -= 127; + if (count > left) + return stbi__errpuc("bad file","scanline overrun"); + + if (!stbi__readval(s,packet->channel,value)) + return 0; + + for(i=0;ichannel,dest,value); + } else { // Raw + ++count; + if (count>left) return stbi__errpuc("bad file","scanline overrun"); + + for(i=0;ichannel,dest)) + return 0; + } + left-=count; + } + break; + } + } + } + } + + return result; +} + +static stbi_uc *stbi__pic_load(stbi__context *s,int *px,int *py,int *comp,int req_comp) +{ + stbi_uc *result; + int i, x,y; + + for (i=0; i<92; ++i) + stbi__get8(s); + + x = stbi__get16be(s); + y = stbi__get16be(s); + if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (pic header)"); + if ((1 << 28) / x < y) return stbi__errpuc("too large", "Image too large to decode"); + + stbi__get32be(s); //skip `ratio' + stbi__get16be(s); //skip `fields' + stbi__get16be(s); //skip `pad' + + // intermediate buffer is RGBA + result = (stbi_uc *) stbi__malloc(x*y*4); + memset(result, 0xff, x*y*4); + + if (!stbi__pic_load_core(s,x,y,comp, result)) { + STBI_FREE(result); + result=0; + } + *px = x; + *py = y; + if (req_comp == 0) req_comp = *comp; + result=stbi__convert_format(result,4,req_comp,x,y); + + return result; +} + +static int stbi__pic_test(stbi__context *s) +{ + int r = stbi__pic_test_core(s); + stbi__rewind(s); + return r; +} +#endif + +// ************************************************************************************************* +// GIF loader -- public domain by Jean-Marc Lienher -- simplified/shrunk by stb + +#ifndef STBI_NO_GIF +typedef struct +{ + stbi__int16 prefix; + stbi_uc first; + stbi_uc suffix; +} stbi__gif_lzw; + +typedef struct +{ + int w,h; + stbi_uc *out; // output buffer (always 4 components) + int flags, bgindex, ratio, transparent, eflags; + stbi_uc pal[256][4]; + stbi_uc lpal[256][4]; + stbi__gif_lzw codes[4096]; + stbi_uc *color_table; + int parse, step; + int lflags; + int start_x, start_y; + int max_x, max_y; + int cur_x, cur_y; + int line_size; +} stbi__gif; + +static int stbi__gif_test_raw(stbi__context *s) +{ + int sz; + if (stbi__get8(s) != 'G' || stbi__get8(s) != 'I' || stbi__get8(s) != 'F' || stbi__get8(s) != '8') return 0; + sz = stbi__get8(s); + if (sz != '9' && sz != '7') return 0; + if (stbi__get8(s) != 'a') return 0; + return 1; +} + +static int stbi__gif_test(stbi__context *s) +{ + int r = stbi__gif_test_raw(s); + stbi__rewind(s); + return r; +} + +static void stbi__gif_parse_colortable(stbi__context *s, stbi_uc pal[256][4], int num_entries, int transp) +{ + int i; + for (i=0; i < num_entries; ++i) { + pal[i][2] = stbi__get8(s); + pal[i][1] = stbi__get8(s); + pal[i][0] = stbi__get8(s); + pal[i][3] = transp == i ? 0 : 255; + } +} + +static int stbi__gif_header(stbi__context *s, stbi__gif *g, int *comp, int is_info) +{ + stbi_uc version; + if (stbi__get8(s) != 'G' || stbi__get8(s) != 'I' || stbi__get8(s) != 'F' || stbi__get8(s) != '8') + return stbi__err("not GIF", "Corrupt GIF"); + + version = stbi__get8(s); + if (version != '7' && version != '9') return stbi__err("not GIF", "Corrupt GIF"); + if (stbi__get8(s) != 'a') return stbi__err("not GIF", "Corrupt GIF"); + + stbi__g_failure_reason = ""; + g->w = stbi__get16le(s); + g->h = stbi__get16le(s); + g->flags = stbi__get8(s); + g->bgindex = stbi__get8(s); + g->ratio = stbi__get8(s); + g->transparent = -1; + + if (comp != 0) *comp = 4; // can't actually tell whether it's 3 or 4 until we parse the comments + + if (is_info) return 1; + + if (g->flags & 0x80) + stbi__gif_parse_colortable(s,g->pal, 2 << (g->flags & 7), -1); + + return 1; +} + +static int stbi__gif_info_raw(stbi__context *s, int *x, int *y, int *comp) +{ + stbi__gif g; + if (!stbi__gif_header(s, &g, comp, 1)) { + stbi__rewind( s ); + return 0; + } + if (x) *x = g.w; + if (y) *y = g.h; + return 1; +} + +static void stbi__out_gif_code(stbi__gif *g, stbi__uint16 code) +{ + stbi_uc *p, *c; + + // recurse to decode the prefixes, since the linked-list is backwards, + // and working backwards through an interleaved image would be nasty + if (g->codes[code].prefix >= 0) + stbi__out_gif_code(g, g->codes[code].prefix); + + if (g->cur_y >= g->max_y) return; + + p = &g->out[g->cur_x + g->cur_y]; + c = &g->color_table[g->codes[code].suffix * 4]; + + if (c[3] >= 128) { + p[0] = c[2]; + p[1] = c[1]; + p[2] = c[0]; + p[3] = c[3]; + } + g->cur_x += 4; + + if (g->cur_x >= g->max_x) { + g->cur_x = g->start_x; + g->cur_y += g->step; + + while (g->cur_y >= g->max_y && g->parse > 0) { + g->step = (1 << g->parse) * g->line_size; + g->cur_y = g->start_y + (g->step >> 1); + --g->parse; + } + } +} + +static stbi_uc *stbi__process_gif_raster(stbi__context *s, stbi__gif *g) +{ + stbi_uc lzw_cs; + stbi__int32 len, code; + stbi__uint32 first; + stbi__int32 codesize, codemask, avail, oldcode, bits, valid_bits, clear; + stbi__gif_lzw *p; + + lzw_cs = stbi__get8(s); + if (lzw_cs > 12) return NULL; + clear = 1 << lzw_cs; + first = 1; + codesize = lzw_cs + 1; + codemask = (1 << codesize) - 1; + bits = 0; + valid_bits = 0; + for (code = 0; code < clear; code++) { + g->codes[code].prefix = -1; + g->codes[code].first = (stbi_uc) code; + g->codes[code].suffix = (stbi_uc) code; + } + + // support no starting clear code + avail = clear+2; + oldcode = -1; + + len = 0; + for(;;) { + if (valid_bits < codesize) { + if (len == 0) { + len = stbi__get8(s); // start new block + if (len == 0) + return g->out; + } + --len; + bits |= (stbi__int32) stbi__get8(s) << valid_bits; + valid_bits += 8; + } else { + stbi__int32 code = bits & codemask; + bits >>= codesize; + valid_bits -= codesize; + // @OPTIMIZE: is there some way we can accelerate the non-clear path? + if (code == clear) { // clear code + codesize = lzw_cs + 1; + codemask = (1 << codesize) - 1; + avail = clear + 2; + oldcode = -1; + first = 0; + } else if (code == clear + 1) { // end of stream code + stbi__skip(s, len); + while ((len = stbi__get8(s)) > 0) + stbi__skip(s,len); + return g->out; + } else if (code <= avail) { + if (first) return stbi__errpuc("no clear code", "Corrupt GIF"); + + if (oldcode >= 0) { + p = &g->codes[avail++]; + if (avail > 4096) return stbi__errpuc("too many codes", "Corrupt GIF"); + p->prefix = (stbi__int16) oldcode; + p->first = g->codes[oldcode].first; + p->suffix = (code == avail) ? p->first : g->codes[code].first; + } else if (code == avail) + return stbi__errpuc("illegal code in raster", "Corrupt GIF"); + + stbi__out_gif_code(g, (stbi__uint16) code); + + if ((avail & codemask) == 0 && avail <= 0x0FFF) { + codesize++; + codemask = (1 << codesize) - 1; + } + + oldcode = code; + } else { + return stbi__errpuc("illegal code in raster", "Corrupt GIF"); + } + } + } +} + +static void stbi__fill_gif_background(stbi__gif *g) +{ + int i; + stbi_uc *c = g->pal[g->bgindex]; + // @OPTIMIZE: write a dword at a time + for (i = 0; i < g->w * g->h * 4; i += 4) { + stbi_uc *p = &g->out[i]; + p[0] = c[2]; + p[1] = c[1]; + p[2] = c[0]; + p[3] = c[3]; + } +} + +// this function is designed to support animated gifs, although stb_image doesn't support it +static stbi_uc *stbi__gif_load_next(stbi__context *s, stbi__gif *g, int *comp, int req_comp) +{ + int i; + stbi_uc *old_out = 0; + + if (g->out == 0) { + if (!stbi__gif_header(s, g, comp,0)) return 0; // stbi__g_failure_reason set by stbi__gif_header + g->out = (stbi_uc *) stbi__malloc(4 * g->w * g->h); + if (g->out == 0) return stbi__errpuc("outofmem", "Out of memory"); + stbi__fill_gif_background(g); + } else { + // animated-gif-only path + if (((g->eflags & 0x1C) >> 2) == 3) { + old_out = g->out; + g->out = (stbi_uc *) stbi__malloc(4 * g->w * g->h); + if (g->out == 0) return stbi__errpuc("outofmem", "Out of memory"); + memcpy(g->out, old_out, g->w*g->h*4); + } + } + + for (;;) { + switch (stbi__get8(s)) { + case 0x2C: /* Image Descriptor */ + { + stbi__int32 x, y, w, h; + stbi_uc *o; + + x = stbi__get16le(s); + y = stbi__get16le(s); + w = stbi__get16le(s); + h = stbi__get16le(s); + if (((x + w) > (g->w)) || ((y + h) > (g->h))) + return stbi__errpuc("bad Image Descriptor", "Corrupt GIF"); + + g->line_size = g->w * 4; + g->start_x = x * 4; + g->start_y = y * g->line_size; + g->max_x = g->start_x + w * 4; + g->max_y = g->start_y + h * g->line_size; + g->cur_x = g->start_x; + g->cur_y = g->start_y; + + g->lflags = stbi__get8(s); + + if (g->lflags & 0x40) { + g->step = 8 * g->line_size; // first interlaced spacing + g->parse = 3; + } else { + g->step = g->line_size; + g->parse = 0; + } + + if (g->lflags & 0x80) { + stbi__gif_parse_colortable(s,g->lpal, 2 << (g->lflags & 7), g->eflags & 0x01 ? g->transparent : -1); + g->color_table = (stbi_uc *) g->lpal; + } else if (g->flags & 0x80) { + for (i=0; i < 256; ++i) // @OPTIMIZE: stbi__jpeg_reset only the previous transparent + g->pal[i][3] = 255; + if (g->transparent >= 0 && (g->eflags & 0x01)) + g->pal[g->transparent][3] = 0; + g->color_table = (stbi_uc *) g->pal; + } else + return stbi__errpuc("missing color table", "Corrupt GIF"); + + o = stbi__process_gif_raster(s, g); + if (o == NULL) return NULL; + + if (req_comp && req_comp != 4) + o = stbi__convert_format(o, 4, req_comp, g->w, g->h); + return o; + } + + case 0x21: // Comment Extension. + { + int len; + if (stbi__get8(s) == 0xF9) { // Graphic Control Extension. + len = stbi__get8(s); + if (len == 4) { + g->eflags = stbi__get8(s); + stbi__get16le(s); // delay + g->transparent = stbi__get8(s); + } else { + stbi__skip(s, len); + break; + } + } + while ((len = stbi__get8(s)) != 0) + stbi__skip(s, len); + break; + } + + case 0x3B: // gif stream termination code + return (stbi_uc *) s; // using '1' causes warning on some compilers + + default: + return stbi__errpuc("unknown code", "Corrupt GIF"); + } + } +} + +static stbi_uc *stbi__gif_load(stbi__context *s, int *x, int *y, int *comp, int req_comp) +{ + stbi_uc *u = 0; + stbi__gif g; + memset(&g, 0, sizeof(g)); + + u = stbi__gif_load_next(s, &g, comp, req_comp); + if (u == (stbi_uc *) s) u = 0; // end of animated gif marker + if (u) { + *x = g.w; + *y = g.h; + } + + return u; +} + +static int stbi__gif_info(stbi__context *s, int *x, int *y, int *comp) +{ + return stbi__gif_info_raw(s,x,y,comp); +} +#endif + +// ************************************************************************************************* +// Radiance RGBE HDR loader +// originally by Nicolas Schulz +#ifndef STBI_NO_HDR +static int stbi__hdr_test_core(stbi__context *s) +{ + const char *signature = "#?RADIANCE\n"; + int i; + for (i=0; signature[i]; ++i) + if (stbi__get8(s) != signature[i]) + return 0; + return 1; +} + +static int stbi__hdr_test(stbi__context* s) +{ + int r = stbi__hdr_test_core(s); + stbi__rewind(s); + return r; +} + +#define STBI__HDR_BUFLEN 1024 +static char *stbi__hdr_gettoken(stbi__context *z, char *buffer) +{ + int len=0; + char c = '\0'; + + c = (char) stbi__get8(z); + + while (!stbi__at_eof(z) && c != '\n') { + buffer[len++] = c; + if (len == STBI__HDR_BUFLEN-1) { + // flush to end of line + while (!stbi__at_eof(z) && stbi__get8(z) != '\n') + ; + break; + } + c = (char) stbi__get8(z); + } + + buffer[len] = 0; + return buffer; +} + +static void stbi__hdr_convert(float *output, stbi_uc *input, int req_comp) +{ + if ( input[3] != 0 ) { + float f1; + // Exponent + f1 = (float) ldexp(1.0f, input[3] - (int)(128 + 8)); + if (req_comp <= 2) + output[0] = (input[0] + input[1] + input[2]) * f1 / 3; + else { + output[0] = input[0] * f1; + output[1] = input[1] * f1; + output[2] = input[2] * f1; + } + if (req_comp == 2) output[1] = 1; + if (req_comp == 4) output[3] = 1; + } else { + switch (req_comp) { + case 4: output[3] = 1; /* fallthrough */ + case 3: output[0] = output[1] = output[2] = 0; + break; + case 2: output[1] = 1; /* fallthrough */ + case 1: output[0] = 0; + break; + } + } +} + +static float *stbi__hdr_load(stbi__context *s, int *x, int *y, int *comp, int req_comp) +{ + char buffer[STBI__HDR_BUFLEN]; + char *token; + int valid = 0; + int width, height; + stbi_uc *scanline; + float *hdr_data; + int len; + unsigned char count, value; + int i, j, k, c1,c2, z; + + + // Check identifier + if (strcmp(stbi__hdr_gettoken(s,buffer), "#?RADIANCE") != 0) + return stbi__errpf("not HDR", "Corrupt HDR image"); + + // Parse header + for(;;) { + token = stbi__hdr_gettoken(s,buffer); + if (token[0] == 0) break; + if (strcmp(token, "FORMAT=32-bit_rle_rgbe") == 0) valid = 1; + } + + if (!valid) return stbi__errpf("unsupported format", "Unsupported HDR format"); + + // Parse width and height + // can't use sscanf() if we're not using stdio! + token = stbi__hdr_gettoken(s,buffer); + if (strncmp(token, "-Y ", 3)) return stbi__errpf("unsupported data layout", "Unsupported HDR format"); + token += 3; + height = (int) strtol(token, &token, 10); + while (*token == ' ') ++token; + if (strncmp(token, "+X ", 3)) return stbi__errpf("unsupported data layout", "Unsupported HDR format"); + token += 3; + width = (int) strtol(token, NULL, 10); + + *x = width; + *y = height; + + if (comp) *comp = 3; + if (req_comp == 0) req_comp = 3; + + // Read data + hdr_data = (float *) stbi__malloc(height * width * req_comp * sizeof(float)); + + // Load image data + // image data is stored as some number of sca + if ( width < 8 || width >= 32768) { + // Read flat data + for (j=0; j < height; ++j) { + for (i=0; i < width; ++i) { + stbi_uc rgbe[4]; + main_decode_loop: + stbi__getn(s, rgbe, 4); + stbi__hdr_convert(hdr_data + j * width * req_comp + i * req_comp, rgbe, req_comp); + } + } + } else { + // Read RLE-encoded data + scanline = NULL; + + for (j = 0; j < height; ++j) { + c1 = stbi__get8(s); + c2 = stbi__get8(s); + len = stbi__get8(s); + if (c1 != 2 || c2 != 2 || (len & 0x80)) { + // not run-length encoded, so we have to actually use THIS data as a decoded + // pixel (note this can't be a valid pixel--one of RGB must be >= 128) + stbi_uc rgbe[4]; + rgbe[0] = (stbi_uc) c1; + rgbe[1] = (stbi_uc) c2; + rgbe[2] = (stbi_uc) len; + rgbe[3] = (stbi_uc) stbi__get8(s); + stbi__hdr_convert(hdr_data, rgbe, req_comp); + i = 1; + j = 0; + STBI_FREE(scanline); + goto main_decode_loop; // yes, this makes no sense + } + len <<= 8; + len |= stbi__get8(s); + if (len != width) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("invalid decoded scanline length", "corrupt HDR"); } + if (scanline == NULL) scanline = (stbi_uc *) stbi__malloc(width * 4); + + for (k = 0; k < 4; ++k) { + i = 0; + while (i < width) { + count = stbi__get8(s); + if (count > 128) { + // Run + value = stbi__get8(s); + count -= 128; + for (z = 0; z < count; ++z) + scanline[i++ * 4 + k] = value; + } else { + // Dump + for (z = 0; z < count; ++z) + scanline[i++ * 4 + k] = stbi__get8(s); + } + } + } + for (i=0; i < width; ++i) + stbi__hdr_convert(hdr_data+(j*width + i)*req_comp, scanline + i*4, req_comp); + } + STBI_FREE(scanline); + } + + return hdr_data; +} + +static int stbi__hdr_info(stbi__context *s, int *x, int *y, int *comp) +{ + char buffer[STBI__HDR_BUFLEN]; + char *token; + int valid = 0; + + if (strcmp(stbi__hdr_gettoken(s,buffer), "#?RADIANCE") != 0) { + stbi__rewind( s ); + return 0; + } + + for(;;) { + token = stbi__hdr_gettoken(s,buffer); + if (token[0] == 0) break; + if (strcmp(token, "FORMAT=32-bit_rle_rgbe") == 0) valid = 1; + } + + if (!valid) { + stbi__rewind( s ); + return 0; + } + token = stbi__hdr_gettoken(s,buffer); + if (strncmp(token, "-Y ", 3)) { + stbi__rewind( s ); + return 0; + } + token += 3; + *y = (int) strtol(token, &token, 10); + while (*token == ' ') ++token; + if (strncmp(token, "+X ", 3)) { + stbi__rewind( s ); + return 0; + } + token += 3; + *x = (int) strtol(token, NULL, 10); + *comp = 3; + return 1; +} +#endif // STBI_NO_HDR + +#ifndef STBI_NO_BMP +static int stbi__bmp_info(stbi__context *s, int *x, int *y, int *comp) +{ + int hsz; + if (stbi__get8(s) != 'B' || stbi__get8(s) != 'M') { + stbi__rewind( s ); + return 0; + } + stbi__skip(s,12); + hsz = stbi__get32le(s); + if (hsz != 12 && hsz != 40 && hsz != 56 && hsz != 108 && hsz != 124) { + stbi__rewind( s ); + return 0; + } + if (hsz == 12) { + *x = stbi__get16le(s); + *y = stbi__get16le(s); + } else { + *x = stbi__get32le(s); + *y = stbi__get32le(s); + } + if (stbi__get16le(s) != 1) { + stbi__rewind( s ); + return 0; + } + *comp = stbi__get16le(s) / 8; + return 1; +} +#endif + +#ifndef STBI_NO_PSD +static int stbi__psd_info(stbi__context *s, int *x, int *y, int *comp) +{ + int channelCount; + if (stbi__get32be(s) != 0x38425053) { + stbi__rewind( s ); + return 0; + } + if (stbi__get16be(s) != 1) { + stbi__rewind( s ); + return 0; + } + stbi__skip(s, 6); + channelCount = stbi__get16be(s); + if (channelCount < 0 || channelCount > 16) { + stbi__rewind( s ); + return 0; + } + *y = stbi__get32be(s); + *x = stbi__get32be(s); + if (stbi__get16be(s) != 8) { + stbi__rewind( s ); + return 0; + } + if (stbi__get16be(s) != 3) { + stbi__rewind( s ); + return 0; + } + *comp = 4; + return 1; +} +#endif + +#ifndef STBI_NO_PIC +static int stbi__pic_info(stbi__context *s, int *x, int *y, int *comp) +{ + int act_comp=0,num_packets=0,chained; + stbi__pic_packet packets[10]; + + stbi__skip(s, 92); + + *x = stbi__get16be(s); + *y = stbi__get16be(s); + if (stbi__at_eof(s)) return 0; + if ( (*x) != 0 && (1 << 28) / (*x) < (*y)) { + stbi__rewind( s ); + return 0; + } + + stbi__skip(s, 8); + + do { + stbi__pic_packet *packet; + + if (num_packets==sizeof(packets)/sizeof(packets[0])) + return 0; + + packet = &packets[num_packets++]; + chained = stbi__get8(s); + packet->size = stbi__get8(s); + packet->type = stbi__get8(s); + packet->channel = stbi__get8(s); + act_comp |= packet->channel; + + if (stbi__at_eof(s)) { + stbi__rewind( s ); + return 0; + } + if (packet->size != 8) { + stbi__rewind( s ); + return 0; + } + } while (chained); + + *comp = (act_comp & 0x10 ? 4 : 3); + + return 1; +} +#endif + +// ************************************************************************************************* +// Portable Gray Map and Portable Pixel Map loader +// by Ken Miller +// +// PGM: http://netpbm.sourceforge.net/doc/pgm.html +// PPM: http://netpbm.sourceforge.net/doc/ppm.html +// +// Known limitations: +// Does not support comments in the header section +// Does not support ASCII image data (formats P2 and P3) +// Does not support 16-bit-per-channel + +#ifndef STBI_NO_PNM + +static int stbi__pnm_test(stbi__context *s) +{ + char p, t; + p = (char) stbi__get8(s); + t = (char) stbi__get8(s); + if (p != 'P' || (t != '5' && t != '6')) { + stbi__rewind( s ); + return 0; + } + return 1; +} + +static stbi_uc *stbi__pnm_load(stbi__context *s, int *x, int *y, int *comp, int req_comp) +{ + stbi_uc *out; + if (!stbi__pnm_info(s, (int *)&s->img_x, (int *)&s->img_y, (int *)&s->img_n)) + return 0; + *x = s->img_x; + *y = s->img_y; + *comp = s->img_n; + + out = (stbi_uc *) stbi__malloc(s->img_n * s->img_x * s->img_y); + if (!out) return stbi__errpuc("outofmem", "Out of memory"); + stbi__getn(s, out, s->img_n * s->img_x * s->img_y); + + if (req_comp && req_comp != s->img_n) { + out = stbi__convert_format(out, s->img_n, req_comp, s->img_x, s->img_y); + if (out == NULL) return out; // stbi__convert_format frees input on failure + } + return out; +} + +static int stbi__pnm_isspace(char c) +{ + return c == ' ' || c == '\t' || c == '\n' || c == '\v' || c == '\f' || c == '\r'; +} + +static void stbi__pnm_skip_whitespace(stbi__context *s, char *c) +{ + while (!stbi__at_eof(s) && stbi__pnm_isspace(*c)) + *c = (char) stbi__get8(s); +} + +static int stbi__pnm_isdigit(char c) +{ + return c >= '0' && c <= '9'; +} + +static int stbi__pnm_getinteger(stbi__context *s, char *c) +{ + int value = 0; + + while (!stbi__at_eof(s) && stbi__pnm_isdigit(*c)) { + value = value*10 + (*c - '0'); + *c = (char) stbi__get8(s); + } + + return value; +} + +static int stbi__pnm_info(stbi__context *s, int *x, int *y, int *comp) +{ + int maxv; + char c, p, t; + + stbi__rewind( s ); + + // Get identifier + p = (char) stbi__get8(s); + t = (char) stbi__get8(s); + if (p != 'P' || (t != '5' && t != '6')) { + stbi__rewind( s ); + return 0; + } + + *comp = (t == '6') ? 3 : 1; // '5' is 1-component .pgm; '6' is 3-component .ppm + + c = (char) stbi__get8(s); + stbi__pnm_skip_whitespace(s, &c); + + *x = stbi__pnm_getinteger(s, &c); // read width + stbi__pnm_skip_whitespace(s, &c); + + *y = stbi__pnm_getinteger(s, &c); // read height + stbi__pnm_skip_whitespace(s, &c); + + maxv = stbi__pnm_getinteger(s, &c); // read max value + + if (maxv > 255) + return stbi__err("max value > 255", "PPM image not 8-bit"); + else + return 1; +} +#endif + +static int stbi__info_main(stbi__context *s, int *x, int *y, int *comp) +{ + #ifndef STBI_NO_JPEG + if (stbi__jpeg_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_PNG + if (stbi__png_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_GIF + if (stbi__gif_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_BMP + if (stbi__bmp_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_PSD + if (stbi__psd_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_PIC + if (stbi__pic_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_PNM + if (stbi__pnm_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_HDR + if (stbi__hdr_info(s, x, y, comp)) return 1; + #endif + + // test tga last because it's a crappy test! + #ifndef STBI_NO_TGA + if (stbi__tga_info(s, x, y, comp)) + return 1; + #endif + return stbi__err("unknown image type", "Image not of any known type, or corrupt"); +} + +#ifndef STBI_NO_STDIO +STBIDEF int stbi_info(char const *filename, int *x, int *y, int *comp) +{ + FILE *f = stbi__fopen(filename, "rb"); + int result; + if (!f) return stbi__err("can't fopen", "Unable to open file"); + result = stbi_info_from_file(f, x, y, comp); + fclose(f); + return result; +} + +STBIDEF int stbi_info_from_file(FILE *f, int *x, int *y, int *comp) +{ + int r; + stbi__context s; + long pos = ftell(f); + stbi__start_file(&s, f); + r = stbi__info_main(&s,x,y,comp); + fseek(f,pos,SEEK_SET); + return r; +} +#endif // !STBI_NO_STDIO + +STBIDEF int stbi_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp) +{ + stbi__context s; + stbi__start_mem(&s,buffer,len); + return stbi__info_main(&s,x,y,comp); +} + +STBIDEF int stbi_info_from_callbacks(stbi_io_callbacks const *c, void *user, int *x, int *y, int *comp) +{ + stbi__context s; + stbi__start_callbacks(&s, (stbi_io_callbacks *) c, user); + return stbi__info_main(&s,x,y,comp); +} + +#endif // STB_IMAGE_IMPLEMENTATION + +/* + revision history: + 2.05 (2015-04-19) fix bug in progressive JPEG handling, fix warning + 2.04 (2015-04-15) try to re-enable SIMD on MinGW 64-bit + 2.03 (2015-04-12) extra corruption checking (mmozeiko) + stbi_set_flip_vertically_on_load (nguillemot) + fix NEON support; fix mingw support + 2.02 (2015-01-19) fix incorrect assert, fix warning + 2.01 (2015-01-17) fix various warnings; suppress SIMD on gcc 32-bit without -msse2 + 2.00b (2014-12-25) fix STBI_MALLOC in progressive JPEG + 2.00 (2014-12-25) optimize JPG, including x86 SSE2 & NEON SIMD (ryg) + progressive JPEG (stb) + PGM/PPM support (Ken Miller) + STBI_MALLOC,STBI_REALLOC,STBI_FREE + GIF bugfix -- seemingly never worked + STBI_NO_*, STBI_ONLY_* + 1.48 (2014-12-14) fix incorrectly-named assert() + 1.47 (2014-12-14) 1/2/4-bit PNG support, both direct and paletted (Omar Cornut & stb) + optimize PNG (ryg) + fix bug in interlaced PNG with user-specified channel count (stb) + 1.46 (2014-08-26) + fix broken tRNS chunk (colorkey-style transparency) in non-paletted PNG + 1.45 (2014-08-16) + fix MSVC-ARM internal compiler error by wrapping malloc + 1.44 (2014-08-07) + various warning fixes from Ronny Chevalier + 1.43 (2014-07-15) + fix MSVC-only compiler problem in code changed in 1.42 + 1.42 (2014-07-09) + don't define _CRT_SECURE_NO_WARNINGS (affects user code) + fixes to stbi__cleanup_jpeg path + added STBI_ASSERT to avoid requiring assert.h + 1.41 (2014-06-25) + fix search&replace from 1.36 that messed up comments/error messages + 1.40 (2014-06-22) + fix gcc struct-initialization warning + 1.39 (2014-06-15) + fix to TGA optimization when req_comp != number of components in TGA; + fix to GIF loading because BMP wasn't rewinding (whoops, no GIFs in my test suite) + add support for BMP version 5 (more ignored fields) + 1.38 (2014-06-06) + suppress MSVC warnings on integer casts truncating values + fix accidental rename of 'skip' field of I/O + 1.37 (2014-06-04) + remove duplicate typedef + 1.36 (2014-06-03) + convert to header file single-file library + if de-iphone isn't set, load iphone images color-swapped instead of returning NULL + 1.35 (2014-05-27) + various warnings + fix broken STBI_SIMD path + fix bug where stbi_load_from_file no longer left file pointer in correct place + fix broken non-easy path for 32-bit BMP (possibly never used) + TGA optimization by Arseny Kapoulkine + 1.34 (unknown) + use STBI_NOTUSED in stbi__resample_row_generic(), fix one more leak in tga failure case + 1.33 (2011-07-14) + make stbi_is_hdr work in STBI_NO_HDR (as specified), minor compiler-friendly improvements + 1.32 (2011-07-13) + support for "info" function for all supported filetypes (SpartanJ) + 1.31 (2011-06-20) + a few more leak fixes, bug in PNG handling (SpartanJ) + 1.30 (2011-06-11) + added ability to load files via callbacks to accomidate custom input streams (Ben Wenger) + removed deprecated format-specific test/load functions + removed support for installable file formats (stbi_loader) -- would have been broken for IO callbacks anyway + error cases in bmp and tga give messages and don't leak (Raymond Barbiero, grisha) + fix inefficiency in decoding 32-bit BMP (David Woo) + 1.29 (2010-08-16) + various warning fixes from Aurelien Pocheville + 1.28 (2010-08-01) + fix bug in GIF palette transparency (SpartanJ) + 1.27 (2010-08-01) + cast-to-stbi_uc to fix warnings + 1.26 (2010-07-24) + fix bug in file buffering for PNG reported by SpartanJ + 1.25 (2010-07-17) + refix trans_data warning (Won Chun) + 1.24 (2010-07-12) + perf improvements reading from files on platforms with lock-heavy fgetc() + minor perf improvements for jpeg + deprecated type-specific functions so we'll get feedback if they're needed + attempt to fix trans_data warning (Won Chun) + 1.23 fixed bug in iPhone support + 1.22 (2010-07-10) + removed image *writing* support + stbi_info support from Jetro Lauha + GIF support from Jean-Marc Lienher + iPhone PNG-extensions from James Brown + warning-fixes from Nicolas Schulz and Janez Zemva (i.stbi__err. Janez (U+017D)emva) + 1.21 fix use of 'stbi_uc' in header (reported by jon blow) + 1.20 added support for Softimage PIC, by Tom Seddon + 1.19 bug in interlaced PNG corruption check (found by ryg) + 1.18 (2008-08-02) + fix a threading bug (local mutable static) + 1.17 support interlaced PNG + 1.16 major bugfix - stbi__convert_format converted one too many pixels + 1.15 initialize some fields for thread safety + 1.14 fix threadsafe conversion bug + header-file-only version (#define STBI_HEADER_FILE_ONLY before including) + 1.13 threadsafe + 1.12 const qualifiers in the API + 1.11 Support installable IDCT, colorspace conversion routines + 1.10 Fixes for 64-bit (don't use "unsigned long") + optimized upsampling by Fabian "ryg" Giesen + 1.09 Fix format-conversion for PSD code (bad global variables!) + 1.08 Thatcher Ulrich's PSD code integrated by Nicolas Schulz + 1.07 attempt to fix C++ warning/errors again + 1.06 attempt to fix C++ warning/errors again + 1.05 fix TGA loading to return correct *comp and use good luminance calc + 1.04 default float alpha is 1, not 255; use 'void *' for stbi_image_free + 1.03 bugfixes to STBI_NO_STDIO, STBI_NO_HDR + 1.02 support for (subset of) HDR files, float interface for preferred access to them + 1.01 fix bug: possible bug in handling right-side up bmps... not sure + fix bug: the stbi__bmp_load() and stbi__tga_load() functions didn't work at all + 1.00 interface to zlib that skips zlib header + 0.99 correct handling of alpha in palette + 0.98 TGA loader by lonesock; dynamically add loaders (untested) + 0.97 jpeg errors on too large a file; also catch another malloc failure + 0.96 fix detection of invalid v value - particleman@mollyrocket forum + 0.95 during header scan, seek to markers in case of padding + 0.94 STBI_NO_STDIO to disable stdio usage; rename all #defines the same + 0.93 handle jpegtran output; verbose errors + 0.92 read 4,8,16,24,32-bit BMP files of several formats + 0.91 output 24-bit Windows 3.0 BMP files + 0.90 fix a few more warnings; bump version number to approach 1.0 + 0.61 bugfixes due to Marc LeBlanc, Christopher Lloyd + 0.60 fix compiling as c++ + 0.59 fix warnings: merge Dave Moore's -Wall fixes + 0.58 fix bug: zlib uncompressed mode len/nlen was wrong endian + 0.57 fix bug: jpg last huffman symbol before marker was >9 bits but less than 16 available + 0.56 fix bug: zlib uncompressed mode len vs. nlen + 0.55 fix bug: restart_interval not initialized to 0 + 0.54 allow NULL for 'int *comp' + 0.53 fix bug in png 3->4; speedup png decoding + 0.52 png handles req_comp=3,4 directly; minor cleanup; jpeg comments + 0.51 obey req_comp requests, 1-component jpegs return as 1-component, + on 'test' only check type, not whether we support this variant + 0.50 (2006-11-19) + first released version +*/ diff --git a/Core/Contents/PolycodeView/MSVC/PolycodeView.cpp b/Core/Contents/PolycodeView/MSVC/PolycodeView.cpp index 557308f79..d1f66e2f7 100644 --- a/Core/Contents/PolycodeView/MSVC/PolycodeView.cpp +++ b/Core/Contents/PolycodeView/MSVC/PolycodeView.cpp @@ -97,16 +97,25 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) core->handleMouseUp(CoreInput::MOUSE_BUTTON2, lParam,wParam); break; -#ifndef NO_TOUCH_API - case WM_TOUCH: - if(core) { - if(core->isMultiTouchEnabled()) { - core->handleTouchEvent(lParam, wParam); +#ifndef NO_TOUCH_API + #ifdef NO_PEN_API + case WM_TOUCH: + if(core) { + if(core->isMultiTouchEnabled()) { + core->handleTouchEvent(lParam, wParam); + } } - } - break; + break; + #else + case WM_POINTERUPDATE: + case WM_POINTERUP: + case WM_POINTERDOWN: + if (core) + core->handlePointerUpdate(lParam, wParam); + break; + #endif #endif - + case WM_MBUTTONDOWN: if(core) core->handleMouseDown(CoreInput::MOUSE_BUTTON3, lParam,wParam); @@ -116,6 +125,7 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) core->handleMouseUp(CoreInput::MOUSE_BUTTON3, lParam,wParam); break; case WM_KEYDOWN: + case WM_SYSKEYDOWN: if(core) { wchar_t unicodeChar = 0; MSG m; @@ -133,6 +143,7 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) } break; case WM_KEYUP: + case WM_SYSKEYUP: if(core) core->handleKeyUp(lParam,wParam); break; @@ -158,7 +169,14 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) PolycodeView::PolycodeView(HINSTANCE hInstance, int nCmdShow, LPCTSTR windowTitle, bool resizable, bool showDebugConsole) : PolycodeViewBase() { -WNDCLASSEX wcex; + /* + typedef BOOL(WINAPI *SetProcessDPIAwarePtr)(VOID); + SetProcessDPIAwarePtr set_process_dpi_aware_func = GetProcAddress(GetModuleHandleA("user32.dll"), "SetProcessDPIAware"); + if (set_process_dpi_aware_func) { + set_process_dpi_aware_func(); + } + */ + WNDCLASSEX wcex; wcex.cbSize = sizeof(WNDCLASSEX); wcex.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC; @@ -175,10 +193,12 @@ WNDCLASSEX wcex; RegisterClassEx(&wcex); + this->resizable = resizable; + if(resizable) { - hwnd = CreateWindowEx(WS_EX_APPWINDOW, L"POLYCODEAPPLICATION", windowTitle, WS_OVERLAPPEDWINDOW|WS_SYSMENU, 0, 0, 640, 480, NULL, NULL, hInstance, NULL); + hwnd = CreateWindowEx(WS_EX_APPWINDOW, L"POLYCODEAPPLICATION", windowTitle, WS_OVERLAPPEDWINDOW | WS_SYSMENU, 0, 0, 640, 480, NULL, NULL, hInstance, NULL); } else { - hwnd = CreateWindowEx(WS_EX_APPWINDOW, L"POLYCODEAPPLICATION", windowTitle, WS_OVERLAPPED|WS_SYSMENU, 0, 0, 640, 480, NULL, NULL, hInstance, NULL); + hwnd = CreateWindowEx(WS_EX_APPWINDOW, L"POLYCODEAPPLICATION", windowTitle, WS_CAPTION | WS_POPUP | WS_SYSMENU, 0, 0, 640, 480, NULL, NULL, hInstance, NULL); } windowData = (void*)&hwnd; diff --git a/Core/Contents/PolycodeView/Mac OS X/PolycodeView.mm b/Core/Contents/PolycodeView/Mac OS X/PolycodeView.mm index 89e9cbf73..08d72b6cf 100644 --- a/Core/Contents/PolycodeView/Mac OS X/PolycodeView.mm +++ b/Core/Contents/PolycodeView/Mac OS X/PolycodeView.mm @@ -46,7 +46,9 @@ - (void) awakeFromNib { contextLock = [[NSLock alloc] init]; [[self window] setAcceptsMouseMovedEvents:YES]; - [[self window] makeFirstResponder:self]; + [[self window] makeFirstResponder:self]; + + [self setAcceptsTouchEvents:YES]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(windowResized:) name:NSWindowDidResizeNotification object:[self window]]; @@ -417,6 +419,154 @@ - (void)scrollWheel:(NSEvent*) event { } +- (void)touchesBeganWithEvent:(NSEvent *)event +{ + if(core == NULL) + return; + + core->lockMutex(core->eventMutex); + + NSSet *touches = [event touchesMatchingPhase:NSTouchPhaseAny inView:self]; + NSArray *touchArray = [touches allObjects]; + + std::vector touchInfos; + + for(NSTouch *touch in touchArray) { + TouchInfo info; + long touchID = (long)[touch identity]; + info.id = touchID; + info.position.x = [touch normalizedPosition].x * core->getXRes(); + info.position.y = (1.0-[touch normalizedPosition].y) * core->getYRes(); + touchInfos.push_back(info); + } + + unsigned int index = 0; + for(NSTouch *touch in touchArray) { + if([touch phase] == NSTouchPhaseBegan) { + CocoaEvent newEvent; + newEvent.eventCode = InputEvent::EVENT_TOUCHES_BEGAN; + newEvent.eventGroup = CocoaEvent::INPUT_EVENT; + newEvent.touches = touchInfos; + newEvent.touch = touchInfos[index]; + core->cocoaEvents.push_back(newEvent); + } + index++; + } + core->unlockMutex(core->eventMutex); + +} + +- (void)touchesMovedWithEvent:(NSEvent *)event +{ + + if(core == NULL) + return; + + core->lockMutex(core->eventMutex); + + NSSet *touches = [event touchesMatchingPhase:NSTouchPhaseAny inView:self]; + NSArray *touchArray = [touches allObjects]; + + std::vector touchInfos; + + for(NSTouch *touch in touchArray) { + TouchInfo info; + long touchID = (long)[touch identity]; + info.id = touchID; + info.position.x = [touch normalizedPosition].x * core->getXRes(); + info.position.y = (1.0-[touch normalizedPosition].y) * core->getYRes(); + touchInfos.push_back(info); + } + + unsigned int index = 0; + for(NSTouch *touch in touchArray) { + if([touch phase] == NSTouchPhaseMoved) { + CocoaEvent newEvent; + newEvent.eventCode = InputEvent::EVENT_TOUCHES_MOVED; + newEvent.eventGroup = CocoaEvent::INPUT_EVENT; + newEvent.touches = touchInfos; + newEvent.touch = touchInfos[index]; + core->cocoaEvents.push_back(newEvent); + } + index++; + } + core->unlockMutex(core->eventMutex); + +} + +- (void)touchesEndedWithEvent:(NSEvent *)event +{ + if(core == NULL) + return; + + core->lockMutex(core->eventMutex); + + NSSet *touches = [event touchesMatchingPhase:NSTouchPhaseAny inView:self]; + NSArray *touchArray = [touches allObjects]; + + std::vector touchInfos; + + for(NSTouch *touch in touchArray) { + TouchInfo info; + long touchID = (long)[touch identity]; + info.id = touchID; + info.position.x = [touch normalizedPosition].x * core->getXRes(); + info.position.y = (1.0-[touch normalizedPosition].y) * core->getYRes(); + touchInfos.push_back(info); + } + + unsigned int index = 0; + for(NSTouch *touch in touchArray) { + if([touch phase] == NSTouchPhaseEnded) { + CocoaEvent newEvent; + newEvent.eventCode = InputEvent::EVENT_TOUCHES_ENDED; + newEvent.eventGroup = CocoaEvent::INPUT_EVENT; + newEvent.touches = touchInfos; + newEvent.touch = touchInfos[index]; + core->cocoaEvents.push_back(newEvent); + } + index++; + } + core->unlockMutex(core->eventMutex); + +} + +- (void)touchesCancelledWithEvent:(NSEvent *)event +{ + if(core == NULL) + return; + + core->lockMutex(core->eventMutex); + + NSSet *touches = [event touchesMatchingPhase:NSTouchPhaseAny inView:self]; + NSArray *touchArray = [touches allObjects]; + + std::vector touchInfos; + + for(NSTouch *touch in touchArray) { + TouchInfo info; + long touchID = (long)[touch identity]; + info.id = touchID; + info.position.x = [touch normalizedPosition].x * core->getXRes(); + info.position.y = (1.0-[touch normalizedPosition].y) * core->getYRes(); + touchInfos.push_back(info); + } + + unsigned int index = 0; + for(NSTouch *touch in touchArray) { + if([touch phase] == NSTouchPhaseCancelled) { + CocoaEvent newEvent; + newEvent.eventCode = InputEvent::EVENT_TOUCHES_ENDED; + newEvent.eventGroup = CocoaEvent::INPUT_EVENT; + newEvent.touches = touchInfos; + newEvent.touch = touchInfos[index]; + core->cocoaEvents.push_back(newEvent); + } + index++; + } + core->unlockMutex(core->eventMutex); +} + - (void) mouseDown:(NSEvent *) event { diff --git a/Core/Contents/Source/OSBasics.cpp b/Core/Contents/Source/OSBasics.cpp index 288b22341..6bfdc4022 100755 --- a/Core/Contents/Source/OSBasics.cpp +++ b/Core/Contents/Source/OSBasics.cpp @@ -48,6 +48,7 @@ void wtoc(char* Dest, const WCHAR* Source) Dest[i] = (char)Source[i]; ++i; } + Dest[i] = 0; } void ctow(WCHAR* Dest, const char* Source) { @@ -56,6 +57,7 @@ void ctow(WCHAR* Dest, const char* Source) Dest[i] = (WCHAR)Source[i]; ++i; } + Dest[i] = 0; } #endif @@ -63,7 +65,7 @@ void ctow(WCHAR* Dest, const char* Source) OSFileEntry::OSFileEntry(const Polycode::String& fullPath, int type) { std::vector parts = fullPath.split("/"); - if(parts.size() > 0) { + if(parts.size() > 1) { String path = parts[0]; if(parts.size() > 1) { @@ -85,7 +87,9 @@ OSFileEntry::OSFileEntry(const String& path, const String& name, int type) { void OSFileEntry::init(const Polycode::String& path, const Polycode::String& name, int type) { this->basePath = path; - if(path == "/") { + if(path == "") { + this->fullPath = name; + } else if(path == "/") { this->fullPath = "/" + name; } else { this->fullPath = path + "/" + name; @@ -311,17 +315,14 @@ vector OSBasics::parseFolder(const String& pathString, bool showHid SetCurrentDirectory(tmp); - HANDLE hFind = FindFirstFile((LPCWSTR)"*", &findFileData); + HANDLE hFind = FindFirstFile(L"*", &findFileData); if(hFind == INVALID_HANDLE_VALUE) { SetCurrentDirectory(curDir); return returnVector; } - char fileName[260]; do { - memset(fileName, 0, 260); - wtoc(fileName, findFileData.cFileName); - String fname = string(fileName); + String fname(findFileData.cFileName); if((fname.c_str()[0] != '.' || (fname.c_str()[0] == '.' && showHidden)) && fname != "..") { if( findFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ) { diff --git a/Core/Contents/Source/PolyBezierCurve.cpp b/Core/Contents/Source/PolyBezierCurve.cpp index fbc5abfae..74d8737ce 100755 --- a/Core/Contents/Source/PolyBezierCurve.cpp +++ b/Core/Contents/Source/PolyBezierCurve.cpp @@ -24,6 +24,8 @@ using namespace Polycode; +bool BezierCurve::cacheHeightValues = false; +unsigned int BezierCurve::defaultHeightCacheResolution = 512; BezierPoint::BezierPoint(Number p1x, Number p1y, Number p1z, Number p2x, Number p2y, Number p2z, Number p3x, Number p3y, Number p3z) { p1.x = p1x; @@ -39,20 +41,14 @@ BezierPoint::BezierPoint(Number p1x, Number p1y, Number p1z, Number p2x, Number BezierCurve::BezierCurve(){ insertPoint = NULL; - for(int i=0; i < BUFFER_CACHE_PRECISION; i++) { - heightBuffer[i] = 0; - } - - buffersDirty = false; + evaluationAccuracy = 0.01; + distancesDirty = false; + heightCacheResolution = defaultHeightCacheResolution; } void BezierCurve::clearControlPoints() { insertPoint = NULL; - for(int i=0; i < BUFFER_CACHE_PRECISION; i++) { - heightBuffer[i] = 0; - } - - buffersDirty = true; + distancesDirty = true; for(int i=0; i < controlPoints.size(); i++) { delete controlPoints[i]; } @@ -60,7 +56,9 @@ void BezierCurve::clearControlPoints() { } BezierCurve::~BezierCurve() { - + for(int i=0; i < controlPoints.size(); i++) { + delete controlPoints[i]; + } } void BezierCurve::addControlPoint(Number p1x, Number p1y, Number p1z, Number p2x, Number p2y, Number p2z, Number p3x, Number p3y, Number p3z) { @@ -80,8 +78,7 @@ void BezierCurve::addControlPoint(Number p1x, Number p1y, Number p1z, Number p2x } distances.push_back(0); - recalculateDistances(); - buffersDirty = true; + distancesDirty = true; } void BezierCurve::addControlPoint3dWithHandles(Number p1x, Number p1y, Number p1z, Number p2x, Number p2y, Number p2z, Number p3x, Number p3y, Number p3z) { @@ -104,6 +101,8 @@ void BezierCurve::addControlPoint2d(Number x, Number y) { void BezierCurve::recalculateDistances() { if(controlPoints.size() < 2) return; + + distancesDirty = false; Number dist, lastDist = 0; distances[0] = 0; @@ -127,6 +126,26 @@ void BezierCurve::recalculateDistances() { for(int i=0; i < controlPoints.size(); i++) { distances[i] = distances[i]/totalDistance; } + + minX = getPointAt(0.0).x; + maxX = getPointAt(1.0).x; + midX = getPointAt(0.5).x; + + if(cacheHeightValues) { + rebuildHeightCache(); + } +} + +void BezierCurve::rebuildHeightCache() { + heightCache.clear(); + + Number xSize = maxX - minX; + + for(int i=0; i < heightCacheResolution; i++) { + Number xVal = minX + (xSize * ((Number)i)/((Number)heightCacheResolution)); + Number heightValue = getPointAt(getTValueAtX(xVal)).y; + heightCache.push_back(heightValue); + } } Vector3 BezierCurve::getPointBetween(Number a, BezierPoint *bp1, BezierPoint *bp2) { @@ -140,6 +159,62 @@ Vector3 BezierCurve::getPointBetween(Number a, BezierPoint *bp1, BezierPoint *bp return retVector; } +Number BezierCurve::getYValueAtX(Number x) { + if(cacheHeightValues) { + if(distancesDirty) { + recalculateDistances(); + } + unsigned int cacheIndex = (x-minX/(maxX-minX)) * ((Number) heightCacheResolution); + if(cacheIndex > heightCacheResolution-1) { + cacheIndex = heightCacheResolution-1; + } + return heightCache[cacheIndex]; + } else { + return getPointAt(getTValueAtX(x)).y; + } +} + +void BezierCurve::setHeightCacheResolution(Number resolution) { + heightCacheResolution = resolution; + distancesDirty = true; +} + +Number BezierCurve::getTValueAtX(Number x) { + if(controlPoints.size() < 2) { + return 0; + } + + if (distancesDirty) { + recalculateDistances(); + } + + if(x <= minX) { + return 0.0; + } + if(x >= maxX) { + return 1.0; + } + + Number _x = midX; + + Number lower = 0.0; + Number upper = 1.0; + Number percent = (upper + lower) / 2.0; + + while(fabs(x - _x) > evaluationAccuracy) { + if(x > _x) { + lower = percent; + } else { + upper = percent; + } + + percent = (upper + lower) / 2.0; + _x = getPointAt(percent).x; + } + + return percent; +} + BezierPoint *BezierCurve::getControlPoint(unsigned int index) { return controlPoints[index]; } @@ -156,36 +231,15 @@ void BezierCurve::removePoint(BezierPoint *point) { break; } } - recalculateDistances(); - buffersDirty = true; - -} - -Number BezierCurve::getHeightAt(Number a) { - if( a< 0) a = 0; - if(a > 1) a = 1; - - if (buffersDirty) - rebuildBuffers(); - - int unsigned index = ((Number)(BUFFER_CACHE_PRECISION)) * a; - - if(index > BUFFER_CACHE_PRECISION-1) - index = BUFFER_CACHE_PRECISION-1; - - return heightBuffer[index]; - -// return getPointAt(a).y; -} - -void BezierCurve::rebuildBuffers() { - for(int i=0; i < BUFFER_CACHE_PRECISION; i++) { - heightBuffer[i] = getPointAt(((Number)i)/((Number)BUFFER_CACHE_PRECISION)).y; - } - buffersDirty = false; + distancesDirty = true; } Vector3 BezierCurve::getPointAt(Number a) { + + if (distancesDirty) { + recalculateDistances(); + } + if(controlPoints.size() == 0) { return Vector3(); } diff --git a/Core/Contents/Source/PolyBone.cpp b/Core/Contents/Source/PolyBone.cpp index 4f3ddf266..0898fd91c 100755 --- a/Core/Contents/Source/PolyBone.cpp +++ b/Core/Contents/Source/PolyBone.cpp @@ -29,20 +29,13 @@ using namespace Polycode; -Bone::Bone(const String& boneName) : SceneEntity() { +Bone::Bone(const String& boneName) : Entity() { this->boneName = boneName; -// boneMesh = new ScenePrimitive(ScenePrimitive::TYPE_BOX, 0.1, 0.1, 0.1); - this->depthTest = false; parentBone = NULL; boneMatrix.identity(); -// addChild(boneMesh); - - boneMesh = new Mesh(Mesh::QUAD_MESH); - boneMesh->createBox(0.2,0.2,0.2); - + disableAnimation = false; } - Bone::~Bone() { } @@ -78,14 +71,48 @@ Matrix4 Bone::getBoneMatrix() const { } } +void Bone::intializeBone(const Vector3 &basePosition, const Vector3 &baseScale, const Quaternion &baseRotation, const Vector3 &restPosition, const Vector3 &restScale, const Quaternion &restRotation) { + + this->baseRotation = baseRotation; + this->baseScale = baseScale; + this->basePosition = basePosition; + + setPosition(basePosition); + setRotationByQuaternion(baseRotation); + setScale(baseScale); + rebuildTransformMatrix(); + + setBaseMatrix(getTransformMatrix()); + setBoneMatrix(getTransformMatrix()); + + Matrix4 restRotationMatrix = restRotation.createMatrix(); + + Matrix4 restPositionMatrix; + restPositionMatrix.identity(); + restPositionMatrix.setPosition(restPosition.x, restPosition.y, restPosition.z); + + Matrix4 restScaleMatrix; + restScaleMatrix.identity(); + restScaleMatrix.setScale(restScale); + + + setRestMatrix(restScaleMatrix*restRotationMatrix*restPositionMatrix); +} + Matrix4 Bone::getFinalMatrix() const { - Matrix4 final = boneMatrix; + return finalMatrix; +} - if(parentBone) { - final = final * parentBone->getFinalMatrix(); - } - - return final; +Matrix4 Bone::buildFinalMatrix() const { + if(parentBone) { + return boneMatrix * parentBone->buildFinalMatrix(); + } else { + return boneMatrix; + } +} + +void Bone::rebuildFinalMatrix() { + finalMatrix = restMatrix * buildFinalMatrix(); } void Bone::setBoneMatrix(const Matrix4& matrix) { @@ -131,25 +158,6 @@ void Bone::setRestMatrix(const Matrix4& matrix) { restMatrix = matrix; } -const String& Bone::getName() const { +String Bone::getName() const { return boneName; } - -void Bone::enableBoneLabel(const String& fontLabel, Number size, Number scale, Color labelColor) { - SceneLabel *label = new SceneLabel(fontLabel, boneName, size, scale, Label::ANTIALIAS_FULL); - label->setColor(labelColor); - label->billboardMode = true; - label->depthTest = false; - addEntity(label); -} - -void Bone::Render() { - - CoreServices::getInstance()->getRenderer()->setTexture(NULL); -// renderer->pushDataArrayForMesh(boneMesh, RenderDataArray::COLOR_DATA_ARRAY); - renderer->pushDataArrayForMesh(boneMesh, RenderDataArray::VERTEX_DATA_ARRAY); - renderer->pushDataArrayForMesh(boneMesh, RenderDataArray::TEXCOORD_DATA_ARRAY); - renderer->pushDataArrayForMesh(boneMesh, RenderDataArray::NORMAL_DATA_ARRAY); - renderer->drawArrays(boneMesh->getMeshType()); - -} diff --git a/Core/Contents/Source/PolyCamera.cpp b/Core/Contents/Source/PolyCamera.cpp index d21e2d08e..38750bcd6 100755 --- a/Core/Contents/Source/PolyCamera.cpp +++ b/Core/Contents/Source/PolyCamera.cpp @@ -33,19 +33,31 @@ using namespace Polycode; -Camera::Camera(Scene *parentScene) : SceneEntity() { +Camera::Camera(Scene *parentScene) : Entity() { + projectionMode = PERSPECTIVE_FOV; + renderer = CoreServices::getInstance()->getRenderer(); setParentScene(parentScene); - orthoMode = false; - fov = 45.0f; + setFOV(45.0f); filterShaderMaterial = NULL; originalSceneTexture = NULL; zBufferSceneTexture = NULL; exposureLevel = 1.0f; _hasFilterShader = false; frustumCulling = true; + nearClipPlane = 1.0; + farClipPlane = 1000.0; + topLeftOrtho = false; + orthoSizeX = 1.0; + orthoSizeY = 1.0; + useGlobalFramebuffer = false; + + Services()->getCore()->addEventListener(this, Core::EVENT_CORE_RESIZE); } -Camera::~Camera() { +Camera::~Camera() { + + Services()->getCore()->removeAllHandlersForListener(this); + for(int i=0; i < localShaderOptions.size(); i++) { delete localShaderOptions[i]; } @@ -54,47 +66,90 @@ Camera::~Camera() { delete zBufferSceneTexture; } -void Camera::setExposureLevel(Number level) { - exposureLevel = level; -} +void Camera::handleEvent(Event *event) { + if(hasFilterShader()) { -Number Camera::getExposureLevel() { - return exposureLevel; + delete originalSceneTexture; + delete zBufferSceneTexture; + + CoreServices::getInstance()->getRenderer()->createRenderTextures(&originalSceneTexture, &zBufferSceneTexture, Services()->getCore()->getXRes(), Services()->getCore()->getYRes(), filterShaderMaterial->fp16RenderTargets); + } } +void Camera::setUseGlobalFramebuffer(bool val) { + useGlobalFramebuffer = val; +} -void Camera::setFOV(Number fov) { - this->fov = fov; +bool Camera::getUseGlobalFramebuffer() const { + return useGlobalFramebuffer; } -Number Camera::getFOV() { - return fov; +void Camera::setClippingPlanes(Number nearClipPlane, Number farClipPlane) { + this->nearClipPlane = nearClipPlane; + this->farClipPlane = farClipPlane; } +void Camera::setFOV(Number fov) { + setProjectionMode(PERSPECTIVE_FOV); + this->fov = fov; +} -bool Camera::isSphereInFrustum(Vector3 pos, Number fRadius) { +bool Camera::isSphereInFrustum(const Vector3 &pos, Number fRadius) { if(!frustumCulling) return true; for( int i = 0; i < 6; ++i ) { - if( frustumPlanes[i][0] * pos.x + - frustumPlanes[i][1] * pos.y + - frustumPlanes[i][2] * pos.z + - frustumPlanes[i][3] <= -fRadius ) + if( frustumPlanes[i].x * pos.x + + frustumPlanes[i].y * pos.y + + frustumPlanes[i].z * pos.z + + frustumPlanes[i].w <= -fRadius ) return false; } return true; } -void Camera::setOrthoMode(bool mode, Number orthoSizeX, Number orthoSizeY) { + +bool Camera::isAABBInFrustum(const AABB &aabb) { + for( int i=0; i < 6; i++) { + int out = 0; + out += (frustumPlanes[i].dot(Vector4(aabb.min.x, aabb.min.y, aabb.min.z, 1.0)) < 0.0) ? 1 : 0; + out += (frustumPlanes[i].dot(Vector4(aabb.max.x, aabb.min.y, aabb.min.z, 1.0)) < 0.0) ? 1 : 0; + out += (frustumPlanes[i].dot(Vector4(aabb.min.x, aabb.max.y, aabb.min.z, 1.0)) < 0.0) ? 1 : 0; + out += (frustumPlanes[i].dot(Vector4(aabb.max.x, aabb.max.y, aabb.min.z, 1.0)) < 0.0) ? 1 : 0; + out += (frustumPlanes[i].dot(Vector4(aabb.min.x, aabb.min.y, aabb.max.z, 1.0)) < 0.0) ? 1 : 0; + out += (frustumPlanes[i].dot(Vector4(aabb.max.x, aabb.min.y, aabb.max.z, 1.0)) < 0.0) ? 1 : 0; + out += (frustumPlanes[i].dot(Vector4(aabb.min.x, aabb.max.y, aabb.max.z, 1.0)) < 0.0) ? 1 : 0; + out += (frustumPlanes[i].dot(Vector4(aabb.max.x, aabb.max.y, aabb.max.z, 1.0)) < 0.0) ? 1 : 0; + if( out==8 ) return false; + } + + return true; +} + +void Camera::setOrthoSize(Number orthoSizeX, Number orthoSizeY) { this->orthoSizeX = orthoSizeX; this->orthoSizeY = orthoSizeY; - orthoMode = mode; +} + +void Camera::setOrthoMode(bool mode) { + if (mode && !getOrthoMode()) { + setProjectionMode(ORTHO_SIZE_LOCK_HEIGHT); + } + else if (!mode && getOrthoMode()) { + setProjectionMode(PERSPECTIVE_FOV); + } } -bool Camera::getOrthoMode() { - return orthoMode; + +void Camera::setFrustumMode(Number left, Number right, Number bottom, Number top, Number front, Number back) { + setProjectionMode(PERSPECTIVE_FRUSTUM); + leftFrustum = left; + rightFrustum = right; + bottomFrustum = bottom; + topFrustum = top; + nearClipPlane = front; + farClipPlane = back; } Number Camera::getOrthoSizeX() { @@ -105,155 +160,183 @@ Number Camera::getOrthoSizeY() { return orthoSizeY; } +unsigned int Camera::getNumLocalShaderOptions()const { + return localShaderOptions.size(); +} + +ShaderBinding* Camera::getLocalShaderOption(unsigned int index) const { + if(index < localShaderOptions.size()) { + return localShaderOptions[index]; + } else { + return NULL; + } +} void Camera::buildFrustumPlanes() { - Matrix4 p; Matrix4 mv; Matrix4 mvp; Number t; - p = CoreServices::getInstance()->getRenderer()->getProjectionMatrix(); - mv = CoreServices::getInstance()->getRenderer()->getModelviewMatrix(); + mv = renderer->getModelviewMatrix(); // // Concatenate the projection matrix and the model-view matrix to produce // a combined model-view-projection matrix. // - mvp.ml[ 0] = mv.ml[ 0] * p.ml[ 0] + mv.ml[ 1] * p.ml[ 4] + mv.ml[ 2] * p.ml[ 8] + mv.ml[ 3] * p.ml[12]; + mvp.ml[ 0] = mv.ml[ 0] * projectionMatrix.ml[ 0] + mv.ml[ 1] * projectionMatrix.ml[ 4] + mv.ml[ 2] * projectionMatrix.ml[ 8] + mv.ml[ 3] * projectionMatrix.ml[12]; - mvp.ml[ 1] = mv.ml[ 0] * p.ml[ 1] + mv.ml[ 1] * p.ml[ 5] + mv.ml[ 2] * p.ml[ 9] + mv.ml[ 3] * p.ml[13]; - mvp.ml[ 2] = mv.ml[ 0] * p.ml[ 2] + mv.ml[ 1] * p.ml[ 6] + mv.ml[ 2] * p.ml[10] + mv.ml[ 3] * p.ml[14]; - mvp.ml[ 3] = mv.ml[ 0] * p.ml[ 3] + mv.ml[ 1] * p.ml[ 7] + mv.ml[ 2] * p.ml[11] + mv.ml[ 3] * p.ml[15]; + mvp.ml[ 1] = mv.ml[ 0] * projectionMatrix.ml[ 1] + mv.ml[ 1] * projectionMatrix.ml[ 5] + mv.ml[ 2] * projectionMatrix.ml[ 9] + mv.ml[ 3] * projectionMatrix.ml[13]; + mvp.ml[ 2] = mv.ml[ 0] * projectionMatrix.ml[ 2] + mv.ml[ 1] * projectionMatrix.ml[ 6] + mv.ml[ 2] * projectionMatrix.ml[10] + mv.ml[ 3] * projectionMatrix.ml[14]; + mvp.ml[ 3] = mv.ml[ 0] * projectionMatrix.ml[ 3] + mv.ml[ 1] * projectionMatrix.ml[ 7] + mv.ml[ 2] * projectionMatrix.ml[11] + mv.ml[ 3] * projectionMatrix.ml[15]; - mvp.ml[ 4] = mv.ml[ 4] * p.ml[ 0] + mv.ml[ 5] * p.ml[ 4] + mv.ml[ 6] * p.ml[ 8] + mv.ml[ 7] * p.ml[12]; - mvp.ml[ 5] = mv.ml[ 4] * p.ml[ 1] + mv.ml[ 5] * p.ml[ 5] + mv.ml[ 6] * p.ml[ 9] + mv.ml[ 7] * p.ml[13]; - mvp.ml[ 6] = mv.ml[ 4] * p.ml[ 2] + mv.ml[ 5] * p.ml[ 6] + mv.ml[ 6] * p.ml[10] + mv.ml[ 7] * p.ml[14]; - mvp.ml[ 7] = mv.ml[ 4] * p.ml[ 3] + mv.ml[ 5] * p.ml[ 7] + mv.ml[ 6] * p.ml[11] + mv.ml[ 7] * p.ml[15]; + mvp.ml[ 4] = mv.ml[ 4] * projectionMatrix.ml[ 0] + mv.ml[ 5] * projectionMatrix.ml[ 4] + mv.ml[ 6] * projectionMatrix.ml[ 8] + mv.ml[ 7] * projectionMatrix.ml[12]; + mvp.ml[ 5] = mv.ml[ 4] * projectionMatrix.ml[ 1] + mv.ml[ 5] * projectionMatrix.ml[ 5] + mv.ml[ 6] * projectionMatrix.ml[ 9] + mv.ml[ 7] * projectionMatrix.ml[13]; + mvp.ml[ 6] = mv.ml[ 4] * projectionMatrix.ml[ 2] + mv.ml[ 5] * projectionMatrix.ml[ 6] + mv.ml[ 6] * projectionMatrix.ml[10] + mv.ml[ 7] * projectionMatrix.ml[14]; + mvp.ml[ 7] = mv.ml[ 4] * projectionMatrix.ml[ 3] + mv.ml[ 5] * projectionMatrix.ml[ 7] + mv.ml[ 6] * projectionMatrix.ml[11] + mv.ml[ 7] * projectionMatrix.ml[15]; - mvp.ml[ 8] = mv.ml[ 8] * p.ml[ 0] + mv.ml[ 9] * p.ml[ 4] + mv.ml[10] * p.ml[ 8] + mv.ml[11] * p.ml[12]; - mvp.ml[ 9] = mv.ml[ 8] * p.ml[ 1] + mv.ml[ 9] * p.ml[ 5] + mv.ml[10] * p.ml[ 9] + mv.ml[11] * p.ml[13]; - mvp.ml[10] = mv.ml[ 8] * p.ml[ 2] + mv.ml[ 9] * p.ml[ 6] + mv.ml[10] * p.ml[10] + mv.ml[11] * p.ml[14]; - mvp.ml[11] = mv.ml[ 8] * p.ml[ 3] + mv.ml[ 9] * p.ml[ 7] + mv.ml[10] * p.ml[11] + mv.ml[11] * p.ml[15]; + mvp.ml[ 8] = mv.ml[ 8] * projectionMatrix.ml[ 0] + mv.ml[ 9] * projectionMatrix.ml[ 4] + mv.ml[10] * projectionMatrix.ml[ 8] + mv.ml[11] * projectionMatrix.ml[12]; + mvp.ml[ 9] = mv.ml[ 8] * projectionMatrix.ml[ 1] + mv.ml[ 9] * projectionMatrix.ml[ 5] + mv.ml[10] * projectionMatrix.ml[ 9] + mv.ml[11] * projectionMatrix.ml[13]; + mvp.ml[10] = mv.ml[ 8] * projectionMatrix.ml[ 2] + mv.ml[ 9] * projectionMatrix.ml[ 6] + mv.ml[10] * projectionMatrix.ml[10] + mv.ml[11] * projectionMatrix.ml[14]; + mvp.ml[11] = mv.ml[ 8] * projectionMatrix.ml[ 3] + mv.ml[ 9] * projectionMatrix.ml[ 7] + mv.ml[10] * projectionMatrix.ml[11] + mv.ml[11] * projectionMatrix.ml[15]; - mvp.ml[12] = mv.ml[12] * p.ml[ 0] + mv.ml[13] * p.ml[ 4] + mv.ml[14] * p.ml[ 8] + mv.ml[15] * p.ml[12]; - mvp.ml[13] = mv.ml[12] * p.ml[ 1] + mv.ml[13] * p.ml[ 5] + mv.ml[14] * p.ml[ 9] + mv.ml[15] * p.ml[13]; - mvp.ml[14] = mv.ml[12] * p.ml[ 2] + mv.ml[13] * p.ml[ 6] + mv.ml[14] * p.ml[10] + mv.ml[15] * p.ml[14]; - mvp.ml[15] = mv.ml[12] * p.ml[ 3] + mv.ml[13] * p.ml[ 7] + mv.ml[14] * p.ml[11] + mv.ml[15] * p.ml[15]; + mvp.ml[12] = mv.ml[12] * projectionMatrix.ml[ 0] + mv.ml[13] * projectionMatrix.ml[ 4] + mv.ml[14] * projectionMatrix.ml[ 8] + mv.ml[15] * projectionMatrix.ml[12]; + mvp.ml[13] = mv.ml[12] * projectionMatrix.ml[ 1] + mv.ml[13] * projectionMatrix.ml[ 5] + mv.ml[14] * projectionMatrix.ml[ 9] + mv.ml[15] * projectionMatrix.ml[13]; + mvp.ml[14] = mv.ml[12] * projectionMatrix.ml[ 2] + mv.ml[13] * projectionMatrix.ml[ 6] + mv.ml[14] * projectionMatrix.ml[10] + mv.ml[15] * projectionMatrix.ml[14]; + mvp.ml[15] = mv.ml[12] * projectionMatrix.ml[ 3] + mv.ml[13] * projectionMatrix.ml[ 7] + mv.ml[14] * projectionMatrix.ml[11] + mv.ml[15] * projectionMatrix.ml[15]; // // Extract the frustum's right clipping plane and normalize it. // - frustumPlanes[0][0] = mvp.ml[ 3] - mvp.ml[ 0]; - frustumPlanes[0][1] = mvp.ml[ 7] - mvp.ml[ 4]; - frustumPlanes[0][2] = mvp.ml[11] - mvp.ml[ 8]; - frustumPlanes[0][3] = mvp.ml[15] - mvp.ml[12]; + frustumPlanes[0].x = mvp.ml[ 3] - mvp.ml[ 0]; + frustumPlanes[0].y = mvp.ml[ 7] - mvp.ml[ 4]; + frustumPlanes[0].z = mvp.ml[11] - mvp.ml[ 8]; + frustumPlanes[0].w = mvp.ml[15] - mvp.ml[12]; - t = (Number) sqrt( frustumPlanes[0][0] * frustumPlanes[0][0] + - frustumPlanes[0][1] * frustumPlanes[0][1] + - frustumPlanes[0][2] * frustumPlanes[0][2] ); + t = (Number) sqrt( frustumPlanes[0].x * frustumPlanes[0].x + + frustumPlanes[0].y * frustumPlanes[0].y + + frustumPlanes[0].z * frustumPlanes[0].z ); - frustumPlanes[0][0] /= t; - frustumPlanes[0][1] /= t; - frustumPlanes[0][2] /= t; - frustumPlanes[0][3] /= t; + frustumPlanes[0].x /= t; + frustumPlanes[0].y /= t; + frustumPlanes[0].z /= t; + frustumPlanes[0].w /= t; // // Extract the frustum's left clipping plane and normalize it. // - frustumPlanes[1][0] = mvp.ml[ 3] + mvp.ml[ 0]; - frustumPlanes[1][1] = mvp.ml[ 7] + mvp.ml[ 4]; - frustumPlanes[1][2] = mvp.ml[11] + mvp.ml[ 8]; - frustumPlanes[1][3] = mvp.ml[15] + mvp.ml[12]; + frustumPlanes[1].x = mvp.ml[ 3] + mvp.ml[ 0]; + frustumPlanes[1].y = mvp.ml[ 7] + mvp.ml[ 4]; + frustumPlanes[1].z = mvp.ml[11] + mvp.ml[ 8]; + frustumPlanes[1].w = mvp.ml[15] + mvp.ml[12]; - t = (Number) sqrt( frustumPlanes[1][0] * frustumPlanes[1][0] + - frustumPlanes[1][1] * frustumPlanes[1][1] + - frustumPlanes[1][2] * frustumPlanes[1][2] ); + t = (Number) sqrt( frustumPlanes[1].x * frustumPlanes[1].x + + frustumPlanes[1].y * frustumPlanes[1].y + + frustumPlanes[1].z * frustumPlanes[1].z ); - frustumPlanes[1][0] /= t; - frustumPlanes[1][1] /= t; - frustumPlanes[1][2] /= t; - frustumPlanes[1][3] /= t; + frustumPlanes[1].x /= t; + frustumPlanes[1].y /= t; + frustumPlanes[1].z /= t; + frustumPlanes[1].w /= t; // // Extract the frustum's bottom clipping plane and normalize it. // - frustumPlanes[2][0] = mvp.ml[ 3] + mvp.ml[ 1]; - frustumPlanes[2][1] = mvp.ml[ 7] + mvp.ml[ 5]; - frustumPlanes[2][2] = mvp.ml[11] + mvp.ml[ 9]; - frustumPlanes[2][3] = mvp.ml[15] + mvp.ml[13]; + frustumPlanes[2].x = mvp.ml[ 3] + mvp.ml[ 1]; + frustumPlanes[2].y = mvp.ml[ 7] + mvp.ml[ 5]; + frustumPlanes[2].z = mvp.ml[11] + mvp.ml[ 9]; + frustumPlanes[2].w = mvp.ml[15] + mvp.ml[13]; - t = (Number) sqrt( frustumPlanes[2][0] * frustumPlanes[2][0] + - frustumPlanes[2][1] * frustumPlanes[2][1] + - frustumPlanes[2][2] * frustumPlanes[2][2] ); + t = (Number) sqrt( frustumPlanes[2].x * frustumPlanes[2].x + + frustumPlanes[2].y * frustumPlanes[2].y + + frustumPlanes[2].z * frustumPlanes[2].z ); - frustumPlanes[2][0] /= t; - frustumPlanes[2][1] /= t; - frustumPlanes[2][2] /= t; - frustumPlanes[2][3] /= t; + frustumPlanes[2].x /= t; + frustumPlanes[2].y /= t; + frustumPlanes[2].z /= t; + frustumPlanes[2].w /= t; // // Extract the frustum's top clipping plane and normalize it. // - frustumPlanes[3][0] = mvp.ml[ 3] - mvp.ml[ 1]; - frustumPlanes[3][1] = mvp.ml[ 7] - mvp.ml[ 5]; - frustumPlanes[3][2] = mvp.ml[11] - mvp.ml[ 9]; - frustumPlanes[3][3] = mvp.ml[15] - mvp.ml[13]; + frustumPlanes[3].x = mvp.ml[ 3] - mvp.ml[ 1]; + frustumPlanes[3].y = mvp.ml[ 7] - mvp.ml[ 5]; + frustumPlanes[3].z = mvp.ml[11] - mvp.ml[ 9]; + frustumPlanes[3].w = mvp.ml[15] - mvp.ml[13]; - t = (Number) sqrt( frustumPlanes[3][0] * frustumPlanes[3][0] + - frustumPlanes[3][1] * frustumPlanes[3][1] + - frustumPlanes[3][2] * frustumPlanes[3][2] ); + t = (Number) sqrt( frustumPlanes[3].x * frustumPlanes[3].x + + frustumPlanes[3].y * frustumPlanes[3].y + + frustumPlanes[3].z * frustumPlanes[3].z ); - frustumPlanes[3][0] /= t; - frustumPlanes[3][1] /= t; - frustumPlanes[3][2] /= t; - frustumPlanes[3][3] /= t; + frustumPlanes[3].x /= t; + frustumPlanes[3].y /= t; + frustumPlanes[3].z /= t; + frustumPlanes[3].w /= t; // // Extract the frustum's far clipping plane and normalize it. // - frustumPlanes[4][0] = mvp.ml[ 3] - mvp.ml[ 2]; - frustumPlanes[4][1] = mvp.ml[ 7] - mvp.ml[ 6]; - frustumPlanes[4][2] = mvp.ml[11] - mvp.ml[10]; - frustumPlanes[4][3] = mvp.ml[15] - mvp.ml[14]; + frustumPlanes[4].x = mvp.ml[ 3] - mvp.ml[ 2]; + frustumPlanes[4].y = mvp.ml[ 7] - mvp.ml[ 6]; + frustumPlanes[4].z = mvp.ml[11] - mvp.ml[10]; + frustumPlanes[4].w = mvp.ml[15] - mvp.ml[14]; - t = (Number) sqrt( frustumPlanes[4][0] * frustumPlanes[4][0] + - frustumPlanes[4][1] * frustumPlanes[4][1] + - frustumPlanes[4][2] * frustumPlanes[4][2] ); + t = (Number) sqrt( frustumPlanes[4].x * frustumPlanes[4].x + + frustumPlanes[4].y * frustumPlanes[4].y + + frustumPlanes[4].z * frustumPlanes[4].z ); - frustumPlanes[4][0] /= t; - frustumPlanes[4][1] /= t; - frustumPlanes[4][2] /= t; - frustumPlanes[4][3] /= t; + frustumPlanes[4].x /= t; + frustumPlanes[4].y /= t; + frustumPlanes[4].z /= t; + frustumPlanes[4].w /= t; // // Extract the frustum's near clipping plane and normalize it. // - frustumPlanes[5][0] = mvp.ml[ 3] + mvp.ml[ 2]; - frustumPlanes[5][1] = mvp.ml[ 7] + mvp.ml[ 6]; - frustumPlanes[5][2] = mvp.ml[11] + mvp.ml[10]; - frustumPlanes[5][3] = mvp.ml[15] + mvp.ml[14]; + frustumPlanes[5].x = mvp.ml[ 3] + mvp.ml[ 2]; + frustumPlanes[5].y = mvp.ml[ 7] + mvp.ml[ 6]; + frustumPlanes[5].z = mvp.ml[11] + mvp.ml[10]; + frustumPlanes[5].w = mvp.ml[15] + mvp.ml[14]; + + t = (Number) sqrt( frustumPlanes[5].x * frustumPlanes[5].x + + frustumPlanes[5].y * frustumPlanes[5].y + + frustumPlanes[5].z * frustumPlanes[5].z ); + + frustumPlanes[5].x /= t; + frustumPlanes[5].y /= t; + frustumPlanes[5].z /= t; + frustumPlanes[5].w /= t; - t = (Number) sqrt( frustumPlanes[5][0] * frustumPlanes[5][0] + - frustumPlanes[5][1] * frustumPlanes[5][1] + - frustumPlanes[5][2] * frustumPlanes[5][2] ); +} - frustumPlanes[5][0] /= t; - frustumPlanes[5][1] /= t; - frustumPlanes[5][2] /= t; - frustumPlanes[5][3] /= t; +Entity *Camera::Clone(bool deepClone, bool ignoreEditorOnly) const { + Camera *newCamera = new Camera(NULL); + applyClone(newCamera, deepClone, ignoreEditorOnly); + return newCamera; +} +void Camera::applyClone(Entity *clone, bool deepClone, bool ignoreEditorOnly) const { + Entity::applyClone(clone, deepClone, ignoreEditorOnly); + + Camera *cloneCamera = (Camera*) clone; + cloneCamera->projectionMatrix = Matrix4(projectionMatrix.ml); + cloneCamera->fov = fov; + cloneCamera->viewport = viewport; + cloneCamera->setOrthoSize(orthoSizeX, orthoSizeY); + cloneCamera->projectionMode = projectionMode; + cloneCamera->setClippingPlanes(nearClipPlane, farClipPlane); + cloneCamera->setExposureLevel(exposureLevel); } -bool Camera::canSee(SceneEntity *entity) { - return isSphereInFrustum(entity->getPosition(), entity->getBBoxRadius()); +Scene *Camera::getParentScene() const { + return parentScene; } void Camera::setParentScene(Scene *parentScene) { @@ -261,7 +344,7 @@ void Camera::setParentScene(Scene *parentScene) { } void Camera::setPostFilterByName(const String& materialName) { - Material *shaderMaterial = (Material*) CoreServices::getInstance()->getResourceManager()->getResource(Resource::RESOURCE_MATERIAL, materialName); + Material *shaderMaterial = (Material*) CoreServices::getInstance()->getResourceManager()->getGlobalPool()->getResource(Resource::RESOURCE_MATERIAL, materialName); if(shaderMaterial) setPostFilter(shaderMaterial); } @@ -281,13 +364,13 @@ void Camera::setPostFilter(Material *shaderMaterial) { this->filterShaderMaterial = shaderMaterial; if(!originalSceneTexture) { - CoreServices::getInstance()->getRenderer()->createRenderTextures(&originalSceneTexture, &zBufferSceneTexture, CoreServices::getInstance()->getCore()->getXRes(), CoreServices::getInstance()->getCore()->getYRes(), shaderMaterial->fp16RenderTargets); + CoreServices::getInstance()->getRenderer()->createRenderTextures(&originalSceneTexture, &zBufferSceneTexture, CoreServices::getInstance()->getCore()->getXRes(), CoreServices::getInstance()->getCore()->getYRes(), shaderMaterial->fp16RenderTargets); } for(int i=0; i < shaderMaterial->getNumShaders(); i++) { ShaderBinding* binding = shaderMaterial->getShader(i)->createBinding(); localShaderOptions.push_back(binding); - binding->addLocalParam("exposure", (void*)&exposureLevel); + binding->addParamPointer(ProgramParam::PARAM_NUMBER, "exposure", (void*)&exposureLevel); } _hasFilterShader = true; @@ -297,98 +380,163 @@ bool Camera::hasFilterShader() { return _hasFilterShader; } -void Camera::setLightDepthTexture(Texture *texture) { - for(int i=0; i < localShaderOptions.size(); i++) { - localShaderOptions[i]->clearTexture("PolyLight0ZBuffer"); - localShaderOptions[i]->addTexture("PolyLight0ZBuffer", texture); - } -} - void Camera::drawFilter(Texture *targetTexture, Number targetTextureWidth, Number targetTextureHeight, Texture *targetColorTexture, Texture *targetZTexture) { if(!filterShaderMaterial) return; - Texture *finalTargetColorTexture; - Texture *finalTargetZTexture; + Texture *finalTargetColorTexture = NULL; + Texture *finalTargetZTexture = NULL; if(targetTexture) { finalTargetColorTexture = targetColorTexture; finalTargetZTexture = targetZTexture; - CoreServices::getInstance()->getRenderer()->setViewportSize(targetTextureWidth, targetTextureHeight); + renderer->setViewportSize(targetTextureWidth, targetTextureHeight); } else { - finalTargetColorTexture = originalSceneTexture; - finalTargetZTexture = zBufferSceneTexture; - CoreServices::getInstance()->getRenderer()->setViewportSize(CoreServices::getInstance()->getRenderer()->getXRes(), CoreServices::getInstance()->getRenderer()->getYRes()); + if(!useGlobalFramebuffer) { + finalTargetColorTexture = originalSceneTexture; + finalTargetZTexture = zBufferSceneTexture; + } + renderer->setViewportSize(renderer->getXRes(), renderer->getYRes()); } - CoreServices::getInstance()->getRenderer()->bindFrameBufferTexture(finalTargetColorTexture); - CoreServices::getInstance()->getRenderer()->bindFrameBufferTextureDepth(finalTargetZTexture); + + if(finalTargetColorTexture) { + renderer->bindFrameBufferTexture(finalTargetColorTexture); + } + if(finalTargetZTexture) { + renderer->bindFrameBufferTextureDepth(finalTargetZTexture); + } parentScene->Render(this); - CoreServices::getInstance()->getRenderer()->unbindFramebuffers(); + + if(finalTargetColorTexture && finalTargetZTexture) { + renderer->unbindFramebuffers(); + } - ShaderBinding* materialBinding; + ShaderBinding* materialBinding; for(int i=0; i < filterShaderMaterial->getNumShaders(); i++) { materialBinding = filterShaderMaterial->getShaderBinding(i); for(int j=0; j < materialBinding->getNumColorTargetBindings(); j++) { RenderTargetBinding *colorBinding = materialBinding->getColorTargetBinding(j); materialBinding->clearTexture(colorBinding->name); - materialBinding->addTexture(colorBinding->name, finalTargetColorTexture); + + if(finalTargetColorTexture) { + materialBinding->addTexture(colorBinding->name, finalTargetColorTexture); + } else { + materialBinding->addTexture(colorBinding->name, renderer->getGlobalColorFramebuffer()); + } } for(int j=0; j < materialBinding->getNumDepthTargetBindings(); j++) { RenderTargetBinding *depthBinding = materialBinding->getDepthTargetBinding(j); materialBinding->clearTexture(depthBinding->name); - materialBinding->addTexture(depthBinding->name, finalTargetZTexture); + if(finalTargetZTexture) { + materialBinding->addTexture(depthBinding->name, finalTargetZTexture); + } else { + materialBinding->addTexture(depthBinding->name, renderer->getGlobalDepthFramebuffer()); + } } - CoreServices::getInstance()->getRenderer()->applyMaterial(filterShaderMaterial, localShaderOptions[i], i); + renderer->applyMaterial(filterShaderMaterial, localShaderOptions[i], i, true); if(i==filterShaderMaterial->getNumShaders()-1) { if(targetTexture) { - CoreServices::getInstance()->getRenderer()->setViewportSize(targetTextureWidth, targetTextureHeight); - CoreServices::getInstance()->getRenderer()->bindFrameBufferTexture(targetTexture); - CoreServices::getInstance()->getRenderer()->clearScreen(); - CoreServices::getInstance()->getRenderer()->loadIdentity(); + renderer->setViewportSize(targetTextureWidth, targetTextureHeight); + renderer->bindFrameBufferTexture(targetTexture); + renderer->clearScreen(); + renderer->loadIdentity(); - CoreServices::getInstance()->getRenderer()->drawScreenQuad(targetTextureWidth, targetTextureHeight); - CoreServices::getInstance()->getRenderer()->unbindFramebuffers(); + renderer->drawScreenQuad(targetTextureWidth, targetTextureHeight); + renderer->unbindFramebuffers(); } else { - CoreServices::getInstance()->getRenderer()->setViewportSize(CoreServices::getInstance()->getRenderer()->getXRes(), CoreServices::getInstance()->getRenderer()->getYRes()); - CoreServices::getInstance()->getRenderer()->clearScreen(); - CoreServices::getInstance()->getRenderer()->loadIdentity(); - CoreServices::getInstance()->getRenderer()->drawScreenQuad(CoreServices::getInstance()->getRenderer()->getXRes(), CoreServices::getInstance()->getRenderer()->getYRes()); + // global framebuffer ONLY used for input + // we must unbind it here. + // this is a bit of a hack, a better system + // would be to define override buffers + if(useGlobalFramebuffer) { + renderer->unbindFramebuffers(); + } + renderer->setViewportSize(renderer->getXRes(), renderer->getYRes()); + renderer->clearScreen(); + renderer->loadIdentity(); + renderer->drawScreenQuad(renderer->getXRes(), renderer->getYRes()); } } else { for(int j=0; j < materialBinding->getNumOutTargetBindings(); j++) { Texture *bindingTexture = materialBinding->getOutTargetBinding(j)->texture; if(bindingTexture) { - CoreServices::getInstance()->getRenderer()->setViewportSize(bindingTexture->getWidth(), bindingTexture->getHeight()); - CoreServices::getInstance()->getRenderer()->bindFrameBufferTexture(bindingTexture); - CoreServices::getInstance()->getRenderer()->drawScreenQuad(bindingTexture->getWidth(), bindingTexture->getHeight()); - CoreServices::getInstance()->getRenderer()->unbindFramebuffers(); + renderer->setViewportSize(bindingTexture->getWidth(), bindingTexture->getHeight()); + renderer->bindFrameBufferTexture(bindingTexture); + renderer->drawScreenQuad(bindingTexture->getWidth(), bindingTexture->getHeight()); + renderer->unbindFramebuffers(); } } } - CoreServices::getInstance()->getRenderer()->clearShader(); - CoreServices::getInstance()->getRenderer()->loadIdentity(); + renderer->clearShader(); + renderer->loadIdentity(); } } -Matrix4 Camera::getProjectionMatrix() const { +Matrix4 Camera::getProjectionMatrix() { return projectionMatrix; } +Polycode::Rectangle Camera::getViewport() { + return viewport; +} + +Number Camera::getNearClippingPlane() { + return nearClipPlane; +} + +Number Camera::getFarClippingPlane() { + return farClipPlane; +} + +void Camera::setProjectionMode(int mode) { + projectionMode = mode; +} + +void Camera::setProjectionMatrix(Matrix4 matrix) { + projectionMatrix = matrix; +} + void Camera::doCameraTransform() { - Renderer *renderer = CoreServices::getInstance()->getRenderer(); - if(!orthoMode) { - renderer->setViewportShift(cameraShift.x, cameraShift.y); - renderer->setFOV(fov); - } - renderer->setExposureLevel(exposureLevel); - - projectionMatrix = renderer->getProjectionMatrix(); + viewport = renderer->getViewport(); + + switch (projectionMode) { + case PERSPECTIVE_FOV: + renderer->setViewportShift(cameraShift.x, cameraShift.y); + renderer->setProjectionFromFoV(fov, nearClipPlane, farClipPlane); + renderer->setPerspectiveDefaults(); + break; + case PERSPECTIVE_FRUSTUM: + renderer->setProjectionFromFrustum(leftFrustum, rightFrustum, bottomFrustum, topFrustum, nearClipPlane, farClipPlane); + renderer->setPerspectiveDefaults(); + break; + case ORTHO_SIZE_MANUAL: + renderer->setProjectionOrtho(orthoSizeX, orthoSizeY, nearClipPlane, farClipPlane, !topLeftOrtho); + break; + case ORTHO_SIZE_LOCK_HEIGHT: + renderer->setProjectionOrtho(orthoSizeY * (viewport.w/viewport.h), orthoSizeY, nearClipPlane, farClipPlane, !topLeftOrtho); + break; + case ORTHO_SIZE_LOCK_WIDTH: + renderer->setProjectionOrtho(orthoSizeX, orthoSizeX * (viewport.h/viewport.w), nearClipPlane, farClipPlane, !topLeftOrtho); + break; + case ORTHO_SIZE_VIEWPORT: + renderer->setProjectionOrtho(viewport.w / renderer->getBackingResolutionScaleX(), viewport.h / renderer->getBackingResolutionScaleY(), !topLeftOrtho); + break; + case MANUAL_MATRIX: + renderer->setProjectionMatrix(projectionMatrix); + break; + } + renderer->setExposureLevel(exposureLevel); + + if(projectionMode != MANUAL_MATRIX) { + projectionMatrix = renderer->getProjectionMatrix(); + } + if(matrixDirty) { rebuildTransformMatrix(); } @@ -396,5 +544,5 @@ void Camera::doCameraTransform() { Matrix4 camMatrix = getConcatenatedMatrix(); renderer->setCameraMatrix(camMatrix); camMatrix = camMatrix.Inverse(); - renderer->multModelviewMatrix(camMatrix); + renderer->multModelviewMatrix(camMatrix); } diff --git a/Core/Contents/Source/PolyClient.cpp b/Core/Contents/Source/PolyClient.cpp index 8782e9fe2..82ded116f 100755 --- a/Core/Contents/Source/PolyClient.cpp +++ b/Core/Contents/Source/PolyClient.cpp @@ -63,7 +63,7 @@ void Client::handlePacket(Packet *packet, PeerConnection *connection) { case PACKET_TYPE_SETCLIENT_ID: { clientID = (unsigned short)*packet->data; ClientEvent *newEvent = new ClientEvent(); - sendReliableData(serverAddress, (char*)&clientID, sizeof(unsigned short), PACKET_TYPE_CLIENT_READY); + sendReliableData(serverAddress, (char*)&clientID, sizeof(unsigned short), PACKET_TYPE_CLIENT_READY); dispatchEvent(newEvent, ClientEvent::EVENT_CLIENT_READY); } break; case PACKET_TYPE_DISONNECT: diff --git a/Core/Contents/Source/PolyCocoaCore.mm b/Core/Contents/Source/PolyCocoaCore.mm index 1816e2bf5..c5a1e8441 100644 --- a/Core/Contents/Source/PolyCocoaCore.mm +++ b/Core/Contents/Source/PolyCocoaCore.mm @@ -82,8 +82,10 @@ long getThreadID() { CGDisplayModeRelease(mode); } -CocoaCore::CocoaCore(PolycodeView *view, int _xRes, int _yRes, bool fullScreen, bool vSync, int aaLevel, int anisotropyLevel, int frameRate, int monitorIndex) : Core(_xRes, _yRes, fullScreen, vSync, aaLevel, anisotropyLevel, frameRate, monitorIndex) { +CocoaCore::CocoaCore(PolycodeView *view, int _xRes, int _yRes, bool fullScreen, bool vSync, int aaLevel, int anisotropyLevel, int frameRate, int monitorIndex, bool retinaSupport) : Core(_xRes, _yRes, fullScreen, vSync, aaLevel, anisotropyLevel, frameRate, monitorIndex) { + this->retinaSupport = retinaSupport; + hidManager = NULL; initGamepad(); this->fullScreen = false; @@ -100,7 +102,7 @@ long getThreadID() { [view setCore:this]; glView = view; - + context = nil; initTime = mach_absolute_time(); @@ -138,7 +140,8 @@ long getThreadID() { return [retString UTF8String]; } -void CocoaCore::setVideoMode(int xRes, int yRes, bool fullScreen, bool vSync, int aaLevel, int anisotropyLevel) { +void CocoaCore::setVideoMode(int xRes, int yRes, bool fullScreen, bool vSync, int aaLevel, int anisotropyLevel, bool retinaSupport) { + this->retinaSupport = retinaSupport; // hack to make sure there are no window race conditions modeChangeInfo.needResolutionChange = true; modeChangeInfo.xRes = xRes; @@ -149,11 +152,40 @@ long getThreadID() { modeChangeInfo.anisotropyLevel = anisotropyLevel; } +Number CocoaCore::getBackingXRes() { + if(!retinaSupport) { + return getXRes(); + } + NSRect backingBounds = [glView convertRectToBacking:[glView bounds]]; + return backingBounds.size.width; +} + +Number CocoaCore::getBackingYRes() { + if(!retinaSupport) { + return getYRes(); + } + NSRect backingBounds = [glView convertRectToBacking:[glView bounds]]; + return backingBounds.size.height; +} + void CocoaCore::_setVideoMode(int xRes, int yRes, bool fullScreen, bool vSync, int aaLevel, int anisotropyLevel) { this->xRes = xRes; this->yRes = yRes; - - bool _wasFullscreen = this->fullScreen; + + NSRect backingBounds; + if(retinaSupport) { + [glView setWantsBestResolutionOpenGLSurface:YES]; + backingBounds = [glView convertRectToBacking: NSMakeRect(0, 0, xRes, yRes)]; + renderer->setBackingResolutionScale(backingBounds.size.width/xRes, backingBounds.size.height/yRes); + } else { + [glView setWantsBestResolutionOpenGLSurface:NO]; + backingBounds.size.width = xRes; + backingBounds.size.height = yRes; + renderer->setBackingResolutionScale(1.0, 1.0); + + } + + bool _wasFullscreen = this->fullScreen; this->fullScreen = fullScreen; this->aaLevel = aaLevel; @@ -200,7 +232,6 @@ long getThreadID() { renderer->setAnisotropyAmount(anisotropyLevel); dispatchEvent(new Event(), EVENT_CORE_RESIZE); - [[glView window] setContentSize: NSMakeSize(xRes, yRes)]; if(fullScreen) { if(monitorIndex > -1) { @@ -232,13 +263,15 @@ long getThreadID() { CGLContextObj ctx = (CGLContextObj) [context CGLContextObj]; if(fullScreen) { - GLint dim[2] = {xRes, yRes}; + GLint dim[2] = {backingBounds.size.width, backingBounds.size.height}; CGLSetParameter(ctx, kCGLCPSurfaceBackingSize, dim); CGLEnable (ctx, kCGLCESurfaceBackingSize); } else { CGLDisable(ctx, kCGLCESurfaceBackingSize); } - renderer->Resize(xRes, yRes); + + renderer->Resize(xRes, yRes); + [[glView window] setContentSize: NSMakeSize(xRes, yRes)]; if(aaLevel > 0) { glEnable( GL_MULTISAMPLE_ARB ); @@ -476,7 +509,16 @@ long getThreadID() { break; case InputEvent::EVENT_KEYUP: input->setKeyState(event.keyCode, event.unicodeChar, false, getTicks()); - break; + break; + case InputEvent::EVENT_TOUCHES_BEGAN: + input->touchesBegan(event.touch, event.touches, getTicks()); + break; + case InputEvent::EVENT_TOUCHES_ENDED: + input->touchesEnded(event.touch, event.touches, getTicks()); + break; + case InputEvent::EVENT_TOUCHES_MOVED: + input->touchesMoved(event.touch, event.touches, getTicks()); + break; } break; case CocoaEvent::FOCUS_EVENT: @@ -538,6 +580,36 @@ long getThreadID() { } } +String CocoaCore::saveFilePicker(std::vector extensions) { + unlockMutex(eventMutex); + String retString; + NSSavePanel *attachmentPanel = [NSSavePanel savePanel]; + + [attachmentPanel setCanCreateDirectories: YES]; + + NSMutableArray *types = nil; + + if(extensions.size() > 0) { + types = [[NSMutableArray alloc] init]; + for(int i=0; i < extensions.size(); i++) { + CoreFileExtension extInfo = extensions[i]; + [types addObject: [NSString stringWithUTF8String: extInfo.extension.c_str()]]; + } + } + [attachmentPanel setAllowedFileTypes:types]; + + if ( [attachmentPanel runModal] == NSOKButton ) + { + NSURL* url = [attachmentPanel URL]; + if(url) { + NSString* fileName = [url path]; + retString = [fileName UTF8String]; + } + } + + return retString; +} + vector CocoaCore::openFilePicker(vector extensions, bool allowMultiple) { unlockMutex(eventMutex); vector retVector; @@ -576,8 +648,7 @@ long getThreadID() { } void CocoaCore::Render() { - lockMutex(CoreServices::getRenderMutex()); - checkEvents(); + lockMutex(CoreServices::getRenderMutex()); if(!paused) { renderer->BeginRender(); @@ -593,7 +664,7 @@ long getThreadID() { unlockMutex(CoreServices::getRenderMutex()); } -bool CocoaCore::Update() { +bool CocoaCore::systemUpdate() { if(!running) return false; doSleep(); @@ -603,12 +674,11 @@ long getThreadID() { modeChangeInfo.needResolutionChange = false; } - updateCore(); + updateCore(); + checkEvents(); return running; } - - static void hatValueToXY(CFIndex value, CFIndex range, int * outX, int * outY) { if (value == range) { *outX = *outY = 0; @@ -748,6 +818,9 @@ static void onDeviceMatched(void * context, IOReturn result, void * sender, IOHI GamepadDeviceEntry *entry = new GamepadDeviceEntry(); entry->device = device; entry->input = core->getInput(); + entry->numButtons = 0; + entry->numAxes = 0; + entry->deviceID = core->nextDeviceID++; core->gamepads.push_back(entry); core->getInput()->addJoystick(entry->deviceID); @@ -803,6 +876,13 @@ static void onDeviceRemoved(void * context, IOReturn result, void * sender, IOHI void CocoaCore::shutdownGamepad() { if (hidManager != NULL) { + + for (int i = 0; i < gamepads.size(); i++) { + IOHIDDeviceRegisterInputValueCallback(gamepads[i]->device, NULL, NULL); + delete gamepads[i]; + } + + IOHIDManagerRegisterDeviceMatchingCallback(hidManager, NULL, NULL); IOHIDManagerRegisterDeviceRemovalCallback(hidManager, NULL, NULL); @@ -810,11 +890,7 @@ static void onDeviceRemoved(void * context, IOReturn result, void * sender, IOHI IOHIDManagerClose(hidManager, 0); CFRelease(hidManager); hidManager = NULL; - - for (int i = 0; i < gamepads.size(); i++) { - IOHIDDeviceRegisterInputValueCallback(gamepads[i]->device, NULL, NULL); - delete gamepads[i]; - } + } } diff --git a/Core/Contents/Source/PolyColor.cpp b/Core/Contents/Source/PolyColor.cpp index 6ae0d8776..2bb4b977c 100755 --- a/Core/Contents/Source/PolyColor.cpp +++ b/Core/Contents/Source/PolyColor.cpp @@ -29,7 +29,11 @@ Color::Color() : r(1),g(1),b(1),a(1){ } -Color::Color(Number r,Number g, Number b, Number a) { +Color::Color(float r,float g, float b, float a) { + setColor(r,g,b,a); +} + +Color::Color(double r,double g, double b, double a) { setColor(r,g,b,a); } diff --git a/Core/Contents/Source/PolyConfig.cpp b/Core/Contents/Source/PolyConfig.cpp index 7966bfe69..92e1223f3 100755 --- a/Core/Contents/Source/PolyConfig.cpp +++ b/Core/Contents/Source/PolyConfig.cpp @@ -112,7 +112,6 @@ void Config::setNumericValue(const String& configNamespace, const String& key, N getEntry(configNamespace, key)->isString = false; } - Number Config::getNumericValue(const String& configNamespace, const String& key) { return getEntry(configNamespace, key)->numVal; } @@ -121,4 +120,16 @@ const String& Config::getStringValue(const String& configNamespace, const String return getEntry(configNamespace, key)->stringVal; } - +void Config::setBoolValue(const String& configNamespace, const String& key, bool value) { + getEntry(configNamespace, key)->stringVal = (!value ? "false" : "true"); + getEntry(configNamespace, key)->isString = true; +} + +bool Config::getBoolValue(const String& configNamespace, const String& key) { + const String& str = getEntry(configNamespace, key)->stringVal; + + if (str == "true" || str == "1") { + return true; + } + return false; +} diff --git a/Core/Contents/Source/PolyCore.cpp b/Core/Contents/Source/PolyCore.cpp index 3d3fa8c0a..f6f18d564 100755 --- a/Core/Contents/Source/PolyCore.cpp +++ b/Core/Contents/Source/PolyCore.cpp @@ -60,6 +60,7 @@ namespace Polycode { input = new CoreInput(); services->setCore(this); fps = 0; + timeLeftOver = 0.0; running = true; frames = 0; lastFrameTicks=0; @@ -80,12 +81,26 @@ namespace Polycode { if(frameRate == 0) frameRate = 60; - refreshInterval = 1000 / frameRate; + setFramerate(frameRate); threadedEventMutex = NULL; } + + int Core::getScreenWidth() { + int width, height, hz; + getScreenInfo(&width, &height, &hz); + return width; + } + + int Core::getScreenHeight() { + int width, height, hz; + getScreenInfo(&width, &height, &hz); + return height; + } - void Core::setFramerate(int frameRate) { + void Core::setFramerate(int frameRate, int maxFixedCycles) { refreshInterval = 1000 / frameRate; + fixedTimestep = 1.0 / ((double) frameRate); + maxFixedElapsed = fixedTimestep * maxFixedCycles; } void Core::enableMouse(bool newval) { @@ -111,6 +126,7 @@ namespace Polycode { Core::~Core() { printf("Shutting down core"); delete services; + delete input; } void Core::Shutdown() { @@ -129,8 +145,8 @@ namespace Polycode { return ((Number)elapsed)/1000.0f; } - Number Core::getTicksFloat() { - return ((Number)getTicks())/1000.0f; + double Core::getTicksFloat() { + return getTicks()/1000.0; } void Core::createThread(Threaded *target) { @@ -184,15 +200,46 @@ namespace Polycode { Render(); return ret; } + + bool Core::fixedUpdate() { + if(fixedElapsed < fixedTimestep) { + return false; + } + services->fixedUpdate(); + fixedElapsed -= fixedTimestep; + return true; + } + + Number Core::getFixedTimestep() { + return fixedTimestep; + } + + bool Core::Update() { + bool ret = systemUpdate(); + while(fixedUpdate()) {} + return ret; + } void Core::updateCore() { frames++; frameTicks = getTicks(); elapsed = frameTicks - lastFrameTicks; - + if(elapsed > 1000) elapsed = 1000; - + + if(fixedElapsed > 0) { + timeLeftOver = fixedElapsed; + } else { + timeLeftOver = 0; + } + + fixedElapsed = (((Number)elapsed)/1000.0f) + timeLeftOver; + + if(fixedElapsed > maxFixedElapsed) { + fixedElapsed = maxFixedElapsed; + } + services->Update(elapsed); if(frameTicks-lastFPSTicks >= 1000) { @@ -228,13 +275,16 @@ namespace Polycode { void Core::doSleep() { unsigned int ticks = getTicks(); unsigned int ticksSinceLastFrame = ticks - lastSleepFrameTicks; - if(ticksSinceLastFrame <= refreshInterval) + int sleepTimeMs = refreshInterval - ticksSinceLastFrame; + if(sleepTimeMs > 0) { #ifdef _WINDOWS - Sleep((refreshInterval - ticksSinceLastFrame)); + Sleep(sleepTimeMs); #else - usleep((refreshInterval - ticksSinceLastFrame) * 1000); + usleep(sleepTimeMs * 1000); #endif + } lastSleepFrameTicks = getTicks(); + timeSleptMs = lastSleepFrameTicks - ticks; } diff --git a/Core/Contents/Source/PolyCoreInput.cpp b/Core/Contents/Source/PolyCoreInput.cpp index 04c1bfdab..c592bf3d3 100755 --- a/Core/Contents/Source/PolyCoreInput.cpp +++ b/Core/Contents/Source/PolyCoreInput.cpp @@ -22,6 +22,8 @@ #include "PolyCoreInput.h" #include "PolyInputEvent.h" +#include "PolyCoreServices.h" +#include "PolyCore.h" namespace Polycode { @@ -38,6 +40,10 @@ namespace Polycode { CoreInput::CoreInput() : EventDispatcher() { clearInput(); simulateTouchWithMouse = false; + simulateTouchAsPen = false; + simulateMouseWithTouch = false; + ignoreOffScreenTouch = false; + keyRepeat = true; } void CoreInput::clearInput() { @@ -58,6 +64,9 @@ namespace Polycode { } JoystickInfo *CoreInput::getJoystickInfoByIndex(unsigned int index) { + if(index > joysticks.size()-1 || joysticks.size() == 0) { + return NULL; + } return &joysticks[index]; } @@ -163,10 +172,13 @@ namespace Polycode { dispatchEvent(evt, InputEvent::EVENT_MOUSEUP); mouseButtons[mouseButton] = state; - if(simulateTouchWithMouse && mouseButton == MOUSE_BUTTON1) { + if(simulateTouchWithMouse) { TouchInfo touch; touch.position = mousePosition; - touch.id = 0; + touch.id = 0; + if (simulateTouchAsPen){ + touch.type = TouchInfo::TYPE_PEN; + } std::vector touches; touches.push_back(touch); @@ -194,15 +206,20 @@ namespace Polycode { InputEvent *evt = new InputEvent(mousePosition, ticks); dispatchEvent(evt, InputEvent::EVENT_MOUSEMOVE); - if(simulateTouchWithMouse && mouseButtons[MOUSE_BUTTON1]) { + if(simulateTouchWithMouse) { TouchInfo touch; touch.position = mousePosition; touch.id = 0; + if (simulateTouchAsPen){ + touch.type = TouchInfo::TYPE_PEN; + } std::vector touches; + touches.push_back(touch); - - touchesMoved(touch, touches, ticks); - } + if(mouseButtons[MOUSE_BUTTON1]) { + touchesMoved(touch, touches, ticks); + } + } } Vector2 CoreInput::getMouseDelta() { @@ -226,6 +243,13 @@ namespace Polycode { } void CoreInput::setKeyState(PolyKEY keyCode, wchar_t code, bool newState, int ticks) { + + if(newState && !keyRepeat) { + if(keyboardState[keyCode]) { + return; + } + } + InputEvent *evt = new InputEvent(keyCode, code, ticks); if(keyCode < 512) keyboardState[keyCode] = newState; @@ -237,27 +261,62 @@ namespace Polycode { } void CoreInput::touchesBegan(TouchInfo touch, std::vector touches, int ticks) { + if(ignoreOffScreenTouch) { + Core *core = CoreServices::getInstance()->getCore(); + if(touch.position.x < 0 || touch.position.x > core->getXRes() || touch.position.y < 0 || touch.position.y > core->getYRes()) { + return; + } + } InputEvent *evt = new InputEvent(); evt->touch = touch; evt->touches = touches; evt->timestamp = ticks; dispatchEvent(evt, InputEvent::EVENT_TOUCHES_BEGAN); + if(simulateMouseWithTouch) { + mousePosition = touch.position; + setMouseButtonState(MOUSE_BUTTON1, true, ticks); + } } void CoreInput::touchesMoved(TouchInfo touch, std::vector touches, int ticks) { + if(ignoreOffScreenTouch) { + Core *core = CoreServices::getInstance()->getCore(); + if(touch.position.x < 0 || touch.position.x > core->getXRes() || touch.position.y < 0 || touch.position.y > core->getYRes()) { + return; + } + } InputEvent *evt = new InputEvent(); evt->touch = touch; evt->touches = touches; evt->timestamp = ticks; - dispatchEvent(evt, InputEvent::EVENT_TOUCHES_MOVED); + dispatchEvent(evt, InputEvent::EVENT_TOUCHES_MOVED); + if(simulateMouseWithTouch) { + setMousePosition(touch.position.x, touch.position.y, ticks); + } } + + + + + + void CoreInput::touchesEnded(TouchInfo touch, std::vector touches, int ticks) { + if(ignoreOffScreenTouch) { + Core *core = CoreServices::getInstance()->getCore(); + if(touch.position.x < 0 || touch.position.x > core->getXRes() || touch.position.y < 0 || touch.position.y > core->getYRes()) { + return; + } + } InputEvent *evt = new InputEvent(); evt->touch = touch; evt->touches = touches; - evt->timestamp = ticks; - dispatchEvent(evt, InputEvent::EVENT_TOUCHES_ENDED); + evt->timestamp = ticks; + dispatchEvent(evt, InputEvent::EVENT_TOUCHES_ENDED); + if(simulateMouseWithTouch) { + mousePosition = touch.position; + setMouseButtonState(MOUSE_BUTTON1, false, ticks); + } } } diff --git a/Core/Contents/Source/PolyCoreServices.cpp b/Core/Contents/Source/PolyCoreServices.cpp index 4d4da4b49..455b01ffd 100755 --- a/Core/Contents/Source/PolyCoreServices.cpp +++ b/Core/Contents/Source/PolyCoreServices.cpp @@ -31,7 +31,6 @@ #include "PolyRenderer.h" #include "PolyConfig.h" #include "PolyFontManager.h" -#include "PolyScreenManager.h" #include "PolySceneManager.h" #include "PolyTimerManager.h" #include "PolyTweenManager.h" @@ -43,6 +42,10 @@ std::map CoreServices::instanceMap; CoreMutex *CoreServices::renderMutex = 0; CoreServices* CoreServices::overrideInstance = NULL; +CoreServices *Polycode::Services() { + return CoreServices::getInstance(); +} + CoreMutex *CoreServices::getRenderMutex() { if(renderMutex == NULL) { Logger::log("Creating render mutex...\n"); @@ -53,7 +56,7 @@ CoreMutex *CoreServices::getRenderMutex() { void CoreServices::setInstance(CoreServices *_instance) { overrideInstance = _instance; - Logger::log("Overriding core instance...\n"); + Logger::log("Overriding core instance to %d...\n", _instance); } CoreServices* CoreServices::getInstance() { @@ -108,8 +111,6 @@ void CoreServices::installModule(PolycodeModule *module) { switch(module->getType()) { case PolycodeModule::TYPE_SHADER: -// renderer->addShaderModule((ShaderModule*)module); - resourceManager->addShaderModule((PolycodeShaderModule*) module); materialManager->addShaderModule((PolycodeShaderModule*) module); renderer->addShaderModule((PolycodeShaderModule*) module); break; @@ -121,92 +122,55 @@ void CoreServices::setupBasicListeners() { } CoreServices::CoreServices() : EventDispatcher() { - logger = new Logger(); + logger = Logger::getInstance(); resourceManager = new ResourceManager(); config = new Config(); - materialManager = new MaterialManager(); - screenManager = new ScreenManager(); - addEventListener(screenManager, InputEvent::EVENT_MOUSEDOWN); - addEventListener(screenManager, InputEvent::EVENT_MOUSEMOVE); - addEventListener(screenManager, InputEvent::EVENT_MOUSEUP); - addEventListener(screenManager, InputEvent::EVENT_MOUSEWHEEL_UP); - addEventListener(screenManager, InputEvent::EVENT_MOUSEWHEEL_DOWN); - addEventListener(screenManager, InputEvent::EVENT_KEYDOWN); - addEventListener(screenManager, InputEvent::EVENT_KEYUP); - addEventListener(screenManager, InputEvent::EVENT_TOUCHES_BEGAN); - addEventListener(screenManager, InputEvent::EVENT_TOUCHES_ENDED); - addEventListener(screenManager, InputEvent::EVENT_TOUCHES_MOVED); + materialManager = new MaterialManager(); sceneManager = new SceneManager(); timerManager = new TimerManager(); tweenManager = new TweenManager(); soundManager = new SoundManager(); fontManager = new FontManager(); - - focusedChild = NULL; } CoreServices::~CoreServices() { delete materialManager; - delete screenManager; delete sceneManager; delete timerManager; delete tweenManager; delete resourceManager; delete soundManager; delete fontManager; + delete logger; + delete config; + delete renderer; + + for(std::size_t i = 0; i < modules.size(); i++) { + delete modules[i]; + } + instanceMap.clear(); overrideInstance = NULL; - } void CoreServices::setCore(Core *core) { this->core = core; - core->getInput()->addEventListener(this, InputEvent::EVENT_MOUSEDOWN); - core->getInput()->addEventListener(this, InputEvent::EVENT_MOUSEMOVE); - core->getInput()->addEventListener(this, InputEvent::EVENT_MOUSEUP); - core->getInput()->addEventListener(this, InputEvent::EVENT_MOUSEWHEEL_DOWN); - core->getInput()->addEventListener(this, InputEvent::EVENT_MOUSEWHEEL_UP); - core->getInput()->addEventListener(this, InputEvent::EVENT_KEYDOWN); - core->getInput()->addEventListener(this, InputEvent::EVENT_KEYUP); - core->getInput()->addEventListener(this, InputEvent::EVENT_TOUCHES_BEGAN); - core->getInput()->addEventListener(this, InputEvent::EVENT_TOUCHES_ENDED); - core->getInput()->addEventListener(this, InputEvent::EVENT_TOUCHES_MOVED); } void CoreServices::handleEvent(Event *event) { - if(event->getDispatcher() == core->getInput()) { - InputEvent *inputEvent = (InputEvent*)event; - switch(event->getEventCode()) { - case InputEvent::EVENT_KEYDOWN: - case InputEvent::EVENT_KEYUP: - dispatchEvent(new InputEvent(inputEvent->key, inputEvent->charCode, inputEvent->timestamp), inputEvent->getEventCode()); - break; - case InputEvent::EVENT_TOUCHES_BEGAN: - case InputEvent::EVENT_TOUCHES_ENDED: - case InputEvent::EVENT_TOUCHES_MOVED: - { - InputEvent *event = new InputEvent(); - event->touches = inputEvent->touches; - event->timestamp = inputEvent->timestamp; - dispatchEvent(event, inputEvent->getEventCode()); - } - break; - default: - InputEvent *_inputEvent = new InputEvent(inputEvent->mousePosition, inputEvent->timestamp); - _inputEvent->mouseButton = inputEvent->mouseButton; - dispatchEvent(_inputEvent, inputEvent->getEventCode()); - break; - } - } } Core *CoreServices::getCore() { return core; } +CoreInput *CoreServices::getInput() { + return core->getInput(); +} void CoreServices::setRenderer(Renderer *renderer) { this->renderer = renderer; + sceneManager->setRenderer(renderer); } Renderer *CoreServices::getRenderer() { @@ -217,21 +181,14 @@ void CoreServices::Render() { if(renderer->doClearBuffer) renderer->clearScreen(); - renderer->setPerspectiveMode(); + renderer->setPerspectiveDefaults(); sceneManager->renderVirtual(); - if(renderer->doClearBuffer) - renderer->clearScreen(); - - if(screenManager->drawScreensFirst) { - renderer->clearLights(); - screenManager->Render(); - renderer->setPerspectiveMode(); - sceneManager->Render(); - } else { - sceneManager->Render(); - renderer->clearLights(); - screenManager->Render(); - } + sceneManager->Render(); + renderer->clearLights(); +} + +void CoreServices::fixedUpdate() { + sceneManager->fixedUpdate(); } void CoreServices::Update(int elapsed) { @@ -241,20 +198,15 @@ void CoreServices::Update(int elapsed) { } resourceManager->Update(elapsed); timerManager->Update(); - tweenManager->Update(); - materialManager->Update(elapsed); + tweenManager->Update(elapsed); sceneManager->Update(); - screenManager->Update(); + soundManager->Update(); } SoundManager *CoreServices::getSoundManager() { return soundManager; } -ScreenManager *CoreServices::getScreenManager() { - return screenManager; -} - SceneManager *CoreServices::getSceneManager() { return sceneManager; } diff --git a/Core/Contents/Source/PolyData.cpp b/Core/Contents/Source/PolyData.cpp index 6af7280c6..0ac2c87d1 100644 --- a/Core/Contents/Source/PolyData.cpp +++ b/Core/Contents/Source/PolyData.cpp @@ -23,6 +23,7 @@ #include "PolyData.h" #include "OSBasics.h" #include +#include using namespace Polycode; diff --git a/Core/Contents/Source/PolyEntity.cpp b/Core/Contents/Source/PolyEntity.cpp index 79e482003..b88f6ad3e 100755 --- a/Core/Contents/Source/PolyEntity.cpp +++ b/Core/Contents/Source/PolyEntity.cpp @@ -21,9 +21,14 @@ */ #include "PolyEntity.h" #include "PolyRenderer.h" +#include "PolyCoreServices.h" +#include "PolyInputEvent.h" using namespace Polycode; + +int Entity::defaultBlendingMode = Renderer::BLEND_MODE_NORMAL; + Rotation::Rotation() { pitch = 0; yaw = 0; @@ -31,36 +36,49 @@ Rotation::Rotation() { } Entity::Entity() : EventDispatcher() { + initEntity(); +} + +Entity::Entity(Number width, Number height, Number depth) : EventDispatcher() { + initEntity(); + bBox.x = width; + bBox.y = height; + bBox.z = depth; +} + +void Entity::initEntity() { userData = NULL; scale.set(1,1,1); renderer = NULL; enabled = true; depthTest = true; visible = true; - bBoxRadius = 0; color.setColor(1.0f,1.0f,1.0f,1.0f); parentEntity = NULL; matrixDirty = true; - matrixAdj = 1.0f; billboardMode = false; billboardRoll = false; billboardIgnoreScale = false; - backfaceCulled = true; depthOnly = false; depthWrite = true; ignoreParentMatrix = false; - alphaTest = false; - blendingMode = Renderer::BLEND_MODE_NORMAL; + blendingMode = Entity::defaultBlendingMode; lockMatrix = false; - renderWireframe = false; colorAffectsChildren = true; visibilityAffectsChildren = true; ownsChildren = false; enableScissor = false; - + processInputEvents = false; + blockMouseInput = false; editorOnly = false; - + snapToPixels = false; tags = NULL; + bBox.z = 0.001; + mouseOver = false; + yAdjust = 1.0; + lastClickTicks = 0.0; + rendererVis = true; + layerID = 0; } Entity *Entity::getEntityById(String id, bool recursive) const { @@ -87,16 +105,12 @@ Entity *Entity::Clone(bool deepClone, bool ignoreEditorOnly) const { void Entity::applyClone(Entity *clone, bool deepClone, bool ignoreEditorOnly) const { clone->ownsChildren = ownsChildren; - clone->position = position; - clone->rotation = rotation; - clone->scale = scale; + clone->setPosition(position); + clone->setRotationByQuaternion(rotationQuat); + clone->setScale(scale); clone->color = color; - clone->custEntityType = custEntityType; clone->billboardMode = billboardMode; clone->billboardRoll = billboardRoll; - clone->alphaTest = alphaTest; - clone->backfaceCulled = backfaceCulled; - clone->renderWireframe = renderWireframe; clone->depthWrite = depthWrite; clone->depthTest = depthTest; clone->blendingMode = blendingMode; @@ -109,7 +123,11 @@ void Entity::applyClone(Entity *clone, bool deepClone, bool ignoreEditorOnly) co clone->ignoreParentMatrix = ignoreParentMatrix; clone->enableScissor = enableScissor; clone->scissorBox = scissorBox; - clone->editorOnly = editorOnly; + clone->editorOnly = editorOnly; + clone->snapToPixels = snapToPixels; + clone->setAnchorPoint(anchorPoint); + clone->layerID = layerID; + clone->id = id; if(tags == NULL) { clone->tags = NULL; @@ -139,6 +157,23 @@ void Entity::setOwnsChildrenRecursive(bool val) { } } +std::vector Entity::getEntitiesByLayerID(unsigned char layerID, bool recursive) const { + std::vector retVector; + + for(int i=0;ilayerID == layerID) { + retVector.push_back(children[i]); + } + + if(recursive) { + std::vector childVector = children[i]->getEntitiesByLayerID(layerID, recursive); + retVector.insert(retVector.end(), childVector.begin(), childVector.end()); + } + } + + return retVector; +} + std::vector Entity::getEntitiesByTag(String tag, bool recursive) const { std::vector retVector; @@ -184,10 +219,8 @@ Color Entity::getCombinedColor() const { Matrix4 Entity::getLookAtMatrix(const Vector3 &loc, const Vector3 &upVector) { rebuildTransformMatrix(); Vector3 D; - if(parentEntity) - D = loc - (parentEntity->getConcatenatedMatrix() *position); - else - D = loc - position; + + D = loc - position; Vector3 back = D * -1; back.Normalize(); @@ -208,6 +241,8 @@ Matrix4 Entity::getLookAtMatrix(const Vector3 &loc, const Vector3 &upVector) { void Entity::lookAt(const Vector3 &loc, const Vector3 &upVector) { Matrix4 newMatrix = getLookAtMatrix(loc, upVector); rotationQuat.createFromMatrix(newMatrix); + rotation = rotationQuat.toEulerAngles(); + rotation = rotation * TODEGREES; matrixDirty = true; } @@ -221,6 +256,7 @@ void Entity::lookAtEntity(Entity *entity, const Vector3 &upVector) { void Entity::removeChild(Entity *entityToRemove) { for(int i=0;isetParentEntity(NULL); children.erase(children.begin()+i); return; } @@ -281,7 +317,10 @@ Entity *Entity::getChildAtIndex(unsigned int index) { } void Entity::addChild(Entity *newChild) { - addEntity(newChild); + newChild->setRenderer(renderer); + newChild->setParentEntity(this); + newChild->setInverseY(getInverseY()); + children.push_back(newChild); } void Entity::setColor(Color color) { @@ -296,40 +335,10 @@ void Entity::setColor(Number r, Number g, Number b, Number a) { color.setColor(r,g,b,a); } -void Entity::recalculateBBox() { - -} - void Entity::setBlendingMode(int newBlendingMode) { blendingMode = newBlendingMode; } -Number Entity::getBBoxRadius() const { - Number compRad; - Number biggest = bBoxRadius; - for(int i=0;igetCompoundBBoxRadius(); - if(compRad > biggest) - biggest = compRad; - } - return biggest; -} - -Number Entity::getCompoundBBoxRadius() const { - Number compRad; - Number biggest = bBoxRadius + position.distance(Vector3(0,0,0)); - for(int i=0;igetCompoundBBoxRadius(); - if(compRad > biggest) - biggest = compRad; - } - return biggest; -} - -void Entity::setBBoxRadius(Number rad) { - bBoxRadius = rad; -} - Entity::~Entity() { if(ownsChildren) { for(int i=0; i < children.size(); i++) { @@ -339,16 +348,35 @@ Entity::~Entity() { if(tags) delete tags; } -Vector3 Entity::getChildCenter() const { - return childCenter; +void Entity::setInverseY(bool val) { + if(val) { + yAdjust = -1.0; + } else { + yAdjust = 1.0; + } + for(int i=0; i < children.size(); i++) { + children[i]->setInverseY(val); + } + matrixDirty = true; } +bool Entity::getInverseY() { + return (yAdjust == -1.0); +} Matrix4 Entity::buildPositionMatrix() { Matrix4 posMatrix; - posMatrix.m[3][0] = position.x*matrixAdj; - posMatrix.m[3][1] = position.y*matrixAdj; - posMatrix.m[3][2] = position.z*matrixAdj; + + posMatrix.m[3][0] = position.x; + posMatrix.m[3][1] = position.y * yAdjust; + posMatrix.m[3][2] = position.z; + + if(snapToPixels) { + posMatrix.m[3][0] = round(posMatrix.m[3][0]); + posMatrix.m[3][1] = round(posMatrix.m[3][1]); + posMatrix.m[3][2] = round(posMatrix.m[3][2]); + } + return posMatrix; } @@ -374,36 +402,30 @@ void Entity::rebuildTransformMatrix() { } void Entity::doUpdates() { - Update(); - for(int i=0; i < children.size(); i++) { - children[i]->doUpdates(); - } -} - -void Entity::checkTransformSetters() { - if(_position != position) { - _position = position; - matrixDirty = true; - } - - if(_scale != scale) { - _scale = scale; - matrixDirty = true; + if (enabled) { + Update(); + for(int i=0; i < children.size(); i++) { + children[i]->doUpdates(); + } } +} - if(_rotation != rotation) { - _rotation = rotation; - rebuildRotation(); - matrixDirty = true; +void Entity::doFixedUpdates() { + if (enabled) { + fixedUpdate(); + for(int i=0; i < children.size(); i++) { + children[i]->doFixedUpdates(); + } } } -void Entity::updateEntityMatrix() { - checkTransformSetters(); +void Entity::updateEntityMatrix() { - if(matrixDirty) + if(matrixDirty) { rebuildTransformMatrix(); - + recalculateAABBAllChildren(); + } + for(int i=0; i < children.size(); i++) { children[i]->updateEntityMatrix(); } @@ -412,17 +434,17 @@ void Entity::updateEntityMatrix() { Vector3 Entity::getCompoundScale() const { if(parentEntity != NULL) { Vector3 parentScale = parentEntity->getCompoundScale(); - return Vector3(scale.x * parentScale.x, scale.y * parentScale.y,scale.z * parentScale.z); + return Vector3(scale.x * parentScale.x, scale.y * parentScale.y, scale.z * parentScale.z); + } else { + return scale; } - else - return scale; } Matrix4 Entity::getConcatenatedRollMatrix() const { Quaternion q; - q.createFromAxisAngle(0.0f, 0.0f, 1.0f, _rotation.roll*matrixAdj); + q.createFromAxisAngle(0.0f, 0.0f, 1.0f, rotation.z); Matrix4 transformMatrix = q.createMatrix(); if(parentEntity != NULL) @@ -431,11 +453,30 @@ Matrix4 Entity::getConcatenatedRollMatrix() const { return transformMatrix; } +Vector2 Entity::getScreenPosition(const Matrix4 &projectionMatrix, const Matrix4 &cameraMatrix, const Polycode::Rectangle &viewport) { + if(renderer){ + return renderer->Project(cameraMatrix, projectionMatrix, viewport, getConcatenatedMatrix().getPosition()); + } else { + return Vector2(); + } +} + +Vector2 Entity::getScreenPositionForMainCamera() { + if(renderer) { + return getScreenPosition(renderer->getProjectionMatrix(), renderer->getCameraMatrix(), renderer->getViewport()); + } else { + return Vector2(); + } +} void Entity::transformAndRender() { if(!renderer || !enabled) return; + if(matrixDirty) { + rebuildTransformMatrix(); + } + if(depthOnly) { renderer->drawToColorBuffer(false); } @@ -461,13 +502,9 @@ void Entity::transformAndRender() { renderer->pushMatrix(); if(ignoreParentMatrix && parentEntity) { renderer->multModelviewMatrix(parentEntity->getConcatenatedMatrix().Inverse()); -// renderer->setCurrentModelMatrix(parentEntity->getConcatenatedMatrix().Inverse()); } - renderer->multModelviewMatrix(transformMatrix); - renderer->setCurrentModelMatrix(transformMatrix); - - renderer->setVertexColor(color.r,color.g,color.b,color.a); + renderer->multModelviewMatrix(transformMatrix); if(billboardMode) { if(billboardIgnoreScale) { @@ -489,33 +526,36 @@ void Entity::transformAndRender() { renderer->enableDepthTest(false); else renderer->enableDepthTest(true); - - renderer->enableAlphaTest(alphaTest); - Color combined = getCombinedColor(); - renderer->setVertexColor(combined.r,combined.g,combined.b,combined.a); + renderer->pushVertexColor(); + renderer->multiplyVertexColor(color); renderer->setBlendingMode(blendingMode); - renderer->enableBackfaceCulling(backfaceCulled); - - int mode = renderer->getRenderMode(); - if(renderWireframe) - renderer->setRenderMode(Renderer::RENDER_MODE_WIREFRAME); - else - renderer->setRenderMode(Renderer::RENDER_MODE_NORMAL); - if(visible) { + + if(visible && rendererVis) { + renderer->pushMatrix(); + renderer->translate3D(-anchorPoint.x * bBox.x * 0.5, -anchorPoint.y * bBox.y * 0.5 * yAdjust, -anchorPoint.z * bBox.z * 0.5); Render(); + renderer->popMatrix(); } + + if(!colorAffectsChildren) { + renderer->pushVertexColor(); + renderer->loadVertexColorIdentity(); + if(visible || (!visible && !visibilityAffectsChildren)) { + renderChildren(); + } + renderer->popVertexColor(); + } else { + if(visible || (!visible && !visibilityAffectsChildren)) { + renderChildren(); + } + } - if(visible || (!visible && !visibilityAffectsChildren)) { - adjustMatrixForChildren(); - renderChildren(); - } - - - renderer->setRenderMode(mode); + renderer->popVertexColor(); + renderer->popMatrix(); - + if(!depthWrite) renderer->enableDepthWrite(true); @@ -537,12 +577,6 @@ void Entity::setRenderer(Renderer *renderer) { } } -void Entity::addEntity(Entity *newChild) { - newChild->setRenderer(renderer); - newChild->setParentEntity(this); - children.push_back(newChild); -} - void Entity::renderChildren() { for(int i=0;irecalculateAABBAllChildren(); + } +} + +void Entity::recalculateAABB() { + + aabb.min = Vector3(); + aabb.max = Vector3(); + + Vector3 bBoxCoords[8] = { + Vector3(-bBox.x * 0.5, -bBox.y * 0.5, bBox.z * 0.5), + Vector3(bBox.x * 0.5, -bBox.y * 0.5, bBox.z * 0.5), + Vector3(-bBox.x * 0.5, -bBox.y * 0.5, -bBox.z * 0.5), + Vector3(bBox.x * 0.5, -bBox.y * 0.5, -bBox.z * 0.5), + Vector3(-bBox.x * 0.5, bBox.y * 0.5, bBox.z * 0.5), + Vector3(bBox.x * 0.5, bBox.y * 0.5, bBox.z * 0.5), + Vector3(-bBox.x * 0.5, bBox.y * 0.5, -bBox.z * 0.5), + Vector3(bBox.x * 0.5, bBox.y * 0.5, -bBox.z * 0.5) + }; + + Matrix4 fullMatrix = getAnchorAdjustedMatrix(); + if(ignoreParentMatrix) { + if(matrixDirty) { + rebuildTransformMatrix(); + } + fullMatrix = transformMatrix; + } + + for(int i=0; i < 8; i++) { + bBoxCoords[i] = fullMatrix * bBoxCoords[i]; + if(i ==0 ) { + aabb.min = bBoxCoords[i]; + aabb.max = bBoxCoords[i]; + } else { + if(bBoxCoords[i].x < aabb.min.x) { + aabb.min.x = bBoxCoords[i].x; + } + if(bBoxCoords[i].y < aabb.min.y) { + aabb.min.y = bBoxCoords[i].y; + } + if(bBoxCoords[i].z < aabb.min.z) { + aabb.min.z = bBoxCoords[i].z; + } + + if(bBoxCoords[i].x > aabb.max.x) { + aabb.max.x = bBoxCoords[i].x; + } + if(bBoxCoords[i].y > aabb.max.y) { + aabb.max.y = bBoxCoords[i].y; + } + if(bBoxCoords[i].z > aabb.max.z) { + aabb.max.z = bBoxCoords[i].z; + } + } + } + +} + +AABB Entity::getWorldAABB() { + return aabb; +} + +Vector3 Entity::getLocalBoundingBox() { + return bBox; +} + +void Entity::setLocalBoundingBox(const Vector3 box) { + bBox = box; + recalculateAABB(); + matrixDirty = true; +} + +void Entity::setLocalBoundingBox(Number x, Number y, Number z) { + bBox.set(x, y, z); + recalculateAABB(); + matrixDirty = true; +} + +void Entity::setLocalBoundingBoxX(Number x) { + bBox.x = x; + recalculateAABB(); + matrixDirty = true; +} + +void Entity::setLocalBoundingBoxY(Number y) { + bBox.y = y; + recalculateAABB(); + matrixDirty = true; +} + +void Entity::setLocalBoundingBoxZ(Number z) { + bBox.z = z; + recalculateAABB(); + matrixDirty = true; +} + void Entity::setRotationQuat(Number w, Number x, Number y, Number z) { rotationQuat.w = w; rotationQuat.x = x; rotationQuat.y = y; rotationQuat.z = z; + rotation = rotationQuat.toEulerAngles(); + rotation = rotation * TODEGREES; + matrixDirty = true; +} + +void Entity::setRotationByQuaternion(const Quaternion &quaternion) { + rotationQuat = quaternion; + rotation = quaternion.toEulerAngles(); + rotation = rotation * TODEGREES; matrixDirty = true; } @@ -566,28 +708,47 @@ Quaternion Entity::getRotationQuat() const { return rotationQuat; } +Quaternion Entity::getConcatenatedQuat() const { + if(parentEntity ) { + return rotationQuat * parentEntity->getConcatenatedQuat(); + } else { + return rotationQuat; + } +} + Vector3 Entity::getScale() const { return scale; } +Vector3 Entity::getRotationEuler() const { + return rotation; +} + Matrix4 Entity::getConcatenatedMatrixRelativeTo(Entity *relativeEntity) { - checkTransformSetters(); - if(matrixDirty) + if(matrixDirty) { rebuildTransformMatrix(); + } - if(parentEntity != NULL && parentEntity != relativeEntity) + if(parentEntity != NULL && parentEntity != relativeEntity) return transformMatrix * parentEntity->getConcatenatedMatrixRelativeTo(relativeEntity); else return transformMatrix; } +Matrix4 Entity::getAnchorAdjustedMatrix() { + Matrix4 mat = getConcatenatedMatrix(); + Matrix4 adjust; + adjust.setPosition(-anchorPoint.x * bBox.x * 0.5, -anchorPoint.y * bBox.y * 0.5 * yAdjust, -anchorPoint.z * bBox.z * 0.5); + return adjust * mat; +} + Matrix4 Entity::getConcatenatedMatrix() { - checkTransformSetters(); - if(matrixDirty) + if(matrixDirty) { rebuildTransformMatrix(); - - if(parentEntity != NULL) + } + + if(parentEntity != NULL && !ignoreParentMatrix) return transformMatrix * parentEntity->getConcatenatedMatrix(); else return transformMatrix; @@ -598,38 +759,49 @@ const Matrix4& Entity::getTransformMatrix() const { } void Entity::Pitch(Number pitch) { - rotation.pitch += pitch; + rotation.x += pitch; + rebuildRotation(); matrixDirty = true; } void Entity::Yaw(Number yaw) { - rotation.yaw += yaw; + rotation.y += yaw; + rebuildRotation(); matrixDirty = true; } void Entity::Roll(Number roll) { - rotation.roll += roll; + rotation.z += roll; + rebuildRotation(); matrixDirty = true; } void Entity::setRoll(Number roll) { - rotation.roll = roll; + rotation.z = roll; + rebuildRotation(); matrixDirty = true; } void Entity::setPitch(Number pitch) { - rotation.pitch = pitch; + rotation.x = pitch; + rebuildRotation(); matrixDirty = true; } void Entity::setYaw(Number yaw) { - rotation.yaw = yaw; + rotation.y = yaw; + rebuildRotation(); matrixDirty = true; } +void Entity::setRotationEuler(const Vector3 &rotation) { + this->rotation = rotation; + rebuildRotation(); + matrixDirty = true; +} void Entity::rebuildRotation() { - rotationQuat.fromAxes(_rotation.pitch, _rotation.yaw, _rotation.roll); + rotationQuat.fromAxes(rotation.x, rotation.y, rotation.z); } void Entity::setEntityProp(const String& propName, const String& propValue) { @@ -667,16 +839,40 @@ void Entity::setParentEntity(Entity *entity) { parentEntity = entity; } +void Entity::setWidth(Number width) { + setLocalBoundingBoxX(width); +} + +void Entity::setHeight(Number height) { + setLocalBoundingBoxY(height); +} + +void Entity::setDepth(Number depth) { + setLocalBoundingBoxZ(depth); +} + +Number Entity::getWidth() const { + return bBox.x; +} + +Number Entity::getHeight() const { + return bBox.y; +} + +Number Entity::getDepth() const { + return bBox.z; +} + Number Entity::getPitch() const { - return rotation.pitch; + return rotation.x; } Number Entity::getYaw() const { - return rotation.yaw; + return rotation.y; } Number Entity::getRoll() const { - return rotation.roll; + return rotation.z; } void Entity::setTransformByMatrixPure(const Matrix4& matrix) { @@ -744,6 +940,11 @@ void Entity::Translate(Number x, Number y, Number z) { matrixDirty = true; } +void Entity::Scale(const Vector3 &scale) { + this->scale = this->scale * scale; + matrixDirty = true; +} + void Entity::Scale(Number x, Number y, Number z) { scale.x *= x; scale.y *= y; @@ -762,25 +963,29 @@ Vector3 Entity::getPosition() const { return position; } +Vector2 Entity::getPosition2D() const { + return Vector2(position.x, position.y); +} + Number Entity::getCombinedPitch() const { if(parentEntity != NULL) - return parentEntity->getCombinedPitch()+rotation.pitch; + return parentEntity->getCombinedPitch()+rotation.x; else - return rotation.pitch; + return rotation.x; } Number Entity::getCombinedYaw() const { if(parentEntity != NULL) - return parentEntity->getCombinedYaw()+rotation.yaw; + return parentEntity->getCombinedYaw()+rotation.y; else - return rotation.yaw; + return rotation.y; } Number Entity::getCombinedRoll() const { if(parentEntity != NULL) - return parentEntity->getCombinedRoll()+rotation.roll; + return parentEntity->getCombinedRoll()+rotation.z; else - return rotation.roll; + return rotation.z; } unsigned int Entity::getNumTags() const { @@ -816,3 +1021,232 @@ void Entity::addTag(String tag) { } } +void Entity::setAnchorPoint(const Vector3 &anchorPoint) { + this->anchorPoint = anchorPoint; + matrixDirty = true; +} + +void Entity::setAnchorPoint(Number x, Number y, Number z) { + anchorPoint.set(x,y,z); + matrixDirty = true; +} + +Vector3 Entity::getAnchorPoint() const { + return anchorPoint; +} + + +MouseEventResult Entity::onMouseDown(const Ray &ray, int mouseButton, int timestamp) { + MouseEventResult ret; + ret.hit = false; + ret.blocked = false; + Number hitDistance; + + if(processInputEvents && enabled) { + hitDistance = ray.boxIntersect(bBox, getAnchorAdjustedMatrix()); + if(hitDistance >= 0.0) { + if(customHitDetection(ray)) { + ret.hit = true; + + Vector3 localCoordinate = Vector3(ray.origin.x,ray.origin.y,0); + Matrix4 inverse = getConcatenatedMatrix().Inverse(); + localCoordinate = inverse * localCoordinate; + + InputEvent *inputEvent = new InputEvent(Vector2(localCoordinate.x, localCoordinate.y*yAdjust), timestamp); + inputEvent->hitDistance = hitDistance; + inputEvent->mouseButton = mouseButton; + dispatchEvent(inputEvent, InputEvent::EVENT_MOUSEDOWN); + + if(timestamp - lastClickTicks < 400) { + InputEvent *inputEvent = new InputEvent(Vector2(localCoordinate.x, localCoordinate.y*yAdjust), timestamp); + inputEvent->mouseButton = mouseButton; + dispatchEvent(inputEvent, InputEvent::EVENT_DOUBLECLICK); + } + lastClickTicks = timestamp; + + if(blockMouseInput) { + ret.blocked = true; + } + } + } + + for(int i=children.size()-1; i>=0; i--) { + MouseEventResult childRes = children[i]->onMouseDown(ray, mouseButton, timestamp); + if(childRes.hit) + ret.hit = true; + + if(childRes.blocked) { + ret.blocked = true; + break; + } + } + } + return ret; +} + +MouseEventResult Entity::onMouseUp(const Ray &ray, int mouseButton, int timestamp) { + MouseEventResult ret; + ret.hit = false; + ret.blocked = false; + Number hitDistance; + + if(processInputEvents && enabled) { + + Vector3 localCoordinate = Vector3(ray.origin.x,ray.origin.y,0); + Matrix4 inverse = getConcatenatedMatrix().Inverse(); + localCoordinate = inverse * localCoordinate; + + InputEvent *inputEvent = new InputEvent(Vector2(localCoordinate.x, localCoordinate.y*yAdjust), timestamp); + inputEvent->mouseButton = mouseButton; + + hitDistance = ray.boxIntersect(bBox, getAnchorAdjustedMatrix()); + if(hitDistance >= 0.0) { + ret.hit = true; + inputEvent->hitDistance = hitDistance; + dispatchEvent(inputEvent, InputEvent::EVENT_MOUSEUP); + if(blockMouseInput) { + ret.blocked = true; + } + } else { + dispatchEvent(inputEvent, InputEvent::EVENT_MOUSEUP_OUTSIDE); + } + + for(int i=children.size()-1; i>=0; i--) { + MouseEventResult childRes = children[i]->onMouseUp(ray, mouseButton, timestamp); + if(childRes.hit) + ret.hit = true; + + if(childRes.blocked) { + ret.blocked = true; + break; + } + } + } + return ret;} + +MouseEventResult Entity::onMouseMove(const Ray &ray, int timestamp) { + MouseEventResult ret; + ret.hit = false; + ret.blocked = false; + Number hitDistance; + + if(processInputEvents && enabled) { + + Vector3 localCoordinate = Vector3(ray.origin.x,ray.origin.y,0); + Matrix4 inverse = getConcatenatedMatrix().Inverse(); + localCoordinate = inverse * localCoordinate; + + hitDistance = ray.boxIntersect(bBox, getAnchorAdjustedMatrix()); + if(hitDistance >= 0.0) { + //setColor(1.0, 0.0, 0.0, 1.0); + ret.hit = true; + InputEvent *inputEvent = new InputEvent(Vector2(localCoordinate.x, localCoordinate.y*yAdjust), timestamp); + inputEvent->hitDistance = hitDistance; + dispatchEvent(inputEvent, InputEvent::EVENT_MOUSEMOVE); + + if(!mouseOver) { + InputEvent *inputEvent = new InputEvent(Vector2(localCoordinate.x, localCoordinate.y*yAdjust), timestamp); + inputEvent->hitDistance = hitDistance; + dispatchEvent(inputEvent, InputEvent::EVENT_MOUSEOVER); + mouseOver = true; + } + + if(blockMouseInput) { + ret.blocked = true; + } + } else { + if(mouseOver) { + dispatchEvent(new InputEvent(Vector2(localCoordinate.x, localCoordinate.y), timestamp), InputEvent::EVENT_MOUSEOUT); + mouseOver = false; + } + } + + for(int i=children.size()-1; i>=0; i--) { + MouseEventResult childRes = children[i]->onMouseMove(ray, timestamp); + if(childRes.hit) + ret.hit = true; + + if(childRes.blocked) { + ret.blocked = true; + break; + } + } + } + return ret; +} + +MouseEventResult Entity::onMouseWheelUp(const Ray &ray, int timestamp) { + MouseEventResult ret; + ret.hit = false; + ret.blocked = false; + Number hitDistance; + + if(processInputEvents && enabled) { + hitDistance = ray.boxIntersect(bBox, getAnchorAdjustedMatrix()); + if(hitDistance >= 0.0) { + ret.hit = true; + + Vector3 localCoordinate = Vector3(ray.origin.x,ray.origin.y,0); + Matrix4 inverse = getConcatenatedMatrix().Inverse(); + localCoordinate = inverse * localCoordinate; + + InputEvent *inputEvent = new InputEvent(Vector2(localCoordinate.x, localCoordinate.y*yAdjust), timestamp); + inputEvent->hitDistance = hitDistance; + dispatchEvent(inputEvent, InputEvent::EVENT_MOUSEWHEEL_UP); + + if(blockMouseInput) { + ret.blocked = true; + } + } + + for(int i=children.size()-1; i>=0; i--) { + MouseEventResult childRes = children[i]->onMouseWheelUp(ray, timestamp); + if(childRes.hit) + ret.hit = true; + + if(childRes.blocked) { + ret.blocked = true; + break; + } + } + } + return ret; +} + +MouseEventResult Entity::onMouseWheelDown(const Ray &ray, int timestamp) { + MouseEventResult ret; + ret.hit = false; + ret.blocked = false; + Number hitDistance; + + if(processInputEvents && enabled) { + hitDistance = ray.boxIntersect(bBox, getAnchorAdjustedMatrix()); + if(hitDistance >= 0.0) { + ret.hit = true; + + Vector3 localCoordinate = Vector3(ray.origin.x,ray.origin.y,0); + Matrix4 inverse = getConcatenatedMatrix().Inverse(); + localCoordinate = inverse * localCoordinate; + + InputEvent *inputEvent = new InputEvent(Vector2(localCoordinate.x, localCoordinate.y*yAdjust), timestamp); + inputEvent->hitDistance = hitDistance; + dispatchEvent(inputEvent, InputEvent::EVENT_MOUSEWHEEL_DOWN); + + if(blockMouseInput) { + ret.blocked = true; + } + } + + for(int i=children.size()-1; i>=0; i--) { + MouseEventResult childRes = children[i]->onMouseWheelDown(ray, timestamp); + if(childRes.hit) + ret.hit = true; + + if(childRes.blocked) { + ret.blocked = true; + break; + } + } + } + return ret; +} diff --git a/Core/Contents/Source/PolyEvent.cpp b/Core/Contents/Source/PolyEvent.cpp index ad879e359..d49ea96d8 100755 --- a/Core/Contents/Source/PolyEvent.cpp +++ b/Core/Contents/Source/PolyEvent.cpp @@ -26,9 +26,11 @@ namespace Polycode { Event::Event() { deleteOnDispatch = true; + cancelEventFlag = false; } - Event::Event(int eventCode) { + Event::Event(int eventCode) { + cancelEventFlag = false; setEventCode(eventCode); } @@ -55,4 +57,9 @@ namespace Polycode { void Event::setEventCode(int eventCode) { this->eventCode = eventCode; } + + void Event::cancelEvent() { + cancelEventFlag = true; + } + } diff --git a/Core/Contents/Source/PolyEventDispatcher.cpp b/Core/Contents/Source/PolyEventDispatcher.cpp index 31a9f22ab..1bff00566 100755 --- a/Core/Contents/Source/PolyEventDispatcher.cpp +++ b/Core/Contents/Source/PolyEventDispatcher.cpp @@ -23,68 +23,82 @@ #include "PolyEventDispatcher.h" #include "PolyEvent.h" -namespace Polycode { - - EventDispatcher::EventDispatcher() : EventHandler() { - } - - EventDispatcher::~EventDispatcher() { - - } - - void EventDispatcher::addEventListener(EventHandler *handler, int eventCode) { - EventEntry newEntry; - newEntry.handler = handler; - newEntry.eventCode = eventCode; - handlerEntries.push_back(newEntry); - } +using namespace Polycode; - void EventDispatcher::removeAllHandlers() { - handlerEntries.clear(); - } - - void EventDispatcher::removeAllHandlersForListener(EventHandler *handler) { - std::vector::iterator iter = handlerEntries.begin(); - while (iter != handlerEntries.end()) { - if((*iter).handler == handler) { - iter = handlerEntries.erase(iter); - } else { - ++iter; - } - } - - } +EventDispatcher::EventDispatcher() : EventHandler() { +} + +EventDispatcher::~EventDispatcher() { + +} + +void EventDispatcher::addEventListenerUnique(EventHandler *handler, int eventCode) { + if(!hasEventListener(handler, eventCode)) { + addEventListener(handler, eventCode); + } +} + +bool EventDispatcher::hasEventListener(EventHandler *handler, int eventCode) { + std::vector::iterator iter = handlerEntries.begin(); + for(int i=0;isetDispatcher(dynamic_cast(this)); - event->setDispatcher(this); - event->setEventCode(eventCode); - for(int i=0;ionEvent != NULL) { - // handlerEntries[i].handler->onEvent(event); - // } - handlerEntries[i].handler->handleEvent(event); - handlerEntries[i].handler->secondaryHandler(event); - } - } - - } - - void EventDispatcher::dispatchEventNoDelete(Event *event, int eventCode) { - __dispatchEvent(event,eventCode); - } +void EventDispatcher::removeAllHandlers() { + handlerEntries.clear(); +} + +void EventDispatcher::removeAllHandlersForListener(EventHandler *handler) { + std::vector::iterator iter = handlerEntries.begin(); + while (iter != handlerEntries.end()) { + if((*iter).handler == handler) { + iter = handlerEntries.erase(iter); + } else { + ++iter; + } + } + +} + +void EventDispatcher::removeEventListener(EventHandler *handler, int eventCode) { + for(int i=0;isetDispatcher(dynamic_cast(this)); + event->setDispatcher(this); + event->setEventCode(eventCode); + for(int i=0;ihandleEvent(event); + if(event->cancelEventFlag) { + break; + } + } + } +} + +void EventDispatcher::dispatchEventNoDelete(Event *event, int eventCode) { + __dispatchEvent(event,eventCode); +} - void EventDispatcher::dispatchEvent(Event *event, int eventCode) { - __dispatchEvent(event,eventCode); - delete event; - } +void EventDispatcher::dispatchEvent(Event *event, int eventCode) { + __dispatchEvent(event,eventCode); + delete event; } diff --git a/Core/Contents/Source/PolyEventHandler.cpp b/Core/Contents/Source/PolyEventHandler.cpp index 6aac6c1ea..a504ce35a 100755 --- a/Core/Contents/Source/PolyEventHandler.cpp +++ b/Core/Contents/Source/PolyEventHandler.cpp @@ -28,9 +28,6 @@ namespace Polycode { EventHandler::EventHandler() { } - void EventHandler::secondaryHandler(Event *event) { - } - EventHandler::~EventHandler() { } diff --git a/Core/Contents/Source/PolyFont.cpp b/Core/Contents/Source/PolyFont.cpp index c7194fe03..38470151f 100755 --- a/Core/Contents/Source/PolyFont.cpp +++ b/Core/Contents/Source/PolyFont.cpp @@ -26,12 +26,9 @@ using namespace Polycode; -Font::Font(const String& fileName) { +Font::Font(const String& fileName, FT_Library FTLibrary) { this->fileName = fileName; - - FT_Library FTLibrary; - FT_Init_FreeType(&FTLibrary); loaded = false; buffer = NULL; diff --git a/Core/Contents/Source/PolyFontGlyphSheet.cpp b/Core/Contents/Source/PolyFontGlyphSheet.cpp new file mode 100755 index 000000000..83bec74e1 --- /dev/null +++ b/Core/Contents/Source/PolyFontGlyphSheet.cpp @@ -0,0 +1,326 @@ +/* + Copyright (C) 2011 by Ivan Safrin + + 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: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + 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. +*/ + +#include "PolyFont.h" +#include "PolyFontGlyphSheet.h" +#include "OSBasics.h" +#include "PolyLogger.h" +#include "PolyRenderer.h" +#include "PolyImage.h" +#include "PolyTexture.h" +#include "PolyCoreServices.h" +#include +#include +#include + +using namespace Polycode; + +FontGlyphSheet::FontGlyphSheet(Font* font, int size, FontTextureGlyphMode mode) +: font(font) +, size(size) +, mode(mode) +, tabWidth(100) +{ +} + +FontGlyphSheet::~FontGlyphSheet() { + Services()->getRenderer()->destroyTexture(texture); +} + +struct GlyphData { + wchar_t character; + short off_x,off_y; + short size_x,size_y; + short pitch; + Vector2 advance; + unsigned char* data; + int texture_u,texture_v; + + GlyphData() + : data(NULL) + { + } + + ~GlyphData() { + delete data; + } +}; + +bool fontGlyphSorter(GlyphData* a, GlyphData* b) { + return a->size_y > b->size_y; +} + +inline int pot_ceil(int x) { + --x; + x |= x >> 1; + x |= x >> 2; + x |= x >> 4; + x |= x >> 8; + x |= x >> 16; + return x+1; +} + +std::set FontGlyphSheet::getCharacters() const { + std::set set; + for (std::map::const_iterator it = locations.begin(); it != locations.end(); it++) { + set.insert(it->first); + } + return set; +} + +void FontGlyphSheet::buildVisibleAscii() { + std::set chars; + for (wchar_t i = 32; i < 127; i++) { + chars.insert(i); + } + buildGlyphs(chars); +} + +void FontGlyphSheet::addGlyphs(String extraCharacters) { + extraCharacters.getWDataWithEncoding(String::ENCODING_UTF8); + std::wstring& str = extraCharacters.w_contents; + for (std::wstring::iterator it = str.begin(); it != str.end(); it++ ) { + if (locations.find(*it) == locations.end()) { + std::set characterSet = getCharacters(); + characterSet.insert(str.begin(), str.end()); + buildGlyphs(characterSet); + break; + } + } +} + +void FontGlyphSheet::setSize(int size) { + if (this->size == size) return; + this->size = size; + buildGlyphs(getCharacters()); +} + +void FontGlyphSheet::buildGlyphs(String charactersIn) { + charactersIn.getWDataWithEncoding(String::ENCODING_UTF8); + buildGlyphs(std::set(charactersIn.w_contents.begin(), charactersIn.w_contents.end())); +} + +void FontGlyphSheet::buildGlyphs(std::set characters) { + + typedef std::set character_container_t; + typedef std::vector glyph_list_t; + glyph_list_t glyphData; + characters.insert('?');//Good backup character - make sure it's always present + + int shift = 0; + Number scaleDown = 1.0f; + FT_UInt height = size; + if (mode == ALPHA_TEST) { + shift = 2; + scaleDown /= (1<getFace(); + FT_Set_Pixel_Sizes(ftFace, 0, height); + + //Get all the glyph data from freetype + for (character_container_t::iterator it = characters.begin(); it != characters.end(); it++) { + FT_Int32 load_flags = FT_LOAD_RENDER; +// if (glyphMode == ALPHA_TEST) { +// load_flags |= FT_LOAD_MONOCHROME; +// } + int error = FT_Load_Char(ftFace, *it, load_flags); + if (error) { + Logger::log("Failed to load glyph for codepoint %d '%lc' error %#x\n",*it,*it,error); + } + else { + glyphData.push_back(new GlyphData()); + GlyphData& gd = *glyphData.back(); + FT_GlyphSlot slot = ftFace->glyph; + gd.character = *it; + gd.off_x = slot->bitmap_left; + gd.off_y = slot->bitmap_top; + gd.size_x = slot->bitmap.width; + gd.size_y = slot->bitmap.rows; + gd.advance.set(Number(slot->advance.x)/(64<advance.y)/(64<bitmap.pitch; + int dataLength = slot->bitmap.pitch * gd.size_y; + if (dataLength) { + gd.data = new unsigned char[dataLength]; + memcpy(gd.data, slot->bitmap.buffer, dataLength); + } + } + } + + std::sort(glyphData.begin(), glyphData.end(), fontGlyphSorter); + + //Compute the layout for the glyphs on the texture + const int padding = 1; + int sheet_width = 512, sheet_height; + { + int sheet_y = padding, sheet_x = padding; + int row_size_y = 0; + for (glyph_list_t::iterator it = glyphData.begin(); it != glyphData.end(); it++) { + GlyphData& gd = **it; + int size_x = gd.size_x + ((1<> shift; + int size_y = gd.size_y + ((1<> shift; + if (sheet_x + size_x + padding >= sheet_width) { + sheet_x = padding; + sheet_y += row_size_y + padding; + row_size_y = 0; + } + if (size_y > row_size_y) { + row_size_y = size_y; + } + gd.texture_u = sheet_x; + gd.texture_v = sheet_y; + sheet_x += size_x + padding; + } + sheet_y += row_size_y; + sheet_height = pot_ceil(sheet_y); + } + + //Paste all the glyphs onto the texture and calculate the render data + locations.clear(); + Image* glyphsImage = new Image(sheet_width, sheet_height); + for (glyph_list_t::iterator it = glyphData.begin(); it != glyphData.end(); it++) { + GlyphData& gd = **it; + int size_x = gd.size_x + ((1<> shift; + int size_y = gd.size_y + ((1<> shift; + for (int glyph_y = 0, i = 0; glyph_y < size_y; glyph_y++) { + for (int glyph_x = 0; glyph_x < size_x; glyph_x++, i++) { + unsigned char value = gd.data[i]; + int x = gd.texture_u + glyph_x; + int y = gd.texture_v + glyph_y; + if (mode == ALPHA_TEST) { + const int SEARCH_RANGE = 2; + //Don't quite use the full range of 128 either side + const Number ALPHA_SCALE = 112.0f / (SEARCH_RANGE << shift); + int scan_x0 = glyph_x - SEARCH_RANGE << shift; + int scan_x1 = glyph_x + SEARCH_RANGE << shift; + int scan_y0 = glyph_y - SEARCH_RANGE << shift; + int scan_y1 = glyph_y + SEARCH_RANGE << shift; + if (scan_x0 < 0) scan_x0 = 0; + if (scan_y0 < 0) scan_y0 = 0; + if (scan_x1 >= gd.size_x) scan_x1 = gd.size_x - 1; + if (scan_y1 >= gd.size_y) scan_y1 = gd.size_y - 1; + +// value = (gd.data[glyph_y * gd.pitch + (glyph_x>>3)] >> (7-(glyph_x&7))) & 1; + value = (gd.data[(glyph_y<>7) & 1; + Number dist = SEARCH_RANGE << shift; + for (int scan_y = scan_y0; scan_y <= scan_y1; scan_y++) { + int dy = (glyph_y<>3)] >> (7-(scan_x&7))) & 1; + int v = (gd.data[scan_y * gd.pitch + scan_x] >> 7) & 1; + if (v != value) { + int dx2 = dx*dx; + Number d(sqrt((Number)(dx2+dy2))); + if (d < dist) { + dist = d; + } + } + } + } + value = (int)round(128 + ((value+value)-1) * (dist * ALPHA_SCALE)); +// value *= 255; + } + glyphsImage->setPixel(x, y, Color(255, 255, 255, value)); + } + } + Number x0 = gd.off_x * scaleDown; + Number y0 = gd.off_y * scaleDown; + Number x1 = x0 + gd.size_x * scaleDown; + Number y1 = y0 - gd.size_y * scaleDown; + Number u0 = Number(gd.texture_u) / sheet_width; + Number v0 = 1.0f - Number(gd.texture_v) / sheet_height; + Number u1 = Number(gd.texture_u + size_x) / sheet_width; + Number v1 = 1.0f - Number(gd.texture_v + size_y) / sheet_height; + FontTextureGlyph& glyph = locations[gd.character]; + glyph.offset[0].set(x0, y0); + glyph.offset[1].set(x0, y1); + glyph.offset[2].set(x1, y1); + glyph.offset[3].set(x1, y0); + glyph.texCoord[0].set(u0, v0); + glyph.texCoord[1].set(u0, v1); + glyph.texCoord[2].set(u1, v1); + glyph.texCoord[3].set(u1, v0); + glyph.advance = gd.advance; + } + + MaterialManager *materialManager = CoreServices::getInstance()->getMaterialManager(); + Services()->getRenderer()->destroyTexture(texture); + + texture = materialManager->createTextureFromImage(glyphsImage, true, materialManager->mipmapsDefault); + delete glyphsImage; + for (glyph_list_t::iterator it = glyphData.begin(); it != glyphData.end(); it++) delete *it; +} + +/* +int FontGlyphSheet::renderStringVertices(String textIn, std::vector& vertices, int index) { + textIn.getWDataWithEncoding(String::ENCODING_UTF8); + std::wstring& text = textIn.w_contents; + + Vector2 cursor; + wchar_t prevChar = -1; + for (std::wstring::const_iterator it = text.begin(); it != text.end(); it++) { + + switch(*it) { + case '\t': + cursor.x = (int(cursor.x / tabWidth) + 1) * tabWidth; + break; + case '\n': + cursor.x = 0; + cursor.y += size; + break; + default: + std::map::iterator glyphLoc = locations.find(*it); + if (glyphLoc == locations.end()) { + Logger::log("Missing glyph for codepoint %d '%lc'\n",*it,*it); + glyphLoc = locations.find('?'); + } + + // if (prevChar != -1) { + // FT_Vector delta; + // FT_Get_Kerning( ftFace, FT_Get_Char_Index(ftFace, prevChar), FT_Get_Char_Index(ftFace, *it), FT_KERNING_DEFAULT, &delta); + // cursor.x += delta.x / Number(64); + // } + + for (int i = 0; i < 4; i++, index++) { + Vertex* vertex; + if (index == vertices.size()) { + vertices.push_back(vertex = new Vertex()); + } + else { + vertex = vertices[index]; + } + vertex->set(cursor.x + glyphLoc->second.offset[i].x, cursor.y + glyphLoc->second.offset[i].y, 0); + vertex->texCoord = glyphLoc->second.texCoord[i]; + } + cursor += glyphLoc->second.advance; + break; + } + + prevChar = *it; + } + return index; +} + +*/ + diff --git a/Core/Contents/Source/PolyFontManager.cpp b/Core/Contents/Source/PolyFontManager.cpp index b5333b268..c1ec53c52 100644 --- a/Core/Contents/Source/PolyFontManager.cpp +++ b/Core/Contents/Source/PolyFontManager.cpp @@ -22,11 +22,13 @@ #include "PolyFontManager.h" #include "PolyFont.h" +#include FT_LCD_FILTER_H using namespace Polycode; FontManager::FontManager() { - + FT_Init_FreeType(&FTLibrary); + FT_Library_SetLcdFilter(FTLibrary, FT_LCD_FILTER_LIGHT); } FontManager::~FontManager() { @@ -35,6 +37,7 @@ FontManager::~FontManager() { delete entry.font; } fonts.clear(); + FT_Done_FreeType(FTLibrary); } unsigned int FontManager::getNumFonts() const { @@ -63,7 +66,7 @@ void FontManager::removeFontEntry(FontEntry *entry, bool deleteFont) { } void FontManager::registerFont(const String& fontName, const String& fontPath) { - Font *font = new Font(fontPath); + Font *font = new Font(fontPath, FTLibrary); if(font->loaded) { FontEntry newEntry; newEntry.font = font; diff --git a/Core/Contents/Source/PolyGLCubemap.cpp b/Core/Contents/Source/PolyGLCubemap.cpp index a975dab27..88b5ad225 100644 --- a/Core/Contents/Source/PolyGLCubemap.cpp +++ b/Core/Contents/Source/PolyGLCubemap.cpp @@ -51,23 +51,34 @@ void OpenGLCubemap::recreateFromTextures() { Texture *tex; tex = getTexture(Cubemap::CUBEMAP_XPOS); - glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X, 0, GL_RGBA, tex->getWidth(), tex->getHeight(), 0, GL_RGBA, GL_UNSIGNED_BYTE, tex->getTextureData()); - - tex = getTexture(Cubemap::CUBEMAP_XNEG); - glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_X, 0, GL_RGBA, tex->getWidth(), tex->getHeight(), 0, GL_RGBA, GL_UNSIGNED_BYTE, tex->getTextureData()); - + if(tex) { + glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X, 0, GL_RGBA, tex->getWidth(), tex->getHeight(), 0, GL_RGBA, GL_UNSIGNED_BYTE, tex->getTextureData()); + } + + tex = getTexture(Cubemap::CUBEMAP_XNEG); + if(tex) { + glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_X, 0, GL_RGBA, tex->getWidth(), tex->getHeight(), 0, GL_RGBA, GL_UNSIGNED_BYTE, tex->getTextureData()); + } + tex = getTexture(Cubemap::CUBEMAP_YPOS); - glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_Y, 0, GL_RGBA, tex->getWidth(), tex->getHeight(), 0, GL_RGBA, GL_UNSIGNED_BYTE, tex->getTextureData()); - + if(tex) { + glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_Y, 0, GL_RGBA, tex->getWidth(), tex->getHeight(), 0, GL_RGBA, GL_UNSIGNED_BYTE, tex->getTextureData()); + } + tex = getTexture(Cubemap::CUBEMAP_YNEG); - glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, 0, GL_RGBA, tex->getWidth(), tex->getHeight(), 0, GL_RGBA, GL_UNSIGNED_BYTE, tex->getTextureData()); - + if(tex) { + glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, 0, GL_RGBA, tex->getWidth(), tex->getHeight(), 0, GL_RGBA, GL_UNSIGNED_BYTE, tex->getTextureData()); + } + tex = getTexture(Cubemap::CUBEMAP_ZPOS); - glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_Z, 0, GL_RGBA, tex->getWidth(), tex->getHeight(), 0, GL_RGBA, GL_UNSIGNED_BYTE, tex->getTextureData()); - + if(tex) { + glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_Z, 0, GL_RGBA, tex->getWidth(), tex->getHeight(), 0, GL_RGBA, GL_UNSIGNED_BYTE, tex->getTextureData()); + } + tex = getTexture(Cubemap::CUBEMAP_ZNEG); - glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_Z, 0, GL_RGBA, tex->getWidth(), tex->getHeight(), 0, GL_RGBA, GL_UNSIGNED_BYTE, tex->getTextureData()); - + if(tex) { + glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_Z, 0, GL_RGBA, tex->getWidth(), tex->getHeight(), 0, GL_RGBA, GL_UNSIGNED_BYTE, tex->getTextureData()); + } glCubemapLoaded = true; } diff --git a/Core/Contents/Source/PolyGLES1Renderer.cpp b/Core/Contents/Source/PolyGLES1Renderer.cpp deleted file mode 100644 index f1df33803..000000000 --- a/Core/Contents/Source/PolyGLES1Renderer.cpp +++ /dev/null @@ -1,710 +0,0 @@ -/* - Copyright (C) 2011 by Ivan Safrin - - 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: - - The above copyright notice and this permission notice shall be included in - all copies or substantial portions of the Software. - - 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. -*/ - -#include "PolyGLES1Renderer.h" - -using namespace Polycode; - -OpenGLES1Renderer::OpenGLES1Renderer() : Renderer() { - nearPlane = 0.1f; - farPlane = 1000.0f; - - glGenFramebuffersOES(1, &defaultFramebuffer); - glGenRenderbuffersOES(1, &colorRenderbuffer); - glBindFramebufferOES(GL_FRAMEBUFFER_OES, defaultFramebuffer); - glBindRenderbufferOES(GL_RENDERBUFFER_OES, colorRenderbuffer); - glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES, GL_COLOR_ATTACHMENT0_OES, GL_RENDERBUFFER_OES, colorRenderbuffer); - -} - -void OpenGLES1Renderer::Resize(int xRes, int yRes) { - - this->xRes = xRes; - this->yRes = yRes; - glClearColor(clearColor.r, clearColor.g, clearColor.b, clearColor.a); - glClearDepthf(1.0f); - - glMatrixMode(GL_PROJECTION); - glLoadIdentity(); - gluPerspective(fov,(GLfloat)xRes/(GLfloat)yRes,nearPlane,farPlane); - glViewport(0, 0, xRes, yRes); - setScissorBox(0, 0, xRex, yRes); - - glMatrixMode(GL_MODELVIEW); - glLineWidth(1); - glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - - glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); - glEnable(GL_BLEND); - glShadeModel(GL_SMOOTH); - glDepthFunc( GL_LEQUAL ); - - glEnable(GL_DEPTH_TEST); - - glLineWidth(1.0f); - glEnable(GL_LINE_SMOOTH); - - GLint numBuffers = 0; -// glGetIntegerv(GL_MAX_DRAW_BUFFERS, &numBuffers); -} - -void OpenGLES1Renderer::enableAlphaTest(bool val) { - if(val) { - glAlphaFunc ( GL_GREATER, 0.01) ; - glEnable ( GL_ALPHA_TEST ) ; - } else { - glDisable( GL_ALPHA_TEST ) ; - } -} - -void OpenGLES1Renderer::setLineSmooth(bool val) { - if(val) - glEnable(GL_LINE_SMOOTH); - else - glDisable(GL_LINE_SMOOTH); -} - -void OpenGLES1Renderer::setFOV(Number fov) { - this->fov = fov; - glMatrixMode(GL_PROJECTION); - glLoadIdentity(); - gluPerspective(fov,(GLfloat)xRes/(GLfloat)yRes,nearPlane,farPlane); - glViewport(0, 0, xRes, yRes); - glScissor(0, 0, xRes, yRes); - glMatrixMode(GL_MODELVIEW); -} - -void OpenGLES1Renderer::setViewportSize(int w, int h, Number fov) { - glMatrixMode(GL_PROJECTION); - glLoadIdentity(); - gluPerspective(fov,(GLfloat)w/(GLfloat)h,nearPlane,farPlane); - glViewport(0, 0, w, h); - glScissor(0, 0, w, h); - glMatrixMode(GL_MODELVIEW); -} - -Vector3 OpenGLES1Renderer::Unproject(Number x, Number y) { - Vector3 coords; - GLfloat wx, wy, wz; - GLfloat cx, cy, cz; - - GLfloat mv[16]; - glGetFloatv( GL_MODELVIEW_MATRIX, mv ); - - GLfloat proj[16]; - glGetFloatv( GL_PROJECTION_MATRIX, proj ); - - GLint vp[4]; - glGetIntegerv( GL_VIEWPORT, vp ); - - wx = ( Number ) x; - wy = ( Number ) vp[3] - ( Number ) y; - glReadPixels( x, wy, 1, 1, GL_DEPTH_COMPONENT16_OES, GL_FLOAT, &wz ); - - gluUnProject((GLdouble)wx, (GLdouble)wy, (GLdouble)wz, (GLdouble*)mv, (GLdouble*)proj, vp, (GLdouble*)&cx, (GLdouble*)&cy, (GLdouble*)&cz ); - - coords = Vector3( cx, cy, cz ); - - return coords; - -} - -bool OpenGLES1Renderer::test2DCoordinate(Number x, Number y, Poly::Polygon *poly, const Matrix4 &matrix, bool billboardMode) { - GLfloat nearPlane[3],farPlane[3]; - - GLfloat mv[16]; - Matrix4 camInverse = cameraMatrix.Inverse(); - Matrix4 cmv; - cmv.identity(); - cmv = cmv * camInverse; - - for(int i=0; i < 16; i++) { - mv[i] = cmv.ml[i]; - } - - GLint vp[4]; - glGetIntegerv( GL_VIEWPORT, vp ); - - gluUnProject((GLdouble)x, (GLdouble)yRes - y, 0.0, (GLdouble*)mv, (GLdouble*)sceneProjectionMatrix, vp, (GLdouble*)&nearPlane[0], (GLdouble*)&nearPlane[1], (GLdouble*)&nearPlane[2]); - gluUnProject((GLdouble)x, (GLdouble)yRes - y, 1.0, (GLdouble*)mv, (GLdouble*)sceneProjectionMatrix, vp, (GLdouble*)&farPlane[0], (GLdouble*)&farPlane[1], (GLdouble*)&farPlane[2]); - - Vector3 nearVec(nearPlane[0], nearPlane[1], nearPlane[2]); - Vector3 farVec(farPlane[0], farPlane[1], farPlane[2]); - - Vector3 dirVec = farVec - nearVec; - dirVec.Normalize(); - - Vector3 hitPoint; - - Matrix4 fullMatrix = matrix; - - if(poly->getVertexCount() == 3) { - return rayTriangleIntersect(Vector3(0,0,0), dirVec, fullMatrix * (*poly->getVertex(0)), fullMatrix * (*poly->getVertex(1)), fullMatrix * (*poly->getVertex(2)), &hitPoint); - } else if(poly->getVertexCount() == 4) { - return (rayTriangleIntersect(Vector3(0,0,0), dirVec, fullMatrix * (*poly->getVertex(2)), fullMatrix * (*poly->getVertex(1)), fullMatrix * (*poly->getVertex(0)), &hitPoint) || - rayTriangleIntersect(Vector3(0,0,0), dirVec, fullMatrix * (*poly->getVertex(0)), fullMatrix * (*poly->getVertex(3)), fullMatrix * (*poly->getVertex(2)), &hitPoint)); - } else { - return false; - } -} - -void OpenGLES1Renderer::enableDepthTest(bool val) { - // if(val) - // glEnable(GL_DEPTH_TEST); - // else - // glDisable(GL_DEPTH_TEST); - if(val) - glDepthMask(GL_TRUE); - else - glDepthMask(GL_FALSE); - -} - -void OpenGLES1Renderer::setModelviewMatrix(Matrix4 m) { - glLoadMatrixf(m.ml); -} - -void OpenGLES1Renderer::multModelviewMatrix(Matrix4 m) { - // glMatrixMode(GL_MODELVIEW); - glMultMatrixf(m.ml); -} - -void OpenGLES1Renderer::enableLighting(bool enable) { - lightingEnabled = enable; -} - -void OpenGLES1Renderer::setLineSize(Number lineSize) { - glLineWidth(lineSize); -} - -void OpenGLES1Renderer::createVertexBufferForMesh(Mesh *mesh) { -// OpenGLVertexBuffer *buffer = new OpenGLVertexBuffer(mesh); -// mesh->setVertexBuffer(buffer); -} - -void OpenGLES1Renderer::drawVertexBuffer(VertexBuffer *buffer) { - /* - OpenGLVertexBuffer *glVertexBuffer = (OpenGLVertexBuffer*)buffer; - - glEnableClientState(GL_VERTEX_ARRAY); - glEnableClientState(GL_TEXTURE_COORD_ARRAY); - glEnableClientState(GL_NORMAL_ARRAY); - // glEnableClientState(GL_COLOR_ARRAY); - - // glBindBufferARB( GL_ARRAY_BUFFER_ARB, glVertexBuffer->getColorBufferID()); - // glTexCoordPointer( 4, GL_FLOAT, 0, (char *) NULL ); - glBindBufferARB( GL_ARRAY_BUFFER_ARB, glVertexBuffer->getVertexBufferID()); - glVertexPointer( 3, GL_FLOAT, 0, (char *) NULL ); - glBindBufferARB( GL_ARRAY_BUFFER_ARB, glVertexBuffer->getNormalBufferID()); - glNormalPointer(GL_FLOAT, 0, (char *) NULL ); - glBindBufferARB( GL_ARRAY_BUFFER_ARB, glVertexBuffer->getTextCoordBufferID()); - glTexCoordPointer( 2, GL_FLOAT, 0, (char *) NULL ); - - glDrawArrays( GL_TRIANGLES, 0, buffer->getVertexCount() ); - - glDisableClientState( GL_VERTEX_ARRAY); - glDisableClientState( GL_TEXTURE_COORD_ARRAY ); - glDisableClientState( GL_NORMAL_ARRAY ); - // glDisableClientState( GL_COLOR_ARRAY ); - */ -} - -void OpenGLES1Renderer::enableFog(bool enable) { - if(enable) - glEnable(GL_FOG); - else { - glDisable(GL_FOG); - glClearColor(clearColor.r, clearColor.g, clearColor.b, clearColor.a); - } -} - -void OpenGLES1Renderer::setBlendingMode(int blendingMode) { - switch(blendingMode) { - case BLEND_MODE_NORMAL: - glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - break; - case BLEND_MODE_LIGHTEN: - glBlendFunc (GL_SRC_ALPHA, GL_ONE); - break; - case BLEND_MODE_COLOR: - glBlendFunc (GL_DST_COLOR, GL_ONE); - break; - default: - glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - break; - } - glEnable(GL_BLEND); -} - -Matrix4 OpenGLES1Renderer::getProjectionMatrix() { - Number m[16]; - glGetFloatv( GL_PROJECTION_MATRIX, m); - return Matrix4(m); -} - -Matrix4 OpenGLES1Renderer::getModelviewMatrix() { - Number m[16]; - glGetFloatv( GL_MODELVIEW_MATRIX, m); - return Matrix4(m); -} - -void OpenGLES1Renderer::renderZBufferToTexture(Texture *targetTexture) { - // OpenGLES1Texture *glTexture = (OpenGLES1Texture*)targetTexture; - // glBindTexture (GL_TEXTURE_2D, glTexture->getTextureID()); - // glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, 0, 0, targetTexture->getWidth(), targetTexture->getHeight(), 0); -} - -void OpenGLES1Renderer::renderToTexture(Texture *targetTexture) { - OpenGLES1Texture *glTexture = (OpenGLES1Texture*)targetTexture; - glBindTexture (GL_TEXTURE_2D, glTexture->getTextureID()); - glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 0, 0, targetTexture->getWidth(), targetTexture->getHeight(), 0); - -} - -void OpenGLES1Renderer::setFogProperties(int fogMode, Color color, Number density, Number startDepth, Number endDepth) { - switch(fogMode) { - case FOG_LINEAR: - glFogx(GL_FOG_MODE, GL_LINEAR); - break; - case FOG_EXP: - glFogx(GL_FOG_MODE, GL_EXP); - break; - case FOG_EXP2: - glFogx(GL_FOG_MODE, GL_EXP2); - break; - default: - glFogx(GL_FOG_MODE, GL_LINEAR); - break; - } - - GLfloat fogColor[4]= {color.r, color.g, color.b, color.a}; - glFogfv(GL_FOG_COLOR, fogColor); - glFogf(GL_FOG_DENSITY, density); - glHint(GL_FOG_HINT, GL_DONT_CARE); - glFogf(GL_FOG_START, startDepth); - glFogf(GL_FOG_END, endDepth); - glClearColor(color.r, color.g, color.b, color.a); -} - -void OpenGLES1Renderer::setOrthoMode() { - setBlendingMode(BLEND_MODE_NORMAL); - if(!orthoMode) { - glDisable(GL_LIGHTING); - glMatrixMode(GL_PROJECTION); - glDisable(GL_CULL_FACE); - glPushMatrix(); - glLoadIdentity(); - glOrthox(0.0f,xRes,yRes,0,-1.0f,1.0f); - // glOrtho(0.0f,2500.0f,2500.0f,0,-1.0f,1.0f); - orthoMode = true; - } - glMatrixMode(GL_MODELVIEW); - glLoadIdentity(); -} - -void OpenGLES1Renderer::enableBackfaceCulling(bool val) { - if(val) - glEnable(GL_CULL_FACE); - else - glDisable(GL_CULL_FACE); -} - -void OpenGLES1Renderer::setPerspectiveMode() { - setBlendingMode(BLEND_MODE_NORMAL); - if(orthoMode) { - if(lightingEnabled) { - } - glEnable (GL_DEPTH_TEST); - glEnable(GL_CULL_FACE); - glMatrixMode( GL_PROJECTION ); - glPopMatrix(); - glMatrixMode( GL_MODELVIEW ); - orthoMode = false; - } - glLoadIdentity(); - - glGetFloatv( GL_PROJECTION_MATRIX, sceneProjectionMatrix); - currentTexture = NULL; -} - -void OpenGLES1Renderer::BeginRender() { - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - glLoadIdentity(); - currentTexture = NULL; -} - -void OpenGLES1Renderer::setClearColor(Number r, Number g, Number b) { - clearColor.setColor(r,g,b,1.0f); - glClearColor(r,g,b,0.0f); -} - -void OpenGLES1Renderer::translate3D(Vector3 *position) { - glTranslatef(position->x, position->y, position->z); -} - -void OpenGLES1Renderer::translate3D(Number x, Number y, Number z) { - glTranslatef(x, y, z); -} - -void OpenGLES1Renderer::scale3D(Vector3 *scale) { - glScalef(scale->x, scale->y, scale->z); -} - -void OpenGLES1Renderer::bindFrameBufferTexture(Texture *texture) { - /* - if(currentFrameBufferTexture) { - previousFrameBufferTexture = currentFrameBufferTexture; - } - OpenGLES1Texture *glTexture = (OpenGLES1Texture*)texture; - - glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, glTexture->getFrameBufferID()); - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - - currentFrameBufferTexture = texture; - */ -} - -void OpenGLES1Renderer::unbindFramebuffers() { - /* - glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); - currentFrameBufferTexture = NULL; - if(previousFrameBufferTexture) { - bindFrameBufferTexture(previousFrameBufferTexture); - previousFrameBufferTexture = NULL; - } - */ -} - - -void OpenGLES1Renderer::createRenderTextures(Texture **colorBuffer, Texture **depthBuffer, int width, int height) { - /* - Logger::log("generating fbo textures %d %d\n", colorBuffer, depthBuffer); - - GLuint depthTexture,colorTexture; - GLenum status; - GLuint frameBufferID; - - glGenFramebuffersEXT(1, &frameBufferID); - - glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, frameBufferID); - glGenTextures(1,&colorTexture); - glBindTexture(GL_TEXTURE_2D,colorTexture); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); - - glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, colorTexture, 0); - - status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT); - if(status == GL_FRAMEBUFFER_COMPLETE_EXT) { - Logger::log("color fbo generation successful\n"); - } else { - Logger::log("color fbo generation failed\n"); - } - - if(colorBuffer) { - OpenGLES1Texture *colorBufferTexture = new OpenGLES1Texture(width, height); - colorBufferTexture->setGLInfo(colorTexture, frameBufferID); - *colorBuffer = ((Texture*)colorBufferTexture); - } - - if(depthBuffer) { - glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, frameBufferID); - glGenTextures(1,&depthTexture); - glBindTexture(GL_TEXTURE_2D,depthTexture); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER); - glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER); - - // glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE,GL_COMPARE_R_TO_TEXTURE); - // glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC, GL_LEQUAL); - glTexParameteri(GL_TEXTURE_2D, GL_DEPTH_TEXTURE_MODE, GL_LUMINANCE); - - glTexImage2D(GL_TEXTURE_2D,0,GL_DEPTH_COMPONENT,width,height,0,GL_DEPTH_COMPONENT,GL_UNSIGNED_BYTE,0); - - glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_TEXTURE_2D, depthTexture, 0); - - status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT); - - if(status == GL_FRAMEBUFFER_COMPLETE_EXT) { - Logger::log("depth fbo generation successful\n"); - } else { - Logger::log("depth fbo generation failed\n"); - } - - OpenGLES1Texture *depthBufferTexture = new OpenGLES1Texture(width, height); - depthBufferTexture->setGLInfo(depthTexture, frameBufferID); - *depthBuffer = ((Texture*)depthBufferTexture); - } - - glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); -*/ -} - -Texture *OpenGLES1Renderer::createFramebufferTexture(unsigned int width, unsigned int height) { - OpenGLES1Texture *newTexture = new OpenGLES1Texture(width, height); - return newTexture; -} - -Cubemap *OpenGLES1Renderer::createCubemap(Texture *t0, Texture *t1, Texture *t2, Texture *t3, Texture *t4, Texture *t5) { -// OpenGLCubemap *newCubemap = new OpenGLCubemap(t0,t1,t2,t3,t4,t5); -// return newCubemap; - return NULL; -} - -Texture *OpenGLES1Renderer::createTexture(unsigned int width, unsigned int height, char *textureData, bool clamp, int type) { - OpenGLES1Texture *newTexture = new OpenGLES1Texture(width, height, textureData, clamp, textureFilteringMode); - return newTexture; -} - -void OpenGLES1Renderer::clearScreen() { - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); -} - -void OpenGLES1Renderer::applyMaterial(Material *material, ShaderBinding *localOptions,unsigned int shaderIndex) { - if(!material->getShader(shaderIndex) || !shadersEnabled) { - setTexture(NULL); - return; - } - - FixedShaderBinding *fBinding; - - switch(material->getShader(shaderIndex)->getType()) { - case Shader::FIXED_SHADER: - // FixedShader *fShader = (FixedShader*)material->getShader(); - fBinding = (FixedShaderBinding*)material->getShaderBinding(shaderIndex); - setTexture(fBinding->getDiffuseTexture()); - // setTexture(fShader->getDiffuseTexture()); - break; - case Shader::CG_SHADER: - break; - } -} - -void OpenGLES1Renderer::clearShader() { - currentMaterial = NULL; -} - -void OpenGLES1Renderer::setTexture(Texture *texture) { - if(texture == NULL) { - glDisable(GL_TEXTURE_2D); - return; - } - - if(renderMode == RENDER_MODE_NORMAL) { - glEnable (GL_TEXTURE_2D); - if(currentTexture != texture) { - OpenGLES1Texture *glTexture = (OpenGLES1Texture*)texture; - glBindTexture (GL_TEXTURE_2D, glTexture->getTextureID()); - } - } else { - glDisable(GL_TEXTURE_2D); - } - - currentTexture = texture; -} - -void OpenGLES1Renderer::beginRenderOperation(int meshType) { - /* - switch(meshType) { - case Mesh::TRI_MESH: - switch(renderMode) { - case RENDER_MODE_NORMAL: - Begin(GL_TRIANGLES); - break; - case RENDER_MODE_WIREFRAME: - glBegin(GL_LINE_LOOP); - break; - } - break; - case Mesh::TRIFAN_MESH: - switch(renderMode) { - case RENDER_MODE_NORMAL: - glBegin(GL_TRIANGLE_FAN); - break; - case RENDER_MODE_WIREFRAME: - glBegin(GL_LINE_LOOP); - break; - } - break; - case Mesh::QUAD_MESH: - switch(renderMode) { - case RENDER_MODE_NORMAL: - glBegin(GL_QUADS); - break; - case RENDER_MODE_WIREFRAME: - glBegin(GL_LINE_LOOP); - break; - } - break; - case Mesh::LINE_MESH: - glBegin(GL_LINES); - break; - } - */ -} - -void OpenGLES1Renderer::pushMatrix() { - glPushMatrix(); -} - -void OpenGLES1Renderer::popMatrix() { - glPopMatrix(); -} - - -void OpenGLES1Renderer::endRenderOperation() { -// glEnd(); -} - -void OpenGLES1Renderer::draw3DPolygon(Poly::Polygon *polygon) { - unsigned int vCount = polygon->getVertexCount(); - for(int i=0; i < vCount; i++) { - if(polygon->usesFaceUV()) - draw3DVertex(polygon->getVertex(i), polygon->getTexCoord(i)); - else - draw3DVertex(polygon->getVertex(i), NULL); - } -} - -void OpenGLES1Renderer::draw3DVertex2UV(Vertex *vertex, Vector2 *faceUV1, Vector2 *faceUV2) { - /* - if(vertex->useVertexColor) - glColor4f(vertex->vertexColor.r, vertex->vertexColor.g, vertex->vertexColor.b, vertex->vertexColor.a); - - if(currentTexture || currentMaterial) { - glMultiTexCoord4x(GL_TEXTURE0, faceUV1->x, faceUV1->y,0,0); - glMultiTexCoord4x(GL_TEXTURE1, faceUV2->x, faceUV2->y,0,0); - } - - // glNormal3f(vertex->normal->x, vertex->normal->y, vertex->normal->z); - glVertex3f(vertex->x, vertex->y, vertex->z); - */ -} - -void OpenGLES1Renderer::setNormal(const Vector3 &normal) { - glNormal3f(normal.x, normal.y, normal.z); -} - -void OpenGLES1Renderer::draw3DVertex(Vertex *vertex, Vector2 *faceUV) { - /* - if(vertex->useVertexColor) - glColor4f(vertex->vertexColor.r, vertex->vertexColor.g, vertex->vertexColor.b, vertex->vertexColor.a); - - if(currentTexture || currentMaterial) { - if(faceUV != NULL) - glTexCoord2f(faceUV->x, faceUV->y); - else - glTexCoord2f(vertex->getTexCoord()->x, vertex->getTexCoord()->y); - } - - // glNormal3f(vertex->normal->x, vertex->normal->y, vertex->normal->z); - glVertex3f(vertex->x, vertex->y, vertex->z); - */ -} - -void OpenGLES1Renderer::drawScreenQuad(Number qx, Number qy) { - /* - setOrthoMode(); - - Number xscale = qx/((Number)getXRes()) * 2.0f; - Number yscale = qy/((Number)getYRes()) * 2.0f; - - glBegin(GL_QUADS); - glColor4f(1.0f,1.0f,1.0f,1.0f); - - glTexCoord2f(0.0f, 1.0f); - glVertex2f(-1, -1+(1.0f*yscale)); - - glTexCoord2f(0.0f, 0.0f); - glVertex2f(-1.0f, -1.0f); - - glTexCoord2f(1.0f, 0.0f); - glVertex2f(-1+(1.0f*xscale), -1.0f); - - glTexCoord2f(1.0f, 1.0f); - glVertex2f(-1+(1.0f*xscale), -1+(1.0f*yscale)); - glEnd(); - setPerspectiveMode(); - */ -} - -void OpenGLES1Renderer::draw2DVertex(Vertex *vertex) { - /* - // glColor4f(0,0,0,0); - if(vertex->useVertexColor) - glColor4f(vertex->vertexColor.r, vertex->vertexColor.g, vertex->vertexColor.b, vertex->vertexColor.a); - if(currentTexture) - glTexCoord2f(vertex->getTexCoord()->x+currentTexture->getScrollOffsetX(), vertex->getTexCoord()->y+currentTexture->getScrollOffsetY()); - glVertex2f(vertex->x, vertex->y); - */ -} - -void OpenGLES1Renderer::draw3DLine(Vector3 origin, Vector3 direction, Number length, Color color) { - /* - glColor4f(color.r,color.g,color.b,color.a); - // glColor4f(1.0f, 1.0f, 1.0f, 1.0f); - glVertex3f(origin.x, origin.y, origin.z); - - Vector3 lineEnd = origin + (direction * length); - glVertex3f(lineEnd.x, lineEnd.y, lineEnd.z); - */ -} - -void OpenGLES1Renderer::translate2D(Number x, Number y) { - glTranslatef(x, y, 0.0f); -} - -void OpenGLES1Renderer::scale2D(Vector2 *scale) { - glScalef(scale->x, scale->y, 1.0f); -} - -void OpenGLES1Renderer::loadIdentity() { - setBlendingMode(BLEND_MODE_NORMAL); - glMatrixMode(GL_MODELVIEW); - glLoadIdentity(); -} - -void OpenGLES1Renderer::rotate2D(Number angle) { - glRotatef(angle, 0.0f, 0.0f, 1.0f); -} - -void OpenGLES1Renderer::setVertexColor(Number r, Number g, Number b, Number a) { - glColor4f(r,g,b,a); -} - -void OpenGLES1Renderer::draw2DPolygon(Poly::Polygon *polygon) { - unsigned int vCount = polygon->getVertexCount(); - for(int i=0; i < vCount; i++) { - draw2DVertex(polygon->getVertex(i)); - } -} - -void OpenGLES1Renderer::EndRender() { -} - -OpenGLES1Renderer::~OpenGLES1Renderer() { - -} diff --git a/Core/Contents/Source/PolyGLES1Texture.cpp b/Core/Contents/Source/PolyGLES1Texture.cpp deleted file mode 100644 index eb11f1bc1..000000000 --- a/Core/Contents/Source/PolyGLES1Texture.cpp +++ /dev/null @@ -1,86 +0,0 @@ -/* - Copyright (C) 2011 by Ivan Safrin - - 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: - - The above copyright notice and this permission notice shall be included in - all copies or substantial portions of the Software. - - 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. -*/ - -#include "PolyGLES1Texture.h" - -using namespace Polycode; - -OpenGLES1Texture::OpenGLES1Texture(unsigned int width, unsigned int height, char *textureData, bool clamp, int filteringMode) : Texture(width, height, textureData,clamp) { - this->filteringMode = filteringMode; - recreateFromImageData(); -} - -void OpenGLES1Texture::recreateFromImageData() { - glGenTextures(1, &textureID); - glBindTexture(GL_TEXTURE_2D, textureID); - if(clamp) { - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - } else { - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); - } - switch(filteringMode) { - case Renderer::TEX_FILTERING_LINEAR: - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - break; - case Renderer::TEX_FILTERING_NEAREST: - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - break; - } - - if(textureData) - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, textureData); -} - -OpenGLES1Texture::OpenGLES1Texture(unsigned int width, unsigned int height) : Texture(width, height, NULL ,true) { - -} - -void OpenGLES1Texture::setGLInfo(GLuint textureID, GLuint frameBufferID) { - this->textureID = textureID; - this->frameBufferID = frameBufferID; -} - -void OpenGLES1Texture::setTextureData(char *data) { - /* - glBindTexture(GL_TEXTURE_2D, textureID); - glDrawBuffer(GL_AUX0); - glDrawPixels(width, height, GL_RGBA, GL_UNSIGNED_BYTE, data); - glReadBuffer(GL_AUX0); - // glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 0, 0, 128, 128, 0); - */ -} - -OpenGLES1Texture::~OpenGLES1Texture() { - glDeleteTextures(1, &textureID); -} - -GLuint OpenGLES1Texture::getFrameBufferID() { - return frameBufferID; -} - -GLuint OpenGLES1Texture::getTextureID() { - return textureID; -} \ No newline at end of file diff --git a/Core/Contents/Source/PolyGLRenderer.cpp b/Core/Contents/Source/PolyGLRenderer.cpp index d3dca8461..c81d6da9d 100755 --- a/Core/Contents/Source/PolyGLRenderer.cpp +++ b/Core/Contents/Source/PolyGLRenderer.cpp @@ -33,7 +33,6 @@ #include "PolyMaterial.h" #include "PolyMesh.h" #include "PolyModule.h" -#include "PolyPolygon.h" #if defined(_WINDOWS) && !defined(_MINGW) @@ -58,6 +57,7 @@ PFNGLGETBUFFERPOINTERVARBPROC glGetBufferPointervARB; PFNGLVERTEXATTRIBPOINTERPROC glVertexAttribPointer; PFNGLENABLEVERTEXATTRIBARRAYARBPROC glEnableVertexAttribArrayARB; +PFNGLDISABLEVERTEXATTRIBARRAYARBPROC glDisableVertexAttribArrayARB; PFNGLBINDATTRIBLOCATIONPROC glBindAttribLocation; PFNGLGETPROGRAMIVPROC glGetProgramiv; @@ -85,20 +85,19 @@ PFNGLGENERATEMIPMAPEXTPROC glGenerateMipmapEXT; #endif using namespace Polycode; +inline void polycodeGLGetNumberv( GLenum pname, GLdouble *params ) { + glGetDoublev(pname, params); +} +inline void polycodeGLGetNumberv( GLenum pname, GLfloat *params ) { + glGetFloatv(pname, params); +} + OpenGLRenderer::OpenGLRenderer() : Renderer() { - nearPlane = 0.1f; - farPlane = 100.0f; verticesToDraw = 0; } -void OpenGLRenderer::setClippingPlanes(Number nearPlane_, Number farPlane_) { - nearPlane = nearPlane_; - farPlane = farPlane_; - Resize(xRes,yRes); -} - bool OpenGLRenderer::Init() { if(!Renderer::Init()) return false; @@ -130,6 +129,7 @@ void OpenGLRenderer::initOSSpecific(){ glVertexAttribPointer = (PFNGLVERTEXATTRIBPOINTERPROC)wglGetProcAddress("glVertexAttribPointer"); glEnableVertexAttribArrayARB = (PFNGLENABLEVERTEXATTRIBARRAYARBPROC)wglGetProcAddress("glEnableVertexAttribArrayARB"); + glDisableVertexAttribArrayARB = (PFNGLDISABLEVERTEXATTRIBARRAYARBPROC)wglGetProcAddress("glDisableVertexAttribArrayARB"); glBindAttribLocation = (PFNGLBINDATTRIBLOCATIONPROC)wglGetProcAddress("glBindAttribLocation"); @@ -164,30 +164,16 @@ void OpenGLRenderer::Resize(int xRes, int yRes) { viewportHeight = xRes; glClearColor(clearColor.r, clearColor.g, clearColor.b, clearColor.a); glClearDepth(1.0f); - - glMatrixMode(GL_PROJECTION); - glLoadIdentity(); - resetViewport(); - - glMatrixMode(GL_MODELVIEW); glLineWidth(1); - glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - - glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); - glEnable(GL_BLEND); - glShadeModel(GL_SMOOTH); glDepthFunc( GL_LEQUAL ); glEnable(GL_DEPTH_TEST); glLineWidth(1.0f); - -// glEnable(GL_LINE_SMOOTH); - + glEnable(GL_VERTEX_PROGRAM_POINT_SIZE); GLint numBuffers; glGetIntegerv(GL_MAX_DRAW_BUFFERS, &numBuffers); -// Logger::log("MAX_DRAW_BUFFERS: %d \n", numBuffers); } @@ -204,13 +190,21 @@ void OpenGLRenderer::setDepthFunction(int depthFunction) { void OpenGLRenderer::enableAlphaTest(bool val) { if(val) { - glAlphaFunc ( GL_GREATER, 0.01) ; + glAlphaFunc ( GL_GREATER, alphaTestValue) ; glEnable ( GL_ALPHA_TEST ) ; } else { glDisable( GL_ALPHA_TEST ) ; } } +void OpenGLRenderer::setPointSmooth(bool val) { + if(val) + glEnable( GL_POINT_SMOOTH ); + else + glDisable( GL_POINT_SMOOTH ); + +} + void OpenGLRenderer::setLineSmooth(bool val) { if(val) glEnable(GL_LINE_SMOOTH); @@ -218,77 +212,125 @@ void OpenGLRenderer::setLineSmooth(bool val) { glDisable(GL_LINE_SMOOTH); } -void OpenGLRenderer::resetViewport() { +void OpenGLRenderer::setProjectionFromFrustum(Number left, Number right, Number bottom, Number top, Number front, Number back) { glMatrixMode(GL_PROJECTION); glLoadIdentity(); - //gluPerspective(fov,(GLfloat)viewportWidth/(GLfloat)viewportHeight,nearPlane,farPlane); - Number fW, fH; - fH = tan( fov / 360.0 * PI ) * nearPlane; - fW = fH * ((GLfloat)viewportWidth/(GLfloat)viewportHeight); - glFrustum(-fW + (viewportShift.x*fW*2.0), fW + (viewportShift.x*fW*2.0), -fH + (viewportShift.y*fH*2.0), fH + (viewportShift.y*fH*2.0), nearPlane, farPlane); - - glViewport(0, 0, viewportWidth, viewportHeight); - glScissor(0, 0, viewportWidth, viewportHeight); - glMatrixMode(GL_MODELVIEW); - glGetDoublev( GL_PROJECTION_MATRIX, sceneProjectionMatrix); + glFrustum(left, right, bottom, top, front, back); + glMatrixMode(GL_MODELVIEW); + polycodeGLGetNumberv(GL_PROJECTION_MATRIX, sceneProjectionMatrix); + } -Vector3 OpenGLRenderer::Unproject(Number x, Number y) { +void OpenGLRenderer::setProjectionFromFoV(Number fov, Number _near, Number _far) { + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + Number fW, fH; + fH = tan( fov / 360.0 * PI ) * _near; + fW = fH * ((GLfloat)viewportWidth/(GLfloat)viewportHeight); + glFrustum(-fW + (viewportShift.x*fW*2.0), fW + (viewportShift.x*fW*2.0), -fH + (viewportShift.y*fH*2.0), fH + (viewportShift.y*fH*2.0), _near, _far); + glMatrixMode(GL_MODELVIEW); + polycodeGLGetNumberv(GL_PROJECTION_MATRIX, sceneProjectionMatrix); +} + +void OpenGLRenderer::resetViewport() { + glViewport(0, 0, viewportWidth*backingResolutionScaleX, viewportHeight*backingResolutionScaleY); + glScissor(0, 0, viewportWidth*backingResolutionScaleX, viewportHeight*backingResolutionScaleY); +} + +Vector3 OpenGLRenderer::Unproject(Number x, Number y, const Matrix4 &cameraMatrix, const Matrix4 &projectionMatrix, const Polycode::Rectangle &viewport) { Vector3 coords; GLfloat wx, wy, wz; GLdouble cx, cy, cz; - + GLdouble mv[16]; - glGetDoublev( GL_MODELVIEW_MATRIX, mv ); - - GLdouble proj[16]; - glGetDoublev( GL_PROJECTION_MATRIX, proj ); - - GLint vp[4]; - glGetIntegerv( GL_VIEWPORT, vp ); + Matrix4 camInverse = cameraMatrix.Inverse(); + Matrix4 cmv; + cmv.identity(); + cmv = cmv * camInverse; + + for(int i=0; i < 16; i++) { + mv[i] = cmv.ml[i]; + } + + GLint vp[4] = {viewport.x, viewport.y, viewport.w, viewport.h}; + + GLdouble _sceneProjectionMatrix[16]; + for(int i=0; i < 16; i++) { + _sceneProjectionMatrix[i] = projectionMatrix.ml[i]; + } wx = ( Number ) x; wy = ( Number ) vp[3] - ( Number ) y; - glReadPixels( x, wy, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &wz ); + glReadPixels( x * backingResolutionScaleX, wy * backingResolutionScaleY, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &wz ); - gluUnProject( wx, wy, wz, mv, proj, vp, &cx, &cy, &cz ); + gluUnProject( wx, wy, wz, mv, _sceneProjectionMatrix, vp, &cx, &cy, &cz ); coords = Vector3( cx, cy, cz ); - return coords; - + return coords; } -Vector3 OpenGLRenderer::projectRayFrom2DCoordinate(Number x, Number y, Matrix4 cameraMatrix, Matrix4 projectionMatrix) { - GLdouble nearPlane[3],farPlane[3]; - +Vector2 OpenGLRenderer::Project(const Matrix4 &cameraMatrix, const Matrix4 &projectionMatrix, const Polycode::Rectangle &viewport, const Vector3 &coordiante) const { + GLdouble mv[16]; Matrix4 camInverse = cameraMatrix.Inverse(); Matrix4 cmv; cmv.identity(); cmv = cmv * camInverse; - + for(int i=0; i < 16; i++) { mv[i] = cmv.ml[i]; } - - GLint vp[4]; - glGetIntegerv( GL_VIEWPORT, vp ); - + + GLint vp[4] = {viewport.x, viewport.y, viewport.w, viewport.h}; + GLdouble _sceneProjectionMatrix[16]; for(int i=0; i < 16; i++) { _sceneProjectionMatrix[i] = projectionMatrix.ml[i]; } + + GLdouble coords[3]; - gluUnProject(x, yRes - y, 0.0, mv, _sceneProjectionMatrix, vp, &nearPlane[0], &nearPlane[1], &nearPlane[2]); - gluUnProject(x, yRes - y, 1.0, mv, _sceneProjectionMatrix, vp, &farPlane[0], &farPlane[1], &farPlane[2]); + gluProject(coordiante.x, coordiante.y, coordiante.z, mv, _sceneProjectionMatrix, vp, &coords[0], &coords[1], &coords[2]); + return Vector2(coords[0] / backingResolutionScaleX, (viewport.h-coords[1]) / backingResolutionScaleY); +} + +Polycode::Rectangle OpenGLRenderer::getViewport() { + GLint vp[4]; + glGetIntegerv( GL_VIEWPORT, vp ); + return Polycode::Rectangle(vp[0], vp[1], vp[2], vp[3]); +} + +Vector3 OpenGLRenderer::projectRayFrom2DCoordinate(Number x, Number y, const Matrix4 &cameraMatrix, const Matrix4 &projectionMatrix, const Polycode::Rectangle &viewport) { + GLdouble nearPlane[3],farPlane[3]; + + GLdouble mv[16]; + Matrix4 camInverse = cameraMatrix.Inverse(); + Matrix4 cmv; + cmv.identity(); + cmv = cmv * camInverse; + + for(int i=0; i < 16; i++) { + mv[i] = cmv.ml[i]; + } + + GLint vp[4] = {viewport.x, viewport.y, viewport.w, viewport.h}; + + GLdouble _sceneProjectionMatrix[16]; + for(int i=0; i < 16; i++) { + _sceneProjectionMatrix[i] = projectionMatrix.ml[i]; + } + + gluUnProject(x, (yRes*backingResolutionScaleY) - y, 0.0, mv, _sceneProjectionMatrix, vp, &nearPlane[0], &nearPlane[1], &nearPlane[2]); + gluUnProject(x, (yRes*backingResolutionScaleY) - y, 1.0, mv, _sceneProjectionMatrix, vp, &farPlane[0], &farPlane[1], &farPlane[2]); + Vector3 nearVec(nearPlane[0], nearPlane[1], nearPlane[2]); Vector3 farVec(farPlane[0], farPlane[1], farPlane[2]); - + Vector3 dirVec = (farVec) - (nearVec); dirVec.Normalize(); - + return dirVec; } @@ -306,13 +348,29 @@ void OpenGLRenderer::enableDepthTest(bool val) { glDisable(GL_DEPTH_TEST); } +inline void loadMatrixNumber(const GLfloat* m) { + glLoadMatrixf(m); +} + +inline void loadMatrixNumber(const GLdouble* m) { + glLoadMatrixd(m); +} + +inline void multMatrixNumber(const GLfloat* m) { + glMultMatrixf(m); +} + +inline void multMatrixNumber(const GLdouble* m) { + glMultMatrixd(m); +} + void OpenGLRenderer::setModelviewMatrix(Matrix4 m) { - glLoadMatrixd(m.ml); + loadMatrixNumber(m.ml); } void OpenGLRenderer::multModelviewMatrix(Matrix4 m) { // glMatrixMode(GL_MODELVIEW); - glMultMatrixd(m.ml); + multMatrixNumber(m.ml); } void OpenGLRenderer::enableLighting(bool enable) { @@ -323,69 +381,71 @@ void OpenGLRenderer::setLineSize(Number lineSize) { glLineWidth(lineSize); } -void OpenGLRenderer::createVertexBufferForMesh(Mesh *mesh) { +void OpenGLRenderer::setPointSize(Number pointSize) { + glPointSize(pointSize); +} + +VertexBuffer *OpenGLRenderer::createVertexBufferForMesh(Mesh *mesh) { OpenGLVertexBuffer *buffer = new OpenGLVertexBuffer(mesh); - mesh->setVertexBuffer(buffer); + return buffer; } void OpenGLRenderer::drawVertexBuffer(VertexBuffer *buffer, bool enableColorBuffer) { OpenGLVertexBuffer *glVertexBuffer = (OpenGLVertexBuffer*)buffer; glEnableClientState(GL_VERTEX_ARRAY); - glEnableClientState(GL_TEXTURE_COORD_ARRAY); - glEnableClientState(GL_NORMAL_ARRAY); - - if(enableColorBuffer) { - glEnableClientState(GL_COLOR_ARRAY); - + glBindBufferARB( GL_ARRAY_BUFFER_ARB, glVertexBuffer->getVertexBufferID()); + glVertexPointer( 3, GL_FLOAT, 0, (char *) NULL ); + + if(enableColorBuffer && glVertexBuffer->getColorBufferID() != -1) { + glEnableClientState(GL_COLOR_ARRAY); glBindBufferARB( GL_ARRAY_BUFFER_ARB, glVertexBuffer->getColorBufferID()); glColorPointer( 4, GL_FLOAT, 0, (char *) NULL ); } - glBindBufferARB( GL_ARRAY_BUFFER_ARB, glVertexBuffer->getVertexBufferID()); - glVertexPointer( 3, GL_FLOAT, 0, (char *) NULL ); - glBindBufferARB( GL_ARRAY_BUFFER_ARB, glVertexBuffer->getNormalBufferID()); - glNormalPointer(GL_FLOAT, 0, (char *) NULL ); - glBindBufferARB( GL_ARRAY_BUFFER_ARB, glVertexBuffer->getTextCoordBufferID()); - glTexCoordPointer( 2, GL_FLOAT, 0, (char *) NULL ); - - glBindBufferARB( GL_ARRAY_BUFFER_ARB, glVertexBuffer->getTangentBufferID()); - glEnableVertexAttribArrayARB(6); - glVertexAttribPointer(6, 3, GL_FLOAT, 0, 0, (char *)NULL); - - - + + if(glVertexBuffer->getNormalBufferID() != -1) { + glEnableClientState(GL_NORMAL_ARRAY); + glBindBufferARB( GL_ARRAY_BUFFER_ARB, glVertexBuffer->getNormalBufferID()); + glNormalPointer(GL_FLOAT, 0, (char *) NULL ); + } + + if(glVertexBuffer->getTextCoordBufferID() != -1) { + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + glBindBufferARB( GL_ARRAY_BUFFER_ARB, glVertexBuffer->getTextCoordBufferID()); + glTexCoordPointer( 2, GL_FLOAT, 0, (char *) NULL ); + } + + if(glVertexBuffer->getTangentBufferID() != -1) { + glBindBufferARB( GL_ARRAY_BUFFER_ARB, glVertexBuffer->getTangentBufferID()); + glEnableVertexAttribArrayARB(6); + glVertexAttribPointer(6, 3, GL_FLOAT, 0, 0, (char *)NULL); + } + + if(glVertexBuffer->getBoneWeightBufferID() != -1) { + + glBindBufferARB( GL_ARRAY_BUFFER_ARB, glVertexBuffer->getBoneWeightBufferID()); + glEnableVertexAttribArrayARB(7); + glVertexAttribPointer(7, 4, GL_FLOAT, 0, 0, (char *)NULL); + } + + if(glVertexBuffer->getBoneIndexBufferID() != -1) { + + glBindBufferARB( GL_ARRAY_BUFFER_ARB, glVertexBuffer->getBoneIndexBufferID()); + glEnableVertexAttribArrayARB(8); + glVertexAttribPointer(8, 4, GL_FLOAT, 0, 0, (char *)NULL); + } + GLenum mode = GL_TRIANGLES; switch(buffer->meshType) { case Mesh::TRI_MESH: - switch(renderMode) { - case RENDER_MODE_NORMAL: - mode = GL_TRIANGLES; - break; - case RENDER_MODE_WIREFRAME: - mode = GL_LINE_LOOP; - break; - } + mode = GL_TRIANGLES; break; case Mesh::TRIFAN_MESH: - switch(renderMode) { - case RENDER_MODE_NORMAL: - mode = GL_TRIANGLE_FAN; - break; - case RENDER_MODE_WIREFRAME: - mode = GL_LINE_LOOP; - break; - } + mode = GL_TRIANGLE_FAN; break; case Mesh::QUAD_MESH: - switch(renderMode) { - case RENDER_MODE_NORMAL: - mode = GL_QUADS; - break; - case RENDER_MODE_WIREFRAME: - mode = GL_LINE_LOOP; - break; - } + mode = GL_QUADS; break; case Mesh::LINE_STRIP_MESH: mode = GL_LINE_STRIP; @@ -401,15 +461,22 @@ void OpenGLRenderer::drawVertexBuffer(VertexBuffer *buffer, bool enableColorBuff break; } - glDrawArrays( mode, 0, buffer->getVertexCount() ); - - glDisableClientState( GL_VERTEX_ARRAY); + if(glVertexBuffer->getIndexBufferID() != -1) { + glBindBufferARB( GL_ELEMENT_ARRAY_BUFFER_ARB, glVertexBuffer->getIndexBufferID()); + glDrawElements(mode, glVertexBuffer->getIndexCount(), GL_UNSIGNED_INT, (void*)0); + glBindBufferARB( GL_ELEMENT_ARRAY_BUFFER_ARB, 0); + } else { + glDrawArrays( mode, 0, buffer->getVertexCount() ); + } + + glDisableVertexAttribArrayARB(6); + glDisableVertexAttribArrayARB(7); + glDisableVertexAttribArrayARB(8); + + glDisableClientState( GL_VERTEX_ARRAY); glDisableClientState( GL_TEXTURE_COORD_ARRAY ); glDisableClientState( GL_NORMAL_ARRAY ); - - if(enableColorBuffer) { - glDisableClientState( GL_COLOR_ARRAY ); - } + glDisableClientState( GL_COLOR_ARRAY ); } void OpenGLRenderer::enableScissor(bool val) { @@ -422,7 +489,7 @@ void OpenGLRenderer::enableScissor(bool val) { } void OpenGLRenderer::setScissorBox(Polycode::Rectangle box) { - glScissor(box.x, yRes-box.y-box.h, box.w, box.h); + glScissor(box.x*backingResolutionScaleX, (((yRes*backingResolutionScaleY)-(box.y*backingResolutionScaleY))-(box.h*backingResolutionScaleY)), box.w *backingResolutionScaleX, box.h * backingResolutionScaleY); Renderer::setScissorBox(box); } @@ -436,6 +503,13 @@ void OpenGLRenderer::enableFog(bool enable) { } void OpenGLRenderer::setBlendingMode(int blendingMode) { + + if(blendingMode == BLEND_MODE_NONE) { + glDisable(GL_BLEND); + } else { + glEnable(GL_BLEND); + } + switch(blendingMode) { case BLEND_MODE_NORMAL: if(blendNormalAsPremultiplied) { @@ -460,26 +534,41 @@ void OpenGLRenderer::setBlendingMode(int blendingMode) { glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); break; } - glEnable(GL_BLEND); } Matrix4 OpenGLRenderer::getProjectionMatrix() { Number m[16]; - glGetDoublev( GL_PROJECTION_MATRIX, m); + polycodeGLGetNumberv( GL_PROJECTION_MATRIX, m); return Matrix4(m); } Matrix4 OpenGLRenderer::getModelviewMatrix() { Number m[16]; - glGetDoublev( GL_MODELVIEW_MATRIX, m); + polycodeGLGetNumberv( GL_MODELVIEW_MATRIX, m); return Matrix4(m); } +Image *OpenGLRenderer::renderBufferToImage(Texture *texture) { + + OpenGLTexture *glTexture = (OpenGLTexture*)texture; + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, glTexture->getFrameBufferID()); + glReadBuffer(GL_COLOR_ATTACHMENT0_EXT); + + char *imageBuffer = (char*)malloc(texture->getWidth() * backingResolutionScaleX * texture->getHeight() * backingResolutionScaleY * 4); + glReadPixels(0, 0, texture->getWidth() * backingResolutionScaleX, texture->getHeight() * backingResolutionScaleY, GL_RGBA, GL_UNSIGNED_BYTE, imageBuffer); + Image *retImage = new Image(imageBuffer, texture->getWidth() * backingResolutionScaleX, texture->getHeight() * backingResolutionScaleY, Image::IMAGE_RGBA); + free(imageBuffer); + + unbindFramebuffers(); + return retImage; +} + Image *OpenGLRenderer::renderScreenToImage() { - glReadBuffer(GL_FRONT); - char *imageBuffer = (char*)malloc(xRes * yRes * 4); - glReadPixels(0, 0, xRes, yRes, GL_RGBA, GL_UNSIGNED_BYTE, imageBuffer); - Image *retImage = new Image(imageBuffer, xRes, yRes, Image::IMAGE_RGBA); + glReadBuffer(GL_FRONT); + + char *imageBuffer = (char*)malloc(xRes* backingResolutionScaleX * yRes * backingResolutionScaleY * 4); + glReadPixels(0, 0, xRes * backingResolutionScaleX, yRes * backingResolutionScaleY, GL_RGBA, GL_UNSIGNED_BYTE, imageBuffer); + Image *retImage = new Image(imageBuffer, xRes * backingResolutionScaleX, yRes * backingResolutionScaleY, Image::IMAGE_RGBA); free(imageBuffer); return retImage; } @@ -508,47 +597,31 @@ void OpenGLRenderer::setFogProperties(int fogMode, Color color, Number density, glFogf(GL_FOG_END, endDepth); } - -void OpenGLRenderer::_setOrthoMode(Number orthoSizeX, Number orthoSizeY) { - this->orthoSizeX = orthoSizeX; - this->orthoSizeY = orthoSizeY; - - if(!orthoMode) { - glMatrixMode(GL_PROJECTION); - glLoadIdentity(); - glOrtho(-orthoSizeX*0.5,orthoSizeX*0.5,-orthoSizeY*0.5,orthoSizeY*0.5,-farPlane,farPlane); - orthoMode = true; - } - glGetDoublev( GL_PROJECTION_MATRIX, sceneProjectionMatrixOrtho); - - glMatrixMode(GL_MODELVIEW); - glLoadIdentity(); -} - -void OpenGLRenderer::setOrthoMode(Number xSize, Number ySize, bool centered) { - - if(xSize == 0) - xSize = xRes; - - if(ySize == 0) - ySize = yRes; - - setBlendingMode(BLEND_MODE_NORMAL); - glDisable(GL_LIGHTING); +void OpenGLRenderer::setProjectionOrtho(Number xSize, Number ySize, Number _near, Number _far, bool centered) { glMatrixMode(GL_PROJECTION); - glDisable(GL_CULL_FACE); glLoadIdentity(); if(centered) { - glOrtho(-xSize*0.5,xSize*0.5,ySize*0.5,-ySize*0.5,-1.0f,1.0f); + glOrtho(-xSize*0.5, xSize*0.5, -ySize*0.5, ySize*0.5, _near, _far); } else { - glOrtho(0.0f,xSize,ySize,0,-1.0f,1.0f); + glOrtho(0.0f, xSize, 0, ySize, _near, _far); } - orthoMode = true; + polycodeGLGetNumberv( GL_PROJECTION_MATRIX, sceneProjectionMatrixOrtho); + glMatrixMode(GL_MODELVIEW); glLoadIdentity(); } +void OpenGLRenderer::setProjectionMatrix(Matrix4 matrix) { + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + + loadMatrixNumber(matrix.ml); + + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); +} + void OpenGLRenderer::enableBackfaceCulling(bool val) { if(val) glEnable(GL_CULL_FACE); @@ -556,24 +629,20 @@ void OpenGLRenderer::enableBackfaceCulling(bool val) { glDisable(GL_CULL_FACE); } -void OpenGLRenderer::setPerspectiveMode() { - setBlendingMode(BLEND_MODE_NORMAL); - if(orthoMode) { - if(lightingEnabled) { - } - glEnable (GL_DEPTH_TEST); - glEnable(GL_CULL_FACE); - glMatrixMode( GL_PROJECTION ); - glMatrixMode( GL_MODELVIEW ); - orthoMode = false; - } +void OpenGLRenderer::setPerspectiveDefaults() { + glEnable(GL_DEPTH_TEST); + glEnable(GL_CULL_FACE); + glMatrixMode(GL_MODELVIEW); glLoadIdentity(); - glGetDoublev( GL_PROJECTION_MATRIX, sceneProjectionMatrix); + polycodeGLGetNumberv( GL_PROJECTION_MATRIX, sceneProjectionMatrix); currentTexture = NULL; } void OpenGLRenderer::BeginRender() { + + Renderer::BeginRender(); + if(doClearBuffer) { glClearColor(clearColor.r, clearColor.g, clearColor.b, clearColor.a); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); @@ -582,16 +651,16 @@ void OpenGLRenderer::BeginRender() { currentTexture = NULL; } -void OpenGLRenderer::translate3D(Vector3 *position) { - glTranslatef(position->x, position->y, position->z); +void OpenGLRenderer::translate3D(const Vector3 &position) { + glTranslatef(position.x, position.y, position.z); } void OpenGLRenderer::translate3D(Number x, Number y, Number z) { glTranslatef(x, y, z); } -void OpenGLRenderer::scale3D(Vector3 *scale) { - glScalef(scale->x, scale->y, scale->z); +void OpenGLRenderer::scale3D(const Vector3 &scale) { + glScalef(scale.x, scale.y, scale.z); } void OpenGLRenderer::bindFrameBufferTextureDepth(Texture *texture) { @@ -599,7 +668,8 @@ void OpenGLRenderer::bindFrameBufferTextureDepth(Texture *texture) { return; OpenGLTexture *glTexture = (OpenGLTexture*)texture; glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, glTexture->getFrameBufferID()); - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + Renderer::bindFrameBufferTextureDepth(texture); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); } @@ -608,12 +678,14 @@ void OpenGLRenderer::bindFrameBufferTexture(Texture *texture) { return; OpenGLTexture *glTexture = (OpenGLTexture*)texture; glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, glTexture->getFrameBufferID()); - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + Renderer::bindFrameBufferTexture(texture); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); } void OpenGLRenderer::unbindFramebuffers() { glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, 0); + Renderer::unbindFramebuffers(); } void OpenGLRenderer::createRenderTextures(Texture **colorBuffer, Texture **depthBuffer, int width, int height, bool floatingPointBuffer) { @@ -627,15 +699,20 @@ void OpenGLRenderer::createRenderTextures(Texture **colorBuffer, Texture **depth glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, frameBufferID); glGenTextures(1,&colorTexture); glBindTexture(GL_TEXTURE_2D,colorTexture); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + if(textureFilteringMode == TEX_FILTERING_NEAREST) { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + } else { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + } glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); if(floatingPointBuffer) { - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F_ARB, width, height, 0, GL_RGBA, GL_FLOAT, NULL); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F_ARB, width* backingResolutionScaleX, height* backingResolutionScaleY, 0, GL_RGBA, GL_FLOAT, NULL); } else { - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width * backingResolutionScaleX, height * backingResolutionScaleY, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); } glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, colorTexture, 0); @@ -657,8 +734,13 @@ void OpenGLRenderer::createRenderTextures(Texture **colorBuffer, Texture **depth glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, frameBufferID); glGenTextures(1,&depthTexture); glBindTexture(GL_TEXTURE_2D,depthTexture); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + if(textureFilteringMode == TEX_FILTERING_NEAREST) { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + } else { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + } glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER); glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER); @@ -667,9 +749,9 @@ void OpenGLRenderer::createRenderTextures(Texture **colorBuffer, Texture **depth glTexParameteri(GL_TEXTURE_2D, GL_DEPTH_TEXTURE_MODE, GL_LUMINANCE); if(floatingPointBuffer) { - glTexImage2D(GL_TEXTURE_2D,0,GL_DEPTH_COMPONENT16,width,height,0,GL_DEPTH_COMPONENT,GL_FLOAT,0); + glTexImage2D(GL_TEXTURE_2D,0,GL_DEPTH_COMPONENT16,width* backingResolutionScaleX,height * backingResolutionScaleY,0,GL_DEPTH_COMPONENT,GL_FLOAT,0); } else { - glTexImage2D(GL_TEXTURE_2D,0,GL_DEPTH_COMPONENT,width,height,0,GL_DEPTH_COMPONENT,GL_UNSIGNED_BYTE,0); + glTexImage2D(GL_TEXTURE_2D,0,GL_DEPTH_COMPONENT,width* backingResolutionScaleX,height* backingResolutionScaleY,0,GL_DEPTH_COMPONENT,GL_UNSIGNED_BYTE,0); } glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_TEXTURE_2D, depthTexture, 0); @@ -705,13 +787,30 @@ Texture *OpenGLRenderer::createTexture(unsigned int width, unsigned int height, return newTexture; } +void OpenGLRenderer::destroyVertexBuffer(VertexBuffer *buffer) { + OpenGLVertexBuffer *glBuffer = (OpenGLVertexBuffer*)buffer; + delete glBuffer; +} + void OpenGLRenderer::destroyTexture(Texture *texture) { OpenGLTexture *glTex = (OpenGLTexture*)texture; delete glTex; } -void OpenGLRenderer::clearScreen() { - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); +void OpenGLRenderer::clearScreen(bool useClearColor, bool useClearDepth) { + if (useClearColor) { + glClearColor(clearColor.r, clearColor.g, clearColor.b, clearColor.a); + } + GLbitfield mask = 0; + if (useClearColor) { + mask |= GL_COLOR_BUFFER_BIT; + } + if (useClearDepth) { + mask |= GL_DEPTH_BUFFER_BIT; + } + if (mask) { + glClear(mask); + } } void OpenGLRenderer::drawToColorBuffer(bool val) { @@ -746,41 +845,6 @@ void OpenGLRenderer::clearBuffer(bool colorBuffer, bool depthBuffer) { glClear(clearMask); } -void OpenGLRenderer::applyMaterial(Material *material, ShaderBinding *localOptions,unsigned int shaderIndex) { - if(!material->getShader(shaderIndex) || !shadersEnabled) { - setTexture(NULL); - return; - } - - FixedShaderBinding *fBinding; - - switch(material->getShader(shaderIndex)->getType()) { - case Shader::FIXED_SHADER: -// FixedShader *fShader = (FixedShader*)material->getShader(); - fBinding = (FixedShaderBinding*)material->getShaderBinding(shaderIndex); - setTexture(fBinding->getDiffuseTexture()); -// setTexture(fShader->getDiffuseTexture()); - break; - case Shader::MODULE_SHADER: - currentMaterial = material; - if(material->shaderModule == NULL) { - for(int m=0; m < shaderModules.size(); m++) { - PolycodeShaderModule *shaderModule = shaderModules[m]; - if(shaderModule->hasShader(material->getShader(shaderIndex))) { - material->shaderModule = (void*)shaderModule; - } - } - } else { - PolycodeShaderModule *shaderModule = (PolycodeShaderModule*)material->shaderModule; - shaderModule->applyShaderMaterial(this, material, localOptions, shaderIndex); - currentShaderModule = shaderModule; - } - break; - } - - setBlendingMode(material->blendingMode); -} - void OpenGLRenderer::clearShader() { glDisable(GL_COLOR_MATERIAL); @@ -807,234 +871,91 @@ void OpenGLRenderer::setTexture(Texture *texture) { return; } - if(renderMode == RENDER_MODE_NORMAL) { - glActiveTexture(GL_TEXTURE0); - glEnable (GL_TEXTURE_2D); - - if(currentTexture != texture) { - OpenGLTexture *glTexture = (OpenGLTexture*)texture; - glBindTexture (GL_TEXTURE_2D, glTexture->getTextureID()); - } - } else { - glDisable(GL_TEXTURE_2D); - } + glActiveTexture(GL_TEXTURE0); + glEnable (GL_TEXTURE_2D); + + if(currentTexture != texture) { + OpenGLTexture *glTexture = (OpenGLTexture*)texture; + glBindTexture (GL_TEXTURE_2D, glTexture->getTextureID()); + } currentTexture = texture; } void OpenGLRenderer::pushMatrix() { + glMatrixMode(GL_MODELVIEW); glPushMatrix(); } void OpenGLRenderer::popMatrix() { + glMatrixMode(GL_MODELVIEW); glPopMatrix(); } void OpenGLRenderer::pushRenderDataArray(RenderDataArray *array) { - - switch(array->arrayType) { + switch(array->type) { case RenderDataArray::VERTEX_DATA_ARRAY: - glEnableClientState(GL_VERTEX_ARRAY); + glEnableClientState(GL_VERTEX_ARRAY); glBindBufferARB( GL_ARRAY_BUFFER_ARB, 0); - glVertexPointer(array->size, GL_FLOAT, 0, array->arrayPtr); - verticesToDraw = array->count; + glVertexPointer(3, GL_FLOAT, 0, array->getArrayData()); + verticesToDraw = array->getDataSize() / 3; break; - case RenderDataArray::COLOR_DATA_ARRAY: - glColorPointer(array->size, GL_FLOAT, 0, array->arrayPtr); + case RenderDataArray::COLOR_DATA_ARRAY: + if(array->getDataSize() != verticesToDraw * 4) { + return; + } + glColorPointer(4, GL_FLOAT, 0, array->getArrayData()); glEnableClientState(GL_COLOR_ARRAY); break; case RenderDataArray::TEXCOORD_DATA_ARRAY: + if(array->getDataSize() != verticesToDraw * 2) { + return; + } glEnableClientState(GL_TEXTURE_COORD_ARRAY); glBindBufferARB( GL_ARRAY_BUFFER_ARB, 0); - glTexCoordPointer(array->size, GL_FLOAT, 0, array->arrayPtr); + glTexCoordPointer(2, GL_FLOAT, 0, array->getArrayData()); break; case RenderDataArray::NORMAL_DATA_ARRAY: + if(array->getDataSize() != verticesToDraw * 3) { + return; + } glEnableClientState(GL_NORMAL_ARRAY); glBindBufferARB( GL_ARRAY_BUFFER_ARB, 0); - glNormalPointer(GL_FLOAT, 0, array->arrayPtr); + glNormalPointer(GL_FLOAT, 0, array->getArrayData()); break; case RenderDataArray::TANGENT_DATA_ARRAY: + if(array->getDataSize() != verticesToDraw * 3) { + return; + } glEnableVertexAttribArrayARB(6); - glVertexAttribPointer(6, array->size, GL_FLOAT, 0, 0, array->arrayPtr); + glVertexAttribPointer(6, 3, GL_FLOAT, 0, 0, array->getArrayData()); break; } } -RenderDataArray *OpenGLRenderer::createRenderDataArrayForMesh(Mesh *mesh, int arrayType) { - RenderDataArray *newArray = createRenderDataArray(arrayType); - - newArray->count = 0; - long bufferSize = 0; - long newBufferSize = 0; - GLfloat* buffer = NULL; - - switch (arrayType) { - case RenderDataArray::VERTEX_DATA_ARRAY: - { - buffer = (GLfloat*)malloc(1); - for(int i=0; i < mesh->getPolygonCount(); i++) { - for(int j=0; j < mesh->getPolygon(i)->getVertexCount(); j++) { - newArray->count++; - newBufferSize = bufferSize + 3; - buffer = (GLfloat*)realloc(buffer, newBufferSize * sizeof(GLfloat)); - buffer[bufferSize+0] = mesh->getPolygon(i)->getVertex(j)->x; - buffer[bufferSize+1] = mesh->getPolygon(i)->getVertex(j)->y; - buffer[bufferSize+2] = mesh->getPolygon(i)->getVertex(j)->z; - bufferSize = newBufferSize; - } - } - } - break; - case RenderDataArray::COLOR_DATA_ARRAY: - { - buffer = (GLfloat*)malloc(1); - for(int i=0; i < mesh->getPolygonCount(); i++) { - for(int j=0; j < mesh->getPolygon(i)->getVertexCount(); j++) { - newBufferSize = bufferSize + 4; - buffer = (GLfloat*)realloc(buffer, newBufferSize * sizeof(GLfloat)); - buffer[bufferSize+0] = mesh->getPolygon(i)->getVertex(j)->vertexColor.r; - buffer[bufferSize+1] = mesh->getPolygon(i)->getVertex(j)->vertexColor.g; - buffer[bufferSize+2] = mesh->getPolygon(i)->getVertex(j)->vertexColor.b; - buffer[bufferSize+3] = mesh->getPolygon(i)->getVertex(j)->vertexColor.a; - bufferSize = newBufferSize; - } - } - } - break; - case RenderDataArray::NORMAL_DATA_ARRAY: - { - buffer = (GLfloat*)malloc(1); - - for(int i=0; i < mesh->getPolygonCount(); i++) { - for(int j=0; j < mesh->getPolygon(i)->getVertexCount(); j++) { - newBufferSize = bufferSize + 3; - buffer = (GLfloat*)realloc(buffer, newBufferSize * sizeof(GLfloat)); - if(mesh->getPolygon(i)->useVertexNormals) { - buffer[bufferSize+0] = mesh->getPolygon(i)->getVertex(j)->normal.x; - buffer[bufferSize+1] = mesh->getPolygon(i)->getVertex(j)->normal.y; - buffer[bufferSize+2] = mesh->getPolygon(i)->getVertex(j)->normal.z; - } else { - buffer[bufferSize+0] = mesh->getPolygon(i)->getFaceNormal().x; - buffer[bufferSize+1] = mesh->getPolygon(i)->getFaceNormal().y; - buffer[bufferSize+2] = mesh->getPolygon(i)->getFaceNormal().z; - } - bufferSize = newBufferSize; - } - } - } - break; - case RenderDataArray::TANGENT_DATA_ARRAY: - { - buffer = (GLfloat*)malloc(1); - - for(int i=0; i < mesh->getPolygonCount(); i++) { - for(int j=0; j < mesh->getPolygon(i)->getVertexCount(); j++) { - newBufferSize = bufferSize + 3; - buffer = (GLfloat*)realloc(buffer, newBufferSize * sizeof(GLfloat)); - buffer[bufferSize+0] = mesh->getPolygon(i)->getVertex(j)->tangent.x; - buffer[bufferSize+1] = mesh->getPolygon(i)->getVertex(j)->tangent.y; - buffer[bufferSize+2] = mesh->getPolygon(i)->getVertex(j)->tangent.z; - bufferSize = newBufferSize; - } - } - } - break; - case RenderDataArray::TEXCOORD_DATA_ARRAY: - { - buffer = (GLfloat*)malloc(1); - for(int i=0; i < mesh->getPolygonCount(); i++) { - for(int j=0; j < mesh->getPolygon(i)->getVertexCount(); j++) { - newBufferSize = bufferSize + 2; - buffer = (GLfloat*)realloc(buffer, newBufferSize * sizeof(GLfloat)); - buffer[bufferSize+0] = mesh->getPolygon(i)->getVertex(j)->getTexCoord().x; - buffer[bufferSize+1] = mesh->getPolygon(i)->getVertex(j)->getTexCoord().y; - bufferSize = newBufferSize; - } - } - } - break; - default: - break; - } - - if(buffer != NULL) { - free(newArray->arrayPtr); - newArray->arrayPtr = buffer; - } - - return newArray; -} - -RenderDataArray *OpenGLRenderer::createRenderDataArray(int arrayType) { - RenderDataArray *newArray = new RenderDataArray(); - newArray->arrayType = arrayType; - newArray->arrayPtr = malloc(1); - newArray->stride = 0; - newArray->count = 0; - - switch (arrayType) { - case RenderDataArray::VERTEX_DATA_ARRAY: - newArray->size = 3; - break; - case RenderDataArray::COLOR_DATA_ARRAY: - newArray->size = 4; - break; - case RenderDataArray::NORMAL_DATA_ARRAY: - newArray->size = 3; - break; - case RenderDataArray::TANGENT_DATA_ARRAY: - newArray->size = 3; - break; - case RenderDataArray::TEXCOORD_DATA_ARRAY: - newArray->size = 2; - break; - default: - break; - } - - return newArray; -} - -void OpenGLRenderer::setRenderArrayData(RenderDataArray *array, Number *arrayData) { - +void OpenGLRenderer::setWireframePolygonMode(bool val) { + if(val) { + glPolygonMode( GL_FRONT_AND_BACK, GL_LINE); + } else { + glPolygonMode( GL_FRONT_AND_BACK, GL_FILL); + } } -void OpenGLRenderer::drawArrays(int drawType) { +void OpenGLRenderer::drawArrays(int drawType, IndexDataArray *indexArray) { GLenum mode = GL_TRIANGLES; switch(drawType) { case Mesh::TRI_MESH: - switch(renderMode) { - case RENDER_MODE_NORMAL: - mode = GL_TRIANGLES; - break; - case RENDER_MODE_WIREFRAME: - mode = GL_LINE_LOOP; - break; - } + mode = GL_TRIANGLES; break; case Mesh::TRIFAN_MESH: - switch(renderMode) { - case RENDER_MODE_NORMAL: - mode = GL_TRIANGLE_FAN; - break; - case RENDER_MODE_WIREFRAME: - mode = GL_LINE_LOOP; - break; - } + mode = GL_TRIANGLE_FAN; break; case Mesh::QUAD_MESH: - switch(renderMode) { - case RENDER_MODE_NORMAL: - mode = GL_QUADS; - break; - case RENDER_MODE_WIREFRAME: - mode = GL_LINE_LOOP; - break; - } + mode = GL_QUADS; break; case Mesh::LINE_STRIP_MESH: mode = GL_LINE_STRIP; @@ -1050,53 +971,53 @@ void OpenGLRenderer::drawArrays(int drawType) { break; } - glDrawArrays( mode, 0, verticesToDraw); - + if(indexArray) { + if(indexArray->getDataSize() > 0) { + glBindBufferARB( GL_ELEMENT_ARRAY_BUFFER_ARB, 0); + glDrawElements(mode, indexArray->getDataSize(), GL_UNSIGNED_INT, indexArray->getArrayData()); + } + } else { + glDrawArrays( mode, 0, verticesToDraw); + } + verticesToDraw = 0; glDisableClientState( GL_VERTEX_ARRAY); glDisableClientState( GL_TEXTURE_COORD_ARRAY ); glDisableClientState( GL_NORMAL_ARRAY ); - glDisableClientState( GL_COLOR_ARRAY ); -} - -/* -void OpenGLRenderer::draw3DVertex2UV(Vertex *vertex, Vector2 *faceUV1, Vector2 *faceUV2) { - if(vertex->useVertexColor) - glColor4f(vertex->vertexColor.r, vertex->vertexColor.g, vertex->vertexColor.b, vertex->vertexColor.a); - - if(currentTexture || currentMaterial) { - glMultiTexCoord2f(GL_TEXTURE0, faceUV1->x, faceUV1->y); - glMultiTexCoord2f(GL_TEXTURE1, faceUV2->x, faceUV2->y); - } - -// glNormal3f(vertex->normal->x, vertex->normal->y, vertex->normal->z); - glVertex3f(vertex->x, vertex->y, vertex->z); + glDisableClientState( GL_COLOR_ARRAY ); + glDisableVertexAttribArrayARB(6); } -*/ void OpenGLRenderer::drawScreenQuad(Number qx, Number qy) { - setOrthoMode(); + glMatrixMode(GL_PROJECTION); + glPushMatrix(); + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + setProjectionOrtho(2.0, 2.0, -1.0, 1.0, true); - Number xscale = qx/((Number)viewportWidth) * 2.0f; - Number yscale = qy/((Number)viewportHeight) * 2.0f; glBegin(GL_QUADS); glColor4f(1.0f,1.0f,1.0f,1.0f); glTexCoord2f(0.0f, 1.0f); - glVertex2f(-1, -1+(1.0f*yscale)); + glVertex2f(-1, 1.0); glTexCoord2f(0.0f, 0.0f); glVertex2f(-1.0f, -1.0f); glTexCoord2f(1.0f, 0.0f); - glVertex2f(-1+(1.0f*xscale), -1.0f); + glVertex2f(1.0, -1.0f); glTexCoord2f(1.0f, 1.0f); - glVertex2f(-1+(1.0f*xscale), -1+(1.0f*yscale)); + glVertex2f(1.0, 1.0); glEnd(); - setPerspectiveMode(); + + glMatrixMode(GL_PROJECTION); + glPopMatrix(); + glMatrixMode(GL_MODELVIEW); + glPopMatrix(); + setPerspectiveDefaults(); } @@ -1104,8 +1025,8 @@ void OpenGLRenderer::translate2D(Number x, Number y) { glTranslatef(x, y, 0.0f); } -void OpenGLRenderer::scale2D(Vector2 *scale) { - glScalef(scale->x, scale->y, 1.0f); +void OpenGLRenderer::scale2D(const Vector2 &scale) { + glScalef(scale.x, scale.y, 1.0f); } void OpenGLRenderer::loadIdentity() { @@ -1122,6 +1043,7 @@ void OpenGLRenderer::setVertexColor(Number r, Number g, Number b, Number a) { } void OpenGLRenderer::EndRender() { + Renderer::EndRender(); /// glFlush(); // glFinish(); } diff --git a/Core/Contents/Source/PolyGLSLProgram.cpp b/Core/Contents/Source/PolyGLSLProgram.cpp index 3f181355b..e7f6518d5 100755 --- a/Core/Contents/Source/PolyGLSLProgram.cpp +++ b/Core/Contents/Source/PolyGLSLProgram.cpp @@ -77,7 +77,7 @@ void GLSLProgram::reloadProgram() { if(program != -1) glDeleteShader(program); - OSFILE *file = OSBasics::open(fileName, "r"); + OSFILE *file = OSBasics::open(fileName, "rb"); if (!file) { Logger::log("Error: shader file %s not found\n", fileName.c_str()); program = -1; diff --git a/Core/Contents/Source/PolyGLSLShader.cpp b/Core/Contents/Source/PolyGLSLShader.cpp index c7cf5bb1f..a479f70f8 100755 --- a/Core/Contents/Source/PolyGLSLShader.cpp +++ b/Core/Contents/Source/PolyGLSLShader.cpp @@ -60,64 +60,6 @@ extern PFNGLGETUNIFORMLOCATIONARBPROC glGetUniformLocation; using namespace Polycode; -GLSLShaderBinding::GLSLShaderBinding(GLSLShader *shader) : ShaderBinding(shader) { - glslShader = shader; -} - -GLSLShaderBinding::~GLSLShaderBinding() { - -} - -Cubemap *GLSLShaderBinding::getCubemap(const String& name) { - for(int i=0; i < cubemaps.size(); i++) { - if(cubemaps[i].name == name) { - return cubemaps[i].cubemap; - } - } - return NULL; -} - -Texture *GLSLShaderBinding::getTexture(const String& name) { - for(int i=0; i < textures.size(); i++) { - if(textures[i].name == name) { - return textures[i].texture; - } - } - return NULL; -} - -void GLSLShaderBinding::addTexture(const String& name, Texture *texture) { - GLSLTextureBinding binding; - binding.name = name; - binding.texture = texture; - textures.push_back(binding); -} - -void GLSLShaderBinding::addCubemap(const String& name, Cubemap *cubemap) { - GLSLCubemapBinding binding; - binding.cubemap = cubemap; - binding.name = name; - cubemaps.push_back(binding); -} - -void GLSLShaderBinding::clearCubemap(const String& name) { - for(int i=0; i < cubemaps.size(); i++) { - if(cubemaps[i].name == name) { - cubemaps.erase(cubemaps.begin()+i); - return; - } - } -} - -void GLSLShaderBinding::clearTexture(const String& name) { - for(int i=0; i < textures.size(); i++) { - if(textures[i].name == name) { - textures.erase(textures.begin()+i); - return; - } - } -} - int GLSLShader::getPolycodeParamType(int glType) { switch(glType) { case GL_FLOAT: @@ -179,7 +121,9 @@ void GLSLShader::linkProgram() { shader_id = glCreateProgram(); glAttachShader(shader_id, ((GLSLProgram*)fp)->program); glAttachShader(shader_id, ((GLSLProgram*)vp)->program); - glBindAttribLocation(shader_id, 6, "vTangent"); + glBindAttribLocation(shader_id, 6, "vTangent"); + glBindAttribLocation(shader_id, 7, "vBoneWeights"); + glBindAttribLocation(shader_id, 8, "vBoneIndices"); glLinkProgram(shader_id); if(vp) { vp->addEventListener(this, Event::RESOURCE_RELOAD_EVENT); @@ -201,16 +145,18 @@ void GLSLShader::linkProgram() { switch(type) { case GL_SAMPLER_2D: expectedTextures.push_back(String(name)); - printf("expectedTextures: %s\n", name); + printf("Shader %s expecting texture: %s\n", this->getName().c_str(), name); break; case GL_SAMPLER_CUBE: expectedCubemaps.push_back(String(name)); + printf("Shader %s expecting cubemap: %s\n", this->getName().c_str(), name); break; default: ProgramParam param; param.name = String(name); param.type = getPolycodeParamType(type); expectedParams.push_back(param); + printf("Shader %s expecting param glType 0x%x, polycode type %d: %s\n", this->getName().c_str(), type, param.type, name); break; } } @@ -266,7 +212,3 @@ void GLSLShader::reload() { GLSLShader::~GLSLShader() { unlinkProgram(); } - -ShaderBinding *GLSLShader::createBinding() { - return new GLSLShaderBinding(this); -} diff --git a/Core/Contents/Source/PolyGLSLShaderModule.cpp b/Core/Contents/Source/PolyGLSLShaderModule.cpp index c387b9916..a55df265d 100755 --- a/Core/Contents/Source/PolyGLSLShaderModule.cpp +++ b/Core/Contents/Source/PolyGLSLShaderModule.cpp @@ -112,15 +112,15 @@ String GLSLShaderModule::getShaderType() { return "glsl"; } -Shader *GLSLShaderModule::createShader(String name, String vpName, String fpName) { +Shader *GLSLShaderModule::createShader(ResourcePool *resourcePool, String name, String vpName, String fpName) { GLSLShader *retShader = NULL; GLSLProgram *vp = NULL; GLSLProgram *fp = NULL; - vp = (GLSLProgram*)CoreServices::getInstance()->getResourceManager()->getResourceByPath(vpName); - fp = (GLSLProgram*)CoreServices::getInstance()->getResourceManager()->getResourceByPath(fpName); + vp = (GLSLProgram*)resourcePool->getResourceByPath(vpName); + fp = (GLSLProgram*)resourcePool->getResourceByPath(fpName); if(vp != NULL && fp != NULL) { GLSLShader *shader = new GLSLShader(vp,fp); @@ -131,7 +131,7 @@ Shader *GLSLShaderModule::createShader(String name, String vpName, String fpName return retShader; } -Shader *GLSLShaderModule::createShader(TiXmlNode *node) { +Shader *GLSLShaderModule::createShader(ResourcePool *resourcePool, TiXmlNode *node) { TiXmlNode* pChild; GLSLProgram *vp = NULL; GLSLProgram *fp = NULL; @@ -146,27 +146,27 @@ Shader *GLSLShaderModule::createShader(TiXmlNode *node) { if(strcmp(pChild->Value(), "vp") == 0) { String vpFileName = String(pChildElement->Attribute("source")); - vp = (GLSLProgram*)CoreServices::getInstance()->getResourceManager()->getResourceByPath(vpFileName); + vp = (GLSLProgram*)resourcePool->getResourceByPath(vpFileName); if(!vp) { vp = (GLSLProgram*)CoreServices::getInstance()->getMaterialManager()->createProgramFromFile(vpFileName); if(vp) { vp->setResourcePath(vpFileName); OSFileEntry entry = OSFileEntry(vpFileName, OSFileEntry::TYPE_FILE); vp->setResourceName(entry.name); - CoreServices::getInstance()->getResourceManager()->addResource(vp); + resourcePool->addResource(vp); } } } if(strcmp(pChild->Value(), "fp") == 0) { String fpFileName = String(pChildElement->Attribute("source")); - fp = (GLSLProgram*)CoreServices::getInstance()->getResourceManager()->getResourceByPath(fpFileName); + fp = (GLSLProgram*)resourcePool->getResourceByPath(fpFileName); if(!fp) { fp = (GLSLProgram*)CoreServices::getInstance()->getMaterialManager()->createProgramFromFile(fpFileName); if(fp) { fp->setResourcePath(fpFileName); OSFileEntry entry = OSFileEntry(fpFileName, OSFileEntry::TYPE_FILE); fp->setResourceName(entry.name); - CoreServices::getInstance()->getResourceManager()->addResource(fp); + resourcePool->addResource(fp); } } } @@ -186,6 +186,19 @@ void GLSLShaderModule::clearShader() { glUseProgram(0); } +void setUniformMatrix(GLint paramLocation, const Polycode::Matrix4& matrix) { +#ifdef POLYCODE_NUMBER_IS_SINGLE + glUniformMatrix4fv(paramLocation, 1, false, matrix.ml); +#else + // no glUniformMatrix4dv on some systems + float copyMatrix[16]; + for(int i=0; i < 16; i++) { + copyMatrix[i] = matrix.ml[i]; + } + glUniformMatrix4fv(paramLocation, 1, false, copyMatrix); +#endif +} + void GLSLShaderModule::updateGLSLParam(Renderer *renderer, GLSLShader *glslShader, ProgramParam ¶m, ShaderBinding *materialOptions, ShaderBinding *localOptions) { LocalShaderParam *localParam = NULL; @@ -230,7 +243,28 @@ void GLSLShaderModule::updateGLSLParam(Renderer *renderer, GLSLShader *glslShade } else { glUniform4f(paramLocation, 0.0f, 0.0f, 0.0f, 0.0f); } - break; + break; + case ProgramParam::PARAM_MATRIX: + if(localParam) { + if(localParam->arraySize > 0) { + Matrix4 *matPointer = (Matrix4*)localParam->data; + std::vector matrixData; + for(int i=0; i < localParam->arraySize; i++) { + for(int j=0; j < 16; j++) { + matrixData.push_back(matPointer[i].ml[j]); + } + } + + glUniformMatrix4fv(paramLocation, localParam->arraySize, false, &matrixData[0]); + + } else { + setUniformMatrix(paramLocation, localParam->getMatrix4()); + } + } else { + Matrix4 defaultMatrix; + setUniformMatrix(paramLocation, defaultMatrix); + } + break; } } @@ -240,17 +274,12 @@ bool GLSLShaderModule::applyShaderMaterial(Renderer *renderer, Material *materia glPushMatrix(); glLoadIdentity(); - - - int numRendererAreaLights = renderer->getNumAreaLights(); + + int numRendererPointLights = renderer->getNumPointLights(); int numRendererSpotLights = renderer->getNumSpotLights(); - int numTotalLights = glslShader->numAreaLights + glslShader->numSpotLights; - - if(numTotalLights > 0) { - renderer->sortLights(); - } - + int numTotalLights = glslShader->numPointLights + glslShader->numSpotLights; + for(int i=0 ; i < numTotalLights; i++) { GLfloat resetData[] = {0.0, 0.0, 0.0, 0.0}; glLightfv (GL_LIGHT0+i, GL_DIFFUSE, resetData); @@ -265,44 +294,43 @@ bool GLSLShaderModule::applyShaderMaterial(Renderer *renderer, Material *materia int lightIndex = 0; - vector areaLights = renderer->getAreaLights(); + vector pointLights = renderer->getPointLights(); GLfloat ambientVal[] = {1, 1, 1, 1.0}; - for(int i=0; i < glslShader->numAreaLights; i++) { + for(int i=0; i < glslShader->numPointLights; i++) { LightInfo light; - if(i < numRendererAreaLights) { - light = areaLights[i]; + if(i < numRendererPointLights) { + light = pointLights[i]; light.position = renderer->getCameraMatrix().Inverse() * light.position; ambientVal[0] = renderer->ambientColor.r; ambientVal[1] = renderer->ambientColor.g; ambientVal[2] = renderer->ambientColor.b; - ambientVal[3] = 1; - - GLfloat data4[] = {light.color.x * light.intensity, light.color.y * light.intensity, light.color.z * light.intensity, 1.0}; - glLightfv (GL_LIGHT0+lightIndex, GL_DIFFUSE, data4); - - data4[0] = light.specularColor.r* light.intensity; - data4[1] = light.specularColor.g* light.intensity; - data4[2] = light.specularColor.b* light.intensity; - data4[3] = light.specularColor.a* light.intensity; - glLightfv (GL_LIGHT0+lightIndex, GL_SPECULAR, data4); - - data4[3] = 1.0; - - glLightfv (GL_LIGHT0+lightIndex, GL_AMBIENT, ambientVal); - glLightf (GL_LIGHT0+lightIndex, GL_SPOT_CUTOFF, 180); - - data4[0] = light.position.x; - data4[1] = light.position.y; - data4[2] = light.position.z; - glLightfv (GL_LIGHT0+lightIndex, GL_POSITION, data4); - - glLightf (GL_LIGHT0+lightIndex, GL_CONSTANT_ATTENUATION, light.constantAttenuation); - glLightf (GL_LIGHT0+lightIndex, GL_LINEAR_ATTENUATION, light.linearAttenuation); - glLightf (GL_LIGHT0+lightIndex, GL_QUADRATIC_ATTENUATION, light.quadraticAttenuation); - - } - lightIndex++; + ambientVal[3] = 1; + + GLfloat data4[] = {light.color.x * light.intensity, light.color.y * light.intensity, light.color.z * light.intensity, 1.0}; + glLightfv (GL_LIGHT0+lightIndex, GL_DIFFUSE, data4); + + data4[0] = light.specularColor.r* light.intensity; + data4[1] = light.specularColor.g* light.intensity; + data4[2] = light.specularColor.b* light.intensity; + data4[3] = light.specularColor.a* light.intensity; + glLightfv (GL_LIGHT0+lightIndex, GL_SPECULAR, data4); + + data4[3] = 1.0; + + glLightfv (GL_LIGHT0+lightIndex, GL_AMBIENT, ambientVal); + glLightf (GL_LIGHT0+lightIndex, GL_SPOT_CUTOFF, 180); + + data4[0] = light.position.x; + data4[1] = light.position.y; + data4[2] = light.position.z; + glLightfv (GL_LIGHT0+lightIndex, GL_POSITION, data4); + + glLightf (GL_LIGHT0+lightIndex, GL_CONSTANT_ATTENUATION, light.constantAttenuation); + glLightf (GL_LIGHT0+lightIndex, GL_LINEAR_ATTENUATION, light.linearAttenuation); + glLightf (GL_LIGHT0+lightIndex, GL_QUADRATIC_ATTENUATION, light.quadraticAttenuation); + lightIndex++; + } } vector spotLights = renderer->getSpotLights(); @@ -330,97 +358,97 @@ bool GLSLShaderModule::applyShaderMaterial(Renderer *renderer, Material *materia ambientVal[2] = renderer->ambientColor.b; ambientVal[3] = 1; - GLfloat data4[] = {light.color.x * light.intensity, light.color.y * light.intensity, light.color.z * light.intensity, 1.0}; - glLightfv (GL_LIGHT0+lightIndex, GL_DIFFUSE, data4); - - data4[0] = light.specularColor.r* light.intensity; - data4[1] = light.specularColor.g* light.intensity; - data4[2] = light.specularColor.b* light.intensity; - data4[3] = light.specularColor.a* light.intensity; - glLightfv (GL_LIGHT0+lightIndex, GL_SPECULAR, data4); - - data4[3] = 1.0; - - glLightfv (GL_LIGHT0+lightIndex, GL_AMBIENT, ambientVal); - glLightf (GL_LIGHT0+lightIndex, GL_SPOT_CUTOFF, light.spotlightCutoff); - - glLightf (GL_LIGHT0+lightIndex, GL_SPOT_EXPONENT, light.spotlightExponent); - - data4[0] = dir.x; - data4[1] = dir.y; - data4[2] = dir.z; - glLightfv (GL_LIGHT0+lightIndex, GL_SPOT_DIRECTION, data4); - - data4[0] = pos.x; - data4[1] = pos.y; - data4[2] = pos.z; - glLightfv (GL_LIGHT0+lightIndex, GL_POSITION, data4); - - glLightf (GL_LIGHT0+lightIndex, GL_CONSTANT_ATTENUATION, light.constantAttenuation); - glLightf (GL_LIGHT0+lightIndex, GL_LINEAR_ATTENUATION, light.linearAttenuation); - glLightf (GL_LIGHT0+lightIndex, GL_QUADRATIC_ATTENUATION, light.quadraticAttenuation); - - if(light.shadowsEnabled) { - if(shadowMapTextureIndex < 4) { - switch(shadowMapTextureIndex) { - case 0: - strcpy(texName, "shadowMap0"); - strcpy(matName, "shadowMatrix0"); - break; - case 1: - strcpy(texName, "shadowMap1"); - strcpy(matName, "shadowMatrix1"); - break; - case 2: - strcpy(texName, "shadowMap2"); - strcpy(matName, "shadowMatrix2"); - break; - case 3: - strcpy(texName, "shadowMap3"); - strcpy(matName, "shadowMatrix3"); - break; - } - - int texture_location = glGetUniformLocation(glslShader->shader_id, texName); - glUniform1i(texture_location, textureIndex); - glActiveTexture(GL_TEXTURE0 + textureIndex); - glBindTexture(GL_TEXTURE_2D, ((OpenGLTexture*)light.shadowMapTexture)->getTextureID()); - textureIndex++; - - int mloc = glGetUniformLocation(glslShader->shader_id, matName); - light.textureMatrix = light.textureMatrix; - - - GLfloat mat[16]; - for(int z=0; z < 16; z++) { - mat[z] = light.textureMatrix.ml[z]; - } - glUniformMatrix4fv(mloc, 1, false, mat); - - - } - shadowMapTextureIndex++; + GLfloat data4[] = {light.color.x * light.intensity, light.color.y * light.intensity, light.color.z * light.intensity, 1.0}; + glLightfv (GL_LIGHT0+lightIndex, GL_DIFFUSE, data4); + + data4[0] = light.specularColor.r* light.intensity; + data4[1] = light.specularColor.g* light.intensity; + data4[2] = light.specularColor.b* light.intensity; + data4[3] = light.specularColor.a* light.intensity; + glLightfv (GL_LIGHT0+lightIndex, GL_SPECULAR, data4); + + data4[3] = 1.0; + + glLightfv (GL_LIGHT0+lightIndex, GL_AMBIENT, ambientVal); + glLightf (GL_LIGHT0+lightIndex, GL_SPOT_CUTOFF, light.spotlightCutoff); + + glLightf (GL_LIGHT0+lightIndex, GL_SPOT_EXPONENT, light.spotlightExponent); + + data4[0] = dir.x; + data4[1] = dir.y; + data4[2] = dir.z; + glLightfv (GL_LIGHT0+lightIndex, GL_SPOT_DIRECTION, data4); + + data4[0] = pos.x; + data4[1] = pos.y; + data4[2] = pos.z; + glLightfv (GL_LIGHT0+lightIndex, GL_POSITION, data4); + + glLightf (GL_LIGHT0+lightIndex, GL_CONSTANT_ATTENUATION, light.constantAttenuation); + glLightf (GL_LIGHT0+lightIndex, GL_LINEAR_ATTENUATION, light.linearAttenuation); + glLightf (GL_LIGHT0+lightIndex, GL_QUADRATIC_ATTENUATION, light.quadraticAttenuation); + + Number shadowAmount = 0.0; + + if(light.shadowsEnabled) { + if(shadowMapTextureIndex < 4) { + switch(shadowMapTextureIndex) { + case 0: + strcpy(texName, "shadowMap0"); + strcpy(matName, "shadowMatrix0"); + break; + case 1: + strcpy(texName, "shadowMap1"); + strcpy(matName, "shadowMatrix1"); + break; + case 2: + strcpy(texName, "shadowMap2"); + strcpy(matName, "shadowMatrix2"); + break; + case 3: + strcpy(texName, "shadowMap3"); + strcpy(matName, "shadowMatrix3"); + break; + } + + int texture_location = glGetUniformLocation(glslShader->shader_id, texName); + glUniform1i(texture_location, textureIndex); + glActiveTexture(GL_TEXTURE0 + textureIndex); + glBindTexture(GL_TEXTURE_2D, ((OpenGLTexture*)light.shadowMapTexture)->getTextureID()); + textureIndex++; + + LocalShaderParam *matParam = material->getShaderBinding(shaderIndex)->getLocalParamByName(matName); + if(matParam) { + matParam->setMatrix4(light.textureMatrix); + } + + shadowAmount = 1.0; + + } + + shadowMapTextureIndex++; + } + + LocalShaderParam *amountParam = material->getShaderBinding(shaderIndex)->getLocalParamByName("shadowAmount"); + if(amountParam) { + amountParam->setNumber(shadowAmount); + } + + lightIndex++; } - else { - light.shadowsEnabled = false; - } - } - lightIndex++; } glPopMatrix(); glEnable(GL_TEXTURE_2D); - - Matrix4 modelMatrix = renderer->getCurrentModelMatrix(); - int mloc = glGetUniformLocation(glslShader->shader_id, "modelMatrix"); - GLfloat mat[16]; - for(int z=0; z < 16; z++) { - mat[z] = modelMatrix.ml[z]; - } - glUniformMatrix4fv(mloc, 1, false, mat); - - - GLSLShaderBinding *cgBinding = (GLSLShaderBinding*)material->getShaderBinding(shaderIndex); + + Matrix4 modelMatrix = renderer->getModelviewMatrix() * renderer->getCameraMatrix(); + LocalShaderParam *modelMatrixParam = material->getShaderBinding(shaderIndex)->getLocalParamByName("modelMatrix"); + + if(modelMatrixParam) { + modelMatrixParam->setMatrix4(modelMatrix); + } + + ShaderBinding *cgBinding = material->getShaderBinding(shaderIndex); for(int i=0; i < glslShader->expectedParams.size(); i++) { ProgramParam param = glslShader->expectedParams[i]; @@ -428,33 +456,35 @@ bool GLSLShaderModule::applyShaderMaterial(Renderer *renderer, Material *materia } for(int i=0; i < cgBinding->textures.size(); i++) { - OpenGLTexture *glTexture = (OpenGLTexture*)cgBinding->textures[i].texture; - if(glTexture) { - int texture_location = glGetUniformLocation(glslShader->shader_id, cgBinding->textures[i].name.c_str()); - glUniform1i(texture_location, textureIndex); - glActiveTexture(GL_TEXTURE0 + textureIndex); - glBindTexture(GL_TEXTURE_2D, glTexture->getTextureID()); - textureIndex++; - } - } - - + if(!localOptions->getTexture(cgBinding->textures[i].name)) { + OpenGLTexture *glTexture = (OpenGLTexture*)cgBinding->textures[i].texture; + if(glTexture) { + int texture_location = glGetUniformLocation(glslShader->shader_id, cgBinding->textures[i].name.c_str()); + glUniform1i(texture_location, textureIndex); + glActiveTexture(GL_TEXTURE0 + textureIndex); + glBindTexture(GL_TEXTURE_2D, glTexture->getTextureID()); + textureIndex++; + } + } + } + for(int i=0; i < cgBinding->cubemaps.size(); i++) { - OpenGLCubemap *glCubemap = (OpenGLCubemap*)cgBinding->cubemaps[i].cubemap; - if(glCubemap) { - int texture_location = glGetUniformLocation(glslShader->shader_id, cgBinding->cubemaps[i].name.c_str()); - glUniform1i(texture_location, textureIndex); - glActiveTexture(GL_TEXTURE0 + textureIndex); - glBindTexture(GL_TEXTURE_CUBE_MAP, glCubemap->getTextureID()); - textureIndex++; - } - } + if(!localOptions->getCubemap(cgBinding->cubemaps[i].name)) { + OpenGLCubemap *glCubemap = (OpenGLCubemap*)cgBinding->cubemaps[i].cubemap; + if(glCubemap) { + int texture_location = glGetUniformLocation(glslShader->shader_id, cgBinding->cubemaps[i].name.c_str()); + glUniform1i(texture_location, textureIndex); + glActiveTexture(GL_TEXTURE0 + textureIndex); + glBindTexture(GL_TEXTURE_CUBE_MAP, glCubemap->getTextureID()); + textureIndex++; + } + } + } - cgBinding = (GLSLShaderBinding*)localOptions; - for(int i=0; i < cgBinding->textures.size(); i++) { - OpenGLTexture *glTexture = (OpenGLTexture*)cgBinding->textures[i].texture; + for(int i=0; i < localOptions->textures.size(); i++) { + OpenGLTexture *glTexture = (OpenGLTexture*)localOptions->textures[i].texture; if(glTexture) { - int texture_location = glGetUniformLocation(glslShader->shader_id, cgBinding->textures[i].name.c_str()); + int texture_location = glGetUniformLocation(glslShader->shader_id, localOptions->textures[i].name.c_str()); glUniform1i(texture_location, textureIndex); glActiveTexture(GL_TEXTURE0 + textureIndex); glBindTexture(GL_TEXTURE_2D, glTexture->getTextureID()); @@ -462,11 +492,11 @@ bool GLSLShaderModule::applyShaderMaterial(Renderer *renderer, Material *materia } } - for(int i=0; i < cgBinding->cubemaps.size(); i++) { - OpenGLCubemap *glCubemap = (OpenGLCubemap*)cgBinding->cubemaps[i].cubemap; + for(int i=0; i < localOptions->cubemaps.size(); i++) { + OpenGLCubemap *glCubemap = (OpenGLCubemap*)localOptions->cubemaps[i].cubemap; if(glCubemap) { - int texture_location = glGetUniformLocation(glslShader->shader_id, cgBinding->cubemaps[i].name.c_str()); - glUniform1i(texture_location, textureIndex); + int texture_location = glGetUniformLocation(glslShader->shader_id, localOptions->cubemaps[i].name.c_str()); + glUniform1i(texture_location, textureIndex); glActiveTexture(GL_TEXTURE0 + textureIndex); glBindTexture(GL_TEXTURE_CUBE_MAP, glCubemap->getTextureID()); textureIndex++; diff --git a/Core/Contents/Source/PolyGLTexture.cpp b/Core/Contents/Source/PolyGLTexture.cpp index 1cb8147d5..52d445c16 100755 --- a/Core/Contents/Source/PolyGLTexture.cpp +++ b/Core/Contents/Source/PolyGLTexture.cpp @@ -44,8 +44,8 @@ OpenGLTexture::OpenGLTexture(unsigned int width, unsigned int height, char *text pixelType = GL_UNSIGNED_BYTE; break; case Image::IMAGE_FP16: - glTextureType = GL_RGBA; - glTextureFormat = GL_RGBA16F_ARB; + glTextureType = GL_RGB; + glTextureFormat = GL_RGB; pixelType = GL_FLOAT; break; default: @@ -59,14 +59,17 @@ OpenGLTexture::OpenGLTexture(unsigned int width, unsigned int height, char *text } void OpenGLTexture::recreateFromImageData() { + + if(!textureData) { + return; + } Number anisotropy = CoreServices::getInstance()->getRenderer()->getAnisotropyAmount(); - if(glTextureLoaded) { - glDeleteTextures(1, &textureID); + if (!glTextureLoaded) { + glGenTextures(1, &textureID); } - glGenTextures(1, &textureID); glBindTexture(GL_TEXTURE_2D, textureID); if(clamp) { @@ -119,16 +122,14 @@ void OpenGLTexture::setGLInfo(GLuint textureID, GLuint frameBufferID) { } void OpenGLTexture::setTextureData(char *data) { -/* + memcpy(textureData, data, width * height * pixelSize); + glBindTexture(GL_TEXTURE_2D, textureID); - glDrawBuffer(GL_AUX0); - glDrawPixels(width, height, glTextureType, pixelType, data); - glReadBuffer(GL_AUX0); -// glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 0, 0, 128, 128, 0); -*/ + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, glTextureType, pixelType, textureData); } OpenGLTexture::~OpenGLTexture() { + glDeleteTextures(1, &textureID); if(frameBufferID != FRAMEBUFFER_NULL) { glDeleteFramebuffersEXT(1, &frameBufferID); diff --git a/Core/Contents/Source/PolyGLVertexBuffer.cpp b/Core/Contents/Source/PolyGLVertexBuffer.cpp index ed08dea69..2d695fa70 100644 --- a/Core/Contents/Source/PolyGLVertexBuffer.cpp +++ b/Core/Contents/Source/PolyGLVertexBuffer.cpp @@ -22,7 +22,6 @@ #include "PolyGLHeaders.h" #include "PolyGLVertexBuffer.h" -#include "PolyPolygon.h" #if defined(__APPLE__) && defined(__MACH__) @@ -48,131 +47,92 @@ extern PFNGLGETBUFFERPOINTERVARBPROC glGetBufferPointervARB; #endif OpenGLVertexBuffer::OpenGLVertexBuffer(Mesh *mesh) : VertexBuffer() { - glGenBuffersARB(1, &vertexBufferID); - glBindBufferARB(GL_ARRAY_BUFFER_ARB, vertexBufferID); + vertexBufferID = -1; + texCoordBufferID = -1; + normalBufferID = -1; + colorBufferID = -1; + tangentBufferID = -1; + indexBufferID = -1; + boneWeightBufferID = -1; + boneIndexBufferID = -1; + + meshType = mesh->getMeshType(); - - long bufferSize = 0; - long newBufferSize = 0; - GLfloat *buffer = (GLfloat*)malloc(1); - - vertexCount = 0; - for(int i=0; i < mesh->getPolygonCount(); i++) { - for(int j=0; j < mesh->getPolygon(i)->getVertexCount(); j++) { - vertexCount++; - newBufferSize = bufferSize + 3; - buffer = (GLfloat*)realloc(buffer, newBufferSize * sizeof(GLfloat)); - buffer[bufferSize+0] = mesh->getPolygon(i)->getVertex(j)->x; - buffer[bufferSize+1] = mesh->getPolygon(i)->getVertex(j)->y; - buffer[bufferSize+2] = mesh->getPolygon(i)->getVertex(j)->z; - bufferSize = newBufferSize; - } - } - glBufferDataARB(GL_ARRAY_BUFFER_ARB, bufferSize*sizeof(GLfloat), buffer, GL_STATIC_DRAW_ARB); - free(buffer); - + vertexCount = mesh->vertexPositionArray.getDataSize() / 3; - glGenBuffersARB(1, &texCoordBufferID); - glBindBufferARB(GL_ARRAY_BUFFER_ARB, texCoordBufferID); - - bufferSize = 0; - newBufferSize = 0; - buffer = (GLfloat*)malloc(1); - - for(int i=0; i < mesh->getPolygonCount(); i++) { - for(int j=0; j < mesh->getPolygon(i)->getVertexCount(); j++) { - newBufferSize = bufferSize + 2; - buffer = (GLfloat*)realloc(buffer, newBufferSize * sizeof(GLfloat)); - buffer[bufferSize+0] = mesh->getPolygon(i)->getVertex(j)->getTexCoord().x; - buffer[bufferSize+1] = mesh->getPolygon(i)->getVertex(j)->getTexCoord().y; - bufferSize = newBufferSize; - } - } - - glBufferDataARB(GL_ARRAY_BUFFER_ARB, bufferSize*sizeof(GLfloat), buffer, GL_STATIC_DRAW_ARB); - free(buffer); - - - glGenBuffersARB(1, &normalBufferID); - glBindBufferARB(GL_ARRAY_BUFFER_ARB, normalBufferID); - - bufferSize = 0; - newBufferSize = 0; - buffer = (GLfloat*)malloc(1); - - for(int i=0; i < mesh->getPolygonCount(); i++) { - for(int j=0; j < mesh->getPolygon(i)->getVertexCount(); j++) { - newBufferSize = bufferSize + 3; - buffer = (GLfloat*)realloc(buffer, newBufferSize * sizeof(GLfloat)); - if(mesh->getPolygon(i)->useVertexNormals) { - buffer[bufferSize+0] = mesh->getPolygon(i)->getVertex(j)->normal.x; - buffer[bufferSize+1] = mesh->getPolygon(i)->getVertex(j)->normal.y; - buffer[bufferSize+2] = mesh->getPolygon(i)->getVertex(j)->normal.z; - } else { - buffer[bufferSize+0] = mesh->getPolygon(i)->getFaceNormal().x; - buffer[bufferSize+1] = mesh->getPolygon(i)->getFaceNormal().y; - buffer[bufferSize+2] = mesh->getPolygon(i)->getFaceNormal().z; - } - bufferSize = newBufferSize; - } - } - - glBufferDataARB(GL_ARRAY_BUFFER_ARB, bufferSize*sizeof(GLfloat), buffer, GL_STATIC_DRAW_ARB); - free(buffer); - - + glGenBuffersARB(1, &vertexBufferID); + glBindBufferARB(GL_ARRAY_BUFFER_ARB, vertexBufferID); + + glBufferDataARB(GL_ARRAY_BUFFER_ARB, mesh->vertexPositionArray.getDataSize() * sizeof(PolyRendererVertexType), mesh->vertexPositionArray.getArrayData(), GL_STATIC_DRAW_ARB); - glGenBuffersARB(1, &tangentBufferID); - glBindBufferARB(GL_ARRAY_BUFFER_ARB, tangentBufferID); - - bufferSize = 0; - newBufferSize = 0; - buffer = (GLfloat*)malloc(1); - - for(int i=0; i < mesh->getPolygonCount(); i++) { - for(int j=0; j < mesh->getPolygon(i)->getVertexCount(); j++) { - newBufferSize = bufferSize + 3; - buffer = (GLfloat*)realloc(buffer, newBufferSize * sizeof(GLfloat)); - buffer[bufferSize+0] = mesh->getPolygon(i)->getVertex(j)->tangent.x; - buffer[bufferSize+1] = mesh->getPolygon(i)->getVertex(j)->tangent.y; - buffer[bufferSize+2] = mesh->getPolygon(i)->getVertex(j)->tangent.z; - bufferSize = newBufferSize; - } - } - - glBufferDataARB(GL_ARRAY_BUFFER_ARB, bufferSize*sizeof(GLfloat), buffer, GL_STATIC_DRAW_ARB); - free(buffer); - - glGenBuffersARB(1, &colorBufferID); - glBindBufferARB(GL_ARRAY_BUFFER_ARB, colorBufferID); - - bufferSize = 0; - newBufferSize = 0; - buffer = (GLfloat*)malloc(1); - - for(int i=0; i < mesh->getPolygonCount(); i++) { - for(int j=0; j < mesh->getPolygon(i)->getVertexCount(); j++) { - newBufferSize = bufferSize + 4; - buffer = (GLfloat*)realloc(buffer, newBufferSize * sizeof(GLfloat)); - buffer[bufferSize+0] = mesh->getPolygon(i)->getVertex(j)->vertexColor.r; - buffer[bufferSize+1] = mesh->getPolygon(i)->getVertex(j)->vertexColor.g; - buffer[bufferSize+2] = mesh->getPolygon(i)->getVertex(j)->vertexColor.b; - buffer[bufferSize+3] = mesh->getPolygon(i)->getVertex(j)->vertexColor.a; - bufferSize = newBufferSize; - } - } - - glBufferDataARB(GL_ARRAY_BUFFER_ARB, bufferSize*sizeof(GLfloat), buffer, GL_STATIC_DRAW_ARB); - free(buffer); - + + if(mesh->vertexTexCoordArray.getDataSize() == vertexCount * 2) { + glGenBuffersARB(1, &texCoordBufferID); + glBindBufferARB(GL_ARRAY_BUFFER_ARB, texCoordBufferID); + + glBufferDataARB(GL_ARRAY_BUFFER_ARB, mesh->vertexTexCoordArray.getDataSize() * sizeof(PolyRendererVertexType), mesh->vertexTexCoordArray.getArrayData(), GL_STATIC_DRAW_ARB); + } + + if(mesh->vertexNormalArray.getDataSize() == vertexCount * 3) { + glGenBuffersARB(1, &normalBufferID); + glBindBufferARB(GL_ARRAY_BUFFER_ARB, normalBufferID); + glBufferDataARB(GL_ARRAY_BUFFER_ARB, mesh->vertexNormalArray.getDataSize() * sizeof(PolyRendererVertexType), mesh->vertexNormalArray.getArrayData(), GL_STATIC_DRAW_ARB); + } + + if(mesh->vertexTangentArray.getDataSize() == vertexCount * 3) { + glGenBuffersARB(1, &tangentBufferID); + glBindBufferARB(GL_ARRAY_BUFFER_ARB, tangentBufferID); + + glBufferDataARB(GL_ARRAY_BUFFER_ARB, mesh->vertexTangentArray.getDataSize() * sizeof(PolyRendererVertexType), mesh->vertexTangentArray.getArrayData(), GL_STATIC_DRAW_ARB); + } + + if(mesh->vertexBoneWeightArray.getDataSize() == vertexCount * 4) { + glGenBuffersARB(1, &boneWeightBufferID); + glBindBufferARB(GL_ARRAY_BUFFER_ARB, boneWeightBufferID); + + glBufferDataARB(GL_ARRAY_BUFFER_ARB, mesh->vertexBoneWeightArray.getDataSize() * sizeof(PolyRendererVertexType), mesh->vertexBoneWeightArray.getArrayData(), GL_STATIC_DRAW_ARB); + } + + if(mesh->vertexBoneIndexArray.getDataSize() == vertexCount * 4) { + glGenBuffersARB(1, &boneIndexBufferID); + glBindBufferARB(GL_ARRAY_BUFFER_ARB, boneIndexBufferID); + + glBufferDataARB(GL_ARRAY_BUFFER_ARB, mesh->vertexBoneIndexArray.getDataSize() * sizeof(PolyRendererVertexType), mesh->vertexBoneIndexArray.getArrayData(), GL_STATIC_DRAW_ARB); + } + + if(mesh->vertexColorArray.getDataSize() == vertexCount * 4) { + glGenBuffersARB(1, &colorBufferID); + glBindBufferARB(GL_ARRAY_BUFFER_ARB, colorBufferID); + + glBufferDataARB(GL_ARRAY_BUFFER_ARB, mesh->vertexColorArray.getDataSize() * sizeof(PolyRendererVertexType), mesh->vertexColorArray.getArrayData(), GL_STATIC_DRAW_ARB); + } + + if(mesh->indexedMesh && mesh->indexArray.getDataSize() > 0) { + glGenBuffersARB(1, &indexBufferID); + glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, indexBufferID); + glBufferDataARB(GL_ELEMENT_ARRAY_BUFFER_ARB, mesh->indexArray.getDataSize() * sizeof(PolyRendererIndexType), mesh->indexArray.getArrayData(), GL_STATIC_DRAW_ARB); + indexCount = mesh->indexArray.getDataSize(); + } + } OpenGLVertexBuffer::~OpenGLVertexBuffer() { glDeleteBuffersARB(1, &vertexBufferID); glDeleteBuffersARB(1, &texCoordBufferID); glDeleteBuffersARB(1, &normalBufferID); - glDeleteBuffersARB(1, &colorBufferID); + glDeleteBuffersARB(1, &colorBufferID); + glDeleteBuffersARB(1, &indexBufferID); + glDeleteBuffersARB(1, &boneWeightBufferID); + glDeleteBuffersARB(1, &boneIndexBufferID); +} + +GLuint OpenGLVertexBuffer::getBoneWeightBufferID() { + return boneWeightBufferID; +} + +GLuint OpenGLVertexBuffer::getBoneIndexBufferID() { + return boneIndexBufferID; } GLuint OpenGLVertexBuffer::getColorBufferID() { @@ -194,3 +154,7 @@ GLuint OpenGLVertexBuffer::getVertexBufferID() { GLuint OpenGLVertexBuffer::getTangentBufferID() { return tangentBufferID; } + +GLuint OpenGLVertexBuffer::getIndexBufferID() { + return indexBufferID; +} diff --git a/Core/Contents/Source/PolyHTTPFetcher.cpp b/Core/Contents/Source/PolyHTTPFetcher.cpp new file mode 100644 index 000000000..7e1a8f790 --- /dev/null +++ b/Core/Contents/Source/PolyHTTPFetcher.cpp @@ -0,0 +1,294 @@ +/* +Copyright (C) 2015 by Joachim Meyer + +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: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +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. +*/ + +#ifdef _WINDOWS +#include +#include +#else +#include +#include +#include +#include +#include +#include +#include +#endif + +#include "PolyHTTPFetcher.h" +#include "PolyLogger.h" +#include "PolyCoreServices.h" +#include "PolyCore.h" + +using namespace Polycode; + +HTTPFetcher::HTTPFetcher(String address, bool saveToPath, String savePath) : Threaded() { + core = CoreServices::getInstance()->getCore(); + eventMutex = core->getEventMutex(); + + storeInFile = saveToPath; + this->savePath = savePath; + + this->address = address; + int protocolIndex = address.find_first_of("://"); + if (protocolIndex != 0){ + protocolIndex += strlen("://"); + protocol = address.substr(0, protocolIndex - strlen("://")); + int pathIndex = address.find_first_of("/", protocolIndex); + path = address.substr(pathIndex+1, address.length()); + + if (pathIndex != 0){ + host = address.substr(protocolIndex, pathIndex - protocolIndex); + } else { + host = address.substr(protocolIndex, address.length()); + } + } else { + int pathIndex = address.find_first_of("/"); + path = address.substr(pathIndex+1, address.length()); + + if (pathIndex != 0){ + host = address.substr(0, pathIndex); + } else { + host = address; + } + } + + if (!createSocket()) + return; + + threadRunning = true; + CoreServices::getInstance()->getCore()->createThread(this); +} + +HTTPFetcher::~HTTPFetcher(){ +#ifdef _WINDOWS + closesocket(s); +#else + close(s); +#endif +} + +bool HTTPFetcher::createSocket(){ + struct sockaddr_in server; + + addrinfo *result = NULL; + addrinfo hints; + + //Create a socket +#if PLATFORM == PLATFORM_WINDOWS + if ((s = socket(AF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) { + Logger::log("HTTP Fetcher: Could not create socket: %d\n", WSAGetLastError()); +#elif PLATFORM == PLATFORM_MAC || PLATFORM == PLATFORM_UNIX + if ((s = socket(AF_INET, SOCK_STREAM, 0)) == -1) { + Logger::log("HTTP Fetcher: Could not create socket: %s\n", strerror(errno)); +#endif + return false; + } + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = IPPROTO_TCP; + + if (getaddrinfo(host.c_str(), protocol.c_str(), &hints, &result) != 0) { +#if PLATFORM == PLATFORM_WINDOWS + Logger::log("HTTP Fetcher: Address resolve error: %d\n", WSAGetLastError()); +#elif PLATFORM == PLATFORM_MAC || PLATFORM == PLATFORM_UNIX + Logger::log("HTTP Fetcher: Address resolve error: %s\n", strerror(errno)); +#endif + return false; + } + + server.sin_addr = ((sockaddr_in*)result->ai_addr)->sin_addr; + server.sin_family = AF_INET; + server.sin_port = ((sockaddr_in*)result->ai_addr)->sin_port; + + //Connect to remote server + if (connect(s, (struct sockaddr *)&server, sizeof(server)) < 0) { +#if PLATFORM == PLATFORM_WINDOWS + Logger::log("HTTP Fetcher: connect error code: %d\n", WSAGetLastError()); +#elif PLATFORM == PLATFORM_MAC || PLATFORM == PLATFORM_UNIX + Logger::log("HTTP Fetcher: connect error code: %s\n", strerror(errno)); +#endif + return false; + } + return true; +} + +void HTTPFetcher::updateThread(){ + int protocolIndex = path.find_first_of("://"); + if (protocolIndex != 0){ + protocolIndex += strlen("://"); + protocol = path.substr(0, protocolIndex - strlen("://")); + int pathIndex = path.find_first_of("/", protocolIndex); + path = path.substr(pathIndex + 1, path.length()); + } else if (path.find_first_of("/") == 0) { + path = path.substr(1, path.length()); + } + + //Send some data + String request; + if (path != "") { + request = "GET /" + path + " " + String(HTTP_VERSION) + "\r\nHost: " + host + "\r\nUser-Agent: " + DEFAULT_USER_AGENT + "\r\nConnection: close\r\n\r\n"; + } else { + request = "GET / " + String(HTTP_VERSION) + "\r\nHost: " + host + "\r\nUser-Agent: " + DEFAULT_USER_AGENT + "\r\nConnection: close\r\n\r\n"; + } + + HTTPFetcherEvent *event = new HTTPFetcherEvent(); + + if (send(s, request.c_str(), strlen(request.c_str()), 0) < 0) { +#if PLATFORM == PLATFORM_WINDOWS + Logger::log("HTTP Fetcher: Send failed: %d\n", WSAGetLastError()); + event->errorCode = WSAGetLastError(); +#elif PLATFORM == PLATFORM_MAC || PLATFORM == PLATFORM_UNIX + Logger::log("HTTP Fetcher: Send failed: %s\n",strerror(errno)); + event->errorCode = errno; +#endif + createSocket(); + dispatchEvent(event, HTTPFetcherEvent::EVENT_HTTP_ERROR); + return; + } + + char *server_reply = (char*)malloc(1); + char *rec = server_reply; + unsigned long recv_size = 0, totalRec = 0; + do { + //Receive a reply from the server +#if PLATFORM == PLATFORM_WINDOWS + if ((recv_size = recv(s, rec, 1, 0)) == SOCKET_ERROR) { + Logger::log("HTTP Fetcher: recv failed: %d\n", WSAGetLastError()); + event->errorCode = WSAGetLastError(); +#elif PLATFORM == PLATFORM_MAC || PLATFORM == PLATFORM_UNIX + if ((recv_size = recv(s, rec, DEFAULT_PAGE_BUF_SIZE, 0)) == -1) { + Logger::log("HTTP Fetcher: recv failed: %s\n", strerror(errno)); + event->errorCode = errno; +#endif + dispatchEvent(event, HTTPFetcherEvent::EVENT_HTTP_ERROR); + killThread(); + return; + } + + + totalRec += recv_size; + server_reply = (char*)realloc(server_reply, totalRec + 1); + rec = server_reply + totalRec; + } while (recv_size != 0 && strstr(server_reply, "\r\n\r\n") == NULL); + + server_reply[totalRec] = '\0'; + event->data = server_reply; + + if (strlen(event->data) == 0){ + createSocket(); + return; + } + + char *charIndex = strstr(event->data, "HTTP/"); + if(charIndex == NULL){ + killThread(); + return; + } + int i; + if (sscanf(charIndex + strlen("HTTP/1.1"), "%d", &i) != 1 || i < 200 || i>299) { + event->errorCode = i; + dispatchEvent(event, HTTPFetcherEvent::EVENT_HTTP_ERROR); + killThread(); + return; + } + charIndex = strstr(event->data, "Content-Length:"); + if (charIndex == NULL) + charIndex = strstr(event->data, "Content-length:"); + if (sscanf(charIndex + strlen("content-length: "), "%d", &i) != 1) { + dispatchEvent(event, HTTPFetcherEvent::EVENT_HTTP_ERROR); + killThread(); + return; + } + + FILE* tempFile; + if (storeInFile){ + if (savePath == "") + savePath = path; + tempFile = fopen(savePath.c_str(), "wb"); + } + + free(server_reply); + server_reply = (char*)malloc(DEFAULT_PAGE_BUF_SIZE); + rec = server_reply; + recv_size = 0, totalRec = 0; + + do { + //Receive a reply from the server +#if PLATFORM == PLATFORM_WINDOWS + if ((recv_size = recv(s, rec, DEFAULT_PAGE_BUF_SIZE, 0)) == SOCKET_ERROR) { + Logger::log("HTTP Fetcher: recv failed: %d\n", WSAGetLastError()); + event->errorCode = WSAGetLastError(); +#elif PLATFORM == PLATFORM_MAC || PLATFORM == PLATFORM_UNIX + if ((recv_size = recv(s, rec, DEFAULT_PAGE_BUF_SIZE, 0)) == -1) { + Logger::log("HTTP Fetcher: recv failed: %s\n", strerror(errno)); + event->errorCode = errno; +#endif + dispatchEvent(event, HTTPFetcherEvent::EVENT_HTTP_ERROR); + killThread(); + return; + } + + + totalRec += recv_size; + if (!storeInFile){ + server_reply = (char*)realloc(server_reply, totalRec + DEFAULT_PAGE_BUF_SIZE); + rec = server_reply + totalRec; + } else { + server_reply[recv_size] = '\0'; + fwrite(server_reply, 1, recv_size, tempFile); + } + } while (recv_size !=0 && totalRec < i); + + if (totalRec > i){ + event->errorCode = HTTPFetcher::HTTPFETCHER_ERROR_WRONG_SIZE; + dispatchEvent(event, HTTPFetcherEvent::EVENT_HTTP_ERROR); + killThread(); + return; + } + if (storeInFile){ + event->storedInFile = true; + event->data = (char*)malloc(sizeof(char)*(savePath.length() + 1)); + strcpy(event->data, savePath.c_str()); + fclose(tempFile); + } else { + event->data = server_reply; + } + + event->contentSize = totalRec; + bodyReturn = event->data; + dispatchEvent(event, HTTPFetcherEvent::EVENT_HTTP_DATA_RECEIVED); + killThread(); +} + +void HTTPFetcher::fetchFile(String pathToFile, bool saveToPath, String savePath){ + path = pathToFile; + this->savePath = savePath; + this->storeInFile = saveToPath; + threadRunning = true; + CoreServices::getInstance()->getCore()->createThread(this); +} + +String HTTPFetcher::getData(){ + return this->bodyReturn; +} diff --git a/Core/Contents/Source/PolyImage.cpp b/Core/Contents/Source/PolyImage.cpp index a34af58cf..40c902ef3 100755 --- a/Core/Contents/Source/PolyImage.cpp +++ b/Core/Contents/Source/PolyImage.cpp @@ -28,6 +28,10 @@ #include "OSBasics.h" #include "PolyPerlin.h" #include +#include +#include "rgbe.h" +#define STB_IMAGE_IMPLEMENTATION +#include "stb_image.h" using namespace Polycode; @@ -62,7 +66,7 @@ void Image::setPixelType(int type) { pixelSize = 4; break; case IMAGE_FP16: - pixelSize = 16; + pixelSize = 6; break; default: pixelSize = 4; @@ -76,7 +80,7 @@ bool Image::isLoaded() const { Image::Image(int width, int height, int type) : imageData(NULL) { setPixelType(type); - createEmpty(width, height); + createEmpty(width, height, Color(0.0, 0.0, 0.0, 0.0)); } Image::Image(Image *copyImage) { @@ -167,14 +171,14 @@ int Image::getHeight() const { return height; } -void Image::createEmpty(int width, int height) { +void Image::createEmpty(int width, int height, const Color &fillColor) { free(imageData); imageData = (char*)malloc(width*height*pixelSize); this->width = width; this->height = height; - fill(Color(0,0,0,0)); + fill(fillColor); } void Image::perlinNoise(int seed, bool alpha) { @@ -193,13 +197,6 @@ void Image::perlinNoise(int seed, bool alpha) { } } -void Image::writeBMP(const String& fileName) const { -// SDL_Surface *image; -// image = SDL_CreateRGBSurface(SDL_SWSURFACE, width, height, 32, 0x0000FF, 0x00FF00, 0xFF0000, 0x000000); -// memcpy(image->pixels,imageData,width * height * 4); -// SDL_SaveBMP(image, fileName.c_str()); -} - void Image::fillRect(int x, int y, int w, int h, Color col) { for(int i=0; i < w; i++) { for(int j=0; j < h; j++) { @@ -252,218 +249,6 @@ void Image::setPixel(int x, int y, Number r, Number g, Number b, Number a) { imageData32[x+(y*width)] = color.getUint(); } -void Image::multiply(Number amt, bool color, bool alpha) { - int startIndex = 0; - int endIndex = 3; - if(!color) - startIndex = 3; - if(!alpha) - endIndex = 2; - - for (int i = 0; i < height*width*pixelSize; i+=pixelSize) { - for(int j = startIndex; j < endIndex+1;j++) { - if(((Number)imageData[i+j]) * amt< 0) - imageData[i+j] = 0; - else if(((Number)imageData[i+j]) * amt > 255) - imageData[i+j] = 255; - else - imageData[i+j] = (char)(((Number)imageData[i+j]) * amt); - } - } -} - -void Image::darken(Number amt, bool color, bool alpha) { - char decAmt = 255.0f * amt; - int startIndex = 0; - int endIndex = 3; - if(!color) - startIndex = 3; - if(!alpha) - endIndex = 2; - - for (int i = 0; i < height*width*pixelSize; i+=pixelSize) { - for(int j = startIndex; j < endIndex+1;j++) { - if(imageData[i+j]-decAmt < 0) - imageData[i+j] = 0; - else - imageData[i+j] -= decAmt; - } - } -} - -void Image::lighten(Number amt, bool color, bool alpha) { - char decAmt = 255.0f * amt; - int startIndex = 0; - int endIndex = 3; - if(!color) - startIndex = 3; - if(!alpha) - endIndex = 2; - - for (int i = 0; i < height*width*pixelSize; i+=pixelSize) { - for(int j = startIndex; j < endIndex+1;j++) { - if(imageData[i+j]+decAmt > 255) - imageData[i+j] = 255; - else - imageData[i+j] += decAmt; - } - } -} - -float* Image::createKernel(float radius, float deviation) { - int size = 2 * (int)radius + 1; - float* kernel = (float*)malloc(sizeof(float) * (size+1)); - float radiusf = fabs(radius) + 1.0f; - - if(deviation == 0.0f) deviation = sqrtf( - -(radiusf * radiusf) / (2.0f * logf(1.0f / 255.0f)) - ); - - kernel[0] = size; - - float value = -radius; - float sum = 0.0f; - int i; - - for(i = 0; i < size; i++) { - kernel[1 + i] = - 1.0f / (2.506628275f * deviation) * - expf(-((value * value) / (2.0f * (deviation * deviation)))); - - sum += kernel[1 + i]; - value += 1.0f; - } - - for(i = 0; i < size; i++) { - kernel[1 + i] /= sum; - } - return kernel; -} - -void Image::gaussianBlur(float radius, float deviation) { - - char *newData = (char*)malloc(width*height*pixelSize); - - char *horzBlur; - char *vertBlur; - - - horzBlur = (char*)malloc(sizeof(float)*pixelSize*width*height); - vertBlur = (char*)malloc(sizeof(float)*pixelSize*width*height); - - float *kernel = createKernel(radius, deviation); - - int i, iY, iX; - - // Horizontal pass. - for(iY = 0; iY < height; iY++) { - for(iX = 0; iX < width; iX++) { - float val[4]; - memset(val, 0, sizeof(float) * 4); - - int offset = ((int)kernel[0]) / -2; - - for(i = 0; i < ((int)kernel[0]); i++) { - int x = iX + offset; - - if(x < 0 || x >= width) { offset++; continue; } - - float kernip1 = kernel[i + 1]; - - if(imageType == IMAGE_FP16) { - float *dataPtr = (float*)&imageData[(width * pixelSize * iY) + (pixelSize * x)]; - for(int c=0; c < 4; c++) { - val[c] += kernip1 * dataPtr[c]; - } - - } else { - char *dataPtr = &imageData[(width * pixelSize * iY) + (pixelSize * x)]; - for(int c=0; c < pixelSize; c++) { - val[c] += kernip1 * ((float)dataPtr[c]); - } - } - - offset++; - } - - if(imageType == IMAGE_FP16) { - int baseOffset = (width * 4 * iY) + (4 * iX); - for(int c=0; c < 4; c++) { - float *f_horzBlur = (float*)horzBlur; - f_horzBlur[baseOffset+c] = val[c]; - } - } else { - int baseOffset = (width * pixelSize * iY) + (pixelSize * iX); - for(int c=0; c < pixelSize; c++) { - if(val[c] > 255.0) { - val[c] = 255.0; - } - horzBlur[baseOffset+c] = (char)val[c]; - } - } - } - } - - // Vertical pass. - for(iY = 0; iY < height; iY++) { - for(iX = 0; iX < width; iX++) { - float val[4]; - memset(val, 0, sizeof(float) * 4); - int offset = ((int)kernel[0]) / -2; - - for(i = 0; i < ((int)kernel[0]); i++) { - int y = iY + offset; - - if(y < 0 || y >= height) { - offset++; - continue; - } - - float kernip1 = kernel[i + 1]; - if(imageType == IMAGE_FP16) { - float *dataPtr = (float*)&horzBlur[(width * pixelSize * y) + (pixelSize * iX)]; - for(int c=0; c < 4; c++) { - val[c] += kernip1 * dataPtr[c]; - } - - } else { - char *dataPtr = &horzBlur[(width * pixelSize * y) + (pixelSize * iX)]; - for(int c=0; c < pixelSize; c++) { - val[c] += kernip1 * ((float)dataPtr[c]); - } - } - offset++; - } - - if(imageType == IMAGE_FP16) { - int baseOffset = (width * 4 * iY) + (4 * iX); - for(int c=0; c < 4; c++) { - float *f_vertBlur = (float*)vertBlur; - f_vertBlur[baseOffset+c] = val[c]; - } - } else { - int baseOffset = (width * pixelSize * iY) + (pixelSize * iX); - for(int c=0; c < pixelSize; c++) { - if(val[c] > 255.0) { - val[c] = 255.0; - } - vertBlur[baseOffset+c] = (char)val[c]; - } - } - } - } - - - memcpy(newData, vertBlur, height * width * pixelSize); - - free(horzBlur); - free(vertBlur); - free(kernel); - - free(imageData); - imageData = newData; -} - void Image::fastBlurHor(int blurSize) { if(blurSize == 0) return; @@ -564,50 +349,6 @@ void Image::fastBlurVert(int blurSize) { void Image::fastBlur(int blurSize) { fastBlurHor(blurSize); fastBlurVert(blurSize); -/* - unsigned char *blurImage = (unsigned char*)malloc(width*height*4); - - int total_r; - int total_g; - int total_b; - int total_a; - - unsigned int *imageData32 = (unsigned int*)imageData; - unsigned char *pixel; - int amt; - - for (int y = 0; y < height; y++) { - for (int x = 0; x < width; x++) { - total_r = 0; - total_g = 0; - total_b = 0; - total_a = 0; - amt = 0; - for (int ky = -blurSize; ky <= blurSize; ky++) { - for (int kx = -blurSize; kx <= blurSize ; kx++) { - if(x+kx+((y+ky)*width) > 0 && x+kx+((y+ky)*width) < width*height) { - pixel = (unsigned char*)&(imageData32[(x+kx)+((y+ky)*width)]); - total_r += pixel[0]; - total_g += pixel[1]; - total_b += pixel[2]; - total_a += pixel[3]; - amt++; - } - } - } - -// Logger::log("%d / %d = %d\n",total_r, amt, (total_r/amt)); - blurImage[((x+(y*width))*4)] = (total_r/amt); - blurImage[((x+(y*width))*4)+1] = (total_g / amt); - blurImage[((x+(y*width))*4)+2] = (total_b / amt); - blurImage[((x+(y*width))*4)+3] = (total_a / amt); - - } - } - - imageData = (char*)blurImage; -// free(imageData32); -*/ } void Image::swap(int *v1, int *v2) { @@ -653,11 +394,20 @@ void Image::drawLine(int x0, int y0, int x1, int y1, Color col) { } } -void Image::fill(Color color) { - unsigned int val = color.getUint(); - unsigned int *imageData32 = (unsigned int*) imageData; - for(int i=0; i< width*height; i++) { - imageData32[i] = val; +void Image::fill(const Color &color) { + if(imageType == Image::IMAGE_RGB) { + for(int i = 0; i < width*height*pixelSize; i+=3) { + imageData[i] = color.r; + imageData[i+1] = color.g; + imageData[i+2] = color.b; + } + } else { + unsigned int val = color.getUint(); + unsigned int *imageData32 = (unsigned int*) imageData; + + for(int i=0; i< width*height; i++) { + imageData32[i] = val; + } } } @@ -763,7 +513,178 @@ bool Image::savePNG(const String &fileName) { bool Image::loadImage(const String& fileName) { - return loadPNG(fileName); + + String extension; + size_t found; + found=fileName.rfind("."); + if (found != -1) { + extension = fileName.substr(found+1); + } else { + extension = ""; + } + + if(extension == "png") { + return loadPNG(fileName); + } else if(extension == "hdr") { + return loadHDR(fileName); + } else if(extension == "jpg" || extension == "tga" || extension == "psd") { + return loadSTB(fileName); + } else { + Logger::log("Error: Invalid image format.\n"); + return false; + } +} + +inline hfloat Image::convertFloatToHFloat(float f) { + float _f = f; + uint32_t x = *(uint32_t *)(&_f); + uint32_t sign = (uint32_t)(x >> 31); + uint32_t mantissa; + uint32_t exp; + hfloat hf; + + // get mantissa + mantissa = x & ((1 << 23) - 1); + // get exponent bits + exp = x & FLOAT_MAX_BIASED_EXP; + if (exp >= HALF_FLOAT_MAX_BIASED_EXP_AS_SINGLE_FP_EXP) + { + // check if the original single precision float number is a NaN + if (mantissa && (exp == FLOAT_MAX_BIASED_EXP)) + { + // we have a single precision NaN + mantissa = (1 << 23) - 1; + } + else + { + // 16-bit half-float representation stores number as Inf + mantissa = 0; + } + hf = (((hfloat)sign) << 15) | (hfloat)(HALF_FLOAT_MAX_BIASED_EXP) | + (hfloat)(mantissa >> 13); + } + // check if exponent is <= -15 + else if (exp <= HALF_FLOAT_MIN_BIASED_EXP_AS_SINGLE_FP_EXP) + { + + // store a denorm half-float value or zero + exp = (HALF_FLOAT_MIN_BIASED_EXP_AS_SINGLE_FP_EXP - exp) >> 23; + mantissa >>= (14 + exp); + + hf = (((hfloat)sign) << 15) | (hfloat)(mantissa); + } + else + { + hf = (((hfloat)sign) << 15) | + (hfloat)((exp - HALF_FLOAT_MIN_BIASED_EXP_AS_SINGLE_FP_EXP) >> 13) | + (hfloat)(mantissa >> 13); + } + + return hf; +} + +TokenArray Image::readTokens(char *line, const char *tokenString) { + char **tokens = (char**)malloc(sizeof(void*)); + char *pch; + int numTokens = 0; + pch = strtok (line, tokenString); + while (pch != NULL) { + numTokens++; + tokens = (char**)realloc(tokens, sizeof(void*) *numTokens); + tokens[numTokens-1] = (char*) malloc(strlen(pch)+1); + memcpy(tokens[numTokens-1], pch, strlen(pch)+1); + pch = strtok (NULL, tokenString); + } + + TokenArray ta; + ta.size = numTokens; + ta.tokens = tokens; + return ta; +} + +void Image::freeTokens(TokenArray tokens) { + int i; + for(i =0; i < tokens.size; i++) { + free(tokens.tokens[i]); + } + free(tokens.tokens); +} + +bool Image::loadSTB(const String &fileName) { + + OSFILE *infile = OSBasics::open(fileName.c_str(), "rb"); + + if(!infile) { + Logger::log("Error opening image file: %s\n", fileName.c_str()); + return false; + } + + OSBasics::seek(infile, 0, SEEK_END); + long bufferLen = OSBasics::tell(infile); + OSBasics::seek(infile, 0, SEEK_SET); + + char *buffer = (char*) malloc(bufferLen); + OSBasics::read(buffer, bufferLen, 1, infile); + + int x,y,n; + stbi_uc *data = stbi_load_from_memory((const stbi_uc*)buffer, bufferLen, &x, &y, &n, 4); + + if(!data) { + Logger::log("Error reading image data: %s\n", fileName.c_str()); + return false; + } + + imageType = Image::IMAGE_RGBA; + + width = x; + height = y; + + free(buffer); + + imageData = (char*)data; + + OSBasics::close(infile); + + return true; +} + +bool Image::loadHDR(const String &fileName) { + + imageType = Image::IMAGE_FP16; + + OSFILE *infile = OSBasics::open(fileName.c_str(), "rb"); + + if(!infile) { + Logger::log("Error opening HDR %s\n", fileName.c_str()); + return false; + } + + OSBasics::seek(infile, 0, SEEK_END); + long bufferLen = OSBasics::tell(infile); + OSBasics::seek(infile, 0, SEEK_SET); + + char *buffer = (char*) malloc(bufferLen); + OSBasics::read(buffer, bufferLen, 1, infile); + + int x,y,n; + float *data = stbi_loadf_from_memory((const stbi_uc*)buffer, bufferLen, &x, &y, &n, 0); + + if(!data) { + Logger::log("Error reading image data: %s\n", fileName.c_str()); + return false; + } + + width = x; + height = y; + + free(buffer); + + imageData = (char*)data; + + OSBasics::close(infile); + + + return true; } bool Image::loadPNG(const String& fileName) { @@ -887,5 +808,5 @@ void Image::transformCoordinates(int *x, int *y) { } void Image::transformCoordinates(int *x, int *y, int *w, int *h) { - *y = this->height - *h - *y - 1; + *y = this->height - *h - *y; } diff --git a/Core/Contents/Source/PolyInputEvent.cpp b/Core/Contents/Source/PolyInputEvent.cpp index 9138a7065..5470ee1fc 100755 --- a/Core/Contents/Source/PolyInputEvent.cpp +++ b/Core/Contents/Source/PolyInputEvent.cpp @@ -24,6 +24,9 @@ namespace Polycode { +TouchInfo::TouchInfo() : type(TYPE_TOUCH) { +} + InputEvent::InputEvent() : Event() { eventType = "InputEvent"; } @@ -40,6 +43,10 @@ InputEvent::InputEvent(PolyKEY key, wchar_t charCode, int timestamp) : Event() { this->timestamp = timestamp; eventType = "InputEvent"; } + +wchar_t InputEvent::getCharCode() { + return charCode; +} /* InputEvent::InputEvent(PolyKEY key, int timestamp) : Event() { diff --git a/Core/Contents/Source/PolyLabel.cpp b/Core/Contents/Source/PolyLabel.cpp index e78024826..2511eeec7 100755 --- a/Core/Contents/Source/PolyLabel.cpp +++ b/Core/Contents/Source/PolyLabel.cpp @@ -21,7 +21,8 @@ */ #include "PolyLabel.h" - +#include + using namespace Polycode; //#define NORMAL_FT_FLAGS FT_LOAD_TARGET_LIGHT @@ -58,7 +59,7 @@ ColorRange::ColorRange(Color color, unsigned int rangeStart, unsigned int rangeE } -Label::Label(Font *font, const String& text, int size, int antiAliasMode, bool premultiplyAlpha) : Image(), _optionsChanged(false) { +Label::Label(Font *font, const String& text, int size, int antiAliasMode, bool premultiplyAlpha, const Color &backgroundColor, const Color &foregroundColor) : Image(), backgroundColor(backgroundColor), foregroundColor(foregroundColor), _optionsChanged(false) { setPixelType(Image::IMAGE_RGBA); this->font = font; this->size = size; @@ -76,6 +77,14 @@ unsigned int Label::getSize() const { return size; } +bool Label::getPremultiplyAlpha() const { + return premultiplyAlpha; +} + +void Label::setPremultiplyAlpha(bool val) { + premultiplyAlpha = val; +} + void Label::setSize(int newSize) { size = newSize; _optionsChanged = true; @@ -210,7 +219,7 @@ Color Label::getColorForIndex(unsigned int index) { return colorRanges[i].color; } } - return Color(1.0,1.0,1.0,1.0); + return foregroundColor; } void Label::precacheGlyphs(String text, GlyphData *glyphData) { @@ -263,12 +272,19 @@ void Label::precacheGlyphs(String text, GlyphData *glyphData) { glyphData->positions[glyphData->num_glyphs].y = pen_y; switch(antiAliasMode) { + case ANTIALIAS_LCD_HINT: + case ANTIALIAS_FULL_HINT: + error = FT_Load_Glyph( face, glyph_index, FT_LOAD_DEFAULT); + break; + case ANTIALIAS_LCD: + error = FT_Load_Glyph( face, glyph_index, FT_LOAD_TARGET_LIGHT); + break; case ANTIALIAS_FULL: case ANTIALIAS_STRONG: - error = FT_Load_Glyph( face, glyph_index, FT_LOAD_TARGET_LIGHT); + error = FT_Load_Glyph( face, glyph_index, FT_LOAD_TARGET_LIGHT); break; default: - error = FT_Load_Glyph( face, glyph_index, FT_LOAD_DEFAULT); + error = FT_Load_Glyph( face, glyph_index, FT_LOAD_DEFAULT); break; } @@ -292,11 +308,35 @@ void Label::precacheGlyphs(String text, GlyphData *glyphData) { } } +Color Label::getBackgroundColor() { + return backgroundColor; +} + +Color Label::getForegroundColor() { + return foregroundColor; +} + +void Label::setBackgroundColor(const Color &color) { + backgroundColor = color; + _optionsChanged = true; +} + +void Label::setForegroundColor(const Color &color) { + foregroundColor = color; + _optionsChanged = true; +} + +void Label::setColors(const Color &backgroundColor, const Color &foregroundColor) { + this->backgroundColor = backgroundColor; + this->foregroundColor = foregroundColor; + _optionsChanged = true; +} + int Label::getBaselineAdjust() { return baseLineAdjust; } -void Label::drawGlyphBitmap(FT_Bitmap *bitmap, unsigned int x, unsigned int y, Color glyphColor) { +void Label::drawGlyphBitmap(FT_Bitmap *bitmap, unsigned int x, unsigned int y, const Color &glyphColor) { int lineoffset = (height-y) * (width*4); int xoff = (x*4); @@ -307,8 +347,47 @@ void Label::drawGlyphBitmap(FT_Bitmap *bitmap, unsigned int x, unsigned int y, C } switch(antiAliasMode) { + case ANTIALIAS_LCD_HINT: + case ANTIALIAS_LCD: + { + unsigned char *src = bitmap->buffer; + for(int j=0; j < bitmap->rows;j++) { + unsigned char *bptr = src; + for(int k=0; k < bitmap->width ; k+=3){ + + // dst = alpha * src + (1 - alpha) * dst + + Number nVal = (((Number)(*(bptr)))/255.0); + Number destVal = pow(((Number)(unsigned char)imageData[xoff+lineoffset]) / 255.0, LCD_BLEND_GAMMA); + + Number final = pow((nVal * pow(glyphColor.r, LCD_BLEND_GAMMA)) + ((1.0-nVal) * destVal), 1.0/LCD_BLEND_GAMMA); + + imageData[xoff+lineoffset] = (int)(final * 255.0); + + nVal = (((Number)(*(bptr+1)))/255.0); + destVal = pow(((Number)(unsigned char)imageData[xoff+lineoffset+1]) / 255.0, LCD_BLEND_GAMMA); + final = pow((nVal * pow(glyphColor.g, LCD_BLEND_GAMMA)) + ((1.0-nVal) * destVal), 1.0/LCD_BLEND_GAMMA); + + imageData[xoff+lineoffset+1] = (int)(final * 255.0); + + nVal = (((Number)(*(bptr+2)))/255.0); + destVal = pow(((Number)(unsigned char)imageData[xoff+lineoffset+2]) / 255.0, LCD_BLEND_GAMMA); + final = pow((nVal * pow(glyphColor.b, LCD_BLEND_GAMMA)) + ((1.0-nVal) * destVal), 1.0/LCD_BLEND_GAMMA); + imageData[xoff+lineoffset+2] = (int)(final * 255.0); + + imageData[xoff+lineoffset+3] = 255; + bptr += 3; + xoff += 4; + } + lineoffset -= ((width*4)+(bitmap->width * 4 / 3)); + src += bitmap->pitch; + } + + } + break; case ANTIALIAS_FULL: - case ANTIALIAS_STRONG: + case ANTIALIAS_STRONG: + case ANTIALIAS_FULL_HINT: for(int j = 0; j < ((bitmap->width * bitmap->rows)); j++) { if(!(j % bitmap->width) && j !=0) lineoffset -= ((width*4)+(bitmap->width * 4)); @@ -322,9 +401,9 @@ void Label::drawGlyphBitmap(FT_Bitmap *bitmap, unsigned int x, unsigned int y, C imageData[xoff+lineoffset+3] = newVal; if(premultiplyAlpha) { - imageData[xoff+lineoffset] = (int)((255.0 * glyphColor.r) * ((Number)imageData[xoff+lineoffset+3])/255.0); - imageData[xoff+lineoffset+1] = (int)((255.0 * glyphColor.g) * ((Number)imageData[xoff+lineoffset+3])/255.0); - imageData[xoff+lineoffset+2] = (int)((255.0 * glyphColor.b) * ((Number)imageData[xoff+lineoffset+3])/255.0); + imageData[xoff+lineoffset] = (int)((255.0 * glyphColor.r) * ((Number)(unsigned char)imageData[xoff+lineoffset+3])/255.0); + imageData[xoff+lineoffset+1] = (int)((255.0 * glyphColor.g) * ((Number)(unsigned char)imageData[xoff+lineoffset+3])/255.0); + imageData[xoff+lineoffset+2] = (int)((255.0 * glyphColor.b) * ((Number)(unsigned char)imageData[xoff+lineoffset+3])/255.0); } else { imageData[xoff+lineoffset] = (int)(255.0 * glyphColor.r); imageData[xoff+lineoffset+1] = (int)(255.0 * glyphColor.g); @@ -362,7 +441,7 @@ void Label::renderGlyphs(GlyphData *glyphData) { useColorRanges = true; } - Color glyphColor = Color(1.0, 1.0, 1.0, 1.0); + Color glyphColor = foregroundColor; int start_x = 0; //( ( my_target_width - string_width ) / 2 ) * 64; int start_y = 0; //( ( my_target_height - string_height ) / 2 ) * 64; @@ -378,8 +457,10 @@ void Label::renderGlyphs(GlyphData *glyphData) { pen.x = (start_x + glyphData->positions[n].x) * 64; pen.y = (start_y + glyphData->positions[n].y) * 64; - if(antiAliasMode == ANTIALIAS_FULL || antiAliasMode == ANTIALIAS_STRONG) { - error = FT_Glyph_To_Bitmap( &image, FT_RENDER_MODE_LIGHT, &pen, 0 ); + if(antiAliasMode == ANTIALIAS_LCD || antiAliasMode == ANTIALIAS_LCD_HINT) { + error = FT_Glyph_To_Bitmap( &image, FT_RENDER_MODE_LCD, &pen, 0 ); + } else if(antiAliasMode == ANTIALIAS_FULL || antiAliasMode == ANTIALIAS_STRONG || antiAliasMode == ANTIALIAS_FULL_HINT) { + error = FT_Glyph_To_Bitmap( &image, FT_RENDER_MODE_NORMAL, &pen, 0 ); } else { error = FT_Glyph_To_Bitmap( &image, FT_RENDER_MODE_MONO, &pen, 0 ); } @@ -391,7 +472,7 @@ void Label::renderGlyphs(GlyphData *glyphData) { FT_BitmapGlyph bit = (FT_BitmapGlyph)image; drawGlyphBitmap(&bit->bitmap, - bit->left - xAdjustOffset, + bit->left - xAdjustOffset + 1, height - bit->top + baseLineOffset, glyphColor); FT_Done_Glyph( image ); @@ -409,7 +490,7 @@ void Label::setText(const String& text) { return; if(!font->isValid()) return; - + this->text = text; precacheGlyphs(text, &labelData); @@ -417,14 +498,24 @@ void Label::setText(const String& text) { FT_BBox bbox; computeStringBbox(&labelData, &bbox); - unsigned int textWidth = (bbox.xMax - bbox.xMin)+1; + unsigned int textWidth = (bbox.xMax - bbox.xMin)+2; unsigned int textHeight = (bbox.yMax - bbox.yMin)+1; - + baseLineOffset = bbox.yMin; xAdjustOffset = bbox.xMin; baseLineAdjust = bbox.yMax; + + + if(textWidth % 2 ){ + textWidth++; + } + if(textHeight % 2 ){ + textHeight++; + baseLineAdjust++; + } + - createEmpty(textWidth,textHeight); + createEmpty(textWidth,textHeight, backgroundColor); renderGlyphs(&labelData); _optionsChanged = false; } diff --git a/Core/Contents/Source/PolyLogger.cpp b/Core/Contents/Source/PolyLogger.cpp index 50fc405c1..c9b6c881c 100755 --- a/Core/Contents/Source/PolyLogger.cpp +++ b/Core/Contents/Source/PolyLogger.cpp @@ -20,14 +20,21 @@ THE SOFTWARE. */ +#include "PolyLogger.h" +#ifdef _MSC_VER +#include +#endif #include "PolyLogger.h" #include #include #include #include +#include using namespace Polycode; +Logger* Logger::overrideInstance = NULL; + LoggerEvent::LoggerEvent(String message) : Event() { this->message = message; } @@ -38,11 +45,15 @@ LoggerEvent::~LoggerEvent() { Logger::Logger() : EventDispatcher() { - + logToFile = false; + logFile = NULL; } Logger::~Logger() { - + if(logFile) { + fclose(logFile); + } + overrideInstance = NULL; } void Logger::logBroadcast(String message) { @@ -59,19 +70,36 @@ void Logger::log(const char *format, ...) { va_start(args, format); vfprintf(stderr, format, args); va_end(args); + + if (Logger::getInstance()->getLogToFile()){ + if (Logger::getInstance()->getLogFile()){ + va_start(args, format); + vfprintf(Logger::getInstance()->getLogFile(), format, args); + fflush(Logger::getInstance()->getLogFile()); + va_end(args); + } else { + time_t t = time(NULL); + char mbstr[100]; + if (strftime(mbstr, sizeof(mbstr), "%y_%m_%d.log", localtime(&t))) { + Logger::getInstance()->setLogFile(fopen((const char*)mbstr, "w")); + } else { + Logger::getInstance()->setLogFile(fopen("poly.log", "w")); + } + } + } -#ifdef MSVC +#ifdef _MSC_VER #ifdef _DEBUG - + char buffer[4096]; va_start(args, format); vsprintf(buffer, format, args); va_end(args); - WCHAR wbuf[4096]; + wchar_t wbuf[4096]; int i = 0; while(buffer[i] != '\0') { - wbuf[i] = (WCHAR)buffer[i]; + wbuf[i] = (wchar_t)buffer[i]; ++i; } wbuf[i] = L'\0'; @@ -81,3 +109,38 @@ void Logger::log(const char *format, ...) { #endif } + +void Logger::setLogToFile(bool val){ + if (!logToFile && val){ + time_t t = time(NULL); + char mbstr[100]; + if (strftime(mbstr, sizeof(mbstr), "%y_%m_%d.log", localtime(&t))) { + logFile = fopen((const char*)mbstr, "w"); + } else { + logFile = fopen("poly.log", "w"); + } + } + + logToFile = val; +} + +void Logger::setLogFile(FILE *f){ + logFile = f; +} + +bool Logger::getLogToFile(){ + return logToFile; +} + +FILE *Logger::getLogFile(){ + return logFile; +} + +Logger *Logger::getInstance(){ + if (overrideInstance) { + return overrideInstance; + } + + overrideInstance = new Logger; + return overrideInstance; +} \ No newline at end of file diff --git a/Core/Contents/Source/PolyMaterial.cpp b/Core/Contents/Source/PolyMaterial.cpp index fe0d349b3..d5d5f5b97 100755 --- a/Core/Contents/Source/PolyMaterial.cpp +++ b/Core/Contents/Source/PolyMaterial.cpp @@ -36,17 +36,22 @@ Material::Material(const String& name) : Resource(Resource::RESOURCE_MATERIAL) { shaderModule = NULL; blendingMode = Renderer::BLEND_MODE_NORMAL; screenMaterial = false; + wireframe = false; + + Services()->getCore()->addEventListener(this, Core::EVENT_CORE_RESIZE); } Material::~Material() { Logger::log("deleting material (%s)\n", name.c_str()); - + Services()->getCore()->removeAllHandlersForListener(this); clearShaders(); } void Material::setName(const String &name) { this->name = name; + setResourceName(name); + dispatchEvent(new Event(), Event::RESOURCE_CHANGE_EVENT); } void Material::clearShaders() { @@ -108,7 +113,7 @@ void Material::recreateRenderTarget(ShaderRenderTarget *renderTarget) { textureWidth = (int)renderTarget->width; textureHeight = (int)renderTarget->height; } - + CoreServices::getInstance()->getRenderer()->createRenderTextures(&newTexture, NULL, textureWidth, textureHeight, fp16RenderTargets); newTexture->setResourceName(renderTarget->id); @@ -132,13 +137,24 @@ void Material::recreateRenderTarget(ShaderRenderTarget *renderTarget) { } void Material::handleEvent(Event *event) { - std::vector _materialShaders = materialShaders; - clearShaders(); - for(int i=0; i < _materialShaders.size(); i++) { - ShaderBinding *newShaderBinding = _materialShaders[i]->createBinding(); - addShader(_materialShaders[i], newShaderBinding); - } - dispatchEvent(new Event(), Event::RESOURCE_RELOAD_EVENT); + + if(event->getDispatcher() == Services()->getCore()) { + recreateRenderTargets(); + } else { + //Fix the bindings when we detect a reload + for (int i = 0; i < materialShaders.size(); i++) { + Shader* shader = materialShaders[i]; + ShaderBinding* shaderBinding = shaderBindings[i]; + CoreServices::getInstance()->getRenderer()->setRendererShaderParams(shader, shaderBinding); + + for(int i=0; i < shader->expectedParams.size(); i++) { + if(!shaderBinding->getLocalParamByName(shader->expectedParams[i].name)) { + shaderBinding->addParam(shader->expectedParams[i].type, shader->expectedParams[i].name); + } + } + } + dispatchEvent(new Event(), Event::RESOURCE_RELOAD_EVENT); + } } void Material::removeShader(int shaderIndex) { diff --git a/Core/Contents/Source/PolyMaterialManager.cpp b/Core/Contents/Source/PolyMaterialManager.cpp index d74e63c50..841427809 100755 --- a/Core/Contents/Source/PolyMaterialManager.cpp +++ b/Core/Contents/Source/PolyMaterialManager.cpp @@ -38,57 +38,43 @@ MaterialManager::MaterialManager() { premultiplyAlphaOnLoad = false; clampDefault = false; mipmapsDefault = true; + keepTextureData = true; } MaterialManager::~MaterialManager() { - -} - -void MaterialManager::Update(int elapsed) { - for(int i=0;i < textures.size(); i++) { - textures[i]->updateScroll(elapsed); - } -} - -Texture *MaterialManager::getTextureByResourcePath(const String& resourcePath) const { - for(int i=0;i < textures.size(); i++) { - if(textures[i]->getResourcePath() == resourcePath) - return textures[i]; - } - return NULL; -} - -void MaterialManager::deleteTexture(Texture *texture) { - for(int i=0;i < textures.size(); i++) { - if(textures[i] == texture) { - textures.erase(textures.begin()+i); - CoreServices::getInstance()->getRenderer()->destroyTexture(texture); - return; - } - } } -void MaterialManager::reloadPrograms() { - for(int m=0; m < shaderModules.size(); m++) { - PolycodeShaderModule *shaderModule = shaderModules[m]; - shaderModule->reloadPrograms(); - } - vector shaders = CoreServices::getInstance()->getResourceManager()->getResources(Resource::RESOURCE_SHADER); - for(int s = 0; s < shaders.size(); s++) { - Shader *shader = (Shader *)shaders[s]; - shader->reload(); - } +void MaterialManager::loadMaterialLibraryIntoPool(ResourcePool *pool, const String &materialFile) { + printf("LOADING [%s] into pool [%s]\n", materialFile.c_str(), pool->getName().c_str()); + std::vector shaders =loadShadersFromFile(pool, materialFile); + + for(int s=0; s < shaders.size(); s++) { + pool->addResource(shaders[s]); + } + + std::vector cubemaps = loadCubemapsFromFile(materialFile); + for(int c=0; c < cubemaps.size(); c++) { + pool->addResource(cubemaps[c]); + } + + std::vector materials = loadMaterialsFromFile(pool, materialFile); + + for(int m=0; m < materials.size(); m++) { + materials[m]->setResourceName(materials[m]->getName()); + pool->addResource(materials[m]); + } } ShaderProgram *MaterialManager::createProgramFromFile(String programPath) { OSFileEntry entry(programPath, OSFileEntry::TYPE_FILE); - + for(int m=0; m < shaderModules.size(); m++) { PolycodeShaderModule *shaderModule = shaderModules[m]; if(shaderModule->acceptsExtension(entry.extension)) { ShaderProgram *newProgram = shaderModule->createProgramFromFile(entry.extension, entry.fullPath); if(newProgram) { newProgram->setResourcePath(programPath); + newProgram->setResourceName(programPath); } return newProgram; } @@ -102,14 +88,14 @@ void MaterialManager::addShaderModule(PolycodeShaderModule *module) { #define DEFAULT_TEXTURE "default/default.png" -Texture *MaterialManager::createTextureFromFile(const String& fileName, bool clamp, bool createMipmaps) { - if(fileName.size() == 0) { - Logger::log("empty texture filename, using default texture.\n"); - return getTextureByResourcePath(DEFAULT_TEXTURE); - } +Texture *MaterialManager::createTextureFromFile(const String& fileName, bool clamp, bool createMipmaps, ResourcePool *resourcePool) { + + if(!resourcePool) { + resourcePool = CoreServices::getInstance()->getResourceManager()->getGlobalPool(); + } Texture *newTexture; - newTexture = getTextureByResourcePath(fileName); + newTexture = (Texture*) resourcePool->getResourceByPath(fileName); if(newTexture) { return newTexture; } @@ -119,12 +105,12 @@ Texture *MaterialManager::createTextureFromFile(const String& fileName, bool cla if(premultiplyAlphaOnLoad) { image->premultiplyAlpha(); } - newTexture = createTexture(image->getWidth(), image->getHeight(), image->getPixels(), clamp, createMipmaps); + newTexture = createTexture(image->getWidth(), image->getHeight(), image->getPixels(), clamp, createMipmaps, image->getType()); newTexture->setResourcePath(fileName); - CoreServices::getInstance()->getResourceManager()->addResource(newTexture); + resourcePool->addResource(newTexture); } else { Logger::log("Error loading image (\"%s\"), using default texture.\n", fileName.c_str()); - newTexture = getTextureByResourcePath(DEFAULT_TEXTURE); + newTexture = (Texture*) CoreServices::getInstance()->getResourceManager()->getGlobalPool()->getResourceByPath(DEFAULT_TEXTURE); } delete image; @@ -147,59 +133,44 @@ Texture *MaterialManager::createNewTexture(int width, int height, bool clamp, bo Texture *MaterialManager::createTexture(int width, int height, char *imageData, bool clamp, bool createMipmaps, int type) { Texture *newTexture = CoreServices::getInstance()->getRenderer()->createTexture(width, height, imageData,clamp, createMipmaps, type); - textures.push_back(newTexture); + if(!keepTextureData) { + free(newTexture->textureData); + newTexture->textureData = NULL; + } return newTexture; } Texture *MaterialManager::createTextureFromImage(Image *image, bool clamp, bool createMipmaps) { Texture *newTexture; newTexture = createTexture(image->getWidth(), image->getHeight(), image->getPixels(),clamp, createMipmaps, image->getType()); + if(!keepTextureData) { + free(newTexture->textureData); + newTexture->textureData = NULL; + } return newTexture; } -void MaterialManager::reloadProgramsAndTextures() { - reloadTextures(); - reloadPrograms(); -} - -void MaterialManager::reloadTextures() { - for(int i=0; i < textures.size(); i++) { - Texture *texture = textures[i]; - texture->recreateFromImageData(); - } -} - -unsigned int MaterialManager::getNumShaders() { - return shaders.size(); -} - -Shader *MaterialManager::getShaderByIndex(unsigned int index) { - if(index < shaders.size()) - return shaders[index]; - else - return NULL; -} - -Shader *MaterialManager::createShader(String shaderType, String name, String vpName, String fpName, bool screenShader) { +Shader *MaterialManager::createShader(ResourcePool *resourcePool, String shaderType, String name, String vpName, String fpName, bool screenShader) { Shader *retShader = NULL; for(int m=0; m < shaderModules.size(); m++) { PolycodeShaderModule *shaderModule = shaderModules[m]; if(shaderModule->getShaderType() == shaderType) { - retShader = shaderModule->createShader(name, vpName, fpName); + retShader = shaderModule->createShader(resourcePool, name, vpName, fpName); } } if(retShader) { retShader->screenShader = screenShader; - retShader->numAreaLights = 0; + retShader->numPointLights = 0; retShader->numSpotLights = 0; + retShader->setResourceName(name); } - + return retShader; } -Shader *MaterialManager::createShaderFromXMLNode(TiXmlNode *node) { +Shader *MaterialManager::createShaderFromXMLNode(ResourcePool *resourcePool, TiXmlNode *node) { TiXmlElement *nodeElement = node->ToElement(); if (!nodeElement) return NULL; // Skip comment nodes @@ -210,7 +181,7 @@ Shader *MaterialManager::createShaderFromXMLNode(TiXmlNode *node) { for(int m=0; m < shaderModules.size(); m++) { PolycodeShaderModule *shaderModule = shaderModules[m]; if(shaderModule->getShaderType() == shaderType) { - retShader = shaderModule->createShader(node); + retShader = shaderModule->createShader(resourcePool, node); } } } @@ -218,11 +189,11 @@ Shader *MaterialManager::createShaderFromXMLNode(TiXmlNode *node) { if (!retShader) return NULL; - int numAreaLights = 0; + int numPointLights = 0; int numSpotLights = 0; - if(nodeElement->Attribute("numAreaLights")) { - numAreaLights = atoi(nodeElement->Attribute("numAreaLights")); + if(nodeElement->Attribute("numPointLights")) { + numPointLights = atoi(nodeElement->Attribute("numPointLights")); } if(nodeElement->Attribute("numSpotLights")) { numSpotLights = atoi(nodeElement->Attribute("numSpotLights")); @@ -237,14 +208,14 @@ Shader *MaterialManager::createShaderFromXMLNode(TiXmlNode *node) { } if(retShader) { - retShader->numAreaLights = numAreaLights; + retShader->numPointLights = numPointLights; retShader->numSpotLights = numSpotLights; } return retShader; } -Shader *MaterialManager::setShaderFromXMLNode(TiXmlNode *node) { +Shader *MaterialManager::setShaderFromXMLNode(ResourcePool *resourcePool, TiXmlNode *node) { TiXmlElement *nodeElement = node->ToElement(); if (!nodeElement) return NULL; // Skip comment nodes @@ -256,22 +227,11 @@ Shader *MaterialManager::setShaderFromXMLNode(TiXmlNode *node) { retShader = fShader; } } else { - retShader = (Shader*)CoreServices::getInstance()->getResourceManager()->getResource(Resource::RESOURCE_SHADER, nodeElement->Attribute("name")); + retShader = (Shader*)resourcePool->getResource(Resource::RESOURCE_SHADER, nodeElement->Attribute("name")); } return retShader; } - -// for (pChild = node->FirstChild(); pChild != 0; pChild = pChild->NextSibling()) { -// if(strcmp(pChild->Value(), "textures") == 0) { -// for (pChild2 = pChild->FirstChild(); pChild2 != 0; pChild2 = pChild2->NextSibling()) { -// if(strcmp(pChild2->Value(), "texture") == 0) -// fShader->setDiffuseTexture((Texture*)CoreServices::getInstance()->getResourceManager()->getResource(Resource::RESOURCE_TEXTURE, pChild2->ToElement()->GetText())); -// } -// } -// } - - Cubemap *MaterialManager::cubemapFromXMLNode(TiXmlNode *node) { TiXmlElement *nodeElement = node->ToElement(); if (!nodeElement) return NULL; // Skip comment nodes @@ -298,15 +258,7 @@ Cubemap *MaterialManager::cubemapFromXMLNode(TiXmlNode *node) { return newCubemap; } -void MaterialManager::addMaterial(Material *material) { - materials.push_back(material); -} - -void MaterialManager::addShader(Shader *shader) { - shaders.push_back(shader); -} - -std::vector MaterialManager::loadShadersFromFile(String fileName) { +std::vector MaterialManager::loadShadersFromFile(ResourcePool *resourcePool, String fileName) { std::vector retVector; TiXmlDocument doc(fileName.c_str()); @@ -319,7 +271,7 @@ std::vector MaterialManager::loadShadersFromFile(String fileName) { if(mElem) { TiXmlNode* pChild; for (pChild = mElem->FirstChild(); pChild != 0; pChild = pChild->NextSibling()) { - Shader *newShader = createShaderFromXMLNode(pChild); + Shader *newShader = createShaderFromXMLNode(resourcePool, pChild); if(newShader != NULL) { Logger::log("Adding shader %s\n", newShader->getName().c_str()); newShader->setResourceName(newShader->getName()); @@ -355,7 +307,7 @@ std::vector MaterialManager::loadCubemapsFromFile(String fileName) { return retVector; } -std::vector MaterialManager::loadMaterialsFromFile(String fileName) { +std::vector MaterialManager::loadMaterialsFromFile(ResourcePool *resourcePool, const String &fileName) { std::vector retVector; TiXmlDocument doc(fileName.c_str()); @@ -368,7 +320,7 @@ std::vector MaterialManager::loadMaterialsFromFile(String fileName) { if(mElem) { TiXmlNode* pChild; for (pChild = mElem->FirstChild(); pChild != 0; pChild = pChild->NextSibling()) { - Material *newMat = materialFromXMLNode(pChild); + Material *newMat = materialFromXMLNode(resourcePool, pChild); if (newMat) { retVector.push_back(newMat); } @@ -379,10 +331,11 @@ std::vector MaterialManager::loadMaterialsFromFile(String fileName) { return retVector; } -Material *MaterialManager::createMaterial(String materialName, String shaderName) { +Material *MaterialManager::createMaterial(ResourcePool *resourcePool, String materialName, String shaderName) { Material *newMaterial = new Material(materialName); + newMaterial->setResourceName(materialName); - Shader *retShader = (Shader*)CoreServices::getInstance()->getResourceManager()->getResource(Resource::RESOURCE_SHADER, shaderName); + Shader *retShader = (Shader*)resourcePool->getResource(Resource::RESOURCE_SHADER, shaderName); if(retShader) { ShaderBinding *newShaderBinding = retShader->createBinding(); @@ -392,7 +345,7 @@ Material *MaterialManager::createMaterial(String materialName, String shaderName return newMaterial; } -Material *MaterialManager::materialFromXMLNode(TiXmlNode *node) { +Material *MaterialManager::materialFromXMLNode(ResourcePool *resourcePool, TiXmlNode *node) { TiXmlElement *nodeElement = node->ToElement(); if (!nodeElement) return NULL; // Skip comment nodes @@ -407,11 +360,17 @@ Material *MaterialManager::materialFromXMLNode(TiXmlNode *node) { Material *newMaterial = new Material(mname); + newMaterial->setResourceName(mname); + if(nodeElement->Attribute("screen")) { if(String(nodeElement->Attribute("screen")) == "true") { newMaterial->screenMaterial = true; } - } + } + + if(nodeElement->Attribute("wireframe")) { + newMaterial->wireframe = String(nodeElement->Attribute("wireframe")) == "true"; + } if(nodeElement->Attribute("blendingMode")) { newMaterial->blendingMode = atoi(nodeElement->Attribute("blendingMode")); @@ -467,7 +426,7 @@ Material *MaterialManager::materialFromXMLNode(TiXmlNode *node) { if (!pChild3Element) continue; // Skip comment nodes if(strcmp(pChild3->Value(), "shader") == 0) { - materialShader = setShaderFromXMLNode(pChild3); + materialShader = setShaderFromXMLNode(resourcePool, pChild3); if(materialShader) { newShaderBinding = materialShader->createBinding(); materialShaders.push_back(materialShader); @@ -485,50 +444,12 @@ Material *MaterialManager::materialFromXMLNode(TiXmlNode *node) { String pname = pChild2Element->Attribute("name"); if(!CoreServices::getInstance()->getRenderer()->getDataPointerForName(pname)) { - String pvalue = pChild2Element->Attribute("value"); - int type = materialShader->getExpectedParamType(pname); - LocalShaderParam *param = newShaderBinding->addParam(type, pname); - - - if(param) { - switch(type) { - case ProgramParam::PARAM_NUMBER: - { - param->setNumber(atof(pvalue.c_str())); - } - break; - case ProgramParam::PARAM_VECTOR2: - { - std::vector values = pvalue.split(" "); - if(values.size() == 2) { - param->setVector2(Vector2(atof(values[0].c_str()), atof(values[1].c_str()))); - } else { - printf("Material parameter error: A Vector2 must have 2 values (%d provided)!\n", (int)values.size()); - } - } - break; - case ProgramParam::PARAM_VECTOR3: - { - std::vector values = pvalue.split(" "); - if(values.size() == 3) { - param->setVector3(Vector3(atof(values[0].c_str()), atof(values[1].c_str()), atof(values[2].c_str()))); - } else { - printf("Material parameter error: A Vector3 must have 3 values (%d provided)!\n", (int)values.size()); - } - } - break; - case ProgramParam::PARAM_COLOR: - { - std::vector values = pvalue.split(" "); - if(values.size() == 4) { - param->setColor(Color(atof(values[0].c_str()), atof(values[1].c_str()), atof(values[2].c_str()), atof(values[3].c_str()))); - } else { - printf("Material parameter error: A Vector3 must have 3 values (%d provided)!\n", (int)values.size()); - } - } - break; - } - } + String pvalue = pChild2Element->Attribute("value"); + int type = materialShader->getExpectedParamType(pname); + LocalShaderParam *param = newShaderBinding->addParam(type, pname); + if(param) { + param->setParamValueFromString(type, pvalue); + } } } } @@ -593,7 +514,7 @@ Material *MaterialManager::materialFromXMLNode(TiXmlNode *node) { if(pChild2Element->Attribute("name")) { tname = pChild2Element->Attribute("name"); } - newShaderBinding->addCubemap(tname, (Cubemap*)CoreServices::getInstance()->getResourceManager()->getResource(Resource::RESOURCE_CUBEMAP, pChild2Element->GetText())); + newShaderBinding->addCubemap(tname, (Cubemap*)resourcePool->getResource(Resource::RESOURCE_CUBEMAP, pChild2Element->GetText())); } } diff --git a/Core/Contents/Source/PolyMesh.cpp b/Core/Contents/Source/PolyMesh.cpp index 368dfbed6..8a7d2eaee 100755 --- a/Core/Contents/Source/PolyMesh.cpp +++ b/Core/Contents/Source/PolyMesh.cpp @@ -1,834 +1,1348 @@ /* - Copyright (C) 2011 by Ivan Safrin - - 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: - - The above copyright notice and this permission notice shall be included in - all copies or substantial portions of the Software. - - 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. +Copyright (C) 2011 by Ivan Safrin + +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: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +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. */ +#include + #include "PolyMesh.h" #include "PolyLogger.h" +#include "PolyRenderer.h" #include "OSBasics.h" using std::min; using std::max; using std::vector; -namespace Polycode { +using namespace Polycode; - Mesh::Mesh(const String& fileName) { - - for(int i=0; i < 16; i++) { - arrayDirtyMap[i] = false; - renderDataArrays[i] = NULL; - } +Mesh::Mesh(const String& fileName) +: vertexPositionArray(RenderDataArray::VERTEX_DATA_ARRAY), +vertexColorArray(RenderDataArray::COLOR_DATA_ARRAY), +vertexNormalArray(RenderDataArray::NORMAL_DATA_ARRAY), +vertexTexCoordArray(RenderDataArray::TEXCOORD_DATA_ARRAY), +vertexTexCoord2Array(RenderDataArray::TEXCOORD2_DATA_ARRAY), +vertexTangentArray(RenderDataArray::TANGENT_DATA_ARRAY), +vertexBoneWeightArray(RenderDataArray::BONE_WEIGHT_DATA_ARRAY), +vertexBoneIndexArray(RenderDataArray::BONE_INDEX_DATA_ARRAY), +indexArray(RenderDataArray::INDEX_DATA_ARRAY) +{ - - meshType = TRI_MESH; - meshHasVertexBuffer = false; - loadMesh(fileName); - vertexBuffer = NULL; - useVertexColors = false; - } - - Mesh::Mesh(int meshType) { - for(int i=0; i < 16; i++) { - arrayDirtyMap[i] = false; - renderDataArrays[i] = NULL; - } - this->meshType = meshType; - meshHasVertexBuffer = false; - vertexBuffer = NULL; - useVertexColors = false; - } - - Mesh *Mesh::MeshFromFileName(String& fileName) { - return new Mesh(fileName); - } - - Mesh::~Mesh() { - clearMesh(); - } - - void Mesh::clearMesh() { - for(int i=0; i < polygons.size(); i++) { - delete polygons[i]; - } - polygons.clear(); - if(vertexBuffer) - delete vertexBuffer; - vertexBuffer = NULL; - - for(int i=0; i < 16; i++) { - if(renderDataArrays[i]) { - free(renderDataArrays[i]->arrayPtr); - delete renderDataArrays[i]; - renderDataArrays[i] = NULL; - } - } - - meshHasVertexBuffer = false; - useVertexColors = false; - } - - VertexBuffer *Mesh::getVertexBuffer() { - return vertexBuffer; - } + indexedMesh = false; + meshType = TRI_MESH; + loadMesh(fileName); + useVertexColors = false; +} - void Mesh::setVertexBuffer(VertexBuffer *buffer) { - vertexBuffer = buffer; - meshHasVertexBuffer = true; - } - - Number Mesh::getRadius() { - Number hRad = 0; - Number len; - for(int i=0; i < polygons.size(); i++) { - for(int j=0; j < polygons[i]->getVertexCount(); j++) { - len = polygons[i]->getVertex(j)->length(); - if(len > hRad) - hRad = len; - } - } - return hRad; - } - - void Mesh::saveToFile(OSFILE *outFile) { - unsigned int numFaces = polygons.size(); - - OSBasics::write(&meshType, sizeof(unsigned int), 1, outFile); - OSBasics::write(&numFaces, sizeof(unsigned int), 1, outFile); - for(int i=0; i < polygons.size(); i++) { - - Vector3_struct pos; - Vector3_struct nor; - Vector4_struct col; - Vector2_struct tex; - - for(int j=0; j < polygons[i]->getVertexCount(); j++) { - - pos.x = polygons[i]->getVertex(j)->x; - pos.y = polygons[i]->getVertex(j)->y; - pos.z = polygons[i]->getVertex(j)->z; - - nor.x = polygons[i]->getVertex(j)->normal.x; - nor.y = polygons[i]->getVertex(j)->normal.y; - nor.z = polygons[i]->getVertex(j)->normal.z; - - col.x = polygons[i]->getVertex(j)->vertexColor.r; - col.y = polygons[i]->getVertex(j)->vertexColor.g; - col.z = polygons[i]->getVertex(j)->vertexColor.b; - col.w = polygons[i]->getVertex(j)->vertexColor.a; - - tex.x = polygons[i]->getVertex(j)->getTexCoord().x; - tex.y = polygons[i]->getVertex(j)->getTexCoord().y; - - OSBasics::write(&pos, sizeof(Vector3_struct), 1, outFile); - OSBasics::write(&nor, sizeof(Vector3_struct), 1, outFile); - OSBasics::write(&col, sizeof(Vector4_struct), 1, outFile); - OSBasics::write(&tex, sizeof(Vector2_struct), 1, outFile); - - unsigned int numBoneWeights = polygons[i]->getVertex(j)->getNumBoneAssignments(); - OSBasics::write(&numBoneWeights, sizeof(unsigned int), 1, outFile); - for(int b=0; b < numBoneWeights; b++) { - BoneAssignment *a = polygons[i]->getVertex(j)->getBoneAssignment(b); - unsigned int boneID = a->boneID; - float weight = a->weight; - OSBasics::write(&boneID, sizeof(unsigned int), 1, outFile); - OSBasics::write(&weight, sizeof(float), 1, outFile); - } - } - - } - } +Mesh::Mesh(int meshType) +: vertexPositionArray(RenderDataArray::VERTEX_DATA_ARRAY), +vertexColorArray(RenderDataArray::COLOR_DATA_ARRAY), +vertexNormalArray(RenderDataArray::NORMAL_DATA_ARRAY), +vertexTexCoordArray(RenderDataArray::TEXCOORD_DATA_ARRAY), +vertexTexCoord2Array(RenderDataArray::TEXCOORD2_DATA_ARRAY), +vertexTangentArray(RenderDataArray::TANGENT_DATA_ARRAY), +vertexBoneWeightArray(RenderDataArray::BONE_WEIGHT_DATA_ARRAY), +vertexBoneIndexArray(RenderDataArray::BONE_INDEX_DATA_ARRAY), +indexArray(RenderDataArray::INDEX_DATA_ARRAY) +{ - - void Mesh::loadFromFile(OSFILE *inFile) { - - unsigned int meshType; - OSBasics::read(&meshType, sizeof(unsigned int), 1, inFile); - setMeshType(meshType); - - int verticesPerFace; - switch(meshType) { - case TRI_MESH: - verticesPerFace = 3; - break; - case QUAD_MESH: - verticesPerFace = 4; - break; - default: - verticesPerFace = 1; - break; - } - - unsigned int numFaces; - OSBasics::read(&numFaces, sizeof(unsigned int), 1, inFile); - - Vector3_struct pos; - Vector3_struct nor; - Vector4_struct col; - Vector2_struct tex; - - for(int i=0; i < numFaces; i++) { - Polygon *poly = new Polygon(); - - for(int j=0; j < verticesPerFace; j++) { - OSBasics::read(&pos, sizeof(Vector3_struct), 1, inFile); - OSBasics::read(&nor, sizeof(Vector3_struct), 1, inFile); - OSBasics::read(&col, sizeof(Vector4_struct), 1, inFile); - OSBasics::read(&tex, sizeof(Vector2_struct), 1, inFile); - - Vertex *vertex = new Vertex(pos.x, pos.y, pos.z); - vertex->setNormal(nor.x,nor.y, nor.z); - vertex->restNormal.set(nor.x,nor.y, nor.z); - vertex->vertexColor.setColor(col.x,col.y, col.z, col.w); - vertex->setTexCoord(tex.x, tex.y); - - unsigned int numBoneWeights; - OSBasics::read(&numBoneWeights, sizeof(unsigned int), 1, inFile); - for(int b=0; b < numBoneWeights; b++) { - float weight; - unsigned int boneID; - OSBasics::read(&boneID, sizeof(unsigned int), 1, inFile); - OSBasics::read(&weight, sizeof(float), 1, inFile); - vertex->addBoneAssignment(boneID, weight); - } - - Number totalWeight = 0; - for(int m=0; m < vertex->getNumBoneAssignments(); m++) { - BoneAssignment *ba = vertex->getBoneAssignment(m); - totalWeight += ba->weight; - } - - for(int m=0; m < vertex->getNumBoneAssignments(); m++) { - BoneAssignment *ba = vertex->getBoneAssignment(m); - ba->weight = ba->weight/totalWeight; - } - - - poly->addVertex(vertex); - } - addPolygon(poly); - } - - calculateTangents(); - - arrayDirtyMap[RenderDataArray::VERTEX_DATA_ARRAY] = true; - arrayDirtyMap[RenderDataArray::COLOR_DATA_ARRAY] = true; - arrayDirtyMap[RenderDataArray::TEXCOORD_DATA_ARRAY] = true; - arrayDirtyMap[RenderDataArray::NORMAL_DATA_ARRAY] = true; - arrayDirtyMap[RenderDataArray::TANGENT_DATA_ARRAY] = true; - } - - void Mesh::saveToFile(const String& fileName) { - OSFILE *outFile = OSBasics::open(fileName, "wb"); - if(!outFile) { - Logger::log("Error opening mesh file for saving: %s", fileName.c_str()); - } - saveToFile(outFile); - OSBasics::close(outFile); - - } - - void Mesh::loadMesh(const String& fileName) { - OSFILE *inFile = OSBasics::open(fileName, "rb"); - if(!inFile) { - Logger::log("Error opening mesh file %s", fileName.c_str()); - } - loadFromFile(inFile); - OSBasics::close(inFile); - arrayDirtyMap[RenderDataArray::VERTEX_DATA_ARRAY] = true; - arrayDirtyMap[RenderDataArray::COLOR_DATA_ARRAY] = true; - arrayDirtyMap[RenderDataArray::TEXCOORD_DATA_ARRAY] = true; - arrayDirtyMap[RenderDataArray::NORMAL_DATA_ARRAY] = true; - arrayDirtyMap[RenderDataArray::TANGENT_DATA_ARRAY] = true; - } - - void Mesh::createVPlane(Number w, Number h) { - Polygon *imagePolygon = new Polygon(); - - imagePolygon->addVertex(0,0,0,0,0); - imagePolygon->addVertex(w,0,0, 1, 0); - imagePolygon->addVertex(w,h,0, 1, 1); - imagePolygon->addVertex(0,h,0,0,1); - - - addPolygon(imagePolygon); - - for(int i=0; i < polygons.size(); i++) { - for(int j=0; j < polygons[i]->getVertexCount(); j++) { - polygons[i]->getVertex(j)->x = polygons[i]->getVertex(j)->x - (w/2.0f); - polygons[i]->getVertex(j)->y = polygons[i]->getVertex(j)->y - (h/2.0f); - } - } + this->meshType = meshType; + useVertexColors = false; + indexedMesh = false; +} - calculateNormals(); - calculateTangents(); - arrayDirtyMap[RenderDataArray::VERTEX_DATA_ARRAY] = true; - arrayDirtyMap[RenderDataArray::COLOR_DATA_ARRAY] = true; - arrayDirtyMap[RenderDataArray::TEXCOORD_DATA_ARRAY] = true; - arrayDirtyMap[RenderDataArray::NORMAL_DATA_ARRAY] = true; - arrayDirtyMap[RenderDataArray::TANGENT_DATA_ARRAY] = true; - } - - void Mesh::createPlane(Number w, Number h) { - Polygon *imagePolygon = new Polygon(); - imagePolygon->addVertex(0,0,h,0,0); - imagePolygon->addVertex(w,0,h, 1, 0); - imagePolygon->addVertex(w,0,0, 1, 1); - imagePolygon->addVertex(0,0,0,0,1); - - addPolygon(imagePolygon); - - for(int i=0; i < polygons.size(); i++) { - for(int j=0; j < polygons[i]->getVertexCount(); j++) { - polygons[i]->getVertex(j)->x = polygons[i]->getVertex(j)->x - (w/2.0f); - polygons[i]->getVertex(j)->z = polygons[i]->getVertex(j)->z - (h/2.0f); - } - } +Mesh *Mesh::MeshFromFileName(String& fileName) { + return new Mesh(fileName); +} - calculateNormals(); - calculateTangents(); - arrayDirtyMap[RenderDataArray::VERTEX_DATA_ARRAY] = true; - arrayDirtyMap[RenderDataArray::COLOR_DATA_ARRAY] = true; - arrayDirtyMap[RenderDataArray::TEXCOORD_DATA_ARRAY] = true; - arrayDirtyMap[RenderDataArray::NORMAL_DATA_ARRAY] = true; - arrayDirtyMap[RenderDataArray::TANGENT_DATA_ARRAY] = true; - } +Mesh::~Mesh() { + clearMesh(); +} - Vector3 Mesh::recenterMesh() { - Vector3 positiveOffset; - Vector3 negativeOffset; - - for(int i=0; i < polygons.size(); i++) { - for(int j=0; j < polygons[i]->getVertexCount(); j++) { - positiveOffset.x = max(positiveOffset.x,polygons[i]->getVertex(j)->x); - positiveOffset.y = max(positiveOffset.y,polygons[i]->getVertex(j)->y); - positiveOffset.z = max(positiveOffset.z,polygons[i]->getVertex(j)->z); - - } - } - - for(int i=0; i < polygons.size(); i++) { - for(int j=0; j < polygons[i]->getVertexCount(); j++) { - negativeOffset.x = min(negativeOffset.x,polygons[i]->getVertex(j)->x); - negativeOffset.y = min(negativeOffset.y,polygons[i]->getVertex(j)->y); - negativeOffset.z = min(negativeOffset.z,polygons[i]->getVertex(j)->z); - - } - } - - Vector3 finalOffset; - - finalOffset.x = (positiveOffset.x + negativeOffset.x)/2.0f; - finalOffset.y = (positiveOffset.y + negativeOffset.y)/2.0f; - finalOffset.z = (positiveOffset.z + negativeOffset.z)/2.0f; - - vector done; - for(int i=0; i < polygons.size(); i++) { - for(int j=0; j < polygons[i]->getVertexCount(); j++) { - - bool alreadyDone = false; - for(int k=0; k < done.size(); k++) { - if(done[k] == polygons[i]->getVertex(j)) - alreadyDone = true; - } - - if(!alreadyDone) { - polygons[i]->getVertex(j)->x = polygons[i]->getVertex(j)->x - finalOffset.x; - polygons[i]->getVertex(j)->y = polygons[i]->getVertex(j)->y - finalOffset.y; - polygons[i]->getVertex(j)->z = polygons[i]->getVertex(j)->z - finalOffset.z; - done.push_back(polygons[i]->getVertex(j)); - } - } - } - - - arrayDirtyMap[RenderDataArray::VERTEX_DATA_ARRAY] = true; - - return finalOffset; - } - - Vector3 Mesh::calculateBBox() { - Vector3 retVec; - - for(int i=0; i < polygons.size(); i++) { - for(int j=0; j < polygons[i]->getVertexCount(); j++) { - retVec.x = max(retVec.x,fabs(polygons[i]->getVertex(j)->x)); - retVec.y = max(retVec.y,fabs(polygons[i]->getVertex(j)->y)); - retVec.z = max(retVec.z,fabs(polygons[i]->getVertex(j)->z)); - - } - } - - return retVec*2; - } - - void Mesh::createSphere(Number _radius, int _segmentsH, int _segmentsW) { +void Mesh::clearMesh() { + vertexPositionArray.data.clear(); + vertexColorArray.data.clear(); + vertexNormalArray.data.clear(); + vertexTexCoordArray.data.clear(); + vertexTexCoord2Array.data.clear(); + vertexTangentArray.data.clear(); + indexArray.data.clear(); + vertexBoneWeightArray.data.clear(); + vertexBoneIndexArray.data.clear(); +} - setMeshType(Mesh::TRI_MESH); +Number Mesh::getRadius() { + Number hRad = 0; + Number len; + for(int i=0; i < vertexPositionArray.data.size()-2; i += 3) { + Vector3 vec; + len = vec.length(); + if(len > hRad) { + hRad = len; + } + } + return hRad; +} - Vector3 **grid = (Vector3 **) malloc(sizeof(Vector3*) * (_segmentsH+1)); - for (int i=0 ; i < _segmentsH+1; i++) { - grid[i] = (Vector3*) malloc(sizeof(Vector3) * _segmentsW+1); - } - - - for (int i = 0; i < _segmentsW; i++) { - grid[0][i] = Vector3(0,-_radius,0); - } - - for (int j = 1; j < _segmentsH; j++) { - Number horangle = ((float)j) / ((float)_segmentsH) * PI; - Number z = -_radius * cos(horangle); - Number ringradius = _radius * sin(horangle); - - for (int i = 0; i < _segmentsW; i++) { - Number verangle = 2.0 * ((float)i) / ((float)_segmentsW) * PI; - Number x = ringradius * sin(verangle); - Number y = ringradius * cos(verangle); - grid[j][i] = Vector3(y, z, x); +void Mesh::writeVertexBlock(VertexDataArray *array, OSFILE *outFile) { + + if(array->getDataSize() == 0) { + return; + } + + unsigned char blockType = array->type; + unsigned int blockCount = array->getDataSize(); + + OSBasics::write(&blockType, sizeof(unsigned char), 1, outFile); + OSBasics::write(&blockCount, sizeof(unsigned int), 1, outFile); + + OSBasics::write(array->getArrayData(), sizeof(PolyRendererVertexType), array->getDataSize(), outFile); +} + +void Mesh::writeIndexBlock(IndexDataArray *array, OSFILE *outFile) { + + if(array->getDataSize() == 0) { + return; + } + + unsigned char blockType = array->type; + unsigned int blockCount = array->getDataSize(); + + OSBasics::write(&blockType, sizeof(unsigned char), 1, outFile); + OSBasics::write(&blockCount, sizeof(unsigned int), 1, outFile); + + OSBasics::write(array->getArrayData(), sizeof(PolyRendererIndexType), array->getDataSize(), outFile); +} + +void Mesh::saveToFile(OSFILE *outFile, bool writeNormals, bool writeTangents, bool writeColors, bool writeBoneWeights, bool writeUVs, bool writeSecondaryUVs) { + + // new mesh format + // IMPORTANT: PolyRendererVertexType type defines mesh format internal type. Consider making floats always. Don't want to cast for now. + + const char headerTag[] = "MSH2"; + OSBasics::write(headerTag, 1, 4, outFile); + + unsigned char meshFlags = 0; + + if(indexedMesh) { + meshFlags |= 1 << 0; + } + + OSBasics::write(&meshFlags, sizeof(unsigned char), 1, outFile); + + writeVertexBlock(&vertexPositionArray, outFile); + + if(indexedMesh) { + writeIndexBlock(&indexArray, outFile); + } + + if(writeColors) { + writeVertexBlock(&vertexColorArray, outFile); + } + + if(writeNormals) { + writeVertexBlock(&vertexNormalArray, outFile); + } + + if(writeUVs) { + writeVertexBlock(&vertexTexCoordArray, outFile); + } + + if(writeSecondaryUVs) { + writeVertexBlock(&vertexTexCoord2Array, outFile); + } + + if(writeTangents) { + writeVertexBlock(&vertexTangentArray, outFile); + } + + if(writeBoneWeights) { + writeVertexBlock(&vertexBoneWeightArray, outFile); + writeVertexBlock(&vertexBoneIndexArray, outFile); + } +} + +void Mesh::loadFromFile(OSFILE *inFile) { + clearMesh(); + + char tag[4]; + OSBasics::read(tag, 1, 4, inFile); + + if(tag[0] == 'M' && tag[1] == 'S' && tag[2] == 'H' && tag[3] == '2') { + loadFromFileV2(inFile); + } else { + OSBasics::seek(inFile, 0, SEEK_SET); + loadFromFileLegacyV1(inFile); + } +} + +void Mesh::loadFromFileV2(OSFILE *inFile) { + + unsigned char meshFlags; + OSBasics::read(&meshFlags, sizeof(unsigned char), 1, inFile); + + indexedMesh = meshFlags & (1 << 0); + + char blockType; + unsigned int blockSize; + while(OSBasics::read(&blockType, sizeof(unsigned char), 1, inFile)) { + OSBasics::read(&blockSize, sizeof(unsigned int), 1, inFile); + + switch(blockType) { + case RenderDataArray::VERTEX_DATA_ARRAY: + vertexPositionArray.data.resize(blockSize); + OSBasics::read(&vertexPositionArray.data[0], sizeof(PolyRendererVertexType), blockSize, inFile); + break; + case RenderDataArray::TEXCOORD_DATA_ARRAY: + vertexTexCoordArray.data.resize(blockSize); + OSBasics::read(&vertexTexCoordArray.data[0], sizeof(PolyRendererVertexType), blockSize, inFile); + break; + case RenderDataArray::NORMAL_DATA_ARRAY: + vertexNormalArray.data.resize(blockSize); + OSBasics::read(&vertexNormalArray.data[0], sizeof(PolyRendererVertexType), blockSize, inFile); + break; + case RenderDataArray::COLOR_DATA_ARRAY: + vertexColorArray.data.resize(blockSize); + OSBasics::read(&vertexColorArray.data[0], sizeof(PolyRendererVertexType), blockSize, inFile); + break; + case RenderDataArray::TANGENT_DATA_ARRAY: + vertexTangentArray.data.resize(blockSize); + OSBasics::read(&vertexTangentArray.data[0], sizeof(PolyRendererVertexType), blockSize, inFile); + break; + case RenderDataArray::BONE_WEIGHT_DATA_ARRAY: + vertexBoneWeightArray.data.resize(blockSize); + OSBasics::read(&vertexBoneWeightArray.data[0], sizeof(PolyRendererVertexType), blockSize, inFile); + break; + case RenderDataArray::INDEX_DATA_ARRAY: + indexArray.data.resize(blockSize); + OSBasics::read(&indexArray.data[0], sizeof(PolyRendererIndexType), blockSize, inFile); + break; + case RenderDataArray::BONE_INDEX_DATA_ARRAY: + vertexBoneIndexArray.data.resize(blockSize); + OSBasics::read(&vertexBoneIndexArray.data[0], sizeof(PolyRendererVertexType), blockSize, inFile); + break; + } + } + + if(vertexBoneIndexArray.getDataSize() > 0) { + normalizeBoneWeights(); + } +} + +void Mesh::normalizeBoneWeights() { + + for(int i=0; i < vertexBoneWeightArray.getDataSize()-3; i += 4) { + Number totalWeight = vertexBoneWeightArray.data[i] + vertexBoneWeightArray.data[i+1] + vertexBoneWeightArray.data[i+2] + vertexBoneWeightArray.data[i+3]; + + if(totalWeight != 0.0) { + vertexBoneWeightArray.data[i] = vertexBoneWeightArray.data[i] / totalWeight; + vertexBoneWeightArray.data[i+1] = vertexBoneWeightArray.data[i+1] / totalWeight; + vertexBoneWeightArray.data[i+2] = vertexBoneWeightArray.data[i+2] / totalWeight; + vertexBoneWeightArray.data[i+3] = vertexBoneWeightArray.data[i+3] / totalWeight; + } + } +} + +void Mesh::loadFromFileLegacyV1(OSFILE *inFile) { + + unsigned char meshFlags; + OSBasics::read(&meshFlags, sizeof(unsigned char), 1, inFile); + + indexedMesh = meshFlags & (1 << 0); + bool hasNormals = meshFlags & (1 << 1); + bool hasTangents = meshFlags & (1 << 2); + bool hasColors = meshFlags & (1 << 3); + bool hasUV = meshFlags & (1 << 4); + bool hasSecondaryUVs = meshFlags & (1 << 5); + bool hasBoneWeights = meshFlags & (1 << 6); + + unsigned int meshType; + OSBasics::read(&meshType, sizeof(unsigned int), 1, inFile); + setMeshType(meshType); + + unsigned int numVertices; + OSBasics::read(&numVertices, sizeof(unsigned int), 1, inFile); + + Vector3_struct pos; + Vector3_struct nor; + Vector3_struct tan; + Vector4_struct col; + Vector2_struct tex; + + for(int i=0; i < numVertices; i++) { + OSBasics::read(&pos, sizeof(Vector3_struct), 1, inFile); + + vertexPositionArray.data.push_back(pos.x); + vertexPositionArray.data.push_back(pos.y); + vertexPositionArray.data.push_back(pos.z); + + if(hasNormals) { + OSBasics::read(&nor, sizeof(Vector3_struct), 1, inFile); + + vertexNormalArray.data.push_back(nor.x); + vertexNormalArray.data.push_back(nor.y); + vertexNormalArray.data.push_back(nor.z); + + + } + if(hasTangents) { + OSBasics::read(&tan, sizeof(Vector3_struct), 1, inFile); + + vertexTangentArray.data.push_back(tan.x); + vertexTangentArray.data.push_back(tan.y); + vertexTangentArray.data.push_back(tan.z); + + } + + if(hasColors) { + OSBasics::read(&col, sizeof(Vector4_struct), 1, inFile); + + vertexColorArray.data.push_back(col.x); + vertexColorArray.data.push_back(col.y); + vertexColorArray.data.push_back(col.z); + vertexColorArray.data.push_back(col.w); + } + + if(hasUV) { + OSBasics::read(&tex, sizeof(Vector2_struct), 1, inFile); + vertexTexCoordArray.data.push_back(tex.x); + vertexTexCoordArray.data.push_back(tex.y); + } + + if(hasSecondaryUVs) { + OSBasics::read(&tex, sizeof(Vector2_struct), 1, inFile); + vertexTexCoord2Array.data.push_back(tex.x); + vertexTexCoord2Array.data.push_back(tex.x); + } + + if(hasBoneWeights) { + unsigned int numBoneWeights; + OSBasics::read(&numBoneWeights, sizeof(unsigned int), 1, inFile); + + Number totalWeight = 0; + int numPushed = 0; + + for(int b=0; b < numBoneWeights; b++) { + float weight; + unsigned int boneID; + OSBasics::read(&boneID, sizeof(unsigned int), 1, inFile); + OSBasics::read(&weight, sizeof(float), 1, inFile); + + if(b < 4) { + vertexBoneWeightArray.data.push_back(weight); + vertexBoneIndexArray.data.push_back(boneID); + numPushed++; + } + totalWeight += weight; } - } + + if(numPushed < 4) { + for(int b=numPushed; b < 4; b++) { + vertexBoneWeightArray.data.push_back(0.0); + vertexBoneIndexArray.data.push_back(0.0); + } + } + + for(int m=0; m < 4; m++) { + vertexBoneWeightArray.data[vertexBoneWeightArray.data.size()-1-m] = vertexBoneWeightArray.data[vertexBoneWeightArray.data.size()-1-m] / totalWeight; + } + } + } + + if(indexedMesh) { + unsigned int numIndices; + OSBasics::read(&numIndices, sizeof(unsigned int), 1, inFile); + unsigned int val; + for(int i=0; i < numIndices; i++) { + OSBasics::read(&val, sizeof(unsigned int), 1, inFile); + indexArray.data.push_back(val); + } + } +} - for (int i = 0; i < _segmentsW; i++) { - grid[_segmentsH][i] = Vector3(0,_radius, 0); - } +Vector2 Mesh::getVertexTexCoord(unsigned int vertexOffset) { + return Vector2(vertexTexCoordArray.data[(vertexOffset*2)], vertexTexCoordArray.data[(vertexOffset*2)+1]); +} - for (int j = 1; j <= _segmentsH; j++) { - for (int i = 0; i < _segmentsW; i++) { - Vector3 a = grid[j][i]; - Vector3 b = grid[j][(i-1+_segmentsW) % _segmentsW]; - Vector3 c = grid[j-1][(i-1+_segmentsW) % _segmentsW]; - Vector3 d = grid[j-1][i]; - - int i2 = i; - if (i == 0) i2 = _segmentsW; - - Number vab = ((float)j) / ((float)_segmentsH); - Number vcd = (((float)j)-1.0) / ((float)_segmentsH); - Number uad = ((float)i2) / ((float)_segmentsW); - Number ubc = (((float)i2)-1.0) / ((float)_segmentsW); - Vector2 uva = Vector2(uad,vab); - Vector2 uvb = Vector2(ubc,vab); - Vector2 uvc = Vector2(ubc,vcd); - Vector2 uvd = Vector2(uad,vcd); - - if (j < _segmentsH) { - Polygon *polygon = new Polygon(); - polygon->addVertex(c.x, c.y, c.z, uvc.x ,uvc.y); - polygon->addVertex(b.x, b.y, b.z, uvb.x ,uvb.y); - polygon->addVertex(a.x, a.y, a.z, uva.x ,uva.y); - addPolygon(polygon); - } - if (j > 1) { - Polygon *polygon = new Polygon(); - polygon->addVertex(d.x, d.y, d.z, uvd.x ,uvd.y); - polygon->addVertex(c.x, c.y, c.z, uvc.x ,uvc.y); - polygon->addVertex(a.x, a.y, a.z, uva.x ,uva.y); - addPolygon(polygon); - } - } - } +Vector2 Mesh::getVertexTexCoordAtIndex(unsigned int index) { + unsigned int vertexOffset = indexArray.data[index]*2; + return Vector2(vertexTexCoordArray.data[vertexOffset], vertexTexCoordArray.data[vertexOffset+1]); +} - calculateNormals(); - calculateTangents(); - arrayDirtyMap[RenderDataArray::VERTEX_DATA_ARRAY] = true; - arrayDirtyMap[RenderDataArray::COLOR_DATA_ARRAY] = true; - arrayDirtyMap[RenderDataArray::TEXCOORD_DATA_ARRAY] = true; - arrayDirtyMap[RenderDataArray::NORMAL_DATA_ARRAY] = true; - arrayDirtyMap[RenderDataArray::TANGENT_DATA_ARRAY] = true; - } - - unsigned int Mesh::getVertexCount() { - unsigned int total = 0; - for(int i=0; i < polygons.size(); i++) { - total += polygons[i]->getVertexCount(); - } - return total; - } - void Mesh::createTorus(Number radius, Number tubeRadius, int rSegments, int tSegments) { - - setMeshType(Mesh::TRI_MESH); - - Vector3 **grid = (Vector3 **) malloc(sizeof(Vector3*) * rSegments); - for (int i=0 ; i < rSegments; i++) { - grid[i] = (Vector3*) malloc(sizeof(Vector3) * tSegments); - } - - for (int i=0 ; i < rSegments; i++) { - for (int j = 0; j < tSegments; ++j) { - Number u = ((Number)i) / rSegments * 2.0 * PI; - Number v = ((Number)j) / tSegments * 2.0 * PI; - - grid[i][j] = Vector3((radius + tubeRadius*cos(v))*cos(u), tubeRadius*sin(v), (radius + tubeRadius*cos(v))*sin(u)); - +Vector3 Mesh::getVertexPosition(unsigned int vertexOffset) { + return Vector3(vertexPositionArray.data[(vertexOffset*3)], vertexPositionArray.data[(vertexOffset*3)+1], vertexPositionArray.data[(vertexOffset*3)+2]); +} + +Vector3 Mesh::getVertexPositionAtIndex(unsigned int index) { + unsigned int vertexOffset = indexArray.data[index]*3; + return Vector3(vertexPositionArray.data[vertexOffset], vertexPositionArray.data[vertexOffset+1], vertexPositionArray.data[vertexOffset+2]); +} + + +void Mesh::addColor(const Color &color) { + vertexColorArray.data.push_back(color.r); + vertexColorArray.data.push_back(color.g); + vertexColorArray.data.push_back(color.b); + vertexColorArray.data.push_back(color.a); +} + +void Mesh::addColor(Number r, Number g, Number b, Number a) { + vertexColorArray.data.push_back(r); + vertexColorArray.data.push_back(g); + vertexColorArray.data.push_back(b); + vertexColorArray.data.push_back(a); +} + +void Mesh::addVertex(Number x, Number y, Number z) { + vertexPositionArray.data.push_back(x); + vertexPositionArray.data.push_back(y); + vertexPositionArray.data.push_back(z); +} + +void Mesh::addTangent(Number x, Number y, Number z) { + vertexTangentArray.data.push_back(x); + vertexTangentArray.data.push_back(y); + vertexTangentArray.data.push_back(z); +} + +void Mesh::addTexCoord(Number u, Number v) { + vertexTexCoordArray.data.push_back(u); + vertexTexCoordArray.data.push_back(v); +} + +void Mesh::addTexCoord2(Number u, Number v) { + vertexTexCoord2Array.data.push_back(u); + vertexTexCoord2Array.data.push_back(v); +} + +void Mesh::addBoneAssignments(Number b1Weight, unsigned int b1Index, Number b2Weight, unsigned int b2Index, Number b3Weight, unsigned int b3Index, Number b4Weight, unsigned int b4Index) { + + vertexBoneWeightArray.data.push_back(b1Weight); + vertexBoneWeightArray.data.push_back(b2Weight); + vertexBoneWeightArray.data.push_back(b3Weight); + vertexBoneWeightArray.data.push_back(b4Weight); + + vertexBoneIndexArray.data.push_back(b1Index); + vertexBoneIndexArray.data.push_back(b2Index); + vertexBoneIndexArray.data.push_back(b3Index); + vertexBoneIndexArray.data.push_back(b4Index); +} + +void Mesh::setVertexAtOffset(unsigned int offset, Number x, Number y, Number z) { + if((offset*3)+2 < vertexPositionArray.data.size()) { + vertexPositionArray.data[(offset*3)] = x; + vertexPositionArray.data[(offset*3)+1] = y; + vertexPositionArray.data[(offset*3)+2] = z; + } +} + +void Mesh::addVertexWithUV(Number x, Number y, Number z, Number u, Number v) { + addVertex(x,y,z); + addTexCoord(u,v); +} + +void Mesh::addVertexWithUVAndNormal(Number x, Number y, Number z, Number u, Number v, Number nx, Number ny, Number nz) { + addVertexWithUV(x,y,z, u, v); + addNormal(nx, ny, nz); +} + +void Mesh::addNormal(Number nx, Number ny, Number nz) { + vertexNormalArray.data.push_back(nx); + vertexNormalArray.data.push_back(ny); + vertexNormalArray.data.push_back(nz); +} + +void Mesh::addNormal(const Vector3 &n) { + vertexNormalArray.data.push_back(n.x); + vertexNormalArray.data.push_back(n.y); + vertexNormalArray.data.push_back(n.z); +} + +void Mesh::saveToFile(const String& fileName, bool writeNormals, bool writeTangents, bool writeColors, bool writeBoneWeights, bool writeUVs, bool writeSecondaryUVs) { + OSFILE *outFile = OSBasics::open(fileName, "wb"); + if(!outFile) { + Logger::log("Error opening mesh file for saving: %s\n", fileName.c_str()); + return; + } + saveToFile(outFile, writeNormals, writeTangents, writeColors, writeBoneWeights, writeUVs, writeSecondaryUVs); + OSBasics::close(outFile); + +} + +void Mesh::loadMesh(const String& fileName) { + OSFILE *inFile = OSBasics::open(fileName, "rb"); + if(!inFile) { + Logger::log("Error opening mesh file %s\n", fileName.c_str()); + return; + } + loadFromFile(inFile); + OSBasics::close(inFile); +} + +void Mesh::createCircle(Number w, Number h, unsigned int numSegments, Number tilingValue) { + setMeshType(Mesh::TRI_MESH); + indexedMesh = false; + + Number lastx = 0; + Number lasty = 0; + Number lastv = 0; + + for (int i=0 ; i < numSegments+1; i++) { + Number v = ((Number)i)/((Number)numSegments); + Number pos = ((PI*2.0)/((Number)numSegments)) * i; + Number x = sin(pos) * w * 0.5; + Number y = cos(pos) * h * 0.5; + + if(i > 0) { + addVertexWithUVAndNormal(0, 0, 0, 0.5*tilingValue, 0.5*tilingValue, 0.0, 0.0, 1.0); + addVertexWithUVAndNormal(x, y, 0, (0.5 + (y / h*0.5))*tilingValue, (0.5 + (x / w*0.5))*tilingValue, 0.0, 0.0, 1.0); + addVertexWithUVAndNormal(lastx, lasty, 0, (0.5 + (lasty / h*0.5))*tilingValue, (0.5 + (lastx / w*0.5))*tilingValue, 0.0, 0.0, 1.0); + } + lastx = x; + lastv = v; + lasty = y; + } +} + +Mesh *Mesh::Copy() const { + Mesh *newMesh = new Mesh(meshType); + newMesh->indexedMesh = indexedMesh; + (*newMesh) = (*this); + return newMesh; +} + +void Mesh::createLineCircle(Number w, Number h, unsigned int numSegments, Number tilingValue) { + setMeshType(Mesh::TRIFAN_MESH); + indexedMesh = false; + + int step; + if(numSegments > 0) { + step = ceil(360.0/((Number)numSegments)); + } else { + step = 1; + } + + addVertexWithUV(cosf(0)*(w / 2), sinf(0)*(h / 2), 0, ((cosf(0)*0.5) + 0.5)*tilingValue, ((sinf(0) * 0.5) + 0.5)*tilingValue); + addNormal(0.0, 0.0, 0.0); + + for (int i=0; i < 361; i+= step) { + Number degInRad = i*TORADIANS; + + Number x = cos(degInRad)*(w/2); + Number y = sin(degInRad)*(h/2); + + addVertexWithUV(x, y, 0, ((cos(degInRad) * 0.5) + 0.5)*tilingValue, (1.0 - ((sin(degInRad) * 0.5) + 0.5))*tilingValue); + + Vector3 normal(x,y, 0.0); + normal.Normalize(); + addNormal(normal.x, normal.y, normal.z); + } +} + +void Mesh::createVPlane(Number w, Number h, Number tilingValue) { + setMeshType(Mesh::TRI_MESH); + indexedMesh = false; + + addVertexWithUVAndNormal(0 - (w/2.0f),0 - (h/2.0f), 0,0,0, 0.0, 0.0, 1.0); + addVertexWithUVAndNormal(w - (w/2.0f), 0- (h/2.0f), 0, tilingValue, 0, 0.0, 0.0, 1.0); + addVertexWithUVAndNormal(w- (w/2.0f), h- (h/2.0f), 0, tilingValue, tilingValue, 0.0, 0.0, 1.0); + + addVertexWithUVAndNormal(0 - (w/2.0f),0- (h/2.0f), 0,0,0, 0.0, 0.0, 1.0); + addVertexWithUVAndNormal(w - (w/2.0f),h - (h/2.0f), 0, tilingValue, tilingValue, 0.0, 0.0, 1.0); + addVertexWithUVAndNormal(0 - (w / 2.0f), h - (h / 2.0f), 0, 0, tilingValue, 0.0, 0.0, 1.0); + + calculateNormals(); + calculateTangents(); +} + +void Mesh::createPlane(Number w, Number h, Number tilingValue) { + setMeshType(Mesh::TRI_MESH); + indexedMesh = false; + + addVertexWithUV(0 - (w / 2.0f), 0, h - (h / 2.0f), 0, 0); + addVertexWithUV(w - (w / 2.0f), 0, h - (h / 2.0f), 1 * tilingValue, 0); + addVertexWithUV(w - (w / 2.0f), 0, 0 - (h / 2.0f), 1 * tilingValue, 1 * tilingValue); + + addVertexWithUV(0 - (w / 2.0f), 0, h - (h / 2.0f), 0, 0); + addVertexWithUV(w - (w / 2.0f), 0, 0 - (h / 2.0f), 1 * tilingValue, 1 * tilingValue); + addVertexWithUV(0 - (w / 2.0f), 0, 0 - (h / 2.0f), 0, 1 * tilingValue); + + calculateNormals(); + calculateTangents(); +} + +Vector3 Mesh::recenterMesh() { + + // TODO: implement + + + Vector3 positiveOffset; + Vector3 negativeOffset; + Vector3 finalOffset; + + /* + for(int i=0; i < vertices.size(); i++) { + positiveOffset.x = max(positiveOffset.x, vertices[i]->x); + positiveOffset.y = max(positiveOffset.y, vertices[i]->y); + positiveOffset.z = max(positiveOffset.z, vertices[i]->z); + + negativeOffset.x = min(negativeOffset.x, vertices[i]->x); + negativeOffset.y = min(negativeOffset.y, vertices[i]->y); + negativeOffset.z = min(negativeOffset.z, vertices[i]->z); + } + + + + finalOffset.x = (positiveOffset.x + negativeOffset.x)/2.0f; + finalOffset.y = (positiveOffset.y + negativeOffset.y)/2.0f; + finalOffset.z = (positiveOffset.z + negativeOffset.z)/2.0f; + + for(int i=0; i < vertices.size(); i++) { + vertices[i]->x = vertices[i]->x - finalOffset.x; + vertices[i]->y = vertices[i]->y - finalOffset.y; + vertices[i]->z = vertices[i]->z - finalOffset.z; + } + + */ + + return finalOffset; +} + +Vector3 Mesh::calculateBBox() { + Vector3 retVec; + + if(vertexPositionArray.data.size() == 0) { + return retVec; + } + + for(int i=0; i < vertexPositionArray.data.size()-2; i += 3) { + retVec.x = max(retVec.x,(Number)fabs(vertexPositionArray.data[i])); + retVec.y = max(retVec.y,(Number)fabs(vertexPositionArray.data[i+1])); + retVec.z = max(retVec.z,(Number)fabs(vertexPositionArray.data[i+2])); + } + + if(retVec.x == 0.0) { + retVec.x = 0.001; + } + if(retVec.y == 0.0) { + retVec.y = 0.001; + } + if(retVec.z == 0.0) { + retVec.z = 0.001; + } + + return retVec*2; +} + +void Mesh::createSphere(Number radius, int segmentsH, int segmentsW, Number tilingValue) { + + segmentsH++; + segmentsW++; + + setMeshType(Mesh::TRI_MESH); + indexedMesh = true; + + Number tdelta = 360.f/(segmentsW-1); + Number pdelta = 180.f/(segmentsH-1); + + Number phi = -90; + Number theta = 0; + + for(unsigned int i = 0; i< segmentsH; i++) { + for(unsigned int j = 0; j < segmentsW; j++) { + Vector3 v; + v.x = radius * cos(phi*PI/180.f) * cos(theta*PI/180.f); + v.y = radius * sin(phi*PI/180.f); + v.z = radius * cos(phi*PI/180.f) * sin(theta*PI/180.f); + addVertex(v.x, v.y, v.z); + v.Normalize(); + addNormal(v.x, v.y, v.z); + addTexCoord((-theta / (360.f))*tilingValue, ((phi + 90.f) / 180.f)*tilingValue); + theta += tdelta; + } + phi += pdelta; + theta = 0; + } + + for(unsigned int i = 0; i < segmentsH-1; i++) { + for(unsigned int j = 0; j< segmentsW-1; j++) { + addIndexedFace(((i+1)*segmentsW) + j, ((i+1)*segmentsW) + j+1, (i*segmentsW) + j+1); + addIndexedFace((i*segmentsW) + j+1, (i*segmentsW)+j, ((i+1)*segmentsW) + j); + } + } + + calculateTangents(); +} + +void Mesh::subdivideToRadius(Number radius, int subdivisions) +{ + typedef std::map, int> EdgeSet; + for (int s = 0; s < subdivisions; s++) { + EdgeSet dividedEdges; + //Take a copy of the number of face indices at the BEGINNING, so we don't go on forever + for (int i = 0, n = indexArray.data.size(); i < n; i += 3) { + + int vi0 = indexArray.data[i]; + int vi1 = indexArray.data[i+1]; + int vi2 = indexArray.data[i+2]; + + Vector3 v0 = Vector3(vertexPositionArray.data[(vi0*3)], vertexPositionArray.data[(vi0*3)+1], vertexPositionArray.data[(vi0*3)+2]); + Vector3 v1 = Vector3(vertexPositionArray.data[(vi1*3)], vertexPositionArray.data[(vi1*3)+1], vertexPositionArray.data[(vi1*3)+2]); + Vector3 v2 = Vector3(vertexPositionArray.data[(vi2*3)], vertexPositionArray.data[(vi2*3)+1], vertexPositionArray.data[(vi2*3)+2]); + + //Midpoints + Vector3 vm01 = (v0 + v1) * 0.5f; + Vector3 vm12 = (v1 + v2) * 0.5f; + Vector3 vm20 = (v2 + v0) * 0.5f; + + //Normalize so they're pushed outwards to the sphere + vm01 = vm01 * (radius / vm01.length()); + vm12 = vm12 * (radius / vm12.length()); + vm20 = vm20 * (radius / vm20.length()); + + std::pair + key01 = vi0 < vi1 ? std::pair(vi0, vi1) : std::pair(vi1, vi0), + key12 = vi1 < vi2 ? std::pair(vi1, vi2) : std::pair(vi2, vi1), + key20 = vi2 < vi0 ? std::pair(vi2, vi0) : std::pair(vi0, vi2); + + EdgeSet::iterator it01 = dividedEdges.find(key01); + int vmi01; + if (it01 != dividedEdges.end()) { + vmi01 = it01->second; } - } - - for (int i=0 ; i < rSegments; i++) { - for (int j = 0; j < tSegments; ++j) { - - int ip = (i+1) % rSegments; - int jp = (j+1) % tSegments; - - Vector3 a = grid[i ][j]; - Vector3 b = grid[ip][j]; - Vector3 c = grid[i ][jp]; - Vector3 d = grid[ip][jp]; - - Vector2 uva = Vector2(((Number)i) / ((Number)rSegments), ((Number)j) / ((Number)tSegments)); - Vector2 uvb = Vector2((((Number)i)+1.0) / ((Number)rSegments), ((Number)j) / ((Number)tSegments)); - Vector2 uvc = Vector2(((Number)i) / ((Number)rSegments), (((Number)j)+1.0) / ((Number)tSegments)); - Vector2 uvd = Vector2((((Number)i)+1.0) / ((Number)rSegments), (((Number)j)+1.0) / ((Number)tSegments)); - - - Polygon *polygon = new Polygon(); - polygon->addVertex(c.x, c.y, c.z, uvc.x ,uvc.y); - polygon->addVertex(b.x, b.y, b.z, uvb.x ,uvb.y); - polygon->addVertex(a.x, a.y, a.z, uva.x ,uva.y); - addPolygon(polygon); - - polygon = new Polygon(); - polygon->addVertex(b.x, b.y, b.z, uvb.x ,uvb.y); - polygon->addVertex(c.x, c.y, c.z, uvc.x ,uvc.y); - polygon->addVertex(d.x, d.y, d.z, uvd.x ,uvd.y); - addPolygon(polygon); + else { + vmi01 = vertexPositionArray.data.size()/3; + addVertex(vm01.x, vm01.y, vm01.z); + addTexCoord(0.0, 0.0); + dividedEdges[key01] = vmi01; } - } - - for (int i=0 ; i < rSegments; i++) { - free(grid[i]); - } - free(grid); - - - calculateNormals(); - calculateTangents(); - arrayDirtyMap[RenderDataArray::VERTEX_DATA_ARRAY] = true; - arrayDirtyMap[RenderDataArray::COLOR_DATA_ARRAY] = true; - arrayDirtyMap[RenderDataArray::TEXCOORD_DATA_ARRAY] = true; - arrayDirtyMap[RenderDataArray::NORMAL_DATA_ARRAY] = true; - arrayDirtyMap[RenderDataArray::TANGENT_DATA_ARRAY] = true; - } - - void Mesh::createCylinder(Number height, Number radius, int numSegments, bool capped) { - - setMeshType(Mesh::TRI_MESH); - Number lastx = 0; - Number lastz = 0; - Number lastv = 0; - for (int i=0 ; i < numSegments+1; i++) { - Number v = ((Number)i)/((Number)numSegments); - Number pos = ((PI*2.0)/((Number)numSegments)) * i; - Number x = sin(pos) * radius; - Number z = cos(pos) * radius; - - if(i > 0) { - Polygon *polygon = new Polygon(); - polygon->addVertex(lastx,0,lastz,lastv,0); - polygon->addVertex(x,0,z, v, 0); - polygon->addVertex(x,height,z, v, 1); - addPolygon(polygon); - - polygon = new Polygon(); - polygon->addVertex(x,height,z, v, 1); - polygon->addVertex(lastx,height,lastz, lastv, 1); - polygon->addVertex(lastx,0,lastz,lastv,0); - addPolygon(polygon); - - if(capped) { - polygon = new Polygon(); - polygon->addVertex(lastx,height,lastz, 0.5+(lastz/radius*0.5), 0.5+(lastx/radius*0.5)); - polygon->addVertex(x,height,z, 0.5+(z/radius*0.5), 0.5+(x/radius*0.5)); - polygon->addVertex(0,height,0,0.5,0.5); - addPolygon(polygon); - - polygon = new Polygon(); - polygon->addVertex(lastx,0,lastz, 0.5+(lastz/radius*0.5), 0.5+(lastx/radius*0.5)); - polygon->addVertex(0,0,0,0.5,0.5); - polygon->addVertex(x,0,z, 0.5+(z/radius*0.5), 0.5+(x/radius*0.5)); - addPolygon(polygon); - } - + EdgeSet::iterator it12 = dividedEdges.find(key12); + int vmi12; + if (it12 != dividedEdges.end()) { + vmi12 = it12->second; } - lastx = x; - lastz = z; - lastv = v; - /* - Polygon *polygon = new Polygon(); - polygon->addVertex(w,0,h, 1, 1); - polygon->addVertex(0,0,h, 1, 0); - polygon->addVertex(0,0,0,0,0); - polygon->addVertex(w,0,0,0,1); - addPolygon(polygon); - */ - } - - for(int i=0; i < polygons.size(); i++) { - for(int j=0; j < polygons[i]->getVertexCount(); j++) { -// polygons[i]->getVertex(j)->x = polygons[i]->getVertex(j)->x - (radius/2.0f); - polygons[i]->getVertex(j)->y = polygons[i]->getVertex(j)->y - (height/2.0f); -// polygons[i]->getVertex(j)->z = polygons[i]->getVertex(j)->z - (radius/2.0f); + else { + vmi12 = vertexPositionArray.data.size()/3; + addVertex(vm12.x, vm12.y, vm12.z); + addTexCoord(0.0, 0.0); + dividedEdges[key12] = vmi12; } - } - - - calculateNormals(); - calculateTangents(); - arrayDirtyMap[RenderDataArray::VERTEX_DATA_ARRAY] = true; - arrayDirtyMap[RenderDataArray::COLOR_DATA_ARRAY] = true; - arrayDirtyMap[RenderDataArray::TEXCOORD_DATA_ARRAY] = true; - arrayDirtyMap[RenderDataArray::NORMAL_DATA_ARRAY] = true; - arrayDirtyMap[RenderDataArray::TANGENT_DATA_ARRAY] = true; - } - - void Mesh::createCone(Number height, Number radius, int numSegments) { - - - setMeshType(Mesh::TRI_MESH); - Number lastx = -1; - Number lastz = -1; - for (int i=0 ; i < numSegments+1; i++) { - Number pos = ((PI*2.0)/((Number)numSegments)) * i; - Number x = sinf(pos) * radius; - Number z = cosf(pos) * radius; - - if(lastx > -1) { - Polygon *polygon = new Polygon(); - polygon->addVertex(lastx,0,lastz,0,0); - polygon->addVertex(x,0,z, 1, 0); - polygon->addVertex(0,height,0, 1, 1); - addPolygon(polygon); - - - polygon = new Polygon(); - polygon->addVertex(x,0,z, 1, 1); - polygon->addVertex(lastx,0,lastz, 1, 1); - polygon->addVertex(0,0,0,0,0); - addPolygon(polygon); - - + EdgeSet::iterator it20 = dividedEdges.find(key20); + int vmi20; + if (it20 != dividedEdges.end()) { + vmi20 = it20->second; } - lastx = x; - lastz = z; - /* - Polygon *polygon = new Polygon(); - polygon->addVertex(w,0,h, 1, 1); - polygon->addVertex(0,0,h, 1, 0); - polygon->addVertex(0,0,0,0,0); - polygon->addVertex(w,0,0,0,1); - addPolygon(polygon); - */ - } - - for(int i=0; i < polygons.size(); i++) { - for(int j=0; j < polygons[i]->getVertexCount(); j++) { -// polygons[i]->getVertex(j)->x = polygons[i]->getVertex(j)->x - (radius/2.0f); - polygons[i]->getVertex(j)->y = polygons[i]->getVertex(j)->y - (height/2.0f); -// polygons[i]->getVertex(j)->z = polygons[i]->getVertex(j)->z - (radius/2.0f); + else { + vmi20 = vertexPositionArray.data.size()/3; + addVertex(vm20.x, vm20.y, vm20.z); + addTexCoord(0.0, 0.0); + dividedEdges[key20] = vmi20; } + + addIndexedFace(vi0, vmi01, vmi20); + addIndexedFace(vi1, vmi12, vmi01); + addIndexedFace(vi2, vmi20, vmi12); + + //Recycle the original face to be the new central face + indexArray.data[i] = vmi01; + indexArray.data[i+1] = vmi12; + indexArray.data[i+2] = vmi20; } - - - calculateNormals(); - calculateTangents(); - arrayDirtyMap[RenderDataArray::VERTEX_DATA_ARRAY] = true; - arrayDirtyMap[RenderDataArray::COLOR_DATA_ARRAY] = true; - arrayDirtyMap[RenderDataArray::TEXCOORD_DATA_ARRAY] = true; - arrayDirtyMap[RenderDataArray::NORMAL_DATA_ARRAY] = true; - arrayDirtyMap[RenderDataArray::TANGENT_DATA_ARRAY] = true; - } +} - void Mesh::createBox(Number w, Number d, Number h) { - Polygon *polygon = new Polygon(); - polygon->addVertex(w,0,h, 1, 1); - polygon->addVertex(0,0,h, 1, 0); - polygon->addVertex(0,0,0,0,0); - polygon->addVertex(w,0,0,0,1); - addPolygon(polygon); - - polygon = new Polygon(); - polygon->addVertex(w,d,h, 1, 1); - polygon->addVertex(w,d,0, 1, 0); - polygon->addVertex(0,d,0,0,0); - polygon->addVertex(0,d,h,0,1); - addPolygon(polygon); - - polygon = new Polygon(); - polygon->addVertex(0,d,0,0,1); - polygon->addVertex(w,d,0, 1, 1); - polygon->addVertex(w,0,0, 1, 0); - polygon->addVertex(0,0,0,0,0); - addPolygon(polygon); - - polygon = new Polygon(); - polygon->addVertex(0,0,h,0,0); - polygon->addVertex(w,0,h, 1, 0); - polygon->addVertex(w,d,h, 1, 1); - polygon->addVertex(0,d,h,0,1); - addPolygon(polygon); - - polygon = new Polygon(); - polygon->addVertex(0,0,h,0,1); - polygon->addVertex(0,d,h, 1, 1); - polygon->addVertex(0,d,0, 1, 0); - polygon->addVertex(0,0,0,0,0); - addPolygon(polygon); - - polygon = new Polygon(); - polygon->addVertex(w,0,h,0,1); - polygon->addVertex(w,0,0, 1, 1); - polygon->addVertex(w,d,0, 1, 0); - polygon->addVertex(w,d,h,0,0); - addPolygon(polygon); - - for(int i=0; i < polygons.size(); i++) { - for(int j=0; j < polygons[i]->getVertexCount(); j++) { - polygons[i]->getVertex(j)->x = polygons[i]->getVertex(j)->x - (w/2.0f); - polygons[i]->getVertex(j)->y = polygons[i]->getVertex(j)->y - (d/2.0f); - polygons[i]->getVertex(j)->z = polygons[i]->getVertex(j)->z - (h/2.0f); - } - } +void Mesh::createOctosphere(Number radius, int subdivisions) { + + setMeshType(Mesh::TRI_MESH); + + indexedMesh = true; - calculateNormals(); - calculateTangents(); - arrayDirtyMap[RenderDataArray::VERTEX_DATA_ARRAY] = true; - arrayDirtyMap[RenderDataArray::COLOR_DATA_ARRAY] = true; - arrayDirtyMap[RenderDataArray::TEXCOORD_DATA_ARRAY] = true; - arrayDirtyMap[RenderDataArray::NORMAL_DATA_ARRAY] = true; - arrayDirtyMap[RenderDataArray::TANGENT_DATA_ARRAY] = true; + Vector3 points[6]={ + Vector3(0,0,-1), + Vector3(0,0,1), + Vector3(-1,0,0), + Vector3(1,0,0), + Vector3(0,-1,0), + Vector3(0,1,0) + }; + + for(int i =0;i<6;i++) { + Vector3 n = points[i]; + Vector3 v = n * radius; + addVertex(v.x, v.y, v.z); + addNormal(n.x, n.y, n.z); + addTexCoord(0.0, 0.0); } - - void Mesh::dirtyArray(unsigned int arrayIndex) { - if(arrayIndex < 16) - arrayDirtyMap[arrayIndex] = true; + + addIndexedFace(0, 4, 2); + addIndexedFace(0, 2, 5); + addIndexedFace(0, 5, 3); + addIndexedFace(0, 3, 4); + addIndexedFace(1, 2, 4); + addIndexedFace(1, 4, 3); + addIndexedFace(1, 3, 5); + addIndexedFace(1, 5, 2); + + subdivideToRadius(radius, subdivisions); + + calculateNormals(); + calculateTangents(); +} + +void Mesh::createIcosphere(Number radius, int subdivisions) { + + setMeshType(Mesh::TRI_MESH); + + const float a = 0.5257311121191336; + const float b = 0.85065080835204; + + indexedMesh = true; + + Vector3 icosahedron_points[12]={ + Vector3(-a, b, 0), + Vector3( a, b, 0), + Vector3(-a, -b, 0), + Vector3( a, -b, 0), + Vector3(0, -a, b), + Vector3(0, a, b), + Vector3(0, -a, -b), + Vector3(0, a, -b), + Vector3( b, 0, -a), + Vector3( b, 0, a), + Vector3(-b, 0, -a), + Vector3(-b, 0, a) + }; + + for(int i =0;i<12;i++) { + Vector3 n = icosahedron_points[i]; + Vector3 v = n * radius; + addVertex(v.x, v.y, v.z); + addNormal(n.x, n.y, n.z); + addTexCoord(0.0, 0.0); } - - void Mesh::dirtyArrays() { - for(int i=0; i < 16; i++) { - arrayDirtyMap[i] = true; - } + + addIndexedFace(0, 11, 5); + addIndexedFace(0, 5, 1); + addIndexedFace(0, 1, 7); + addIndexedFace(0, 7, 10); + addIndexedFace(0, 10, 11); + addIndexedFace(1, 5, 9); + addIndexedFace(5, 11, 4); + addIndexedFace(11, 10, 2); + addIndexedFace(10, 7, 6); + addIndexedFace(7, 1, 8); + addIndexedFace(3, 9, 4); + addIndexedFace(3, 4, 2); + addIndexedFace(3, 2, 6); + addIndexedFace(3, 6, 8); + addIndexedFace(3, 8, 9); + addIndexedFace(4, 9, 5); + addIndexedFace(2, 4, 11); + addIndexedFace(6, 2, 10); + addIndexedFace(8, 6, 7); + addIndexedFace(9, 8, 1); + + subdivideToRadius(radius, subdivisions); + + calculateNormals(); + calculateTangents(); +} + +unsigned int Mesh::getVertexCount() { + return vertexPositionArray.data.size()/3; +} + +unsigned int Mesh::getIndexCount() { + return indexArray.data.size(); +} + +void Mesh::createTorus(Number radius, Number tubeRadius, int segmentsW, int segmentsH, Number tilingValue) { + + segmentsH++; + segmentsW++; + + setMeshType(Mesh::TRI_MESH); + indexedMesh = true; + + Number tdelta = 360.f/(segmentsW-1); + Number pdelta = 360.f/(segmentsH-1); + + Number phi = -90; + Number theta = 0; + + for(unsigned int i = 0; i< segmentsH; i++) { + for(unsigned int j = 0; j < segmentsW; j++) { + Vector3 v; + + v.x = (radius + tubeRadius*cos(phi*TORADIANS))*cos(theta*TORADIANS); + v.y = tubeRadius*sin(phi*TORADIANS); + v.z = (radius + tubeRadius*cos(phi*TORADIANS))*sin(theta*TORADIANS); + + addVertex(v.x, v.y, v.z); + addTexCoord((-theta / (360.f))*tilingValue, ((phi / (360.f)) + 0.5)*tilingValue); + theta += tdelta; + } + phi += pdelta; + theta = 0; + } + + for(unsigned int i = 0; i < segmentsH-1; i++) { + for(unsigned int j = 0; j< segmentsW-1; j++) { + addIndexedFace(((i+1)*segmentsW) + j, ((i+1)*segmentsW) + j+1, (i*segmentsW) + j+1); + addIndexedFace((i*segmentsW) + j+1, (i*segmentsW)+j, ((i+1)*segmentsW) + j); + } + } + + calculateNormals(); + calculateTangents(); +} + +void Mesh::createCylinder(Number height, Number radius, int numSegments, bool capped, Number tilingValue) { + + setMeshType(Mesh::TRI_MESH); + indexedMesh = true; + + Number lastx = 0; + Number lastz = 0; + Number lastv = 0; + + numSegments++; + + if(capped) { + addVertexWithUVAndNormal(0, 0 - (height / 2.0f), 0, 0.5*tilingValue, 0.5*tilingValue, 0.0, -1.0, 0.0); + addVertexWithUVAndNormal(0, height - (height / 2.0f), 0, 0.5*tilingValue, 0.5*tilingValue, 0.0, 1.0, 0.0); + } + + for (int i=0 ; i < numSegments; i++) { + Number v = ((Number)i)/((Number)numSegments-1); + Number pos = ((PI*2.0)/((Number)numSegments-1)) * i; + Number x = sin(pos); + Number z = cos(pos); + + addVertexWithUVAndNormal(x*radius, 0 - (height / 2.0f), z*radius, v*tilingValue, 0, x, 0, z); + addVertexWithUVAndNormal(x*radius, height - (height / 2.0f), z*radius, v*tilingValue, tilingValue, x, 0, z); + + if(capped) { + addVertexWithUVAndNormal(x*radius, 0 - (height / 2.0f), z*radius, (0.5 + (z*0.5))*tilingValue, (0.5 + (x*0.5))*tilingValue, 0.0, -1.0, 0.0); + addVertexWithUVAndNormal(x*radius, height - (height / 2.0f), z*radius, (0.5 + (z*0.5))*tilingValue, (0.5 + (x*0.5))*tilingValue, 0.0, 1.0, 0.0); + } + + lastx = x; + lastz = z; + lastv = v; + } + + + int vertexOffset = 2; + int vertexInterval = 1; + if(capped) { + vertexInterval = 3; + vertexOffset = 6; + } + + + for (int i=1 ; i <= numSegments-1; i++) { + addIndexedFace(vertexOffset, vertexOffset-vertexInterval, vertexOffset-vertexInterval-1 ); + addIndexedFace(vertexOffset, vertexOffset+1, vertexOffset-vertexInterval ); + vertexOffset += 2; + + if(capped) { + addIndexedFace(vertexOffset, vertexOffset-vertexInterval-1, 0); + addIndexedFace(1, vertexOffset-vertexInterval, vertexOffset+1); + vertexOffset += 2; + } + } + + calculateTangents(); +} + +void Mesh::createCone(Number height, Number radius, int numSegments, Number tilingValue) { + + setMeshType(Mesh::TRI_MESH); + indexedMesh = true; + + Number lastx = 0; + Number lastz = 0; + + numSegments *= 2; + + if(!(numSegments % 2)) { + numSegments++; + } + + addVertexWithUVAndNormal(0, 0 - (height / 2.0f), 0, 0.5*tilingValue, 0.5*tilingValue, 0.0, -1.0, 0.0); + + for (int i=0 ; i < numSegments; i++) { + Number pos = ((PI*2.0)/((Number)numSegments-1)) * i; + Number x = sin(pos); + Number z = cos(pos); + + if(!(i % 2)) { + addVertexWithUVAndNormal(x*radius, 0 - (height / 2.0f), z*radius, (0.5 + (z*0.5))*tilingValue, (0.5 + (x*0.5))*tilingValue, x, 0.0, z); + addVertexWithUVAndNormal(x*radius, 0 - (height / 2.0f), z*radius, (0.5 + (z*0.5))*tilingValue, (0.5 + (x*0.5))*tilingValue, 0.0, -1.0, 0.0); + } else { + addVertexWithUVAndNormal(0, height - (height / 2.0f), 0, 0.5*tilingValue, 0.5*tilingValue, 0.0, 1.0, 0.0); + } + + lastx = x; + lastz = z; + + } + + + int vertexOffset = 4; + + for (int i=1 ; i <= (numSegments-1)/2; i++) { + addIndexedFace(vertexOffset, vertexOffset-1, vertexOffset-3); + addIndexedFace(vertexOffset+1, vertexOffset-2, 0); + vertexOffset += 3; + } + + + calculateTangents(); +} + +void Mesh::addIndex(unsigned int index) { + indexArray.data.push_back(index); +} + +void Mesh::addIndexedFace(unsigned int i1, unsigned int i2, unsigned int i3) { + indexArray.data.push_back(i1); + indexArray.data.push_back(i2); + indexArray.data.push_back(i3); +} + +void Mesh::addIndexedFace(unsigned int i1, unsigned int i2) { + indexArray.data.push_back(i1); + indexArray.data.push_back(i2); +} + +void Mesh::addIndexedFace(unsigned int i1, unsigned int i2, unsigned int i3, unsigned int i4) { + indexArray.data.push_back(i1); + indexArray.data.push_back(i2); + indexArray.data.push_back(i3); + indexArray.data.push_back(i4); +} + +void Mesh::removeFace(unsigned int faceIndex) { + unsigned int groupSize = getIndexGroupSize(); + unsigned int startOffset = faceIndex * groupSize; + if (indexedMesh) { + std::vector::iterator start = indexArray.data.begin() + startOffset; + indexArray.data.erase(start, start+groupSize); } - - - void Mesh::useVertexNormals(bool val) { - for(int i =0; i < polygons.size(); i++) { - polygons[i]->useVertexNormals = val; - } - arrayDirtyMap[RenderDataArray::NORMAL_DATA_ARRAY] = true; + else { + removeVertexRange(startOffset, startOffset + groupSize); } - - vector Mesh::getConnectedFaces(Vertex *v) { - vector retVec; - for(int i=0; i < polygons.size(); i++) { - bool pushed = false; - for(int j=0; j < polygons[i]->getVertexCount(); j++) { - Vertex *vn = polygons[i]->getVertex(j); - if(*vn == *v) { - if(!pushed) { - retVec.push_back(polygons[i]); - pushed = true; - } +} + +void Mesh::removeVertexRange(unsigned int beginRemoveVertex, int vertexRemovalCount) { + // TODO: fix + /* + if (!vertices.size()) return; + unsigned int endRemoveVertex = beginRemoveVertex + vertexRemovalCount; + vertices.erase(vertices.begin() + beginRemoveVertex, vertices.begin() + endRemoveVertex); + if (indexedMesh) { + unsigned int groupSize = getIndexGroupSize(); + for (unsigned int i = 0; i < indices.size(); ) { + unsigned int faceVertexIndex = indices[i]; + //Encountered a face that references an index being removed + if (faceVertexIndex >= beginRemoveVertex && faceVertexIndex < endRemoveVertex) { + //Rewind to beginning of group, going to remove entire face + unsigned int faceIndex = i/groupSize; + i = faceIndex * groupSize; + indices.erase(indices.begin() + i, indices.begin() + i + groupSize); + } + else { + if (faceVertexIndex > beginRemoveVertex) { + indices[i] = faceVertexIndex - vertexRemovalCount; } + i++; } } - return retVec; - } - - void Mesh::calculateTangents() { - for(int i =0; i < polygons.size(); i++) { - polygons[i]->calculateTangent(); - } - /* - for(int i=0; i < polygons.size(); i++) { - for(int j=0; j < polygons[i]->getVertexCount(); j++) { - Vertex *v = polygons[i]->getVertex(j); - - Vector3 tangent; - vector connectedFaces = getConnectedFaces(v); - int numConnected = connectedFaces.size(); - if(numConnected > 2) - numConnected = 2; - for(int k=0; k < numConnected; k++) { - tangent += connectedFaces[k]->getFaceTangent(); - } - tangent = tangent / numConnected; - tangent.Normalize(); - v->tangent = tangent; - } - } - */ - arrayDirtyMap[RenderDataArray::TANGENT_DATA_ARRAY] = true; } - - void Mesh::calculateNormals(bool smooth, Number smoothAngle) { - for(int i =0; i < polygons.size(); i++) { - polygons[i]->calculateNormal(); - } - - if(smooth) { - for(int i=0; i < polygons.size(); i++) { - for(int j=0; j < polygons[i]->getVertexCount(); j++) { - Vertex *v = polygons[i]->getVertex(j); - - Vector3 normal; - vector connectedFaces = getConnectedFaces(v); - for(int k=0; k < connectedFaces.size(); k++) { - normal += connectedFaces[k]->getFaceNormal(); - } - normal = normal / connectedFaces.size(); - normal.Normalize(); - v->setNormal(normal.x, normal.y, normal.z); - } + */ +} + +int Mesh::removeUnusedVertices() { + int removals = 0; + // TODO: fix + /* + if (indexedMesh) { + std::vector vertexMap(vertices.size()); + //Mark all used vertices first + for (unsigned int i = 0; i < indices.size(); i++) { + vertexMap[indices[i]] = 1; + } + //Create relocation map, move vertices + unsigned int dst = 0; + for (unsigned int src = 0; src < vertexMap.size(); src++) { + if (vertexMap[src]) { + vertices[dst] = vertices[src]; + vertexMap[src] = dst; + dst++; } - } - - arrayDirtyMap[RenderDataArray::NORMAL_DATA_ARRAY] = true; - } - - int Mesh::getMeshType() { - return meshType; - } - - void Mesh::setMeshType(int newType) { - meshType = newType; - } - - void Mesh::addPolygon(Polygon *newPolygon) { - polygons.push_back(newPolygon); - arrayDirtyMap[RenderDataArray::VERTEX_DATA_ARRAY] = true; - arrayDirtyMap[RenderDataArray::COLOR_DATA_ARRAY] = true; - arrayDirtyMap[RenderDataArray::TEXCOORD_DATA_ARRAY] = true; - arrayDirtyMap[RenderDataArray::NORMAL_DATA_ARRAY] = true; - arrayDirtyMap[RenderDataArray::TANGENT_DATA_ARRAY] = true; - } - - - unsigned int Mesh::getPolygonCount() { - return polygons.size(); + removals = dst - vertices.size(); + vertices.resize(dst); + //Apply map to indices + for (unsigned int i = 0; i < indices.size(); i++) { + indices[i] = vertexMap[indices[i]]; + } } - - Polygon *Mesh::getPolygon(unsigned int index) { - return polygons[index]; + */ + return removals; +} + +void Mesh::createBox(Number w, Number d, Number h, Number tilingValue) { + setMeshType(Mesh::TRI_MESH); + indexedMesh = false; + + addVertexWithUV(w,0,h, tilingValue, tilingValue); + addVertexWithUV(0,0,h, tilingValue, 0); + addVertexWithUV(0,0,0,0,0); + + addVertexWithUV(w,0,h, tilingValue, tilingValue); + addVertexWithUV(0,0,0,0,0); + addVertexWithUV(w,0,0,0,tilingValue); + + addVertexWithUV(w,d,h, tilingValue, tilingValue); + addVertexWithUV(w,d,0, tilingValue, 0); + addVertexWithUV(0,d,0,0,0); + + addVertexWithUV(w,d,h, tilingValue, tilingValue); + addVertexWithUV(0,d,0,0,0); + addVertexWithUV(0,d,h,0,tilingValue); + + addVertexWithUV(0,d,0,0,tilingValue); + addVertexWithUV(w,d,0, tilingValue, tilingValue); + addVertexWithUV(w,0,0, tilingValue, 0); + + addVertexWithUV(0,d,0,0,tilingValue); + addVertexWithUV(w,0,0, tilingValue, 0); + addVertexWithUV(0,0,0,0,0); + + addVertexWithUV(0,0,h,0,0); + addVertexWithUV(w,0,h, tilingValue, 0); + addVertexWithUV(w,d,h, tilingValue, tilingValue); + + addVertexWithUV(0,0,h,0,0); + addVertexWithUV(w,d,h, tilingValue, tilingValue); + addVertexWithUV(0,d,h,0,tilingValue); + + addVertexWithUV(0,0,h,0,tilingValue); + addVertexWithUV(0,d,h, tilingValue, tilingValue); + addVertexWithUV(0,d,0, tilingValue, 0); + + addVertexWithUV(0,0,h,0,tilingValue); + addVertexWithUV(0,d,0, tilingValue, 0); + addVertexWithUV(0,0,0,0,0); + + addVertexWithUV(w,0,h,0,tilingValue); + addVertexWithUV(w,0,0, tilingValue, tilingValue); + addVertexWithUV(w,d,0, tilingValue, 0); + + addVertexWithUV(w,0,h,0,tilingValue); + addVertexWithUV(w,d,0, tilingValue, 0); + addVertexWithUV(w,d,h,0,0); + + for(int i=0; i < vertexPositionArray.data.size()-2; i += 3) { + vertexPositionArray.data[i] = vertexPositionArray.data[i] - (w/2.0f); + vertexPositionArray.data[i+1] = vertexPositionArray.data[i+1] - (d/2.0f); + vertexPositionArray.data[i+2] = vertexPositionArray.data[i+2] - (h/2.0f); + } + + calculateNormals(); + calculateTangents(); +} + +Vector3 Mesh::calculateFaceTangent(const Vector3 &v1, const Vector3 &v2, const Vector3 &v3, const Vector2 &texCoord1, const Vector2 &texCoord2, const Vector2 &texCoord3) { + Vector3 tangent; + Vector3 side0 = v1 - v2; + Vector3 side1 = v3 - v1; + Vector3 normal = side1.crossProduct(side0); + normal.Normalize(); + Number deltaV0 = texCoord1.y - texCoord2.y; + Number deltaV1 = texCoord3.y - texCoord1.y; + tangent = side0 * deltaV1 - side1 * deltaV0; + tangent.Normalize(); + + Number deltaU0 = texCoord1.x - texCoord2.x; + Number deltaU1 = texCoord3.x - texCoord1.x; + + Vector3 binormal = side0 * deltaU1 - side1 * deltaU0; + binormal.Normalize(); + Vector3 tangentCross = tangent.crossProduct(binormal); + + if (tangentCross.dot(normal) < 0.0f) { + tangent = tangent * -1; + } + + return tangent; +} + + +void Mesh::calculateTangents() { + + vertexTangentArray.data.clear(); + + int polySize = 3; + if(meshType == Mesh::QUAD_MESH) { + polySize = 4; + } + + for(int i=0; i < vertexPositionArray.data.size() / 3; i++) { + addTangent(0.0, 0.0, 0.0); + } + + if(indexedMesh) { + for(int i=0; i+polySize-1 < indexArray.data.size(); i += polySize) { + + Vector3 tangent = calculateFaceTangent(getVertexPositionAtIndex(i), getVertexPositionAtIndex(i+1), getVertexPositionAtIndex(i+2), getVertexTexCoordAtIndex(i), getVertexTexCoordAtIndex(i+1), getVertexTexCoordAtIndex(i+2)); + + for(int j=0; j < polySize; j++) { + unsigned int index= indexArray.data[i+j]; + vertexTangentArray.data[(index*3)] -= tangent.x; + vertexTangentArray.data[(index*3)+1] -= tangent.y; + vertexTangentArray.data[(index*3)+2] -= tangent.z; + } + } + } else { + for(int i=0; i+polySize-1 < vertexPositionArray.data.size() / 3; i += polySize) { + Vector3 tangent = calculateFaceTangent(getVertexPosition(i), getVertexPosition(i+1), getVertexPosition(i+2), getVertexTexCoord(i), getVertexTexCoord(i+1), getVertexTexCoord(i+2)); + + for(int j=0; j < polySize; j++) { + vertexTangentArray.data[(i+j) * 3] = tangent.x; + vertexTangentArray.data[((i+j) * 3) + 1] = tangent.y; + vertexTangentArray.data[((i+j) * 3) + 2] = tangent.z; + } + } + } + + // normalize tangents + for(int i=0; i < vertexTangentArray.data.size()-2; i += 3) { + Vector3 v(vertexTangentArray.data[i], vertexTangentArray.data[i+1], vertexTangentArray.data[i+2]); + v.Normalize(); + vertexTangentArray.data[i] = v.x; + vertexTangentArray.data[i+1] = v.y; + vertexTangentArray.data[i+2] = v.z; + } + +} + +void Mesh::calculateNormals() { + + int polySize = 3; + if(meshType == Mesh::QUAD_MESH) { + polySize = 4; + } + + vertexNormalArray.data.clear(); + + for(int i=0; i < vertexPositionArray.data.size()-2; i += 3) { + addNormal(0.0, 0.0, 0.0); + } + + if(indexedMesh) { + for(int i=0; i+polySize-1 < indexArray.data.size(); i += polySize) { + const Vector3 e1 = getVertexPositionAtIndex(i) - getVertexPositionAtIndex(i+1); + const Vector3 e2 = getVertexPositionAtIndex(i+2) - getVertexPositionAtIndex(i+1); + const Vector3 no = e1.crossProduct(e2); + + for(int j=0; j < polySize; j++) { + unsigned int index= indexArray.data[i+j]; + vertexNormalArray.data[(index*3)] -= no.x; + vertexNormalArray.data[(index*3)+1] -= no.y; + vertexNormalArray.data[(index*3)+2] -= no.z; + } + } + } else { + for(int i=0; i+polySize-1 < vertexPositionArray.data.size() / 3; i += polySize) { + const Vector3 e1 = getVertexPosition(i) - getVertexPosition(i+1); + const Vector3 e2 = getVertexPosition(i+2) - getVertexPosition(i+1); + const Vector3 no = e1.crossProduct(e2); + + for(int j=0; j < polySize; j++) { + vertexNormalArray.data[(i+j) * 3] = -no.x; + vertexNormalArray.data[((i+j) * 3) + 1] = -no.y; + vertexNormalArray.data[((i+j) * 3) + 2] = -no.z; + } + } + } + + // normalize normals + for(int i=0; i < vertexNormalArray.data.size()-2; i += 3) { + Vector3 v(vertexNormalArray.data[i], vertexNormalArray.data[i+1], vertexNormalArray.data[i+2]); + v.Normalize(); + vertexNormalArray.data[i] = v.x; + vertexNormalArray.data[i+1] = v.y; + vertexNormalArray.data[i+2] = v.z; + } +} + +void Mesh::saveAsOBJ(const String fileName) { + FILE *f = fopen(fileName.c_str(), "w"); + + if (!f) { + return; } + + char buffer[256]; + + if(vertexPositionArray.data.size() > 2) { + for(int i=0; i < vertexPositionArray.data.size()-2; i += 3) { + sprintf(buffer, "v %f %f %f\n", vertexPositionArray.data[i], vertexPositionArray.data[i+1], vertexPositionArray.data[i+2]); + fputs(buffer, f); + } + } + + if(vertexTexCoordArray.data.size() > 1) { + for(int i=0; i < vertexTexCoordArray.data.size()-1; i += 2) { + sprintf(buffer, "vt %f %f\n", vertexTexCoordArray.data[i], vertexTexCoordArray.data[i+1]); + fputs(buffer, f); + } + } + + if(vertexNormalArray.data.size() > 2) { + for(int i=0; i < vertexNormalArray.data.size()-2; i += 3) { + sprintf(buffer, "vn %f %f %f\n", vertexNormalArray.data[i], vertexNormalArray.data[i+1], vertexNormalArray.data[i+2]); + fputs(buffer, f); + } + } + + if(indexArray.data.size() > 2) { + for(int i=0; i < indexArray.data.size()-2; i += 3) { + sprintf(buffer, "f %d %d %d\n", indexArray.data[i]+1, indexArray.data[i+1]+1, indexArray.data[i+2]+1); + fputs(buffer, f); + } + } + + fclose(f); +} + +int Mesh::getMeshType() { + return meshType; +} + +void Mesh::setMeshType(int newType) { + meshType = newType; } diff --git a/Core/Contents/Source/PolyObject.cpp b/Core/Contents/Source/PolyObject.cpp index e2ca56f02..727af0fdf 100644 --- a/Core/Contents/Source/PolyObject.cpp +++ b/Core/Contents/Source/PolyObject.cpp @@ -28,6 +28,21 @@ using namespace Polycode; +ObjectEntry::ObjectEntry() : +type(UNKNOWN_ENTRY), +NumberVal(0.0), +length(0), +intVal(0) +{ + +} + +ObjectEntry::~ObjectEntry() { + for(int i=0; i < children.size(); i++) { + delete children[i]; + } +} + void ObjectEntry::Clear() { for(int i=0; i < children.size(); i++) { children[i]->Clear(); @@ -132,6 +147,7 @@ TiXmlElement *Object::createElementFromObjectEntry(ObjectEntry *entry) { break; case ObjectEntry::FLOAT_ENTRY: { std::ostringstream o; // Avoid NumberToString, it truncates + o << std::fixed; o << childEntry->NumberVal; newElement->SetAttribute(childTypedName.c_str(), o.str().c_str()); } break; diff --git a/Core/Contents/Source/PolyParticle.cpp b/Core/Contents/Source/PolyParticle.cpp deleted file mode 100755 index b8df3ef50..000000000 --- a/Core/Contents/Source/PolyParticle.cpp +++ /dev/null @@ -1,152 +0,0 @@ -/* - Copyright (C) 2011 by Ivan Safrin - - 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: - - The above copyright notice and this permission notice shall be included in - all copies or substantial portions of the Software. - - 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. -*/ - -#include "PolyParticle.h" -#include "PolyMesh.h" -#include "PolyPolygon.h" -#include "PolySceneMesh.h" -#include "PolyScreenShape.h" - -using namespace Polycode; - -Mesh *Particle::billboardMesh = 0; - -Particle::Particle(int particleType, bool isScreenParticle, Material *material, Texture *texture, Mesh *particleMesh) { - life = 0; - if(isScreenParticle) { - createScreenParticle(particleType, texture, particleMesh); - } else { - createSceneParticle(particleType, material, particleMesh); - } - - Reset(true); -} - -void Particle::createSceneParticle(int particleType, Material *material, Mesh *particleMesh) { - switch(particleType) { - case BILLBOARD_PARTICLE: - { - if(!billboardMesh) { - billboardMesh = new Mesh(Mesh::QUAD_MESH); - - Polygon *imagePolygon = new Polygon(); - imagePolygon->addVertex(0,1,0,0,0); - imagePolygon->addVertex(1,1,0, 1, 0); - imagePolygon->addVertex(1,0,0, 1, 1); - imagePolygon->addVertex(0,0,0,0,1); - - billboardMesh->addPolygon(imagePolygon); - - for(int i=0; i < billboardMesh->getPolygonCount(); i++) { - for(int j=0; j < billboardMesh->getPolygon(i)->getVertexCount(); j++) { - billboardMesh->getPolygon(i)->getVertex(j)->x = billboardMesh->getPolygon(i)->getVertex(j)->x - (1.0/2.0f); - billboardMesh->getPolygon(i)->getVertex(j)->z = billboardMesh->getPolygon(i)->getVertex(j)->z - (1.0/2.0f); - } - } - - billboardMesh->calculateNormals(); - billboardMesh->arrayDirtyMap[RenderDataArray::VERTEX_DATA_ARRAY] = true; - billboardMesh->arrayDirtyMap[RenderDataArray::COLOR_DATA_ARRAY] = true; - billboardMesh->arrayDirtyMap[RenderDataArray::TEXCOORD_DATA_ARRAY] = true; - billboardMesh->arrayDirtyMap[RenderDataArray::NORMAL_DATA_ARRAY] = true; - - } - SceneMesh *primitive = new SceneMesh(billboardMesh); - - primitive->setMaterial(material); - primitive->billboardMode = true; - primitive->billboardRoll = true; -// primitive->alphaTest = true; -// primitive->depthTest = false; - primitive->depthWrite = false; - primitive->backfaceCulled = false; - particleBody = primitive; - } - break; - case MESH_PARTICLE: - { - SceneMesh *primitive = new SceneMesh(particleMesh); - if(particleMesh->getMeshType() == Mesh::TRI_MESH) - primitive->cacheToVertexBuffer(true); - primitive->setMaterial(material); - // primitive->billboardMode = true; - // primitive->billboardRoll = true; - //primitive->depthTest = false; - // primitive->backfaceCulled = false; - particleBody = primitive; - } - break; - default: - assert(0); - break; - } -} - -void Particle::createScreenParticle(int particleType, Texture *texture, Mesh *particleMesh) { - - ScreenShape *primitive = new ScreenShape(ScreenShape::SHAPE_RECT, 1.0, 1.0f); - primitive->setTexture(texture); -// primitive->billboardMode = true; -// primitive->billboardRoll = true; - - particleBody = primitive; - return; - - switch(particleType) { - case BILLBOARD_PARTICLE: - { - ScreenShape *primitive = new ScreenShape(ScreenShape::SHAPE_RECT, 1.0f, 1.0f); -// primitive->setTexture(texture->get) - particleBody = primitive; - } - break; - case MESH_PARTICLE: - { -// ScreenMesh *primitive = new ScreenMesh(particleMesh); -// primitive->cacheToVertexBuffer(true); -// primitive->setMaterial(texture); -// particleBody = primitive; - } - break; - } -} - - -void Particle::Reset(bool continuious) { - if(continuious) { - if(life > lifespan) - life = 0 + (life - lifespan); - else - life = 0; - } else { - life = 0; - } - - perlinPosX = (Number)rand()/RAND_MAX; - perlinPosY = (Number)rand()/RAND_MAX; - perlinPosZ = (Number)rand()/RAND_MAX; - -} - -Particle::~Particle() { - -} diff --git a/Core/Contents/Source/PolyParticleEmitter.cpp b/Core/Contents/Source/PolyParticleEmitter.cpp index e7c7ca8c8..0db228d6b 100755 --- a/Core/Contents/Source/PolyParticleEmitter.cpp +++ b/Core/Contents/Source/PolyParticleEmitter.cpp @@ -22,553 +22,492 @@ #include "PolyParticleEmitter.h" #include "PolyCoreServices.h" -#include "PolyParticle.h" -#include "PolyPerlin.h" -#include "PolyResource.h" -#include "PolyScene.h" -#include "PolyScreen.h" -#include "PolyTimer.h" -#include "PolyMaterialManager.h" -#include "PolyResourceManager.h" -#include "PolyScreenMesh.h" +#include "PolyCore.h" +#include "PolyMesh.h" #include "PolyRenderer.h" using namespace Polycode; -SceneParticleEmitter::SceneParticleEmitter(const String& materialName, int particleType, int emitterType, Number lifespan, unsigned int numParticles, Vector3 direction, Vector3 gravity, Vector3 deviation, Vector3 emitterRadius, Mesh *particleMesh, SceneMesh *emitter) -: SceneEntity(), -ParticleEmitter(materialName, particleMesh, particleType, emitterType, lifespan, numParticles, direction, gravity, deviation, emitterRadius) -{ - isScreenEmitter = false; - emitterMesh = emitter; - createParticles(); - +SceneParticleEmitter::SceneParticleEmitter(unsigned int particleCount, Number lifetime, Number speed) : SceneMesh(Mesh::POINT_MESH), particleCount(particleCount), particleSpeed(speed), lifetime(lifetime), directionVector(0.0, 1.0, 0.0), useFloorPlane(false), floorPlaneOffset(-1.0), floorDamping(0.5), particlesInWorldSpace(false), perlinEnabled(false), perlinValue(1.0,1.0,1.0), particleType(SceneParticleEmitter::PARTICLE_TYPE_QUAD), particleSize(0.1), particleRotationSpeed(0.0, 0.0, 0.0), useColorCurves(false), useScaleCurve(false), loopParticles(true){ + + core = CoreServices::getInstance()->getCore(); + motionPerlin = new Perlin(3,5,1.0,RANDOM_NUMBER); + mesh->useVertexColors = true; + depthWrite = false; + systemEnabled = true; + setParticleCount(particleCount); + colorDeviation = Color(0.0, 0.0, 0.0, 0.0); } SceneParticleEmitter::~SceneParticleEmitter() { - + delete motionPerlin; +} + +Entity *SceneParticleEmitter::Clone(bool deepClone, bool ignoreEditorOnly) const { + SceneParticleEmitter *newEmitter = new SceneParticleEmitter(1, 1, 1); + applyClone(newEmitter, deepClone, ignoreEditorOnly); + return newEmitter; +} + +void SceneParticleEmitter::addSourceMesh(Mesh *mesh) { + sourceMeshes.push_back(mesh); +} + +int SceneParticleEmitter::getNumSourceMeshes() { + return sourceMeshes.size(); +} + +Mesh *SceneParticleEmitter::getSourcesMeshAtIndex(int index) { + if(index > 0 && index < sourceMeshes.size()) { + return sourceMeshes[index]; + } + return NULL; +} + +void SceneParticleEmitter::removeSourceMeshAtIndex(int index) { + if(index > 0 && index < sourceMeshes.size()) { + sourceMeshes.erase(sourceMeshes.begin() + index); + } } -void SceneParticleEmitter::respawnSceneParticles() { - for(int i=0; i < particles.size(); i++) { - Particle *particle = particles[i]; - removeChild((SceneEntity*)particle->particleBody); - addParticleBody(particle->particleBody); - resetParticle(particle); - particle->life = lifespan * ((Number)rand()/RAND_MAX); - } - updateEmitter(); +void SceneParticleEmitter::applyClone(Entity *clone, bool deepClone, bool ignoreEditorOnly) const { + + SceneMesh::applyClone(clone, deepClone, ignoreEditorOnly); + + SceneParticleEmitter *cloneEmitter = (SceneParticleEmitter*) clone; + + cloneEmitter->setParticleCount(particleCount); + cloneEmitter->setParticleSpeed(particleSpeed); + cloneEmitter->setParticleLifetime(lifetime); + cloneEmitter->setParticleDirection(directionVector); + cloneEmitter->setDirectionDeviation(directionDeviation); + cloneEmitter->setEmitterSize(emitterSize); + cloneEmitter->setGravity(gravity); + cloneEmitter->setUseFloorPlane(useFloorPlane); + cloneEmitter->setParticlesInWorldSpace(particlesInWorldSpace); + cloneEmitter->setPerlinEnabled(perlinEnabled); + cloneEmitter->setPerlinValue(perlinValue); + cloneEmitter->setParticleSize(particleSize); + cloneEmitter->setFloorPlaneOffset(floorPlaneOffset); + cloneEmitter->setFloorDamping(floorDamping); + cloneEmitter->setLoopParticles(loopParticles); + cloneEmitter->setParticleType(particleType); + + cloneEmitter->scaleCurve = scaleCurve; + cloneEmitter->useScaleCurve = useScaleCurve; + + cloneEmitter->colorCurveR = colorCurveR; + cloneEmitter->colorCurveG = colorCurveG; + cloneEmitter->colorCurveB = colorCurveB; + cloneEmitter->colorCurveA = colorCurveA; + cloneEmitter->useColorCurves = useColorCurves; + + cloneEmitter->getMesh()->useVertexColors = true; } -void SceneParticleEmitter::addParticleBody(Entity *particleBody) { - addEntity((SceneEntity*)particleBody); - particleBody->editorOnly = true; +void SceneParticleEmitter::resetParticle(unsigned int index) { + particles[index].lifetime = 0.0; + + if(sourceMeshes.size() > 0) { + particles[index].varianceIndex = rand() % sourceMeshes.size(); + } else { + particles[index].varianceIndex = 0; + } + + positionParticle(index); + + particles[index].rotation = Vector3(RANDOM_NUMBER * 360.0 *particleRotationSpeed.x, RANDOM_NUMBER * 360.0 *particleRotationSpeed.y, RANDOM_NUMBER * 360.0 *particleRotationSpeed.z); + + + particles[index].color = Color(1.0 - (RANDOM_NUMBER*colorDeviation.r), + 1.0 - (RANDOM_NUMBER*colorDeviation.g), + 1.0 - (RANDOM_NUMBER*colorDeviation.b), + 1.0 - (RANDOM_NUMBER*colorDeviation.a)); +} + +void SceneParticleEmitter::positionParticle(unsigned int index) { + q.fromAxes(-directionDeviation.x + (directionDeviation.x * RANDOM_NUMBER * 2.0), -directionDeviation.y + (directionDeviation.y * RANDOM_NUMBER * 2.0), -directionDeviation.z + (directionDeviation.z * RANDOM_NUMBER * 2.0)); + particles[index].velocity = q.applyTo(directionVector); + particles[index].position = Vector3(-emitterSize.x + (emitterSize.x * RANDOM_NUMBER * 2.0), -emitterSize.y + (emitterSize.y * RANDOM_NUMBER * 2.0), -emitterSize.z + (emitterSize.z * RANDOM_NUMBER * 2.0)); + + if(particlesInWorldSpace) { + particles[index].position = systemTrasnformMatrix * particles[index].position; + particles[index].velocity = systemTrasnformMatrix.rotateVector( particles[index].velocity); + } +} + + +void SceneParticleEmitter::setParticleCount(unsigned int newParticleCount) { + particleCount = newParticleCount; + particles.resize(particleCount); + for(int i=0; i < particles.size(); i++) { + resetParticle(i); + particles[i].lifetime = RANDOM_NUMBER * -lifetime; + particles[i].perlinPos = Vector3(RANDOM_NUMBER, RANDOM_NUMBER, RANDOM_NUMBER); + particles[i].brightnessDeviation = 1.0; + particles[i].scale = 1.0; + } +} + +void SceneParticleEmitter::setGravity(const Vector3 &newGravity) { + gravity = newGravity; +} + +void SceneParticleEmitter::setDirectionDeviation(const Vector3 &newDeviation) { + directionDeviation = newDeviation; +} + +void SceneParticleEmitter::setEmitterSize(const Vector3 &newSize) { + emitterSize = newSize; +} + +void SceneParticleEmitter::setParticleType(unsigned int particleType) { + this->particleType = particleType; +} + +void SceneParticleEmitter::setParticleSize(Number particleSize) { + this->particleSize = particleSize; +} + +void SceneParticleEmitter::setParticleRotationSpeed(const Vector3 &rotationSpeed) { + particleRotationSpeed = rotationSpeed; + for(int i=0; i < particles.size(); i++) { + resetParticle(i); + particles[i].lifetime = RANDOM_NUMBER * -lifetime; + } +} + +void SceneParticleEmitter::setLoopParticles(bool val) { + loopParticles = val; + for(int i=0; i < particles.size(); i++) { + resetParticle(i); + particles[i].lifetime = RANDOM_NUMBER * -lifetime; + } +} + +bool SceneParticleEmitter::getLoopParticles() const { + return loopParticles; +} + +void SceneParticleEmitter::enableParticleSystem(bool val) { + if(systemEnabled == val) { + return; + } + + systemEnabled = val; + if(val) { + for(int i=0; i < particles.size(); i++) { + resetParticle(i); + particles[i].lifetime = RANDOM_NUMBER * -lifetime; + } + } +} + +void SceneParticleEmitter::rebuildParticles() { + mesh->clearMesh(); + Matrix4 inverseMatrix = systemTrasnformMatrix.Inverse(); + + switch(particleType) { + case PARTICLE_TYPE_POINT: + { + mesh->setMeshType(Mesh::POINT_MESH); + for(int i=0; i < particles.size(); i++) { + if(particles[i].lifetime > lifetime || particles[i].lifetime < 0.0) { + continue; + } + Vector3 vertexPosition = particles[i].position; + if(particlesInWorldSpace) { + vertexPosition = inverseMatrix * vertexPosition; + } + mesh->addVertexWithUV(vertexPosition.x, vertexPosition.y, vertexPosition.z, 0.5, 0.5); + mesh->addColor(particles[i].color); + } + } + break; + case PARTICLE_TYPE_MESH: + case PARTICLE_TYPE_QUAD: + { + Matrix4 cameraMatrix = renderer->getCameraMatrix(); + Quaternion q; + + Color vertexColor; + Number finalParticleSize; + for(int i=0; i < particles.size(); i++) { + if(particles[i].lifetime > lifetime || particles[i].lifetime < 0.0) { + continue; + } + q.fromAxes(particles[i].rotation.x, particles[i].rotation.y, particles[i].rotation.z); + vertexColor = particles[i].color; + finalParticleSize = particleSize * particles[i].scale; + + Vector3 particlePosition = particles[i].position; + if(particlesInWorldSpace) { + particlePosition = inverseMatrix * particlePosition; + } + + if(particleType == PARTICLE_TYPE_MESH) { + mesh->setMeshType(Mesh::TRI_MESH); + + int indexOffset = 0; + + mesh->indexedMesh = true; + + int meshIndex = particles[i].varianceIndex; + if(meshIndex < sourceMeshes.size()) { + + indexOffset = mesh->vertexPositionArray.data.size()/3; + + Mesh *sourceMesh = sourceMeshes[meshIndex]; + + mesh->setMeshType(sourceMesh->getMeshType()); + + for(int v=0; v < sourceMesh->getVertexCount(); v++) { + + Vector3 vpos = Vector3(sourceMesh->vertexPositionArray.data[(v * 3)], sourceMesh->vertexPositionArray.data[(v * 3)+1], sourceMesh->vertexPositionArray.data[(v * 3)+2]) * finalParticleSize; + vpos = q.applyTo(vpos); + + vpos += particlePosition; + mesh->addVertex(vpos.x, vpos.y, vpos.z); + mesh->addTexCoord(sourceMesh->vertexTexCoordArray.data[(v * 2)], sourceMesh->vertexTexCoordArray.data[(v * 2) + 1]); + mesh->addColor(vertexColor); + Vector3 svNormal = Vector3(sourceMesh->vertexNormalArray.data[(v * 3)], sourceMesh->vertexNormalArray.data[(v * 3) + 1], sourceMesh->vertexNormalArray.data[(v * 3) + 2]); + svNormal = q.applyTo(svNormal); + mesh->addNormal(svNormal.x, svNormal.y, svNormal.z); + } + + for (int v = 0; v < sourceMesh->indexArray.data.size(); v++) { + mesh->addIndex(indexOffset + sourceMesh->indexArray.data[v]); + } + + } + + } else { + mesh->setMeshType(Mesh::QUAD_MESH); + + Vector3 vertexPosition = Vector3(-finalParticleSize, -finalParticleSize, 0.0); + vertexPosition = q.applyTo(vertexPosition); + vertexPosition = cameraMatrix.rotateVector(vertexPosition); + mesh->addVertexWithUV(particlePosition.x+vertexPosition.x, particlePosition.y+vertexPosition.y, particlePosition.z+vertexPosition.z, 0.0, 0.0); + mesh->addColor(vertexColor); + + vertexPosition = Vector3(finalParticleSize, -finalParticleSize, 0.0); + vertexPosition = q.applyTo(vertexPosition); + vertexPosition = cameraMatrix.rotateVector(vertexPosition); + mesh->addVertexWithUV(particlePosition.x+vertexPosition.x, particlePosition.y+vertexPosition.y, particlePosition.z+vertexPosition.z, 1.0, 0.0); + mesh->addColor(vertexColor); + + vertexPosition = Vector3(finalParticleSize, finalParticleSize, 0.0); + vertexPosition = q.applyTo(vertexPosition); + vertexPosition = cameraMatrix.rotateVector(vertexPosition); + mesh->addVertexWithUV(particlePosition.x+vertexPosition.x, particlePosition.y+vertexPosition.y, particlePosition.z+vertexPosition.z, 1.0, 1.0); + mesh->addColor(vertexColor); + + vertexPosition = Vector3(-finalParticleSize, finalParticleSize, 0.0); + vertexPosition = q.applyTo(vertexPosition); + vertexPosition = cameraMatrix.rotateVector(vertexPosition); + mesh->addVertexWithUV(particlePosition.x+vertexPosition.x, particlePosition.y+vertexPosition.y, particlePosition.z+vertexPosition.z, 0.0, 1.0); + mesh->addColor(vertexColor); + } + + } + } + break; + + } + + if(useVertexBuffer) { + CoreServices::getInstance()->getRenderer()->createVertexBufferForMesh(mesh); + } } -void SceneParticleEmitter::dispatchTriggerCompleteEvent() { - ((EventDispatcher*)this)->dispatchEvent(new Event(Event::COMPLETE_EVENT), Event::COMPLETE_EVENT); +unsigned int SceneParticleEmitter::getParticleCount() const { + return particleCount; } -Matrix4 SceneParticleEmitter::getBaseMatrix() { - rebuildTransformMatrix(); - return getConcatenatedMatrix(); +unsigned int SceneParticleEmitter::getParticleType() const { + return particleType; } -void SceneParticleEmitter::Update() { - updateEmitter(); +void SceneParticleEmitter::setUseFloorPlane(bool val) { + useFloorPlane = val; } +void SceneParticleEmitter::setParticlesInWorldSpace(bool val) { + particlesInWorldSpace = val; +} -ScreenParticleEmitter::ScreenParticleEmitter(const String& imageFile, int particleType, int emitterType, Number lifespan, unsigned int numParticles, Vector3 direction, Vector3 gravity, Vector3 deviation, Vector3 emitterRadius, Mesh *particleMesh, ScreenMesh *emitter) - : ScreenEntity(), -ParticleEmitter(imageFile, particleMesh, particleType, emitterType, lifespan, numParticles, direction, gravity, deviation, emitterRadius) -{ - particleSize = 10.0; - isScreenEmitter = true; - emitterMesh = emitter; - createParticles(); -} - -ScreenParticleEmitter::~ScreenParticleEmitter(){ - for(int i=0;i < particles.size(); i++) { - removeChild((ScreenEntity*)particles[i]->particleBody); - delete particles[i]; - } -} +void SceneParticleEmitter::setParticleLifetime(Number lifetime) { + this->lifetime = lifetime; + for(int i=0; i < particles.size(); i++) { + resetParticle(i); + particles[i].lifetime = RANDOM_NUMBER * -lifetime; + } +} -Entity *ScreenParticleEmitter::Clone(bool deepClone, bool ignoreEditorOnly) const { - ScreenParticleEmitter *newEmitter = new ScreenParticleEmitter("default.png", Particle::BILLBOARD_PARTICLE, ParticleEmitter::CONTINUOUS_EMITTER, 2.0, 0, Vector3(0.0, -40.0, 0.0), Vector3(0.0, 0.0, 0.0), Vector3(0.0, 0.0, 0.0), Vector3(10.0, 10.0, 0.0)); - applyClone(newEmitter, deepClone, ignoreEditorOnly); - return newEmitter; -} - -void ScreenParticleEmitter::applyClone(Entity *clone, bool deepClone, bool ignoreEditorOnly) const { - ScreenParticleEmitter *_clone = (ScreenParticleEmitter*) clone; - - _clone->emitterRadius = this->emitterRadius; - _clone->dirVector = this->dirVector; - _clone->gravVector = this->gravVector; - _clone->deviation = this->deviation; - - _clone->setIgnoreParentMatrix(getIgnoreParentMatrix()); - - _clone->brightnessDeviation = this->brightnessDeviation; - _clone->particleSize = this->particleSize; - _clone->perlinModSize = this->perlinModSize; - _clone->perlinEnabled = this->perlinEnabled; - _clone->particleSpeedMod = this->particleSpeedMod; - - _clone->rotationSpeed = this->rotationSpeed; - _clone->lifespan = this->lifespan; - _clone->particleSpeedMod = this->particleSpeedMod; - _clone->setParticleCount(this->getNumParticles()); - - _clone->rotationFollowsPath = this->rotationFollowsPath; - _clone->useScaleCurves = this->useScaleCurves; - _clone->scaleCurve = this->scaleCurve; +void SceneParticleEmitter::triggerParticles(bool allAtOnce) { + for(int i=0; i < particles.size(); i++) { + resetParticle(i); + if(allAtOnce) { + particles[i].lifetime = 0.0; + } else { + particles[i].lifetime = RANDOM_NUMBER * -lifetime; + } + } +} - _clone->useColorCurves = this->useColorCurves; - - _clone->colorCurveR = this->colorCurveR; - _clone->colorCurveG = this->colorCurveG; - _clone->colorCurveB = this->colorCurveB; - _clone->colorCurveA = this->colorCurveA; - _clone->setParticleBlendingMode(this->getParticleBlendingMode()); - _clone->setParticleTexture(this->getParticleTexture()); - _clone->setWidth(_clone->emitterRadius.x); - _clone->setHeight(_clone->emitterRadius.y); - - ScreenEntity::applyClone(clone, false, ignoreEditorOnly); +Vector3 SceneParticleEmitter::getDirectionDeviation() const { + return directionDeviation; } -void ScreenParticleEmitter::Update() { - updateEmitter(); -} - -void ScreenParticleEmitter::addParticleBody(Entity *particleBody) { - addChild((ScreenEntity*)particleBody); - particleBody->editorOnly = true; -} - -void ScreenParticleEmitter::dispatchTriggerCompleteEvent() { - ((EventDispatcher*)this)->dispatchEvent(new Event(Event::COMPLETE_EVENT), Event::COMPLETE_EVENT); -} - -Matrix4 ScreenParticleEmitter::getBaseMatrix() { - rebuildTransformMatrix(); - return getConcatenatedMatrix(); -} - -ParticleEmitter::ParticleEmitter(const String& imageFile, Mesh *particleMesh, int particleType, int emitterType, Number lifespan, unsigned int numParticles, Vector3 direction, Vector3 gravity, Vector3 deviation, Vector3 emitterRadius) { - - this->emitterRadius = emitterRadius; - isScreenEmitter = false; - dirVector = direction; - gravVector = gravity; - ignoreParentMatrix = false; - this->emitterType = emitterType; - // TODO: initialize emitSpeed - this->deviation = deviation; - pMesh = particleMesh; - rotationFollowsPath = false; - rotationSpeed = 100.0f; - perlinEnabled = false; - emitterRadius = Vector3(0.0f,0.0f,0.0f); - perlinModSize = 0.002; - brightnessDeviation = 0.0f; - particleSpeedMod = 1.0f; - isEmitterEnabled = true; - allAtOnce = false; - - blendingMode = Renderer::BLEND_MODE_NORMAL; - - particleSize = 1.0; - - scaleCurve.addControlPoint2dWithHandles(-0.1, 0.5, 0.0, 0.5, 0.1, 0.5); - scaleCurve.addControlPoint2dWithHandles(0.9, 0.5, 1.0, 0.5, 1.1, 0.5); - - colorCurveR.addControlPoint2dWithHandles(-0.1, 0.5, 0.0, 0.5, 0.1, 0.5); - colorCurveR.addControlPoint2dWithHandles(0.9, 0.5, 1.0, 0.5, 1.1, 0.5); - - colorCurveG.addControlPoint2dWithHandles(-0.1, 0.5, 0.0, 0.5, 0.1, 0.5); - colorCurveG.addControlPoint2dWithHandles(0.9, 0.5, 1.0, 0.5, 1.1, 0.5); - - colorCurveB.addControlPoint2dWithHandles(-0.1, 0.5, 0.0, 0.5, 0.1, 0.5); - colorCurveB.addControlPoint2dWithHandles(0.9, 0.5, 1.0, 0.5, 1.1, 0.5); - - colorCurveA.addControlPoint2dWithHandles(-0.1, 0.5, 0.0, 0.5, 0.1, 0.5); - colorCurveA.addControlPoint2dWithHandles(0.9, 0.5, 1.0, 0.5, 1.1, 0.5); - - - this->particleType = particleType; - - this->numParticles = numParticles; - - this->lifespan = lifespan; - timer = new Timer(true, 1); - motionPerlin = new Perlin(3,5,1.0,rand()); - - textureFile = imageFile; - - useColorCurves = false; - useScaleCurves = false; -} - -bool ParticleEmitter::getIgnoreParentMatrix() const { - return ignoreParentMatrix; +Vector3 SceneParticleEmitter::getPerlinValue() const { + return perlinValue; } -void ParticleEmitter::setIgnoreParentMatrix(bool val) { - ignoreParentMatrix = val; - for(int i=0; i < particles.size(); i++) { - particles[i]->particleBody->ignoreParentMatrix = ignoreParentMatrix; - } +bool SceneParticleEmitter::getPerlinEnabled() const { + return perlinEnabled; } +Vector3 SceneParticleEmitter::getEmitterSize() const { + return emitterSize; +} -Texture *ParticleEmitter::getParticleTexture() const { - return particleTexture; +Vector3 SceneParticleEmitter::getGravity() const { + return gravity; } -void ParticleEmitter::setParticleTexture(Texture *texture) { - particleTexture = texture; - for(int i=0; i < particles.size(); i++) { - ((ScreenMesh*)particles[i]->particleBody)->setTexture(particleTexture); - } +Number SceneParticleEmitter::getParticleLifetime() const { + return lifetime; } - -void ParticleEmitter::createParticles() { - - if(isScreenEmitter) - particleTexture = CoreServices::getInstance()->getMaterialManager()->createTextureFromFile(textureFile); - else - particleMaterial = (Material*)CoreServices::getInstance()->getResourceManager()->getResource(Resource::RESOURCE_MATERIAL, textureFile); - - - Particle *particle; - for(int i=0; i < numParticles; i++) { - particle = new Particle(particleType, isScreenEmitter, particleMaterial, particleTexture, pMesh); - particle->particleBody->ignoreParentMatrix = ignoreParentMatrix; - particle->velVector = dirVector; - particle->dirVector = dirVector; - particle->deviation = deviation; - particle->lifespan = lifespan; - particles.push_back(particle); - addParticleBody(particle->particleBody); - resetParticle(particle); - particle->life = lifespan * ((Number)rand()/RAND_MAX); - } - updateEmitter(); -} - -void ParticleEmitter::dispatchTriggerCompleteEvent() { -} - -void ParticleEmitter::addParticleBody(Entity *particleBody) { -} - -Matrix4 ParticleEmitter::getBaseMatrix() { - return Matrix4(); + +bool SceneParticleEmitter::getParticlesInWorldSpace() const { + return particlesInWorldSpace; } - -void ParticleEmitter::setEmitterRadius(Vector3 rad) { - emitterRadius = rad; + +Number SceneParticleEmitter::getParticleSize() const { + return particleSize; } -void ParticleEmitter::setRotationSpeed(Number speed) { - rotationSpeed = speed; +void SceneParticleEmitter::setFloorPlaneOffset(Number floorPlaneOffset) { + this->floorPlaneOffset = floorPlaneOffset; } -void ParticleEmitter::setParticleVisibility(bool val) { - for(int i=0;i < particles.size(); i++) { - particles[i]->particleBody->visible = val; - } +void SceneParticleEmitter::setParticleDirection(const Vector3 &direction) { + directionVector = direction; } -void ParticleEmitter::setParticleBlendingMode(int mode) { - blendingMode = mode; - for(int i=0;i < particles.size(); i++) { - particles[i]->particleBody->setBlendingMode(mode); - } +Vector3 SceneParticleEmitter::getParticleDirection() const { + return directionVector; } -int ParticleEmitter::getParticleBlendingMode() const { - return blendingMode; +void SceneParticleEmitter::setFloorDamping(Number floorDamping) { + this->floorDamping = floorDamping; } -void ParticleEmitter::setAlphaTest(bool val) { - for(int i=0;i < particles.size(); i++) { - particles[i]->particleBody->alphaTest = val; - } +Vector3 SceneParticleEmitter::getParticleRotationSpeed() const { + return particleRotationSpeed; } -void ParticleEmitter::setDepthWrite(bool val) { - for(int i=0;i < particles.size(); i++) { - particles[i]->particleBody->depthWrite = val; - } +void SceneParticleEmitter::setPerlinEnabled(bool val) { + perlinEnabled = val; } -void ParticleEmitter::setDepthTest(bool val) { - for(int i=0;i < particles.size(); i++) { - particles[i]->particleBody->depthTest= val; - } +Number SceneParticleEmitter::getParticleSpeed() const { + return particleSpeed; } - - -void ParticleEmitter::setBillboardMode(bool mode) { - for(int i=0;i < particles.size(); i++) { - particles[i]->particleBody->billboardMode = mode; - } -} - -void ParticleEmitter::enablePerlin(bool val) { - perlinEnabled = val; -} - -ParticleEmitter::~ParticleEmitter() { - -} - -void ParticleEmitter::setParticleCount(int count) { - if(count > particles.size()) { - int oldSize = count-particles.size(); - Particle *particle; - for(int i=0; i < oldSize; i++) { - particle = new Particle(particleType, isScreenEmitter, particleMaterial, particleTexture, pMesh); - particle->particleBody->ignoreParentMatrix = ignoreParentMatrix; - particle->velVector = dirVector; - particle->dirVector = dirVector; - particle->deviation = deviation; - particle->lifespan = lifespan; - particle->life = lifespan * ((Number)rand()/RAND_MAX); - particles.push_back(particle); - addParticleBody(particle->particleBody); - } - } - numParticles = count; - for(int i=0; i < particles.size(); i++) { - if(i < numParticles) - particles[i]->particleBody->visible =true; - else - particles[i]->particleBody->visible = false; - } - resetAll(); -} - -void ParticleEmitter::setPerlinModSize(Number size) { - perlinModSize = size; - -} - -void ParticleEmitter::enableEmitter(bool val) { - isEmitterEnabled = val; - if(val) { - for(int i=0;i < numParticles; i++) { - particles[i]->life = particles[i]->lifespan * ((Number)rand()/RAND_MAX); - } - } -} - -void ParticleEmitter::Trigger() { - if(!isEmitterEnabled) - return; - for(int i=0;i < numParticles; i++) { - resetParticle(particles[i]); - } -} - -bool ParticleEmitter::emitterEnabled() { - return isEmitterEnabled; -} - -void ParticleEmitter::resetParticle(Particle *particle) { -// particle->particleBody->visible = true; - particle->lifespan = lifespan; - Matrix4 concatMatrix = getBaseMatrix(); - - Vector3 startVector; - - Vector3 compoundScale(1.0, 1.0, 1.0); - if(ignoreParentMatrix) { - compoundScale = getParticleCompoundScale(); - } - - particle->dirVector = dirVector; -// if(emitterMesh) { -// Polygon *randPoly = emitterMesh->getMesh()->getPolygon(rand() % emitterMesh->getMesh()->getPolygonCount()); -// startVector = *randPoly->getVertex(rand() % 3); -// startVector = emitterMesh->getConcatenatedMatrix() * startVector; -// } else { - startVector = Vector3(-(emitterRadius.x/2.0f)+emitterRadius.x*((Number)rand()/RAND_MAX),-(emitterRadius.y/2.0f)+emitterRadius.y*((Number)rand()/RAND_MAX),-(emitterRadius.z/2.0f)+emitterRadius.z*((Number)rand()/RAND_MAX)); -// } - - - particle->Reset(emitterType != TRIGGERED_EMITTER); - particle->velVector = particle->dirVector; - Number dev = ((deviation.x/2.0f)*-1.0f) + ((deviation.x)*((Number)rand()/RAND_MAX)); - particle->velVector.x += dev; - dev = (deviation.y/2.0f*-1.0f) + ((deviation.y)*((Number)rand()/RAND_MAX)); - particle->velVector.y += dev; - dev = (deviation.z/2.0f*-1.0f) + ((deviation.z)*((Number)rand()/RAND_MAX)); - particle->velVector.z += dev; - - particle->brightnessDeviation = 1.0f - ( (-brightnessDeviation) + ((brightnessDeviation*2) * ((Number)rand()/RAND_MAX))); - -// particle->velVector = concatMatrix.rotateVector(particle->velVector); - - if(ignoreParentMatrix) { - particle->particleBody->setPosition(concatMatrix.getPosition()); - } else { - particle->particleBody->setPosition(0.0, 0.0, 0.0); - } - - particle->particleBody->Translate(startVector); - particle->particleBody->rebuildTransformMatrix(); - - if(useScaleCurves) { - particle->particleBody->setScale(scaleCurve.getHeightAt(0) * particleSize * compoundScale.x, - scaleCurve.getHeightAt(0) * particleSize * compoundScale.y, - scaleCurve.getHeightAt(0) * particleSize * compoundScale.z); - } else { - particle->particleBody->setScale(particleSize * compoundScale.x, particleSize * compoundScale.y, particleSize * compoundScale.z); - } - - if(useColorCurves) { - particle->particleBody->color.setColor(colorCurveR.getHeightAt(0), - colorCurveG.getHeightAt(0), - colorCurveB.getHeightAt(0), - colorCurveA.getHeightAt(0)); - } else { - particle->particleBody->color.setColor(1.0, 1.0, 1.0, 1.0); - } - - -} - -Vector3 ScreenParticleEmitter::getParticleCompoundScale() { - return getCompoundScale(); -} - -Vector3 SceneParticleEmitter::getParticleCompoundScale() { - return getCompoundScale(); -} - - -Vector3 ParticleEmitter::getParticleCompoundScale() { - return Vector3(); -} - -void ParticleEmitter::resetAll() { - for(int i=0;i < particles.size(); i++) { - if(allAtOnce) - particles[i]->life = 0; - else - particles[i]->life = particles[i]->lifespan * ((Number)rand()/RAND_MAX); - } -} - -void ParticleEmitter::setAllAtOnce(bool val) { - allAtOnce = val; - resetAll(); -} - -unsigned int ParticleEmitter::getNumParticles() const { - return numParticles; -} - -Particle *ParticleEmitter::getParticleAtIndex(unsigned int index) const { - if(index < particles.size()) { - return particles[index]; - } else { - return NULL; - } -} - - -void ParticleEmitter::updateEmitter() { - - Vector3 translationVector; - Number elapsed = timer->getElapsedf(); - - Particle *particle; - Number normLife; - - Vector3 compoundScale(1.0, 1.0, 1.0); - if(ignoreParentMatrix) { - compoundScale = getParticleCompoundScale(); - } - - for(int i=0;i < numParticles; i++) { - particle = particles[i]; - - normLife = particle->life / particle->lifespan; - Vector3 gVec = gravVector; - particle->life += elapsed; - particle->velVector -= gVec*elapsed*particleSpeedMod; - translationVector = particle->velVector; - translationVector = translationVector*elapsed*particleSpeedMod; - if(perlinEnabled) { - translationVector.x += ((perlinModSize * motionPerlin->Get((particle->life/particle->lifespan), particle->perlinPosX))*elapsed*particleSpeedMod); - translationVector.y += ((perlinModSize * motionPerlin->Get((particle->life/particle->lifespan), particle->perlinPosY))*elapsed*particleSpeedMod); - translationVector.z += ((perlinModSize * motionPerlin->Get((particle->life/particle->lifespan), particle->perlinPosZ))*elapsed*particleSpeedMod); - } - - if(isScreenEmitter) { - translationVector.z = 0; - } - - particle->particleBody->Translate(translationVector); - - - if(rotationFollowsPath) { - if(isScreenEmitter) { - Number angle = atan2(translationVector.x, translationVector.y); - particle->particleBody->setRoll(360 - ((angle * TODEGREES)+180)); - - } else { - particle->particleBody->lookAt(particle->particleBody->getPosition() + translationVector, Vector3(1,0,0)); - } - } else { - if(isScreenEmitter) { - particle->particleBody->Roll(rotationSpeed*elapsed); - } else { - particle->particleBody->Roll(rotationSpeed*elapsed); - particle->particleBody->Pitch(rotationSpeed*elapsed); - particle->particleBody->Yaw(rotationSpeed*elapsed); - } - } - -// if(isScreenEmitter) -// particle->particleBody->setPositionZ(0); - - if(useColorCurves) { - particle->particleBody->color.setColor(colorCurveR.getHeightAt(normLife)*particle->brightnessDeviation, - colorCurveG.getHeightAt(normLife)*particle->brightnessDeviation, - colorCurveB.getHeightAt(normLife)*particle->brightnessDeviation, - colorCurveA.getHeightAt(normLife)*particle->brightnessDeviation); - } else { - particle->particleBody->color.setColor(particle->brightnessDeviation, - particle->brightnessDeviation, - particle->brightnessDeviation, - 1.0); - } - - if(useScaleCurves) { - particle->particleBody->setScale(scaleCurve.getHeightAt(normLife) * particleSize * compoundScale.x, - scaleCurve.getHeightAt(normLife) * particleSize * compoundScale.y, - scaleCurve.getHeightAt(normLife) * particleSize * compoundScale.z); - - } else { - particle->particleBody->setScale(particleSize*compoundScale.x, particleSize*compoundScale.y, particleSize*compoundScale.z); - } - - if(particle->life > particle->lifespan && isEmitterEnabled) { - if(emitterType == CONTINUOUS_EMITTER) { - resetParticle(particle); - } else { - // dispatchTriggerCompleteEvent(); -// particle->particleBody->visible = false; - } - } - } + +void SceneParticleEmitter::setParticleSpeed(Number speed) { + particleSpeed = speed; +} + +void SceneParticleEmitter::setPerlinValue(const Vector3 &perlinValue) { + this->perlinValue = perlinValue; +} + +void SceneParticleEmitter::updateParticles() { + + Matrix4 inverseMatrix = systemTrasnformMatrix.Inverse(); + + Number normLife; + Vector3 newBBox; + + Number timeStep = core->getFixedTimestep(); + + for(int i=0; i < particles.size(); i++) { + if(particles[i].lifetime < 0.0 && particles[i].lifetime + timeStep >= 0.0) { + positionParticle(i); + } + + if(particles[i].lifetime >= 0.0 || (particles[i].lifetime < 0.0 && systemEnabled)) { + particles[i].lifetime += timeStep; + } + if(particles[i].lifetime > lifetime) { + if(loopParticles && systemEnabled) { + resetParticle(i); + } + } + + if(particles[i].lifetime < 0.0) { + continue; + } + + normLife = particles[i].lifetime / lifetime; + if(useColorCurves) { + particles[i].color.setColor(colorCurveR.getYValueAtX(normLife)*particles[i].brightnessDeviation, + colorCurveG.getYValueAtX(normLife)*particles[i].brightnessDeviation, + colorCurveB.getYValueAtX(normLife)*particles[i].brightnessDeviation, + colorCurveA.getYValueAtX(normLife)*particles[i].brightnessDeviation); + } + + if(useScaleCurve) { + particles[i].scale = scaleCurve.getYValueAtX(normLife); + } else { + particles[i].scale = 1.0; + } + + particles[i].rotation += particleRotationSpeed *timeStep; + + particles[i].velocity += gravity * timeStep; + particles[i].position += particles[i].velocity * timeStep * particleSpeed; + + if(perlinEnabled) { + + particles[i].position += Vector3(motionPerlin->Get((particles[i].lifetime/lifetime), particles[i].perlinPos.x) * perlinValue.x * timeStep, motionPerlin->Get((particles[i].lifetime/lifetime), particles[i].perlinPos.y) * perlinValue.y * timeStep , motionPerlin->Get((particles[i].lifetime/lifetime), particles[i].perlinPos.z) * perlinValue.z * timeStep); + } + + if(useFloorPlane) { + if(particles[i].position.y <= floorPlaneOffset) { + particles[i].position.y = floorPlaneOffset; + particles[i].velocity.y *= -1.0 * floorDamping; + } + } + + Vector3 bBoxTest = particles[i].position; + if(particlesInWorldSpace) { + bBoxTest = inverseMatrix * bBoxTest; + } + + if(fabs(bBoxTest.x) > newBBox.x) { + newBBox.x = fabs(bBoxTest.x); + } + if(fabs(bBoxTest.y) > newBBox.y) { + newBBox.y = fabs(bBoxTest.y); + } + if(fabs(bBoxTest.z) > newBBox.z) { + newBBox.z = fabs(bBoxTest.z); + } + } + + setLocalBoundingBox((newBBox + Vector3(particleSize, particleSize, particleSize))* 2.0); +} + +void SceneParticleEmitter::Render() { + systemTrasnformMatrix = getConcatenatedMatrix(); + rebuildParticles(); + SceneMesh::Render(); +} + +void SceneParticleEmitter::fixedUpdate() { + systemTrasnformMatrix = getConcatenatedMatrix(); + updateParticles(); + SceneMesh::Update(); } diff --git a/Core/Contents/Source/PolyPeer.cpp b/Core/Contents/Source/PolyPeer.cpp index b4340458e..4108a35d2 100755 --- a/Core/Contents/Source/PolyPeer.cpp +++ b/Core/Contents/Source/PolyPeer.cpp @@ -27,6 +27,16 @@ THE SOFTWARE. using namespace Polycode; +PeerConnection::PeerConnection() { + localSequence = 0; + remoteSequence = 0; + receivedPacketQueue.resize(32, 0); +} + +PeerConnection::~PeerConnection() { + +} + void PeerConnection::ackPackets(unsigned int ack) { std::vector::iterator it; for(it = reliablePacketQueue.begin(); it != reliablePacketQueue.end();) { @@ -39,6 +49,19 @@ void PeerConnection::ackPackets(unsigned int ack) { } } +void PeerConnection::ackPacketsWithBitfield(unsigned int ack, unsigned int ackBitfield) { + + if(reliablePacketQueue.size() == 0) { + return; + } + + for(int i=0; i <32; i++) { + if(ackBitfield & (1<addEventListener(this, Timer::EVENT_TRIGGER); #endif + reliableRetransmissionInverval = 1000; } Peer::~Peer() { @@ -86,6 +110,10 @@ void Peer::removePeerConnection(PeerConnection* connection) { } } +void Peer::setReliableRetransmissionInterval(int interval) { + reliableRetransmissionInverval = interval; +} + Packet *Peer::createPacket(const Address &target, char *data, unsigned int size, unsigned short type) { PeerConnection *connection = getPeerConnection(target); if(!connection) @@ -93,11 +121,26 @@ Packet *Peer::createPacket(const Address &target, char *data, unsigned int size, Packet *packet = new Packet(); packet->header.sequence = connection->localSequence; packet->header.headerHash = 20; - packet->header.reliableID = 0; packet->header.ack = connection->remoteSequence; packet->header.ackBitfield = 0; - packet->header.size = size; + + for(int i=0; i < 32; i++) { + if(connection->receivedPacketQueue[31-i] == connection->remoteSequence-i) { + packet->header.ackBitfield = (packet->header.ackBitfield & ~(1 << i)) | (1 << i); + } else { + packet->header.ackBitfield = (packet->header.ackBitfield & ~(1 << i)) | (0 << i); + } + } + + int sizeToCopy = size; + if(size > MAX_PACKET_SIZE) { + size = MAX_PACKET_SIZE; + } + + packet->header.size = sizeToCopy; packet->header.type = type; + + if(size > 0) memcpy(packet->data, data, size); connection->localSequence++; @@ -108,15 +151,9 @@ void Peer::sendReliableData(const Address &target, char *data, unsigned int size PeerConnection *connection = getPeerConnection(target); if(!connection) connection = addPeerConnection(target); - Packet *packet = createPacket(target, data, size, type); - packet->header.reliableID = connection->reliableID; - connection->reliableID++; - - if(connection->reliableID == 0) - connection->reliableID = 1; - + Packet *packet = createPacket(target, data, size, type); sendPacket(target, packet); - + SentPacketEntry entry; entry.packet = packet; entry.timestamp = CoreServices::getInstance()->getCore()->getTicks(); @@ -148,31 +185,17 @@ void Peer::sendPacket(const Address &target, Packet *packet) { } bool Peer::checkPacketAcks(PeerConnection *connection, Packet *packet) { - bool retVal = true; - if(packet->header.sequence > connection->remoteSequence) + if(packet->header.sequence > connection->remoteSequence || connection->remoteSequence == 0) { connection->remoteSequence = packet->header.sequence; - else // ignore old packets - retVal = false; - - // if this is a reliable packet, check if it was recently received - if(packet->header.reliableID != 0) { - retVal = true; - for(int i=0; i < connection->recentReliableIDs.size(); i++) { - if(connection->recentReliableIDs[i] == packet->header.reliableID) - retVal = false; - } - - // if still good, push the id into recent reliable acks - if(retVal) { - connection->recentReliableIDs.push_back(packet->header.reliableID); - if(connection->recentReliableIDs.size() > 50) - connection->recentReliableIDs.erase(connection->recentReliableIDs.begin()); - } + } else { + return false; } - connection->ackPackets(packet->header.ack); + connection->receivedPacketQueue.push_back(packet->header.sequence); + connection->receivedPacketQueue.pop_front(); - return retVal; + connection->ackPacketsWithBitfield(packet->header.ack, packet->header.ackBitfield); + return true; } void Peer::handleEvent(Event *event) { @@ -195,8 +218,13 @@ void Peer::handleEvent(Event *event) { void Peer::updateReliableDataQueue() { for(int i=0; i < peerConnections.size(); i++) { for(int j=0; j < peerConnections[i]->reliablePacketQueue.size(); j++) { - if(peerConnections[i]->reliablePacketQueue[j].timestamp < CoreServices::getInstance()->getCore()->getTicks() - 1000) { + if(peerConnections[i]->reliablePacketQueue[j].timestamp < CoreServices::getInstance()->getCore()->getTicks() - reliableRetransmissionInverval) { peerConnections[i]->reliablePacketQueue[j].timestamp = CoreServices::getInstance()->getCore()->getTicks(); + + Packet *oldPacket = peerConnections[i]->reliablePacketQueue[j].packet; + peerConnections[i]->reliablePacketQueue[j].packet = createPacket(peerConnections[i]->address, oldPacket->data, oldPacket->header.size, oldPacket->header.type); + delete oldPacket; + sendPacket(peerConnections[i]->address, peerConnections[i]->reliablePacketQueue[j].packet); } } diff --git a/Core/Contents/Source/PolyPolygon.cpp b/Core/Contents/Source/PolyPolygon.cpp deleted file mode 100755 index c5e4073bb..000000000 --- a/Core/Contents/Source/PolyPolygon.cpp +++ /dev/null @@ -1,169 +0,0 @@ -/* - Copyright (C) 2011 by Ivan Safrin - - 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: - - The above copyright notice and this permission notice shall be included in - all copies or substantial portions of the Software. - - 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. -*/ - -#include "PolyPolygon.h" -#include "PolyVector2.h" -#include "PolyVertex.h" - -using std::min; -using std::max; - -namespace Polycode { - -Polygon::Polygon() : useVertexNormals(false), vertexCount(0) { - useVertexNormals = true; -} - -Polygon::~Polygon() { - - for(int i=0; i < vertices.size(); i++) { - delete vertices[i]; - } - vertices.clear(); -} - -void Polygon::flipUVY() { - for(int i=0; i < vertices.size(); i++) { - Vector2 coord = vertices[i]->getTexCoord(); - vertices[i]->setTexCoord(coord.x, 1-coord.y); - } -} - -unsigned int Polygon::getVertexCount() { - return vertices.size(); -} - -Vertex *Polygon::getVertex(unsigned int index) { - return vertices[index]; -} - -Vector3 Polygon::getFaceTangent() { - return tangent; -} - -Vector3 Polygon::getFaceNormal() { -/* - Vector3 fNormal; - fNormal.x = (vertices[2]->z-vertices[1]->z)*(vertices[2]->y-vertices[1]->y)-(vertices[0]->y-vertices[1]->y)*(vertices[2]->z-vertices[1]->z); - fNormal.y = (vertices[0]->x-vertices[1]->x)*(vertices[2]->z-vertices[1]->z)-(vertices[0]->z-vertices[1]->z)*(vertices[2]->x-vertices[1]->x); - fNormal.z = (vertices[0]->y-vertices[1]->y)*(vertices[2]->x-vertices[1]->x)-(vertices[0]->x-vertices[1]->x)*(vertices[2]->y-vertices[1]->y); - fNormal.Normalize(); - return fNormal; - */ - return normal; -} - -Rectangle Polygon::getBounds2D() { - Rectangle retBox; - retBox.x = 1000000000; - retBox.y = 1000000000; - for(int i=0; i < vertices.size(); i++) { - retBox.x = min(retBox.x,vertices[i]->x); - retBox.y = min(retBox.y,vertices[i]->y); - } - for(int i=0; i < vertices.size(); i++) { - retBox.w = max(retBox.w, vertices[i]->x - retBox.x); - retBox.h = max(retBox.h, vertices[i]->y - retBox.y); - } - - return retBox; -} - -void Polygon::removeVertex(int index) { - Vertex *vert = vertices[index]; - vertices.erase(vertices.begin() + index); - delete vert; -} - -void Polygon::setNormal(Vector3 normal) { - this->normal = normal; -} - -void Polygon::calculateNormal() { - if(vertices.size() < 3) - return; - -// normal->x = (vertices[2]->z-vertices[1]->z)*(vertices[2]->y-vertices[1]->y)-(vertices[0]->y-vertices[1]->y)*(vertices[2]->z-vertices[1]->z); -// normal->y = (vertices[0]->x-vertices[1]->x)*(vertices[2]->z-vertices[1]->z)-(vertices[0]->z-vertices[1]->z)*(vertices[2]->x-vertices[1]->x); -// normal->z = (vertices[0]->y-vertices[1]->y)*(vertices[2]->x-vertices[1]->x)-(vertices[0]->x-vertices[1]->x)*(vertices[2]->y-vertices[1]->y); - - normal = (*vertices[0] - *vertices[1]).crossProduct((*vertices[1] - *vertices[2])); - - normal.Normalize(); - - for(int i=0; i < vertices.size(); i++) { - vertices[i]->normal.x = normal.x; - vertices[i]->normal.y = normal.y; - vertices[i]->normal.z = normal.z; - } -} - -void Polygon::calculateTangent() { - if(vertices.size() < 3) - return; - - - Vector3 side0 = *vertices[0] - *vertices[1]; - Vector3 side1 = *vertices[2] - *vertices[0]; - Vector3 normal = side1.crossProduct(side0); - normal.Normalize(); - Number deltaV0 = vertices[0]->texCoord.y - vertices[1]->texCoord.y; - Number deltaV1 = vertices[2]->texCoord.y - vertices[0]->texCoord.y; - tangent = side0 * deltaV1 - side1 * deltaV0; - tangent.Normalize(); - - Number deltaU0 = vertices[0]->texCoord.x - vertices[1]->texCoord.x; - Number deltaU1 = vertices[2]->texCoord.x - vertices[0]->texCoord.x; - Vector3 binormal = side0 * deltaU1 - side1 * deltaU0; - binormal.Normalize(); - Vector3 tangentCross = tangent.crossProduct(binormal); - - if (tangentCross.dot(normal) < 0.0f) { - tangent = tangent * -1; - } - - for(int i=0; i < vertices.size(); i++) { - vertices[i]->tangent.x = tangent.x; - vertices[i]->tangent.y = tangent.y; - vertices[i]->tangent.z = tangent.z; - } - - -} - -Vertex *Polygon::addVertex(Number x, Number y, Number z) { - Vertex *vertex = new Vertex(x,y,z); - vertices.push_back(vertex); - return vertex; -} - -void Polygon::addVertex(Vertex *vertex) { - vertices.push_back(vertex); -} - -Vertex *Polygon::addVertex(Number x, Number y, Number z, Number u, Number v) { - Vertex *vertex = new Vertex(x,y,z,u,v); - vertices.push_back(vertex); - return vertex; -} - -} diff --git a/Core/Contents/Source/PolyQuaternionCurve.cpp b/Core/Contents/Source/PolyQuaternionCurve.cpp index dda768200..1f14df6ff 100755 --- a/Core/Contents/Source/PolyQuaternionCurve.cpp +++ b/Core/Contents/Source/PolyQuaternionCurve.cpp @@ -36,15 +36,6 @@ QuaternionCurve::~QuaternionCurve() { void QuaternionCurve::generatePointsFromCurves(BezierCurve *wCurve, BezierCurve *xCurve, BezierCurve *yCurve, BezierCurve *zCurve) { for(int i=0; i < wCurve->getNumControlPoints(); i++) { - /* - Quaternion newQuat(wCurve->getControlPoint(i)->p2.y, - xCurve->getControlPoint(i)->p2.y, - yCurve->getControlPoint(i)->p2.y, - zCurve->getControlPoint(i)->p2.y); - - points.push_back(newQuat); - recalcTangents(); - */ Quaternion quat1(wCurve->getControlPoint(i)->p1.y, xCurve->getControlPoint(i)->p1.y, yCurve->getControlPoint(i)->p1.y, @@ -64,130 +55,42 @@ void QuaternionCurve::generatePointsFromCurves(BezierCurve *wCurve, BezierCurve QuatTriple newTriple; newTriple.q1 = quat1; newTriple.q2 = quat2; - newTriple.q3 = quat3; + newTriple.q3 = quat3; tPoints.push_back(newTriple); } } - Quaternion QuaternionCurve::interpolate(Number t, bool useShortestPath) - { - // Work out which segment this is in - Number fSeg = t * (tPoints.size() - 1); - unsigned int segIdx = (unsigned int)fSeg; - // Apportion t - t = fSeg - segIdx; - - return interpolate(segIdx, t, useShortestPath); - - } - //--------------------------------------------------------------------- - Quaternion QuaternionCurve::interpolate(unsigned int fromIndex, Number t, - bool useShortestPath) - { - // Bounds check - assert (fromIndex >= 0 && fromIndex < tPoints.size() && - "fromIndex out of bounds"); - - if ((fromIndex + 1) == tPoints.size() && tPoints[fromIndex].q2 != tPoints[0].q2) - { - Logger::log("size\n"); - // Duff request, cannot blend to nothing - // Just return source - return points[fromIndex]; - - } - // Fast special cases - if (t == 0.0f) - { - return tPoints[fromIndex].q2; - } - else if(t == 1.0f) - { - return tPoints[fromIndex + 1].q2; - } - - Quaternion &p = tPoints[fromIndex].q2; - Quaternion &q = tPoints[fromIndex+1].q2; - Quaternion &a = tPoints[fromIndex].q3; - Quaternion &b = tPoints[fromIndex+1].q1; - - if ((fromIndex + 1) == tPoints.size() && tPoints[fromIndex].q2 == tPoints[0].q2) { - q = tPoints[1].q2; - b = tPoints[1].q1; - } - - // NB interpolate to nearest rotation - return Quaternion::Squad(t, p, a, b, q, useShortestPath); - - } - -void QuaternionCurve::recalcTangents() +Quaternion QuaternionCurve::interpolate(Number t, bool useShortestPath) { - unsigned int i, numPoints; - bool isClosed; - - numPoints = (unsigned int)points.size(); - - if (numPoints < 2) { - return; - } - - tangents.resize(numPoints); - - if (points[0] == points[numPoints-1]) - { - isClosed = true; - } - else - { - isClosed = false; - } - - Quaternion invp, part1, part2, preExp; - for(i = 0; i < numPoints; ++i) - { - Quaternion &p = points[i]; - invp = p.Inverse(); - - if (i ==0) - { - // special case start - part1 = (invp * points[i+1]).Log(); - if (isClosed) - { - // Use numPoints-2 since numPoints-1 == end == start == this one - part2 = (invp * points[numPoints-2]).Log(); - } - else - { - part2 = (invp * p).Log(); - } - } - else if (i == numPoints-1) - { - // special case end - if (isClosed) - { - // Wrap to [1] (not [0], this is the same as end == this one) - part1 = (invp * points[1]).Log(); - } - else - { - part1 = (invp * p).Log(); - } - part2 = (invp * points[i-1]).Log(); - } - else - { - part1 = (invp * points[i+1]).Log(); - part2 = (invp * points[i-1]).Log(); - } - - preExp =(part1 + part2) * -0.25 ; - tangents[i] = p * preExp.Exp(); - - } + t = std::min(std::max(t, Number(0.0)), Number(1.0)); + Number fSeg = t * (tPoints.size() - 1); + unsigned int segIdx = (unsigned int)fSeg; + t = fSeg - segIdx; + return interpolate(segIdx, t, useShortestPath); +} +Quaternion QuaternionCurve::interpolate(unsigned int fromIndex, Number t, bool useShortestPath) { + if ((fromIndex + 1) == tPoints.size() && tPoints[fromIndex].q2 != tPoints[0].q2) { + return tPoints[fromIndex].q2; } + if (t == 0.0f) { + return tPoints[fromIndex].q2; + } + else if(t == 1.0f) { + return tPoints[fromIndex + 1].q2; + } + + Quaternion &p = tPoints[fromIndex].q2; + Quaternion &q = tPoints[fromIndex+1].q2; + Quaternion &a = tPoints[fromIndex].q3; + Quaternion &b = tPoints[fromIndex+1].q1; + + if ((fromIndex + 1) == tPoints.size() && tPoints[fromIndex].q2 == tPoints[0].q2) { + q = tPoints[1].q2; + b = tPoints[1].q1; + } + + return Quaternion::Squad(t, p, a, b, q, useShortestPath); +} \ No newline at end of file diff --git a/Core/Contents/Source/PolyRay.cpp b/Core/Contents/Source/PolyRay.cpp new file mode 100644 index 000000000..7e28db93c --- /dev/null +++ b/Core/Contents/Source/PolyRay.cpp @@ -0,0 +1,181 @@ +/* + Copyright (C) 2013 by Ivan Safrin + + 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: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + 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. +*/ + +#include "PolyRay.h" +#include + +using namespace Polycode; + +Ray::Ray() { + inv_direction = Vector3(1.0/direction.x, 1.0/direction.y, 1.0/direction.z); + sign[0] = (inv_direction.x < 0); + sign[1] = (inv_direction.y < 0); + sign[2] = (inv_direction.z < 0); + this->origin = origin; + this->direction = direction; +} + +Ray::Ray(const Vector3 &origin, const Vector3 &direction) { + inv_direction = Vector3(1.0/direction.x, 1.0/direction.y, 1.0/direction.z); + sign[0] = (inv_direction.x < 0); + sign[1] = (inv_direction.y < 0); + sign[2] = (inv_direction.z < 0); + this->origin = origin; + this->direction = direction; +} + +Ray Ray::tranformByMatrix(const Matrix4& matrix) const { + Vector3 pos = matrix * origin; + Vector3 dir = matrix.rotateVector(direction); + dir.Normalize(); + return Ray(pos, dir); +} + +Vector3 Ray::planeIntersectPoint(const Vector3 &planeNormal, Number planeDistance) const { + Number distanceToOrigin = origin.dot(planeNormal) - planeDistance; + return origin + direction * (-distanceToOrigin / direction.dot(planeNormal)); +} + +Vector3 Ray::planeIntersectPoint(const Vector3 &planeNormal, const Vector3 &planePosition) const { + Number d = (planePosition - origin).dot(planeNormal) / direction.dot(planeNormal); + return origin + direction * d; +} + +bool Ray::polygonIntersect(const Vector3 &v1, const Vector3 &v2, const Vector3 &v3) const { + + Number t,u,v; + t = 0; u = 0; v = 0; + + Vector3 edge1 = v2 - v3; + Vector3 edge2 = v1 - v3; + + Vector3 tvec, pvec, qvec; + Number det, inv_det; + + pvec = direction.crossProduct(edge2); + det = edge1.dot(pvec); + + if (det > -0.00001f) + return false; + + inv_det = 1.0f / det; + + tvec = origin - v3; + + u = tvec.dot(pvec) * inv_det; + if (u < -0.001f || u > 1.001f) + return false; + + qvec = tvec.crossProduct(edge1); + + v = direction.dot(qvec) * inv_det; + if (v < -0.001f || u + v > 1.001f) + return false; + + t = edge2.dot(qvec) * inv_det; + + if (t <= 0) + return false; + + return true; +} + +Vector3 Ray::closestPointOnRay(const Vector3 &point) const { + Number b = (point - origin).dot(direction)/ direction.dot(direction); + return origin + direction*b; +} + +bool Ray::closestPointsBetween(const Ray &ray2, Vector3 *point1, Vector3 *point2) { + Vector3 wOrigin = origin - ray2.origin; + + Number a = direction.dot(direction); + Number b = direction.dot(ray2.direction); + Number c = ray2.direction.dot(ray2.direction); + Number d = direction.dot(wOrigin); + Number e = ray2.direction.dot(wOrigin); + Number denom = a*c - b*b; + + if(denom < 0.00001) { + if(point1) + *point1 = Vector3(0); + if(point2) + *point2 = Vector3(0); + return false; + } + + Number s = (b*e - c*d)/denom; + Number t = (a*e - b*d)/denom; + + + if(point1) + *point1 = origin + direction*s; + if(point2) + *point2 = ray2.origin + ray2.direction*t; + return true; +} + +Number Ray::boxIntersect(const Vector3 &box, const Matrix4 &transformMatrix, float near, float far) const { + + if(box.x == 0 || box.y == 0 || box.z == 0) + return -1.0; + + Ray r = tranformByMatrix(transformMatrix.Inverse()); + + Vector3 bounds[2]; + bounds[0] = Vector3(-box.x * 0.5, -box.y * 0.5, -box.z * 0.5); + bounds[1] = Vector3(box.x * 0.5, box.y * 0.5, box.z * 0.5); + + float tmin, tmax, tymin, tymax, tzmin, tzmax; + tmin = (bounds[r.sign[0]].x - r.origin.x) * r.inv_direction.x; + tmax = (bounds[1-r.sign[0]].x - r.origin.x) * r.inv_direction.x; + tymin = (bounds[r.sign[1]].y - r.origin.y) * r.inv_direction.y; + tymax = (bounds[1-r.sign[1]].y - r.origin.y) * r.inv_direction.y; + + if ( (tmin > tymax) || (tymin > tmax) ) + return -1.0; + + if (tymin > tmin) + tmin = tymin; + + if (tymax < tmax) + tmax = tymax; + + tzmin = (bounds[r.sign[2]].z - r.origin.z) * r.inv_direction.z; + tzmax = (bounds[1-r.sign[2]].z - r.origin.z) * r.inv_direction.z; + + if ( (tmin > tzmax) || (tzmin > tmax) ) + return -1.0; + + if (tzmin > tmin) + tmin = tzmin; + + if (tzmax < tmax) + tmax = tzmax; + + if( (tmin < far) && (tmax > near) ) { + Vector3 hitpoint = r.origin + (r.direction * fabs(tmin)); + hitpoint = transformMatrix * hitpoint; + return origin.distance(hitpoint); + } else { + return -1.0; + } +} diff --git a/Core/Contents/Source/PolyRenderDataArray.cpp b/Core/Contents/Source/PolyRenderDataArray.cpp new file mode 100755 index 000000000..8295e9cc9 --- /dev/null +++ b/Core/Contents/Source/PolyRenderDataArray.cpp @@ -0,0 +1,54 @@ +/* + Copyright (C) 2014 by Ivan Safrin + + 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: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + 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. + */ + +#include "PolyRenderDataArray.h" + +using namespace Polycode; + +RenderDataArray::RenderDataArray(unsigned int type) { + this->type = type; +} + +void *RenderDataArray::getArrayData() { + return NULL; +} + +unsigned int RenderDataArray::getDataSize() { + return 0; +} + +void *VertexDataArray::getArrayData() { + return (void*) data.data(); +} + +unsigned int VertexDataArray::getDataSize() { + return data.size(); +} + + +void *IndexDataArray::getArrayData() { + return (void*) data.data(); +} + +unsigned int IndexDataArray::getDataSize() { + return data.size(); +} diff --git a/Core/Contents/Source/PolyRenderer.cpp b/Core/Contents/Source/PolyRenderer.cpp index fdf7d2ba1..dc3b3c4f6 100755 --- a/Core/Contents/Source/PolyRenderer.cpp +++ b/Core/Contents/Source/PolyRenderer.cpp @@ -21,11 +21,14 @@ */ #include "PolyRenderer.h" +#include "PolyFixedShader.h" +#include "PolyMaterial.h" +#include "PolyModule.h" #include "PolyMesh.h" using namespace Polycode; -Renderer::Renderer() : clearColor(0.2f, 0.2f, 0.2f, 0.0), currentTexture(NULL), renderMode(0), lightingEnabled(false), orthoMode(false), xRes(0), yRes(0) { +Renderer::Renderer() : clearColor(0.2, 0.2, 0.2, 0.0), currentTexture(NULL), lightingEnabled(false), xRes(0), yRes(0) { anisotropy = 0; textureFilteringMode = TEX_FILTERING_LINEAR; currentMaterial = NULL; @@ -34,18 +37,76 @@ Renderer::Renderer() : clearColor(0.2f, 0.2f, 0.2f, 0.0), currentTexture(NULL), shadersEnabled = true; currentMaterial = NULL; numLights = 0; - numAreaLights = 0; + numPointLights = 0; numSpotLights = 0; exposureLevel = 1; shadersEnabled = true; currentShaderModule = NULL; - fov = 45.0; setAmbientColor(0.0,0.0,0.0); cullingFrontFaces = false; scissorEnabled = false; blendNormalAsPremultiplied = false; - + alphaTestValue = 0.01; doClearBuffer = true; + backingResolutionScaleX = 1.0; + backingResolutionScaleY = 1.0; + overrideMaterial = NULL; + renderToGlobalFramebuffer = false; + globalColorFramebuffer = NULL; + globalDepthFramebuffer = NULL; +} + +void Renderer::setRenderToGlobalFramebuffer(bool val) { + + if(val == renderToGlobalFramebuffer) { + return; + } + + renderToGlobalFramebuffer = val; + + if(renderToGlobalFramebuffer) { + createRenderTextures(&globalColorFramebuffer, &globalDepthFramebuffer, getXRes(), getYRes(), false); + } else { + delete globalColorFramebuffer; + delete globalDepthFramebuffer; + } +} + +void Renderer::BeginRender() { + if(renderToGlobalFramebuffer) { + bindFrameBufferTexture(globalColorFramebuffer); + bindFrameBufferTextureDepth(globalDepthFramebuffer); + } +} + +void Renderer::EndRender() { + if(renderToGlobalFramebuffer) { + unbindFramebuffers(); + } +} + +bool Renderer::getRenderToGlobalFramebuffer() const { + return renderToGlobalFramebuffer; +} + +Texture *Renderer::getGlobalColorFramebuffer() const { + return globalColorFramebuffer; +} + +Texture *Renderer::getGlobalDepthFramebuffer() const { + return globalDepthFramebuffer; +} + +void Renderer::setOverrideMaterial(Material *material) { + overrideMaterial = material; +} + +Number Renderer::getBackingResolutionScaleX() { + return backingResolutionScaleX; +} + +Number Renderer::getBackingResolutionScaleY() { + return backingResolutionScaleY; } Renderer::~Renderer() { @@ -57,6 +118,25 @@ bool Renderer::Init() { return true; } +void Renderer::pushVertexColor() { + vertexColorStack.push(currentVertexColor); +} + +void Renderer::popVertexColor() { + currentVertexColor = vertexColorStack.top(); + vertexColorStack.pop(); +} + +void Renderer::multiplyVertexColor(const Color &color) { + currentVertexColor = currentVertexColor * color; + setVertexColor(currentVertexColor.r, currentVertexColor.g, currentVertexColor.b, currentVertexColor.a); +} + +void Renderer::loadVertexColorIdentity() { + currentVertexColor = Color(1.0, 1.0, 1.0, 1.0); + setVertexColor(currentVertexColor.r, currentVertexColor.g, currentVertexColor.b, currentVertexColor.a); +} + void Renderer::enableShaders(bool flag) { shadersEnabled = flag; } @@ -67,162 +147,46 @@ void Renderer::setCameraMatrix(const Matrix4& matrix) { void Renderer::clearLights() { numLights = 0; - numAreaLights = 0; + numPointLights = 0; numSpotLights = 0; lights.clear(); - areaLights.clear(); + pointLights.clear(); spotLights.clear(); -// shadowMapTextures.clear(); -} -/* -void Renderer::addShadowMap(Texture *texture) { - shadowMapTextures.push_back(texture); -} -*/ -void Renderer::setExposureLevel(Number level) { - exposureLevel = level; } -bool Renderer::test2DCoordinateInPolygon(Number x, Number y, Matrix4 cameraMatrix, Matrix4 projectionMatrix, Polycode::Polygon *poly, const Matrix4 &matrix, bool ortho, bool testBackfacing, bool billboardMode, bool reverseDirection, Matrix4 *adjustMatrix) { +void Renderer::bindFrameBufferTexture(Texture *texture) { + framebufferStackColor.push(texture); +} - Vector3 dirVec; - Vector3 origin; - - if(ortho) { - origin = Vector3(((x/(Number)xRes)*orthoSizeX) - (orthoSizeX*0.5), (((yRes-y)/(Number)yRes)*orthoSizeY) - (orthoSizeY*0.5), 0.0); - origin = cameraMatrix * origin; - - dirVec = Vector3(0.0, 0.0, -1.0); - dirVec = cameraMatrix.rotateVector(dirVec); - } else { - dirVec = projectRayFrom2DCoordinate(x, y, cameraMatrix, projectionMatrix); - origin = cameraMatrix.getPosition(); - } - - Vector3 hitPoint; - - Matrix4 fullMatrix = matrix; - - if(billboardMode) { - Matrix4 camInverse = cameraMatrix.Inverse(); - fullMatrix = fullMatrix * camInverse; - - fullMatrix.m[0][0] = 1; - fullMatrix.m[0][1] = 0; - fullMatrix.m[0][2] = 0; - - fullMatrix.m[1][0] = 0; - fullMatrix.m[1][1] = 1; - fullMatrix.m[1][2] = 0; - - fullMatrix.m[2][0] = 0; - fullMatrix.m[2][1] = 0; - fullMatrix.m[2][2] = 1; - - origin = camInverse * origin; - dirVec = camInverse.rotateVector(dirVec); - } - - if(adjustMatrix) { - fullMatrix = (*adjustMatrix) * fullMatrix; - } - - bool retStatus = false; - - - if(poly->getVertexCount() == 3) { - - if(reverseDirection) { - retStatus = rayTriangleIntersect(origin, dirVec, fullMatrix * (*poly->getVertex(2)), fullMatrix * (*poly->getVertex(1)), fullMatrix * (*poly->getVertex(0)), &hitPoint); - if(testBackfacing && !retStatus) { - retStatus = rayTriangleIntersect(origin, dirVec, fullMatrix * (*poly->getVertex(0)), fullMatrix * (*poly->getVertex(1)), fullMatrix * (*poly->getVertex(2)), &hitPoint); - - } - } else { - retStatus = rayTriangleIntersect(origin, dirVec, fullMatrix * (*poly->getVertex(0)), fullMatrix * (*poly->getVertex(1)), fullMatrix * (*poly->getVertex(2)), &hitPoint); - if(testBackfacing && !retStatus) { - retStatus = rayTriangleIntersect(origin, dirVec, fullMatrix * (*poly->getVertex(2)), fullMatrix * (*poly->getVertex(1)), fullMatrix * (*poly->getVertex(0)), &hitPoint); - - } - } - } else if(poly->getVertexCount() == 4) { - - if(reverseDirection) { - - retStatus = (rayTriangleIntersect(origin, dirVec, fullMatrix * (*poly->getVertex(0)), fullMatrix * (*poly->getVertex(1)), fullMatrix * (*poly->getVertex(2)), &hitPoint) || - rayTriangleIntersect(origin, dirVec, fullMatrix * (*poly->getVertex(2)), fullMatrix * (*poly->getVertex(3)), fullMatrix * (*poly->getVertex(0)), &hitPoint)); - if(testBackfacing && !retStatus) { - retStatus = (rayTriangleIntersect(origin, dirVec, fullMatrix * (*poly->getVertex(2)), fullMatrix * (*poly->getVertex(1)), fullMatrix * (*poly->getVertex(0)), &hitPoint) || - rayTriangleIntersect(origin, dirVec, fullMatrix * (*poly->getVertex(0)), fullMatrix * (*poly->getVertex(3)), fullMatrix * (*poly->getVertex(2)), &hitPoint)); - - } - - - } else { - - retStatus = (rayTriangleIntersect(origin, dirVec, fullMatrix * (*poly->getVertex(2)), fullMatrix * (*poly->getVertex(1)), fullMatrix * (*poly->getVertex(0)), &hitPoint) || - rayTriangleIntersect(origin, dirVec, fullMatrix * (*poly->getVertex(0)), fullMatrix * (*poly->getVertex(3)), fullMatrix * (*poly->getVertex(2)), &hitPoint)); - if(testBackfacing && !retStatus) { - retStatus = (rayTriangleIntersect(origin, dirVec, fullMatrix * (*poly->getVertex(0)), fullMatrix * (*poly->getVertex(1)), fullMatrix * (*poly->getVertex(2)), &hitPoint) || - rayTriangleIntersect(origin, dirVec, fullMatrix * (*poly->getVertex(2)), fullMatrix * (*poly->getVertex(3)), fullMatrix * (*poly->getVertex(0)), &hitPoint)); - - } - - } - } else { - retStatus = false; - } - - return retStatus; +void Renderer::bindFrameBufferTextureDepth(Texture *texture) { + framebufferStackDepth.push(texture); } -bool Renderer::rayTriangleIntersect(Vector3 ray_origin, Vector3 ray_direction, Vector3 vert0, Vector3 vert1, Vector3 vert2, Vector3 *hitPoint) -{ +void Renderer::unbindFramebuffers() { + if(framebufferStackColor.size() > 0) { + framebufferStackColor.pop(); + } + if(framebufferStackDepth.size() > 0) { + framebufferStackDepth.pop(); + } -// printf("TESTING RAY\nORIGIN: %f,%f,%f\nDIR: %f,%f,%f\nVERT0: %f,%f,%f\nnVERT1: %f,%f,%f\nnVERT2: %f,%f,%f\n", ray_origin.x, ray_origin.y, ray_origin.z, ray_direction.x, ray_direction.y, ray_direction.z, vert0.x, vert0.y, vert0.z, vert1.x, vert1.y, vert1.z, vert2.x, vert2.y, vert2.z); + if(framebufferStackColor.size() > 0) { + Texture *rebindTexture = framebufferStackColor.top(); + framebufferStackColor.pop(); + bindFrameBufferTexture(rebindTexture); + } + if(framebufferStackDepth.size() > 0) { + Texture *rebindTexture = framebufferStackDepth.top(); + framebufferStackDepth.pop(); + bindFrameBufferTextureDepth(rebindTexture); + } + +} - Number t,u,v; - t = 0; u = 0; v = 0; - - Vector3 edge1 = vert1 - vert0; - Vector3 edge2 = vert2 - vert0; - - Vector3 tvec, pvec, qvec; - Number det, inv_det; - - - pvec = ray_direction.crossProduct(edge2); - det = edge1.dot(pvec); - - if (det > -0.00001f) - return false; - - inv_det = 1.0f / det; - - tvec = ray_origin - vert0; - - u = tvec.dot(pvec) * inv_det; - if (u < -0.001f || u > 1.001f) - return false; - - qvec = tvec.crossProduct(edge1); - - v = ray_direction.dot(qvec) * inv_det; - if (v < -0.001f || u + v > 1.001f) - return false; - - t = edge2.dot(qvec) * inv_det; - - if (t <= 0) - return false; - - hitPoint->x = ray_origin.x+t*ray_direction.x; - hitPoint->y = ray_origin.y+t*ray_direction.y; - hitPoint->z = ray_origin.z+t*ray_direction.z; - - return true; +void Renderer::setExposureLevel(Number level) { + exposureLevel = level; } void Renderer::addShaderModule(PolycodeShaderModule *module) { @@ -230,14 +194,12 @@ void Renderer::addShaderModule(PolycodeShaderModule *module) { } void Renderer::sortLights(){ - - sorter.basePosition = (getModelviewMatrix()).getPosition(); - sorter.cameraMatrix = getCameraMatrix().Inverse(); - sort (areaLights.begin(), areaLights.end(), sorter); + sorter.basePosition = (getModelviewMatrix() * cameraMatrix).getPosition(); + sort (pointLights.begin(), pointLights.end(), sorter); sort (spotLights.begin(), spotLights.end(), sorter); } -void Renderer::addLight(int lightImportance, Vector3 position, Vector3 direction, int type, Color color, Color specularColor, Number constantAttenuation, Number linearAttenuation, Number quadraticAttenuation, Number intensity, Number spotlightCutoff, Number spotlightExponent, bool shadowsEnabled, Matrix4 *textureMatrix,Texture *shadowMapTexture) { +void Renderer::addLight(int lightImportance, const Vector3 &position, const Vector3 &direction, int type, const Color &color, const Color &specularColor, Number constantAttenuation, Number linearAttenuation, Number quadraticAttenuation, Number intensity, Number spotlightCutoff, Number spotlightExponent, bool shadowsEnabled, Matrix4 *textureMatrix,Texture *shadowMapTexture) { numLights++; @@ -263,9 +225,9 @@ void Renderer::addLight(int lightImportance, Vector3 position, Vector3 direction info.position = position; lights.push_back(info); switch(type) { - case 0: //area light - areaLights.push_back(info); - numAreaLights++; + case 0: //point light + pointLights.push_back(info); + numPointLights++; break; case 1: //spot light spotLights.push_back(info); @@ -286,12 +248,6 @@ const Matrix4& Renderer::getCameraMatrix() const { return cameraMatrix; } -void Renderer::setCameraPosition(Vector3 pos) { - cameraPosition = pos; - pos = pos * -1; - this->translate3D(&pos); -} - void Renderer::enableScissor(bool val) { scissorEnabled = val; } @@ -358,21 +314,60 @@ void Renderer::setRendererShaderParams(Shader *shader, ShaderBinding *binding) { for(int i=0; i < shader->expectedParams.size(); i++) { void *dataPtr = getDataPointerForName(shader->expectedParams[i].name); if(dataPtr) { - binding->addLocalParam(shader->expectedParams[i].name, dataPtr); + binding->addParamPointer(shader->expectedParams[i].type, shader->expectedParams[i].name, dataPtr); } } } -void Renderer::pushDataArrayForMesh(Mesh *mesh, int arrayType) { - if(mesh->arrayDirtyMap[arrayType] == true || mesh->renderDataArrays[arrayType] == NULL) { - if(mesh->renderDataArrays[arrayType] != NULL) { - free(mesh->renderDataArrays[arrayType]->arrayPtr); - delete mesh->renderDataArrays[arrayType]; - } - mesh->renderDataArrays[arrayType] = createRenderDataArrayForMesh(mesh, arrayType); - mesh->arrayDirtyMap[arrayType] = false; +void Renderer::applyMaterial(Material *material, ShaderBinding *localOptions,unsigned int shaderIndex, bool forceMaterial) { + + if(overrideMaterial) { + if(!forceMaterial) { + material = overrideMaterial; + } + } + + if(!material->getShader(shaderIndex) || !shadersEnabled) { + setTexture(NULL); + return; + } + + FixedShaderBinding *fBinding; + + Shader *shader = material->getShader(shaderIndex); + if(shader->numPointLights + shader->numSpotLights > 0) { + sortLights(); } - pushRenderDataArray(mesh->renderDataArrays[arrayType]); + + switch(material->getShader(shaderIndex)->getType()) { + case Shader::FIXED_SHADER: + fBinding = (FixedShaderBinding*)material->getShaderBinding(shaderIndex); + setTexture(fBinding->getDiffuseTexture()); + break; + case Shader::MODULE_SHADER: + currentMaterial = material; + if(material->shaderModule == NULL) { + for(int m=0; m < shaderModules.size(); m++) { + PolycodeShaderModule *shaderModule = shaderModules[m]; + if(shaderModule->hasShader(material->getShader(shaderIndex))) { + material->shaderModule = (void*)shaderModule; + } + } + } else { + PolycodeShaderModule *shaderModule = (PolycodeShaderModule*)material->shaderModule; + shaderModule->applyShaderMaterial(this, material, localOptions, shaderIndex); + currentShaderModule = shaderModule; + } + break; + } + + setBlendingMode(material->blendingMode); + setWireframePolygonMode(material->wireframe); +} + +void Renderer::setBackingResolutionScale(Number xScale, Number yScale) { + backingResolutionScaleX = xScale; + backingResolutionScaleY = yScale; } int Renderer::getXRes() { @@ -395,36 +390,16 @@ void Renderer::setClearColor(Color color) { setClearColor(color.r, color.g, color.b, color.a); } -void Renderer::setRenderMode(int newRenderMode) { - renderMode = newRenderMode; -} - void Renderer::setTextureFilteringMode(int mode) { textureFilteringMode = mode; } -int Renderer::getRenderMode() { - return renderMode; -} - -void Renderer::setFOV(Number fov) { - this->fov = fov; - resetViewport(); -} - void Renderer::setViewportSize(int w, int h) { viewportWidth = w; viewportHeight = h; resetViewport(); } -void Renderer::setViewportSizeAndFOV(int w, int h, Number fov) { - this->fov = fov; - viewportWidth = w; - viewportHeight = h; - resetViewport(); -} - Number Renderer::getViewportWidth() { return viewportWidth; } diff --git a/Core/Contents/Source/PolyResource.cpp b/Core/Contents/Source/PolyResource.cpp index 5e036bb82..afd525a5e 100755 --- a/Core/Contents/Source/PolyResource.cpp +++ b/Core/Contents/Source/PolyResource.cpp @@ -26,9 +26,11 @@ using namespace Polycode; +bool Resource::defaultReloadOnFileModify = false; + Resource::Resource(int type) : EventDispatcher() { this->type = type; - reloadOnFileModify = false; + reloadOnFileModify = defaultReloadOnFileModify; resourceFileTime = 0; } diff --git a/Core/Contents/Source/PolyResourceManager.cpp b/Core/Contents/Source/PolyResourceManager.cpp index 814ac9de5..c56f721bd 100755 --- a/Core/Contents/Source/PolyResourceManager.cpp +++ b/Core/Contents/Source/PolyResourceManager.cpp @@ -23,6 +23,7 @@ #include "PolyResourceManager.h" #include "PolyCoreServices.h" #include "PolyCubemap.h" +#include "PolyRenderer.h" #include "PolyMaterialManager.h" #include "PolyModule.h" #include "PolyFontManager.h" @@ -38,38 +39,199 @@ using std::vector; using namespace Polycode; -ResourceManager::ResourceManager() { - PHYSFS_init(NULL); +bool ResourcePool::defaultReloadResourcesOnModify = false; + +ResourcePool::ResourcePool(const String &name, ResourcePool *fallbackPool) { + + this->name = name; + this->fallbackPool = fallbackPool; + dispatchChangeEvents = false; + reloadResourcesOnModify = ResourcePool::defaultReloadResourcesOnModify; ticksSinceCheck = 0; - reloadResourcesOnModify = false; + resourceSubscribers = 0; + deleteOnUnsubscribe = false; } -ResourceManager::~ResourceManager() { - printf("Shutting down resource manager...\n"); - PHYSFS_deinit(); - - for(int i=0; i < resources.size(); i++) { - if(resources[i]->getResourceType() == Resource::RESOURCE_MATERIAL) { - delete resources[i]; - } +ResourcePool::~ResourcePool() { + + CoreServices::getInstance()->getResourceManager()->removeResourcePool(this); + + for(int i=0; i < resources.size(); i++) { + if(resources[i]->getResourceType() == Resource::RESOURCE_MATERIAL) { + delete resources[i]; + resources[i] = NULL; + } + } + + for(int i=0; i < resources.size(); i++) { + if(resources[i]) { + if(resources[i]->getResourceType() == Resource::RESOURCE_SHADER) { + delete resources[i]; + resources[i] = NULL; + } + } + } + + for(int i=0; i < resources.size(); i++) { + if(resources[i]) { + if(resources[i]->getResourceType() == Resource::RESOURCE_PROGRAM) { + delete resources[i]; + resources[i] = NULL; + } + } + } + + for(int i=0; i < resources.size(); i++) { + if(resources[i]) { + if(resources[i]->getResourceType() == Resource::RESOURCE_TEXTURE) { + Services()->getRenderer()->destroyTexture((Texture*)resources[i]); + resources[i] = NULL; + } + } + } + + for(int i=0; i < resources.size(); i++) { + delete resources[i]; + } + + resources.clear(); +} + +String ResourcePool::getName() { + return name; +} + +void ResourcePool::setName(const String &name) { + this->name = name; +} + +void ResourcePool::removeResource(Resource *resource) { + for(int i=0;igetResourceType() == Resource::RESOURCE_SHADER) { - delete resources[i]; - } + } +} + +bool ResourcePool::hasResource(Resource *resource) { + for(int i=0; i < resources.size(); i++) { + if(resources[i] == resource) { + return true; } + } + return false; +} + +void ResourcePool::addResource(Resource *resource) { + resource->addEventListener(this, Event::RESOURCE_CHANGE_EVENT); + resources.push_back(resource); + resource->resourceFileTime = OSBasics::getFileTime(resource->getResourcePath()); + if(dispatchChangeEvents) { + dispatchEvent(new Event(), Event::CHANGE_EVENT); + } +} + +void ResourcePool::setFallbackPool(ResourcePool *pool) { + fallbackPool = pool; +} + +void ResourceManager::addDirResource(const String& dirPath, bool recursive) { + parseTexturesIntoPool(globalPool, dirPath, recursive, ""); + parseProgramsIntoPool(globalPool, dirPath, recursive); + parseShadersIntoPool(globalPool, dirPath, recursive); + parseCubemapsIntoPool(globalPool, dirPath, recursive); + parseMaterialsIntoPool(globalPool, dirPath, recursive); + parseOtherIntoPool(globalPool, dirPath, recursive); +} + +Resource *ResourcePool::getResourceByPath(const String& resourcePath) const { + for(int i =0; i < resources.size(); i++) { + if(resources[i]->getResourcePath() == resourcePath) { + return resources[i]; + } + } + + if(fallbackPool) { + return fallbackPool->getResourceByPath(resourcePath); + } else { + Logger::log("Could not find resource for path [%s] in pool [%s]\n", resourcePath.c_str(), name.c_str()); + return NULL; + } +} - for(int i=0; i < resources.size(); i++) { - if(resources[i]->getResourceType() == Resource::RESOURCE_PROGRAM) { - delete resources[i]; +std::vector ResourcePool::getResources(int resourceType) { + std::vector result; + for(int i =0; i < resources.size(); i++) { + if(resources[i]->getResourceType() == resourceType) { + result.push_back(resources[i]); + } + } + + return result; +} + +Resource *ResourcePool::getResource(int resourceType, const String& resourceName) const { + for(int i =0; i < resources.size(); i++) { + if(resources[i]->getResourceName() == resourceName && resources[i]->getResourceType() == resourceType) { + return resources[i]; + } + } + + if(resourceType == Resource::RESOURCE_TEXTURE && resourceName != "default/default.png") { + Logger::log("Texture [%s] not found in pool [%s], using default\n", resourceName.c_str(), name.c_str()); + return getResource(Resource::RESOURCE_TEXTURE, "default/default.png"); + } + + if(fallbackPool) { + return fallbackPool->getResource(resourceType, resourceName); + } else { + Logger::log("Could not find resource [%s] in pool [%s]\n", resourceName.c_str(), name.c_str()); + + return NULL; + } +} + +void ResourcePool::checkForChangedFiles() { + for(int i=0; i < resources.size(); i++) { + if(resources[i]->reloadOnFileModify == true) { + time_t newFileTime = OSBasics::getFileTime(resources[i]->getResourcePath()); + // printf("%s\n%lld %lld\n", resources[i]->getResourcePath().c_str(), newFileTime, resources[i]->resourceFileTime); + if((newFileTime != resources[i]->resourceFileTime) && newFileTime != 0) { + resources[i]->reloadResource(); + resources[i]->resourceFileTime = newFileTime; } } - - resources.clear(); + } +} + +ResourceManager::ResourceManager() : EventDispatcher() { + PHYSFS_init(NULL); + globalPool = new ResourcePool("Global", NULL); +} + +ResourceManager::~ResourceManager() { + printf("Shutting down resource manager...\n"); + PHYSFS_deinit(); + + for(int i=0; i < pools.size(); i++) { + delete pools[i]; + } + pools.clear(); +} + +void ResourcePool::Update(int elapsed) { + if(!reloadResourcesOnModify) + return; + + ticksSinceCheck += elapsed; + if(ticksSinceCheck > RESOURCE_CHECK_INTERVAL) { + ticksSinceCheck = 0; + checkForChangedFiles(); + } } -void ResourceManager::parseShaders(const String& dirPath, bool recursive) { +void ResourceManager::parseShadersIntoPool(ResourcePool *pool, const String& dirPath, bool recursive) { vector resourceDir; resourceDir = OSBasics::parseFolder(dirPath, false); @@ -77,25 +239,36 @@ void ResourceManager::parseShaders(const String& dirPath, bool recursive) { if(resourceDir[i].type == OSFileEntry::TYPE_FILE) { if(resourceDir[i].extension == "mat") { MaterialManager *materialManager = CoreServices::getInstance()->getMaterialManager(); - std::vector shaders = materialManager->loadShadersFromFile(resourceDir[i].fullPath); + std::vector shaders = materialManager->loadShadersFromFile(pool, resourceDir[i].fullPath); for(int s=0; s < shaders.size(); s++) { - addResource(shaders[s]); - materialManager->addShader(shaders[s]); + pool->addResource(shaders[s]); } } } else { if(recursive) - parseShaders(dirPath+"/"+resourceDir[i].name, true); + parseShadersIntoPool(pool, dirPath+"/"+resourceDir[i].name, true); } } } -void ResourceManager::addShaderModule(PolycodeShaderModule *module) { - shaderModules.push_back(module); +void ResourceManager::parseOtherIntoPool(ResourcePool *pool, const String& dirPath, bool recursive) { + vector resourceDir; + resourceDir = OSBasics::parseFolder(dirPath, false); + for(int i=0; i < resourceDir.size(); i++) { + if(resourceDir[i].type == OSFileEntry::TYPE_FILE) { + if(resourceDir[i].extension == "ttf") { + Logger::log("Registering font: %s\n", resourceDir[i].nameWithoutExtension.c_str()); + CoreServices::getInstance()->getFontManager()->registerFont(resourceDir[i].nameWithoutExtension, resourceDir[i].fullPath); + } + } else { + if(recursive) + parseOtherIntoPool(pool, dirPath+"/"+resourceDir[i].name, true); + } + } } -void ResourceManager::parsePrograms(const String& dirPath, bool recursive) { +void ResourceManager::parseProgramsIntoPool(ResourcePool *pool, const String& dirPath, bool recursive) { vector resourceDir; resourceDir = OSBasics::parseFolder(dirPath, false); for(int i=0; i < resourceDir.size(); i++) { @@ -106,16 +279,16 @@ void ResourceManager::parsePrograms(const String& dirPath, bool recursive) { if(newProgram) { newProgram->setResourceName(resourceDir[i].name); newProgram->setResourcePath(resourceDir[i].fullPath); - addResource(newProgram); + pool->addResource(newProgram); } } else { if(recursive) - parsePrograms(dirPath+"/"+resourceDir[i].name, true); + parseProgramsIntoPool(pool, dirPath+"/"+resourceDir[i].name, true); } } } -void ResourceManager::parseMaterials(const String& dirPath, bool recursive) { +void ResourceManager::parseMaterialsIntoPool(ResourcePool *pool, const String& dirPath, bool recursive) { vector resourceDir; resourceDir = OSBasics::parseFolder(dirPath, false); @@ -123,22 +296,21 @@ void ResourceManager::parseMaterials(const String& dirPath, bool recursive) { if(resourceDir[i].type == OSFileEntry::TYPE_FILE) { if(resourceDir[i].extension == "mat") { MaterialManager *materialManager = CoreServices::getInstance()->getMaterialManager(); - std::vector materials = materialManager->loadMaterialsFromFile(resourceDir[i].fullPath); + std::vector materials = materialManager->loadMaterialsFromFile(pool, resourceDir[i].fullPath); for(int m=0; m < materials.size(); m++) { materials[m]->setResourceName(materials[m]->getName()); - addResource(materials[m]); - materialManager->addMaterial(materials[m]); + pool->addResource(materials[m]); } } } else { if(recursive) - parseMaterials(dirPath+"/"+resourceDir[i].name, true); + parseMaterialsIntoPool(pool, dirPath+"/"+resourceDir[i].name, true); } } } -void ResourceManager::parseCubemaps(const String& dirPath, bool recursive) { +void ResourceManager::parseCubemapsIntoPool(ResourcePool *pool, const String& dirPath, bool recursive) { vector resourceDir; resourceDir = OSBasics::parseFolder(dirPath, false); @@ -149,41 +321,27 @@ void ResourceManager::parseCubemaps(const String& dirPath, bool recursive) { MaterialManager *materialManager = CoreServices::getInstance()->getMaterialManager(); std::vector cubemaps = materialManager->loadCubemapsFromFile(resourceDir[i].fullPath); for(int c=0; c < cubemaps.size(); c++) { - addResource(cubemaps[c]); + pool->addResource(cubemaps[c]); } } } else { if(recursive) - parseCubemaps(dirPath+"/"+resourceDir[i].name, true); + parseCubemapsIntoPool(pool, dirPath+"/"+resourceDir[i].name, true); } } } -bool ResourceManager::hasResource(Resource *resource) { - for(int i=0; i < resources.size(); i++) { - if(resources[i] == resource) { - return true; - } +void ResourceManager::handleEvent(Event *event) { + if(event->getEventCode() == Event::RESOURCE_CHANGE_EVENT) { + dispatchEvent(new Event(), Event::CHANGE_EVENT); } - return false; -} - -void ResourceManager::addResource(Resource *resource) { - resources.push_back(resource); - resource->resourceFileTime = OSBasics::getFileTime(resource->getResourcePath()); } -void ResourceManager::removeResource(Resource *resource) { - for(int i=0;igetMaterialManager(); vector resourceDir; resourceDir = OSBasics::parseFolder(dirPath, false); @@ -200,38 +358,21 @@ void ResourceManager::parseTextures(const String& dirPath, bool recursive, const t->setResourceName(basePath+"/"+resourceDir[i].name); t->setResourcePath(resourceDir[i].fullPath); } - addResource(t); + pool->addResource(t); } } } else { if(recursive) { if(basePath == "") { - parseTextures(dirPath+"/"+resourceDir[i].name, true, resourceDir[i].name); + parseTexturesIntoPool(pool, dirPath+"/"+resourceDir[i].name, true, resourceDir[i].name); } else { - parseTextures(dirPath+"/"+resourceDir[i].name, true, basePath+"/"+resourceDir[i].name); + parseTexturesIntoPool(pool, dirPath+"/"+resourceDir[i].name, true, basePath+"/"+resourceDir[i].name); } } } } } -void ResourceManager::parseOthers(const String& dirPath, bool recursive) { - vector resourceDir; - resourceDir = OSBasics::parseFolder(dirPath, false); - for(int i=0; i < resourceDir.size(); i++) { - if(resourceDir[i].type == OSFileEntry::TYPE_FILE) { - if(resourceDir[i].extension == "ttf") { - Logger::log("Registering font: %s\n", resourceDir[i].nameWithoutExtension.c_str()); - CoreServices::getInstance()->getFontManager()->registerFont(resourceDir[i].nameWithoutExtension, resourceDir[i].fullPath); - } - } else { - if(recursive) - parseOthers(dirPath+"/"+resourceDir[i].name, true); - } - } -} - - void ResourceManager::addArchive(const String& path) { if(PHYSFS_addToSearchPath(path.c_str(), 1) == 0) { Logger::log("Error adding archive to resource manager... %s\n", PHYSFS_getLastError()); @@ -244,74 +385,66 @@ void ResourceManager::removeArchive(const String& path) { PHYSFS_removeFromSearchPath(path.c_str()); } +void ResourceManager::Update(int elapsed) { + globalPool->Update(elapsed); + for(int i=0; i < pools.size(); i++) { + pools[i]->Update(elapsed); + } +} -void ResourceManager::addDirResource(const String& dirPath, bool recursive) { - parseTextures(dirPath, recursive, ""); - parsePrograms(dirPath, recursive); - parseShaders(dirPath, recursive); - parseCubemaps(dirPath, recursive); - parseMaterials(dirPath, recursive); - parseOthers(dirPath, recursive); +void ResourceManager::removeResource(Resource *resource) { + globalPool->removeResource(resource); + for(int i=0; i < pools.size(); i++) { + pools[i]->removeResource(resource); + } } -Resource *ResourceManager::getResourceByPath(const String& resourcePath) const { - Logger::log("requested %s\n", resourcePath.c_str()); - for(int i =0; i < resources.size(); i++) { - if(resources[i]->getResourcePath() == resourcePath) { - return resources[i]; - } - } - Logger::log("return NULL\n"); - return NULL; +void ResourceManager::addResourcePool(ResourcePool *pool) { + pools.push_back(pool); } -Resource *ResourceManager::getResource(int resourceType, const String& resourceName) const { - Logger::log("requested %s\n", resourceName.c_str()); - for(int i =0; i < resources.size(); i++) { - if(resources[i]->getResourceName() == resourceName && resources[i]->getResourceType() == resourceType) { - return resources[i]; - } - } - - if(resourceType == Resource::RESOURCE_TEXTURE && resourceName != "default/default.png") { - Logger::log("Texture not found, using default\n"); - return getResource(Resource::RESOURCE_TEXTURE, "default/default.png"); - } - Logger::log("return NULL\n"); - // need to add some sort of default resource for each type - return NULL; +ResourcePool *ResourceManager::getResourcePoolByName(const String &name) { + printf("request resource pool [%s]\n", name.c_str()); + for(int i=0; i < pools.size(); i++) { + if(pools[i]->getName() == name) { + return pools[i]; + } + } + printf("resource pool not found!\n"); + return NULL; } -void ResourceManager::checkForChangedFiles() { - for(int i=0; i < resources.size(); i++) { - if(resources[i]->reloadOnFileModify == true) { - time_t newFileTime = OSBasics::getFileTime(resources[i]->getResourcePath()); -// printf("%s\n%lld %lld\n", resources[i]->getResourcePath().c_str(), newFileTime, resources[i]->resourceFileTime); - if((newFileTime != resources[i]->resourceFileTime) && newFileTime != 0) { - resources[i]->reloadResource(); - resources[i]->resourceFileTime = newFileTime; - } - } - } +void ResourceManager::removeResourcePool(ResourcePool *pool) { + for(int i=0; i < pools.size(); i++) { + if(pools[i] == pool) { + pools.erase(pools.begin()+i); + return; + } + } } -void ResourceManager::Update(int elapsed) { - if(!reloadResourcesOnModify) - return; - - ticksSinceCheck += elapsed; - if(ticksSinceCheck > RESOURCE_CHECK_INTERVAL) { - ticksSinceCheck = 0; - checkForChangedFiles(); - } +void ResourceManager::subscribeToResourcePool(ResourcePool *pool) { + pool->resourceSubscribers++; +} + +void ResourceManager::unsubscibeFromResourcePool(ResourcePool *pool) { + pool->resourceSubscribers--; + if(pool->deleteOnUnsubscribe && pool->resourceSubscribers < 1) { + delete pool; + } } std::vector ResourceManager::getResources(int resourceType) { std::vector result; - for(int i =0; i < resources.size(); i++) { - if(resources[i]->getResourceType() == resourceType) { - result.push_back(resources[i]); - } + + std::vector subresult = globalPool->getResources(resourceType); + result.insert(result.end(), subresult.begin(), subresult.end()); + + for(int i =0; i < pools.size(); i++) { + subresult = pools[i]->getResources(resourceType); + result.insert(result.end(), subresult.begin(), subresult.end()); } + return result; } + diff --git a/Core/Contents/Source/PolySDLCore.cpp b/Core/Contents/Source/PolySDLCore.cpp index 2f94d5c11..831ef3e8f 100644 --- a/Core/Contents/Source/PolySDLCore.cpp +++ b/Core/Contents/Source/PolySDLCore.cpp @@ -80,7 +80,7 @@ void Core::getScreenInfo(int *width, int *height, int *hz) { if (hz) *hz = 0; } -SDLCore::SDLCore(PolycodeView *view, int _xRes, int _yRes, bool fullScreen, bool vSync, int aaLevel, int anisotropyLevel, int frameRate, int monitorIndex) : Core(_xRes, _yRes, fullScreen, vSync, aaLevel, anisotropyLevel, frameRate, monitorIndex) { +SDLCore::SDLCore(PolycodeView *view, int _xRes, int _yRes, bool fullScreen, bool vSync, int aaLevel, int anisotropyLevel, int frameRate, int monitorIndex, bool retinaSupport) : Core(_xRes, _yRes, fullScreen, vSync, aaLevel, anisotropyLevel, frameRate, monitorIndex) { this->resizableWindow = view->resizable; @@ -133,7 +133,7 @@ SDLCore::SDLCore(PolycodeView *view, int _xRes, int _yRes, bool fullScreen, bool CoreServices::getInstance()->installModule(new GLSLShaderModule()); } -void SDLCore::setVideoMode(int xRes, int yRes, bool fullScreen, bool vSync, int aaLevel, int anisotropyLevel) { +void SDLCore::setVideoMode(int xRes, int yRes, bool fullScreen, bool vSync, int aaLevel, int anisotropyLevel, bool retinaSupport) { this->xRes = xRes; this->yRes = yRes; this->fullScreen = fullScreen; @@ -313,7 +313,7 @@ void SDLCore::Render() { SDL_GL_SwapBuffers(); } -bool SDLCore::Update() { +bool SDLCore::systemUpdate() { if(!running) return false; doSleep(); @@ -499,6 +499,11 @@ vector SDLCore::openFilePicker(vector extensions, boo return r; } +String SDLCore::saveFilePicker(std::vector extensions) { + String r = ""; + return r; +} + void SDLCore::resizeTo(int xRes, int yRes) { renderer->Resize(xRes, yRes); } diff --git a/Core/Contents/Source/PolyScene.cpp b/Core/Contents/Source/PolyScene.cpp index f8d27d9a6..cdda582d8 100755 --- a/Core/Contents/Source/PolyScene.cpp +++ b/Core/Contents/Source/PolyScene.cpp @@ -31,31 +31,31 @@ #include "PolyResource.h" #include "PolyResourceManager.h" #include "PolySceneLight.h" +#include "PolyInputEvent.h" #include "PolySceneMesh.h" +#include "PolyRay.h" #include "PolySceneManager.h" using std::vector; using namespace Polycode; Scene::Scene() : EventDispatcher() { - defaultCamera = new Camera(this); - activeCamera = defaultCamera; - fogEnabled = false; - lightingEnabled = false; - enabled = true; - isSceneVirtual = false; - hasLightmaps = false; - clearColor.setColor(0.13f,0.13f,0.13f,1.0f); - ambientColor.setColor(0.0,0.0,0.0,1.0); - useClearColor = false; - ownsChildren = false; - CoreServices::getInstance()->getSceneManager()->addScene(this); + initScene(SCENE_3D, false); +} + +Scene::Scene(int sceneType, bool virtualScene) : EventDispatcher() { + initScene(sceneType, virtualScene); } -Scene::Scene(bool virtualScene) : EventDispatcher() { +void Scene::initScene(int sceneType, bool virtualScene) { + + core = CoreServices::getInstance()->getCore(); + this->sceneType = sceneType; defaultCamera = new Camera(this); activeCamera = defaultCamera; fogEnabled = false; + fogMode = Renderer::FOG_LINEAR; + overrideMaterial = NULL; lightingEnabled = false; enabled = true; isSceneVirtual = virtualScene; @@ -63,10 +63,50 @@ Scene::Scene(bool virtualScene) : EventDispatcher() { clearColor.setColor(0.13f,0.13f,0.13f,1.0f); ambientColor.setColor(0.0,0.0,0.0,1.0); useClearColor = false; + useClearDepth = true; ownsChildren = false; - if (!isSceneVirtual) { - CoreServices::getInstance()->getSceneManager()->addScene(this); + remapMouse = false; + _doVisibilityChecking = true; + constrainPickingToViewport = true; + renderer = CoreServices::getInstance()->getRenderer(); + rootEntity.setRenderer(renderer); + CoreServices::getInstance()->getSceneManager()->addScene(this); + + setSceneType(sceneType); + + core->addEventListener(this, Core::EVENT_CORE_RESIZE); + core->getInput()->addEventListener(this, InputEvent::EVENT_MOUSEDOWN); + core->getInput()->addEventListener(this, InputEvent::EVENT_MOUSEUP); + core->getInput()->addEventListener(this, InputEvent::EVENT_MOUSEMOVE); + core->getInput()->addEventListener(this, InputEvent::EVENT_MOUSEWHEEL_UP); + core->getInput()->addEventListener(this, InputEvent::EVENT_MOUSEWHEEL_DOWN); +} + +void Scene::setOverrideMaterial(Material *material) { + overrideMaterial = material; +} + +void Scene::setSceneType(int newType) { + sceneType = newType; + switch(sceneType) { + case SCENE_2D: + defaultCamera->setClippingPlanes(-100.0, 100.0); + defaultCamera->setOrthoMode(true); + defaultCamera->setOrthoSize(1.0, 1.0); + break; + case SCENE_2D_TOPLEFT: + defaultCamera->setClippingPlanes(-100.0, 100.0); + defaultCamera->setOrthoMode(true); + defaultCamera->setOrthoSizeMode(Camera::ORTHO_SIZE_VIEWPORT); + defaultCamera->topLeftOrtho = true; + rootEntity.setInverseY(true); + rootEntity.setPositionY(-CoreServices::getInstance()->getCore()->getYRes()); + break; + case SCENE_3D: + defaultCamera->setClippingPlanes(1.0, 1000.0); + break; } + } void Scene::setActiveCamera(Camera *camera) { @@ -94,18 +134,18 @@ bool Scene::isEnabled() { } void Scene::Update() { - for(int i=0; idoUpdates(); - entities[i]->updateEntityMatrix(); - } + rootEntity.updateEntityMatrix(); + rootEntity.doUpdates(); +} + +void Scene::fixedUpdate() { + rootEntity.updateEntityMatrix(); + rootEntity.doFixedUpdates(); } Scene::~Scene() { - if(ownsChildren) { - for(int i=0; i < entities.size(); i++) { - delete entities[i]; - } - } + core->getInput()->removeAllHandlersForListener(this); + core->removeAllHandlersForListener(this); CoreServices::getInstance()->getSceneManager()->removeScene(this); delete defaultCamera; } @@ -115,8 +155,6 @@ void Scene::enableLighting(bool enable) { CoreServices::getInstance()->getRenderer()->enableLighting(enable); } - - void Scene::enableFog(bool enable) { fogEnabled = enable; @@ -131,50 +169,61 @@ void Scene::setFogProperties(int fogMode, Color color, Number density, Number st } -SceneEntity *Scene::getEntityAtScreenPosition(Number x, Number y) { - for(int i =0; i< entities.size(); i++) { - if(entities[i]->testMouseCollision(x,y)) { - return entities[i]; - } - } - return NULL; -} - -void Scene::addEntity(SceneEntity *entity) { - entity->setRenderer(CoreServices::getInstance()->getRenderer()); - entities.push_back(entity); +void Scene::addEntity(Entity *entity) { + rootEntity.addChild(entity); } -void Scene::addChild(SceneEntity *entity) { +void Scene::addChild(Entity *entity) { addEntity(entity); } -void Scene::removeEntity(SceneEntity *entity) { - for(int i=0; i < entities.size(); i++) { - if(entities[i] == entity) { - entities.erase(entities.begin()+i); - return; - } - } +void Scene::removeEntity(Entity *entity) { + rootEntity.removeChild(entity); } Camera *Scene::getDefaultCamera() { return defaultCamera; } -void Scene::Render(Camera *targetCamera) { - +void Scene::doVisibilityChecking(bool val) { + _doVisibilityChecking = val; + if(!_doVisibilityChecking) { + setEntityVisibilityBool(&rootEntity, true); + } +} + +bool Scene::doesVisibilityChecking() { + return _doVisibilityChecking; +} + +void Scene::setEntityVisibilityBool(Entity *entity, bool val) { + entity->rendererVis = val; + for(int i=0; i < entity->getNumChildren(); i++) { + setEntityVisibilityBool(entity->getChildAtIndex(i), val); + } +} + +void Scene::setEntityVisibility(Entity *entity, Camera *camera) { + if(camera->frustumCulling) { + entity->recalculateAABB(); + entity->rendererVis = camera->isAABBInFrustum(entity->getWorldAABB()); + } else { + entity->rendererVis = true; + } + for(int i=0; i < entity->getNumChildren(); i++) { + setEntityVisibility(entity->getChildAtIndex(i), camera); + } +} + +void Scene::Render(Camera *targetCamera) { if(!targetCamera && !activeCamera) return; + + renderer->setOverrideMaterial(overrideMaterial); if(!targetCamera) targetCamera = activeCamera; - - // prepare lights... - for(int i=0; iupdateEntityMatrix(); - } - + //make these the closest Matrix4 textureMatrix; @@ -183,8 +232,12 @@ void Scene::Render(Camera *targetCamera) { targetCamera->rebuildTransformMatrix(); - if(useClearColor) - CoreServices::getInstance()->getRenderer()->setClearColor(clearColor.r,clearColor.g,clearColor.b); + if(useClearColor) { + CoreServices::getInstance()->getRenderer()->setClearColor(clearColor.r,clearColor.g,clearColor.b, clearColor.a); + } + if (useClearColor || useClearDepth) { + CoreServices::getInstance()->getRenderer()->clearScreen(useClearColor, useClearDepth); + } CoreServices::getInstance()->getRenderer()->setAmbientColor(ambientColor.r,ambientColor.g,ambientColor.b); @@ -198,13 +251,12 @@ void Scene::Render(Camera *targetCamera) { Vector3 direction; Vector3 position; matrixPtr = NULL; - direction.x = 0; - direction.y = 0; - direction.z = -1; - + direction.x = 0; + direction.y = 0.0; + direction.z = -1.0; + direction.Normalize(); direction = light->getConcatenatedMatrix().rotateVector(direction); - direction.Normalize(); Texture *shadowMapTexture = NULL; if(light->areShadowsEnabled()) { @@ -216,7 +268,6 @@ void Scene::Render(Camera *targetCamera) { 0.0f, 0.0f, 0.5f, 0.0f, 0.5f, 0.5f, 0.5f, 1.0f ); - light->renderDepthMap(this); textureMatrix = light->getLightViewMatrix() * matTexAdj; matrixPtr = &textureMatrix; @@ -231,70 +282,146 @@ void Scene::Render(Camera *targetCamera) { } CoreServices::getInstance()->getRenderer()->addLight(light->getLightImportance(), position, direction, light->getLightType(), light->lightColor, light->specularLightColor, light->getConstantAttenuation(), light->getLinearAttenuation(), light->getQuadraticAttenuation(), light->getIntensity(), light->getSpotlightCutoff(), light->getSpotlightExponent(), light->areShadowsEnabled(), matrixPtr, shadowMapTexture); } - - if(targetCamera->getOrthoMode()) { - CoreServices::getInstance()->getRenderer()->_setOrthoMode(targetCamera->getOrthoSizeX(), targetCamera->getOrthoSizeY()); - } targetCamera->doCameraTransform(); - targetCamera->buildFrustumPlanes(); - - CoreServices::getInstance()->getRenderer()->enableFog(fogEnabled); + + CoreServices::getInstance()->getRenderer()->enableFog(fogEnabled); if(fogEnabled) { CoreServices::getInstance()->getRenderer()->setFogProperties(fogMode, fogColor, fogDensity, fogStartDepth, fogEndDepth); } else { CoreServices::getInstance()->getRenderer()->setFogProperties(fogMode, fogColor, 0.0, fogStartDepth, fogEndDepth); } - - for(int i=0; igetBBoxRadius() > 0) { - if(targetCamera->isSphereInFrustum((entities[i]->getPosition()), entities[i]->getBBoxRadius())) - entities[i]->transformAndRender(); - } else { - entities[i]->transformAndRender(); - } - } - - if(targetCamera->getOrthoMode()) { - CoreServices::getInstance()->getRenderer()->setPerspectiveMode(); - } - + if(_doVisibilityChecking) { + targetCamera->buildFrustumPlanes(); + setEntityVisibility(&rootEntity, targetCamera); + } + rootEntity.transformAndRender(); } void Scene::RenderDepthOnly(Camera *targetCamera) { CoreServices::getInstance()->getRenderer()->cullFrontFaces(true); -/* - for(int i=0; idoUpdates(); - entities[i]->updateEntityMatrix(); - } -*/ + targetCamera->rebuildTransformMatrix(); - targetCamera->doCameraTransform(); - targetCamera->buildFrustumPlanes(); + targetCamera->doCameraTransform(); CoreServices::getInstance()->getRenderer()->setTexture(NULL); CoreServices::getInstance()->getRenderer()->enableShaders(false); - for(int i=0; icastShadows) { - if(entities[i]->getBBoxRadius() > 0) { - if(targetCamera->isSphereInFrustum((entities[i]->getPosition()), entities[i]->getBBoxRadius())) - entities[i]->transformAndRender(); - } else { - entities[i]->transformAndRender(); - } - } - } + + if(_doVisibilityChecking) { + targetCamera->buildFrustumPlanes(); + setEntityVisibility(&rootEntity, targetCamera); + } + rootEntity.transformAndRender(); + CoreServices::getInstance()->getRenderer()->enableShaders(true); CoreServices::getInstance()->getRenderer()->cullFrontFaces(false); } +Ray Scene::projectRayFromCameraAndViewportCoordinate(Camera *camera, Vector2 coordinate) { + + Polycode::Rectangle viewport = camera->getViewport(); + + if(remapMouse) { + viewport.x = sceneMouseRect.x * renderer->getBackingResolutionScaleX(); + viewport.y = (core->getYRes() - (sceneMouseRect.y + sceneMouseRect.h)) * renderer->getBackingResolutionScaleY(); + } + + Vector3 dir = renderer->projectRayFrom2DCoordinate(coordinate.x * renderer->getBackingResolutionScaleX(), coordinate.y * renderer->getBackingResolutionScaleY(), camera->getConcatenatedMatrix(), camera->getProjectionMatrix(), viewport); + Vector3 pos; + + switch(sceneType) { + case SCENE_2D: + { + + Number orthoSizeX = camera->getOrthoSizeX(); + Number orthoSizeY = camera->getOrthoSizeY(); + + switch(camera->getProjectionMode()) { + case Camera::ORTHO_SIZE_LOCK_HEIGHT: + orthoSizeX = orthoSizeY * (viewport.w/viewport.h); + break; + case Camera::ORTHO_SIZE_LOCK_WIDTH: + orthoSizeY = orthoSizeX * (viewport.h/viewport.w); + break; + case Camera::ORTHO_SIZE_VIEWPORT: + orthoSizeX = camera->getViewport().x; + orthoSizeY = camera->getViewport().y; + break; + } + + Vector2 remappedMouse = Vector2(coordinate.x, coordinate.y); + Vector2 screenSize = Vector2(core->getXRes(), core->getYRes()); + if(remapMouse) { + remappedMouse.x = coordinate.x - sceneMouseRect.x; + remappedMouse.y = coordinate.y - sceneMouseRect.y; + screenSize = Vector2(sceneMouseRect.w, sceneMouseRect.h); + } + + pos = Vector3(((remappedMouse.x/screenSize.x)*orthoSizeX) - (orthoSizeX*0.5), (((screenSize.y-remappedMouse.y)/screenSize.y)*orthoSizeY) - (orthoSizeY*0.5), 0.0); + + pos = camera->getConcatenatedMatrix() * pos; + + } + break; + case SCENE_2D_TOPLEFT: + pos = Vector3(coordinate.x, core->getYRes()-coordinate.y, 0.0); + pos = camera->getConcatenatedMatrix() * pos; + break; + case SCENE_3D: + pos = camera->getConcatenatedMatrix().getPosition(); + break; + } + + return Ray(pos, dir); +} + + +void Scene::handleEvent(Event *event) { + if(event->getDispatcher() == core) { + if(sceneType == SCENE_2D_TOPLEFT) { + rootEntity.setPositionY(-CoreServices::getInstance()->getCore()->getYRes()); + } + } else if(event->getDispatcher() == core->getInput() && rootEntity.processInputEvents) { + InputEvent *inputEvent = (InputEvent*) event; + + if(constrainPickingToViewport) { + Polycode::Rectangle v = activeCamera->getViewport(); + if(remapMouse) { + v.x = sceneMouseRect.x; + v.y = sceneMouseRect.y; + } + if(inputEvent->mousePosition.x < v.x || inputEvent->mousePosition.x > v.x+(v.w / renderer->getBackingResolutionScaleX()) || inputEvent->mousePosition.y < v.y || inputEvent->mousePosition.y > v.y + (v.h/renderer->getBackingResolutionScaleY())) { + return; + } + } + + Ray ray = projectRayFromCameraAndViewportCoordinate(activeCamera, inputEvent->mousePosition); + + switch(inputEvent->getEventCode()) { + case InputEvent::EVENT_MOUSEDOWN: + rootEntity.onMouseDown(ray, inputEvent->mouseButton, inputEvent->timestamp); + break; + case InputEvent::EVENT_MOUSEMOVE: + rootEntity.onMouseMove(ray, inputEvent->timestamp); + break; + case InputEvent::EVENT_MOUSEUP: + rootEntity.onMouseUp(ray, inputEvent->mouseButton, inputEvent->timestamp); + break; + case InputEvent::EVENT_MOUSEWHEEL_UP: + rootEntity.onMouseWheelUp(ray, inputEvent->timestamp); + break; + case InputEvent::EVENT_MOUSEWHEEL_DOWN: + rootEntity.onMouseWheelDown(ray,inputEvent->timestamp); + break; + } + } +} + void Scene::addLight(SceneLight *light) { lights.push_back(light); - addEntity(light); } void Scene::removeLight(SceneLight *light) { @@ -307,13 +434,6 @@ void Scene::removeLight(SceneLight *light) { } } -SceneLight *Scene::getNearestLight(Vector3 pos) { - if(lights.size() > 0) - return lights[0]; - else - return NULL; -} - int Scene::getNumLights() { return lights.size(); } diff --git a/Core/Contents/Source/PolySceneEntity.cpp b/Core/Contents/Source/PolySceneEntity.cpp deleted file mode 100755 index 95fb2889b..000000000 --- a/Core/Contents/Source/PolySceneEntity.cpp +++ /dev/null @@ -1,34 +0,0 @@ -/* - Copyright (C) 2011 by Ivan Safrin - - 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: - - The above copyright notice and this permission notice shall be included in - all copies or substantial portions of the Software. - - 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. -*/ - -#include "PolySceneEntity.h" - -using namespace Polycode; - -SceneEntity::SceneEntity() : Entity() { - castShadows = true; -} - -SceneEntity::~SceneEntity() { - -} - diff --git a/Core/Contents/Source/PolySceneEntityInstance.cpp b/Core/Contents/Source/PolySceneEntityInstance.cpp new file mode 100644 index 000000000..e90ebf7ec --- /dev/null +++ b/Core/Contents/Source/PolySceneEntityInstance.cpp @@ -0,0 +1,748 @@ +/* + Copyright (C) 2011 by Ivan Safrin + + 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: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + 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. +*/ + +#include "PolySceneEntityInstance.h" +#include "PolyLogger.h" +#include "PolyCoreServices.h" +#include "PolyResourceManager.h" +#include "PolyMaterial.h" +#include "PolySceneLight.h" +#include "PolySceneMesh.h" +#include "PolySceneLabel.h" +#include "PolySceneSound.h" +#include "PolyCamera.h" + +using namespace Polycode; + +SceneEntityInstanceResourceEntry::SceneEntityInstanceResourceEntry(SceneEntityInstance *instance) : Resource(Resource::RESOURCE_ENTITY_INSTANCE) { + this->instance = instance; +} + +SceneEntityInstanceResourceEntry::~SceneEntityInstanceResourceEntry() { + +} + +SceneEntityInstance *SceneEntityInstanceResourceEntry::getInstance() { + return instance; +} + +void SceneEntityInstanceResourceEntry::reloadResource() { + instance->reloadEntityInstance(); + Resource::reloadResource(); +} + +SceneEntityInstance *SceneEntityInstance::BlankSceneEntityInstance(Scene *parentScene) { + return new SceneEntityInstance(parentScene); +} + +SceneEntityInstance::SceneEntityInstance(Scene *parentScene, const String& fileName, ResourcePool *loadIntoPool) : Entity(), loadIntoPool(loadIntoPool) { + createNewLayer("default"); + this->parentScene = parentScene; + resourceEntry = new SceneEntityInstanceResourceEntry(this); + topLevelResourcePool = CoreServices::getInstance()->getResourceManager()->getGlobalPool(); + loadFromFile(fileName); + resourceEntry->setResourceName(fileName); + resourceEntry->setResourcePath(fileName); + cloneUsingReload = false; + ownsChildren = true; +} + +SceneEntityInstance::SceneEntityInstance(Scene *parentScene) : Entity() { + createNewLayer("default"); + this->parentScene = parentScene; + cloneUsingReload = true; + ownsChildren = true; + topLevelResourcePool = CoreServices::getInstance()->getResourceManager()->getGlobalPool(); + resourceEntry = new SceneEntityInstanceResourceEntry(this); +} + +SceneEntityInstance::~SceneEntityInstance() { + for(int i=0; i < layers.size(); i++) { + delete layers[i]; + } + + CoreServices::getInstance()->getResourceManager()->removeResource(resourceEntry); + delete resourceEntry; + for(int i=0; i < resourcePools.size(); i++) { + CoreServices::getInstance()->getResourceManager()->unsubscibeFromResourcePool(resourcePools[i]); + } +} + +void SceneEntityInstance::reloadEntityInstance() { + loadFromFile(fileName); +} + +SceneEntityInstanceResourceEntry *SceneEntityInstance::getResourceEntry() { + return resourceEntry; +} + +Entity *SceneEntityInstance::Clone(bool deepClone, bool ignoreEditorOnly) const { + SceneEntityInstance *newEntity; + if(cloneUsingReload) { + newEntity = new SceneEntityInstance(parentScene, fileName, loadIntoPool); + } else { + newEntity = new SceneEntityInstance(parentScene); + } + applyClone(newEntity, deepClone, ignoreEditorOnly); + return newEntity; +} + +void SceneEntityInstance::applyClone(Entity *clone, bool deepClone, bool ignoreEditorOnly) const { + if(cloneUsingReload) { + Entity::applyClone(clone, false, ignoreEditorOnly); + } else { + Entity::applyClone(clone, deepClone, ignoreEditorOnly); + SceneEntityInstance *_clone = (SceneEntityInstance*) clone; + _clone->fileName = fileName; + } +} + + +void SceneEntityInstance::linkResourcePool(ResourcePool *pool) { + for(int i=0; i < resourcePools.size(); i++) { + if(resourcePools[i] == pool) { + return; + } + } + pool->setFallbackPool(topLevelResourcePool); + topLevelResourcePool = pool; + CoreServices::getInstance()->getResourceManager()->subscribeToResourcePool(pool); + resourcePools.push_back(pool); +} + +unsigned int SceneEntityInstance::getNumLinkedResourePools() { + return resourcePools.size(); +} + +ResourcePool *SceneEntityInstance::getLinkedResourcePoolAtIndex(unsigned int index) { + return resourcePools[index]; +} + +void SceneEntityInstance::rebuildResourceLinks() { + for(int i=0; i < resourcePools.size(); i++) { + if(i == 0) { + resourcePools[i]->setFallbackPool(CoreServices::getInstance()->getResourceManager()->getGlobalPool()); + } else { + resourcePools[i]->setFallbackPool(resourcePools[i-1]); + } + } + + if(resourcePools.size() > 0) { + topLevelResourcePool = resourcePools[resourcePools.size()-1]; + } else { + topLevelResourcePool = CoreServices::getInstance()->getResourceManager()->getGlobalPool(); + } +} + +void SceneEntityInstance::unlinkResourcePool(ResourcePool *pool) { + for(int i=0; i < resourcePools.size(); i++) { + if(resourcePools[i] == pool) { + resourcePools.erase(resourcePools.begin() + i); + rebuildResourceLinks(); + CoreServices::getInstance()->getResourceManager()->unsubscibeFromResourcePool(pool); + return; + } + } +} + +void SceneEntityInstance::applySceneMesh(ObjectEntry *entry, SceneMesh *sceneMesh) { + if(!entry) { + return; + } + + if((*entry)["sendBoneMatricesToMaterial"]) { + sceneMesh->sendBoneMatricesToMaterial = (*entry)["sendBoneMatricesToMaterial"]->boolVal; + } + + if((*entry)["alphaTest"]) { + sceneMesh->alphaTest = (*entry)["alphaTest"]->boolVal; + } + + if((*entry)["backfaceCulled"]) { + sceneMesh->backfaceCulled = (*entry)["backfaceCulled"]->boolVal; + } + + ObjectEntry *materialName =(*entry)["material"]; + if(materialName) { + sceneMesh->setMaterialByName(materialName->stringVal, topLevelResourcePool); + if(sceneMesh->getMaterial()) { + ObjectEntry *optionsEntry =(*entry)["shader_options"]; + if(optionsEntry) { + for(int i=0; i < optionsEntry->length; i++) { + ObjectEntry *shaderEntry =(*optionsEntry)[i]; + if(shaderEntry) { + + // parse in texture bindings + ObjectEntry *texturesEntry =(*shaderEntry)["textures"]; + if(texturesEntry) { + for(int j=0; j < texturesEntry->length; j++) { + ObjectEntry *textureEntry =(*texturesEntry)[j]; + if(textureEntry) { + ObjectEntry *nameEntry = (*textureEntry)["name"]; + if(nameEntry && textureEntry->stringVal != "") { + + if(textureEntry->name == "cubemap") { + Cubemap *cubemap; + + cubemap = (Cubemap*)topLevelResourcePool->getResource(Resource::RESOURCE_CUBEMAP, textureEntry->stringVal); + + if(cubemap) { + sceneMesh->getLocalShaderOptions()->addCubemap(nameEntry->stringVal, cubemap); + } + } else { + sceneMesh->getLocalShaderOptions()->addTexture(nameEntry->stringVal, CoreServices::getInstance()->getMaterialManager()->createTextureFromFile(textureEntry->stringVal, Services()->getMaterialManager()->clampDefault, Services()->getMaterialManager()->mipmapsDefault, loadIntoPool)); + } + } + } + } + } + + ObjectEntry *paramsEntry =(*shaderEntry)["params"]; + if(paramsEntry) { + for(int j=0; j < paramsEntry->length; j++) { + ObjectEntry *paramEntry =(*paramsEntry)[j]; + if(paramEntry) { + ObjectEntry *nameEntry = (*paramEntry)["name"]; + ObjectEntry *valueEntry = (*paramEntry)["value"]; + if(nameEntry && valueEntry) { + Shader *materialShader = sceneMesh->getMaterial()->getShader(i); + if(materialShader) { + int type = materialShader->getExpectedParamType(nameEntry->stringVal); + LocalShaderParam *param = sceneMesh->getLocalShaderOptions()->addParam(type, nameEntry->stringVal); + if(param) { + param->setParamValueFromString(type, valueEntry->stringVal); + } + } + } + + } + } + } + } + } + } + } + } + +} + +void SceneEntityInstance::parseObjectIntoCurve(ObjectEntry *entry, BezierCurve *curve) { + curve->clearControlPoints(); + ObjectEntry *controlPoints =(*entry)["controlPoints"]; + if(controlPoints) { + for(int i=0; i < controlPoints->length; i++) { + ObjectEntry *controlPoint = ((*controlPoints))[i]; + if(controlPoint) { + Vector3 vpt1; + Vector3 vpt2; + Vector3 vpt3; + + ObjectEntry *pt1 = ((*controlPoint))["pt1"]; + if(pt1) { + vpt1.x = ((*pt1))["x"]->NumberVal; + vpt1.y = ((*pt1))["y"]->NumberVal; + vpt1.z = ((*pt1))["z"]->NumberVal; + } + + ObjectEntry *pt2 = ((*controlPoint))["pt2"]; + if(pt2) { + vpt2.x = ((*pt2))["x"]->NumberVal; + vpt2.y = ((*pt2))["y"]->NumberVal; + vpt2.z = ((*pt2))["z"]->NumberVal; + + } + + ObjectEntry *pt3 = ((*controlPoint))["pt3"]; + if(pt3) { + vpt3.x = ((*pt3))["x"]->NumberVal; + vpt3.y = ((*pt3))["y"]->NumberVal; + vpt3.z = ((*pt3))["z"]->NumberVal; + } + + curve->addControlPoint(vpt1.x, vpt1.y, vpt1.z, vpt2.x, vpt2.y, vpt2.z, vpt3.x, vpt3.y, vpt3.z); + } + } + } + +} + +Entity *SceneEntityInstance::loadObjectEntryIntoEntity(ObjectEntry *entry, Entity *targetEntity, int entityFileVersion) { + + Entity *entity = NULL; + + ObjectEntry *entityType = (*entry)["type"]; + if(entityType) { + if(entityType->stringVal == "SceneEntityInstance") { + ObjectEntry *instanceEntry = (*entry)["SceneEntityInstance"]; + String filePath = (*instanceEntry)["filePath"]->stringVal; + SceneEntityInstance *instance = new SceneEntityInstance(parentScene, filePath, loadIntoPool); + entity = instance; + } else if(entityType->stringVal == "SceneCurve") { + ObjectEntry *curveEntry = (*entry)["SceneCurve"]; + + SceneCurve *curve = new SceneCurve(); + + if(curveEntry) { + curve->renderCurve = (*curveEntry)["render"]->boolVal; + curve->curveResolution = (*curveEntry)["resolution"]->intVal; + parseObjectIntoCurve((*curveEntry)["curve"], curve->getCurve()); + } + + entity = curve; + + } else if(entityType->stringVal == "SceneSprite") { + + ObjectEntry *spriteEntry = (*entry)["SceneSprite"]; + String spriteSetName = (*spriteEntry)["sprite_set"]->stringVal; + + SpriteSet *spriteSet = (SpriteSet*)CoreServices::getInstance()->getResourceManager()->getResourcePoolByName(spriteSetName); + + if(spriteSet) { + SceneSprite *sprite = new SceneSprite(spriteSet); + + String spriteName = (*spriteEntry)["sprite"]->stringVal; + sprite->setSpriteByName(spriteName); + + + String stateName = (*spriteEntry)["state"]->stringVal; + + if(sprite->getCurrentSprite()) { + SpriteState *state = sprite->getCurrentSprite()->getStateByName(stateName); + if(state) { + sprite->setSpriteState(state, 0, false); + } + + ObjectEntry *randomFrameEntry = (*spriteEntry)["random_frame"]; + if(randomFrameEntry) { + sprite->setStartOnRandomFrame(randomFrameEntry->boolVal); + } + + } + + entity = sprite; + applySceneMesh((*entry)["SceneMesh"], sprite); + } + + } else if(entityType->stringVal == "SceneLabel") { + ObjectEntry *labelEntry = (*entry)["SceneLabel"]; + + String text = (*labelEntry)["text"]->stringVal; + String font = (*labelEntry)["font"]->stringVal; + int size = (*labelEntry)["size"]->intVal; + Number actualHeight = (*labelEntry)["actualHeight"]->NumberVal; + int aaMode = (*labelEntry)["aaMode"]->intVal; + + SceneLabel *label = new SceneLabel(text, size, font, aaMode, actualHeight); + label->setAnchorPoint(0.0, 0.0, 0.0); + label->snapToPixels = false; + label->positionAtBaseline = false; + applySceneMesh((*entry)["SceneMesh"], label); + if(label->getLocalShaderOptions()) { + label->getLocalShaderOptions()->clearTexture("diffuse"); + label->getLocalShaderOptions()->addTexture("diffuse", label->getTexture()); + } + + entity = label; + } else if(entityType->stringVal == "SceneParticleEmitter") { + + ObjectEntry *emitterEntry = (*entry)["SceneParticleEmitter"]; + SceneParticleEmitter *emitter = new SceneParticleEmitter(1, 1, 1); + + emitter->setParticleType((*emitterEntry)["type"]->intVal); + emitter->setParticleSpeed((*emitterEntry)["speed"]->NumberVal); + emitter->setParticleCount((*emitterEntry)["count"]->intVal); + emitter->setParticleLifetime((*emitterEntry)["lifetime"]->NumberVal); + emitter->setParticleSize((*emitterEntry)["size"]->NumberVal); + emitter->setParticlesInWorldSpace((*emitterEntry)["world"]->boolVal); + emitter->setLoopParticles((*emitterEntry)["loop"]->boolVal); + + emitter->setParticleRotationSpeed(Vector3((*emitterEntry)["rX"]->NumberVal, (*emitterEntry)["rY"]->NumberVal, (*emitterEntry)["rZ"]->NumberVal)); + emitter->setGravity(Vector3((*emitterEntry)["gX"]->NumberVal, (*emitterEntry)["gY"]->NumberVal, (*emitterEntry)["gZ"]->NumberVal)); + emitter->setParticleDirection(Vector3((*emitterEntry)["dirX"]->NumberVal, (*emitterEntry)["dirY"]->NumberVal, (*emitterEntry)["dirZ"]->NumberVal)); + emitter->setEmitterSize(Vector3((*emitterEntry)["eX"]->NumberVal, (*emitterEntry)["eY"]->NumberVal, (*emitterEntry)["eZ"]->NumberVal)); + emitter->setDirectionDeviation(Vector3((*emitterEntry)["devX"]->NumberVal, (*emitterEntry)["devY"]->NumberVal, (*emitterEntry)["devZ"]->NumberVal)); + + emitter->setPerlinEnabled((*emitterEntry)["perlin"]->boolVal); + if(emitter->getPerlinEnabled()) { + emitter->setPerlinValue(Vector3((*emitterEntry)["pX"]->NumberVal, (*emitterEntry)["pY"]->NumberVal, (*emitterEntry)["pZ"]->NumberVal)); + } + + emitter->useColorCurves = (*emitterEntry)["useColorCurves"]->boolVal; + emitter->useScaleCurve = (*emitterEntry)["useScaleCurve"]->boolVal; + + parseObjectIntoCurve((*emitterEntry)["colorCurveR"], &emitter->colorCurveR); + parseObjectIntoCurve((*emitterEntry)["colorCurveG"], &emitter->colorCurveG); + parseObjectIntoCurve((*emitterEntry)["colorCurveB"], &emitter->colorCurveB); + parseObjectIntoCurve((*emitterEntry)["colorCurveA"], &emitter->colorCurveA); + parseObjectIntoCurve((*emitterEntry)["scaleCurve"], &emitter->scaleCurve); + + applySceneMesh((*entry)["SceneMesh"], emitter); + entity = emitter; + + } else if(entityType->stringVal == "SceneLight") { + + ObjectEntry *lightEntry = (*entry)["SceneLight"]; + if(lightEntry) { + int lightType = (*lightEntry)["type"]->intVal; + SceneLight *newLight = new SceneLight(lightType, parentScene, 0); + + newLight->setIntensity((*lightEntry)["intensity"]->NumberVal); + + ObjectEntry *importanceEntry = (*lightEntry)["importance"]; + if(importanceEntry) { + newLight->setLightImportance(importanceEntry->intVal); + } + + newLight->lightColor.setColor((*lightEntry)["cR"]->NumberVal, (*lightEntry)["cG"]->NumberVal, (*lightEntry)["cB"]->NumberVal, (*lightEntry)["cA"]->NumberVal); + newLight->specularLightColor.setColor((*lightEntry)["scR"]->NumberVal, (*lightEntry)["scG"]->NumberVal, (*lightEntry)["scB"]->NumberVal, (*lightEntry)["scA"]->NumberVal); + + newLight->setAttenuation((*lightEntry)["cAtt"]->NumberVal, (*lightEntry)["lAtt"]->NumberVal, (*lightEntry)["qAtt"]->NumberVal); + + if(newLight->getType() == SceneLight::SPOT_LIGHT) { + newLight->setSpotlightProperties((*lightEntry)["spotCutoff"]->NumberVal, (*lightEntry)["spotExponent"]->NumberVal); + + if((*lightEntry)["shadows"]->boolVal) { + newLight->enableShadows(true, (*lightEntry)["shadowmapRes"]->intVal); + newLight->setShadowMapFOV((*lightEntry)["shadowmapFOV"]->NumberVal); + } + } + + parentScene->addLight(newLight); + entity = newLight; + } + + } else if(entityType->stringVal == "ScenePrimitive") { + ObjectEntry *scenePrimitiveEntry = (*entry)["ScenePrimitive"]; + int pType = (*scenePrimitiveEntry)["type"]->intVal; + Number p1 = (*scenePrimitiveEntry)["p1"]->NumberVal; + Number p2 = (*scenePrimitiveEntry)["p2"]->NumberVal; + Number p3 = (*scenePrimitiveEntry)["p3"]->NumberVal; + Number p4 = (*scenePrimitiveEntry)["p4"]->NumberVal; + Number p5 = (*scenePrimitiveEntry)["p5"]->NumberVal; + + ScenePrimitive *primitive = new ScenePrimitive(pType, p1, p2, p3, p4, p5); + entity = primitive; + applySceneMesh((*entry)["SceneMesh"], primitive); + } else if(entityType->stringVal == "SceneMesh") { + ObjectEntry *meshEntry = (*entry)["SceneMesh"]; + if(meshEntry) { + ObjectEntry *fileName = (*meshEntry)["file"]; + if(fileName) { + SceneMesh *newMesh = new SceneMesh(fileName->stringVal); + applySceneMesh(meshEntry, newMesh); + newMesh->cacheToVertexBuffer(true); + entity = newMesh; + } + } + } else if(entityType->stringVal == "SceneSound") { + ObjectEntry *soundEntry = (*entry)["SceneSound"]; + + String filePath = (*soundEntry)["filePath"]->stringVal; + Number refDistance = (*soundEntry)["refDistance"]->NumberVal; + Number maxDistance = (*soundEntry)["maxDistance"]->NumberVal; + Number volume = (*soundEntry)["volume"]->NumberVal; + Number pitch = (*soundEntry)["pitch"]->NumberVal; + + SceneSound *sound = new SceneSound(filePath, refDistance, maxDistance); + sound->getSound()->setVolume(volume); + sound->getSound()->setPitch(pitch); + + if((*soundEntry)["loopOnLoad"]) { + bool loopOnLoad = (*soundEntry)["loopOnLoad"]->boolVal; + sound->setLoopOnLoad(loopOnLoad); + if(loopOnLoad) { + sound->getSound()->Play(true); + } + } + + + entity = sound; + } else if(entityType->stringVal == "Camera") { + ObjectEntry *cameraEntry = (*entry)["Camera"]; + + Camera *camera = new Camera(parentScene); + + camera->setExposureLevel((*cameraEntry)["exposure"]->NumberVal); + camera->setClippingPlanes((*cameraEntry)["nearClip"]->NumberVal, (*cameraEntry)["farClip"]->NumberVal); + camera->setOrthoMode((*cameraEntry)["ortho"]->boolVal); + + if(camera->getOrthoMode()) { + camera->setOrthoSizeMode((*cameraEntry)["sizeMode"]->intVal); + camera->setOrthoSize((*cameraEntry)["orthoWidth"]->NumberVal, (*cameraEntry)["orthoHeight"]->NumberVal); + } else { + camera->setFOV((*cameraEntry)["fov"]->NumberVal); + } + + entity = camera; + } + + + } + + if(!entity) { + if(targetEntity) { + entity = targetEntity; + } else { + entity = new Entity(); + } + } + + entity->ownsChildren = true; + + Vector3 bBox; + entry->readNumber("bbX", &bBox.x); + entry->readNumber("bbY", &bBox.y); + entry->readNumber("bbZ", &bBox.z); + entity->setLocalBoundingBox(bBox); + + entity->color.r = (*entry)["cR"]->NumberVal; + entity->color.g = (*entry)["cG"]->NumberVal; + entity->color.b = (*entry)["cB"]->NumberVal; + entity->color.a = (*entry)["cA"]->NumberVal; + + + if(!targetEntity) { + entity->blendingMode = (*entry)["blendMode"]->intVal; + + entity->setScale((*entry)["sX"]->NumberVal, (*entry)["sY"]->NumberVal, (*entry)["sZ"]->NumberVal); + entity->setPosition((*entry)["pX"]->NumberVal, (*entry)["pY"]->NumberVal, (*entry)["pZ"]->NumberVal); + + if(entityFileVersion > 1) { + entity->setRotationQuat((*entry)["rW"]->NumberVal, (*entry)["rX"]->NumberVal, (*entry)["rY"]->NumberVal, (*entry)["rZ"]->NumberVal); + } else { + entity->setRotationEuler(Vector3((*entry)["rX"]->NumberVal, (*entry)["rY"]->NumberVal, (*entry)["rZ"]->NumberVal)); + } + } + + if((*entry)["id"]->stringVal != "") { + entity->id = (*entry)["id"]->stringVal; + } + + if((*entry)["layerID"]) { + entity->layerID = (unsigned int)((*entry)["layerID"]->intVal); + } + + String tagString = (*entry)["tags"]->stringVal; + + if(tagString != "") { + std::vector tags = tagString.split(","); + for(int i=0; i < tags.size(); i++) { + entity->addTag(tags[i]); + } + } + + ObjectEntry *props = (*entry)["props"]; + if(props) { + for(int i=0; i < props->length; i++) { + ObjectEntry *prop = ((*props))[i]; + if(prop) { + entity->setEntityProp((*prop)["name"]->stringVal, (*prop)["value"]->stringVal); + } + } + } + + ObjectEntry *children = (*entry)["children"]; + + if(children) { + for(int i=0; i < children->length; i++) { + ObjectEntry *childEntry = ((*children))[i]; + ScreenEntity *childEntity = loadObjectEntryIntoEntity(childEntry, NULL, entityFileVersion); + entity->addChild(childEntity); + } + } + + return entity; +} + +String SceneEntityInstance::getFileName() const { + return fileName; +} + +ResourcePool *SceneEntityInstance::getTopLevelResourcePool() { + return topLevelResourcePool; +} + +void SceneEntityInstance::clearInstance() { + + resourcePools.clear(); + topLevelResourcePool = CoreServices::getInstance()->getResourceManager()->getGlobalPool(); + for(int i=0; i < children.size(); i++) { + children[i]->setOwnsChildrenRecursive(true); + delete children[i]; + } + children.clear(); +} + +bool SceneEntityInstance::hasLayerID(unsigned char layerID) const { + for(int i=0; i < layers.size(); i++) { + if(layers[i]->layerID == layerID) { + return true; + } + } + return false; +} + +SceneEntityInstanceLayer::SceneEntityInstanceLayer(SceneEntityInstance *instance, String name) { + this->instance = instance; + this->name = name; + visible = true; +} + +void SceneEntityInstanceLayer::setLayerVisibility(bool val) { + visible = val; + + std::vector entities = instance->getEntitiesByLayerID(layerID, true); + for(int i=0; i < entities.size(); i++) { + entities[i]->visible = val; + } +} + +SceneEntityInstanceLayer *SceneEntityInstance::createNewLayer(String name) { + SceneEntityInstanceLayer *newLayer = new SceneEntityInstanceLayer(this, name); + + unsigned char layerID; + for(layerID=0; layerID <= 255; layerID++) { + if(!hasLayerID(layerID)) { + break; + } + } + newLayer->layerID = layerID; + layers.push_back(newLayer); + return newLayer; +} + +unsigned int SceneEntityInstance::getNumLayers() const { + return layers.size(); +} + +void SceneEntityInstance::removeLayer(SceneEntityInstanceLayer *layer) { + for(int i=0; i < layers.size(); i++) { + if(layers[i] == layer) { + delete layers[i]; + layers.erase(layers.begin()+i); + } + } +} + +SceneEntityInstanceLayer *SceneEntityInstance::getLayerAtIndex(unsigned int index) const { + if(index < layers.size()) { + return layers[index]; + } else { + return NULL; + } +} + +bool SceneEntityInstance::loadFromFile(const String& fileName) { + + clearInstance(); + + resourceEntry->resourceFileTime = OSBasics::getFileTime(fileName); + + this->ownsChildren = true; + this->fileName = fileName; + Object loadObject; + if(!loadObject.loadFromBinary(fileName)) { + if(!loadObject.loadFromXML(fileName)) { + Logger::log("Error loading entity instance.\n"); + } + } + + int entityFileVersion = 1; + + ObjectEntry *versionObject = loadObject.root["version"]; + if(versionObject) { + entityFileVersion = versionObject->intVal; + } + + ObjectEntry *settings = loadObject.root["settings"]; + if(settings) { + + ObjectEntry *layersEntry = (*settings)["layers"]; + if(layersEntry) { + for(int i=0; i < layersEntry->length; i++) { + ObjectEntry *layer = (*layersEntry)[i]; + if(layer) { + ObjectEntry *name = (*layer)["name"]; + ObjectEntry *layerID = (*layer)["id"]; + ObjectEntry *visible = (*layer)["visible"]; + if(name && layerID && visible) { + SceneEntityInstanceLayer *newLayer = new SceneEntityInstanceLayer(this, name->stringVal); + newLayer->visible = visible->boolVal; + newLayer->layerID = (unsigned char) layerID->intVal; + layers.push_back(newLayer); + } + } + } + } + ObjectEntry *resPools = (*settings)["linkedResourcePools"]; + if(resPools) { + for(int i=0; i < resPools->length; i++) { + ObjectEntry *resPool = (*resPools)[i]; + if(resPool) { + ObjectEntry *path = (*resPool)["path"]; + if(path) { + ResourcePool *newPool = CoreServices::getInstance()->getResourceManager()->getResourcePoolByName(path->stringVal); + + if(!newPool) { + + String extension = path->stringVal.substr(path->stringVal.find_last_of(".")+1, path->stringVal.length()); + + if(extension == "mat") { + newPool = new ResourcePool(path->stringVal, CoreServices::getInstance()->getResourceManager()->getGlobalPool()); + newPool->deleteOnUnsubscribe = true; + CoreServices::getInstance()->getMaterialManager()->loadMaterialLibraryIntoPool(newPool, path->stringVal); + } else if( extension == "sprites") { + SpriteSet *spriteSet = new SpriteSet(path->stringVal, CoreServices::getInstance()->getResourceManager()->getGlobalPool()); + spriteSet->deleteOnUnsubscribe = true; + newPool = spriteSet; + + } + + CoreServices::getInstance()->getResourceManager()->addResourcePool(newPool); + } + + linkResourcePool(newPool); + } + } + } + } + } + + ObjectEntry *root = loadObject.root["root"]; + if(root) { + loadObjectEntryIntoEntity(root, this, entityFileVersion); + } + + for(int i=0; i < layers.size(); i++ ) { + SceneEntityInstanceLayer *layer = layers[i]; + if(layer->layerID != 0) { + if(layer->visible == false) { + std::vector layerEntities = getEntitiesByLayerID(layer->layerID, true); + for(int j=0; j < layerEntities.size(); j++) { + layerEntities[j]->visible = false; + } + } + } + } + + return true; +} diff --git a/Core/Contents/Source/PolySceneImage.cpp b/Core/Contents/Source/PolySceneImage.cpp new file mode 100644 index 000000000..852ab99d9 --- /dev/null +++ b/Core/Contents/Source/PolySceneImage.cpp @@ -0,0 +1,131 @@ +/* + Copyright (C) 2011 by Ivan Safrin + + 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: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + 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. +*/ + +#include "PolySceneImage.h" +#include "PolyMesh.h" +#include "PolyTexture.h" + +using namespace Polycode; + +SceneImage* SceneImage::SceneImageWithImage(Image *image) { + return new SceneImage(image); +} + +SceneImage* SceneImage::SceneImageWithTexture(Texture *texture) { + return new SceneImage(texture); +} + +SceneImage::SceneImage(const String& fileName) : ScenePrimitive(ScenePrimitive::TYPE_VPLANE, 1, 1) { + loadTexture(fileName); + + imageWidth = texture->getWidth(); + imageHeight = texture->getHeight(); + + setWidth(texture->getWidth()); + setHeight(texture->getHeight()); + setPrimitiveOptions(ScenePrimitive::TYPE_VPLANE, getWidth(), getHeight()); +} + +SceneImage::SceneImage(Image *image) : ScenePrimitive(ScenePrimitive::TYPE_VPLANE, 1, 1) { + loadTextureFromImage(image); + + imageWidth = texture->getWidth(); + imageHeight = texture->getHeight(); + + setWidth(texture->getWidth()); + setHeight(texture->getHeight()); + setPrimitiveOptions(ScenePrimitive::TYPE_VPLANE, getWidth(), getHeight()); +} + +SceneImage::SceneImage(Texture *texture) : ScenePrimitive(ScenePrimitive::TYPE_VPLANE, 1, 1) { + setTexture(texture); + + imageWidth = texture->getWidth(); + imageHeight = texture->getHeight(); + + setWidth(texture->getWidth()); + setHeight(texture->getHeight()); + setPrimitiveOptions(ScenePrimitive::TYPE_VPLANE, getWidth(), getHeight()); +} + +SceneImage::~SceneImage() { + +} + +Entity *SceneImage::Clone(bool deepClone, bool ignoreEditorOnly) const { + SceneImage *newImage = new SceneImage(getTexture()->getResourcePath()); + applyClone(newImage, deepClone, ignoreEditorOnly); + return newImage; +} + +void SceneImage::applyClone(Entity *clone, bool deepClone, bool ignoreEditorOnly) const { + ScenePrimitive::applyClone(clone, deepClone, ignoreEditorOnly); +} + +void SceneImage::setImageCoordinates(Number x, Number y, Number width, Number height, Number realWidth, Number realHeight) { + Number pixelSizeX = 1 / imageWidth; + Number pixelSizeY = 1 / imageHeight; + + if (realWidth == -1) + realWidth = width; + if (realHeight == -1) + realHeight = height; + + setWidth(realWidth); + setHeight(realHeight); + + Number whalf = realWidth / 2.0f; + Number hhalf = realHeight / 2.0f; + + Number xFloat = x * pixelSizeX; + Number yFloat = y * pixelSizeY; + Number wFloat = width * pixelSizeX; + Number hFloat = height * pixelSizeY; + + mesh->vertexPositionArray.data.clear(); + mesh->vertexTexCoordArray.data.clear(); + + mesh->setMeshType(Mesh::QUAD_MESH); + + mesh->addVertex(0 - whalf, 0 - hhalf, 0); + mesh->addTexCoord(xFloat, (1.0 - yFloat) - hFloat); + + mesh->addVertex(realWidth - whalf, 0 - hhalf, 0); + mesh->addTexCoord(xFloat + wFloat, (1.0 - yFloat) - hFloat); + + mesh->addVertex(realWidth - whalf, realHeight - hhalf, 0); + mesh->addTexCoord(xFloat + wFloat, 1.0 - yFloat); + + mesh->addVertex(0 - whalf, realHeight - hhalf, 0); + mesh->addTexCoord(xFloat, 1.0 - yFloat); + + rebuildTransformMatrix(); + matrixDirty = true; +} + +Number SceneImage::getImageWidth() const { + return imageWidth; +} + +Number SceneImage::getImageHeight() const { + return imageHeight; +} \ No newline at end of file diff --git a/Core/Contents/Source/PolySceneLabel.cpp b/Core/Contents/Source/PolySceneLabel.cpp index 030cfcab2..99914c3ee 100755 --- a/Core/Contents/Source/PolySceneLabel.cpp +++ b/Core/Contents/Source/PolySceneLabel.cpp @@ -25,54 +25,114 @@ #include "PolyFontManager.h" #include "PolyLabel.h" #include "PolyMesh.h" -#include "PolyPolygon.h" #include "PolyRenderer.h" #include "PolyMaterialManager.h" using namespace Polycode; -SceneLabel::SceneLabel(const String& fontName, const String& text, int size, Number scale, int amode, bool premultiplyAlpha) : ScenePrimitive(ScenePrimitive::TYPE_PLANE, 1, 1) { - label = new Label(CoreServices::getInstance()->getFontManager()->getFontByName(fontName), text, size, amode, premultiplyAlpha); - this->scale = scale; - updateFromLabel(); +Vector3 SceneLabel::defaultAnchor = Vector3(); +bool SceneLabel::defaultPositionAtBaseline = false; +bool SceneLabel::defaultSnapToPixels = false; +bool SceneLabel::createMipmapsForLabels = true; + +SceneLabel::SceneLabel(const String& text, int size, const String& fontName, int amode, Number actualHeight, bool premultiplyAlpha, const Color &backgroundColor, const Color &foregroundColor) : ScenePrimitive(ScenePrimitive::TYPE_VPLANE, 1, 1){ + + label = new Label(CoreServices::getInstance()->getFontManager()->getFontByName(fontName), text, size * CoreServices::getInstance()->getRenderer()->getBackingResolutionScaleX(), amode, premultiplyAlpha, backgroundColor, foregroundColor); + + positionAtBaseline = SceneLabel::defaultPositionAtBaseline; + setAnchorPoint(SceneLabel::defaultAnchor); + snapToPixels = SceneLabel::defaultSnapToPixels; + setLabelActualHeight(actualHeight); +} + +Entity *SceneLabel::Clone(bool deepClone, bool ignoreEditorOnly) const { + SceneLabel *newLabel = new SceneLabel(label->getText(), label->getSize(), label->getFont()->getFontName(), label->getAntialiasMode(), actualHeight, label->getPremultiplyAlpha(), label->getBackgroundColor(), label->getForegroundColor()); + applyClone(newLabel, deepClone, ignoreEditorOnly); + return newLabel; +} + +void SceneLabel::applyClone(Entity *clone, bool deepClone, bool ignoreEditorOnly) const { + + SceneLabel* cloneLabel = (SceneLabel*) clone; + + + cloneLabel->getLabel()->setSize(label->getSize()); + cloneLabel->getLabel()->setAntialiasMode(label->getAntialiasMode()); + cloneLabel->getLabel()->setFont(label->getFont()); + cloneLabel->getLabel()->setPremultiplyAlpha(label->getPremultiplyAlpha()); + cloneLabel->setLabelActualHeight(actualHeight); + cloneLabel->getLabel()->setBackgroundColor(label->getBackgroundColor()); + cloneLabel->getLabel()->setForegroundColor(label->getForegroundColor()); + cloneLabel->positionAtBaseline = positionAtBaseline; + cloneLabel->setText(label->getText()); + + ScenePrimitive::applyClone(clone, deepClone, ignoreEditorOnly); } + SceneLabel::~SceneLabel() { + delete label; } Label *SceneLabel::getLabel() { return label; } +String SceneLabel::getText() { + return label->getText(); +} + +void SceneLabel::setLabelActualHeight(Number actualHeight) { + this->actualHeight = actualHeight; + + if(actualHeight > 0.0) { + labelScale = actualHeight/((Number)label->getSize()) * CoreServices::getInstance()->getRenderer()->getBackingResolutionScaleX(); + } else { + labelScale = 1.0; + } + updateFromLabel(); +} + +Number SceneLabel::getLabelActualHeight() { + return actualHeight; +} + void SceneLabel::updateFromLabel() { - MaterialManager *materialManager = CoreServices::getInstance()->getMaterialManager(); - if(texture) - materialManager->deleteTexture(texture); + MaterialManager *materialManager = CoreServices::getInstance()->getMaterialManager(); - texture = materialManager->createTextureFromImage(label, materialManager->clampDefault, materialManager->mipmapsDefault); + Services()->getRenderer()->destroyTexture(texture); + + if(SceneLabel::createMipmapsForLabels) { + texture = materialManager->createTextureFromImage(label, materialManager->clampDefault, materialManager->mipmapsDefault); + } else { + texture = materialManager->createTextureFromImage(label, materialManager->clampDefault, false); + } if(material) { localShaderOptions->clearTexture("diffuse"); localShaderOptions->addTexture("diffuse", texture); } - delete mesh; - mesh = new Mesh(Mesh::QUAD_MESH); - mesh->createVPlane(label->getWidth()*scale,label->getHeight()*scale); - - bBox.x = label->getWidth()*scale; - bBox.y = label->getHeight()*scale; - bBox.z = 0; - + + setPrimitiveOptions(type, label->getWidth()*labelScale/CoreServices::getInstance()->getRenderer()->getBackingResolutionScaleX(),label->getHeight()*labelScale/CoreServices::getInstance()->getRenderer()->getBackingResolutionScaleX()); + setLocalBoundingBox(label->getWidth()*labelScale / CoreServices::getInstance()->getRenderer()->getBackingResolutionScaleX(), label->getHeight()*labelScale/ CoreServices::getInstance()->getRenderer()->getBackingResolutionScaleX(), 0.001); + if(useVertexBuffer) CoreServices::getInstance()->getRenderer()->createVertexBufferForMesh(mesh); - // TODO: resize it here - - bBoxRadius = label->getWidth()*scale; +} + +void SceneLabel::Render() { + if(positionAtBaseline) { + CoreServices::getInstance()->getRenderer()->translate2D(0.0, (((Number)label->getSize()*labelScale) * -1.0 / CoreServices::getInstance()->getRenderer()->getBackingResolutionScaleY()) + (((Number)label->getBaselineAdjust())*labelScale/CoreServices::getInstance()->getRenderer()->getBackingResolutionScaleY())); + } + ScenePrimitive::Render(); +} +int SceneLabel::getTextWidthForString(String text) { + return label->getTextWidthForString(text) / CoreServices::getInstance()->getRenderer()->getBackingResolutionScaleX(); } void SceneLabel::setText(const String& newText) { diff --git a/Core/Contents/Source/PolySceneLight.cpp b/Core/Contents/Source/PolySceneLight.cpp index 0255528b6..d064e82d9 100755 --- a/Core/Contents/Source/PolySceneLight.cpp +++ b/Core/Contents/Source/PolySceneLight.cpp @@ -26,11 +26,12 @@ #include "PolyCoreServices.h" #include "PolyMesh.h" #include "PolyRenderer.h" +#include "PolyScenePrimitive.h" #include "PolyScene.h" using namespace Polycode; -SceneLight::SceneLight(int type, Scene *parentScene, Number intensity, Number constantAttenuation, Number linearAttenuation, Number quadraticAttenuation) : SceneEntity() { +SceneLight::SceneLight(int type, Scene *parentScene, Number intensity, Number constantAttenuation, Number linearAttenuation, Number quadraticAttenuation) : Entity() { this->type = type; this->intensity = intensity; this->constantAttenuation = constantAttenuation; @@ -40,11 +41,11 @@ SceneLight::SceneLight(int type, Scene *parentScene, Number intensity, Number co spotlightCutoff = 40; spotlightExponent = 10; + shadowMapRes = 256; this->depthWrite = false; lightMesh = new Mesh(Mesh::QUAD_MESH); lightMesh->createBox(0.1,0.1,0.1); - bBoxRadius = lightMesh->getRadius(); - bBox = lightMesh->calculateBBox(); + setLocalBoundingBox(lightMesh->calculateBBox()); shadowMapFOV = 60.0f; zBufferTexture = NULL; spotCamera = NULL; @@ -53,29 +54,13 @@ SceneLight::SceneLight(int type, Scene *parentScene, Number intensity, Number co lightColor.setColor(1.0f,1.0f,1.0f,1.0f); setSpotlightProperties(40,0.1); - /* - if(type == SceneLight::SPOT_LIGHT) { - lightShape = new ScenePrimitive(ScenePrimitive::TYPE_CONE, 3, 1.0, 8); - lightShape->Translate(0,0,-1.5); - lightShape->setPitch(90.0); - lightShape->setColor(1.0,1.0,0.0, 0.75); - lightShape->renderWireframe = true; - addChild(lightShape); - } else { - lightShape = new ScenePrimitive(ScenePrimitive::TYPE_BOX, 0.5, 0.5, 0.5); - lightShape->setColor(1.0,1.0,0.0, 0.75); - lightShape->renderWireframe = true; - addChild(lightShape); - } - lightShape->castShadows = false; - lightShape->visible = false; - */ - - lightShape = NULL; - lightImportance = 0; } +void SceneLight::setLightType(int lightType) { + this->type = lightType; +} + void SceneLight::setLightImportance(int newImportance) { lightImportance = newImportance; } @@ -84,22 +69,20 @@ int SceneLight::getLightImportance() const { return lightImportance; } - -void SceneLight::enableDebugDraw(bool val) { - if(lightShape) { - lightShape->visible = val; - } -} - -void SceneLight::enableShadows(bool val, Number resolution) { +void SceneLight::enableShadows(bool val, unsigned int resolution) { if(val) { - if(!zBufferTexture) { - CoreServices::getInstance()->getRenderer()->createRenderTextures(NULL, &zBufferTexture, resolution, resolution, false); - } + delete zBufferTexture; + CoreServices::getInstance()->getRenderer()->createRenderTextures(NULL, &zBufferTexture, resolution, resolution, false); if(!spotCamera) { spotCamera = new Camera(parentScene); -// spotCamera->setPitch(-45.0f); - addEntity(spotCamera); + /* + spotCamera->setProjectionMode(Camera::ORTHO_SIZE_MANUAL); + spotCamera->setOrthoSize(5.0, 5.0); + */ + spotCamera->editorOnly = true; + spotCamera->setClippingPlanes(0.01, 100.0); +// spotCamera->setPitch(90.0); + addChild(spotCamera); } shadowMapRes = resolution; shadowsEnabled = true; @@ -127,24 +110,74 @@ void SceneLight::setShadowMapFOV(Number fov) { shadowMapFOV = fov; } +Number SceneLight::getShadowMapFOV() const { + return shadowMapFOV; +} + SceneLight::~SceneLight() { + if(parentScene) { + parentScene->removeLight(this); + } printf("Destroying scene light...\n"); } +unsigned int SceneLight::getShadowMapResolution() const { + return shadowMapRes; +} + void SceneLight::renderDepthMap(Scene *scene) { - CoreServices::getInstance()->getRenderer()->clearScreen(); - CoreServices::getInstance()->getRenderer()->pushMatrix(); - CoreServices::getInstance()->getRenderer()->loadIdentity(); + spotCamera->setFOV(shadowMapFOV); + Renderer* renderer = CoreServices::getInstance()->getRenderer(); + renderer->pushMatrix(); + renderer->loadIdentity(); - CoreServices::getInstance()->getRenderer()->setViewportSizeAndFOV(shadowMapRes, shadowMapRes, shadowMapFOV); - CoreServices::getInstance()->getRenderer()->bindFrameBufferTexture(zBufferTexture); + Number vpW = renderer->getViewportWidth(); + Number vpH = renderer->getViewportHeight(); + + renderer->setViewportSize(shadowMapRes, shadowMapRes); + renderer->bindFrameBufferTexture(zBufferTexture); scene->RenderDepthOnly(spotCamera); - lightViewMatrix = CoreServices::getInstance()->getRenderer()->getModelviewMatrix() * CoreServices::getInstance()->getRenderer()->getProjectionMatrix(); - CoreServices::getInstance()->getRenderer()->unbindFramebuffers(); - CoreServices::getInstance()->getRenderer()->popMatrix(); - CoreServices::getInstance()->getRenderer()->setViewportSizeAndFOV(CoreServices::getInstance()->getCore()->getXRes(), CoreServices::getInstance()->getCore()->getYRes(), 45.0f); + lightViewMatrix = getConcatenatedMatrix().Inverse() * renderer->getProjectionMatrix(); + renderer->unbindFramebuffers(); + renderer->popMatrix(); + renderer->setViewportSize(vpW , vpH); +} + +Entity *SceneLight::Clone(bool deepClone, bool ignoreEditorOnly) const { + SceneLight *newLight = new SceneLight(type, NULL, intensity, constantAttenuation, linearAttenuation, quadraticAttenuation); + applyClone(newLight, deepClone, ignoreEditorOnly); + return newLight; +} + +void SceneLight::applyClone(Entity *clone, bool deepClone, bool ignoreEditorOnly) const { + Entity::applyClone(clone, deepClone, ignoreEditorOnly); + SceneLight *cloneLight = (SceneLight*) clone; + + cloneLight->setAttenuation(constantAttenuation, linearAttenuation, quadraticAttenuation); + cloneLight->setIntensity(intensity); + cloneLight->lightColor = lightColor; + cloneLight->specularLightColor = specularLightColor; + cloneLight->enableShadows(shadowsEnabled, shadowMapRes); + cloneLight->setShadowMapFOV(shadowMapFOV); + cloneLight->setSpotlightProperties(spotlightCutoff, spotlightExponent); + cloneLight->setLightType(type); +} + +Scene *SceneLight::getParentScene() const { + return parentScene; +} + +void SceneLight::setParentScene(Scene *scene) { + parentScene = scene; + if(spotCamera) { + spotCamera->setParentScene(scene); + } +} + +Camera *SceneLight::getSpotlightCamera() { + return spotCamera; } const Matrix4& SceneLight::getLightViewMatrix() const { @@ -160,14 +193,7 @@ Number SceneLight::getIntensity() const { } void SceneLight::Render() { -/* - CoreServices::getInstance()->getRenderer()->setTexture(NULL); - CoreServices::getInstance()->getRenderer()->beginRenderOperation(lightMesh->getMeshType()); - for(int i=0; i < lightMesh->getPolygonCount(); i++) { - CoreServices::getInstance()->getRenderer()->draw3DPolygon(lightMesh->getPolygon(i)); - } - CoreServices::getInstance()->getRenderer()->endRenderOperation(); - */ + } int SceneLight::getType() const { diff --git a/Core/Contents/Source/PolySceneLine.cpp b/Core/Contents/Source/PolySceneLine.cpp index 3d3adc70a..6186683bb 100755 --- a/Core/Contents/Source/PolySceneLine.cpp +++ b/Core/Contents/Source/PolySceneLine.cpp @@ -22,20 +22,95 @@ #include "PolySceneLine.h" #include "PolyRenderer.h" -#include "PolyPolygon.h" using namespace Polycode; +using std::min; +using std::max; + +SceneCurve::SceneCurve() : SceneMesh(Mesh::LINE_STRIP_MESH) { + curveResolution = 256; + renderCurve = true; + curve = new BezierCurve(); +} + +SceneCurve::SceneCurve(BezierCurve *curve) : SceneMesh(Mesh::LINE_STRIP_MESH) { + curveResolution = 256; + renderCurve = true; + this->curve = curve; +} + +SceneCurve *SceneCurve::SceneCurveWithCurve(BezierCurve *curve) { + return new SceneCurve(curve); +} + +Vector3 SceneCurve::getWorldPointAt(Number t) { + return getConcatenatedMatrix() * curve->getPointAt(t); +} + +Entity *SceneCurve::Clone(bool deepClone, bool ignoreEditorOnly) const { + SceneCurve *newCurve = new SceneCurve(); + applyClone(newCurve, deepClone, ignoreEditorOnly); + return newCurve; +} + +void SceneCurve::applyClone(Entity *clone, bool deepClone, bool ignoreEditorOnly) const { + SceneMesh::applyClone(clone, deepClone, ignoreEditorOnly); + + SceneCurve *cloneCurve = (SceneCurve*)clone; + cloneCurve->renderCurve = renderCurve; + cloneCurve->curveResolution = curveResolution; + + for(int i=0; i < curve->getNumControlPoints(); i++) { + BezierPoint *pt = curve->getControlPoint(i); + cloneCurve->getCurve()->addControlPoint( + pt->p1.x, pt->p1.y, pt->p1.z, + pt->p2.x, pt->p2.y, pt->p2.z, + pt->p3.x, pt->p3.y, pt->p3.z); + } +} + + +SceneCurve::~SceneCurve() { + delete curve; +} + +BezierCurve *SceneCurve::getCurve() { + return curve; +} + +void SceneCurve::Update() { + mesh->clearMesh(); + Vector3 bBox; + + if(renderCurve) { + + Number step = (1.0 / ((Number)curveResolution)); + + for(Number offset=0.0; offset <= 1.0; offset += step) { + Vector3 pt = curve->getPointAt(offset); + mesh->addVertexWithUV(pt.x, pt.y, pt.z, offset, 0.0); + + bBox.x = max(bBox.x,(Number)fabs(pt.x)); + bBox.y = max(bBox.y,(Number)fabs(pt.y)); + bBox.z = max(bBox.z,(Number)fabs(pt.z)); + + } + } + + setLocalBoundingBox(bBox * 2.0); +} + SceneLine::SceneLine(Vector3 start, Vector3 end) : SceneMesh(Mesh::LINE_MESH) { this->ent1 = NULL; this->ent2 = NULL; this->start = start; this->end = end; initLine(); - ignoreParentMatrix = true; + ignoreParentMatrix = false; } -SceneLine::SceneLine(SceneEntity *ent1, SceneEntity *ent2) : SceneMesh(Mesh::LINE_MESH) { +SceneLine::SceneLine(Entity *ent1, Entity *ent2) : SceneMesh(Mesh::LINE_MESH) { this->ent1 = ent1; this->ent2 = ent2; initLine(); @@ -43,12 +118,9 @@ SceneLine::SceneLine(SceneEntity *ent1, SceneEntity *ent2) : SceneMesh(Mesh::LIN } -void SceneLine::initLine() { - Polygon *poly = new Polygon(); - poly->addVertex(0,0,0,0,0); - poly->addVertex(0,0,0,1,0); - mesh->addPolygon(poly); - mesh->arrayDirtyMap[RenderDataArray::TEXCOORD_DATA_ARRAY] = true; +void SceneLine::initLine() { + mesh->addVertexWithUV(0,0,0,0,0); + mesh->addVertexWithUV(0,0,0,1,0); } SceneLine *SceneLine::SceneLineWithPositions(Vector3 start, Vector3 end) { @@ -70,16 +142,20 @@ void SceneLine::Update(){ Vector3 v1; Vector3 v2; + + mesh->vertexPositionArray.data.clear(); if(ent1 != NULL && ent2 != NULL) { v1 = ent1->getConcatenatedMatrix().getPosition(); v2 = ent2->getConcatenatedMatrix().getPosition(); + + mesh->addVertex(v1.x,v1.y,v1.z); + mesh->addVertex(v2.x,v2.y,v2.z); } else { v1 = start; v2 = end; + mesh->addVertex(v1.x,v1.y*yAdjust,v1.z); + mesh->addVertex(v2.x,v2.y*yAdjust,v2.z); + } - - mesh->getPolygon(0)->getVertex(0)->set(v1.x,v1.y,v1.z); - mesh->getPolygon(0)->getVertex(1)->set(v2.x,v2.y,v2.z); - mesh->arrayDirtyMap[RenderDataArray::VERTEX_DATA_ARRAY] = true; -} +} \ No newline at end of file diff --git a/Core/Contents/Source/PolySceneManager.cpp b/Core/Contents/Source/PolySceneManager.cpp index c84770089..2ebdb0154 100755 --- a/Core/Contents/Source/PolySceneManager.cpp +++ b/Core/Contents/Source/PolySceneManager.cpp @@ -42,11 +42,10 @@ SceneManager::~SceneManager() { } void SceneManager::removeScene(Scene *scene) { - Logger::log("Removing scene\n"); for(int i=0;irenderer = renderer; +} + void SceneManager::renderVirtual() { + bool anyVirtualsRendered = false; for(int i=0;igetRenderer()->setViewportSize(renderTextures[i]->getTargetTexture()->getWidth(), renderTextures[i]->getTargetTexture()->getHeight()); - CoreServices::getInstance()->getRenderer()->loadIdentity(); - if(renderTextures[i]->getTargetScene()->isVirtual()) - renderTextures[i]->getTargetScene()->Update(); - - if(renderTextures[i]->getTargetCamera()->hasFilterShader()) { - renderTextures[i]->getTargetCamera()->drawFilter(renderTextures[i]->getTargetTexture(), renderTextures[i]->getTargetTexture()->getWidth(), renderTextures[i]->getTargetTexture()->getHeight(), renderTextures[i]->getFilterColorBufferTexture(), renderTextures[i]->getFilterZBufferTexture()); - } else { - CoreServices::getInstance()->getRenderer()->bindFrameBufferTexture(renderTextures[i]->getTargetTexture()); - renderTextures[i]->getTargetScene()->Render(renderTextures[i]->getTargetCamera()); - CoreServices::getInstance()->getRenderer()->unbindFramebuffers(); - } - - - CoreServices::getInstance()->getRenderer()->clearScreen(); - CoreServices::getInstance()->getRenderer()->loadIdentity(); + if(renderTextures[i]->enabled) { + renderTextures[i]->Render(); + anyVirtualsRendered = true; + } + } + renderer->setViewportSize(renderer->getXRes(), renderer->getYRes()); + if (anyVirtualsRendered) { + renderer->clearScreen(); } - CoreServices::getInstance()->getRenderer()->setViewportSize(CoreServices::getInstance()->getRenderer()->getXRes(), CoreServices::getInstance()->getRenderer()->getYRes()); - } void SceneManager::Render() { for(int i=0;iisEnabled() && !scenes[i]->isVirtual()) { - CoreServices::getInstance()->getRenderer()->loadIdentity(); + renderer->loadIdentity(); Scene *scene = scenes[i]; if(scene->getActiveCamera()->hasFilterShader()) { scene->getActiveCamera()->drawFilter(); @@ -104,6 +98,15 @@ void SceneManager::Render() { scene->Render(); } } + renderer->loadIdentity(); + } +} + +void SceneManager::fixedUpdate() { + for(int i=0;iisEnabled()) { + scenes[i]->fixedUpdate(); + } } } diff --git a/Core/Contents/Source/PolySceneMesh.cpp b/Core/Contents/Source/PolySceneMesh.cpp index eb231daff..a00bd0008 100755 --- a/Core/Contents/Source/PolySceneMesh.cpp +++ b/Core/Contents/Source/PolySceneMesh.cpp @@ -24,10 +24,10 @@ #include "PolyCoreServices.h" #include "PolyBone.h" #include "PolyMaterial.h" -#include "PolyPolygon.h" #include "PolyRenderer.h" #include "PolyMaterial.h" #include "PolyMesh.h" +#include "PolyImage.h" #include "PolyShader.h" #include "PolySkeleton.h" #include "PolyResourceManager.h" @@ -43,61 +43,130 @@ SceneMesh *SceneMesh::SceneMeshWithType(int meshType) { return new SceneMesh(meshType); } -SceneMesh::SceneMesh(const String& fileName) : SceneEntity(), texture(NULL), material(NULL), skeleton(NULL), localShaderOptions(NULL) { - mesh = new Mesh(fileName); - bBoxRadius = mesh->getRadius(); - bBox = mesh->calculateBBox(); - lightmapIndex=0; - showVertexNormals = false; +SceneMesh::SceneMesh(const String& fileName) : Entity(), texture(NULL), material(NULL), skeleton(NULL), localShaderOptions(NULL), mesh(NULL), skeletalVertexPositions(RenderDataArray::VERTEX_DATA_ARRAY), skeletalVertexNormals(RenderDataArray::NORMAL_DATA_ARRAY) { + loadFromFile(fileName); useVertexBuffer = false; lineSmooth = false; ownsMesh = true; ownsSkeleton = true; lineWidth = 1.0; + pointSize = 1.0; + pointSmooth = false; + overlayWireframe = false; + useGeometryHitDetection = false; + forceMaterial = false; + backfaceCulled = true; + vertexBuffer = NULL; + alphaTest = false; + sendBoneMatricesToMaterial = false; } -SceneMesh::SceneMesh(Mesh *mesh) : SceneEntity(), texture(NULL), material(NULL), skeleton(NULL), localShaderOptions(NULL) { +SceneMesh::SceneMesh(Mesh *mesh) : Entity(), texture(NULL), material(NULL), skeleton(NULL), localShaderOptions(NULL), skeletalVertexPositions(RenderDataArray::VERTEX_DATA_ARRAY), skeletalVertexNormals(RenderDataArray::NORMAL_DATA_ARRAY) { this->mesh = mesh; - bBoxRadius = mesh->getRadius(); - bBox = mesh->calculateBBox(); - lightmapIndex=0; - showVertexNormals = false; + setLocalBoundingBox(mesh->calculateBBox()); useVertexBuffer = false; lineSmooth = false; ownsMesh = true; ownsSkeleton = true; lineWidth = 1.0; - + pointSize = 1.0; + pointSmooth = false; + overlayWireframe = false; + useGeometryHitDetection = false; + forceMaterial = false; + vertexBuffer = NULL; + backfaceCulled = true; + alphaTest = false; + sendBoneMatricesToMaterial = false; } -SceneMesh::SceneMesh(int meshType) : texture(NULL), material(NULL), skeleton(NULL), localShaderOptions(NULL) { +SceneMesh::SceneMesh(int meshType) : texture(NULL), material(NULL), skeleton(NULL), localShaderOptions(NULL), skeletalVertexPositions(RenderDataArray::VERTEX_DATA_ARRAY), skeletalVertexNormals(RenderDataArray::NORMAL_DATA_ARRAY) { mesh = new Mesh(meshType); - bBoxRadius = mesh->getRadius(); - bBox = mesh->calculateBBox(); - lightmapIndex=0; - showVertexNormals = false; + setLocalBoundingBox(mesh->calculateBBox()); useVertexBuffer = false; lineSmooth = false; ownsMesh = true; ownsSkeleton = true; - lineWidth = 1.0; + lineWidth = 1.0; + overlayWireframe = false; + useGeometryHitDetection = false; + forceMaterial = false; + backfaceCulled = true; + vertexBuffer = NULL; + alphaTest = false; + sendBoneMatricesToMaterial = false; } void SceneMesh::setMesh(Mesh *mesh) { this->mesh = mesh; - bBoxRadius = mesh->getRadius(); - bBox = mesh->calculateBBox(); - showVertexNormals = false; + setLocalBoundingBox(mesh->calculateBBox()); useVertexBuffer = false; } - SceneMesh::~SceneMesh() { if(ownsSkeleton) delete skeleton; if(ownsMesh) delete mesh; delete localShaderOptions; + + if(useVertexBuffer) { + CoreServices::getInstance()->getRenderer()->destroyVertexBuffer(vertexBuffer); + } +} + +Entity *SceneMesh::Clone(bool deepClone, bool ignoreEditorOnly) const { + SceneMesh *newEntity = new SceneMesh(mesh->getMeshType()); + applyClone(newEntity, deepClone, ignoreEditorOnly); + return newEntity; +} + +void SceneMesh::applyClone(Entity *clone, bool deepClone, bool ignoreEditorOnly) const { + Entity::applyClone(clone, deepClone, ignoreEditorOnly); + SceneMesh *_clone = (SceneMesh*) clone; + + _clone->lineWidth = lineWidth; + _clone->lineSmooth = lineSmooth; + _clone->pointSize = pointSize; + _clone->pointSmooth = pointSmooth; + _clone->ownsMesh = ownsMesh; + _clone->alphaTest = alphaTest; + _clone->backfaceCulled = backfaceCulled; + _clone->ownsSkeleton = ownsSkeleton; + _clone->overlayWireframe = overlayWireframe; + _clone->wireFrameColor = wireFrameColor; + _clone->useGeometryHitDetection = useGeometryHitDetection; + _clone->forceMaterial = forceMaterial; + _clone->setFilename(fileName); + + Mesh *newMesh = mesh->Copy(); + _clone->setMesh(newMesh); + + if(useVertexBuffer) { + _clone->cacheToVertexBuffer(true); + } + + _clone->setMaterial(material); + if(material) { + localShaderOptions->copyTo(_clone->getLocalShaderOptions()); + } +} + +void SceneMesh::setFilename(String fileName) { + this->fileName = fileName; +} + +void SceneMesh::loadFromFile(String fileName) { + if(mesh && ownsMesh) { + delete mesh; + } + mesh = new Mesh(fileName); + setLocalBoundingBox(mesh->calculateBBox()); + this->fileName = fileName; +} + +String SceneMesh::getFilename() { + return fileName; } Mesh *SceneMesh::getMesh() { @@ -135,14 +204,19 @@ void SceneMesh::setMaterial(Material *material) { } -void SceneMesh::setMaterialByName(const String& materialName) { - Material *material = (Material*)CoreServices::getInstance()->getResourceManager()->getResource(Resource::RESOURCE_MATERIAL, materialName); - if(!material) - return; - setMaterial(material); +void SceneMesh::setMaterialByName(const String& materialName, ResourcePool *resourcePool) { + + Material *material; + if(resourcePool) { + material = (Material*)resourcePool->getResource(Resource::RESOURCE_MATERIAL, materialName); + } else { + material = (Material*)CoreServices::getInstance()->getResourceManager()->getGlobalPool()->getResource(Resource::RESOURCE_MATERIAL, materialName); + + } + setMaterial(material); } -Texture *SceneMesh::getTexture() { +Texture *SceneMesh::getTexture() const { return texture; } @@ -152,29 +226,28 @@ void SceneMesh::loadTexture(const String& fileName) { texture = materialManager->createTextureFromFile(fileName, materialManager->clampDefault, materialManager->mipmapsDefault); } +void SceneMesh::loadTextureFromImage(Image *image) { + MaterialManager *materialManager = CoreServices::getInstance()->getMaterialManager(); + texture = materialManager->createTextureFromImage(image, materialManager->clampDefault, materialManager->mipmapsDefault); +} + ShaderBinding *SceneMesh::getLocalShaderOptions() { return localShaderOptions; } -void SceneMesh::loadSkeleton(const String& fileName) { +Skeleton *SceneMesh::loadSkeleton(const String& fileName) { skeleton = new Skeleton(fileName); - addEntity(skeleton); - + addChild(skeleton); setSkeleton(skeleton); + return skeleton; } void SceneMesh::setSkeleton(Skeleton *skeleton) { this->skeleton = skeleton; - for(int i=0; i < mesh->getPolygonCount(); i++) { - Polygon *polygon = mesh->getPolygon(i); - unsigned int vCount = polygon->getVertexCount(); - for(int j=0; j < vCount; j++) { - Vertex *vertex = polygon->getVertex(j); - for(int k=0; k < vertex->getNumBoneAssignments(); k++) { - vertex->getBoneAssignment(k)->bone = skeleton->getBone(vertex->getBoneAssignment(k)->boneID); - } - } - } +} + +void SceneMesh::setLineWidth(Number newWidth) { + lineWidth = newWidth; } Material *SceneMesh::getMaterial() { @@ -187,118 +260,184 @@ Skeleton *SceneMesh::getSkeleton() { void SceneMesh::renderMeshLocally() { Renderer *renderer = CoreServices::getInstance()->getRenderer(); + - if(skeleton) { - for(int i=0; i < mesh->getPolygonCount(); i++) { - Polygon *polygon = mesh->getPolygon(i); - unsigned int vCount = polygon->getVertexCount(); - for(int j=0; j < vCount; j++) { - Vertex *vert = polygon->getVertex(j); - Vector3 norm; - - Vector3 aPos = vert->restPosition; - Vector3 tPos; - - Number mult = 1; -/* - Number mult = 0; - for(int b =0; b < vert->getNumBoneAssignments(); b++) { - BoneAssignment *bas = vert->getBoneAssignment(b); - mult += bas->weight; - } - mult = 1.0f/mult; -*/ - for(int b =0; b < vert->getNumBoneAssignments(); b++) { - BoneAssignment *bas = vert->getBoneAssignment(b); - Bone *bone = bas->bone; - if(bone) { - - Matrix4 restMatrix = bone->getRestMatrix(); - Matrix4 finalMatrix = bone->getFinalMatrix(); - - Vector3 vec = restMatrix * aPos; - tPos += finalMatrix * vec * (bas->weight*mult); - - Vector3 nvec = vert->restNormal; - nvec = restMatrix.rotateVector(nvec); - nvec = finalMatrix.rotateVector(nvec); - - norm += nvec * (bas->weight*mult); - } - } - - - vert->x = tPos.x; - vert->y = tPos.y; - vert->z = tPos.z; - - norm.Normalize(); - vert->setNormal(norm.x, norm.y, norm.z); - - } - } - mesh->arrayDirtyMap[RenderDataArray::VERTEX_DATA_ARRAY] = true; - mesh->arrayDirtyMap[RenderDataArray::NORMAL_DATA_ARRAY] = true; - mesh->arrayDirtyMap[RenderDataArray::TANGENT_DATA_ARRAY] = true; - } + if(skeleton) { + + skeletalVertexPositions.data.clear(); + skeletalVertexNormals.data.clear(); + + for(int i=0; i < mesh->vertexPositionArray.data.size()/3; i++) { + + Vector3 norm; + Vector3 tPos; + + for(int b=0; b < 4; b++) { + + PolyRendererVertexType boneWeight = mesh->vertexBoneWeightArray.data[(i*4)+b]; + + if(boneWeight > 0.0) { + + Bone *bone = skeleton->getBone(mesh->vertexBoneIndexArray.data[(i*4)+b]); + if(bone) { + Vector3 restVert(mesh->vertexPositionArray.data[i*3], mesh->vertexPositionArray.data[(i*3)+1], mesh->vertexPositionArray.data[(i*3)+2]); + + tPos += bone->finalMatrix * restVert * (boneWeight); + + Vector3 nvec(mesh->vertexNormalArray.data[i*3], mesh->vertexNormalArray.data[(i*3)+1], mesh->vertexNormalArray.data[(i*3)+2]); + + nvec = bone->finalMatrix.rotateVector(nvec); + + norm += nvec * (boneWeight); + } + } + } + skeletalVertexPositions.data.push_back(tPos.x); + skeletalVertexPositions.data.push_back(tPos.y); + skeletalVertexPositions.data.push_back(tPos.z); + + norm.Normalize(); + + skeletalVertexNormals.data.push_back(norm.x); + skeletalVertexNormals.data.push_back(norm.y); + skeletalVertexNormals.data.push_back(norm.z); + } + + renderer->pushRenderDataArray(&skeletalVertexPositions); + renderer->pushRenderDataArray(&skeletalVertexNormals); + + } else { + renderer->pushRenderDataArray(&mesh->vertexPositionArray); + renderer->pushRenderDataArray(&mesh->vertexNormalArray); + } + + renderer->pushRenderDataArray(&mesh->vertexTangentArray); + renderer->pushRenderDataArray(&mesh->vertexTexCoordArray); + if(mesh->useVertexColors) { - renderer->pushDataArrayForMesh(mesh, RenderDataArray::COLOR_DATA_ARRAY); + renderer->pushRenderDataArray(&mesh->vertexColorArray); } - - renderer->pushDataArrayForMesh(mesh, RenderDataArray::VERTEX_DATA_ARRAY); - renderer->pushDataArrayForMesh(mesh, RenderDataArray::NORMAL_DATA_ARRAY); - renderer->pushDataArrayForMesh(mesh, RenderDataArray::TANGENT_DATA_ARRAY); - renderer->pushDataArrayForMesh(mesh, RenderDataArray::TEXCOORD_DATA_ARRAY); - - renderer->drawArrays(mesh->getMeshType()); + + if(mesh->indexedMesh) { + renderer->drawArrays(mesh->getMeshType(), &mesh->indexArray); + } else { + renderer->drawArrays(mesh->getMeshType(), NULL); + } } void SceneMesh::cacheToVertexBuffer(bool cache) { - if(cache && !mesh->hasVertexBuffer()) { - CoreServices::getInstance()->getRenderer()->createVertexBufferForMesh(mesh); + if(useVertexBuffer) { + CoreServices::getInstance()->getRenderer()->destroyVertexBuffer(vertexBuffer); + vertexBuffer = NULL; + } + + if(cache) { + vertexBuffer = CoreServices::getInstance()->getRenderer()->createVertexBufferForMesh(mesh); } useVertexBuffer = cache; } +bool SceneMesh::customHitDetection(const Ray &ray) { + if(!useGeometryHitDetection) + return true; + + Ray transformedRay; + + Matrix4 adjustedMatrix = getAnchorAdjustedMatrix().Inverse(); + transformedRay.origin = adjustedMatrix * ray.origin; + transformedRay.direction = adjustedMatrix.rotateVector(ray.direction); + + if(mesh->indexedMesh) { + for(int i=0; i < mesh->getIndexCount(); i+=3) { + if(i+2 < mesh->getIndexCount()) { + if(transformedRay.polygonIntersect(mesh->getVertexPositionAtIndex(i), mesh->getVertexPositionAtIndex(i+1), mesh->getVertexPositionAtIndex(i+2))) { + return true; + } + } + } + + } else { + for(int i=0; i < mesh->getVertexCount(); i+=3) { + if(i+2 < mesh->getVertexCount()) { + if(transformedRay.polygonIntersect(mesh->getVertexPosition(i), mesh->getVertexPosition(i+1), mesh->getVertexPosition(i+2))) { + return true; + } + } + } + } + + return false; +} + void SceneMesh::Render() { Renderer *renderer = CoreServices::getInstance()->getRenderer(); + renderer->enableAlphaTest(alphaTest); + renderer->enableBackfaceCulling(backfaceCulled); + renderer->setLineSize(lineWidth); renderer->setLineSmooth(lineSmooth); + renderer->setPointSize(pointSize); + renderer->setPointSmooth(pointSmooth); if(material) { - renderer->applyMaterial(material, localShaderOptions,0); + + renderer->applyMaterial(material, localShaderOptions,0, forceMaterial); } else { if(texture) renderer->setTexture(texture); else renderer->setTexture(NULL); } - + + if(sendBoneMatricesToMaterial && localShaderOptions && skeleton) { + LocalShaderParam *skeletonMatrix = localShaderOptions->getLocalParamByName("skeletonMatrix[0]"); + + if(skeletonMatrix) { + for(int i=0; i < skeleton->getNumBones(); i++) { + materialBoneMatrices[i] = skeleton->getBone(i)->getFinalMatrix(); + } + } else { + materialBoneMatrices.resize(skeleton->getNumBones()); + localShaderOptions->addParamPointer(ProgramParam::PARAM_MATRIX, "skeletonMatrix[0]", materialBoneMatrices.data())->arraySize = skeleton->getNumBones(); + } + } + + bool useVertexBuffer = this->useVertexBuffer; + + if(useVertexBuffer && skeleton && !sendBoneMatricesToMaterial) { + useVertexBuffer = false; + } + if(useVertexBuffer) { - renderer->drawVertexBuffer(mesh->getVertexBuffer(), mesh->useVertexColors); + if(vertexBuffer){ + renderer->drawVertexBuffer(vertexBuffer, mesh->useVertexColors); + } } else { renderMeshLocally(); } - if(material) + if(material) { renderer->clearShader(); - - if(showVertexNormals) { - renderer->setTexture(NULL); - /* - for(int i=0; i < mesh->getPolygonCount(); i++) { - Polygon *polygon = mesh->getPolygon(i); - unsigned int vCount = polygon->getVertexCount(); - for(int j=0; j < vCount; j++) { - Vertex *vert = polygon->getVertex(j); - Vector3 norm = *vert->normal; - CoreServices::getInstance()->getRenderer()->draw3DLine(*vert, norm, 0.4f, Color(0.0f,0.7f,1.0f,0.5f)); - } + } + + renderer->setTexture(NULL); + + if(overlayWireframe) { + bool depthTestVal = depthTest; + renderer->enableDepthTest(false); + renderer->setWireframePolygonMode(true); + renderer->setVertexColor(wireFrameColor.r, wireFrameColor.g, wireFrameColor.b, wireFrameColor.a); + + if(useVertexBuffer) { + renderer->drawVertexBuffer(vertexBuffer, mesh->useVertexColors); + } else { + renderMeshLocally(); } - */ + renderer->enableDepthTest(depthTestVal); } + renderer->setWireframePolygonMode(false); } diff --git a/Core/Contents/Source/PolyScenePrimitive.cpp b/Core/Contents/Source/PolyScenePrimitive.cpp index fd986f669..41176291a 100755 --- a/Core/Contents/Source/PolyScenePrimitive.cpp +++ b/Core/Contents/Source/PolyScenePrimitive.cpp @@ -27,58 +27,117 @@ using namespace Polycode; ScenePrimitive::ScenePrimitive(int type, Number v1, Number v2, Number v3,Number v4,Number v5) : SceneMesh(Mesh::QUAD_MESH) { + this->type = type; + this->v1 = v1; + this->v2 = v2; + this->v3 = v3; + this->v4 = v4; + this->v5 = v5; + + recreatePrimitive(); +} + +int ScenePrimitive::getPrimitiveType() const { + return type; +} + +Number ScenePrimitive::getPrimitiveParameter1() const { + return v1; +} + +Number ScenePrimitive::getPrimitiveParameter2() const { + return v2; +} + +Number ScenePrimitive::getPrimitiveParameter3() const { + return v3; +} + +Number ScenePrimitive::getPrimitiveParameter4() const { + return v4; +} + +Number ScenePrimitive::getPrimitiveParameter5() const { + return v5; +} + +void ScenePrimitive::recreatePrimitive() { + mesh->clearMesh(); switch(type) { case TYPE_PLANE: - mesh->createPlane(v1,v2); - bBox.x = v1; - bBox.y = 0; - bBox.z = v2; + mesh->createPlane(v1, v2, v3); + setLocalBoundingBox(v1, 0.001, v2); break; case TYPE_VPLANE: - mesh->createVPlane(v1,v2); - bBox.x = v1; - bBox.y = v2; - bBox.z = 0; - break; + mesh->createVPlane(v1, v2, v3); + setLocalBoundingBox(v1, v2, 0.001); + break; case TYPE_BOX: - mesh->createBox(v1,v2,v3); - bBox.x = v1; - bBox.y = v2; - bBox.z = v3; + mesh->createBox(v1, v2, v3, v4); + setLocalBoundingBox(v1, v2, v3); break; case TYPE_SPHERE: - mesh->createSphere(v1,v2,v3); - bBox.x = v1*2; - bBox.y = v1*2; - bBox.z = v1*2; + mesh->createSphere(v1, v2, v3, v4); + setLocalBoundingBox(v1*2, v1*2, v1*2); break; case TYPE_CYLINDER: - mesh->createCylinder(v1,v2,v3); - bBox.x = v2*2; - bBox.y = v1; - bBox.z = v2*2; + mesh->createCylinder(v1, v2, v3, true, v4); + setLocalBoundingBox(v2*2, v1, v2*2); break; case TYPE_UNCAPPED_CYLINDER: - mesh->createCylinder(v1,v2,v3, false); - bBox.x = v2*2; - bBox.y = v1; - bBox.z = v2*2; - break; + mesh->createCylinder(v1, v2, v3, false, v5); + setLocalBoundingBox(v2*2, v1, v2*2); + break; case TYPE_CONE: - mesh->createCone(v1,v2,v3); - bBox.x = v2*2; - bBox.y = v1; - bBox.z = v2*2; - break; + mesh->createCone(v1, v2, v3, v4); + setLocalBoundingBox(v2*2, v1, v2*2); + break; case TYPE_TORUS: - mesh->createTorus(v1,v2,v3,v4); - bBox.x = v1*2; - bBox.y = v2; - bBox.z = v1*2; - break; + mesh->createTorus(v1, v2, v3, v4, v5); + setLocalBoundingBox((v1*2) + (v2*2), v2 * 2, (v1*2) + (v2*2)); + break; + case TYPE_CIRCLE: + mesh->createCircle(v1, v2, v3, v4); + setLocalBoundingBox(v1, v2, 0.001); + break; + case TYPE_LINE_CIRCLE: + mesh->createLineCircle(v1, v2, v3, v4); + setLocalBoundingBox(v1, v2, 0.001); + break; + case TYPE_ICOSPHERE: + mesh->createIcosphere(v1, v2); + setLocalBoundingBox(v1*2, v1*2, v1*2); + break; + case TYPE_OCTOSPHERE: + mesh->createOctosphere(v1, v2); + setLocalBoundingBox(v1*2, v1*2, v1*2); + break; } } +Entity *ScenePrimitive::Clone(bool deepClone, bool ignoreEditorOnly) const { + ScenePrimitive *newEntity = new ScenePrimitive(type, v1, v2, v3, v4, v5); + applyClone(newEntity, deepClone, ignoreEditorOnly); + return newEntity; +} + +void ScenePrimitive::applyClone(Entity *clone, bool deepClone, bool ignoreEditorOnly) const { + SceneMesh::applyClone(clone, deepClone, ignoreEditorOnly); + ((ScenePrimitive*)clone)->setPrimitiveOptions(type, v1, v2, v3, v4, v5); +} + +void ScenePrimitive::setPrimitiveOptions(int type, Number v1, Number v2, Number v3,Number v4,Number v5) { + + this->type = type; + this->v1 = v1; + this->v2 = v2; + this->v3 = v3; + this->v4 = v4; + this->v5 = v5; + + recreatePrimitive(); +} + ScenePrimitive::~ScenePrimitive() { } diff --git a/Core/Contents/Source/PolySceneRenderTexture.cpp b/Core/Contents/Source/PolySceneRenderTexture.cpp index 1bae03004..0bfc94e2b 100755 --- a/Core/Contents/Source/PolySceneRenderTexture.cpp +++ b/Core/Contents/Source/PolySceneRenderTexture.cpp @@ -24,7 +24,9 @@ #include "PolyCoreServices.h" #include "PolyRenderer.h" #include "PolySceneManager.h" - +#include "PolyTexture.h" +#include "PolyScene.h" +#include "PolyCamera.h" using namespace Polycode; @@ -36,6 +38,8 @@ SceneRenderTexture::SceneRenderTexture(Scene *targetScene, Camera *targetCamera, CoreServices::getInstance()->getRenderer()->createRenderTextures(&filterColorBufferTexture, &filterZBufferTexture, renderWidth, renderHeight, floatingPoint); CoreServices::getInstance()->getSceneManager()->registerRenderTexture(this); + renderer = CoreServices::getInstance()->getRenderer(); + enabled = true; } void SceneRenderTexture::resizeRenderTexture(int newWidth, int newHeight) { @@ -67,6 +71,23 @@ Camera *SceneRenderTexture::getTargetCamera() { return targetCamera; } +void SceneRenderTexture::Render() { + renderer->setViewportSize(targetTexture->getWidth(), targetTexture->getHeight()); + renderer->loadIdentity(); + if(targetCamera->hasFilterShader()) { + targetCamera->drawFilter(targetTexture, targetTexture->getWidth(), targetTexture->getHeight(), filterColorBufferTexture, filterZBufferTexture); + } else { + renderer->bindFrameBufferTexture(targetTexture); + targetScene->Render(targetCamera); + renderer->unbindFramebuffers(); + } + renderer->loadIdentity(); +} + +Image *SceneRenderTexture::saveToImage() { + return renderer->renderBufferToImage(targetTexture); +} + Texture *SceneRenderTexture::getTargetTexture() { return targetTexture; } diff --git a/Core/Contents/Source/PolySceneSound.cpp b/Core/Contents/Source/PolySceneSound.cpp index a2cb5f76f..2101415f6 100644 --- a/Core/Contents/Source/PolySceneSound.cpp +++ b/Core/Contents/Source/PolySceneSound.cpp @@ -27,7 +27,7 @@ THE SOFTWARE. using namespace Polycode; -SceneSoundListener::SceneSoundListener() : SceneEntity() { +SceneSoundListener::SceneSoundListener() : Entity() { } SceneSoundListener::~SceneSoundListener() { @@ -43,11 +43,35 @@ void SceneSoundListener::Update() { CoreServices::getInstance()->getSoundManager()->setListenerOrientation(direction, upVector); } +void SceneSound::setLoopOnLoad(bool val) { + loopOnLoad = val; +} + +bool SceneSound::getLoopOnLoad() { + return loopOnLoad; +} + + +Entity *SceneSound::Clone(bool deepClone, bool ignoreEditorOnly) const { + SceneSound *newSound = new SceneSound(sound->getFileName(), sound->getReferenceDistance(), sound->getMaxDistance(), directionalSound); + applyClone(newSound, deepClone, ignoreEditorOnly); + return newSound; +} -SceneSound::SceneSound(const String& fileName, Number referenceDistance, Number maxDistance, bool directionalSound) : SceneEntity() { +void SceneSound::applyClone(Entity *clone, bool deepClone, bool ignoreEditorOnly) const { + Entity::applyClone(clone, deepClone, ignoreEditorOnly); + SceneSound *cloneSound = (SceneSound*) clone; + cloneSound->setLoopOnLoad(loopOnLoad); + cloneSound->getSound()->setPositionalProperties(sound->getReferenceDistance(), sound->getMaxDistance()); + cloneSound->setDirectionalSound(directionalSound); + cloneSound->getSound()->setVolume(sound->getVolume()); + cloneSound->getSound()->setPitch(sound->getPitch()); +} + +SceneSound::SceneSound(const String& fileName, Number referenceDistance, Number maxDistance, bool directionalSound) : Entity() { this->directionalSound = directionalSound; - + loopOnLoad = false; sound = new Sound(fileName); sound->setIsPositional(true); sound->setPositionalProperties(referenceDistance, maxDistance); @@ -57,6 +81,13 @@ SceneSound::~SceneSound() { delete sound; } +bool SceneSound::isDirectionalSound() const { + return directionalSound; +} +void SceneSound::setDirectionalSound(bool val) { + directionalSound = val; +} + void SceneSound::Update() { Matrix4 finalMatrix = getConcatenatedMatrix(); sound->setSoundPosition(finalMatrix.getPosition()); diff --git a/Core/Contents/Source/PolySceneSprite.cpp b/Core/Contents/Source/PolySceneSprite.cpp new file mode 100644 index 000000000..e306239ba --- /dev/null +++ b/Core/Contents/Source/PolySceneSprite.cpp @@ -0,0 +1,923 @@ +/* + Copyright (C) 2013 by Ivan Safrin + + 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: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + 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. +*/ + +#include "PolySceneSprite.h" +#include "PolyCore.h" +#include "PolyCoreServices.h" +#include "PolyMesh.h" +#include "PolyTexture.h" +#include "PolyLogger.h" + +using std::vector; +using namespace Polycode; + + +SceneSprite::SceneSprite(SpriteSet *spriteSet) : SceneMesh(Mesh::QUAD_MESH) { + currentSprite = NULL; + currentSpriteState = NULL; + this->spriteSet = NULL; + setSpriteSet(spriteSet); + defaultMesh = mesh; + currentFrame = 0; + core = Services()->getCore(); + spriteTimer = 0.0; + paused = false; + playOnce = false; + spriteTimerVal = 0.1; + useGeometryHitDetection = false; + ownsMesh = false; + startOnRandomFrame = false; +} + +bool SceneSprite::getStartOnRandomFrame() { + return startOnRandomFrame; +} + +void SceneSprite::setStartOnRandomFrame(bool val) { + startOnRandomFrame = val; + if(val && currentSpriteState) { + currentFrame = rand() % currentSpriteState->getNumFrameIDs(); + } +} + +Entity *SceneSprite::Clone(bool deepClone, bool ignoreEditorOnly) const { + SceneSprite *newSprite = new SceneSprite(spriteSet); + newSprite->setSprite(currentSprite); + newSprite->setSpriteState(currentSpriteState, currentFrame, playOnce); + newSprite->setStartOnRandomFrame(startOnRandomFrame); + applyClone(newSprite, deepClone, ignoreEditorOnly); + return newSprite; +} + +void SceneSprite::applyClone(Entity *clone, bool deepClone, bool ignoreEditorOnly) const { + SceneMesh::applyClone(clone, deepClone, ignoreEditorOnly); +} + + +SpriteState *SceneSprite::getCurrentSpriteState() { + return currentSpriteState; +} + +SpriteSet *SceneSprite::getSpriteSet() { + return spriteSet; +} + +Sprite *SceneSprite::getCurrentSprite() { + return currentSprite; +} + +void SceneSprite::setPaused(bool val) { + paused = val; +} + +bool SceneSprite::isPaused() { + return paused; +} + +void SceneSprite::setCurrentFrame(unsigned int frameIndex) { + currentFrame = frameIndex; +} + +void SceneSprite::setSpriteByName(String spriteName) { + Sprite *sprite = spriteSet->getSpriteByName(spriteName); + if(sprite) { + setSprite(sprite); + } +} + +void SceneSprite::setSprite(Sprite *spriteEntry) { + + if(currentSprite){ + currentSprite->removeAllHandlersForListener(this); + } + + if(spriteEntry) { + setSpriteSet(spriteEntry->getParentSpriteSet()); + } + currentSprite = spriteEntry; + currentSpriteState = NULL; + + if(currentSprite) { + currentSprite->addEventListener(this, Event::CHANGE_EVENT); + } +} + +void SceneSprite::setSpriteSet(SpriteSet *spriteSet) { + + if(this->spriteSet) { + this->spriteSet->removeAllHandlersForListener(this); + } + + this->spriteSet = spriteSet; + spriteSet->addEventListener(this, Event::CHANGE_EVENT); + + setTexture(spriteSet->getTexture()); + + if(getLocalShaderOptions()) { + getLocalShaderOptions()->clearTexture("diffuse"); + getLocalShaderOptions()->addTexture("diffuse", getTexture()); + } + + currentSprite = NULL; + currentSpriteState = NULL; +} + +Vector3 SceneSprite::getSpriteBoundingBox() const { + return spriteBoundingBox; +} + +void SceneSprite::handleEvent(Event *event) { + if(event->getDispatcher() == spriteSet) { + if(event->getEventCode() == Event::CHANGE_EVENT) { + + bool hasSprite = false; + for(int i=0; i < spriteSet->getNumSpriteEntries(); i++) { + if(currentSprite == spriteSet->getSpriteEntry(i)) { + hasSprite = true; + break; + } + } + if(!hasSprite) { + if(spriteSet->getNumSpriteEntries() > 0) { + setSprite(spriteSet->getSpriteEntry(0)); + } else { + setSprite(NULL); + } + } + } + } else if(event->getDispatcher() == currentSprite) { + bool hasState = false; + for(int i=0; i < currentSprite->getNumStates(); i++) { + if(currentSprite->getState(i) == currentSpriteState) { + hasState = true; + break; + } + } + if(!hasState) { + if(currentSprite->getNumStates() > 0) { + setSpriteState(currentSprite->getState(0), 0, playOnce); + } else { + setSpriteState(NULL, 0, playOnce); + } + } + } +} + +void SceneSprite::setSpriteStateByName(String name, unsigned int startingFrame, bool playOnce) { + if(!currentSprite) { + return; + } + SpriteState *spriteState = currentSprite->getStateByName(name); + if(spriteState) { + setSpriteState(spriteState, startingFrame, playOnce); + } +} + +void SceneSprite::setSpriteState(SpriteState *spriteState, unsigned int startingFrame, bool playOnce) { + + if(currentSpriteState != spriteState || playOnce) { + currentFrame = startingFrame; + } + + currentSpriteState = spriteState; + + if(!currentSpriteState) { + return; + } + + Vector2 bBox = currentSpriteState->getBoundingBox(); + setLocalBoundingBox(bBox.x / currentSpriteState->getPixelsPerUnit(), bBox.y / currentSpriteState->getPixelsPerUnit(), 0.001); + + spriteBoundingBox = currentSpriteState->getLargestFrameBoundingBox(); + + this->playOnce = playOnce; + +} + +void SceneSprite::Update() { + + if(!currentSprite || !currentSpriteState) { + return; + } + + Vector2 bBox = currentSpriteState->getBoundingBox(); + setLocalBoundingBox(bBox.x / currentSpriteState->getPixelsPerUnit(), bBox.y / currentSpriteState->getPixelsPerUnit(), 0.001); + + spriteBoundingBox = currentSpriteState->getLargestFrameBoundingBox(); + + setTexture(spriteSet->getTexture()); + + if(paused) { + return; + } + + spriteTimer += core->getElapsed(); + + if(spriteTimer > 1.0/currentSpriteState->getStateFPS()) { + spriteTimer = 0.0; + currentFrame++; + if(currentFrame >= currentSpriteState->getNumFrameIDs()) { + if(playOnce) { + currentFrame = currentSpriteState->getNumFrameIDs()-1; + } else { + currentFrame = 0; + } + } + } +} + +unsigned int SceneSprite::getCurrentFrame() { + return currentFrame; +} + +void SceneSprite::Render() { + + if(!currentSprite || !currentSpriteState) { + return; + } + + Mesh *stateMesh = currentSpriteState->getMeshForFrameIndex(currentFrame); + if(stateMesh) { + this->mesh = stateMesh; + useVertexBuffer = false; + } else { + this->mesh = defaultMesh; + useVertexBuffer = false; + } + + SceneMesh::Render(); +} + +SceneSprite::~SceneSprite() { + +} + +SpriteState::SpriteState(SpriteSet *spriteSet, String name) { + this->spriteSet = spriteSet; + this->name = name; + stateFPS = 60.0; + pixelsPerUnit = 1.0; +} + +void SpriteState::setBoundingBox(Vector2 boundingBox) { + this->boundingBox = boundingBox; + rebuildStateMeshes(); +} + +void SpriteState::clearFrames() { + frameIDs.clear(); + rebuildStateMeshes(); +} + +Vector2 SpriteState::getBoundingBox() { + return boundingBox; +} + +Vector2 SpriteState::getSpriteOffset() { + return spriteOffset; +} + +void SpriteState::setSpriteOffset(const Vector2 &offset) { + spriteOffset = offset; + rebuildStateMeshes(); +} + + +void SpriteState::setPixelsPerUnit(Number ppu) { + pixelsPerUnit = ppu; + rebuildStateMeshes(); +} + +Number SpriteState::getPixelsPerUnit() { + return pixelsPerUnit; +} + +void SpriteState::removeFrameByIndex(unsigned int index) { + if(index < frameIDs.size()) { + frameIDs.erase(frameIDs.begin()+index); + } + rebuildStateMeshes(); +} + +void SpriteState::removeFrameIndices(std::vector indices) { + std::vector newFrames; + + for(int i=0; i < frameIDs.size(); i++) { + bool hasIndex = false; + for(int j=0; j < indices.size(); j++) { + if(indices[j] == i) { + hasIndex = true; + break; + } + } + if(!hasIndex) { + newFrames.push_back(frameIDs[i]); + } + } + + frameIDs = newFrames; + rebuildStateMeshes(); +} + +void SpriteState::setStateFPS(Number fps) { + stateFPS = fps; +} + +Number SpriteState::getStateFPS() { + return stateFPS; +} + +void SpriteState::setName(String name) { + this->name = name; +} + +void SpriteState::setNewFrameIDs(std::vector newIDs) { + frameIDs = newIDs; + rebuildStateMeshes(); +} + +Vector3 SpriteState::getLargestFrameBoundingBox() const { + return largestFrameBoundingBox; +} + +void SpriteState::insertFrame(unsigned int index, unsigned int frameID) { + if(index < frameIDs.size()) { + frameIDs.insert(frameIDs.begin()+index, frameID); + } + rebuildStateMeshes(); +} + +Mesh *SpriteState::getMeshForFrameIndex(unsigned int index) { + if(index < frameMeshes.size()) { + return frameMeshes[index]; + } else { + return NULL; + } +} + +void SpriteState::rebuildStateMeshes() { + for(int i=0; i < frameMeshes.size(); i++) { + delete frameMeshes[i]; + } + frameMeshes.clear(); + + largestFrameBoundingBox = Vector3(); + + for(int i=0; i < frameIDs.size(); i++) { + Mesh *frameMesh = new Mesh(Mesh::QUAD_MESH); + SpriteFrame frame = spriteSet->getSpriteFrameByID(frameIDs[i]); + + frameMesh->indexedMesh = true; + + Number aspectRatio = frame.coordinates.w / frame.coordinates.h; + Number textureAspectRatio = ((Number)spriteSet->getTexture()->getWidth()) / ((Number)spriteSet->getTexture()->getHeight()); + + + Number frameHeight = frame.coordinates.h * ((Number)spriteSet->getTexture()->getHeight()) / pixelsPerUnit; + + Number frameWidth = frameHeight * aspectRatio * textureAspectRatio; + + + + Vector2 meshOffset; + meshOffset.x = boundingBox.x * spriteOffset.x / pixelsPerUnit; + meshOffset.y = boundingBox.y * spriteOffset.y / pixelsPerUnit; + + meshOffset.x -= frameWidth * frame.anchorPoint.x; + meshOffset.y += frameHeight * frame.anchorPoint.y; + + frameMesh->addVertexWithUVAndNormal(meshOffset.x+-frameWidth*0.5, meshOffset.y+frameHeight*0.5, 0.0, frame.coordinates.x, 1.0-frame.coordinates.y, 0.0, 0.0, 1.0); + frameMesh->addVertexWithUVAndNormal(meshOffset.x+-frameWidth*0.5, meshOffset.y+frameHeight*0.5-frameHeight, 0.0, frame.coordinates.x, 1.0-frame.coordinates.y - frame.coordinates.h, 0.0, 0.0, 1.0); + frameMesh->addVertexWithUVAndNormal(meshOffset.x+-frameWidth*0.5+frameWidth, meshOffset.y+frameHeight*0.5-frameHeight, 0.0, frame.coordinates.x+frame.coordinates.w, 1.0- frame.coordinates.y - frame.coordinates.h, 0.0, 0.0, 1.0); + frameMesh->addVertexWithUVAndNormal(meshOffset.x+-frameWidth*0.5+frameWidth, meshOffset.y+frameHeight*0.5, 0.0, frame.coordinates.x+frame.coordinates.w, 1.0-frame.coordinates.y, 0.0, 0.0, 1.0); + + + for(int j=0; j < 4; j++) { + + Vector3 vertex(frameMesh->vertexPositionArray.data[j], frameMesh->vertexPositionArray.data[j+1], frameMesh->vertexPositionArray.data[j+2]); + + Number val = fabs(vertex.x); + if(val > largestFrameBoundingBox.x) { + largestFrameBoundingBox.x = val; + } + val = fabs(vertex.y); + if(val > largestFrameBoundingBox.y) { + largestFrameBoundingBox.y = val; + } + val = fabs(vertex.z); + if(val > largestFrameBoundingBox.z) { + largestFrameBoundingBox.z = val; + } + } + frameMesh->addIndexedFace(0,1); + frameMesh->addIndexedFace(1,2); + frameMesh->addIndexedFace(2,3); + frameMesh->addIndexedFace(3,0); + + frameMeshes.push_back(frameMesh); + } + + largestFrameBoundingBox = largestFrameBoundingBox * 2.0; +} + +String SpriteState::getName() const { + return name; +} + +unsigned int SpriteState::getNumFrameIDs() { + return frameIDs.size(); +} + +unsigned int SpriteState::getFrameIDAtIndex(unsigned int index) { + if(index < frameIDs.size()) { + return frameIDs[index]; + } else { + return 0; + } +} + +void SpriteState::appendFrames(std::vector newFrameIDs) { + frameIDs.insert( frameIDs.end(), newFrameIDs.begin(), newFrameIDs.end()); + rebuildStateMeshes(); +} + +unsigned int Sprite::getNumStates() { + return states.size(); +} + +SpriteState *Sprite::getState(unsigned int index) { + if(index < states.size()) { + return states[index]; + } else { + return NULL; + } +} + +SpriteState *Sprite::getStateByName(const String &name) { + for(int i=0; i < states.size(); i++) { + if(states[i]->getName() == name) { + return states[i]; + } + } + return NULL; +} + +void Sprite::addSpriteState(SpriteState *state) { + states.push_back(state); + dispatchEvent(new Event(), Event::CHANGE_EVENT); +} + +Sprite::Sprite(String name) : Resource(Resource::RESOURCE_SPRITE){ + this->name = name; + this->setResourceName(name); +} + +void Sprite::setParentSpritSet(SpriteSet *spriteSet) { + parentSpriteSet = spriteSet; +} + +SpriteSet *Sprite::getParentSpriteSet() { + return parentSpriteSet; +} + +void Sprite::removeSpriteState(SpriteState *state) { + for(int i=0; i < states.size(); i++) { + if(states[i] == state) { + states.erase(states.begin() + i); + dispatchEvent(new Event(), Event::CHANGE_EVENT); + return; + } + } +} + +Sprite::~Sprite() { + for(int i=0; i < states.size(); i++) { + delete states[i]; + } +} + +String Sprite::getName() { + return name; +} + +void Sprite::setName(String name) { + this->name = name; +} + + +SpriteSet::SpriteSet(const String &fileName, ResourcePool *parentPool) : ResourcePool(fileName, parentPool) { + nextFrameIDIndex = 0; + loadSpriteSet(fileName); +} + +void SpriteSet::loadSpriteSet(String fileName) { + Object loadObject; + if(!loadObject.loadFromBinary(fileName)) { + if(!loadObject.loadFromXML(fileName)) { + Logger::log("Error loading sprite sheet: %s.\n", fileName.c_str()); + return; + } + } + + ObjectEntry *spriteSheetEntry = loadObject.root["sprite_sheet"]; + if(spriteSheetEntry) { + ObjectEntry *fileNameEntry = (*spriteSheetEntry)["fileName"]; + if(fileNameEntry) { + loadTexture(fileNameEntry->stringVal); + } + + ObjectEntry *framesEntry = (*spriteSheetEntry)["frames"]; + if(framesEntry) { + for(int i=0; i < framesEntry->length; i++) { + ObjectEntry *frameEntry = (*framesEntry)[i]; + + if(frameEntry) { + SpriteFrame frame; + + ObjectEntry *idEntry = (*frameEntry)["id"]; + if(idEntry) { + frame.frameID = idEntry->intVal; + } + + ObjectEntry *xEntry = (*frameEntry)["x"]; + if(xEntry) { + frame.coordinates.x = xEntry->NumberVal; + } + ObjectEntry *yEntry = (*frameEntry)["y"]; + if(yEntry) { + frame.coordinates.y = yEntry->NumberVal; + } + ObjectEntry *wEntry = (*frameEntry)["w"]; + if(wEntry) { + frame.coordinates.w = wEntry->NumberVal; + } + ObjectEntry *hEntry = (*frameEntry)["h"]; + if(hEntry) { + frame.coordinates.h = hEntry->NumberVal; + } + ObjectEntry *axEntry = (*frameEntry)["ax"]; + if(axEntry) { + frame.anchorPoint.x = axEntry->NumberVal; + } + ObjectEntry *ayEntry = (*frameEntry)["ay"]; + if(ayEntry) { + frame.anchorPoint.y = ayEntry->NumberVal; + } + + addSpriteFrame(frame, false); + } + + } + } + + } else { + return; + } + + ObjectEntry *spritesEntry = loadObject.root["sprites"]; + if(spritesEntry) { + for(int i=0; i < spritesEntry->length; i++) { + ObjectEntry *spriteEntry = (*spritesEntry)[i]; + if(spriteEntry) { + ObjectEntry *nameEntry = (*spriteEntry)["name"]; + String spriteName; + if(nameEntry) { + spriteName = nameEntry->stringVal; + } + Sprite *newSprite = new Sprite(spriteName); + addSpriteEntry(newSprite); + + ObjectEntry *statesEntry = (*spriteEntry)["states"]; + + if(statesEntry) { + for(int j=0; j < statesEntry->length; j++) { + ObjectEntry *stateEntry = (*statesEntry)[j]; + if(stateEntry) { + SpriteState *newState = new SpriteState(this, ""); + + ObjectEntry *nameEntry = (*stateEntry)["name"]; + if(nameEntry) { + newState->setName(nameEntry->stringVal); + } + + ObjectEntry *fpsEntry = (*stateEntry)["fps"]; + if(fpsEntry) { + newState->setStateFPS(fpsEntry->NumberVal); + } + + ObjectEntry *scaleEntry = (*stateEntry)["scale"]; + if(scaleEntry) { + newState->setPixelsPerUnit(scaleEntry->NumberVal); + } + + ObjectEntry *widthEntry = (*stateEntry)["width"]; + ObjectEntry *heightEntry = (*stateEntry)["height"]; + if(widthEntry && heightEntry) { + newState->setBoundingBox(Vector2(widthEntry->NumberVal, heightEntry->NumberVal)); + } + + ObjectEntry *xOffsetEntry = (*stateEntry)["offset_x"]; + ObjectEntry *yOffsetEntry = (*stateEntry)["offset_y"]; + if(xOffsetEntry && yOffsetEntry) { + newState->setSpriteOffset(Vector2(xOffsetEntry->NumberVal, yOffsetEntry->NumberVal)); + } + + ObjectEntry *frameIDsEntry = (*stateEntry)["frame_ids"]; + + if(frameIDsEntry) { + std::vector frameIDs = frameIDsEntry->stringVal.split(","); + + std::vector frameIDInts; + for(int f=0; f < frameIDs.size(); f++) { + frameIDInts.push_back(frameIDs[f].toInteger()); + } + + newState->appendFrames(frameIDInts); + } + + newSprite->addSpriteState(newState); + } + } + } + } + } + } +} + +void SpriteSet::removeFrameByID(unsigned int frameID) { + for(int i=0; i < frames.size(); i++) { + if(frames[i].frameID == frameID) { + frames.erase(frames.begin() + i); + return; + } + } +} + +void SpriteSet::removeSprite(Sprite *sprite) { + for(int i=0; i < sprites.size(); i++) { + if(sprites[i] == sprite) { + removeResource(sprites[i]); + sprites.erase(sprites.begin()+i); + dispatchEvent(new Event(), Event::CHANGE_EVENT); + return; + } + } +} + +Sprite *SpriteSet::getSpriteByName(String spriteName) { + for(int i=0; i < sprites.size(); i++) { + if(sprites[i]->getName() == spriteName) { + return sprites[i]; + } + } + return NULL; +} + +Texture *SpriteSet::loadTexture(String imageFileName) { + Texture *spriteTexture = Services()->getMaterialManager()->createTextureFromFile(imageFileName, true, Services()->getMaterialManager()->mipmapsDefault); + setTexture(spriteTexture); + return spriteTexture; +} + +void SpriteSet::addSpriteFrame(const SpriteFrame &frame, bool assignID) { + + // do not add existing frames + for(int i=0; i < frames.size(); i++) { + SpriteFrame existingFrame = frames[i]; + + if(existingFrame.coordinates == frame.coordinates) { + return; + } + + } + + frames.push_back(frame); + if(assignID) { + frames[frames.size()-1].frameID = nextFrameIDIndex; + nextFrameIDIndex++; + } else { + nextFrameIDIndex = frame.frameID + 1; + } + +} + +void SpriteSet::setSpriteFrame(const SpriteFrame &frame) { + for(int i=0 ;i < frames.size(); i++) { + if(frames[i].frameID == frame.frameID) { + frames[i].coordinates = frame.coordinates; + frames[i].anchorPoint = frame.anchorPoint; + return; + } + } +} + +SpriteFrame SpriteSet::getSpriteFrameByID(unsigned int frameID) const { + for(int i=0; i < frames.size(); i++) { + if(frames[i].frameID == frameID ){ + return frames[i]; + } + } + return SpriteFrame(); +} + +unsigned int SpriteSet::getNumFrames() const { + return frames.size(); +} + +SpriteFrame SpriteSet::getSpriteFrame(unsigned int index) const { + if(index < frames.size()) { + return frames[index]; + } else { + return SpriteFrame(); + } +} + +void SpriteSet::addSpriteEntry(Sprite *newEntry) { + addResource(newEntry); + newEntry->setParentSpritSet(this); + sprites.push_back(newEntry); + dispatchEvent(new Event(), Event::CHANGE_EVENT); +} + +unsigned int SpriteSet::getNumSpriteEntries() const { + return sprites.size(); +} + +Sprite *SpriteSet::getSpriteEntry(unsigned int index) const { + if(index < sprites.size()) { + return sprites[index]; + } else { + return NULL; + } +} + +void SpriteSet::setTexture(Texture *texture) { + spriteTexture = texture; +} + +Texture *SpriteSet::getTexture() { + return spriteTexture; +} + +SpriteSet::~SpriteSet() { + +} + +void SpriteSet::clearFrames() { + frames.clear(); + nextFrameIDIndex = 0; +} + +void SpriteSet::createGridFrames(unsigned int xCount, unsigned int yCount, const Vector2 &defaultAnchor) { + + Number frameWidth = 1.0/(Number)xCount; + Number frameHeight = 1.0/(Number)yCount; + + for(int y = 0; y < yCount; y++) { + for(Number x = 0; x < xCount; x++) { + SpriteFrame frame; + frame.coordinates = Polycode::Rectangle(x * frameWidth, y * frameHeight, frameWidth, frameHeight); + frame.anchorPoint = defaultAnchor; + addSpriteFrame(frame); + } + } +} + +Polycode::Rectangle createBoxAtCoordinate(Image *image, unsigned int x, unsigned int y) { + Polycode::Rectangle rect; + + rect.x = x; + rect.y = y; + + while(x < image->getWidth()) { + if(image->getPixel(x, y).a == 0.0) { + break; + } + x++; + } + rect.w = x - rect.x; + + // look down at first x + Number y1 = y; + while(y1 < image->getHeight()) { + if(image->getPixel(rect.x, y1).a == 0.0) { + break; + } + y1++; + } + Number h1 = y1; + + // look down at last x + while(y < image->getHeight()) { + if(image->getPixel(x, y).a == 0.0) { + break; + } + y++; + } + Number h2 = y; + + if(h1 > h2) { + h2 = h1; + } + + rect.h = h2 - rect.y; + + + + return rect; +} + +bool rectIntersect(const Polycode::Rectangle &r1, const Polycode::Rectangle &r2, Number minDistance) { + return !(r2.x - minDistance > r1.x + r1.w || + r2.x + r2.w + minDistance < r1.x || + r2.y - minDistance > r1.y + r1.h || + r2.y + r2.h + minDistance < r1.y); +} + +void SpriteSet::createFramesFromIslands(unsigned int minDistance, const Vector2 &defaultAnchor) { + String imageFileName = getTexture()->getResourcePath(); + + Image *image = new Image(imageFileName); + + + std::vector rects; + + for(int y=0; y < image->getHeight(); y++) { + for(int x=0; x < image->getWidth(); x++) { + if(image->getPixel(x, y).a > 0.0) { + Polycode::Rectangle rect = createBoxAtCoordinate(image,x,y); + rects.push_back(rect); + x += rect.w; + } + } + } + + while(rects.size() > 1) { + + bool intersected = false; + for(int i=0; i < rects.size(); i++) { + for(int i2=0; i2 < rects.size(); i2++) { + if(i != i2) { + if(rectIntersect(rects[i], rects[i2], minDistance)) { + + Polycode::Rectangle newRect; + + newRect.x = std::min(rects[i].x, rects[i2].x); + newRect.y = std::min(rects[i].y, rects[i2].y); + + newRect.w = std::max(rects[i].x + rects[i].w, rects[i2].x + rects[i2].w) - newRect.x; + newRect.h = std::max(rects[i].y + rects[i].h, rects[i2].y + rects[i2].h) - newRect.y; + + rects[i] = newRect; + rects.erase(rects.begin() + i2); + + intersected = true; + + break; + } + } + } + } + + if(!intersected) { + break; + } + + } + + + for(int i=0; i < rects.size(); i++) { + SpriteFrame frame; + frame.coordinates = rects[i]; + + frame.coordinates.x = frame.coordinates.x / ((Number)image->getWidth()); + frame.coordinates.y = frame.coordinates.y / ((Number)image->getHeight()); + frame.coordinates.w = frame.coordinates.w / ((Number)image->getWidth()); + frame.coordinates.h = frame.coordinates.h / ((Number)image->getHeight()); + + frame.anchorPoint = defaultAnchor; + + addSpriteFrame(frame); + } + + delete image; +} diff --git a/Core/Contents/Source/PolyScreen.cpp b/Core/Contents/Source/PolyScreen.cpp deleted file mode 100755 index 45e73ed66..000000000 --- a/Core/Contents/Source/PolyScreen.cpp +++ /dev/null @@ -1,213 +0,0 @@ -/* - Copyright (C) 2011 by Ivan Safrin - - 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: - - The above copyright notice and this permission notice shall be included in - all copies or substantial portions of the Software. - - 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. -*/ - -#include "PolyScreen.h" -#include "PolyCoreServices.h" -#include "PolyInputEvent.h" -#include "PolyScreenManager.h" -#include "PolyResourceManager.h" -#include "PolyCore.h" -#include "PolyMaterial.h" -#include "PolyRenderer.h" -#include "PolyScreenEntity.h" -#include "PolyScreenEvent.h" -#include "PolyShader.h" -#include "PolyTexture.h" - -using namespace Polycode; - -Screen::Screen() : EventDispatcher() { - enabled = true; - focusChild = NULL; - originalSceneTexture = NULL; - CoreServices::getInstance()->getScreenManager()->addScreen(this); - filterShaderMaterial = NULL; - _hasFilterShader = false; - useNormalizedCoordinates = false; - processTouchEventsAsMouse = false; - ownsChildren = false; - - rootEntity.processInputEvents = true; - rootEntity.setPositionMode(ScreenEntity::POSITION_CENTER); - rootEntity.setRenderer(renderer); - rootEntity.setDefaultScreenOptions(false); -} - -Screen::~Screen() { - CoreServices::getInstance()->getScreenManager()->removeScreen(this); - - for(int i=0; i < localShaderOptions.size(); i++) { - delete localShaderOptions[i]; - } - delete originalSceneTexture; -} - -void Screen::setNormalizedCoordinates(bool newVal, Number yCoordinateSize) { - useNormalizedCoordinates = newVal; - this->yCoordinateSize = yCoordinateSize; -} - -void Screen::handleInputEvent(InputEvent *inputEvent) { - - switch(inputEvent->getEventCode()) { - - case InputEvent::EVENT_TOUCHES_BEGAN: - if(processTouchEventsAsMouse) { - for(int j=0; j < inputEvent->touches.size(); j++) { - rootEntity._onMouseDown(inputEvent->touches[j].position.x, inputEvent->touches[j].position.y, CoreInput::MOUSE_BUTTON1, inputEvent->timestamp); - } - } - break; - case InputEvent::EVENT_MOUSEDOWN: - rootEntity._onMouseDown(inputEvent->mousePosition.x, inputEvent->mousePosition.y, inputEvent->mouseButton, inputEvent->timestamp); - break; - case InputEvent::EVENT_MOUSEMOVE: - rootEntity._onMouseMove(inputEvent->mousePosition.x, inputEvent->mousePosition.y, inputEvent->timestamp); - break; - case InputEvent::EVENT_MOUSEUP: - rootEntity._onMouseUp(inputEvent->mousePosition.x, inputEvent->mousePosition.y, inputEvent->mouseButton, inputEvent->timestamp); - break; - case InputEvent::EVENT_MOUSEWHEEL_UP: - rootEntity._onMouseWheelUp(inputEvent->mousePosition.x, inputEvent->mousePosition.y, inputEvent->timestamp); - break; - case InputEvent::EVENT_MOUSEWHEEL_DOWN: - rootEntity._onMouseWheelDown(inputEvent->mousePosition.x, inputEvent->mousePosition.y,inputEvent->timestamp); - break; - case InputEvent::EVENT_KEYDOWN: - rootEntity._onKeyDown(inputEvent->key, inputEvent->charCode); - break; - case InputEvent::EVENT_KEYUP: - rootEntity._onKeyUp(inputEvent->key, inputEvent->charCode); - break; - } -} - -void Screen::setRenderer(Renderer *renderer) { - this->renderer = renderer; -} - -void Screen::setScreenShader(const String& shaderName) { - filterShaderMaterial = (Material*)CoreServices::getInstance()->getResourceManager()->getResource(Resource::RESOURCE_MATERIAL, shaderName); - if(!filterShaderMaterial) - return; - - if(filterShaderMaterial->getNumShaders() == 0) - return; - - if(!originalSceneTexture) { - CoreServices::getInstance()->getRenderer()->createRenderTextures(&originalSceneTexture, NULL, CoreServices::getInstance()->getCore()->getXRes(), CoreServices::getInstance()->getCore()->getYRes(), filterShaderMaterial->fp16RenderTargets); - } - - for(int i=0; i < filterShaderMaterial->getNumShaders(); i++) { - ShaderBinding* binding = filterShaderMaterial->getShader(i)->createBinding(); - localShaderOptions.push_back(binding); - } - _hasFilterShader = true; - -} - -void Screen::clearScreenShader() { - if(_hasFilterShader) { - _hasFilterShader = false; - filterShaderMaterial = NULL; - } -} - - -void Screen::drawFilter() { - - if(!filterShaderMaterial) - return; - - Renderer *renderer = CoreServices::getInstance()->getRenderer(); - - renderer->bindFrameBufferTexture(originalSceneTexture); - - Render(); - renderer->unbindFramebuffers(); - - ShaderBinding* materialBinding; - for(int i=0; i < filterShaderMaterial->getNumShaders(); i++) { - materialBinding = filterShaderMaterial->getShaderBinding(i); - - for(int j=0; j < materialBinding->getNumColorTargetBindings(); j++) { - RenderTargetBinding *colorBinding = materialBinding->getColorTargetBinding(j); - materialBinding->clearTexture(colorBinding->name); - materialBinding->addTexture(colorBinding->name, originalSceneTexture); - } - - renderer->applyMaterial(filterShaderMaterial, localShaderOptions[i], i); - - if(i==filterShaderMaterial->getNumShaders()-1) { - renderer->loadIdentity(); - renderer->drawScreenQuad(renderer->getXRes(), renderer->getYRes()); - } else { - for(int j=0; j < materialBinding->getNumOutTargetBindings(); j++) { - Texture *bindingTexture = materialBinding->getOutTargetBinding(j)->texture; - renderer->bindFrameBufferTexture(bindingTexture); - - renderer->drawScreenQuad(bindingTexture->getWidth(), bindingTexture->getHeight()); - renderer->unbindFramebuffers(); - } - } - renderer->clearShader(); - renderer->loadIdentity(); - renderer->setOrthoMode(); - } - -} - -void Screen::setScreenOffset(Number x, Number y) { - offset.x = x; - offset.y = y; -} - -bool Screen::hasFilterShader() const { - return _hasFilterShader; -} - -void Screen::addEntity(ScreenEntity *newEntity) { - rootEntity.addChild(newEntity); -} - -void Screen::addChild(ScreenEntity *newEntity) { - addEntity(newEntity); -} - -void Screen::removeChild(ScreenEntity *entityToRemove) { - rootEntity.removeChild(entityToRemove); -} - -void Screen::Shutdown() { - -} - -void Screen::Update() { - rootEntity.doUpdates(); -} - -void Screen::Render() { - renderer->loadIdentity(); - renderer->translate2D(offset.x, offset.y); - rootEntity.updateEntityMatrix(); - rootEntity.transformAndRender(); -} diff --git a/Core/Contents/Source/PolyScreenEntity.cpp b/Core/Contents/Source/PolyScreenEntity.cpp deleted file mode 100755 index b233b8fe1..000000000 --- a/Core/Contents/Source/PolyScreenEntity.cpp +++ /dev/null @@ -1,653 +0,0 @@ -/* - Copyright (C) 2011 by Ivan Safrin - - 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: - - The above copyright notice and this permission notice shall be included in - all copies or substantial portions of the Software. - - 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. -*/ - -#include "PolyScreenEntity.h" -#include "PolyInputEvent.h" -#include "PolyRectangle.h" -#include "PolyPolygon.h" -#include "PolyVertex.h" -#include "PolyRenderer.h" -#include "PolyCoreServices.h" -#include "PolyLogger.h" - -inline double round(double x) { return floor(x + 0.5); } - -using namespace Polycode; - -ScreenEntity::ScreenEntity() : Entity() { - color = Color(1.0f,1.0f,1.0f,1.0f); - width = 0; - height = 0; - setHitbox(0, 0); - backfaceCulled = false; - positionMode = POSITION_TOPLEFT; - mouseOver = false; - dragged = false; - - dragOffsetX = 0; - dragOffsetY = 0; - parentEntity = NULL; - - depthWrite = false; - depthTest = false; - - focusable = false; - hasFocus = false; - focusChildren = false; - blockMouseInput = false; - - snapToPixels = false; - - lastClickTicks = 0; - dragLimits = NULL; - - xmouse = 0; - ymouse = 0; - - processInputEvents = false; - -} - -Entity *ScreenEntity::Clone(bool deepClone, bool ignoreEditorOnly) const { - ScreenEntity *newEntity = new ScreenEntity(); - applyClone(newEntity, deepClone, ignoreEditorOnly); - return newEntity; -} - -void ScreenEntity::applyClone(Entity *clone, bool deepClone, bool ignoreEditorOnly) const { - Entity::applyClone(clone, deepClone, ignoreEditorOnly); - - ScreenEntity *_clone = (ScreenEntity*) clone; - _clone->width = width; - _clone->height = height; - _clone->setHitbox(hit.x, hit.y, hit.w, hit.h); - _clone->positionMode = positionMode; - _clone->focusable = focusable; - _clone->blockMouseInput = blockMouseInput; - _clone->snapToPixels = snapToPixels; - _clone->processInputEvents = processInputEvents; - -} - -void ScreenEntity::addEntity(Entity *newChild) { - ((ScreenEntity*)newChild)->setDefaultScreenOptions(snapToPixels); - Entity::addEntity(newChild); -} - -void ScreenEntity::setDefaultScreenOptions(bool snapToPixels) { - this->snapToPixels = snapToPixels; - for(int i=0; i < children.size(); i++) { - ((ScreenEntity*)children[i])->setDefaultScreenOptions(snapToPixels); - } -} - -void ScreenEntity::focusNextChild() { - int j = 0; - bool hasFocusedChild = false; - if(CoreServices::getInstance()->focusedChild) { - for(int i=0; i < children.size(); i++) { - if(children[i] == CoreServices::getInstance()->focusedChild) { - j = i; - hasFocusedChild = true; - } - } - } - - if(!hasFocusedChild) - return; - - for(int i=0; i < children.size(); i++) { - if(((ScreenEntity*)children[j])->isFocusable() && children[j] != CoreServices::getInstance()->focusedChild) { - focusChild(((ScreenEntity*)children[j])); - return; - } - - j++; - if(j == children.size()) - j = 0; - } -} - -Number ScreenEntity::getRotation() const { - return this->getRoll(); -} - -void ScreenEntity::focusChild(ScreenEntity *child) { - if(CoreServices::getInstance()->focusedChild != NULL) { - ((ScreenEntity*)CoreServices::getInstance()->focusedChild)->onLoseFocus(); - ((ScreenEntity*)CoreServices::getInstance()->focusedChild)->hasFocus = false; - } - CoreServices::getInstance()->focusedChild = child; - if(child) { - ((ScreenEntity*)CoreServices::getInstance()->focusedChild)->hasFocus = true; - ((ScreenEntity*)CoreServices::getInstance()->focusedChild)->onGainFocus(); - } -} - -bool ScreenEntity::isFocusable() const { - return focusable; -} - -void ScreenEntity::startDrag(Number xOffset, Number yOffset) { - dragged = true; - dragOffsetX = xOffset; - dragOffsetY = yOffset; -} - -void ScreenEntity::stopDrag() { - dragged = false; -} - -ScreenEntity::~ScreenEntity() { - if(CoreServices::getInstance()->focusedChild == this) { - CoreServices::getInstance()->focusedChild = NULL; - } - if(dragLimits) delete dragLimits; -} - -void ScreenEntity::setBlendingMode(int newBlendingMode) { - blendingMode = newBlendingMode; -} - -void ScreenEntity::setPosition(Number x, Number y) { - position.x = x; - position.y = y; - matrixDirty = true; -} - -void ScreenEntity::setPosition(const Vector2 &v) { - position.x = v.x; - position.y = v.y; - matrixDirty = true; -} - -void ScreenEntity::setScale(Number x, Number y) { - scale.x = x; - scale.y = y; - matrixDirty = true; -} - -void ScreenEntity::setScale(const Vector2 &v) { - scale.x = v.x; - scale.y = v.y; - matrixDirty = true; -} - - -Number ScreenEntity::getWidth() const { - return width; -} - -Number ScreenEntity::getHeight() const { - return height; -} - -bool isPointInsidePolygon2D(Polycode::Polygon *poly, const Vector2 &p) { - Number angle = 0.0; - Vector2 p1,p2; -/* - printf("PIP: %f,%f in [%f,%f], [%f,%f], [%f,%f], [%f,%f]\n", p.x, p.y, - poly->getVertex(0)->x, poly->getVertex(0)->y, - poly->getVertex(1)->x, poly->getVertex(1)->y, - poly->getVertex(2)->x, poly->getVertex(2)->y, - poly->getVertex(3)->x, poly->getVertex(3)->y); -*/ - for (int i=0; i < poly->getVertexCount(); i++) { - - p1.x = poly->getVertex(i)->x - p.x; - p1.y = poly->getVertex(i)->y - p.y; - p2.x = poly->getVertex((i+1)%poly->getVertexCount())->x - p.x; - p2.y = poly->getVertex((i+1)%poly->getVertexCount())->y - p.y; - - Vector2 vec(p1.x,p1.y); - angle += vec.angle(Vector2(p2.x,p2.y)); - - } - - if (fabs(angle) < PI) - return false; - else - return true; -} - - -bool ScreenEntity::hitTest(const Number x, const Number y) const { - - Vector3 v; - Polygon testPoly; - - // matrix will give the center of the entity - Matrix4 screenMatrix = getScreenConcatenatedMatrix(); - if(positionMode == POSITION_TOPLEFT) { - // Translate hitbox so it matches the visible object bounds - // This is a bit of a hack because ScreenEntities are expected - // to rotate about their center and not their center point. - Matrix4 retMatrix; - retMatrix.setPosition(width/2.0, height/2.0, 0.0); - screenMatrix = screenMatrix * retMatrix; - } - - v = Vector3(hit.x, hit.y, 0); - v = screenMatrix * v; - testPoly.addVertex(v.x, v.y, 0.0); - - v = Vector3(hit.x+hit.w, hit.y, 0); - v = screenMatrix * v; - testPoly.addVertex(v.x, v.y, 0.0); - - v = Vector3(hit.x+hit.w, hit.y+hit.h, 0); - v = screenMatrix * v; - testPoly.addVertex(v.x, v.y, 0.0); - - v = Vector3(hit.x,hit.y+hit.h, 0); - v = screenMatrix * v; - testPoly.addVertex(v.x, v.y, 0.0); - - return isPointInsidePolygon2D(&testPoly, Vector2(x,y)); -} - -bool ScreenEntity::hitTest(Vector2 v) const -{ - return hitTest(v.x, v.y); -} - -void ScreenEntity::setPositionMode(int newPositionMode) { - positionMode = newPositionMode; -} - -void ScreenEntity::_onKeyDown(PolyKEY key, wchar_t charCode) { - onKeyDown(key, charCode); - for(int i=0;i_onKeyDown(key, charCode); - } -} - -void ScreenEntity::_onKeyUp(PolyKEY key, wchar_t charCode) { - onKeyUp(key, charCode); - for(int i=0;i_onKeyUp(key, charCode); - } -} - -int ScreenEntity::getPositionMode() const { - return positionMode; -} - -void ScreenEntity::setDragLimits(Polycode::Rectangle rect) { - if(!dragLimits) - dragLimits = new Polycode::Rectangle(); - dragLimits->x = rect.x; - dragLimits->y = rect.y; - dragLimits->w = rect.w; - dragLimits->h = rect.h; -} - -void ScreenEntity::clearDragLimits() { - delete dragLimits; - dragLimits = NULL; -} - -Rectangle ScreenEntity::getHitbox() const { - return hit; -} - -void ScreenEntity::setHitbox(Number width, Number height) { - hit.w = width; - hit.h = height; - hit.x = -width/2; - hit.y = -height/2; -} -void ScreenEntity::setHitbox(Number width, Number height, Number left, Number top) { - hit.w = width; - hit.h = height; - hit.x = left; - hit.y = top; -} - -bool ScreenEntity::isDragged() { - return dragged; -} - -Matrix4 ScreenEntity::getScreenConcatenatedMatrix() const { - Matrix4 retMatrix = transformMatrix; - if(positionMode == POSITION_TOPLEFT) { - retMatrix.setPosition(position.x, position.y, position.z); - } - - if(parentEntity) { - return retMatrix * ((ScreenEntity*)parentEntity)->getScreenConcatenatedMatrix(); - } else { - return retMatrix; - } -} - -MouseEventResult ScreenEntity::_onMouseMove(Number x, Number y, int timestamp) { - - if(dragged) { - Vector3 localCoordinate = Vector3(x,y,0); - - if(parentEntity) { - Matrix4 inverse = ((ScreenEntity*)parentEntity)->getScreenConcatenatedMatrix().Inverse(); - localCoordinate = inverse * localCoordinate; - } - - setPosition(localCoordinate.x-dragOffsetX,localCoordinate.y-dragOffsetY); - if(dragLimits) { - if(position.x < dragLimits->x) - position.x = dragLimits->x; - if(position.x > dragLimits->x + dragLimits->w) - position.x = dragLimits->x + dragLimits->w; - if(position.y < dragLimits->y) - position.y = dragLimits->y; - if(position.y > dragLimits->y + dragLimits->h) - position.y = dragLimits->y + dragLimits->h; - } - } - - MouseEventResult ret; - ret.hit = false; - ret.blocked = false; - - if(processInputEvents && enabled) { - if(hitTest(x,y)) { - - Vector3 localCoordinate = Vector3(x,y,0); - Matrix4 inverse = getScreenConcatenatedMatrix().Inverse(); - localCoordinate = inverse * localCoordinate; - - onMouseMove(localCoordinate.x,localCoordinate.y); - xmouse = localCoordinate.x; - ymouse = localCoordinate.y; - - dispatchEvent(new InputEvent(Vector2(localCoordinate.x,localCoordinate.y), timestamp), InputEvent::EVENT_MOUSEMOVE); - - if(!mouseOver) { - dispatchEvent(new InputEvent(Vector2(localCoordinate.x,localCoordinate.y), timestamp), InputEvent::EVENT_MOUSEOVER); - mouseOver = true; - } - ret.hit = true; - if(blockMouseInput) { - ret.blocked = true; - } - - } else { - if(mouseOver) { - - Vector3 localCoordinate = Vector3(x,y,0); - Matrix4 inverse = getScreenConcatenatedMatrix().Inverse(); - localCoordinate = inverse * localCoordinate; - - dispatchEvent(new InputEvent(Vector2(localCoordinate.x,localCoordinate.y), timestamp), InputEvent::EVENT_MOUSEOUT); - mouseOver = false; - } - } - - for(int i=children.size()-1;i>=0;i--) { - MouseEventResult childRes = ((ScreenEntity*)children[i])->_onMouseMove(x,y, timestamp); - if(childRes.hit) - ret.hit = true; - if(childRes.blocked) { - ret.blocked = true; - break; - } - } - } - - return ret; -} - -MouseEventResult ScreenEntity::_onMouseUp(Number x, Number y, int mouseButton, int timestamp) { - MouseEventResult ret; - ret.hit = false; - ret.blocked = false; - - if(processInputEvents && enabled) { - if(hitTest(x,y)) { - Vector3 localCoordinate = Vector3(x,y,0); - - Matrix4 inverse = getScreenConcatenatedMatrix().Inverse(); - localCoordinate = inverse * localCoordinate; - - onMouseUp(localCoordinate.x,localCoordinate.y); - InputEvent *inputEvent = new InputEvent(Vector2(localCoordinate.x,localCoordinate.y), timestamp); - inputEvent->mouseButton = mouseButton; - dispatchEvent(inputEvent, InputEvent::EVENT_MOUSEUP); - - ret.hit = true; - if(blockMouseInput) { - ret.blocked = true; - } - - } else { - Vector3 localCoordinate = Vector3(x,y,0); - - Matrix4 inverse = getScreenConcatenatedMatrix().Inverse(); - localCoordinate = inverse * localCoordinate; - - InputEvent *inputEvent = new InputEvent(Vector2(localCoordinate.x,localCoordinate.y), timestamp); - inputEvent->mouseButton = mouseButton; - dispatchEvent(inputEvent, InputEvent::EVENT_MOUSEUP_OUTSIDE); - } - - for(int i=children.size()-1;i>=0;i--) { - MouseEventResult childRes = ((ScreenEntity*)children[i])->_onMouseUp(x,y, mouseButton, timestamp); - if(childRes.hit) - ret.hit = true; - if(childRes.blocked) { - ret.blocked = true; - break; - } - - } - } - - return ret; -} - -MouseEventResult ScreenEntity::_onMouseWheelUp(Number x, Number y, int timestamp) { - - MouseEventResult ret; - ret.hit = false; - ret.blocked = false; - - if(processInputEvents && enabled) { - if(hitTest(x,y)) { - Vector3 localCoordinate = Vector3(x,y,0); - - Matrix4 inverse = getScreenConcatenatedMatrix().Inverse(); - localCoordinate = inverse * localCoordinate; - - onMouseWheelUp(localCoordinate.x,localCoordinate.y); - - InputEvent *inputEvent = new InputEvent(Vector2(localCoordinate.x,localCoordinate.y), timestamp); - dispatchEvent(inputEvent, InputEvent::EVENT_MOUSEWHEEL_UP); - - ret.hit = true; - if(blockMouseInput) { - ret.blocked = true; - } - - } - - for(int i=children.size()-1;i>=0;i--) { - MouseEventResult childRes = ((ScreenEntity*)children[i])->_onMouseWheelUp(x,y, timestamp); - if(childRes.hit) - ret.hit = true; - if(childRes.blocked) { - ret.blocked = true; - break; - } - } - } - return ret; -} - -MouseEventResult ScreenEntity::_onMouseWheelDown(Number x, Number y, int timestamp) { - MouseEventResult ret; - ret.hit = false; - ret.blocked = false; - - if(processInputEvents && enabled) { - if(hitTest(x,y)) { - Vector3 localCoordinate = Vector3(x,y,0); - - Matrix4 inverse = getScreenConcatenatedMatrix().Inverse(); - localCoordinate = inverse * localCoordinate; - - onMouseWheelDown(localCoordinate.x,localCoordinate.y); - - InputEvent *inputEvent = new InputEvent(Vector2(localCoordinate.x,localCoordinate.y), timestamp); - dispatchEvent(inputEvent, InputEvent::EVENT_MOUSEWHEEL_DOWN); - - ret.hit = true; - if(blockMouseInput) { - ret.blocked = true; - } - } - - for(int i=children.size()-1;i>=0;i--) { - MouseEventResult childRes = ((ScreenEntity*)children[i])->_onMouseWheelDown(x,y, timestamp); - if(childRes.hit) - ret.hit = true; - if(childRes.blocked) { - ret.blocked = true; - break; - } - - } - } - - return ret; -} - -MouseEventResult ScreenEntity::_onMouseDown(Number x, Number y, int mouseButton, int timestamp) { - MouseEventResult ret; - ret.hit = false; - ret.blocked = false; - - if(processInputEvents && enabled) { - if(hitTest(x,y)) { - Vector3 localCoordinate = Vector3(x,y,0); - - Matrix4 inverse = getScreenConcatenatedMatrix().Inverse(); - localCoordinate = inverse * localCoordinate; - - onMouseDown(localCoordinate.x,localCoordinate.y); - - InputEvent *inputEvent = new InputEvent(Vector2(localCoordinate.x,localCoordinate.y), timestamp); - - inputEvent->mouseButton = mouseButton; - dispatchEvent(inputEvent, InputEvent::EVENT_MOUSEDOWN); - - if(timestamp - lastClickTicks < 400) { - InputEvent *inputEvent = new InputEvent(Vector2(x,y), timestamp); - inputEvent->mouseButton = mouseButton; - dispatchEvent(inputEvent, InputEvent::EVENT_DOUBLECLICK); - } - lastClickTicks = timestamp; - ret.hit = true; - if(blockMouseInput) { - ret.blocked = true; - } - } - - for(int i=children.size()-1;i>=0;i--) { - MouseEventResult childRes = ((ScreenEntity*)children[i])->_onMouseDown(x,y, mouseButton, timestamp); - if(childRes.hit) - ret.hit = true; - if(childRes.blocked) { - ret.blocked = true; - break; - } - } - } - - return ret; -} - -Vector2 ScreenEntity::getScreenPosition() const { - Matrix4 screenTransform = getScreenConcatenatedMatrix(); - Vector3 screenPosition = screenTransform.getPosition(); - return Vector2(screenPosition.x, screenPosition.y); -} - -void ScreenEntity::setRotation(Number rotation) { - setRoll(rotation); -} - -Vector2 ScreenEntity::getPosition2D() const { - return Vector2(position.x, position.y); -} - -Vector2 ScreenEntity::getScale2D() const { - return Vector2(scale.x, scale.y); -} - -Matrix4 ScreenEntity::buildPositionMatrix() { - - Matrix4 posMatrix; - switch(positionMode) { - case POSITION_TOPLEFT: - posMatrix.m[3][0] = (position.x+floor(width/2.0f)*scale.x)*matrixAdj; - posMatrix.m[3][1] = (position.y+floor(height/2.0f)*scale.y)*matrixAdj; - posMatrix.m[3][2] = position.z*matrixAdj; - break; - case POSITION_CENTER: - posMatrix.m[3][0] = position.x*matrixAdj; - posMatrix.m[3][1] = position.y*matrixAdj; - posMatrix.m[3][2] = position.z*matrixAdj; - break; - } - - - if(snapToPixels) { - posMatrix.m[3][0] = round(posMatrix.m[3][0]); - posMatrix.m[3][1] = round(posMatrix.m[3][1]); - posMatrix.m[3][2] = round(posMatrix.m[3][2]); - } - - return posMatrix; -} - -void ScreenEntity::adjustMatrixForChildren() { - if(positionMode == POSITION_TOPLEFT) { - if(snapToPixels) { - renderer->translate2D(-floor(width/2.0f), -floor(height/2.0f)); - } else { - renderer->translate2D(-width/2.0f, -height/2.0f); - } - } -} - -ScreenEntity *ScreenEntity::getScreenEntityById(String id, bool recursive) const { - return (ScreenEntity*)getEntityById(id, recursive); -} - -std::vector ScreenEntity::getScreenEntitiesByTag(String tag, bool recursive) const { - std::vector entities = getEntitiesByTag(tag, recursive); - std::vector retEntities; - for(int i=0; i < entities.size(); i++) { - retEntities.push_back((ScreenEntity*)entities[i]); - } - return retEntities; -} - diff --git a/Core/Contents/Source/PolyScreenEntityInstance.cpp b/Core/Contents/Source/PolyScreenEntityInstance.cpp deleted file mode 100755 index 638e80cc5..000000000 --- a/Core/Contents/Source/PolyScreenEntityInstance.cpp +++ /dev/null @@ -1,398 +0,0 @@ -/* - Copyright (C) 2011 by Ivan Safrin - - 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: - - The above copyright notice and this permission notice shall be included in - all copies or substantial portions of the Software. - - 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. -*/ - -#include "PolyScreenEntityInstance.h" -#include "PolyLogger.h" -#include "PolyCoreServices.h" -#include "PolyResourceManager.h" - -using namespace Polycode; - -ScreenEntityInstanceResourceEntry::ScreenEntityInstanceResourceEntry(ScreenEntityInstance *instance) : Resource(Resource::RESOURCE_SCREEN_ENTITY_INSTANCE) { - this->instance = instance; -} - -ScreenEntityInstanceResourceEntry::~ScreenEntityInstanceResourceEntry() { - -} - -ScreenEntityInstance *ScreenEntityInstanceResourceEntry::getInstance() { - return instance; -} - -void ScreenEntityInstanceResourceEntry::reloadResource() { - instance->reloadEntityInstance(); - Resource::reloadResource(); -} - -ScreenEntityInstance *ScreenEntityInstance::BlankScreenEntityInstance() { - return new ScreenEntityInstance(); -} - -ScreenEntityInstance::ScreenEntityInstance(const String& fileName) : ScreenEntity() { - setPositionMode(ScreenEntity::POSITION_CENTER); - resourceEntry = new ScreenEntityInstanceResourceEntry(this); - loadFromFile(fileName); - resourceEntry->setResourceName(fileName); - resourceEntry->setResourcePath(fileName); - cloneUsingReload = false; - ownsChildren = true; -} - -ScreenEntityInstance::ScreenEntityInstance() : ScreenEntity() { - cloneUsingReload = true; - ownsChildren = true; - resourceEntry = new ScreenEntityInstanceResourceEntry(this); -} - -ScreenEntityInstance::~ScreenEntityInstance() { - CoreServices::getInstance()->getResourceManager()->removeResource(resourceEntry); - delete resourceEntry; -} - -void ScreenEntityInstance::reloadEntityInstance() { - loadFromFile(fileName); -} - -ScreenEntityInstanceResourceEntry *ScreenEntityInstance::getResourceEntry() { - return resourceEntry; -} - -Entity *ScreenEntityInstance::Clone(bool deepClone, bool ignoreEditorOnly) const { - ScreenEntityInstance *newEntity; - if(cloneUsingReload) { - newEntity = new ScreenEntityInstance(fileName); - } else { - newEntity = new ScreenEntityInstance(); - } - applyClone(newEntity, deepClone, ignoreEditorOnly); - return newEntity; -} - -void ScreenEntityInstance::applyClone(Entity *clone, bool deepClone, bool ignoreEditorOnly) const { - if(cloneUsingReload) { - ScreenEntity::applyClone(clone, false, ignoreEditorOnly); - } else { - ScreenEntity::applyClone(clone, deepClone, ignoreEditorOnly); - ScreenEntityInstance *_clone = (ScreenEntityInstance*) clone; - _clone->fileName = fileName; - } -} - -void ScreenEntityInstance::applyScreenShape(ObjectEntry *entry, ScreenShape *shape) { - if(!entry) - return; - - Number swidth = (*entry)["width"]->NumberVal; - Number sheight = (*entry)["height"]->NumberVal; - int type = (*entry)["type"]->intVal; - - shape->setShapeType(type); - shape->setShapeSize(swidth, sheight); - - Number strokeColorR = (*entry)["strokeColorR"]->NumberVal; - Number strokeColorG = (*entry)["strokeColorG"]->NumberVal; - Number strokeColorB = (*entry)["strokeColorB"]->NumberVal; - Number strokeColorA = (*entry)["strokeColorA"]->NumberVal; - - bool strokeEnabled = (*entry)["strokeEnabled"]->boolVal; - Number strokeWidth = (*entry)["strokeWidth"]->NumberVal; - - shape->strokeEnabled = strokeEnabled; - shape->strokeWidth = strokeWidth; - shape->strokeColor = Color(strokeColorR, strokeColorG, strokeColorB, strokeColorA); - - -} - -void ScreenEntityInstance::parseObjectIntoCurve(ObjectEntry *entry, BezierCurve *curve) { - curve->clearControlPoints(); - ObjectEntry *controlPoints =(*entry)["controlPoints"]; - if(controlPoints) { - for(int i=0; i < controlPoints->length; i++) { - ObjectEntry *controlPoint = ((*controlPoints))[i]; - if(controlPoint) { - Vector2 vpt1; - Vector2 vpt2; - Vector2 vpt3; - - ObjectEntry *pt1 = ((*controlPoint))["pt1"]; - if(pt1) { - vpt1.x = ((*pt1))["x"]->NumberVal; - vpt1.y = ((*pt1))["y"]->NumberVal; - } - - ObjectEntry *pt2 = ((*controlPoint))["pt2"]; - if(pt2) { - vpt2.x = ((*pt2))["x"]->NumberVal; - vpt2.y = ((*pt2))["y"]->NumberVal; - } - - ObjectEntry *pt3 = ((*controlPoint))["pt3"]; - if(pt3) { - vpt3.x = ((*pt3))["x"]->NumberVal; - vpt3.y = ((*pt3))["y"]->NumberVal; - } - - curve->addControlPoint(vpt1.x, vpt1.y, 0.0, vpt2.x, vpt2.y, 0.0, vpt3.x, vpt3.y, 0.0); - } - } - } - -} - -ScreenEntity *ScreenEntityInstance::loadObjectEntryIntoEntity(ObjectEntry *entry, ScreenEntity *targetEntity) { - - ScreenEntity *entity = NULL; - - ObjectEntry *entityType = (*entry)["type"]; - if(entityType) { - - if(entityType->stringVal == "ScreenImage") { - ObjectEntry *screenImageEntry = (*entry)["ScreenImage"]; - String imagePath = (*screenImageEntry)["filePath"]->stringVal; - ScreenImage *image = new ScreenImage(imagePath); - - ObjectEntry *screenShapeEntry = (*entry)["ScreenShape"]; - applyScreenShape(screenShapeEntry, image); - entity = image; - } - - if(entityType->stringVal == "ScreenParticleEmitter") { - ObjectEntry *emitterEntry = (*entry)["ScreenParticleEmitter"]; - - ScreenParticleEmitter *placingEmitter = new ScreenParticleEmitter((*emitterEntry)["texturePath"]->stringVal, Particle::BILLBOARD_PARTICLE, ParticleEmitter::CONTINUOUS_EMITTER, (*emitterEntry)["lifespan"]->NumberVal, (*emitterEntry)["particleCount"]->NumberVal, Vector3((*emitterEntry)["dirX"]->NumberVal, (*emitterEntry)["dirY"]->NumberVal, 0.0), Vector3((*emitterEntry)["gravX"]->NumberVal, (*emitterEntry)["gravY"]->NumberVal, 0.0), Vector3((*emitterEntry)["deviationX"]->NumberVal, (*emitterEntry)["deviationY"]->NumberVal, 0.0), Vector3((*emitterEntry)["radiusX"]->NumberVal, (*emitterEntry)["radiusY"]->NumberVal, 0.0)); - - placingEmitter->brightnessDeviation = (*emitterEntry)["brightnessDeviation"]->NumberVal; - placingEmitter->particleSize = (*emitterEntry)["particleSize"]->NumberVal; - placingEmitter->perlinModSize = (*emitterEntry)["perlinModSize"]->NumberVal; - placingEmitter->perlinEnabled = (*emitterEntry)["perlinEnabled"]->boolVal; - placingEmitter->particleSpeedMod = (*emitterEntry)["particleSpeedMod"]->NumberVal; - - placingEmitter->rotationSpeed = (*emitterEntry)["rotationSpeed"]->NumberVal; - placingEmitter->rotationFollowsPath = (*emitterEntry)["rotationFollowsPath"]->boolVal; - placingEmitter->useScaleCurves = (*emitterEntry)["useScaleCurves"]->boolVal; - placingEmitter->useColorCurves = (*emitterEntry)["useColorCurves"]->boolVal; - - bool boolVal; - if(emitterEntry->readBool("ignoreParentMatrix", &boolVal)) { - placingEmitter->setIgnoreParentMatrix(boolVal); - } - - placingEmitter->setParticleBlendingMode((*emitterEntry)["particleBlendMode"]->intVal); - - placingEmitter->setWidth(placingEmitter->emitterRadius.x); - placingEmitter->setHeight(placingEmitter->emitterRadius.y); - - parseObjectIntoCurve((*emitterEntry)["scaleCurve"], &placingEmitter->scaleCurve); - parseObjectIntoCurve((*emitterEntry)["colorCurveR"], &placingEmitter->colorCurveR); - parseObjectIntoCurve((*emitterEntry)["colorCurveG"], &placingEmitter->colorCurveG); - parseObjectIntoCurve((*emitterEntry)["colorCurveB"], &placingEmitter->colorCurveB); - parseObjectIntoCurve((*emitterEntry)["colorCurveA"], &placingEmitter->colorCurveA); - - entity = placingEmitter; - - } - - if(entityType->stringVal == "ScreenSprite") { - ObjectEntry *screenSpriteEntry = (*entry)["ScreenSprite"]; - String filePath = (*screenSpriteEntry)["filePath"]->stringVal; - - ScreenSprite *sprite = new ScreenSprite(filePath); - - String animName = (*screenSpriteEntry)["anim"]->stringVal; - sprite->playAnimation(animName, -1, false); - - - ObjectEntry *screenShapeEntry = (*entry)["ScreenShape"]; - applyScreenShape(screenShapeEntry, sprite); - entity = sprite; - } - - - if(entityType->stringVal == "ScreenEntityInstance") { - ObjectEntry *screenInstanceEntry = (*entry)["ScreenEntityInstance"]; - String filePath = (*screenInstanceEntry)["filePath"]->stringVal; - ScreenEntityInstance *instance = new ScreenEntityInstance(filePath); - entity = instance; - } - - - if(entityType->stringVal == "ScreenShape") { - ObjectEntry *screenShapeEntry = (*entry)["ScreenShape"]; - ScreenShape *shape = new ScreenShape(0, 1, 1); - applyScreenShape(screenShapeEntry, shape); - entity = shape; - } - - if(entityType->stringVal == "ScreenSound") { - ObjectEntry *screenSoundEntry = (*entry)["ScreenSound"]; - - String filePath = (*screenSoundEntry)["filePath"]->stringVal; - Number refDistance = (*screenSoundEntry)["refDistance"]->NumberVal; - Number maxDistance = (*screenSoundEntry)["maxDistance"]->NumberVal; - Number volume = (*screenSoundEntry)["volume"]->NumberVal; - Number pitch = (*screenSoundEntry)["pitch"]->NumberVal; - - ScreenSound *sound = new ScreenSound(filePath, refDistance, maxDistance); - sound->getSound()->setVolume(volume); - sound->getSound()->setPitch(pitch); - - sound->setWidth(50); - sound->setHeight(50); - - entity = sound; - } - - - if(entityType->stringVal == "ScreenLabel") { - ObjectEntry *screenLabelEntry = (*entry)["ScreenLabel"]; - - String text = (*screenLabelEntry)["text"]->stringVal; - String font = (*screenLabelEntry)["font"]->stringVal; - int size = (*screenLabelEntry)["size"]->intVal; - int aaMode = (*screenLabelEntry)["aaMode"]->intVal; - - ScreenLabel *label = new ScreenLabel(text, size, font, aaMode); - label->positionAtBaseline = false; - - ObjectEntry *screenShapeEntry = (*entry)["ScreenShape"]; - applyScreenShape(screenShapeEntry, label); - entity = label; - } - - } - - if(!entity) { - if(targetEntity) { - entity = targetEntity; - } else { - entity = new ScreenEntity(); - } - } - - entity->ownsChildren = true; - - if((*entry)["positionMode"]) { - entity->setPositionMode((*entry)["positionMode"]->intVal); - } else { - entity->setPositionMode(ScreenEntity::POSITION_CENTER); - } - - Number _width, _height; - - if(entry->readNumber("width", &_width)) { - entity->setWidth(_width); - } - - if(entry->readNumber("height", &_height)) { - entity->setHeight(_height); - } - - entity->color.r = (*entry)["colorR"]->NumberVal; - entity->color.g = (*entry)["colorG"]->NumberVal; - entity->color.b = (*entry)["colorB"]->NumberVal; - entity->color.a = (*entry)["colorA"]->NumberVal; - - - if(!targetEntity) { - entity->blendingMode = (*entry)["blendMode"]->intVal; - - entity->scale.x = (*entry)["scaleX"]->NumberVal; - entity->scale.y = (*entry)["scaleY"]->NumberVal; - - entity->position.x = (*entry)["posX"]->NumberVal; - entity->position.y = (*entry)["posY"]->NumberVal; - - entity->setRotation((*entry)["rotation"]->NumberVal); - } else { - - } - - if((*entry)["id"]->stringVal != "") { - entity->id = (*entry)["id"]->stringVal; - } - - String tagString = (*entry)["tags"]->stringVal; - - if(tagString != "") { - std::vector tags = tagString.split(","); - for(int i=0; i < tags.size(); i++) { - entity->addTag(tags[i]); - } - } - - ObjectEntry *props = (*entry)["props"]; - if(props) { - for(int i=0; i < props->length; i++) { - ObjectEntry *prop = ((*props))[i]; - if(prop) { - entity->setEntityProp((*prop)["name"]->stringVal, (*prop)["value"]->stringVal); - } - } - } - - ObjectEntry *children = (*entry)["children"]; - - if(children) { - for(int i=0; i < children->length; i++) { - ObjectEntry *childEntry = ((*children))[i]; - ScreenEntity *childEntity = loadObjectEntryIntoEntity(childEntry); - entity->addChild(childEntity); - } - } - - return entity; -} - -String ScreenEntityInstance::getFileName() const { - return fileName; -} - -void ScreenEntityInstance::clearInstance() { - for(int i=0; i < children.size(); i++) { - removeChild(children[i]); - children[i]->setOwnsChildrenRecursive(true); - delete children[i]; - } -} - -bool ScreenEntityInstance::loadFromFile(const String& fileName) { - - clearInstance(); - - resourceEntry->resourceFileTime = OSBasics::getFileTime(fileName); - - this->ownsChildren = true; - this->fileName = fileName; - Object loadObject; - if(!loadObject.loadFromBinary(fileName)) { - Logger::log("Error loading entity instance.\n"); - } - ObjectEntry *root = loadObject.root["root"]; - - if(root) { - loadObjectEntryIntoEntity(root, this); - } - - return true; -} diff --git a/Core/Contents/Source/PolyScreenEvent.cpp b/Core/Contents/Source/PolyScreenEvent.cpp deleted file mode 100755 index e0fd8049c..000000000 --- a/Core/Contents/Source/PolyScreenEvent.cpp +++ /dev/null @@ -1,33 +0,0 @@ -/* - Copyright (C) 2011 by Ivan Safrin - - 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: - - The above copyright notice and this permission notice shall be included in - all copies or substantial portions of the Software. - - 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. -*/ - -#include "PolyScreenEvent.h" - -using namespace Polycode; - -ScreenEvent::ScreenEvent() { - eventType = "ScreenEvent"; -} - -ScreenEvent::~ScreenEvent() { - -} \ No newline at end of file diff --git a/Core/Contents/Source/PolyScreenImage.cpp b/Core/Contents/Source/PolyScreenImage.cpp deleted file mode 100755 index b09be0ad6..000000000 --- a/Core/Contents/Source/PolyScreenImage.cpp +++ /dev/null @@ -1,138 +0,0 @@ -/* - Copyright (C) 2011 by Ivan Safrin - - 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: - - The above copyright notice and this permission notice shall be included in - all copies or substantial portions of the Software. - - 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. -*/ - -#include "PolyScreenImage.h" -#include "PolyMesh.h" -#include "PolyPolygon.h" -#include "PolyTexture.h" -#include "PolyVertex.h" - -using namespace Polycode; - -ScreenImage* ScreenImage::ScreenImageWithImage(Image *image) { - return new ScreenImage(image); -} - -ScreenImage* ScreenImage::ScreenImageWithTexture(Texture *texture) { - return new ScreenImage(texture); -} - -ScreenImage::ScreenImage(const String& fileName) : ScreenShape(ScreenShape::SHAPE_RECT,1,1) { - loadTexture(fileName); - - imageWidth = texture->getWidth(); - imageHeight = texture->getHeight(); - - width = texture->getWidth(); - height = texture->getHeight(); - setShapeSize(width, height); - - setPositionMode(POSITION_TOPLEFT); -} - -ScreenImage::ScreenImage(Image *image) : ScreenShape(ScreenShape::SHAPE_RECT,1,1) { - loadTexture(image); - - imageWidth = texture->getWidth(); - imageHeight = texture->getHeight(); - - width = texture->getWidth(); - height = texture->getHeight(); - setShapeSize(width, height); - - setPositionMode(POSITION_TOPLEFT); -} - -ScreenImage::ScreenImage(Texture *texture) : ScreenShape(ScreenShape::SHAPE_RECT,1,1) { - setTexture(texture); - - imageWidth = texture->getWidth(); - imageHeight = texture->getHeight(); - - width = texture->getWidth(); - height = texture->getHeight(); - setShapeSize(width, height); - - setPositionMode(POSITION_TOPLEFT); -} - -ScreenImage::~ScreenImage() { - -} - -Entity *ScreenImage::Clone(bool deepClone, bool ignoreEditorOnly) const { - ScreenImage *newImage = new ScreenImage(getTexture()->getResourcePath()); - applyClone(newImage, deepClone, ignoreEditorOnly); - return newImage; -} - -void ScreenImage::applyClone(Entity *clone, bool deepClone, bool ignoreEditorOnly) const { - ScreenShape::applyClone(clone, deepClone, ignoreEditorOnly); -} - -void ScreenImage::setImageCoordinates(Number x, Number y, Number width, Number height) { - Vertex *vertex; - Number pixelSizeX = 1/imageWidth; - Number pixelSizeY = 1/imageHeight; - - this->width = width; - this->height = height; - setHitbox(width, height); - Number whalf = floor(width/2.0f); - Number hhalf = floor(height/2.0f); - - Number xFloat = x * pixelSizeX; - Number yFloat = 1 - (y * pixelSizeY); - Number wFloat = width * pixelSizeX; - Number hFloat = height * pixelSizeY; - - Polygon *imagePolygon = mesh->getPolygon(0); - vertex = imagePolygon->getVertex(0); - vertex->set(-whalf,-hhalf,0); - vertex->setTexCoord(xFloat, yFloat); - - vertex = imagePolygon->getVertex(1); - vertex->set(-whalf+width,-hhalf,0); - vertex->setTexCoord(xFloat + wFloat, yFloat); - - vertex = imagePolygon->getVertex(2); - vertex->set(-whalf+width,-hhalf+height,0); - vertex->setTexCoord(xFloat + wFloat, yFloat - hFloat); - - vertex = imagePolygon->getVertex(3); - vertex->set(-whalf,-hhalf+height,0); - vertex->setTexCoord(xFloat, yFloat - hFloat); - - mesh->arrayDirtyMap[RenderDataArray::VERTEX_DATA_ARRAY] = true; - mesh->arrayDirtyMap[RenderDataArray::TEXCOORD_DATA_ARRAY] = true; - rebuildTransformMatrix(); - matrixDirty = true; - -} - -Number ScreenImage::getImageWidth() const { - return imageWidth; -} - -Number ScreenImage::getImageHeight() const { - return imageHeight; -} diff --git a/Core/Contents/Source/PolyScreenLabel.cpp b/Core/Contents/Source/PolyScreenLabel.cpp deleted file mode 100755 index 4b3031d29..000000000 --- a/Core/Contents/Source/PolyScreenLabel.cpp +++ /dev/null @@ -1,100 +0,0 @@ -/* - Copyright (C) 2011 by Ivan Safrin - - 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: - - The above copyright notice and this permission notice shall be included in - all copies or substantial portions of the Software. - - 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. -*/ - -#include "PolyScreenLabel.h" -#include "PolyCoreServices.h" -#include "PolyFontManager.h" -#include "PolyFont.h" -#include "PolyLabel.h" -#include "PolyMaterialManager.h" -#include "PolyMesh.h" -#include "PolyPolygon.h" -#include "PolyScreenImage.h" -#include "PolyRenderer.h" - -using namespace Polycode; - -ScreenLabel::ScreenLabel(const String& text, int size, const String& fontName, int amode, bool premultiplyAlpha) : ScreenShape(ScreenShape::SHAPE_RECT,1,1) { - label = new Label(CoreServices::getInstance()->getFontManager()->getFontByName(fontName), text, size, amode, premultiplyAlpha); - texture = NULL; - updateTexture(); - setPositionMode(POSITION_TOPLEFT); - colorAffectsChildren = false; - positionAtBaseline = true; -} - -ScreenLabel::~ScreenLabel() { - delete label; -} - -Label *ScreenLabel::getLabel() const { - return label; -} - -Entity *ScreenLabel::Clone(bool deepClone, bool ignoreEditorOnly) const { - ScreenLabel *newLabel = new ScreenLabel(getText(), label->getSize(), label->getFont()->getFontName(), label->getAntialiasMode()); - applyClone(newLabel, deepClone, ignoreEditorOnly); - return newLabel; -} - -void ScreenLabel::applyClone(Entity *clone, bool deepClone, bool ignoreEditorOnly) const { - ScreenShape::applyClone(clone, deepClone, ignoreEditorOnly); - ScreenLabel *_clone = (ScreenLabel*) clone; - _clone->positionAtBaseline = positionAtBaseline; -} - -const String& ScreenLabel::getText() const { - return label->getText(); -} - -void ScreenLabel::updateTexture() { - - if(texture) { - CoreServices::getInstance()->getMaterialManager()->deleteTexture(texture); - } - - texture = NULL; - if(!label->getFont()) - return; - if(!label->getFont()->isValid()) - return; - - texture = CoreServices::getInstance()->getMaterialManager()->createTextureFromImage(label, true, false); - setWidth(label->getWidth()); - setHeight(label->getHeight()); - setShapeSize(width, height); - -} - -void ScreenLabel::Render() { - if(positionAtBaseline) { - CoreServices::getInstance()->getRenderer()->translate2D(0.0, -label->getBaselineAdjust() + label->getSize()); - } - ScreenShape::Render(); -} - -void ScreenLabel::setText(const String& newText) { - if(newText != label->getText() || label->optionsChanged()) { - label->setText(newText); - updateTexture(); - } -} diff --git a/Core/Contents/Source/PolyScreenLine.cpp b/Core/Contents/Source/PolyScreenLine.cpp deleted file mode 100755 index d359be380..000000000 --- a/Core/Contents/Source/PolyScreenLine.cpp +++ /dev/null @@ -1,117 +0,0 @@ -/* - Copyright (C) 2011 by Ivan Safrin - - 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: - - The above copyright notice and this permission notice shall be included in - all copies or substantial portions of the Software. - - 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. -*/ - -#include "PolyScreenLine.h" -#include "PolyCoreServices.h" -#include "PolyMesh.h" -#include "PolyPolygon.h" -#include "PolyRenderer.h" - -using namespace Polycode; - -ScreenLine::ScreenLine(Vector2 start, Vector2 end) : ScreenMesh(Mesh::LINE_MESH) { - target1 = NULL; - initMesh(); - - startVertex->x = start.x; - startVertex->y = start.y; - endVertex->x = end.x; - endVertex->y = end.y; - lineWidth = 1.0f; - mesh->arrayDirtyMap[RenderDataArray::VERTEX_DATA_ARRAY] = true; -} - -ScreenLine *ScreenLine::ScreenLineBetweenEntities(ScreenEntity* target1, ScreenEntity* target2) { - return new ScreenLine(target1, target2); -} - - -ScreenLine::ScreenLine(ScreenEntity* target1, ScreenEntity* target2) : ScreenMesh(Mesh::LINE_MESH) { - initMesh(); - this->target1 = target1; - this->target2 = target2; - lineWidth = 1.0f; - -} - -void ScreenLine::setStart(Vector2 point) { - startVertex->x = point.x; - startVertex->y = point.y; - mesh->arrayDirtyMap[RenderDataArray::VERTEX_DATA_ARRAY] = true; -} - -void ScreenLine::setEnd(Vector2 point) { - endVertex->x = point.x; - endVertex->y = point.y; - mesh->arrayDirtyMap[RenderDataArray::VERTEX_DATA_ARRAY] = true; -} - -void ScreenLine::initMesh() { - Polygon *poly = new Polygon(); - startVertex = poly->addVertex(0, 0, 0, 0,0); - endVertex = poly->addVertex(0,0,0,1,0); - mesh->addPolygon(poly); - mesh->arrayDirtyMap[RenderDataArray::VERTEX_DATA_ARRAY] = true; - mesh->arrayDirtyMap[RenderDataArray::COLOR_DATA_ARRAY] = true; - mesh->arrayDirtyMap[RenderDataArray::TEXCOORD_DATA_ARRAY] = true; -} - -ScreenLine::~ScreenLine() { - -} - - -void ScreenLine::setLineWidth(Number width) { - lineWidth = width; -} - -void ScreenLine::Update() { - if(!target1) - return; - Vector3 pos1 = target1->getPosition(); - Vector3 pos2 = target2->getPosition(); - - setPosition(pos1.x, pos1.y); - endVertex->x = pos2.x-pos1.x; - endVertex->y = pos2.y-pos1.y; - - mesh->arrayDirtyMap[RenderDataArray::VERTEX_DATA_ARRAY] = true; -} - - -void ScreenLine::Render() { - Renderer *renderer = CoreServices::getInstance()->getRenderer(); - renderer->setLineSize(lineWidth); - if(lineSmooth) { - renderer->setLineSmooth(true); - } - - renderer->setTexture(texture); - if(mesh->useVertexColors) { - renderer->pushDataArrayForMesh(mesh, RenderDataArray::COLOR_DATA_ARRAY); - } - renderer->pushDataArrayForMesh(mesh, RenderDataArray::VERTEX_DATA_ARRAY); - renderer->pushDataArrayForMesh(mesh, RenderDataArray::TEXCOORD_DATA_ARRAY); - renderer->drawArrays(mesh->getMeshType()); - - renderer->setLineSmooth(false); -} diff --git a/Core/Contents/Source/PolyScreenManager.cpp b/Core/Contents/Source/PolyScreenManager.cpp deleted file mode 100755 index b0bf01891..000000000 --- a/Core/Contents/Source/PolyScreenManager.cpp +++ /dev/null @@ -1,87 +0,0 @@ -/* - Copyright (C) 2011 by Ivan Safrin - - 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: - - The above copyright notice and this permission notice shall be included in - all copies or substantial portions of the Software. - - 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. -*/ - -#include "PolyScreenManager.h" -#include "PolyCoreServices.h" -#include "PolyRenderer.h" -#include "PolyScreen.h" - -using namespace Polycode; - -ScreenManager::ScreenManager() : EventDispatcher() { - drawScreensFirst = false; -} - -ScreenManager::~ScreenManager() { - -} - -void ScreenManager::removeScreen(Screen *screen) { - for(int i=0;isetRenderer(CoreServices::getInstance()->getRenderer()); - screens.push_back(screen); -} - -void ScreenManager::handleEvent(Event *event) { -// if(event->getDispatcher() == CoreServices::getInstance()->getCore()->getInput()) { - InputEvent *inputEvent = (InputEvent*)event; - for(int i=0;ihandleInputEvent(inputEvent); - } -// } -} - -void ScreenManager::Render() { - Renderer *renderer = CoreServices::getInstance()->getRenderer(); - for(int i=0;ienabled) { - if(!screens[i]->usesNormalizedCoordinates()) { - renderer->setOrthoMode(renderer->getXRes(), renderer->getYRes(), false); - } else { - Number yCoordinateSize = screens[i]->getYCoordinateSize(); - Number ratio = ((Number)renderer->getXRes())/((Number)renderer->getYRes()); - renderer->setOrthoMode(ratio*yCoordinateSize, yCoordinateSize, true); - } - - if(screens[i]->hasFilterShader()) { - screens[i]->drawFilter(); - } else { - screens[i]->Render(); - } - } - } -} - -void ScreenManager::Update() { - for(int i=0;ienabled) { - screens[i]->Update(); - } - } -} diff --git a/Core/Contents/Source/PolyScreenMesh.cpp b/Core/Contents/Source/PolyScreenMesh.cpp deleted file mode 100755 index 5dd8e66a1..000000000 --- a/Core/Contents/Source/PolyScreenMesh.cpp +++ /dev/null @@ -1,175 +0,0 @@ -/* - Copyright (C) 2011 by Ivan Safrin - - 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: - - The above copyright notice and this permission notice shall be included in - all copies or substantial portions of the Software. - - 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. -*/ - -#include "PolyScreenMesh.h" -#include "PolyCoreServices.h" -#include "PolyMaterialManager.h" -#include "PolyResourceManager.h" -#include "PolyMesh.h" -#include "PolyRenderer.h" - -using namespace Polycode; - -ScreenMesh::ScreenMesh(Mesh *mesh) : ScreenEntity(), material(NULL), texture(NULL) { - this->mesh = mesh; - lineSmooth = false; - lineWidth = 1.0; - ownsMesh = true; - updateHitBox(); -} - -ScreenMesh::ScreenMesh(const String& fileName) : ScreenEntity(), material(NULL), texture(NULL) { - mesh = new Mesh(fileName); - lineSmooth = false; - lineWidth = 1.0; - -} - -ScreenMesh::ScreenMesh(int meshType) : ScreenEntity(), material(NULL), texture(NULL) { - mesh = new Mesh(meshType); - lineSmooth = false; - lineWidth = 1.0; - -} - -ScreenMesh *ScreenMesh::ScreenMeshWithMesh(Mesh *mesh) { - return new ScreenMesh(mesh); -} - -ScreenMesh *ScreenMesh::ScreenMeshWithType(int meshType) { - return new ScreenMesh(meshType); -} - -ScreenMesh::~ScreenMesh() { - if(ownsMesh) { - delete mesh; - } -} - -Mesh *ScreenMesh::getMesh() const { - return mesh; -} - -Texture *ScreenMesh::getTexture() const { - return texture; -} - -void ScreenMesh::setTexture(Texture *texture) { - this->texture = texture; -} - -void ScreenMesh::loadTexture(const String& fileName) { - MaterialManager *materialManager = CoreServices::getInstance()->getMaterialManager(); - texture = materialManager->createTextureFromFile(fileName, materialManager->clampDefault, materialManager->mipmapsDefault); -} - -void ScreenMesh::loadTexture(Image *image) { - MaterialManager *materialManager = CoreServices::getInstance()->getMaterialManager(); - texture = materialManager->createTextureFromImage(image, materialManager->clampDefault, materialManager->mipmapsDefault); -} - -void ScreenMesh::clearMaterial() { - if(localShaderOptions) - delete localShaderOptions; - localShaderOptions = NULL; - this->material = NULL; -} - -void ScreenMesh::setMaterial(Material *material) { - - if(this->material) - clearMaterial(); - - if(!material) - return; - - if(material->getNumShaders() == 0) - return; - - this->material = material; - localShaderOptions = material->getShader(0)->createBinding(); - if(texture) { - localShaderOptions->clearTexture("diffuse"); - localShaderOptions->addTexture("diffuse", texture); - } - -} - -Material *ScreenMesh::getMaterial() { - return material; -} - -ShaderBinding *ScreenMesh::getLocalShaderOptions() { - return localShaderOptions; -} - -void ScreenMesh::setMaterialByName(const String& materialName) { - Material *material = (Material*)CoreServices::getInstance()->getResourceManager()->getResource(Resource::RESOURCE_MATERIAL, materialName); - if(!material) - return; - setMaterial(material); -} - -void ScreenMesh::Render() { - Renderer *renderer = CoreServices::getInstance()->getRenderer(); - - renderer->clearShader(); - - renderer->setLineSize(lineWidth); - renderer->setLineSmooth(lineSmooth); - - if(material) { - renderer->applyMaterial(material, localShaderOptions,0); - } else { - renderer->setTexture(texture); - } - - if(mesh->useVertexColors) { - renderer->pushDataArrayForMesh(mesh, RenderDataArray::COLOR_DATA_ARRAY); - } - renderer->pushDataArrayForMesh(mesh, RenderDataArray::VERTEX_DATA_ARRAY); - renderer->pushDataArrayForMesh(mesh, RenderDataArray::TEXCOORD_DATA_ARRAY); - renderer->drawArrays(mesh->getMeshType()); -} - -void ScreenMesh::updateHitBox() { - Number xmin, ymin, xmax, ymax; - bool any = false; - for(int c = 0; c < mesh->getPolygonCount(); c++) { - Polygon *poly = mesh->getPolygon(c); - for(int d = 0; d < poly->getVertexCount(); d++) { - Vertex *v = poly->getVertex(d); - if (any) { - xmin = MIN(v->x, xmin); - ymin = MIN(v->y, ymin); - xmax = MAX(v->x, xmax); - ymax = MAX(v->y, ymax); - } else { - xmin = v->x; xmax = v->x; - ymin = v->y; ymax = v->y; - any = true; - } - } - } - - setHitbox(xmax-xmin, ymax-ymin, xmin, ymin); -} diff --git a/Core/Contents/Source/PolyScreenShape.cpp b/Core/Contents/Source/PolyScreenShape.cpp deleted file mode 100755 index ecf18e037..000000000 --- a/Core/Contents/Source/PolyScreenShape.cpp +++ /dev/null @@ -1,246 +0,0 @@ -/* - Copyright (C) 2011 by Ivan Safrin - - 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: - - The above copyright notice and this permission notice shall be included in - all copies or substantial portions of the Software. - - 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. -*/ - -#include "PolyScreenShape.h" -#include "PolyCoreServices.h" -#include "PolyMesh.h" -#include "PolyPolygon.h" -#include "PolyRenderer.h" - -using namespace Polycode; - - -ScreenShape::ScreenShape(int shapeType, Number option1, Number option2, Number option3, Number option4) : ScreenMesh(Mesh::QUAD_MESH) { - strokeWidth = 1.0f; - this->shapeType = shapeType; - width = option1; - height = option2; - - setHitbox(width, height); - - this->option1 = option1; - this->option2 = option2; - this->option3 = option3; - this->option4 = option4; - lineSmooth = false; - - buildShapeMesh(); - - setPositionMode(POSITION_CENTER); - strokeEnabled = false; -} - -void ScreenShape::operator=(const ScreenShape& copy) { - strokeWidth = copy.strokeWidth; - shapeType = copy.getShapeType(); - - width = copy.getWidth(); - height = copy.getHeight(); - - setHitbox(width, height); - - this->option3 = copy.option3; - this->option4 = copy.option4; - lineSmooth = copy.lineSmooth; - - buildShapeMesh(); - - strokeColor = copy.strokeColor; - - setPositionMode(POSITION_CENTER); - strokeEnabled = copy.strokeEnabled; - -} - -Entity *ScreenShape::Clone(bool deepClone, bool ignoreEditorOnly) const { - ScreenShape *newEntity = new ScreenShape(ScreenShape::SHAPE_RECT, 1,1); - applyClone(newEntity, deepClone, ignoreEditorOnly); - return newEntity; - -} - -void ScreenShape::applyClone(Entity *clone, bool deepClone, bool ignoreEditorOnly) const { - ScreenEntity::applyClone(clone, deepClone, ignoreEditorOnly); - ScreenShape *_clone = (ScreenShape*) clone; - *_clone = *this; -} - -void ScreenShape::buildShapeMesh() { - - mesh->clearMesh(); - - switch(shapeType) { - case SHAPE_RECT: { - mesh->setMeshType(Mesh::QUAD_MESH); - - Number whalf = width/2.0f; - Number hhalf = height/2.0f; - - if(snapToPixels) { - whalf = floor(whalf); - hhalf = floor(hhalf); - } - - Polygon *poly = new Polygon(); - - poly->addVertex(-whalf,-hhalf,0,0,1); - poly->addVertex(-whalf+width,-hhalf,0, 1, 1); - poly->addVertex(-whalf+width,-hhalf+height,0, 1, 0); - poly->addVertex(-whalf,-hhalf+height,0,0,0); - - mesh->addPolygon(poly); - } - break; - case SHAPE_CIRCLE: { - mesh->setMeshType(Mesh::TRIFAN_MESH); - Polygon *poly = new Polygon(); - int step; - if(option3 > 0) - step = ceil(360/option3); - else - step = 1; - - poly->addVertex(cosf(0)*(width/2),sinf(0)*(height/2), 0, (cosf(0)*0.5) + 0.5,(sinf(0) * 0.5)+ 0.5); - - for (int i=0; i < 361; i+= step) { - Number degInRad = i*TORADIANS; - poly->addVertex(cos(degInRad)*(width/2),sin(degInRad)*(height/2), 0, (cos(degInRad) * 0.5)+ 0.5 , 1.0- ((sin(degInRad) * 0.5)+ 0.5)); - } - mesh->addPolygon(poly); - } - break; - default: - break; - } - - mesh->arrayDirtyMap[RenderDataArray::VERTEX_DATA_ARRAY] = true; -} - -int ScreenShape::getShapeType() const { - return shapeType; -} - -void ScreenShape::setShapeType(unsigned int type) { - shapeType = type; - buildShapeMesh(); -} - -void ScreenShape::setShapeSize(Number newWidth, Number newHeight) { - - setWidth(newWidth); - setHeight(newHeight); - - - if(shapeType == SHAPE_RECT) { - Number whalf = floor(width/2.0f); - Number hhalf = floor(height/2.0f); - Polygon *polygon; - Vertex *vertex; - - polygon = mesh->getPolygon(0); - vertex = polygon->getVertex(0); - vertex->set(-whalf,-hhalf,0); - vertex = polygon->getVertex(1); - vertex->set(-whalf+width,-hhalf,0); - vertex = polygon->getVertex(2); - vertex->set(-whalf+width,-hhalf+height,0); - vertex = polygon->getVertex(3); - vertex->set(-whalf,-hhalf+height,0); - mesh->arrayDirtyMap[RenderDataArray::VERTEX_DATA_ARRAY] = true; - } else { - buildShapeMesh(); - } - - rebuildTransformMatrix(); - matrixDirty = true; -} - -void ScreenShape::setGradient(Number r1, Number g1, Number b1, Number a1, Number r2, Number g2, Number b2, Number a2) { - - mesh->useVertexColors = true; - for(int i=0; i < mesh->getPolygon(0)->getVertexCount(); i++) { - mesh->getPolygon(0)->getVertex(i)->useVertexColor = true; - } - - switch(shapeType) { - case SHAPE_RECT: - mesh->getPolygon(0)->getVertex(0)->vertexColor.setColor(r1,g1,b1,a1); - mesh->getPolygon(0)->getVertex(1)->vertexColor.setColor(r1,g1,b1,a1); - mesh->getPolygon(0)->getVertex(2)->vertexColor.setColor(r2,g2,b2,a2); - mesh->getPolygon(0)->getVertex(3)->vertexColor.setColor(r2,g2,b2,a2); - break; - case SHAPE_CIRCLE: - mesh->getPolygon(0)->getVertex(0)->vertexColor.setColor(r1,g1,b1,a1); - for(int i=1; i < mesh->getPolygon(0)->getVertexCount(); i++) { - mesh->getPolygon(0)->getVertex(i)->vertexColor.setColor(r2,g2,b2,a2); - } - break; - } -} - -void ScreenShape::clearGradient() { - for(int i=0; i < mesh->getPolygon(0)->getVertexCount(); i++) { - mesh->getPolygon(0)->getVertex(i)->useVertexColor = false; - } - mesh->useVertexColors = false; -} - -void ScreenShape::setStrokeWidth(Number width) { - strokeWidth = width; -} - -void ScreenShape::setStrokeColor(Number r, Number g, Number b, Number a) { - strokeColor.setColor(r,g,b,a); -} - -void ScreenShape::Render() { - Renderer *renderer = CoreServices::getInstance()->getRenderer(); - - ScreenMesh::Render(); - - if(strokeEnabled) { - renderer->setTexture(NULL); - if(lineSmooth) { - renderer->setLineSmooth(true); - } - - renderer->setLineSize(strokeWidth); - renderer->setVertexColor(strokeColor.r,strokeColor.g,strokeColor.b,strokeColor.a); - int rmode = renderer->getRenderMode(); - renderer->setRenderMode(Renderer::RENDER_MODE_WIREFRAME); - -// renderer->pushDataArrayForMesh(mesh, RenderDataArray::COLOR_DATA_ARRAY); - renderer->pushDataArrayForMesh(mesh, RenderDataArray::VERTEX_DATA_ARRAY); -// renderer->pushDataArrayForMesh(mesh, RenderDataArray::TEXCOORD_DATA_ARRAY); - renderer->drawArrays(mesh->getMeshType()); - - renderer->setRenderMode(rmode); - renderer->setLineSize(1.0f); - renderer->setLineSmooth(false); - } - -} - - -ScreenShape::~ScreenShape() { - -} diff --git a/Core/Contents/Source/PolyScreenSound.cpp b/Core/Contents/Source/PolyScreenSound.cpp deleted file mode 100644 index 0207e0735..000000000 --- a/Core/Contents/Source/PolyScreenSound.cpp +++ /dev/null @@ -1,88 +0,0 @@ -/* -Copyright (C) 2011 by Ivan Safrin - -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: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -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. -*/ - -#include "PolyScreenSound.h" -#include "PolySound.h" -#include "PolyCoreServices.h" -#include "PolySoundManager.h" - -using namespace Polycode; - -ScreenSoundListener::ScreenSoundListener() : ScreenEntity() { - -} -ScreenSoundListener::~ScreenSoundListener() { - -} - -void ScreenSoundListener::Update() { - Matrix4 finalMatrix = getConcatenatedMatrix(); - CoreServices::getInstance()->getSoundManager()->setListenerPosition(finalMatrix.getPosition()); - - Vector3 upVector = Vector3(0.0, 1.0, 0.0); - - Vector3 direction; - direction.x = 0; - direction.y = 0; - direction.z = -1; -// direction = finalMatrix.rotateVector(direction); - - CoreServices::getInstance()->getSoundManager()->setListenerOrientation(direction, upVector); - -} - -Entity *ScreenSound::Clone(bool deepClone, bool ignoreEditorOnly) const { - ScreenSound *newSound = new ScreenSound(sound->getFileName(), sound->getReferenceDistance(), sound->getMaxDistance()); - applyClone(newSound, deepClone, ignoreEditorOnly); - return newSound; -} - -void ScreenSound::applyClone(Entity *clone, bool deepClone, bool ignoreEditorOnly) const { - ScreenEntity::applyClone(clone, deepClone, ignoreEditorOnly); -} - -ScreenSound::ScreenSound(const String& fileName, Number referenceDistance, Number maxDistance) : ScreenEntity() { - sound = new Sound(fileName); - sound->setIsPositional(true); - sound->setPositionalProperties(referenceDistance, maxDistance); -} - -ScreenSound::~ScreenSound() { - delete sound; -} - -void ScreenSound::Update() { - Matrix4 finalMatrix = getConcatenatedMatrix(); - sound->setSoundPosition(finalMatrix.getPosition()); - - Vector3 direction; - direction.x = 0; - direction.y = 0; - direction.z = -1; -// direction = finalMatrix.rotateVector(direction); - sound->setSoundDirection(direction); - -} - -Sound *ScreenSound::getSound() const { - return sound; -} diff --git a/Core/Contents/Source/PolyScreenSprite.cpp b/Core/Contents/Source/PolyScreenSprite.cpp deleted file mode 100644 index 113fd45e8..000000000 --- a/Core/Contents/Source/PolyScreenSprite.cpp +++ /dev/null @@ -1,388 +0,0 @@ -/* - Copyright (C) 2011 by Ivan Safrin - - 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: - - The above copyright notice and this permission notice shall be included in - all copies or substantial portions of the Software. - - 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. -*/ - -#include "PolyScreenSprite.h" -#include "PolyCore.h" -#include "PolyCoreServices.h" -#include "PolyMesh.h" -#include "PolyPolygon.h" -#include "PolyTexture.h" - -using std::vector; -using namespace Polycode; - -ScreenSpriteResourceEntry::ScreenSpriteResourceEntry(ScreenSprite *sprite) : Resource(Resource::RESOURCE_SCREEN_ENTITY_INSTANCE) { - this->sprite = sprite; -} - -ScreenSpriteResourceEntry::~ScreenSpriteResourceEntry() { - -} - -ScreenSprite *ScreenSpriteResourceEntry::getSprite() { - return sprite; -} - -void ScreenSpriteResourceEntry::reloadResource() { - sprite->reloadSprite(); - Resource::reloadResource(); -} - -ScreenSprite* ScreenSprite::ScreenSpriteFromImageFile(const String& fileName, Number spriteWidth, Number spriteHeight) { - return new ScreenSprite(fileName, spriteWidth, spriteHeight); -} - -ScreenSprite::ScreenSprite(const String& fileName) : ScreenShape(ScreenShape::SHAPE_RECT, 1, 1) { - - currentFrame = 0; - currentAnimation = NULL; - paused = false; - - resourceEntry = new ScreenSpriteResourceEntry(this); - loadFromFile(fileName); - resourceEntry->setResourceName(fileName); - resourceEntry->setResourcePath(fileName); -} - -Entity *ScreenSprite::Clone(bool deepClone, bool ignoreEditorOnly) const { - ScreenSprite *newSprite = new ScreenSprite(getTexture()->getResourcePath(), spriteWidth, spriteHeight); - for(int i=0; i < animations.size(); i++) { - newSprite->addAnimation(animations[i]->name, animations[i]->frames, animations[i]->speed); - } - if(currentAnimation) { - newSprite->playAnimation(currentAnimation->name, currentFrame, playingOnce); - } - applyClone(newSprite, deepClone, ignoreEditorOnly); - return newSprite; -} - -void ScreenSprite::applyClone(Entity *clone, bool deepClone, bool ignoreEditorOnly) const { - ScreenShape::applyClone(clone, deepClone, ignoreEditorOnly); - -} - -bool ScreenSprite::loadFromFile(const String& fileName) { - Object loadObject; - - animations.clear(); - - if(!loadObject.loadFromXML(fileName)) { - return false; - } - - this->fileName = fileName; - - ObjectEntry *image = loadObject.root["image"]; - if(image) { - ObjectEntry *frameWidth = (*image)["frameWidth"]; - ObjectEntry *frameHeight = (*image)["frameHeight"]; - ObjectEntry *fileName = (*image)["fileName"]; - - if(fileName) { - loadTexture(fileName->stringVal); - } - - if(frameWidth && frameHeight) { - setShapeSize(frameWidth->NumberVal, frameHeight->NumberVal); - setSpriteSize(frameWidth->NumberVal, frameHeight->NumberVal); - } - } - - ObjectEntry *animationsEntry = loadObject.root["animations"]; - - if(animationsEntry) { - for(int i=0; i < animationsEntry->length; i++) { - ObjectEntry *animation = (*animationsEntry)[i]; - if(animation) { - ObjectEntry *name = (*animation)["name"]; - ObjectEntry *frames = (*animation)["frames"]; - ObjectEntry *speed = (*animation)["speed"]; - - if(name && frames && speed) { - addAnimation(name->stringVal, frames->stringVal, speed->NumberVal); - } else { - printf("Error parsing animation node\n"); - } - } - } - } - - recalculateSpriteDimensions(); - - return true; -} - -unsigned int ScreenSprite::getNumAnimations() { - return animations.size(); -} - -SpriteAnimation *ScreenSprite::getAnimationAtIndex(unsigned int index) { - if(index < animations.size()) { - return animations[index]; - } else { - return NULL; - } -} - -void ScreenSprite::reloadSprite() { - - String _animName = ""; - int _currentFrame; - bool _playingOnce; - - if(currentAnimation) { - _animName = currentAnimation->name; - _currentFrame = currentFrame; - _playingOnce = playingOnce; - } - loadFromFile(fileName); - - if(_animName != "") { - playAnimation(_animName, _currentFrame, _playingOnce); - } -} - -ScreenSpriteResourceEntry *ScreenSprite::getResourceEntry() { - return resourceEntry; -} - -ScreenSprite::ScreenSprite(const String& fileName, Number spriteWidth, Number spriteHeight) : ScreenShape(ScreenShape::SHAPE_RECT, spriteWidth, spriteHeight) { - this->spriteWidth = spriteWidth; - this->spriteHeight = spriteHeight; - loadTexture(fileName); - - recalculateSpriteDimensions(); - currentFrame = 0; - currentAnimation = NULL; - paused = false; -} - -ScreenSprite::~ScreenSprite() { - -} - -void ScreenSprite::recalculateSpriteDimensions() { - if(!texture) - return; - - spriteUVWidth = 1.0f / ((Number) texture->getWidth() / spriteWidth); - spriteUVHeight = 1.0f / ((Number) texture->getHeight() / spriteHeight); - - for(int i =0 ; i < animations.size(); i++) { - animations[i]->numFramesX = texture->getWidth() / spriteWidth; - if(animations[i]->numFramesX < 1) { - animations[i]->numFramesX = 1; - } - animations[i]->numFramesY = texture->getHeight() / spriteHeight; - if(animations[i]->numFramesY < 1) { - animations[i]->numFramesY = 1; - } - animations[i]->spriteUVWidth = spriteUVWidth; - animations[i]->spriteUVHeight = spriteUVHeight; - animations[i]->setOffsetsFromFrameString(animations[i]->frames); - } -} - -Vector2 ScreenSprite::getSpriteSize() { - return Vector2(spriteWidth, spriteHeight); -} - -String ScreenSprite::getFileName() const { - return fileName; -} - -void ScreenSprite::setSpriteSize(const Number spriteWidth, const Number spriteHeight) { - this->spriteWidth = spriteWidth; - this->spriteHeight = spriteHeight; - - recalculateSpriteDimensions(); -} - -SpriteAnimation *ScreenSprite::getCurrentAnimation() { - return currentAnimation; -} - -unsigned int ScreenSprite::getCurrentAnimationFrame() { - return currentFrame; -} - -bool ScreenSprite::isCurrentAnimationFinished() { - if(currentAnimation) { - if(currentFrame >= currentAnimation->numFrames) - return true; - } - return false; -} - -void SpriteAnimation::setOffsetsFromFrameString(const String& frames) { - framesOffsets.clear(); - vector frameNumbers = frames.split(","); - - int frameNumber; - int frameX; - int frameY; - - numFrames = 0; - - for(int i=0; i < frameNumbers.size(); i++) { - if(frameNumbers[i].find_first_of("-") != -1) { - vector _frameNumbers = frameNumbers[i].split("-"); - if(_frameNumbers.size() > 1) { - int frameNumberStart = atoi(_frameNumbers[0].c_str()); - int frameNumberEnd = atoi(_frameNumbers[1].c_str()); - int dir = 1; - if(frameNumberEnd < frameNumberStart) { - dir = -1; - } - for(int j=frameNumberStart; j != frameNumberEnd + dir; j += dir) { - frameX = j % numFramesX; - frameY = j/numFramesX; - framesOffsets.push_back(Vector2(spriteUVWidth * frameX, spriteUVHeight * frameY)); - numFrames++; - } - } - } else if(frameNumbers[i].find_first_of("x") != -1) { - vector _frameNumbers = frameNumbers[i].split("x"); - if(_frameNumbers.size() > 1) { - int _frameNumber = atoi(_frameNumbers[0].c_str()); - int frameNumberCount = atoi(_frameNumbers[1].c_str()); - for(int j=0; j < frameNumberCount; j++) { - frameX = _frameNumber % numFramesX; - frameY = _frameNumber/numFramesX; - framesOffsets.push_back(Vector2(spriteUVWidth * frameX, spriteUVHeight * frameY)); - numFrames++; - } - } - } else { - frameNumber = atoi(frameNumbers[i].c_str()); - frameX = frameNumber % numFramesX; - frameY = frameNumber/numFramesX; - framesOffsets.push_back(Vector2(spriteUVWidth * frameX, spriteUVHeight * frameY)); - numFrames++; - } - } - - this->frames = frames; - -} - -SpriteAnimation *ScreenSprite::addAnimation(const String& name, const String& frames, Number speed) { - SpriteAnimation *newAnimation = new SpriteAnimation(); - - - newAnimation->numFramesX = texture->getWidth() / spriteWidth; - if(newAnimation->numFramesX < 1) { - newAnimation->numFramesX = 1; - } - newAnimation->numFramesY = texture->getHeight() / spriteHeight; - if(newAnimation->numFramesY < 1) { - newAnimation->numFramesY = 1; - } - newAnimation->spriteUVWidth = spriteUVWidth; - newAnimation->spriteUVHeight = spriteUVHeight; - - newAnimation->setOffsetsFromFrameString(frames); - - newAnimation->speed = speed; - newAnimation->name = name; - animations.push_back(newAnimation); - return newAnimation; -} - -void ScreenSprite::playAnimation(const String& name, int startFrame, bool once) { - paused = false; - for(int i=0; i < animations.size(); i++) { - if(animations[i]->name == name) { - if(currentAnimation == animations[i] && !playingOnce && !once) - return; - currentFrame = 0; - currentAnimation = animations[i]; - if(startFrame == -1) { - currentFrame = rand() % currentAnimation->numFrames; - } else { - if(startFrame < currentAnimation->numFrames) { - currentFrame = startFrame; - } - } - playingOnce = once; - lastTick = 0; - } - } -} - -void ScreenSprite::Pause(bool val) { - paused = val; -} - -void ScreenSprite::showFrame(unsigned int frameIndex) { - if(!currentAnimation) - return; - - if(frameIndex < currentAnimation->numFrames) { - currentFrame = frameIndex; - updateSprite(); - } -} - -void ScreenSprite::Update() { - if(!currentAnimation) - return; - - Number newTick = CoreServices::getInstance()->getCore()->getTicksFloat(); - - Number elapsed = newTick - lastTick; - - if(paused) - return; - - if(elapsed > currentAnimation->speed) { - currentFrame++; - if(currentFrame >= currentAnimation->numFrames) { - if(playingOnce) { - dispatchEvent(new Event(), Event::COMPLETE_EVENT); - return; - } else { - currentFrame = 0; - } - } - - updateSprite(); - - lastTick = newTick; - - } -} - -void ScreenSprite::updateSprite() { - Number xOffset = currentAnimation->framesOffsets[currentFrame].x; - Number yOffset = 1.0f - currentAnimation->framesOffsets[currentFrame].y - spriteUVHeight; - - Polygon *imagePolygon = mesh->getPolygon(0); - - imagePolygon->getVertex(0)->setTexCoord(xOffset, yOffset+spriteUVHeight); - imagePolygon->getVertex(1)->setTexCoord(xOffset+spriteUVWidth, yOffset+spriteUVHeight); - imagePolygon->getVertex(2)->setTexCoord(xOffset+spriteUVWidth, yOffset); - imagePolygon->getVertex(3)->setTexCoord(xOffset, yOffset); - - mesh->arrayDirtyMap[RenderDataArray::TEXCOORD_DATA_ARRAY] = true; - -} diff --git a/Core/Contents/Source/PolyServer.cpp b/Core/Contents/Source/PolyServer.cpp index 8f669ac12..dc7df36fa 100755 --- a/Core/Contents/Source/PolyServer.cpp +++ b/Core/Contents/Source/PolyServer.cpp @@ -28,7 +28,7 @@ using namespace Polycode; using std::vector; ServerClient::ServerClient() { - + clientReady = false; } ServerClient::~ServerClient() { @@ -78,6 +78,11 @@ void Server::handleEvent(Event *event) { world->getWorldState(client, &worldData, &worldDataSize); sendData(client->connection->address, (char*)worldData, worldDataSize, PACKET_TYPE_SERVER_DATA); } + } else { + for(int i=0; i < clients.size(); i++) { + client = clients[i]; + sendData(client->connection->address, 0, 0, PACKET_TYPE_SERVER_DATA); + } } } @@ -101,6 +106,7 @@ void Server::handlePeerConnection(PeerConnection *connection) { clients.push_back(newClient); unsigned short clientID = newClient->clientID; + printf("SENDING PACKET_TYPE_SETCLIENT_ID\n"); sendReliableData(newClient->connection->address, (char*)&clientID, sizeof(unsigned short), PACKET_TYPE_SETCLIENT_ID); } @@ -142,9 +148,12 @@ void Server::handlePacket(Packet *packet, PeerConnection *connection) { switch (packet->header.type) { case PACKET_TYPE_CLIENT_READY: { - ServerEvent *event = new ServerEvent(); - event->client = client; - dispatchEvent(event, ServerEvent::EVENT_CLIENT_CONNECTED); + if(!client->clientReady) { + client->clientReady = true; + ServerEvent *event = new ServerEvent(); + event->client = client; + dispatchEvent(event, ServerEvent::EVENT_CLIENT_CONNECTED); + } } break; case PACKET_TYPE_DISONNECT: diff --git a/Core/Contents/Source/PolyShader.cpp b/Core/Contents/Source/PolyShader.cpp index 48db0abd5..f111b9341 100755 --- a/Core/Contents/Source/PolyShader.cpp +++ b/Core/Contents/Source/PolyShader.cpp @@ -92,18 +92,13 @@ ShaderBinding::~ShaderBinding() { for(int i=0; i < renderTargetBindings.size(); i++) { delete renderTargetBindings[i]; } - for(int i=0; i < inTargetBindings.size(); i++) { - delete inTargetBindings[i]; - } - for(int i=0; i < outTargetBindings.size(); i++) { - delete outTargetBindings[i]; - } } unsigned int ShaderBinding::getNumLocalParams() { return localParams.size(); } + LocalShaderParam *ShaderBinding::getLocalParam(unsigned int index) { return localParams[index]; } @@ -117,23 +112,26 @@ LocalShaderParam *ShaderBinding::getLocalParamByName(const String& name) { return NULL; } -LocalShaderParam *ShaderBinding::addLocalParam(const String& name, void *ptr) { - LocalShaderParam *newParam = new LocalShaderParam(); - newParam->name = name; - newParam->data = ptr; - localParams.push_back(newParam); - return newParam; -} - LocalShaderParam * ShaderBinding::addParam(int type, const String& name) { void *defaultData = ProgramParam::createParamData(type); LocalShaderParam *newParam = new LocalShaderParam(); newParam->data = defaultData; newParam->name = name; + newParam->type = type; localParams.push_back(newParam); return newParam; } +LocalShaderParam *ShaderBinding::addParamPointer(int type, const String& name, void *ptr) { + LocalShaderParam *newParam = new LocalShaderParam(); + newParam->name = name; + newParam->data = ptr; + newParam->type = type; + newParam->ownsPointer = false; + localParams.push_back(newParam); + return newParam; +} + void ShaderBinding::addRenderTargetBinding(RenderTargetBinding *binding) { renderTargetBindings.push_back(binding); switch (binding->mode) { @@ -185,7 +183,22 @@ void ShaderBinding::removeRenderTargetBinding(RenderTargetBinding *binding) { return; } } - +} + +void ShaderBinding::copyTo(ShaderBinding *targetBinding) { + for(int i=0; i < textures.size(); i++) { + targetBinding->textures.push_back(textures[i]); + } + + for(int i=0; i < cubemaps.size(); i++) { + targetBinding->cubemaps.push_back(cubemaps[i]); + } + + for(int i=0; i < localParams.size(); i++) { + LocalShaderParam *copyParam = localParams[i]->Copy(); + targetBinding->localParams.push_back(copyParam); + } + } unsigned int ShaderBinding::getNumRenderTargetBindings() { @@ -231,7 +244,7 @@ RenderTargetBinding *ShaderBinding::getDepthTargetBinding(unsigned int index) { Shader::Shader(int type) : Resource(Resource::RESOURCE_SHADER) { numSpotLights = 0; - numAreaLights = 0; + numPointLights = 0; this->type = type; vp = NULL; fp = NULL; @@ -246,6 +259,10 @@ int Shader::getExpectedParamType(String name) { return ProgramParam::PARAM_UNKNOWN; } +ShaderBinding *Shader::createBinding() { + return new ShaderBinding(this); +} + Shader::~Shader() { } @@ -258,6 +275,231 @@ void Shader::setName(const String& name) { this->name = name; } +Number LocalShaderParam::getNumber() { + if(type != ProgramParam::PARAM_NUMBER) { + return 0.0; + } + return *((Number *)data); +} + +Vector2 LocalShaderParam::getVector2() { + if(type != ProgramParam::PARAM_VECTOR2) { + return Vector2(); + } + return *((Vector2 *)data); +} + +Vector3 LocalShaderParam::getVector3() { + if(type != ProgramParam::PARAM_VECTOR3) { + return Vector3(); + } + return *((Vector3 *)data); +} + +Matrix4 LocalShaderParam::getMatrix4() { + if(type != ProgramParam::PARAM_MATRIX) { + return Matrix4(); + } + return *((Matrix4 *)data); +} + +Color LocalShaderParam::getColor() { + if(type != ProgramParam::PARAM_COLOR) { + return Color(0.0, 0.0, 0.0, 0.0); + } + return *((Color *)data); +} + +void LocalShaderParam::setNumber(Number x) { + if(type != ProgramParam::PARAM_NUMBER) { + return; + } + memcpy(data, &x, sizeof(x)); +} + +void LocalShaderParam::setVector2(Vector2 x) { + if(type != ProgramParam::PARAM_VECTOR2) { + return; + } + memcpy(data, &x, sizeof(x)); +} + +void LocalShaderParam::setVector3(Vector3 x) { + if(type != ProgramParam::PARAM_VECTOR3) { + return; + } + memcpy(data, &x, sizeof(x)); +} + +void LocalShaderParam::setMatrix4(Matrix4 x) { + if(type != ProgramParam::PARAM_MATRIX) { + return; + } + memcpy(data, &x, sizeof(x)); +} + +void LocalShaderParam::setColor(Color x) { + if(type != ProgramParam::PARAM_COLOR) { + return; + } + static_cast(data)->setColor(&x); +} + const String& Shader::getName() const { return name; } + +LocalShaderParam::LocalShaderParam() { + data = NULL; + arraySize = 0; + ownsPointer = true; +} + +LocalShaderParam::~LocalShaderParam() { + if(ownsPointer) { + switch(type) { + case ProgramParam::PARAM_NUMBER: + delete ((Number*) data); + break; + case ProgramParam::PARAM_VECTOR2: + delete ((Vector2*) data); + break; + case ProgramParam::PARAM_VECTOR3: + delete ((Vector3*) data); + break; + case ProgramParam::PARAM_COLOR: + delete ((Color*) data); + break; + case ProgramParam::PARAM_MATRIX: + delete ((Matrix4*) data); + break; + } + } +} + +LocalShaderParam *LocalShaderParam::Copy() { + LocalShaderParam *copyParam = new LocalShaderParam(); + copyParam->name = name; + copyParam->type = type; + copyParam->data = ProgramParam::createParamData(type); + copyParam->ownsPointer = ownsPointer; + + switch(type) { + case ProgramParam::PARAM_NUMBER: + { + copyParam->setNumber(getNumber()); + } + break; + case ProgramParam::PARAM_VECTOR2: + { + copyParam->setVector2(getVector2()); + } + break; + case ProgramParam::PARAM_VECTOR3: + { + copyParam->setVector3(getVector3()); + } + break; + case ProgramParam::PARAM_COLOR: + { + copyParam->setColor(getColor()); + } + break; + case ProgramParam::PARAM_MATRIX: + { + copyParam->setMatrix4(getMatrix4()); + } + break; + } + return copyParam; +} + +void LocalShaderParam::setParamValueFromString(int type, String pvalue) { + switch(type) { + case ProgramParam::PARAM_NUMBER: + { + setNumber(atof(pvalue.c_str())); + } + break; + case ProgramParam::PARAM_VECTOR2: + { + std::vector values = pvalue.split(" "); + if(values.size() == 2) { + setVector2(Vector2(atof(values[0].c_str()), atof(values[1].c_str()))); + } else { + printf("Material parameter error: Vector2 %s must have 2 values (%d provided)!\n", name.c_str(), (int)values.size()); + } + } + break; + case ProgramParam::PARAM_VECTOR3: + { + std::vector values = pvalue.split(" "); + if(values.size() == 3) { + setVector3(Vector3(atof(values[0].c_str()), atof(values[1].c_str()), atof(values[2].c_str()))); + } else { + printf("Material parameter error: Vector3 %s must have 3 values (%d provided)!\n", name.c_str(), (int)values.size()); + } + } + break; + case ProgramParam::PARAM_COLOR: + { + std::vector values = pvalue.split(" "); + if(values.size() == 4) { + setColor(Color(atof(values[0].c_str()), atof(values[1].c_str()), atof(values[2].c_str()), atof(values[3].c_str()))); + } else { + printf("Material parameter error: Color %s must have 4 values (%d provided)!\n", name.c_str(), (int)values.size()); + } + } + break; + } +} + +Cubemap *ShaderBinding::getCubemap(const String& name) { + for(int i=0; i < cubemaps.size(); i++) { + if(cubemaps[i].name == name) { + return cubemaps[i].cubemap; + } + } + return NULL; +} + +Texture *ShaderBinding::getTexture(const String& name) { + for(int i=0; i < textures.size(); i++) { + if(textures[i].name == name) { + return textures[i].texture; + } + } + return NULL; +} + +void ShaderBinding::addTexture(const String& name, Texture *texture) { + TextureBinding binding; + binding.name = name; + binding.texture = texture; + textures.push_back(binding); +} + +void ShaderBinding::addCubemap(const String& name, Cubemap *cubemap) { + CubemapBinding binding; + binding.cubemap = cubemap; + binding.name = name; + cubemaps.push_back(binding); +} + +void ShaderBinding::clearCubemap(const String& name) { + for(int i=0; i < cubemaps.size(); i++) { + if(cubemaps[i].name == name) { + cubemaps.erase(cubemaps.begin()+i); + return; + } + } +} + +void ShaderBinding::clearTexture(const String& name) { + for(int i=0; i < textures.size(); i++) { + if(textures[i].name == name) { + textures.erase(textures.begin()+i); + return; + } + } +} diff --git a/Core/Contents/Source/PolySkeleton.cpp b/Core/Contents/Source/PolySkeleton.cpp index 1964b9d67..1bb5118ca 100755 --- a/Core/Contents/Source/PolySkeleton.cpp +++ b/Core/Contents/Source/PolySkeleton.cpp @@ -27,6 +27,7 @@ #include "PolySceneLabel.h" #include "PolySceneLine.h" #include "PolyTween.h" +#include "PolyTweenManager.h" #include "OSBasics.h" using namespace Polycode; @@ -35,16 +36,25 @@ Skeleton *Skeleton::BlankSkeleton() { return new Skeleton(); } -Skeleton::Skeleton(const String& fileName) : SceneEntity() { +Skeleton::Skeleton(const String& fileName) : Entity() { + baseAnimation = NULL; + bonesEntity = new Entity(); + bonesEntity->visible = false; + addChild(bonesEntity); loadSkeleton(fileName); - currentAnimation = NULL; } Skeleton::Skeleton() { - currentAnimation = NULL; + baseAnimation = NULL; + bonesEntity = new Entity(); + bonesEntity->visible = false; + addChild(bonesEntity); } Skeleton::~Skeleton() { + for(int i=0; i < animations.size(); i++) { + delete animations[i]; + } } int Skeleton::getNumBones() const { @@ -59,54 +69,55 @@ Bone *Skeleton::getBoneByName(const String& name) const { return NULL; } -Bone *Skeleton::getBone(int index) const { +Bone *Skeleton::getBone(unsigned int index) const { + if(index >= bones.size()) { + return NULL; + } return bones[index]; } -void Skeleton::enableBoneLabels(const String& labelFont, Number size, Number scale, Color labelColor) { - for(int i=0; i < bones.size(); i++) { - bones[i]->enableBoneLabel(labelFont, size, scale,labelColor); - } - - SceneLabel *label = new SceneLabel(labelFont, "Skeleton", size, scale, Label::ANTIALIAS_FULL); - label->setColor(labelColor); - label->billboardMode = true; - label->depthWrite = false; - addEntity(label); - +void Skeleton::setBaseAnimationByName(const String &animName) { + SkeletonAnimation *anim = getAnimation(animName); + if(anim) { + setBaseAnimation(anim); + } } -void Skeleton::playAnimationByIndex(int index, bool once) { - if(index > animations.size()-1) - return; - - SkeletonAnimation *anim = animations[index]; - if(!anim) - return; - - if(anim == currentAnimation && !once) - return; - - if(currentAnimation) - currentAnimation->Stop(); - - currentAnimation = anim; - anim->Play(once); +void Skeleton::setBaseAnimation(SkeletonAnimation *animation){ + baseAnimation = animation; + baseAnimation->setWeight(1.0); + animation->Play(false); } -void Skeleton::playAnimation(const String& animName, bool once) { + +void Skeleton::playAnimationByName(const String& animName, Number weight, bool once, bool restartIfPlaying) { SkeletonAnimation *anim = getAnimation(animName); - if(!anim) - return; - - if(anim == currentAnimation && !once) - return; - - if(currentAnimation) - currentAnimation->Stop(); - - currentAnimation = anim; - anim->Play(once); + if(anim) { + playAnimation(anim, weight, once, restartIfPlaying); + } +} + +void Skeleton::playAnimation(SkeletonAnimation *animation, Number weight, bool once, bool restartIfPlaying) { + + if(weight > 1.0) { + weight = 1.0; + } + if(weight < 0.0) { + weight = 0.0; + } + + animation->setWeight(weight); + + if(animation->isPlaying()) { + if(restartIfPlaying) { + animation->Reset(); + } + return; + } + + animation->Reset(); + playingAnimations.push_back(animation); + animation->Play(once); } SkeletonAnimation *Skeleton::getAnimation(const String& name) const { @@ -118,10 +129,53 @@ SkeletonAnimation *Skeleton::getAnimation(const String& name) const { } void Skeleton::Update() { + + for(int i=0; i < bones.size(); i++) { + if(!bones[i]->disableAnimation) { + bones[i]->setRotationByQuaternion(bones[i]->baseRotation); + bones[i]->setPosition(bones[i]->basePosition); + bones[i]->setScale(bones[i]->baseScale); + } + } + + if(baseAnimation) { + baseAnimation->Update(); + } + + for(int i=0; i < playingAnimations.size(); i++) { + playingAnimations[i]->Update(); + } + + for(int i=0; i < bones.size(); i++) { + bones[i]->rebuildTransformMatrix(); + bones[i]->setBoneMatrix(bones[i]->getTransformMatrix()); + } + + for(int i=0; i < bones.size(); i++) { + bones[i]->rebuildFinalMatrix(); + } +} - if(currentAnimation != NULL) { - currentAnimation->Update(); - } +void Skeleton::addBone(Bone *bone) { + bones.push_back(bone); +} + +void Skeleton::removeBone(Bone *bone) { + for(int i=0; i < bones.size(); i++) { + if(bones[i] == bone) { + bones.erase(bones.begin()+i); + return; + } + } +} + +unsigned int Skeleton::getBoneIndexByBone(Bone *bone) { + for(int i=0; i < bones.size(); i++) { + if(bones[i] == bone) { + return i; + } + } + return 0; } void Skeleton::loadSkeleton(const String& fileName) { @@ -130,10 +184,6 @@ void Skeleton::loadSkeleton(const String& fileName) { return; } - bonesEntity = new SceneEntity(); - bonesEntity->visible = false; - addChild(bonesEntity); - unsigned int numBones; float t[3],rq[4],s[3]; @@ -165,6 +215,13 @@ void Skeleton::loadSkeleton(const String& fileName) { bones.push_back(newBone); + Quaternion bq; + bq.set(rq[0], rq[1], rq[2], rq[3]); + + newBone->baseRotation = bq; + newBone->baseScale = Vector3(s[0], s[1], s[2]); + newBone->basePosition = Vector3(t[0], t[1], t[2]); + newBone->setPosition(t[0], t[1], t[2]); newBone->setRotationQuat(rq[0], rq[1], rq[2], rq[3]); newBone->setScale(s[0], s[1], s[2]); @@ -187,102 +244,130 @@ void Skeleton::loadSkeleton(const String& fileName) { } Bone *parentBone; -// SceneEntity *bProxy; for(int i=0; i < bones.size(); i++) { if(bones[i]->parentBoneId != -1) { parentBone = bones[bones[i]->parentBoneId]; parentBone->addChildBone(bones[i]); bones[i]->setParentBone(parentBone); - parentBone->addEntity(bones[i]); -// addEntity(bones[i]); - - SceneLine *connector = new SceneLine(bones[i], parentBone); - connector->depthTest = false; - bonesEntity->addEntity(connector); - connector->setColor(((Number)(rand() % RAND_MAX)/(Number)RAND_MAX),((Number)(rand() % RAND_MAX)/(Number)RAND_MAX),((Number)(rand() % RAND_MAX)/(Number)RAND_MAX),1.0f); + parentBone->addChild(bones[i]); } else { -// bProxy = new SceneEntity(); -// addEntity(bProxy); -// bProxy->addEntity(bones[i]); bonesEntity->addChild(bones[i]); } - // bones[i]->visible = false; } OSBasics::close(inFile); } +SkeletonAnimation *Skeleton::getBaseAnimation() { + return baseAnimation; +} + +void Skeleton::stopAllAnimations() { + for(int i=0; i < playingAnimations.size(); i++) { + playingAnimations[i]->Stop(); + } + playingAnimations.clear(); +} + +void Skeleton::stopAnimationByName(const String &name) { + SkeletonAnimation *anim = getAnimation(name); + if(anim) { + stopAnimation(anim); + } +} + +void Skeleton::stopAnimation(SkeletonAnimation *animation) { + for(int i=0; i < playingAnimations.size(); i++) { + if(playingAnimations[i] == animation) { + playingAnimations[i]->Stop(); + playingAnimations.erase(playingAnimations.begin()+i); + return; + } + } +} + void Skeleton::addAnimation(const String& name, const String& fileName) { - OSFILE *inFile = OSBasics::open(fileName.c_str(), "rb"); - if(!inFile) { + + OSFILE *inFile = OSBasics::open(fileName.c_str(), "rb"); + + if(!inFile) { return; } - unsigned int activeBones,boneIndex,numPoints,numCurves, curveType; - float length; - OSBasics::read(&length, 1, sizeof(float), inFile); - SkeletonAnimation *newAnimation = new SkeletonAnimation(name, length); - - OSBasics::read(&activeBones, sizeof(unsigned int), 1, inFile); - - // Logger::log("activeBones: %d\n", activeBones); - for(int j=0; j < activeBones; j++) { - OSBasics::read(&boneIndex, sizeof(unsigned int), 1, inFile); - BoneTrack *newTrack = new BoneTrack(bones[boneIndex], length); - - BezierCurve *curve; - float vec1[2]; //,vec2[2],vec3[2]; - - OSBasics::read(&numCurves, sizeof(unsigned int), 1, inFile); - // Logger::log("numCurves: %d\n", numCurves); - for(int l=0; l < numCurves; l++) { - curve = new BezierCurve(); - OSBasics::read(&curveType, sizeof(unsigned int), 1, inFile); - OSBasics::read(&numPoints, sizeof(unsigned int), 1, inFile); - for(int k=0; k < numPoints; k++) { - OSBasics::read(vec1, sizeof(float), 2, inFile); - curve->addControlPoint2d(vec1[1], vec1[0]); - // curve->addControlPoint(vec1[1]-10, vec1[0], 0, vec1[1], vec1[0], 0, vec1[1]+10, vec1[0], 0); - } - switch(curveType) { - case 0: - newTrack->scaleX = curve; - break; - case 1: - newTrack->scaleY = curve; - break; - case 2: - newTrack->scaleZ = curve; - break; - case 3: - newTrack->QuatW = curve; - break; - case 4: - newTrack->QuatX = curve; - break; - case 5: - newTrack->QuatY = curve; - break; - case 6: - newTrack->QuatZ = curve; - break; - case 7:; - newTrack->LocX = curve; - break; - case 8: - newTrack->LocY = curve; - break; - case 9: - newTrack->LocZ = curve; - break; - } - } - - newAnimation->addBoneTrack(newTrack); - } - animations.push_back(newAnimation); - - + unsigned int activeBones,numPoints,numCurves, curveType; + float length; + OSBasics::read(&length, 1, sizeof(float), inFile); + + SkeletonAnimation *newAnimation = new SkeletonAnimation(name, length); + OSBasics::read(&activeBones, sizeof(unsigned int), 1, inFile); + + unsigned short boneNameLen; + char boneNameBuffer[1024]; + + for(int j=0; j < activeBones; j++) { + + OSBasics::read(&boneNameLen, sizeof(unsigned short), 1, inFile); + OSBasics::read(boneNameBuffer, 1, boneNameLen, inFile); + boneNameBuffer[boneNameLen] = '\0'; + + Bone *trackBone = getBoneByName(boneNameBuffer); + if(!trackBone) { + printf("WARNING, INVALID BONE NAME: %s\n", boneNameBuffer); + continue; + } + + BoneTrack *newTrack = new BoneTrack(trackBone, length); + + BezierCurve *curve; + float vec1[2]; + + OSBasics::read(&numCurves, sizeof(unsigned int), 1, inFile); + for(int l=0; l < numCurves; l++) { + curve = new BezierCurve(); + OSBasics::read(&curveType, sizeof(unsigned int), 1, inFile); + OSBasics::read(&numPoints, sizeof(unsigned int), 1, inFile); + + for(int k=0; k < numPoints; k++) { + OSBasics::read(vec1, sizeof(float), 2, inFile); + curve->addControlPoint2d(vec1[1], vec1[0]); + } + switch(curveType) { + case 0: + newTrack->scaleX = curve; + break; + case 1: + newTrack->scaleY = curve; + break; + case 2: + newTrack->scaleZ = curve; + break; + case 3: + newTrack->QuatW = curve; + break; + case 4: + newTrack->QuatX = curve; + break; + case 5: + newTrack->QuatY = curve; + break; + case 6: + newTrack->QuatZ = curve; + break; + case 7:; + newTrack->LocX = curve; + break; + case 8: + newTrack->LocY = curve; + break; + case 9: + newTrack->LocZ = curve; + break; + } + } + newAnimation->addBoneTrack(newTrack); + } + + animations.push_back(newAnimation); OSBasics::close(inFile); } @@ -291,8 +376,14 @@ void Skeleton::bonesVisible(bool val) { } BoneTrack::BoneTrack(Bone *bone, Number length) { + weight = 0.0; this->length = length; targetBone = bone; + paused = false; + time = 0.0; + speed = 1.0; + playOnce = false; + scaleX = NULL; scaleY = NULL; scaleZ = NULL; @@ -303,7 +394,8 @@ BoneTrack::BoneTrack(Bone *bone, Number length) { LocX = NULL; LocY = NULL; LocZ = NULL; - initialized = false; + + quatCurve = NULL; } BoneTrack::~BoneTrack() { @@ -317,116 +409,110 @@ BoneTrack::~BoneTrack() { delete LocX; delete LocY; delete LocZ; + delete quatCurve; } +void BoneTrack::Reset() { + time = 0.0; +} void BoneTrack::Stop() { - if(initialized) { - for(int i=0; i < pathTweens.size(); i++) { - pathTweens[i]->Pause(true); - } - quatTween->Pause(true); - } + paused = true; } -void BoneTrack::Play(bool once) { - if(!initialized ) { - // TODO: change it so that you can set the tweens to not manually restart so you can calculate the - // time per tween - - Number durTime = length; //(LocX->getControlPoint(LocX->getNumControlPoints()-1)->p2.x);//25.0f; - - BezierPathTween *testTween; - if(LocX) { - testTween = new BezierPathTween(&LocXVec, LocX, Tween::EASE_NONE, durTime, !once); - pathTweens.push_back(testTween); - } - if(LocY) { - testTween = new BezierPathTween(&LocYVec, LocY, Tween::EASE_NONE, durTime, !once); - pathTweens.push_back(testTween); - } - - if(LocZ) { - testTween = new BezierPathTween(&LocZVec, LocZ, Tween::EASE_NONE, durTime, !once); - pathTweens.push_back(testTween); - } - testTween = new BezierPathTween(&ScaleXVec, scaleX, Tween::EASE_NONE, durTime, !once); - pathTweens.push_back(testTween); - testTween = new BezierPathTween(&ScaleYVec, scaleY, Tween::EASE_NONE, durTime, !once); - pathTweens.push_back(testTween); - testTween = new BezierPathTween(&ScaleZVec, scaleZ, Tween::EASE_NONE, durTime, !once); - pathTweens.push_back(testTween); - - - if(QuatW) - quatTween = new QuaternionTween(&boneQuat, QuatW, QuatX, QuatY, QuatZ, Tween::EASE_NONE, durTime, !once); - - initialized = true; - } else { - for(int i=0; i < pathTweens.size(); i++) { - pathTweens[i]->Reset(); - pathTweens[i]->Pause(false); - } - quatTween->Reset(); - quatTween->Pause(false); - } +void BoneTrack::Play(bool once) { + paused = true; + playOnce = once; } -void BoneTrack::Update() { - +void BoneTrack::Update(Number elapsed) { if(!targetBone) return; - - Matrix4 newMatrix; - newMatrix = boneQuat.createMatrix(); - - - Matrix4 scaleMatrix; - scaleMatrix.m[0][0] *= ScaleXVec.y; - scaleMatrix.m[1][1] *= ScaleYVec.y; - scaleMatrix.m[2][2] *= ScaleZVec.y; - - - Matrix4 posMatrix; - - if(LocX) - posMatrix.m[3][0] = LocXVec.y; - else - posMatrix.m[3][0] = targetBone->getBaseMatrix()[3][0]; - - if(LocY) - posMatrix.m[3][1] = LocYVec.y; - else - posMatrix.m[3][1] = targetBone->getBaseMatrix()[3][1]; - - if(LocZ) - posMatrix.m[3][2] = LocZVec.y; - else - posMatrix.m[3][2] = targetBone->getBaseMatrix()[3][2]; - - - newMatrix = scaleMatrix*newMatrix*posMatrix; - - targetBone->setBoneMatrix(newMatrix); - targetBone->setTransformByMatrixPure(newMatrix); - + +// if(!paused) { + time += elapsed * speed; +// } + + if(time > length) { + if(playOnce) { + time = length; + return; + } else { + time = time - length; + } + } + + if(LocX) { + position.x = LocX->getYValueAtX(time); + } + if(LocY) { + position.y = LocY->getYValueAtX(time); + } + if(LocZ) { + position.z = LocZ->getYValueAtX(time); + } + + if(scaleX) { + scale.x = scaleX->getYValueAtX(time); + } + if(scaleY) { + scale.y = scaleY->getYValueAtX(time); + } + if(scaleZ) { + scale.z = scaleZ->getYValueAtX(time); + } + + if(!quatCurve) { + if(QuatW) { + quatCurve = new QuaternionCurve(QuatW, QuatX, QuatY, QuatZ); + } + } + + if(quatCurve) { + boneQuat = quatCurve->interpolate(time/length, true); + } + + if(targetBone->disableAnimation) { + return; + } + + Quaternion rotationQuat = targetBone->getRotationQuat(); + rotationQuat = Quaternion::Slerp(weight, rotationQuat, boneQuat, true); + targetBone->setRotationByQuaternion(rotationQuat); + + targetBone->setPosition((position * weight) + (targetBone->getPosition() * (1.0 - weight))); + + targetBone->setScale((scale * weight) + (targetBone->getScale() * (1.0 - weight))); + } void BoneTrack::setSpeed(Number speed) { - for(int i=0; i < pathTweens.size(); i++) { - pathTweens[i]->setSpeed(speed); - } - quatTween->setSpeed(speed); + this->speed = speed; } SkeletonAnimation::SkeletonAnimation(const String& name, Number duration) { this->name = name; this->duration = duration; + this->weight = 1.0; + this->playing = false; +} + +bool SkeletonAnimation::isPlaying() const { + return playing; } +void SkeletonAnimation::setWeight(Number newWeight) { + weight = newWeight; +} + +Number SkeletonAnimation::getWeight() const { + return weight; +} + + void SkeletonAnimation::setSpeed(Number speed) { for(int i=0; i < boneTracks.size(); i++) { boneTracks[i]->setSpeed(speed); @@ -434,25 +520,37 @@ void SkeletonAnimation::setSpeed(Number speed) { } void SkeletonAnimation::Update() { + Number elapsed = CoreServices::getInstance()->getCore()->getElapsed(); for(int i=0; i < boneTracks.size(); i++) { - boneTracks[i]->Update(); + boneTracks[i]->weight = weight; + boneTracks[i]->Update(elapsed); + } +} + +void SkeletonAnimation::Reset() { + for(int i=0; i < boneTracks.size(); i++) { + boneTracks[i]->Reset(); } } void SkeletonAnimation::Stop() { + playing = false; for(int i=0; i < boneTracks.size(); i++) { boneTracks[i]->Stop(); } } void SkeletonAnimation::Play(bool once) { + playing = true; for(int i=0; i < boneTracks.size(); i++) { boneTracks[i]->Play(once); } } SkeletonAnimation::~SkeletonAnimation() { - + for(int i=0; i < boneTracks.size(); i++) { + delete boneTracks[i]; + } } const String& SkeletonAnimation::getName() const { diff --git a/Core/Contents/Source/PolySocket.cpp b/Core/Contents/Source/PolySocket.cpp index a8401c582..f47a03b7b 100755 --- a/Core/Contents/Source/PolySocket.cpp +++ b/Core/Contents/Source/PolySocket.cpp @@ -22,6 +22,7 @@ THE SOFTWARE. #include "PolySocket.h" #include "PolyLogger.h" +#include #ifndef _WINDOWS #include diff --git a/Core/Contents/Source/PolySound.cpp b/Core/Contents/Source/PolySound.cpp index eb4d5cb97..61d731b4a 100755 --- a/Core/Contents/Source/PolySound.cpp +++ b/Core/Contents/Source/PolySound.cpp @@ -26,17 +26,40 @@ #undef OV_EXCLUDE_STATIC_CALLBACKS #include "PolyString.h" #include "PolyLogger.h" +#include "PolySoundManager.h" #include "OSBasics.h" #include #include +#include #include #define MAX_FLOAT (std::numeric_limits::infinity()) +#define INT32_MAX (std::numeric_limits::max()) using namespace std; using namespace Polycode; +AudioStreamingSource::AudioStreamingSource(unsigned int channels, unsigned int bps, unsigned int freq) : channels(channels), freq(freq), bps(bps) { +} + +unsigned int AudioStreamingSource::getNumChannels() { + return channels; +} + +unsigned int AudioStreamingSource::getBitsPerSample() { + return bps; +} + +unsigned int AudioStreamingSource::getFrequency() { + return freq; +} + +unsigned int AudioStreamingSource::streamData(char *buffer, unsigned int size) { + return 0; +} + + size_t custom_readfunc(void *ptr, size_t size, size_t nmemb, void *datasource) { OSFILE *file = (OSFILE*) datasource; return OSBasics::read(ptr, size, nmemb, file); @@ -57,20 +80,20 @@ long custom_tellfunc(void *datasource) { return OSBasics::tell(file); } -Sound::Sound(const String& fileName) : referenceDistance(1), maxDistance(MAX_FLOAT), pitch(1), volume(1), sampleLength(-1) { +Sound::Sound(const String& fileName, bool generateFloatBuffer) : referenceDistance(1), maxDistance(MAX_FLOAT), pitch(1), volume(1), sampleLength(-1), streamingSound(false) { checkALError("Construct: Loose error before construction"); soundLoaded = false; - loadFile(fileName); + loadFile(fileName, generateFloatBuffer); setIsPositional(false); checkALError("Construct from file: Finished"); } -Sound::Sound(const char *data, int size, int channels, int freq, int bps) : referenceDistance(1), maxDistance(MAX_FLOAT), pitch(1), volume(1), buffer(AL_NONE), soundSource(AL_NONE), sampleLength(-1) { +Sound::Sound(int size, const char *data, int channels, int freq, int bps, bool generateFloatBuffer) : referenceDistance(1), maxDistance(MAX_FLOAT), pitch(1), volume(1), buffer(AL_NONE), soundSource(AL_NONE), sampleLength(-1), streamingSound(false) { checkALError("Construct: Loose error before construction"); - buffer = loadBytes(data, size, freq, channels, bps); + buffer = loadBytes(data, size, freq, channels, bps, generateFloatBuffer); soundSource = GenSource(buffer); @@ -82,7 +105,76 @@ Sound::Sound(const char *data, int size, int channels, int freq, int bps) : refe checkALError("Construct from data: Finished"); } -void Sound::loadFile(String fileName) { +Sound::Sound(AudioStreamingSource *streamingSource) : referenceDistance(1), maxDistance(MAX_FLOAT), pitch(1), volume(1), buffer(AL_NONE), soundSource(AL_NONE), sampleLength(-1), streamingSound(true), streamingSource(streamingSource) { + + + alGenSources(1, &soundSource); + + alSourcef(soundSource, AL_PITCH, 1.0); + alSourcef(soundSource, AL_GAIN, 1.0); + + ALfloat sourcePos[] = {0.0, 0.0, 0.0}; + ALfloat sourceVel[] = {0.0, 0.0, 0.0}; + + alSourcefv(soundSource, AL_POSITION, sourcePos); + alSourcefv(soundSource, AL_VELOCITY, sourceVel); + + + alGenBuffers(STREAMING_BUFFER_COUNT, streamingBuffers); + + for(int i=0; i < STREAMING_BUFFER_COUNT; i++) { + if(updateALBuffer(streamingBuffers[i])) { + alSourceQueueBuffers(soundSource, 1, &streamingBuffers[i]); + } + } + Services()->getSoundManager()->registerStreamingSound(this); + + alSourcePlay(soundSource); + +} + +void Sound::updateStream() { + ALint processed = 0; + alGetSourcei(soundSource, AL_BUFFERS_PROCESSED, &processed); + + while(processed--) { + ALuint buffer; + alSourceUnqueueBuffers(soundSource, 1, &buffer); + if(updateALBuffer(buffer)) { + alSourceQueueBuffers(soundSource, 1, &buffer); + } + } + + ALenum state; + alGetSourcei(soundSource, AL_SOURCE_STATE, &state); + if(state != AL_PLAYING) { + alSourcePlay(soundSource); + } +} + +bool Sound::updateALBuffer(ALuint buffer) { + + char data[STREAMING_BUFFER_SIZE]; + unsigned int bytesStreamed = streamingSource->streamData(data, STREAMING_BUFFER_SIZE); + + if(bytesStreamed == 0) { + return false; + } + + ALenum format; + if (streamingSource->getNumChannels() == 1) { + format = (streamingSource->getBitsPerSample() == 8) ? AL_FORMAT_MONO8 : AL_FORMAT_MONO16; + } else { + format = (streamingSource->getBitsPerSample() == 8) ? AL_FORMAT_STEREO8 : AL_FORMAT_STEREO16; + } + + alBufferData(buffer, format, data, bytesStreamed, streamingSource->getFrequency()); + + return true; +} + + +void Sound::loadFile(String fileName, bool generateFloatBuffer) { if(soundLoaded) { alDeleteSources(1,&soundSource); @@ -106,9 +198,9 @@ void Sound::loadFile(String fileName) { } if(extension == "wav" || extension == "WAV") { - buffer = loadWAV(actualFilename); + buffer = loadWAV(actualFilename, generateFloatBuffer); } else if(extension == "ogg" || extension == "OGG") { - buffer = loadOGG(actualFilename); + buffer = loadOGG(actualFilename, generateFloatBuffer); } this->fileName = actualFilename; @@ -143,10 +235,19 @@ Number Sound::getPitch() { } Sound::~Sound() { + + Stop(); + + alSourcei(soundSource, AL_BUFFER, 0); + alDeleteSources(1,&soundSource); checkALError("Destroying sound"); alDeleteBuffers(1, &buffer); checkALError("Deleting buffer"); + if(streamingSound) { + alDeleteBuffers(STREAMING_BUFFER_COUNT, streamingBuffers); + Services()->getSoundManager()->unregisterStreamingSound(this); + } } void Sound::soundCheck(bool result, const String& err) { @@ -295,7 +396,6 @@ Number Sound::getMaxDistance() { return maxDistance; } - void Sound::setIsPositional(bool isPositional) { this->isPositional = isPositional; if(isPositional) { @@ -371,7 +471,7 @@ ALuint Sound::GenSource(ALuint buffer) { return source; } -ALuint Sound::loadBytes(const char *data, int size, int freq, int channels, int bps) { +ALuint Sound::loadBytes(const char *data, int size, int freq, int channels, int bps, bool generateFloatBuffer) { ALenum format; if (channels == 1) format = (bps == 8) ? AL_FORMAT_MONO8 : AL_FORMAT_MONO16; @@ -388,10 +488,19 @@ ALuint Sound::loadBytes(const char *data, int size, int freq, int channels, int alBufferData(buffer, format, data, size, freq); checkALError("LoadBytes: load buffer data"); + + if(generateFloatBuffer) { + int32_t *ptr32 = (int32_t*) &data[0]; + for(int i=0; i < size/4; i++ ) { + floatBuffer.push_back(((Number)ptr32[i])/((Number)INT32_MAX)); + } + } + return buffer; } -ALuint Sound::loadOGG(const String& fileName) { +ALuint Sound::loadOGG(const String& fileName, bool generateFloatBuffer) { +// floatBuffer.clear(); vector data; alGenBuffers(1, &buffer); @@ -444,10 +553,22 @@ ALuint Sound::loadOGG(const String& fileName) { alBufferData(buffer, format, &data[0], static_cast(data.size()), freq); + if(generateFloatBuffer) { + int32_t *ptr32 = (int32_t*) &data[0]; + for(int i=0; i < data.size()/4; i++ ) { + floatBuffer.push_back(((Number)ptr32[i])/((Number)INT32_MAX)); + } + } return buffer; } -ALuint Sound::loadWAV(const String& fileName) { + +std::vector *Sound::getFloatBuffer() { + return &floatBuffer; +} + + +ALuint Sound::loadWAV(const String& fileName, bool generateFloatBuffer) { long bytes; vector data; ALsizei freq; @@ -544,7 +665,7 @@ ALuint Sound::loadWAV(const String& fileName) { OSBasics::close(f); f = NULL; - return loadBytes(&data[0], data.size(), freq, channels, bps); + return loadBytes(&data[0], data.size(), freq, channels, bps, generateFloatBuffer); // if (buffer) // if (alIsBuffer(buffer) == AL_TRUE) // alDeleteBuffers(1, &buffer); diff --git a/Core/Contents/Source/PolySoundManager.cpp b/Core/Contents/Source/PolySoundManager.cpp index 04ae5f580..63ed2d607 100755 --- a/Core/Contents/Source/PolySoundManager.cpp +++ b/Core/Contents/Source/PolySoundManager.cpp @@ -30,47 +30,43 @@ SoundManager::SoundManager() { } void SoundManager::initAL() { - alGetError(); - if(alcGetCurrentContext() == 0) { - Logger::log("AL already initialized\n"); - } - + captureDevice = NULL; + device = alcOpenDevice(0); if(device == 0) { Logger::log("InitializeAL: Cannot open preferred device\n"); return; } - - if (alcGetError(device) != ALC_NO_ERROR) { + + ALCenum error = alcGetError(device); + if (error != ALC_NO_ERROR) { alcCloseDevice(device); - // PCCE_THROW("InitializeAL: Could not open device (alc error)"); + Logger::log("InitializeAL: Could not open device (%d).", error); + return; } context = alcCreateContext(device, 0); if (context == 0) { alcCloseDevice(device); - // PCCE_THROW("InitializeAL: Could not create context"); + Logger::log("InitializeAL: Could not create context"); + return; } - if (alcGetError(device) != ALC_NO_ERROR) { + + error = alcGetError(device); + if (error != ALC_NO_ERROR) { alcDestroyContext(context); alcCloseDevice(device); - // PCCE_THROW("InitializeAL: Could not open device (alc error)"); + Logger::log("InitializeAL: Could not open device (%d).", error); + return; } if (alcMakeContextCurrent(context) != ALC_TRUE) { alcDestroyContext(context); alcCloseDevice(device); - // PCCE_THROW("InitializeAL: Could not make context current"); - } - if (alcGetError(device) != ALC_NO_ERROR) { - alcMakeContextCurrent(0); - alcDestroyContext(context); - alcCloseDevice(device); - // PCCE_THROW("InitializeAL: Could not make context current (alc error)"); + Logger::log("InitializeAL: Could not make context current"); + return; } - alGetError(); - ALfloat listenerPos[] = { 0.0, 0.0, 0.0 }; ALfloat listenerVel[] = { 0.0, 0.0, 0.0 }; ALfloat listenerOri[] = { 0.0, 0.0, -1.0, 0.0, 1.0, 0.0 }; @@ -78,13 +74,8 @@ void SoundManager::initAL() { alListenerfv(AL_POSITION, listenerPos); alListenerfv(AL_VELOCITY, listenerVel); alListenerfv(AL_ORIENTATION, listenerOri); - if (alGetError() != AL_NO_ERROR) { -// ShutdownAL(); -// PCCE_THROW("InitializeAL: Could not set listener position"); - } alDistanceModel(AL_LINEAR_DISTANCE_CLAMPED); -// alDistanceModel(AL_INVERSE_DISTANCE_CLAMPED); Logger::log("OpenAL initialized...\n"); } @@ -109,6 +100,75 @@ void SoundManager::setListenerOrientation(Vector3 orientation, Vector3 upVector) alListenerfv(AL_ORIENTATION,ori); } +bool SoundManager::recordSound(unsigned int rate, unsigned int sampleSize) { + + if(captureDevice) { + Logger::log("Error: Audio capture already in progress\n"); + return false; + } + + captureDevice = alcCaptureOpenDevice(NULL, rate, AL_FORMAT_STEREO16, sampleSize); + if (alGetError() != AL_NO_ERROR) { + captureDevice = NULL; + return false; + } + recordingBufferRate = rate; + + recordingBuffer = (ALbyte*) malloc(1); + recordingBufferSize = 0; + + alcCaptureStart(captureDevice); + return true; +} + +Sound *SoundManager::stopRecording(bool generateFloatBuffer) { + if(!captureDevice) { + Logger::log("No recording in process\n"); + return NULL; + } + alcCaptureStop(captureDevice); + alcCaptureCloseDevice(captureDevice); + captureDevice = NULL; + + Sound *newSound = new Sound(recordingBufferSize, (const char*)recordingBuffer, 2, recordingBufferRate, 16, generateFloatBuffer); + + free(recordingBuffer); + + return newSound; +} + +void SoundManager::registerStreamingSound(Sound *sound) { + streamingSounds.push_back(sound); +} + +void SoundManager::unregisterStreamingSound(Sound *sound) { + for(int i=0; i < streamingSounds.size(); i++) { + if(streamingSounds[i] == sound) { + streamingSounds.erase(streamingSounds.begin()+i); + return; + } + } +} + +void SoundManager::Update() { + // if recording sound, save samples + if(captureDevice) { + ALint samples; + alcGetIntegerv(captureDevice, ALC_CAPTURE_SAMPLES, (ALCsizei)sizeof(ALint), &samples); + if(samples) { + unsigned int newBufferSize = sizeof(ALbyte) * samples * 4; + recordingBuffer = (ALbyte*) realloc(recordingBuffer, recordingBufferSize + newBufferSize); + + alcCaptureSamples(captureDevice, (ALCvoid *)(recordingBuffer+recordingBufferSize), samples); + recordingBufferSize += newBufferSize; + } + } + + for(int i=0; i < streamingSounds.size(); i++) { + streamingSounds[i]->updateStream(); + } +} + SoundManager::~SoundManager() { if (context != 0 ) { alcSuspendContext(context); diff --git a/Core/Contents/Source/PolyString.cpp b/Core/Contents/Source/PolyString.cpp index 48080077d..e6bdd9567 100644 --- a/Core/Contents/Source/PolyString.cpp +++ b/Core/Contents/Source/PolyString.cpp @@ -21,6 +21,8 @@ */ #include "PolyString.h" +#include +#include using namespace Polycode; using namespace std; @@ -70,7 +72,7 @@ size_t String::getDataSizeWithEncoding(int encoding) const { return contents.size(); } default: - return NULL; + return 0; } } const char *String::getDataWithEncoding(int encoding) const { @@ -155,6 +157,14 @@ vector String::split(const String &delim) const { return tokens; } +Number String::toNumber() { + return atof(contents.c_str()); +} + +int String::toInteger() { + return atoi(contents.c_str()); +} + String String::replace(const String &what, const String &withWhat) const { size_t pos = 0; @@ -180,16 +190,15 @@ String String::toUpperCase() const { String String::NumberToString(Number value, int precision) { - char temp[128]; - String precisionStr = String("%.")+IntToString(precision)+String("f"); - sprintf(temp, precisionStr.c_str(), value); - return String(temp); + stringstream ss; + ss << fixed << setprecision(precision) << value; + return String(ss.str()); } String String::IntToString(int value) { - char temp[128]; - sprintf(temp, "%d", value); - return String(temp); + stringstream ss; + ss << value; + return String(ss.str()); } diff --git a/Core/Contents/Source/PolyTextMesh.cpp b/Core/Contents/Source/PolyTextMesh.cpp new file mode 100644 index 000000000..6eaeb2a7d --- /dev/null +++ b/Core/Contents/Source/PolyTextMesh.cpp @@ -0,0 +1,46 @@ + +#include "PolyTextMesh.h" +#include "PolyFontGlyphSheet.h" + +using namespace Polycode; + +TextMesh::TextMesh(FontGlyphSheet *font, const String &text) +: Mesh(QUAD_MESH) +, font(font) +, text(text) +{ + rebuild(); +} + +void TextMesh::setFont(FontGlyphSheet *font) { + this->font = font; + rebuild(); +} + +void TextMesh::setText(const String &text) { + if (text == this->text) return; + this->text = text; + rebuild(); +} + +void TextMesh::rebuild() { + + // TODO: FIX +/* + if (text == "" || font == NULL) { + for (std::vector::iterator it = vertices.begin(); it != vertices.end(); it++) delete *it; + vertices.clear(); + return; + } + + int last = font->renderStringVertices(text, vertices); + if (last < vertices.size()) { + for (int i = last; i < vertices.size(); i++) { + delete vertices[i]; + } + vertices.resize(last); + } + */ +} + + diff --git a/Core/Contents/Source/PolyTexture.cpp b/Core/Contents/Source/PolyTexture.cpp index 022a235e3..8d552d488 100755 --- a/Core/Contents/Source/PolyTexture.cpp +++ b/Core/Contents/Source/PolyTexture.cpp @@ -22,6 +22,7 @@ #include "string.h" #include "PolyTexture.h" +#include using namespace Polycode; @@ -39,7 +40,7 @@ Texture::Texture(unsigned int width, unsigned int height, char *textureData,bool pixelSize = 4; break; case Image::IMAGE_FP16: - pixelSize = 16; + pixelSize = 12; break; default: pixelSize = 4; @@ -74,6 +75,7 @@ int Texture::getHeight() const { } Texture::~Texture(){ + printf("DELETING TEXTURE: [%s][%s]\n", getResourceName().c_str(), getResourcePath().c_str()); free(textureData); } @@ -87,7 +89,7 @@ void Texture::setImageData(Image *data) { pixelSize = 4; break; case Image::IMAGE_FP16: - pixelSize = 16; + pixelSize = 12; break; default: pixelSize = 4; diff --git a/Core/Contents/Source/PolyTween.cpp b/Core/Contents/Source/PolyTween.cpp index 2f02e6aba..d51f67adc 100755 --- a/Core/Contents/Source/PolyTween.cpp +++ b/Core/Contents/Source/PolyTween.cpp @@ -45,16 +45,19 @@ Tween:: Tween(Number *target, int easeType, Number startVal, Number endVal, Numb tweenTime = 0; if(waitTime == 0.0) *targetVal = startVal; - tweenTimer = new Timer(true, 1); - tweenTimer->addEventListener(this, 0); complete = false; - + paused = false; + actEndTime = time; CoreServices::getInstance()->getTweenManager()->addTween(this); } +Number *Tween::getTarget() { + return targetVal; +} + void Tween::Pause(bool pauseVal) { - tweenTimer->Pause(pauseVal); + paused = pauseVal; } void Tween::setSpeed(Number speed) { @@ -65,11 +68,6 @@ void Tween::setSpeed(Number speed) { } Tween::~Tween() { - tweenTimer->removeEventListener(this, 0); - delete tweenTimer; - - deleteOnComplete = false; // Prevent loop when we removeTween in next line. - CoreServices::getInstance()->getTweenManager()->removeTween(this); } bool Tween::isComplete() { @@ -80,7 +78,14 @@ void Tween::doOnComplete() { dispatchEvent(new Event(), Event::COMPLETE_EVENT); } -void Tween::handleEvent(Event *event) { +void Tween::updateTween(Number elapsed) { + + if(paused) { + return; + } + + tweenTime += elapsed; + if(tweenTime >= endTime+waitTime) { if(repeat){ Reset(); @@ -96,13 +101,13 @@ void Tween::handleEvent(Event *event) { localTargetVal = interpolateTween(); *targetVal = localTargetVal; } - tweenTime += tweenTimer->getElapsedf(); updateCustomTween(); } void Tween::Reset() { tweenTime = 0; complete = false; + *targetVal = startVal; } Number Tween::interpolateTween() { @@ -229,6 +234,7 @@ BezierPathTween::BezierPathTween(Vector3 *target, BezierCurve *curve, int easeTy this->curve = curve; this->target = target; pathValue = 0; + updateCustomTween(); } void BezierPathTween::updateCustomTween() { diff --git a/Core/Contents/Source/PolyTweenManager.cpp b/Core/Contents/Source/PolyTweenManager.cpp index a017bb4c9..5d6dab8af 100755 --- a/Core/Contents/Source/PolyTweenManager.cpp +++ b/Core/Contents/Source/PolyTweenManager.cpp @@ -22,11 +22,11 @@ #include "PolyTweenManager.h" #include "PolyTween.h" +#include "PolyCore.h" using namespace Polycode; TweenManager::TweenManager() { - } TweenManager::~TweenManager() { @@ -34,35 +34,70 @@ TweenManager::~TweenManager() { } void TweenManager::addTween(Tween *tween) { - tweens.push_back(tween); + tweensToAdd.push_back(tween); } void TweenManager::removeTween(Tween *tween) { - for(int i=0;ideleteOnComplete) - delete tween; - + return; } } } -void TweenManager::Update() { - Tween *tween; - for(int i=0;iisComplete()) { - if(tweens[i]->repeat) { - tweens[i]->Reset(); - return; +void TweenManager::removeTweensForTarget(Number *target) { + std::vector::iterator iter = tweens.begin(); + while (iter != tweens.end()) { + bool mustRemove = false; + if(target == (*iter)->getTarget()) { + mustRemove = true; + if((*iter)->deleteOnComplete) { + Tween *tween = (*iter); + delete tween; + } + } + + if(mustRemove) { + iter = tweens.erase(iter); + } else { + ++iter; + } + } +} + +void TweenManager::Update(Number elapsed) { + + std::vector::iterator iter = tweens.begin(); + while (iter != tweens.end()) { + bool mustRemove = false; + + (*iter)->updateTween(elapsed/1000.0); + + if((*iter)->isComplete()) { + if((*iter)->repeat) { + (*iter)->Reset(); } else { - tween = tweens[i]; - tweens.erase(tweens.begin()+i); - tween->doOnComplete(); - if(tween->deleteOnComplete) + mustRemove = true; + (*iter)->doOnComplete(); + + if((*iter)->deleteOnComplete) { + Tween *tween = (*iter); delete tween; - return; + } } } + + if(mustRemove) { + iter = tweens.erase(iter); + } else { + ++iter; + } + } + + for(int i=0; i < tweensToAdd.size(); i++) { + tweens.push_back(tweensToAdd[i]); } + tweensToAdd.clear(); + } diff --git a/Core/Contents/Source/PolyVector4.cpp b/Core/Contents/Source/PolyVector4.cpp new file mode 100755 index 000000000..e273309d8 --- /dev/null +++ b/Core/Contents/Source/PolyVector4.cpp @@ -0,0 +1,47 @@ +/* + Copyright (C) 2011 by Ivan Safrin + + 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: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + 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. +*/ + +#include "PolyVector4.h" + +using namespace Polycode; + +Vector4::Vector4() : x(0),y(0),z(0), w(0){ +} + +void Vector4::set(Number x, Number y, Number z, Number w) { + this->x = x; + this->y = y; + this->z = z; + this->w = w; +} + +Vector4::Vector4(Number x,Number y,Number z, Number w) { + set(x, y, z, w); +} + +Vector4::Vector4(Number val) { + set(val,val,val,val); +} + +Vector4::~Vector4() { + +} diff --git a/Core/Contents/Source/PolyVertex.cpp b/Core/Contents/Source/PolyVertex.cpp deleted file mode 100755 index 4828b11b5..000000000 --- a/Core/Contents/Source/PolyVertex.cpp +++ /dev/null @@ -1,111 +0,0 @@ -/* - Copyright (C) 2011 by Ivan Safrin - - 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: - - The above copyright notice and this permission notice shall be included in - all copies or substantial portions of the Software. - - 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. -*/ - -#include "PolyVertex.h" - -using namespace Polycode; - -Vertex::Vertex() : Vector3(0,0,0) { - texCoord = Vector2(0,0); - normal = Vector3(0,0,0); - useVertexColor = false; -} - -Vertex::Vertex(Number pos_x, Number pos_y, Number pos_z, Number nor_x, Number nor_y, Number nor_z) : Vector3(pos_x, pos_y, pos_z) { - normal = Vector3(nor_x, nor_y, nor_z); - texCoord = Vector2(0,0); - useVertexColor = false; - restPosition.set(pos_x, pos_y, pos_z); -} - -Vertex::Vertex(Number pos_x, Number pos_y, Number pos_z, Number nor_x, Number nor_y, Number nor_z, Number u, Number v): Vector3(pos_x, pos_y, pos_z) { - normal = Vector3(nor_x, nor_y, nor_z); - texCoord = Vector2(u,v); - useVertexColor = false; - restPosition.set(pos_x, pos_y, pos_z); -} - -Vertex::Vertex(Number x, Number y, Number z) : Vector3(x,y,z) { - useVertexColor = false; - restPosition.set(x, y, z); -} - -Vertex::Vertex(Number x, Number y, Number z, Number u, Number v) : Vector3(x,y,z) { - texCoord = Vector2(u,v); - useVertexColor = false; - restPosition.set(x, y, z); -} - -void Vertex::addBoneAssignment(unsigned int boneID, Number boneWeight) { - BoneAssignment *newBas = new BoneAssignment(); - newBas->boneID = boneID; - if(boneWeight > 1) - boneWeight = 1; - if(boneWeight < 0) - boneWeight = 0; - - newBas->weight = boneWeight; - boneAssignments.push_back(newBas); -} - -void Vertex::setNormal(Number x, Number y, Number z) { - normal.x = x; - normal.y = y; - normal.z = z; -} - -void Vertex::normalizeWeights() { - Number allWeights = 0; -// if(boneAssignments.size() == 1) -// if(boneAssignments[0]->weight < 1) -// return; - - for(int i =0 ;i < boneAssignments.size(); i++) { - allWeights += boneAssignments[i]->weight; - } - - for(int i =0 ;i < boneAssignments.size(); i++) { - boneAssignments[i]->weight *= 1.0f/allWeights; - } -} - -int Vertex::getNumBoneAssignments() { - return boneAssignments.size(); -} - -BoneAssignment *Vertex::getBoneAssignment(unsigned int index) { - return boneAssignments[index]; -} - -Vertex::~Vertex() { -// delete normal; -// delete texCoord; -} - -Vector2 Vertex::getTexCoord() { - return texCoord; -} - -void Vertex::setTexCoord(Number u, Number v) { - texCoord.x = u; - texCoord.y = v; -} \ No newline at end of file diff --git a/Core/Contents/Source/PolyWinCore.cpp b/Core/Contents/Source/PolyWinCore.cpp index dedff25c8..8cbfcac50 100644 --- a/Core/Contents/Source/PolyWinCore.cpp +++ b/Core/Contents/Source/PolyWinCore.cpp @@ -38,6 +38,11 @@ #include #include +#include + +#include // std::cout +#include // std::string, std::to_string + #if defined(_MINGW) #ifndef MAPVK_VSC_TO_VK_EX #define MAPVK_VSC_TO_VK_EX 3 @@ -45,12 +50,14 @@ #else PFNWGLSWAPINTERVALEXTPROC wglSwapIntervalEXT = NULL; PFNWGLGETSWAPINTERVALEXTPROC wglGetSwapIntervalEXT = NULL; +PFNWGLCHOOSEPIXELFORMATARBPROC wglChoosePixelFormatARB = NULL; + #endif using namespace Polycode; long getThreadID() { - return 0; + return GetCurrentThreadId(); } extern Win32Core *core; @@ -78,13 +85,16 @@ void Core::getScreenInfo(int *width, int *height, int *hz) { if (hz) *hz = mode.dmDisplayFrequency; } -Win32Core::Win32Core(PolycodeViewBase *view, int _xRes, int _yRes, bool fullScreen, bool vSync, int aaLevel, int anisotropyLevel, int frameRate, int monitorIndex) +Win32Core::Win32Core(PolycodeViewBase *view, int _xRes, int _yRes, bool fullScreen, bool vSync, int aaLevel, int anisotropyLevel, int frameRate, int monitorIndex, bool retinaSupport) : Core(_xRes, _yRes, fullScreen, vSync, aaLevel, anisotropyLevel, frameRate, monitorIndex) { hWnd = *((HWND*)view->windowData); + hInstance = (HINSTANCE)GetWindowLong(hWnd, GWL_HINSTANCE); core = this; hasCopyDataString = false; + scaleFactor = 1.0; + char *buffer = _getcwd(NULL, 0); defaultWorkingDirectory = String(buffer); free(buffer); @@ -101,7 +111,6 @@ Win32Core::Win32Core(PolycodeViewBase *view, int _xRes, int _yRes, bool fullScre hDC = NULL; hRC = NULL; - PixelFormat = 0; this->aaLevel = 999; @@ -111,10 +120,15 @@ Win32Core::Win32Core(PolycodeViewBase *view, int _xRes, int _yRes, bool fullScre eventMutex = createMutex(); isFullScreen = fullScreen; + this->resizable = view->resizable; renderer = new OpenGLRenderer(); services->setRenderer(renderer); + renderer->setBackingResolutionScale(scaleFactor, scaleFactor); + + getWglFunctionPointers(); + setVideoMode(xRes, yRes, fullScreen, vSync, aaLevel, anisotropyLevel); WSADATA WsaData; @@ -124,9 +138,6 @@ Win32Core::Win32Core(PolycodeViewBase *view, int _xRes, int _yRes, bool fullScre ((OpenGLRenderer*)renderer)->Init(); - wglSwapIntervalEXT = (PFNWGLSWAPINTERVALEXTPROC) wglGetProcAddress("wglSwapIntervalEXT"); - wglGetSwapIntervalEXT = (PFNWGLGETSWAPINTERVALEXTPROC) wglGetProcAddress("wglGetSwapIntervalEXT"); - LARGE_INTEGER li; QueryPerformanceFrequency(&li); pcFreq = double(li.QuadPart)/1000.0; @@ -136,6 +147,14 @@ Win32Core::Win32Core(PolycodeViewBase *view, int _xRes, int _yRes, bool fullScre CoreServices::getInstance()->installModule(new GLSLShaderModule()); } +Number Win32Core::getBackingXRes() { + return getXRes() *scaleFactor; +} + +Number Win32Core::getBackingYRes() { + return getYRes() *scaleFactor; +} + Win32Core::~Win32Core() { shutdownGamepad(); destroyContext(); @@ -150,7 +169,29 @@ void Win32Core::enableMouse(bool newval) { void Win32Core::captureMouse(bool newval) { // Capture the mouse in the window holding // our polycode screen. - SetCapture(hWnd); + if (newval){ + RECT rect; + GetWindowRect(core->hWnd, &rect); + + RECT crect; + RECT arect; + + GetClientRect(core->hWnd, &crect); + arect = crect; + if (!fullScreen){ + AdjustWindowRectEx(&arect, WS_CAPTION | WS_BORDER, FALSE, 0); + } + + rect.left += (crect.left - arect.left); + rect.right += (crect.right - arect.right); + rect.top += (crect.top - arect.top); + rect.bottom += (crect.bottom - arect.bottom); + + ClipCursor(&rect); + } + else { + ClipCursor(NULL); + } Core::captureMouse(newval); } @@ -168,7 +209,7 @@ void Win32Core::warpCursor(int x, int y) { unsigned int Win32Core::getTicks() { LARGE_INTEGER li; QueryPerformanceCounter(&li); - return unsigned int(li.QuadPart / pcFreq); + return (unsigned int)(li.QuadPart / pcFreq); } void Win32Core::Render() { @@ -178,9 +219,10 @@ void Win32Core::Render() { SwapBuffers(hDC); } -bool Win32Core::Update() { +bool Win32Core::systemUpdate() { if(!running) return false; + captureMouse(Core::mouseCaptured); doSleep(); checkEvents(); Gamepad_processEvents(); @@ -198,7 +240,7 @@ void Win32Core::setVSync(bool vSyncVal) { } } -void Win32Core::setVideoMode(int xRes, int yRes, bool fullScreen, bool vSync, int aaLevel, int anisotropyLevel) { +void Win32Core::setVideoMode(int xRes, int yRes, bool fullScreen, bool vSync, int aaLevel, int anisotropyLevel, bool retinaSupport) { bool resetContext = false; @@ -206,6 +248,8 @@ void Win32Core::setVideoMode(int xRes, int yRes, bool fullScreen, bool vSync, in resetContext = true; } + bool wasFullscreen = this->fullScreen; + this->xRes = xRes; this->yRes = yRes; this->fullScreen = fullScreen; @@ -215,6 +259,7 @@ void Win32Core::setVideoMode(int xRes, int yRes, bool fullScreen, bool vSync, in SetWindowLong(hWnd, GWL_STYLE, WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_POPUP); ShowWindow(hWnd, SW_SHOW); + MoveWindow(hWnd, 0, 0, xRes, yRes, TRUE); DEVMODE dmScreenSettings; // Device Mode memset(&dmScreenSettings,0,sizeof(dmScreenSettings)); // Makes Sure Memory's Cleared @@ -225,22 +270,31 @@ void Win32Core::setVideoMode(int xRes, int yRes, bool fullScreen, bool vSync, in dmScreenSettings.dmFields=DM_BITSPERPEL|DM_PELSWIDTH|DM_PELSHEIGHT; ChangeDisplaySettings(&dmScreenSettings,CDS_FULLSCREEN); - SetWindowPos(hWnd, NULL, 0, 0, xRes, yRes, SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE); + //SetWindowPos(hWnd, NULL, 0, 0, xRes, yRes, SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE); } else { - // SetWindowLong(hWnd, GWL_STYLE, WS_OVERLAPPED|WS_SYSMENU); - // ShowWindow(hWnd, SW_SHOW); - ClientResize(hWnd, xRes, yRes); + RECT rect; + rect.left = 0; + rect.top = 0; + rect.right = xRes; + rect.bottom = yRes; + if (resizable){ + SetWindowLongPtr(hWnd, GWL_STYLE, WS_OVERLAPPEDWINDOW | WS_SYSMENU | WS_VISIBLE); + AdjustWindowRect(&rect, WS_OVERLAPPEDWINDOW | WS_SYSMENU, FALSE); + } else { + SetWindowLongPtr(hWnd, GWL_STYLE, WS_CAPTION | WS_POPUP | WS_SYSMENU | WS_VISIBLE); + AdjustWindowRect(&rect, WS_CAPTION | WS_POPUP | WS_SYSMENU, FALSE); + } + MoveWindow(hWnd, 0, 0, rect.right-rect.left, rect.bottom-rect.top, TRUE); + + ChangeDisplaySettings(0, 0); + } isFullScreen = fullScreen; if(resetContext) { - initContext(false, 0); - - if(aaLevel > 0) { - initMultisample(aaLevel); - } + initContext(aaLevel); } setVSync(vSync); @@ -251,47 +305,138 @@ void Win32Core::setVideoMode(int xRes, int yRes, bool fullScreen, bool vSync, in core->dispatchEvent(new Event(), Core::EVENT_CORE_RESIZE); } -void Win32Core::initContext(bool usePixelFormat, unsigned int pixelFormat) { +void Win32Core::getWglFunctionPointers() { + + PIXELFORMATDESCRIPTOR pfd; + memset(&pfd, 0, sizeof(PIXELFORMATDESCRIPTOR)); + pfd.nSize = sizeof(PIXELFORMATDESCRIPTOR); + pfd.nVersion = 1; + pfd.dwFlags = PFD_DOUBLEBUFFER | + PFD_SUPPORT_OPENGL | + PFD_DRAW_TO_WINDOW; + pfd.iPixelType = PFD_TYPE_RGBA; + pfd.cColorBits = 24; + pfd.cDepthBits = 16; + pfd.cAccumBlueBits = 8; + pfd.cAccumRedBits = 8; + pfd.cAccumGreenBits = 8; + pfd.cAccumAlphaBits = 8; + pfd.cAccumBits = 24; + pfd.iLayerType = PFD_MAIN_PLANE; + + WNDCLASSEX wcex; + + wcex.cbSize = sizeof(WNDCLASSEX); + wcex.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC; + wcex.lpfnWndProc = DefWindowProc; + wcex.cbClsExtra = 0; + wcex.cbWndExtra = 0; + wcex.hInstance = hInstance; + wcex.hIcon = LoadIcon(hInstance, IDI_APPLICATION); + wcex.hCursor = LoadCursor(NULL, IDC_ARROW); + wcex.hbrBackground = NULL; + wcex.lpszMenuName = NULL; + wcex.lpszClassName = L"FAKECONTEXTCLASS"; + wcex.hIconSm = LoadIcon(hInstance, IDI_APPLICATION); + + RegisterClassEx(&wcex); + + HWND tempHWND = CreateWindowEx(WS_EX_APPWINDOW, L"FAKECONTEXTCLASS", L"FAKE", WS_OVERLAPPEDWINDOW | WS_MAXIMIZE | WS_CLIPCHILDREN, 0, 0, 0, 0, NULL, NULL, hInstance, NULL); + //CreateWindow(_T("FakeWindow"), _T("FAKE"), WS_OVERLAPPEDWINDOW | WS_MAXIMIZE | WS_CLIPCHILDREN, 0, 0, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInstance, NULL); + + HGLRC tempHRC; + unsigned int PixelFormat; + + HDC tempDC = GetDC(tempHWND); + PixelFormat = ChoosePixelFormat(tempDC, &pfd); + SetPixelFormat(tempDC, PixelFormat, &pfd); + + tempHRC = wglCreateContext(tempDC); + wglMakeCurrent(tempDC, tempHRC); + + wglSwapIntervalEXT = (PFNWGLSWAPINTERVALEXTPROC)wglGetProcAddress("wglSwapIntervalEXT"); + wglGetSwapIntervalEXT = (PFNWGLGETSWAPINTERVALEXTPROC)wglGetProcAddress("wglGetSwapIntervalEXT"); + wglChoosePixelFormatARB = (PFNWGLCHOOSEPIXELFORMATARBPROC)wglGetProcAddress("wglChoosePixelFormatARB"); + + wglMakeCurrent(NULL, NULL); + wglDeleteContext(tempHRC); + DestroyWindow(tempHWND); + +} + +void Win32Core::initContext(int aaLevel) { destroyContext(); - memset(&pfd, 0, sizeof(PIXELFORMATDESCRIPTOR)) ; - pfd.nSize = sizeof(PIXELFORMATDESCRIPTOR); - pfd.nVersion = 1 ; - pfd.dwFlags = PFD_DOUBLEBUFFER | - PFD_SUPPORT_OPENGL | - PFD_DRAW_TO_WINDOW ; - pfd.iPixelType = PFD_TYPE_RGBA ; - pfd.cColorBits = 24; - pfd.cDepthBits = 16; - pfd.cAccumBlueBits = 8; - pfd.cAccumRedBits = 8; - pfd.cAccumGreenBits = 8; - pfd.cAccumAlphaBits = 8; - pfd.cAccumBits = 24; - pfd.iLayerType = PFD_MAIN_PLANE ; - - - if (!(hDC=GetDC(hWnd))) // Did We Get A Device Context? - { + int pixelFormat; + bool intializedAA = false; + + + if (!(hDC = GetDC(hWnd))) { Logger::log("Can't Create A GL Device Context.\n"); - return; // Return FALSE + return; } - if(usePixelFormat) { - PixelFormat = pixelFormat; - } else { - if (!(PixelFormat=ChoosePixelFormat(hDC,&pfd))) // Did Windows Find A Matching Pixel Format? + if (aaLevel > 0) { + if (!wglChoosePixelFormatARB) { + Logger::log("Multisampling not supported!\n"); + } else { + + UINT numFormats; + float fAttributes[] = { 0, 0 }; + + int attributes[] = { + WGL_DRAW_TO_WINDOW_ARB, GL_TRUE, + WGL_SUPPORT_OPENGL_ARB, GL_TRUE, + WGL_DOUBLE_BUFFER_ARB, GL_TRUE, + WGL_PIXEL_TYPE_ARB, WGL_TYPE_RGBA_ARB, + WGL_COLOR_BITS_ARB, 24, + WGL_DEPTH_BITS_ARB, 16, + WGL_STENCIL_BITS_ARB, 8, + WGL_SAMPLE_BUFFERS_ARB, 1, + WGL_SAMPLES_ARB, aaLevel, + 0 + }; + + if (!wglChoosePixelFormatARB(hDC, attributes, fAttributes, 1, &pixelFormat, &numFormats)) { + Logger::log("Invalid pixel format chosen\n"); + + } else { + intializedAA = true; + } + } + } + + PIXELFORMATDESCRIPTOR pfd; + + if (!intializedAA) { + memset(&pfd, 0, sizeof(PIXELFORMATDESCRIPTOR)); + pfd.nSize = sizeof(PIXELFORMATDESCRIPTOR); + pfd.nVersion = 1; + pfd.dwFlags = PFD_DOUBLEBUFFER | + PFD_SUPPORT_OPENGL | + PFD_DRAW_TO_WINDOW; + pfd.iPixelType = PFD_TYPE_RGBA; + pfd.cColorBits = 24; + pfd.cDepthBits = 16; + pfd.cAccumBlueBits = 8; + pfd.cAccumRedBits = 8; + pfd.cAccumGreenBits = 8; + pfd.cAccumAlphaBits = 8; + pfd.cAccumBits = 24; + pfd.iLayerType = PFD_MAIN_PLANE; + + if (!(pixelFormat = ChoosePixelFormat(hDC, &pfd))) // Did Windows Find A Matching Pixel Format? { Logger::log("Can't Find A Suitable PixelFormat.\n"); return; // Return FALSE } } - Logger::log("Setting format: %d\n", PixelFormat); - if(!SetPixelFormat(hDC,PixelFormat,&pfd)) // Are We Able To Set The Pixel Format? + Logger::log("Setting format: %d\n", pixelFormat); + if(!SetPixelFormat(hDC,pixelFormat,&pfd)) // Are We Able To Set The Pixel Format? { - Logger::log("Can't Set The PixelFormat: %d.\n", PixelFormat); + Logger::log("Can't Set The PixelFormat: %d.\n", pixelFormat); return; // Return FALSE } @@ -306,6 +451,10 @@ void Win32Core::initContext(bool usePixelFormat, unsigned int pixelFormat) { Logger::log("Can't Activate The GL Rendering Context.\n"); return; // Return FALSE } + + if (intializedAA) { + glEnable(GL_MULTISAMPLE_ARB); + } } void Win32Core::destroyContext() { @@ -322,43 +471,6 @@ void Win32Core::destroyContext() { ChangeDisplaySettings (NULL,0); } -void Win32Core::initMultisample(int numSamples) { - - PFNWGLCHOOSEPIXELFORMATARBPROC wglChoosePixelFormatARB = - (PFNWGLCHOOSEPIXELFORMATARBPROC)wglGetProcAddress("wglChoosePixelFormatARB"); - - if (!wglChoosePixelFormatARB) { - Logger::log("Multisampling not supported!\n"); - return; - } - int pixelFormat; - UINT numFormats; - float fAttributes[] = {0,0}; - - int iAttributes[] = { WGL_DRAW_TO_WINDOW_ARB,GL_TRUE, - WGL_SUPPORT_OPENGL_ARB,GL_TRUE, - WGL_ACCELERATION_ARB, WGL_FULL_ACCELERATION_ARB, - WGL_COLOR_BITS_ARB,24, - WGL_DEPTH_BITS_ARB,24, - WGL_DOUBLE_BUFFER_ARB,GL_TRUE, - WGL_ACCUM_GREEN_BITS_ARB, 8, - WGL_ACCUM_RED_BITS_ARB, 8, - WGL_ACCUM_BLUE_BITS_ARB, 8, - WGL_ACCUM_ALPHA_BITS_ARB, 8, - WGL_SAMPLE_BUFFERS_ARB,GL_TRUE, - WGL_SAMPLES_ARB, numSamples , - 0,0}; - - if(!wglChoosePixelFormatARB(hDC,iAttributes,fAttributes,1,&pixelFormat,&numFormats)) { - Logger::log("Invalid pixel format chosen\n"); - return; - } - - // initContext(true, pixelFormat); - - glEnable(GL_MULTISAMPLE_ARB); -} - void Win32Core::initKeymap() { for (int i=0; i<1024; ++i ) @@ -539,38 +651,38 @@ void Win32Core::handleKeyUp(LPARAM lParam, WPARAM wParam) { #ifndef NO_TOUCH_API void Win32Core::handleTouchEvent(LPARAM lParam, WPARAM wParam) { - - // Bail out now if multitouch is not available on this system - if ( hasMultiTouch == false ) - { - return; - } - - lockMutex(eventMutex); - int iNumContacts = LOWORD(wParam); - HTOUCHINPUT hInput = (HTOUCHINPUT)lParam; - TOUCHINPUT *pInputs = new TOUCHINPUT[iNumContacts]; - - if(pInputs != NULL) { - if(GetTouchInputInfoFunc(hInput, iNumContacts, pInputs, sizeof(TOUCHINPUT))) { - - std::vector touches; - for(int i = 0; i < iNumContacts; i++) { - TOUCHINPUT ti = pInputs[i]; - TouchInfo touchInfo; - touchInfo.id = (int) ti.dwID; - - POINT pt; - pt.x = TOUCH_COORD_TO_PIXEL(ti.x); - pt.y = TOUCH_COORD_TO_PIXEL(ti.y); - ScreenToClient(hWnd, &pt); - touchInfo.position.x = pt.x; - touchInfo.position.y = pt.y; - - touches.push_back(touchInfo); - } - for(int i = 0; i < iNumContacts; i++) { + // Bail out now if multitouch is not available on this system + if (hasMultiTouch == false) + { + return; + } + + lockMutex(eventMutex); + + int iNumContacts = LOWORD(wParam); + HTOUCHINPUT hInput = (HTOUCHINPUT)lParam; + TOUCHINPUT *pInputs = new TOUCHINPUT[iNumContacts]; + + if (pInputs != NULL) { + if (GetTouchInputInfoFunc(hInput, iNumContacts, pInputs, sizeof(TOUCHINPUT))) { + + std::vector touches; + for (int i = 0; i < iNumContacts; i++) { + TOUCHINPUT ti = pInputs[i]; + TouchInfo touchInfo; + touchInfo.id = (int)ti.dwID; + + POINT pt; + pt.x = TOUCH_COORD_TO_PIXEL(ti.x); + pt.y = TOUCH_COORD_TO_PIXEL(ti.y); + ScreenToClient(hWnd, &pt); + touchInfo.position.x = pt.x; + touchInfo.position.y = pt.y; + + touches.push_back(touchInfo); + } + for (int i = 0; i < iNumContacts; i++) { TOUCHINPUT ti = pInputs[i]; if (ti.dwFlags & TOUCHEVENTF_UP) { Win32Event newEvent; @@ -578,15 +690,17 @@ void Win32Core::handleTouchEvent(LPARAM lParam, WPARAM wParam) { newEvent.eventCode = InputEvent::EVENT_TOUCHES_ENDED; newEvent.touches = touches; newEvent.touch = touches[i]; - win32Events.push_back(newEvent); - } else if(ti.dwFlags & TOUCHEVENTF_MOVE) { + win32Events.push_back(newEvent); + } + else if (ti.dwFlags & TOUCHEVENTF_MOVE) { Win32Event newEvent; newEvent.eventGroup = Win32Event::INPUT_EVENT; newEvent.eventCode = InputEvent::EVENT_TOUCHES_MOVED; newEvent.touches = touches; newEvent.touch = touches[i]; win32Events.push_back(newEvent); - } else if(ti.dwFlags & TOUCHEVENTF_DOWN) { + } + else if (ti.dwFlags & TOUCHEVENTF_DOWN) { Win32Event newEvent; newEvent.eventGroup = Win32Event::INPUT_EVENT; newEvent.eventCode = InputEvent::EVENT_TOUCHES_BEGAN; @@ -594,10 +708,79 @@ void Win32Core::handleTouchEvent(LPARAM lParam, WPARAM wParam) { newEvent.touch = touches[i]; win32Events.push_back(newEvent); } - } + } + } } + unlockMutex(eventMutex); } - unlockMutex(eventMutex); +#endif + +#ifndef NO_PEN_API +void Win32Core::handlePointerUpdate(LPARAM lParam, WPARAM wParam) { + + lockMutex(eventMutex); + + POINTER_PEN_INFO penInfo; + POINTER_INFO pointerInfo; + UINT32 pointerId = GET_POINTERID_WPARAM(wParam); + POINTER_INPUT_TYPE pointerType = PT_POINTER; + TouchInfo tempInfo; + + if (!GetPointerType(pointerId, &pointerType)) + {} // failure + else { // success + + switch (pointerType){ + case PT_TOUCH: + //case PT_MOUSE: + //case PT_TOUCHPAD: + GetPointerInfo(pointerId, &pointerInfo); + tempInfo.type = TouchInfo::TYPE_TOUCH; + break; + case PT_PEN: + !GetPointerPenInfo(pointerId, &penInfo); + pointerInfo = penInfo.pointerInfo; + tempInfo.type = TouchInfo::TYPE_PEN; + break; + } + + tempInfo.id = GET_POINTERID_WPARAM(wParam); + tempInfo.position.x = pointerInfo.ptPixelLocation.x; + tempInfo.position.y = pointerInfo.ptPixelLocation.y; + + Win32Event newEvent; + newEvent.eventGroup = Win32Event::INPUT_EVENT; + + if (pointerInfo.pointerFlags & POINTER_FLAG_UPDATE){ + newEvent.eventCode = InputEvent::EVENT_TOUCHES_MOVED; + for (int i = 0; i < pointerTouches.size(); i++) { + if (pointerTouches[i].id == tempInfo.id) { + pointerTouches[i].position = tempInfo.position; + break; + } + } + } + else if (pointerInfo.pointerFlags & POINTER_FLAG_DOWN){ + newEvent.eventCode = InputEvent::EVENT_TOUCHES_BEGAN; + pointerTouches.push_back(tempInfo); + } + else if (pointerInfo.pointerFlags & POINTER_FLAG_UP){ + newEvent.eventCode = InputEvent::EVENT_TOUCHES_ENDED; + for (int i = 0; i < pointerTouches.size(); i++) { + if (pointerTouches[i].id == tempInfo.id) { + pointerTouches.erase(pointerTouches.begin() + i); + break; + } + } + } + + newEvent.touches = pointerTouches; + + newEvent.touch = tempInfo; + win32Events.push_back(newEvent); + } + + unlockMutex(eventMutex); } #endif @@ -611,6 +794,7 @@ void Win32Core::handleMouseMove(LPARAM lParam, WPARAM wParam) { win32Events.push_back(newEvent); unlockMutex(eventMutex); } + void Win32Core::handleMouseWheel(LPARAM lParam, WPARAM wParam) { lockMutex(eventMutex); Win32Event newEvent; @@ -727,7 +911,7 @@ void Win32Core::checkEvents() { break; case InputEvent::EVENT_KEYUP: input->setKeyState(event.keyCode, (char)event.unicodeChar, false, getTicks()); - break; + break; } break; } @@ -989,6 +1173,7 @@ DWORD WINAPI Win32LaunchThread(LPVOID data) { void Win32Core::createThread(Threaded *target) { + Core::createThread(target); DWORD dwGenericThread; HANDLE handle = CreateThread(NULL,0,Win32LaunchThread,target,0,&dwGenericThread); } @@ -1020,31 +1205,26 @@ std::vector Win32Core::getVideoModes() { String Win32Core::executeExternalCommand(String command, String args, String inDirectory) { String execInDirectory = inDirectory; + if(inDirectory == "") { execInDirectory = defaultWorkingDirectory; } - SHELLEXECUTEINFO lpExecInfo; - lpExecInfo.cbSize = sizeof(SHELLEXECUTEINFO); - lpExecInfo.lpFile = command.getWDataWithEncoding(String::ENCODING_UTF8); - lpExecInfo.fMask=SEE_MASK_DOENVSUBST|SEE_MASK_NOCLOSEPROCESS ; - lpExecInfo.hwnd = NULL; - lpExecInfo.lpVerb = L"open"; // to open program - lpExecInfo.lpParameters = args.getWDataWithEncoding(String::ENCODING_UTF8); // file name as an argument - lpExecInfo.lpDirectory = execInDirectory.getWDataWithEncoding(String::ENCODING_UTF8); - lpExecInfo.nShow = SW_SHOW ; // show command prompt with normal window size - lpExecInfo.hInstApp = (HINSTANCE) SE_ERR_DDEFAIL ; //WINSHELLAPI BOOL WINAPI result; - ShellExecuteEx(&lpExecInfo); - - - //wait until a file is finished printing - if(lpExecInfo.hProcess !=NULL) - { - ::WaitForSingleObject(lpExecInfo.hProcess, INFINITE); - ::CloseHandle(lpExecInfo.hProcess); - } - - return ""; + String cmdString = inDirectory.substr(0, inDirectory.find_first_of(":")+1)+" & cd \"" + execInDirectory + "\" & " + command + " " + args; + + char psBuffer[128]; + FILE *pPipe; + if((pPipe = _popen(cmdString.c_str(), "rt" )) == NULL) { + return ""; + } + + String retString; + while(fgets(psBuffer, 128, pPipe)) { + retString += String(psBuffer); + } + + _pclose(pPipe); + return retString; } String Win32Core::openFolderPicker() { @@ -1111,16 +1291,35 @@ std::vector Win32Core::openFilePicker(std::vector ext ofn.lpstrInitialDir=NULL; if(allowMultiple) { - ofn.Flags = OFN_PATHMUSTEXIST|OFN_FILEMUSTEXIST|OFN_EXPLORER; - } else { ofn.Flags = OFN_PATHMUSTEXIST|OFN_FILEMUSTEXIST|OFN_ALLOWMULTISELECT|OFN_EXPLORER; + } else { + ofn.Flags = OFN_PATHMUSTEXIST|OFN_FILEMUSTEXIST|OFN_EXPLORER; } std::vector retVec; if(GetOpenFileName(&ofn)) { if(allowMultiple) { - + String path = fBuffer; + + std::string buf; + for (int i = ofn.nFileOffset; i < sizeof( fBuffer ); i++) + { + if (fBuffer[i] != NULL) + buf.push_back(fBuffer[i]); + else if (fBuffer[i-1] != NULL) + { + retVec.push_back(path + "/" + buf); + buf = ""; + } + else // 2 NULL characters = no more files + break; + } + if (retVec.size() == 1) + { + retVec.clear(); + retVec.push_back(path); // If only 1 file selected, path is the full path of the file + } } else { retVec.push_back(String(fBuffer)); } @@ -1135,6 +1334,66 @@ std::vector Win32Core::openFilePicker(std::vector ext return retVec; } +String Win32Core::saveFilePicker(std::vector extensions) { + OPENFILENAME ofn; + wchar_t fBuffer[2048]; + + wchar_t filterString[2048]; + + ZeroMemory(&ofn, sizeof(OPENFILENAME)); + + ofn.lStructSize = sizeof (ofn); + ofn.hwndOwner = hWnd; + ofn.lpstrFile = fBuffer; + ofn.lpstrFile[0] = '\0'; + ofn.nMaxFile = sizeof(fBuffer); + + if (extensions.size() > 0) { + int offset = 0; + for (int i = 0; i < extensions.size(); i++) { + // filterString += extensions[i].description+"\0*."+extensions[i].extension+"\0"; + memcpy(filterString + offset, extensions[i].description.getWDataWithEncoding(String::ENCODING_UTF8), extensions[i].description.length() * sizeof(wchar_t)); + offset += extensions[i].description.length(); + filterString[offset] = '\0'; + offset++; + filterString[offset] = '*'; + offset++; + filterString[offset] = '.'; + offset++; + memcpy(filterString + offset, extensions[i].extension.getWDataWithEncoding(String::ENCODING_UTF8), extensions[i].extension.length() * sizeof(wchar_t)); + offset += extensions[i].extension.length(); + filterString[offset] = '\0'; + offset++; + } + filterString[offset] = '\0'; + ofn.lpstrFilter = filterString; + + ofn.nFilterIndex = 1; + } + else { + ofn.lpstrFilter = NULL; + } + + ofn.lpstrFileTitle = NULL; + ofn.nMaxFileTitle = 0; + ofn.lpstrInitialDir = NULL; + + ofn.Flags = OFN_PATHMUSTEXIST | OFN_EXPLORER; + + std::vector retVec; + + String retPath = ""; + + if (GetSaveFileName(&ofn)) { + retPath = String(fBuffer); + } + + SetCurrentDirectory(defaultWorkingDirectory.getWDataWithEncoding(String::ENCODING_UTF8)); + + retPath = retPath.replace("\\", "/"); + return retPath; +} + void Win32Core::createFolder(const String& folderPath) { String path = folderPath; CreateDirectory(path.getWDataWithEncoding(String::ENCODING_UTF8), NULL); diff --git a/Core/Contents/Source/PolyiPhoneCore.cpp b/Core/Contents/Source/PolyiPhoneCore.cpp deleted file mode 100644 index a7dd9137f..000000000 --- a/Core/Contents/Source/PolyiPhoneCore.cpp +++ /dev/null @@ -1,90 +0,0 @@ -/* - Copyright (C) 2011 by Ivan Safrin - - 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: - - The above copyright notice and this permission notice shall be included in - all copies or substantial portions of the Software. - - 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. -*/ - -#include "PolyiPhoneCore.h" - -using namespace Polycode; - -long getThreadID() { - return (long)pthread_self(); -} - -IPhoneCore::IPhoneCore(int frameRate) : Core(480, 320, true, 0, frameRate) { - -} - -IPhoneCore::~IPhoneCore() { - -} - -void IPhoneCore::enableMouse(bool newval) { - -} - -unsigned int IPhoneCore::getTicks() { - return 0; -} - -bool IPhoneCore::Update() { - return running; -} - -void IPhoneCore::setVideoMode(int xRes, int yRes, bool fullScreen, int aaLevel) { - -} - -void *PosixThreadFunc(void *data) { - Threaded *target = static_cast(data); - target->runThread(); - return NULL; -} - -void IPhoneCore::createThread(Threaded *target) { - pthread_t thread; - pthread_create( &thread, NULL, PosixThreadFunc, (void*)target); -} - -void IPhoneCore::lockMutex(CoreMutex *mutex) { - PosixMutex *m = (PosixMutex*) mutex; - pthread_mutex_lock(&m->pMutex); -} - -void IPhoneCore::unlockMutex(CoreMutex *mutex) { - PosixMutex *m = (PosixMutex*) mutex; - pthread_mutex_unlock(&m->pMutex); -} - -CoreMutex *IPhoneCore::createMutex() { - PosixMutex *mutex = new PosixMutex(); - pthread_mutex_init(&mutex->pMutex, NULL); - return mutex; -} - -void IPhoneCore::checkEvents() { - -} - -vector IPhoneCore::getVideoModes() { - vector modes; - - return modes; -} diff --git a/Core/Contents/Source/rgbe.cpp b/Core/Contents/Source/rgbe.cpp new file mode 100644 index 000000000..b180ddd2d --- /dev/null +++ b/Core/Contents/Source/rgbe.cpp @@ -0,0 +1,414 @@ +/* THIS CODE CARRIES NO GUARANTEE OF USABILITY OR FITNESS FOR ANY PURPOSE. + * WHILE THE AUTHORS HAVE TRIED TO ENSURE THE PROGRAM WORKS CORRECTLY, + * IT IS STRICTLY USE AT YOUR OWN RISK. */ + +#include "rgbe.h" +#include +#include +#include +#include +#include + +/* This file contains code to read and write four byte rgbe file format + developed by Greg Ward. It handles the conversions between rgbe and + pixels consisting of floats. The data is assumed to be an array of floats. + By default there are three floats per pixel in the order red, green, blue. + (RGBE_DATA_??? values control this.) Only the mimimal header reading and + writing is implemented. Each routine does error checking and will return + a status value as defined below. This code is intended as a skeleton so + feel free to modify it to suit your needs. + + (Place notice here if you modified the code.) + posted to http://www.graphics.cornell.edu/~bjw/ + written by Bruce Walter (bjw@graphics.cornell.edu) 5/26/95 + based on code written by Greg Ward +*/ + +#ifdef _CPLUSPLUS +/* define if your compiler understands inline commands */ +#define INLINE inline +#else +#define INLINE +#endif + +/* offsets to red, green, and blue components in a data (float) pixel */ +#define RGBE_DATA_RED 0 +#define RGBE_DATA_GREEN 1 +#define RGBE_DATA_BLUE 2 +/* number of floats per pixel */ +#define RGBE_DATA_SIZE 3 + +enum rgbe_error_codes { + rgbe_read_error, + rgbe_write_error, + rgbe_format_error, + rgbe_memory_error, +}; + +/* default error routine. change this to change error handling */ +static int rgbe_error(int rgbe_error_code, char *msg) +{ + switch (rgbe_error_code) { + case rgbe_read_error: + perror("RGBE read error"); + break; + case rgbe_write_error: + perror("RGBE write error"); + break; + case rgbe_format_error: + fprintf(stderr,"RGBE bad file format: %s\n",msg); + break; + default: + case rgbe_memory_error: + fprintf(stderr,"RGBE error: %s\n",msg); + } + return RGBE_RETURN_FAILURE; +} + +/* standard conversion from float pixels to rgbe pixels */ +/* note: you can remove the "inline"s if your compiler complains about it */ +static INLINE void +float2rgbe(unsigned char rgbe[4], float red, float green, float blue) +{ + float v; + int e; + + v = red; + if (green > v) v = green; + if (blue > v) v = blue; + if (v < 1e-32) { + rgbe[0] = rgbe[1] = rgbe[2] = rgbe[3] = 0; + } + else { + v = frexp(v,&e) * 256.0/v; + rgbe[0] = (unsigned char) (red * v); + rgbe[1] = (unsigned char) (green * v); + rgbe[2] = (unsigned char) (blue * v); + rgbe[3] = (unsigned char) (e + 128); + } +} + +/* standard conversion from rgbe to float pixels */ +/* note: Ward uses ldexp(col+0.5,exp-(128+8)). However we wanted pixels */ +/* in the range [0,1] to map back into the range [0,1]. */ +static INLINE void +rgbe2float(float *red, float *green, float *blue, unsigned char rgbe[4]) +{ + float f; + + if (rgbe[3]) { /*nonzero pixel*/ + f = ldexp(1.0,rgbe[3]-(int)(128+8)); + *red = rgbe[0] * f; + *green = rgbe[1] * f; + *blue = rgbe[2] * f; + } + else + *red = *green = *blue = 0.0; +} + +/* default minimal header. modify if you want more information in header */ +int RGBE_WriteHeader(FILE *fp, int width, int height, rgbe_header_info *info) +{ + char *programtype = "RGBE"; + + if (info && (info->valid & RGBE_VALID_PROGRAMTYPE)) + programtype = info->programtype; + if (fprintf(fp,"#?%s\n",programtype) < 0) + return rgbe_error(rgbe_write_error,NULL); + /* The #? is to identify file type, the programtype is optional. */ + if (info && (info->valid & RGBE_VALID_GAMMA)) { + if (fprintf(fp,"GAMMA=%g\n",info->gamma) < 0) + return rgbe_error(rgbe_write_error,NULL); + } + if (info && (info->valid & RGBE_VALID_EXPOSURE)) { + if (fprintf(fp,"EXPOSURE=%g\n",info->exposure) < 0) + return rgbe_error(rgbe_write_error,NULL); + } + if (fprintf(fp,"FORMAT=32-bit_rle_rgbe\n\n") < 0) + return rgbe_error(rgbe_write_error,NULL); + if (fprintf(fp, "-Y %d +X %d\n", height, width) < 0) + return rgbe_error(rgbe_write_error,NULL); + return RGBE_RETURN_SUCCESS; +} + +/* minimal header reading. modify if you want to parse more information */ +int RGBE_ReadHeader(FILE *fp, int *width, int *height, rgbe_header_info *info) +{ + char buf[128]; + int found_format; + float tempf; + int i; + + found_format = 0; + if (info) { + info->valid = 0; + info->programtype[0] = 0; + info->gamma = info->exposure = 1.0; + } + if (fgets(buf,sizeof(buf)/sizeof(buf[0]),fp) == NULL) + return rgbe_error(rgbe_read_error,NULL); + if ((buf[0] != '#')||(buf[1] != '?')) { + /* if you want to require the magic token then uncomment the next line */ + /*return rgbe_error(rgbe_format_error,"bad initial token"); */ + } + else if (info) { + info->valid |= RGBE_VALID_PROGRAMTYPE; + for(i=0;iprogramtype)-1;i++) { + if ((buf[i+2] == 0) || isspace(buf[i+2])) + break; + info->programtype[i] = buf[i+2]; + } + info->programtype[i] = 0; + if (fgets(buf,sizeof(buf)/sizeof(buf[0]),fp) == 0) + return rgbe_error(rgbe_read_error,NULL); + } + for(;;) { + if ((buf[0] == 0)||(buf[0] == '\n')) + return rgbe_error(rgbe_format_error,"no FORMAT specifier found"); + else if (strcmp(buf,"FORMAT=32-bit_rle_rgbe\n") == 0) + break; /* format found so break out of loop */ + else if (info && (sscanf(buf,"GAMMA=%g",&tempf) == 1)) { + info->gamma = tempf; + info->valid |= RGBE_VALID_GAMMA; + } + else if (info && (sscanf(buf,"EXPOSURE=%g",&tempf) == 1)) { + info->exposure = tempf; + info->valid |= RGBE_VALID_EXPOSURE; + } + if (fgets(buf,sizeof(buf)/sizeof(buf[0]),fp) == 0) + return rgbe_error(rgbe_read_error,NULL); + } + if (fgets(buf,sizeof(buf)/sizeof(buf[0]),fp) == 0) + return rgbe_error(rgbe_read_error,NULL); + if (strcmp(buf,"\n") != 0) + return rgbe_error(rgbe_format_error, + "missing blank line after FORMAT specifier"); + if (fgets(buf,sizeof(buf)/sizeof(buf[0]),fp) == 0) + return rgbe_error(rgbe_read_error,NULL); + if (sscanf(buf,"-Y %d +X %d",height,width) < 2) + return rgbe_error(rgbe_format_error,"missing image size specifier"); + return RGBE_RETURN_SUCCESS; +} + +/* simple write routine that does not use run length encoding */ +/* These routines can be made faster by allocating a larger buffer and + fread-ing and fwrite-ing the data in larger chunks */ +int RGBE_WritePixels(FILE *fp, float *data, int numpixels) +{ + unsigned char rgbe[4]; + + while (numpixels-- > 0) { + float2rgbe(rgbe,data[RGBE_DATA_RED], + data[RGBE_DATA_GREEN],data[RGBE_DATA_BLUE]); + data += RGBE_DATA_SIZE; + if (fwrite(rgbe, sizeof(rgbe), 1, fp) < 1) + return rgbe_error(rgbe_write_error,NULL); + } + return RGBE_RETURN_SUCCESS; +} + +/* simple read routine. will not correctly handle run length encoding */ +int RGBE_ReadPixels(FILE *fp, float *data, int numpixels) +{ + unsigned char rgbe[4]; + + while(numpixels-- > 0) { + if (fread(rgbe, sizeof(rgbe), 1, fp) < 1) + return rgbe_error(rgbe_read_error,NULL); + rgbe2float(&data[RGBE_DATA_RED],&data[RGBE_DATA_GREEN], + &data[RGBE_DATA_BLUE],rgbe); + data += RGBE_DATA_SIZE; + } + return RGBE_RETURN_SUCCESS; +} + +/* The code below is only needed for the run-length encoded files. */ +/* Run length encoding adds considerable complexity but does */ +/* save some space. For each scanline, each channel (r,g,b,e) is */ +/* encoded separately for better compression. */ + +static int RGBE_WriteBytes_RLE(FILE *fp, unsigned char *data, int numbytes) +{ +#define MINRUNLENGTH 4 + int cur, beg_run, run_count, old_run_count, nonrun_count; + unsigned char buf[2]; + + cur = 0; + while(cur < numbytes) { + beg_run = cur; + /* find next run of length at least 4 if one exists */ + run_count = old_run_count = 0; + while((run_count < MINRUNLENGTH) && (beg_run < numbytes)) { + beg_run += run_count; + old_run_count = run_count; + run_count = 1; + while( (beg_run + run_count < numbytes) && (run_count < 127) + && (data[beg_run] == data[beg_run + run_count])) + run_count++; + } + /* if data before next big run is a short run then write it as such */ + if ((old_run_count > 1)&&(old_run_count == beg_run - cur)) { + buf[0] = 128 + old_run_count; /*write short run*/ + buf[1] = data[cur]; + if (fwrite(buf,sizeof(buf[0])*2,1,fp) < 1) + return rgbe_error(rgbe_write_error,NULL); + cur = beg_run; + } + /* write out bytes until we reach the start of the next run */ + while(cur < beg_run) { + nonrun_count = beg_run - cur; + if (nonrun_count > 128) + nonrun_count = 128; + buf[0] = nonrun_count; + if (fwrite(buf,sizeof(buf[0]),1,fp) < 1) + return rgbe_error(rgbe_write_error,NULL); + if (fwrite(&data[cur],sizeof(data[0])*nonrun_count,1,fp) < 1) + return rgbe_error(rgbe_write_error,NULL); + cur += nonrun_count; + } + /* write out next run if one was found */ + if (run_count >= MINRUNLENGTH) { + buf[0] = 128 + run_count; + buf[1] = data[beg_run]; + if (fwrite(buf,sizeof(buf[0])*2,1,fp) < 1) + return rgbe_error(rgbe_write_error,NULL); + cur += run_count; + } + } + return RGBE_RETURN_SUCCESS; +#undef MINRUNLENGTH +} + +int RGBE_WritePixels_RLE(FILE *fp, float *data, int scanline_width, + int num_scanlines) +{ + unsigned char rgbe[4]; + unsigned char *buffer; + int i, err; + + if ((scanline_width < 8)||(scanline_width > 0x7fff)) + /* run length encoding is not allowed so write flat*/ + return RGBE_WritePixels(fp,data,scanline_width*num_scanlines); + buffer = (unsigned char *)malloc(sizeof(unsigned char)*4*scanline_width); + if (buffer == NULL) + /* no buffer space so write flat */ + return RGBE_WritePixels(fp,data,scanline_width*num_scanlines); + while(num_scanlines-- > 0) { + rgbe[0] = 2; + rgbe[1] = 2; + rgbe[2] = scanline_width >> 8; + rgbe[3] = scanline_width & 0xFF; + if (fwrite(rgbe, sizeof(rgbe), 1, fp) < 1) { + free(buffer); + return rgbe_error(rgbe_write_error,NULL); + } + for(i=0;i 0x7fff)) + /* run length encoding is not allowed so read flat*/ + return RGBE_ReadPixels(fp,data,scanline_width*num_scanlines); + scanline_buffer = NULL; + /* read in each successive scanline */ + while(num_scanlines > 0) { + if (fread(rgbe,sizeof(rgbe),1,fp) < 1) { + free(scanline_buffer); + return rgbe_error(rgbe_read_error,NULL); + } + if ((rgbe[0] != 2)||(rgbe[1] != 2)||(rgbe[2] & 0x80)) { + /* this file is not run length encoded */ + rgbe2float(&data[0],&data[1],&data[2],rgbe); + data += RGBE_DATA_SIZE; + free(scanline_buffer); + return RGBE_ReadPixels(fp,data,scanline_width*num_scanlines-1); + } + if ((((int)rgbe[2])<<8 | rgbe[3]) != scanline_width) { + free(scanline_buffer); + return rgbe_error(rgbe_format_error,"wrong scanline width"); + } + if (scanline_buffer == NULL) + scanline_buffer = (unsigned char *) + malloc(sizeof(unsigned char)*4*scanline_width); + if (scanline_buffer == NULL) + return rgbe_error(rgbe_memory_error,"unable to allocate buffer space"); + + ptr = &scanline_buffer[0]; + /* read each of the four channels for the scanline into the buffer */ + for(i=0;i<4;i++) { + ptr_end = &scanline_buffer[(i+1)*scanline_width]; + while(ptr < ptr_end) { + if (fread(buf,sizeof(buf[0])*2,1,fp) < 1) { + free(scanline_buffer); + return rgbe_error(rgbe_read_error,NULL); + } + if (buf[0] > 128) { + /* a run of the same value */ + count = buf[0]-128; + if ((count == 0)||(count > ptr_end - ptr)) { + free(scanline_buffer); + return rgbe_error(rgbe_format_error,"bad scanline data"); + } + while(count-- > 0) + *ptr++ = buf[1]; + } + else { + /* a non-run */ + count = buf[0]; + if ((count == 0)||(count > ptr_end - ptr)) { + free(scanline_buffer); + return rgbe_error(rgbe_format_error,"bad scanline data"); + } + *ptr++ = buf[1]; + if (--count > 0) { + if (fread(ptr,sizeof(*ptr)*count,1,fp) < 1) { + free(scanline_buffer); + return rgbe_error(rgbe_read_error,NULL); + } + ptr += count; + } + } + } + } + /* now convert data from buffer into floats */ + for(i=0;i