diff --git a/.gitignore b/.gitignore index 04577dcf4..e706f4140 100644 --- a/.gitignore +++ b/.gitignore @@ -14,11 +14,9 @@ *.sdf *.suo *.filters -*.user *.tlog *.obj *.res -*.rc *.log *.pdb *.idb @@ -29,6 +27,8 @@ *.exp *.zip *.swo +*.swm +*.swn *.opensdf *.user.* *.xcuserstate @@ -36,6 +36,7 @@ *.xcuserdata *.xcuserdatad *.orig +*.tag UserInterfaceState.* Player/Build/Mac OS X/Polycode Player.xcodeproj/project.xcworkspace/xcuserdata/ivansafrin.xcuserdatad/UserInterfaceState.xcuserstate @@ -84,6 +85,18 @@ Player/Build/Mac OS X/Polycode Player.xcodeproj/project.xcworkspace/xcuserdata/i !/IDE/Build/Windows/Polycode.vcxproj !/IDE/Build/Windows/Polycode.vcxproj.filters !/IDE/Build/Windows/Polycode.vcxproj.user +!/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 @@ -103,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* @@ -130,6 +148,7 @@ Player/Build/Mac OS X/Polycode Player.xcodeproj/project.xcworkspace/xcuserdata/i /Documentation/Lua/xml/*.xml +/Documentation/Lua/site_html /Documentation/Lua/html/* !/Documentation/Lua/html/css !/Documentation/Lua/html/images 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 e120ff117..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 035638d14..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 = diffuse_color.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 64bec2b29..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; @@ -115,7 +115,7 @@ void main() fogFactor = clamp(fogFactor, 0.0, 1.0); color = mix(gl_Fog.color, color, fogFactor ); - color.a = diffuse_color.a; + color.a = vertexColor.a; gl_FragColor = color; } 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 new file mode 100644 index 000000000..bbd21e0d0 --- /dev/null +++ b/Assets/Default asset pack/default/LightCube.frag @@ -0,0 +1,46 @@ +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() +{ + 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); + + // 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/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 26e4e50b1..695146a76 100644 --- a/Assets/Default asset pack/default/default.mat +++ b/Assets/Default asset pack/default/default.mat @@ -1,136 +1,72 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + - - - - - - - - - - - - - + + + + + + + + + + + - - - - - - - - - - - - - - - + + + + + + + - - - - - - - - - - - - + + + - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -140,6 +76,84 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Assets/Default asset pack/hdr.pak b/Assets/Default asset pack/hdr.pak index 3eebfdb45..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 new file mode 100644 index 000000000..234b2532d --- /dev/null +++ b/Assets/Default asset pack/hdr/DefaultShaderNoTextureHDR.frag @@ -0,0 +1,120 @@ +varying vec3 normal; +varying vec4 pos; +varying vec4 vertexColor; + +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); + + vec4 color = (diffuse_val * vertexColor) + + (specular_val * 1.0)+ + (ambient_color * vertexColor); + + + // 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 = diffuse_color.a; + gl_FragColor = color; + +} diff --git a/Assets/Default asset pack/hdr/HDRBloomH.frag b/Assets/Default asset pack/hdr/HDRBloomH.frag index 4e3f32cba..3b3c71f2c 100644 --- a/Assets/Default asset pack/hdr/HDRBloomH.frag +++ b/Assets/Default asset pack/hdr/HDRBloomH.frag @@ -1,5 +1,5 @@ uniform sampler2D screenTexture; -const float blurSize = 1.0/512.0; +uniform float blurSize; void main(void) { diff --git a/Assets/Default asset pack/hdr/HDRBloomV.frag b/Assets/Default asset pack/hdr/HDRBloomV.frag index ed9d1d73f..44ee71070 100644 --- a/Assets/Default asset pack/hdr/HDRBloomV.frag +++ b/Assets/Default asset pack/hdr/HDRBloomV.frag @@ -1,5 +1,5 @@ uniform sampler2D screenTexture; -const float blurSize = 1.0/512.0; +uniform float blurSize; void main(void) { 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 95b3e7e7b..c87b4f092 100644 --- a/Assets/Default asset pack/hdr/hdr.mat +++ b/Assets/Default asset pack/hdr/hdr.mat @@ -1,82 +1,98 @@ - - - - - - - - - - - - - - - - - - - + + + + + + + + - - - - - - + + - - - - - - - + + - - + + - - - - - - + + - - - - - - - + + + + + + + + + + + + + + + - + - - - - - - + + + + + + + + + + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -92,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/app_file_icon.ico b/Assets/Icons/app_file_icon.ico new file mode 100644 index 000000000..c4b80b34f Binary files /dev/null and b/Assets/Icons/app_file_icon.ico 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/Icons/main_icon.ico b/Assets/Icons/main_icon.ico new file mode 100644 index 000000000..805475693 Binary files /dev/null and b/Assets/Icons/main_icon.ico differ diff --git a/Assets/Icons/player_icon.ico b/Assets/Icons/player_icon.ico new file mode 100644 index 000000000..665bb190f Binary files /dev/null and b/Assets/Icons/player_icon.ico differ diff --git a/Assets/Icons/polycode_project.ico b/Assets/Icons/polycode_project.ico new file mode 100644 index 000000000..35fbbb00f Binary files /dev/null and b/Assets/Icons/polycode_project.ico differ diff --git a/Assets/Templates/C++/Linux/HelloPolycodeApp.cpp b/Assets/Templates/C++/Linux/HelloPolycodeApp.cpp index 5a15ae99f..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() { @@ -17,5 +15,5 @@ HelloPolycodeApp::~HelloPolycodeApp() { } bool HelloPolycodeApp::Update() { - return core->Update(); + return core->updateAndRender(); } 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++/Windows/PolycodeTemplateApp.cpp b/Assets/Templates/C++/Windows/PolycodeTemplateApp.cpp index 45fe6bc84..8badcf218 100644 --- a/Assets/Templates/C++/Windows/PolycodeTemplateApp.cpp +++ b/Assets/Templates/C++/Windows/PolycodeTemplateApp.cpp @@ -12,5 +12,5 @@ PolycodeTemplateApp::~PolycodeTemplateApp() { } bool PolycodeTemplateApp::Update() { - return core->Update(); + return core->updateAndRender(); } \ No newline at end of file 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"> Update(); + return core->updateAndRender(); } \ No newline at end of file diff --git a/Assets/Templates/C++/Xcode/PolycodeTemplate/main_icon.icns b/Assets/Templates/C++/Xcode/PolycodeTemplate/main_icon.icns index 7eeafd11b..4094f4fbb 100644 Binary files a/Assets/Templates/C++/Xcode/PolycodeTemplate/main_icon.icns and b/Assets/Templates/C++/Xcode/PolycodeTemplate/main_icon.icns differ diff --git a/Assets/UIThemes.pak b/Assets/UIThemes.pak index 2048cff90..9e448f42e 100644 Binary files a/Assets/UIThemes.pak and b/Assets/UIThemes.pak differ diff --git a/Assets/UIThemes/dark/arrowIcon.png b/Assets/UIThemes/dark/arrowIcon.png new file mode 100644 index 000000000..982fabfb5 Binary files /dev/null and b/Assets/UIThemes/dark/arrowIcon.png differ diff --git a/Assets/UIThemes/dark/boxIcon.png b/Assets/UIThemes/dark/boxIcon.png new file mode 100644 index 000000000..6ae2e4443 Binary files /dev/null and b/Assets/UIThemes/dark/boxIcon.png differ diff --git a/Assets/UIThemes/dark/button.png b/Assets/UIThemes/dark/button.png new file mode 100644 index 000000000..9bc7e37c4 Binary files /dev/null and b/Assets/UIThemes/dark/button.png differ diff --git a/Assets/UIThemes/dark/buttonFocused.png b/Assets/UIThemes/dark/buttonFocused.png new file mode 100644 index 000000000..d5d25c99d Binary files /dev/null and b/Assets/UIThemes/dark/buttonFocused.png differ diff --git a/Assets/UIThemes/dark/checkboxChecked.png b/Assets/UIThemes/dark/checkboxChecked.png new file mode 100644 index 000000000..8d4683f33 Binary files /dev/null and b/Assets/UIThemes/dark/checkboxChecked.png differ diff --git a/Assets/UIThemes/dark/checkboxUnchecked.png b/Assets/UIThemes/dark/checkboxUnchecked.png new file mode 100644 index 000000000..d9fd93f9c Binary files /dev/null and b/Assets/UIThemes/dark/checkboxUnchecked.png differ diff --git a/Assets/UIThemes/dark/closeIcon.png b/Assets/UIThemes/dark/closeIcon.png new file mode 100644 index 000000000..2a71b35eb Binary files /dev/null and b/Assets/UIThemes/dark/closeIcon.png differ diff --git a/Assets/UIThemes/dark/colorPickerHue.png b/Assets/UIThemes/dark/colorPickerHue.png new file mode 100644 index 000000000..32d1d9d93 Binary files /dev/null and b/Assets/UIThemes/dark/colorPickerHue.png differ diff --git a/Assets/UIThemes/dark/colorPickerHueSelector.png b/Assets/UIThemes/dark/colorPickerHueSelector.png new file mode 100644 index 000000000..cad61d24b Binary files /dev/null and b/Assets/UIThemes/dark/colorPickerHueSelector.png differ diff --git a/Assets/UIThemes/dark/colorPickerMainBg.png b/Assets/UIThemes/dark/colorPickerMainBg.png new file mode 100644 index 000000000..c778d46c5 Binary files /dev/null and b/Assets/UIThemes/dark/colorPickerMainBg.png differ diff --git a/Assets/UIThemes/dark/colorPickerMainFrame.png b/Assets/UIThemes/dark/colorPickerMainFrame.png new file mode 100644 index 000000000..35f0b077c Binary files /dev/null and b/Assets/UIThemes/dark/colorPickerMainFrame.png differ diff --git a/Assets/UIThemes/dark/colorPickerMainTarget.png b/Assets/UIThemes/dark/colorPickerMainTarget.png new file mode 100644 index 000000000..ef54be6cd Binary files /dev/null and b/Assets/UIThemes/dark/colorPickerMainTarget.png differ diff --git a/Assets/UIThemes/dark/colorboxBg.png b/Assets/UIThemes/dark/colorboxBg.png new file mode 100644 index 000000000..df85c81e7 Binary files /dev/null and b/Assets/UIThemes/dark/colorboxBg.png differ diff --git a/Assets/UIThemes/dark/colorboxFrame.png b/Assets/UIThemes/dark/colorboxFrame.png new file mode 100644 index 000000000..b82bf576b Binary files /dev/null and b/Assets/UIThemes/dark/colorboxFrame.png differ diff --git a/Assets/UIThemes/dark/comboBoxBg.png b/Assets/UIThemes/dark/comboBoxBg.png new file mode 100644 index 000000000..20d95d88d Binary files /dev/null and b/Assets/UIThemes/dark/comboBoxBg.png differ diff --git a/Assets/UIThemes/dark/comboBoxDrop.png b/Assets/UIThemes/dark/comboBoxDrop.png new file mode 100644 index 000000000..9334509e1 Binary files /dev/null and b/Assets/UIThemes/dark/comboBoxDrop.png differ diff --git a/Assets/UIThemes/dark/file.png b/Assets/UIThemes/dark/file.png new file mode 100644 index 000000000..c145c76e2 Binary files /dev/null and b/Assets/UIThemes/dark/file.png differ diff --git a/Assets/UIThemes/dark/folder.png b/Assets/UIThemes/dark/folder.png new file mode 100644 index 000000000..d363dd30d Binary files /dev/null and b/Assets/UIThemes/dark/folder.png differ diff --git a/Assets/UIThemes/dark/hsliderBg.png b/Assets/UIThemes/dark/hsliderBg.png new file mode 100644 index 000000000..0ff7d2ef0 Binary files /dev/null and b/Assets/UIThemes/dark/hsliderBg.png differ diff --git a/Assets/UIThemes/dark/hsliderHandle.png b/Assets/UIThemes/dark/hsliderHandle.png new file mode 100644 index 000000000..6922e398b Binary files /dev/null and b/Assets/UIThemes/dark/hsliderHandle.png differ diff --git a/Assets/UIThemes/dark/iconSelectorBg.png b/Assets/UIThemes/dark/iconSelectorBg.png new file mode 100644 index 000000000..aa5d7ab00 Binary files /dev/null and b/Assets/UIThemes/dark/iconSelectorBg.png differ diff --git a/Assets/UIThemes/dark/iconSelectorSelection.png b/Assets/UIThemes/dark/iconSelectorSelection.png new file mode 100644 index 000000000..a069d7361 Binary files /dev/null and b/Assets/UIThemes/dark/iconSelectorSelection.png differ diff --git a/Assets/UIThemes/dark/menuBg.png b/Assets/UIThemes/dark/menuBg.png new file mode 100644 index 000000000..516ea42e7 Binary files /dev/null and b/Assets/UIThemes/dark/menuBg.png differ diff --git a/Assets/UIThemes/dark/menuSelector.png b/Assets/UIThemes/dark/menuSelector.png new file mode 100644 index 000000000..c543b5b33 Binary files /dev/null and b/Assets/UIThemes/dark/menuSelector.png differ diff --git a/Assets/UIThemes/dark/scrollBg.png b/Assets/UIThemes/dark/scrollBg.png new file mode 100644 index 000000000..45985472f Binary files /dev/null and b/Assets/UIThemes/dark/scrollBg.png differ diff --git a/Assets/UIThemes/dark/scrollHandle.png b/Assets/UIThemes/dark/scrollHandle.png new file mode 100644 index 000000000..2e7b50cad Binary files /dev/null and b/Assets/UIThemes/dark/scrollHandle.png differ diff --git a/Assets/UIThemes/dark/selector.png b/Assets/UIThemes/dark/selector.png new file mode 100644 index 000000000..8c2935862 Binary files /dev/null and b/Assets/UIThemes/dark/selector.png differ diff --git a/Assets/UIThemes/dark/textfield.png b/Assets/UIThemes/dark/textfield.png new file mode 100644 index 000000000..0581ccc62 Binary files /dev/null and b/Assets/UIThemes/dark/textfield.png differ diff --git a/Assets/UIThemes/dark/textfieldMulti.png b/Assets/UIThemes/dark/textfieldMulti.png new file mode 100644 index 000000000..6e36d345b Binary files /dev/null and b/Assets/UIThemes/dark/textfieldMulti.png differ diff --git a/Assets/UIThemes/dark/textfield_focus.png b/Assets/UIThemes/dark/textfield_focus.png new file mode 100644 index 000000000..52992df14 Binary files /dev/null and b/Assets/UIThemes/dark/textfield_focus.png differ diff --git a/Assets/UIThemes/dark/theme.xml b/Assets/UIThemes/dark/theme.xml new file mode 100644 index 000000000..96c728a54 --- /dev/null +++ b/Assets/UIThemes/dark/theme.xml @@ -0,0 +1,187 @@ + + + + 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 0529b4a00..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/class.lua b/Bindings/Contents/LUA/API/class.lua index 4daa87e31..329df54af 100644 --- a/Bindings/Contents/LUA/API/class.lua +++ b/Bindings/Contents/LUA/API/class.lua @@ -13,10 +13,16 @@ function __is_class(c, T) return (c.__classname == T.__classname) end +function __are_tables_same_c_class(a,b) + return __are_same_c_class(a.__ptr,b.__ptr) +end + function class(name) local cls = {} cls.__classname = name + cls.__eq = __are_tables_same_c_class + cls.__tostring = function(c) return "Class of type "..c.__classname end diff --git a/Bindings/Contents/LUA/API/defaults.lua b/Bindings/Contents/LUA/API/defaults.lua index 3e108a19a..4a707fa09 100644 --- a/Bindings/Contents/LUA/API/defaults.lua +++ b/Bindings/Contents/LUA/API/defaults.lua @@ -18,6 +18,11 @@ _G["cast"] = function (c, T) return ret end +function __update(elapsed) + Services.TweenManager:Update(elapsed) + Update(elapsed) +end + function __is_table_kind_of(T,c) local __baseclass = T while __baseclass do @@ -64,18 +69,12 @@ 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()) Services.TimerManager = TimerManager("__skip_ptr__") Services.TimerManager.__ptr = Polycore.CoreServices_getTimerManager(Polycore.CoreServices_getInstance()) -Services.TweenManager = TweenManager("__skip_ptr__") -Services.TweenManager.__ptr = Polycore.CoreServices_getTweenManager(Polycore.CoreServices_getInstance()) - Services.ResourceManager = ResourceManager("__skip_ptr__") Services.ResourceManager.__ptr = Polycore.CoreServices_getResourceManager(Polycore.CoreServices_getInstance()) @@ -85,10 +84,25 @@ Services.SoundManager.__ptr = Polycore.CoreServices_getSoundManager(Polycore.Cor Services.FontManager = FontManager("__skip_ptr__") Services.FontManager.__ptr = Polycore.CoreServices_getFontManager(Polycore.CoreServices_getInstance()) +Services.TweenManager = TweenManager() + function delete(c) c:__delete() end +__safe_delete_list = {} + +function __process_safe_delete() + for i=1,count(__safe_delete_list) do + __safe_delete_list[i]:__delete() + end + __safe_delete_list = {} +end + +function safe_delete(c) + __safe_delete_list[count(__safe_delete_list)+1] = c +end + function onKeyDown(key) end @@ -104,9 +118,21 @@ end function onMouseMove(x,y) end +function onJoystickButtonDown(id, button) +end + +function onJoystickButtonUp(id, button) +end + +function onJoystickAxisMoved(id, axis, value) +end + function Update(e) end +function fixedUpdate() +end + KEY_UNKNOWN= 0 KEY_FIRST= 0 KEY_BACKSPACE= 8 diff --git a/Bindings/Contents/LUA/API/tweens.lua b/Bindings/Contents/LUA/API/tweens.lua new file mode 100644 index 000000000..c019e72f4 --- /dev/null +++ b/Bindings/Contents/LUA/API/tweens.lua @@ -0,0 +1,263 @@ +class "TweenManager" + +function TweenManager:TweenManager() + self.tweens = {} +end + +function TweenManager:addTween(tween) + self.tweens[count(self.tweens)+1] = tween +end + +function TweenManager:Update(elapsed) + for i=#self.tweens,1,-1 do + self.tweens[i]:Update(elapsed) + if self.tweens[i].complete == true then + table.remove(self.tweens, i) + end + end +end + +class "Tween" (EventDispatcher) + +Tween.EASE_NONE = 0 +Tween.EASE_IN_QUAD = 1 +Tween.EASE_OUT_QUAD = 2 +Tween.EASE_INOUT_QUAD = 3 +Tween.EASE_IN_CUBIC= 4 +Tween.EASE_OUT_CUBIC= 5 +Tween.EASE_INOUT_CUBIC= 6 +Tween.EASE_IN_QUART= 7 +Tween.EASE_OUT_QUART= 8 +Tween.EASE_INOUT_QUART= 9 +Tween.EASE_IN_QUINT= 10 +Tween.EASE_OUT_QUINT= 11 +Tween.EASE_INOUT_QUINT= 12 +Tween.EASE_IN_SINE= 13 +Tween.EASE_OUT_SINE= 14 +Tween.EASE_INOUT_SINE= 15 +Tween.EASE_IN_EXPO= 16 +Tween.EASE_OUT_EXPO= 17 +Tween.EASE_INOUT_EXPO= 18 +Tween.EASE_IN_CIRC= 19 +Tween.EASE_OUT_CIRC= 20 +Tween.EASE_INOUT_CIRC= 21 +Tween.EASE_IN_BOUNCE= 22 +Tween.EASE_OUT_BOUNCE = 23 +Tween.EASE_INOUT_BOUNCE = 24 + +function Tween.fEASE_IN_QUAD(t, startVal, cVal, endTime) + t = t / endTime + return cVal*t*t + startVal +end + +function Tween.fEASE_OUT_QUAD(t, startVal, cVal, endTime) + t = t / endTime + return -cVal * t*(t-2.0) + startVal +end + +function Tween.fEASE_INOUT_QUAD(t, startVal, cVal, endTime) + t = t / (endTime/2.0) + if t < 1.0 then return cVal/2.0*t*t + startVal end + t = t - 1 + return -cVal/2.0 * (t*(t-2.0) - 1.0) + startVal +end + +function Tween.fEASE_IN_CUBIC(t, startVal, cVal, endTime) + t = t / endTime + return cVal*t*t*t + startVal +end + +function Tween.fEASE_OUT_CUBIC(t, startVal, cVal, endTime) + t = t / endTime + t = t - 1 + return cVal*(t*t*t + 1.0) + startVal +end + +function Tween.fEASE_INOUT_CUBIC(t, startVal, cVal, endTime) + t = t / (endTime/2.0) + if t < 1.0 then return cVal/2.0*t*t*t + startVal end + t = t - 2.0 + return cVal/2.0*(t*t*t + 2.0) + startVal +end + +function Tween.fEASE_IN_QUART(t, startVal, cVal, endTime) + t = t / endTime + return cVal*t*t*t*t + startVal +end + +function Tween.fEASE_OUT_QUART(t, startVal, cVal, endTime) + t = t / endTime + t = t - 1 + return -cVal * (t*t*t*t - 1.0) + startVal +end + +function Tween.fEASE_INOUT_QUART(t, startVal, cVal, endTime) + t = t / (endTime/2.0) + if t < 1.0 then return (cVal/2.0*t*t*t*t) + startVal end + t = t - 2.0 + return -cVal/2.0 * (t*t*t*t - 2.0) + startVal +end + +function Tween.fEASE_IN_QUINT(t, startVal, cVal, endTime) + t = t / endTime + return cVal*t*t*t*t*t + startVal +end + +function Tween.fEASE_OUT_QUINT(t, startVal, cVal, endTime) + t = t / endTime + t = t - 1 + return cVal*(t*t*t*t*t + 1.0) + startVal +end + +function Tween.fEASE_INOUT_QUINT(t, startVal, cVal, endTime) + t = t / (endTime / 2.0) + if t < 1.0 then + return (cVal/2.0*t*t*t*t*t) + startVal + else + t = t - 2.0 + return ((cVal/2.0)*((t*t*t*t*t) + 2.0)) + startVal + end +end + +function Tween.fEASE_IN_SINE(t, startVal, cVal, endTime) + return -cVal * cos(t/endTime * (pi/2.0)) + cVal + startVal +end + +function Tween.fEASE_OUT_SINE(t, startVal, cVal, endTime) + return cVal * sin(t/endTime * (pi/2.0)) + startVal +end + +function Tween.fEASE_INOUT_SINE(t, startVal, cVal, endTime) + return -cVal/2.0 * (cos(pi*t/endTime) - 1.0) + startVal +end + +function Tween.fEASE_IN_EXPO(t, startVal, cVal, endTime) + return (cVal * pow( 2.0, 10.0 * (t/endTime - 1.0) ) + startVal) +end + +function Tween.fEASE_OUT_EXPO(t, startVal, cVal, endTime) + return cVal * ( -pow( 2.0, -10.0 * t/endTime ) + 1.0 ) + startVal +end + +function Tween.fEASE_INOUT_EXPO(t, startVal, cVal, endTime) + t = t / (endTime/2.0) + if t < 1.0 then return cVal/2.0 * pow( 2.0, 10.0 * (t - 1.0) ) + startVal end + t = t - 1 + return cVal/2.0 * ( -pow( 2.0, -10.0 * t) + 2.0 ) + startVal +end + +function Tween.fEASE_IN_CIRC(t, startVal, cVal, endTime) + t = t / endTime + return -cVal * (sqrt(1.0 - t*t) - 1.0) + startVal +end + +function Tween.fEASE_OUT_CIRC(t, startVal, cVal, endTime) + t = t/endTime + t = t - 1 + return cVal * sqrt(1.0 - t*t) + startVal +end + +function Tween.fEASE_INOUT_CIRC(t, startVal, cVal, endTime) + t = t/(endTime/2.0) + if t < 1.0 then return -cVal/2.0 * (sqrt(1.0 - t*t) - 1.0) + startVal end + t = t - 2.0 + return cVal/2.0 * (sqrt(1.0 - t*t) + 1.0) + startVal +end + +function Tween.fEASE_IN_BOUNCE(t, startVal, cVal, endTime) + t = t / endTime + if t < 1/2.75 then + return cVal*(7.5625*t*t) + startVal + elseif t < (2/2.75) then + t = t - (1.5/2.75) + return cVal*(7.5625*(t)*t + .75) + startVal + elseif (t < (2.5/2.75)) then + t = t - (2.25/2.75) + return cVal*(7.5625*(t)*t + .9375) + startVal + else + t = t - (2.625/2.75) + return cVal*(7.5625*(t)*t + .984375) + startVal + end +end + +function Tween.fEASE_NONE(t, startVal, cVal, endTime) + return cVal*t/endTime+startVal +end + +Tween.fEASE_OUT_BOUNCE = Tween.fEASE_IN_BOUNCE +Tween.fEASE_INOUT_BOUNCE = Tween.fEASE_IN_BOUNCE + +Tween.interpolateFunctions = {} + + +Tween.interpolateFunctions[Tween.EASE_NONE] = Tween.fEASE_NONE +Tween.interpolateFunctions[Tween.EASE_IN_QUAD] = Tween.fEASE_IN_QUAD +Tween.interpolateFunctions[Tween.EASE_OUT_QUAD] = Tween.fEASE_OUT_QUAD +Tween.interpolateFunctions[Tween.EASE_INOUT_QUAD] = Tween.fEASE_INOUT_QUAD +Tween.interpolateFunctions[Tween.EASE_IN_CUBIC] = Tween.fEASE_IN_CUBIC +Tween.interpolateFunctions[Tween.EASE_OUT_CUBIC] = Tween.fEASE_OUT_CUBIC +Tween.interpolateFunctions[Tween.EASE_INOUT_CUBIC] = Tween.fEASE_INOUT_CUBIC +Tween.interpolateFunctions[Tween.EASE_IN_QUART] = Tween.fEASE_IN_QUART +Tween.interpolateFunctions[Tween.EASE_OUT_QUART] = Tween.fEASE_OUT_QUART +Tween.interpolateFunctions[Tween.EASE_INOUT_QUART] = Tween.fEASE_INOUT_QUART +Tween.interpolateFunctions[Tween.EASE_IN_QUINT] = Tween.fEASE_IN_QUINT +Tween.interpolateFunctions[Tween.EASE_OUT_QUINT] = Tween.fEASE_OUT_QUINT +Tween.interpolateFunctions[Tween.EASE_INOUT_QUINT] = Tween.fEASE_INOUT_QUINT +Tween.interpolateFunctions[Tween.EASE_IN_SINE] = Tween.fEASE_IN_SINE +Tween.interpolateFunctions[Tween.EASE_OUT_SINE] = Tween.fEASE_OUT_SINE +Tween.interpolateFunctions[Tween.EASE_INOUT_SINE] = Tween.fEASE_INOUT_SINE +Tween.interpolateFunctions[Tween.EASE_IN_EXPO] = Tween.fEASE_IN_EXPO +Tween.interpolateFunctions[Tween.EASE_OUT_EXPO] = Tween.fEASE_OUT_EXPO +Tween.interpolateFunctions[Tween.EASE_INOUT_EXPO] = Tween.fEASE_INOUT_EXPO +Tween.interpolateFunctions[Tween.EASE_IN_CIRC] = Tween.fEASE_IN_CIRC +Tween.interpolateFunctions[Tween.EASE_OUT_CIRC] = Tween.fEASE_OUT_CIRC +Tween.interpolateFunctions[Tween.EASE_INOUT_CIRC] = Tween.fEASE_INOUT_CIRC +Tween.interpolateFunctions[Tween.EASE_IN_BOUNCE] = Tween.fEASE_IN_BOUNCE +Tween.interpolateFunctions[Tween.EASE_OUT_BOUNCE] = Tween.fEASE_OUT_BOUNCE +Tween.interpolateFunctions[Tween.EASE_INOUT_BOUNCE] = Tween.fEASE_INOUT_BOUNCE + +function Tween.fEASE_NONE(t, startVal, cVal, endTime) + return cVal*t/endTime+startVal +end + +function Tween:Tween(target, key, easeType, startVal, endVal, time, repeating, waitTime) + EventDispatcher.EventDispatcher(self) + self.target = target + self.key = key + self.easeType = easeType + self.startVal = startVal + self.endVal = endVal + self.endTime = time + self.repeating = repeating + self.waitTime =waitTime + self.cVal = endVal - startVal + self.tweenTime = 0 + self.complete = false + Services.TweenManager:addTween(self) +end + +function Tween:Reset() + self.tweenTime = 0 + self.complete = false +end + +function Tween:Update(elapsed) + if self.tweenTime >= self.endTime + self.waitTime then + if self.repeating == true then + self:Reset() + else + self.target[self.key] = self.endVal + self.complete = true + return + end + end + + if self.tweenTime > self.waitTime then + self.target[self.key] = self:interpolateTween() + end + self.tweenTime = self.tweenTime + elapsed +end + +function Tween:interpolateTween() + return Tween.interpolateFunctions[self.easeType](self.tweenTime - self.waitTime, self.startVal, self.cVal, self.endTime) +end \ No newline at end of file diff --git a/Bindings/Scripts/create_lua_library/CppHeaderParser.py b/Bindings/Scripts/create_lua_library/CppHeaderParser.py index 6091638ed..4667489e6 100644 --- a/Bindings/Scripts/create_lua_library/CppHeaderParser.py +++ b/Bindings/Scripts/create_lua_library/CppHeaderParser.py @@ -1974,7 +1974,11 @@ def __init__(self, headerFileName, argType="file", **kwargs): num_newlines = len(filter(lambda a: a=="\n", m)) headerFileStr = headerFileStr.replace(m, "\n" * num_newlines) headerFileStr = re.sub(r'extern[ ]+"[Cc]"[ ]*', "", headerFileStr) - + + #Filter out "using" statements since they aren't properly handled currently + # TODO: properly handle "using" statements + headerFileStr = re.sub(r'using[\t ]+[^\n\r]+', "", headerFileStr) + self.braceDepth = 0 lex.lex() lex.input(headerFileStr) diff --git a/Bindings/Scripts/create_lua_library/create_lua_library.py b/Bindings/Scripts/create_lua_library/create_lua_library.py index 509167651..5acb24a9f 100644 --- a/Bindings/Scripts/create_lua_library/create_lua_library.py +++ b/Bindings/Scripts/create_lua_library/create_lua_library.py @@ -16,9 +16,9 @@ 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) + out += "%s\tlocal __c = _G[%s](\"__skip_ptr__\")\n" % (prefix, className.replace("*", "")) out += "%s\t__c.__ptr = %s[i]\n" % (prefix, ptr) out += "%s\t%s[i] = __c\n" % (prefix, ptr) out += "%send\n" % (prefix) @@ -27,8 +27,8 @@ def template_returnPtrLookupArray(prefix, className, ptr): # Note we expect className to be a valid string def template_returnPtrLookup(prefix, className, ptr): - out = "" - out += "%slocal __c = _G[%s](\"__skip_ptr__\")\n" % (prefix, className) + 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) return out @@ -40,7 +40,7 @@ def cleanDocs(docs): return docs.replace("/*", "").replace("*/", "").replace("*", "").replace("\n", "").replace("\r", "").replace("::", ".").replace("\t", "") def toLuaType(t): - return t.replace("void", "nil").replace("int", "Integer").replace("bool", "Boolean") + return t.replace("void", "nil").replace("int", "Integer").replace("bool", "Boolean").replace("*", "") # FIXME: Some "unsigned int *" functions are still being generated on the polycode API? def typeFilter(ty): @@ -52,7 +52,10 @@ 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" ty = ty.replace("unsigned", "int") @@ -77,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" @@ -89,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" @@ -109,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 = ["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) @@ -121,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", "ScreenLabel", "SceneLabel", "SceneMesh", "Screen", "Scene", "Texture", "Image", "Camera", "ScreenParticleEmitter", "SceneParticleEmitter", "Mesh", "Vertex", "Polygon", "Polycode::Polygon", "Material", "ScenePrimitive", "SceneLine", "SceneLight", "SceneSound", "ScreenImage", "SceneEntity"] + 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 @@ -147,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"] + 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, @@ -163,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: @@ -181,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 @@ -191,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: @@ -207,8 +212,11 @@ def createLUABindings(inputPath, prefix, mainInclude, libSmallName, libName, api if 'doxygen' in c: luaDocOut += "\t\t\n" % (cleanDocs(c['doxygen'])) + if ckey in disable_gc: + luaDocOut += "\t\tNOTE: %s instances are not automatically garbage collected.\n" % (ckey) + parsed_methods = [] # Def: List of discovered methods - ignore_methods = ["readByte32", "readByte16", "getCustomEntitiesByType", "Core", "Renderer", "Shader", "Texture", "handleEvent", "secondaryHandler", "getSTLString"] + ignore_methods = ["readByte32", "readByte16", "getCustomEntitiesByType", "Core", "Renderer", "Shader", "Texture", "handleEvent", "secondaryHandler", "getSTLString", "readInt"] luaClassBindingOut += "\n\n" luaDocOut += "\t\t\n" @@ -220,14 +228,22 @@ def createLUABindings(inputPath, prefix, mainInclude, libSmallName, libName, api continue if pp["type"].find("static ") != -1: # If static. FIXME: Static doesn't work? if "defaltValue" in pp: # FIXME: defaltValue is misspelled. - luaClassBindingOut += "%s.%s = %s\n" % (ckey, pp["name"], pp["defaltValue"]) + defaltValue = pp["defaltValue"] + + # The "Default Value" is more or less a literal C++ string. This causes a problem: + # Frequently we say static const int A = 1; static const int B = A + 1. + # Put in a one-off hack to ensure namespacing works in this special case. + if re.match(r'\s*[a-zA-Z_][a-zA-Z0-9_]*\s*\+', defaltValue): + defaltValue = "%s.%s" % (ckey, defaltValue) + + luaClassBindingOut += "%s.%s = %s\n" % (ckey, pp["name"], defaltValue) luaDocOut += "\t\t\t\n" % (pp["name"], toLuaType(typeFilter(pp["type"])), pp["defaltValue"]) if 'doxygen' in pp: luaDocOut += "\t\t\t\t\n" % (cleanDocs(pp['doxygen'])) luaDocOut += "\t\t\t\n" else: # FIXME: Nonstatic method ? variable ?? found. #there are some bugs in the class parser that cause it to return junk - if pp["type"].find("*") == -1 and pp["type"].find("vector") == -1 and pp["name"] != "setScale" and pp["name"] != "setPosition" and pp["name"] != "BUFFER_CACHE_PRECISION" and not pp["name"].isdigit(): + if pp["type"].find("vector") == -1 and pp["name"] != "setScale" and pp["name"] != "setPosition" and pp["name"] != "BUFFER_CACHE_PRECISION" and not pp["name"].isdigit(): classProperties.append(pp) luaDocOut += "\t\t\n" @@ -303,8 +319,16 @@ def createLUABindings(inputPath, prefix, mainInclude, libSmallName, libName, api if pp["type"] == "Number" or pp["type"] == "String" or pp["type"] == "int" or pp["type"] == "bool" or pp["type"] == "PolyKEY": wrappersHeaderOut += "\t%s(L, inst->%s%s);\n" % (outfunc, pp["name"], retFunc) else: - wrappersHeaderOut += "\tPolyBase **userdataPtr = (PolyBase**)lua_newuserdata(L, sizeof(PolyBase*));\n" - wrappersHeaderOut += "\t*userdataPtr = (PolyBase*)&inst->%s%s;\n" % (pp["name"], retFunc) + if pp["type"].find("*") != -1: + 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) wrappersHeaderOut += "\treturn 1;\n" wrappersHeaderOut += "}\n\n" @@ -346,6 +370,7 @@ def createLUABindings(inputPath, prefix, mainInclude, libSmallName, libName, api wrappersHeaderOut += "\t%s *inst = (%s*) *((PolyBase**)lua_touserdata(L, 1));\n" % (ckey, ckey) outfunc = "this_shouldnt_happen" + outfuncsuffix = "" if pp["type"] == "Number": outfunc = "lua_tonumber" if pp["type"] == "String": @@ -356,8 +381,9 @@ def createLUABindings(inputPath, prefix, mainInclude, libSmallName, libName, api outfunc = "(PolyKEY)lua_tointeger" if pp["type"] == "bool": outfunc = "lua_toboolean" + outfuncsuffix = " != 0" - wrappersHeaderOut += "\t%s param = %s(L, 2);\n" % (pp["type"], outfunc) + wrappersHeaderOut += "\t%s param = %s(L, 2)%s;\n" % (pp["type"], outfunc, outfuncsuffix) wrappersHeaderOut += "\tinst->%s = param;\n" % (pp["name"]) wrappersHeaderOut += "\treturn 0;\n" @@ -524,7 +550,7 @@ def createLUABindings(inputPath, prefix, mainInclude, libSmallName, libName, api luafunc = "lua_toboolean" luatype = "LUA_TBOOLEAN" checkfunc = "lua_isboolean" - luafuncsuffix = "" + luafuncsuffix = " != 0" lend = "" if param["type"] == "Number" or param["type"] == "float" or param["type"] == "double": luatype = "LUA_TNUMBER" @@ -629,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": @@ -710,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") @@ -737,14 +762,15 @@ def createLUABindings(inputPath, prefix, mainInclude, libSmallName, libName, api cppRegisterOut += "\t\t{\"delete_%s\", %s_delete_%s},\n" % (ckey, libName, ckey) wrappersHeaderOut += "static int %s_delete_%s(lua_State *L) {\n" % (libName, ckey) wrappersHeaderOut += "\tluaL_checktype(L, 1, LUA_TUSERDATA);\n" - wrappersHeaderOut += "\t%s *inst = (%s*) *((PolyBase**)lua_touserdata(L, 1));\n" % (ckey, ckey) - wrappersHeaderOut += "\tdelete inst;\n" + wrappersHeaderOut += "\tPolyBase **inst = (PolyBase**)lua_touserdata(L, 1);\n" + wrappersHeaderOut += "\tdelete ((%s*) *inst);\n" % (ckey) + wrappersHeaderOut += "\t*inst = NULL;\n" wrappersHeaderOut += "\treturn 0;\n" wrappersHeaderOut += "}\n\n" # Delete method (Lua side) luaClassBindingOut += "function %s:__delete()\n" % (ckey) - luaClassBindingOut += "\t%s.delete_%s(self.__ptr)\n" % (libName, ckey) + luaClassBindingOut += "\tif self then %s.delete_%s(self.__ptr) end\n" % (libName, ckey) luaClassBindingOut += "end\n" # Add class to lua index file 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 cdba32806..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} @@ -27,7 +28,7 @@ ExternalProject_Add(box2d URL_MD5 59d142cd8d4d73e8832c7b67591f590c # Box2D's source isn't in the top level directory so add a dummy file to set cmake right - PATCH_COMMAND cmake -E echo ADD_SUBDIRECTORY(Box2D) > /CMakeLists.txt + PATCH_COMMAND ${CMAKE_COMMAND} -E echo ADD_SUBDIRECTORY(Box2D) > /CMakeLists.txt INSTALL_DIR ${POLYCODE_DEPS_MODULES_PREFIX} CMAKE_ARGS ${box2d_CMAKE_ARGS} 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 2f5343acb..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} @@ -17,8 +18,8 @@ EXTERNALPROJECT_ADD(zlib PREFIX ${libpng_PREFIX} DOWNLOAD_DIR ${POLYCODE_DEPS_DOWNLOAD_DIR} - URL http://zlib.net/zlib-1.2.7.tar.gz - URL_MD5 60df6a37c56e7c1366cca812414f7b85 + URL http://zlib.net/zlib-1.2.8.tar.gz + URL_MD5 44d667c142d7cda120332623eab69f40 PATCH_COMMAND ${CMAKE_COMMAND} -E copy_if_different ${PolycodeDependencies_SOURCE_DIR}/../CMake/zlib.cmake /CMakeLists.txt && ${CMAKE_COMMAND} -E remove /zconf.h 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 a4c427800..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,34 +201,13 @@ 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 AND BULLET_LIBRARIES_DEBUG) +IF(BULLET_INCLUDE_DIR AND BULLET_LIBRARIES) SET(BULLET_FOUND TRUE) -ENDIF(BULLET_INCLUDE_DIR AND BULLET_LIBRARIES AND BULLET_LIBRARIES_DEBUG) +ENDIF(BULLET_INCLUDE_DIR AND BULLET_LIBRARIES) # show the BULLET_INCLUDE_DIR and BULLET_LIBRARIES variables only in the advanced view IF(BULLET_FOUND) 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/PolycodeDependencies.cmake b/CMake/PolycodeDependencies.cmake new file mode 100644 index 000000000..68874d719 --- /dev/null +++ b/CMake/PolycodeDependencies.cmake @@ -0,0 +1,100 @@ +# Find the required dependency libraries to use the polycode static library. +# +# Required variables by this cmake file: +# ${POLYCODE_CMAKE_DIR} points to the directory with all of polycode's cmake scripts(including this one) +# ${POLYCODE_RELEASE_DIR} points to the directory where the binares are produced to(Polycode/Release/${SYSTEM_NAME}) by default +# +# Sets ${POLYCODE_DEPENDENCY_LIBS} to something you can pass to TARGET_LINK_LIBRARIES +# It will use the "debug" and "optimized" cmake keywords, so it will not work for anything other than TARGET_LINK_LIBRARIES +# +# +# Uses INCLUDE_DIRECTORIES AND LINK_DIRECTORIES to add the necessary directories for the given links/includes to work. + + +INCLUDE(${POLYCODE_CMAKE_DIR}/PolycodeIncludes.cmake) +INCLUDE(${POLYCODE_CMAKE_DIR}/FindBullet.cmake) + + +INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/Include + ${POLYCODE_RELEASE_DIR}/Framework/Core/include + ${POLYCODE_RELEASE_DIR}/Framework/Modules/include + ${POLYCODE_RELEASE_DIR}/Framework/Core/Dependencies/include + ${POLYCODE_RELEASE_DIR}/Framework/Core/Dependencies/include/AL + ${PolycodeIDE_SOURCE_DIR}/include) + +LINK_DIRECTORIES(${POLYCODE_RELEASE_DIR}/Framework/Core/lib + ${POLYCODE_RELEASE_DIR}/Framework/Modules/lib + ${POLYCODE_RELEASE_DIR}/Framework/Bindings/Lua/Core/lib + ${POLYCODE_RELEASE_DIR}/Framework/Core/Dependencies/lib) + + +IF(MSVC OR MINGW) + SET(POLYCODE_DEPENDENCY_LIBS + PolycodeLua_d + Polycore_d + ${ZLIB_LIBRARIES} + ${LUA_LIBRARY} + ${OPENGL_LIBRARIES} + ${OPENAL_LIBRARY} + ${PNG_LIBRARIES} + ${FREETYPE_LIBRARIES} + ${PHYSFS_LIBRARY} + ${OGG_LIBRARY} + ${VORBIS_LIBRARY} + ${VORBISFILE_LIBRARY} + opengl32 + glu32 + winmm + ws2_32 + Polycode2DPhysics + Polycode3DPhysics + PolycodeUI + optimized ${BOX2D_RELEASE_LIBRARY} + debug ${BOX2D_DEBUG_LIBRARY} + optimized ${LIBBULLETMULTITHREADED} + optimized ${LIBBULLETSOFTBODY} + optimized ${LIBBULLETDYNAMICS} + optimized ${LIBBULLETCOLLISION} + optimized ${LIBBULLETMATH} + debug ${LIBBULLETMULTITHREADED_DEBUG} + debug ${LIBBULLETSOFTBODY_DEBUG} + debug ${LIBBULLETDYNAMICS_DEBUG} + debug ${LIBBULLETCOLLISION_DEBUG} + debug ${LIBBULLETMATH_DEBUG}) +ELSEIF(APPLE) + # TODO: Add apple support +ELSE(MSVC OR MINGW) + SET(POLYCODE_DEPENDENCY_LIBS + rt + pthread + PolycodeLua_d + PolycodeUI_d + Polycore_d + ${LUA_LIBRARY} + ${FREETYPE_LIBRARIES} + ${VORBISFILE_LIBRARY} + ${VORBIS_LIBRARY} + ${OGG_LIBRARY} + ${OPENAL_LIBRARY} + ${PHYSFS_LIBRARY} + ${PNG_LIBRARIES} + ${ZLIB_LIBRARY} + ${OPENGL_LIBRARIES} + ${SDL_LIBRARY} + dl + Polycode2DPhysics_d + Polycode3DPhysics_d + ${BOX2D_RELEASE_LIBRARY} + + optimized ${LIBBULLETMULTITHREADED} + optimized ${LIBBULLETSOFTBODY} + optimized ${LIBBULLETDYNAMICS} + optimized ${LIBBULLETCOLLISION} + optimized ${LIBBULLETMATH} + + debug ${LIBBULLETMULTITHREADED_DEBUG} + debug ${LIBBULLETSOFTBODY_DEBUG} + debug ${LIBBULLETDYNAMICS_DEBUG} + debug ${LIBBULLETCOLLISION_DEBUG} + debug ${LIBBULLETMATH_DEBUG}) +ENDIF(MSVC OR MINGW) 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/libogg.cmake b/CMake/libogg.cmake index 0d9063b8e..983c2885d 100644 --- a/CMake/libogg.cmake +++ b/CMake/libogg.cmake @@ -23,6 +23,19 @@ IF(MSVC) LIST(APPEND libogg_SRCS win32/ogg.def) ENDIF(MSVC) +IF("${CMAKE_SYSTEM_NAME}" STREQUAL "Linux") + # libogg expects configure to be called on linux to + # generate config_types.h + LIST(APPEND libogg_HDRS include/ogg/config_types.h) + + add_custom_command(OUTPUT include/ogg/config_types.h + COMMAND ./configure + DEPENDS include/ogg/ogg.h # Hopefully if the libogg version changes, so does this file + # so configure_types.h will be regenerated. + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + ) +ENDIF() + #ADD_LIBRARY(libogg_dynamic SHARED ${libogg_SRCS} ${libogg_HDRS}) ADD_LIBRARY(libogg ${libogg_SRCS} ${libogg_HDRS}) 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 f577c78dd..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) @@ -46,48 +51,9 @@ SET(CMAKE_PREFIX_PATH ${POLYCODE_RELEASE_DIR}/Framework/Modules/Dependencies ${POLYCODE_RELEASE_DIR}/Framework/Tools/Dependencies) -MESSAGE(DEBUG " CMAKE_PREFIX_PATH=${CMAKE_PREFIX_PATH}") - -# If the following large ugly mess is not present, failures can occur with -G "Unix Makefiles". -# Even WITH this, you may get "library not found" errors on mingw first run; if so just re-run. -# FIXME: Shouldn't be necessary if FIND_LIBRARY were working on mingw. -IF (MINGW) -LINK_DIRECTORIES(${POLYCODE_RELEASE_DIR}/Framework/Core/Dependencies/lib) -LINK_DIRECTORIES(${POLYCODE_RELEASE_DIR}/Framework/Modules/Dependencies/lib) -SET(ZLIB_LIBRARY zlib) -SET(ZLIB_INCLUDE_DIR ${POLYCODE_RELEASE_DIR}/Framework/Core/Dependencies/include) -SET(OGG_LIBRARY libogg) -SET(OGG_INCLUDE_DIR ${POLYCODE_RELEASE_DIR}/Framework/Core/Dependencies/include) -SET(PNG_LIBRARY png) -SET(PNG_INCLUDE_DIR ${POLYCODE_RELEASE_DIR}/Framework/Core/Dependencies/include) -SET(PHYSFS_LIBRARY physfs) -SET(PHYSFS_INCLUDE_DIR ${POLYCODE_RELEASE_DIR}/Framework/Core/Dependencies/include) -SET(OPENAL_LIBRARY OpenAL32) -SET(OPENAL_INCLUDE_DIR ${POLYCODE_RELEASE_DIR}/Framework/Core/Dependencies/include/AL) -SET(FREETYPE_LIBRARY freetype) -SET(FREETYPE_INCLUDE_DIRS ${POLYCODE_RELEASE_DIR}/Framework/Core/Dependencies/include) -SET(VORBIS_LIBRARY libvorbis) -SET(VORBIS_INCLUDE_DIR ${POLYCODE_RELEASE_DIR}/Framework/Core/Dependencies/include) -SET(VORBISFILE_LIBRARY libvorbisfile) -SET(VORBISFILE_INCLUDE_DIR ${POLYCODE_RELEASE_DIR}/Framework/Core/Dependencies/include) -SET(LUA_LIBRARY lua5.1) -SET(LUA_LIBRARIES lua5.1) -SET(LUA_INCLUDE_DIR ${POLYCODE_RELEASE_DIR}/Framework/Core/Dependencies/include/lua5.1) -SET(BOX2D_RELEASE_LIBRARY Box2D) -SET(BOX2D_INCLUDE_DIR ${POLYCODE_RELEASE_DIR}/Framework/Modules/Dependencies/include) -SET(ASSIMP_INCLUDE_DIR ${POLYCODE_RELEASE_DIR}/Framework/Core/Dependencies/include) -SET(BULLET_INCLUDE_DIR ${POLYCODE_RELEASE_DIR}/Framework/Modules/Dependencies/include/bullet) -SET(LIBBULLETCOLLISION BulletCollision) -SET(LIBBULLETDYNAMICS BulletDynamics) -SET(LIBBULLETMATH LinearMath) -SET(LIBBULLETMULTITHREADED BulletMultiThreaded) -SET(LIBBULLETSOFTBODY BulletSoftBody) -ENDIF() - # 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 e83b27788..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,45 +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/PolyScreenCurve.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 @@ -83,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 @@ -92,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 @@ -113,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 @@ -135,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 @@ -152,26 +145,15 @@ SET(polycore_HDRS Include/PolySceneManager.h Include/PolySceneMesh.h Include/PolyScenePrimitive.h + Include/PolySceneImage.h Include/PolySceneRenderTexture.h Include/PolySceneSound.h - Include/PolyScreenCurve.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 @@ -180,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 @@ -188,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") @@ -210,6 +198,8 @@ IF (MINGW) ) ENDIF(MINGW) +INCLUDE(FindPkgConfig) + IF(MSVC OR MINGW) SET(polycore_SRCS ${polycore_SRCS} PolycodeView/MSVC/PolycodeView.cpp Source/PolyWinCore.cpp) SET(polycore_HDRS ${polycore_HDRS} PolycodeView/MSVC/PolycodeView.h Include/PolyWinCore.h) @@ -241,6 +231,21 @@ ENDIF(MSVC OR MINGW) ADD_LIBRARY(Polycore ${polycore_SRCS} ${polycore_HDRS}) #ENDIF(POLYCODE_BUILD_STATIC) +# On linux, check for x11 +IF("${CMAKE_SYSTEM_NAME}" STREQUAL "Linux") + # TODO: Allow to set X11 path manually instead of using pkg-config + IF(NOT ${PKG_CONFIG_FOUND}) + message( FATAL_ERROR "You seem to be using Linux, but pkg-config is not installed. Please install pkg-config." ) + endif() + PKG_CHECK_MODULES(X11 x11) + IF(${X11_FOUND}) + INCLUDE_DIRECTORIES(${X11_INCLUDE_DIRS}) + SET_TARGET_PROPERTIES(Polycore PROPERTIES COMPILE_FLAGS "-DUSE_X11") + ELSE() + message( FATAL_ERROR "You seem to be using Linux, but X11 is not installed. Please install X11." ) + ENDIF() +ENDIF() + IF(POLYCODE_INSTALL_FRAMEWORK) # install headers INSTALL(FILES ${polycore_HDRS} diff --git a/Core/Contents/Include/OSBasics.h b/Core/Contents/Include/OSBasics.h index 1cd44e4bf..741532a4e 100755 --- a/Core/Contents/Include/OSBasics.h +++ b/Core/Contents/Include/OSBasics.h @@ -72,9 +72,11 @@ class _PolyExport OSBasics : public PolyBase { static std::vector parsePhysFSFolder(const Polycode::String& pathString, bool showHidden); static std::vector parseFolder(const Polycode::String& pathString, bool showHidden); + static bool fileExists(const Polycode::String& pathString); static bool isFolder(const Polycode::String& pathString); static void createFolder(const Polycode::String& pathString); static void removeItem(const Polycode::String& pathString); + static time_t getFileTime(const Polycode::String& pathString); private: 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 3b5090ddc..15b47fbe3 100755 --- a/Core/Contents/Include/PolyCamera.h +++ b/Core/Contents/Include/PolyCamera.h @@ -23,7 +23,9 @@ #pragma once #include "PolyGlobals.h" -#include "PolySceneEntity.h" +#include "PolyEntity.h" +#include "PolyVector2.h" +#include "PolyVector4.h" namespace Polycode { @@ -35,34 +37,58 @@ 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(); - void buildFrustrumPlanes(); + /** + * Builds the frustum clipping planes for this camera using the current render modelview and the camera's projection matrices. + */ + void buildFrustumPlanes(); /** * Checks if the camera can see a sphere. * @param pos Position of the sphere to check. * @param fRadius Radius of the sphere. - * @return Returns true if the sphere is within the camera's frustrum, or false if it isn't. + * @return Returns true if the sphere is within the camera's frustum, or false if it isn't. * @see canSee() */ - bool isSphereInFrustrum(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 frustrum, or false if it isn't. - * @see isSphereInFrustrum() - */ - 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. @@ -70,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. @@ -100,35 +143,84 @@ 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); /** - * 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(); - - void createPostFilter(Material *shaderMaterial); - + Number getExposureLevel() { + return exposureLevel; + } + /** * Sets the post-processing shader for the camera. - * @param shaderName The shader name of the post-processing filter. + * @param shaderMaterial Post processing shader material. + */ + void setPostFilter(Material *shaderMaterial); + + /** + * Sets the post-processing shader for the camera by name. The material needs have been added as a resource. + * @param materialName The material name of the post-processing filter. */ - void setPostFilter(const String& shaderName); + void setPostFilterByName(const String& shaderName); /** * Removes the currently assigned post filter. @@ -140,28 +232,110 @@ 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; } + 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; - protected: + /** + * 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). + */ + 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; - - bool fovSet; Material *filterShaderMaterial; Texture *originalSceneTexture; diff --git a/Core/Contents/Include/PolyClient.h b/Core/Contents/Include/PolyClient.h index d95326ffe..434894cf4 100755 --- a/Core/Contents/Include/PolyClient.h +++ b/Core/Contents/Include/PolyClient.h @@ -41,10 +41,11 @@ namespace Polycode { char data[MAX_PACKET_SIZE]; unsigned int dataSize; unsigned short dataType; - - static const int EVENT_SERVER_DATA = 0; - static const int EVENT_CLIENT_READY = 1; - static const int EVENT_SERVER_DISCONNECTED = 2; + + static const int EVENTBASE_CLIENTEVENT = 0x600; + static const int EVENT_SERVER_DATA = EVENTBASE_CLIENTEVENT+0; + static const int EVENT_CLIENT_READY = EVENTBASE_CLIENTEVENT+1; + static const int EVENT_SERVER_DISCONNECTED = EVENTBASE_CLIENTEVENT+2; }; class _PolyExport Client : public Peer { diff --git a/Core/Contents/Include/PolyCocoaCore.h b/Core/Contents/Include/PolyCocoaCore.h index b37f95c40..2636e160b 100644 --- a/Core/Contents/Include/PolyCocoaCore.h +++ b/Core/Contents/Include/PolyCocoaCore.h @@ -59,13 +59,17 @@ namespace Polycode { int mouseX; int mouseY; + std::vector touches; + TouchInfo touch; + PolyKEY keyCode; wchar_t unicodeChar; char mouseButton; - static const int INPUT_EVENT = 0; - static const int FOCUS_EVENT = 1; + static const int EVENTBASE_PLATFORMEVENT = 0x300; + static const int INPUT_EVENT = EVENTBASE_PLATFORMEVENT+0; + static const int FOCUS_EVENT = EVENTBASE_PLATFORMEVENT+1; }; @@ -88,6 +92,7 @@ namespace Polycode { public: GamepadDeviceEntry() { numAxes = 0; + numButtons = 0; } vector axisElements; vector buttonElements; @@ -98,16 +103,30 @@ namespace Polycode { CoreInput *input; }; + class VideoModeChangeInfo { + public: + int xRes; + int yRes; + bool fullScreen; + bool vSync; + int aaLevel; + int anisotropyLevel; + bool needResolutionChange; + }; + 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(); - void setVideoMode(int xRes, int yRes, bool fullScreen, bool vSync, int aaLevel, int anisotropyLevel); + bool systemUpdate(); + + void Render(); + + 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); @@ -117,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=""); @@ -140,9 +160,7 @@ namespace Polycode { void unlockMutex(CoreMutex *mutex); CoreMutex *createMutex(); - void checkEvents(); - - vector getVideoModes(); + void checkEvents(); int lastMouseY; int lastMouseX; @@ -155,10 +173,20 @@ namespace Polycode { unsigned int nextDeviceID; bool checkSpecialKeyEvents(PolyKEY key); - + + Number getBackingXRes(); + Number getBackingYRes(); + + protected: + + void _setVideoMode(int xRes, int yRes, bool fullScreen, bool vSync, int aaLevel, int anisotropyLevel); + PolycodeView *glView; - uint64_t initTime; + uint64_t initTime; + bool retinaSupport; + + VideoModeChangeInfo modeChangeInfo; IOHIDManagerRef hidManager; }; diff --git a/Core/Contents/Include/PolyColor.h b/Core/Contents/Include/PolyColor.h index d289ebac9..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. */ @@ -69,6 +74,21 @@ namespace Polycode { */ Color(unsigned int hex); + /** + * Create from 0-255 integer data. + * @param r Red value 0-255. + * @param g Green value 0-255. + * @param b Blue value 0-255. + * @param a Alpha value 0-255. + */ + static Color ColorWithInts(int r,int g, int b, int a); + + /** + * Create from integer color. + * @param hex Integer color value. + */ + static Color ColorWithHex(unsigned int hex); + virtual ~Color(); /** @@ -181,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()); @@ -247,7 +267,8 @@ namespace Polycode { const static int BLEND_NORMAL = 0; const static int BLEND_REPLACE_COLOR = 1; - + const static int BLEND_ADDITIVE = 2; + protected: }; 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 bf36b6163..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,14 +93,28 @@ 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(); /** * Show or hide cursor. * @param newval True to show mouse, false to hide it. */ virtual void enableMouse(bool newval); + + /** + * Capture the mouse. + * + * The mouse will be unable to exit the polycode screen. + * + * @param newval True to capture the mouse, false to uncapture it. + */ + virtual void captureMouse(bool newval); /** * Sets the cursor the application is using. @@ -194,16 +214,19 @@ namespace Polycode { * @return Current vertical resolution. */ Number getYRes(); - - // deprecated - int getNumVideoModes(); - + /** - * Returns the available system video modes. - * @return An STL vector of video modes. - */ - virtual std::vector getVideoModes() = 0; - + * 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. * @param width If non-NULL, current screen width will be written here (or 0 if unknown). @@ -211,7 +234,10 @@ namespace Polycode { * @param hz If non-NULL, current screen refresh rate will be written here (or 0 if unknown). */ 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. @@ -241,12 +267,10 @@ namespace Polycode { /** * Opens a system folder picker and suspends operation. * @return The selected path returned from the picker. - */ - - - void setFramerate(int frameRate); - + */ virtual String openFolderPicker() = 0; + + void setFramerate(int frameRate, int maxFixedCycles = 8); /** * Opens a system file picker for the specified extensions. @@ -256,7 +280,8 @@ namespace Polycode { */ virtual std::vector openFilePicker(std::vector extensions, bool allowMultiple) = 0; - void setVideoModeIndex(int index, bool fullScreen, bool vSync, int aaLevel, int anisotropyLevel); + virtual String saveFilePicker(std::vector extensions) = 0; + /** * Sets a new video mode. @@ -265,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. @@ -292,27 +317,39 @@ 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; } - static const int EVENT_CORE_RESIZE = 0; - static const int EVENT_LOST_FOCUS = 1; - static const int EVENT_GAINED_FOCUS = 2; + static const int EVENTBASE_CORE = 0x200; + static const int EVENT_CORE_RESIZE = EVENTBASE_CORE+0; + static const int EVENT_LOST_FOCUS = EVENTBASE_CORE+1; + static const int EVENT_GAINED_FOCUS = EVENTBASE_CORE+2; - static const int EVENT_UNDO = 3; - static const int EVENT_REDO = 4; - static const int EVENT_COPY = 5; - static const int EVENT_CUT = 6; - static const int EVENT_SELECT_ALL = 7; - static const int EVENT_PASTE = 8; + static const int EVENT_UNDO = EVENTBASE_CORE+3; + static const int EVENT_REDO = EVENTBASE_CORE+4; + static const int EVENT_COPY = EVENTBASE_CORE+5; + static const int EVENT_CUT = EVENTBASE_CORE+6; + static const int EVENT_SELECT_ALL = EVENTBASE_CORE+7; + static const int EVENT_PASTE = EVENTBASE_CORE+8; virtual String executeExternalCommand(String command, String args, String inDirectory) = 0; @@ -346,6 +383,16 @@ namespace Polycode { bool paused; bool pauseOnLoseFocus; + + /** + * Default width of the desktop screen + */ + int defaultScreenWidth; + + /** + * Default height of the desktop screen + */ + int defaultScreenHeight; protected: @@ -354,13 +401,13 @@ namespace Polycode { void loseFocus(); void gainFocus(); - String userHomeDirectory; String defaultWorkingDirectory; void *userPointer; long refreshInterval; + unsigned int timeSleptMs; bool fullScreen; int aaLevel; @@ -376,8 +423,14 @@ namespace Polycode { unsigned int lastFrameTicks; unsigned int lastFPSTicks; unsigned int elapsed; + + double fixedElapsed; + double fixedTimestep; + double timeLeftOver; + double maxFixedElapsed; bool mouseEnabled; + bool mouseCaptured; unsigned int lastSleepFrameTicks; @@ -385,8 +438,8 @@ namespace Polycode { CoreMutex *threadedEventMutex; int xRes; - int yRes; - + int yRes; + int monitorIndex; int frames; diff --git a/Core/Contents/Include/PolyCoreInput.h b/Core/Contents/Include/PolyCoreInput.h index 9a5da6b77..7087305d9 100755 --- a/Core/Contents/Include/PolyCoreInput.h +++ b/Core/Contents/Include/PolyCoreInput.h @@ -97,6 +97,22 @@ namespace Polycode { */ bool getKeyState(PolyKEY keyCode); + /** + * Returns the state of the specified joystick button for the specified joystick index. If the joystick index is invalid, returns false + * @param joystickIndex Joystick index to check the state on + * @param button Joystick button to check the stat of. + * @return True if the button is pressed, false otherwise or if joystick index is invalid. + */ + bool getJoystickButtonState(int joystickIndex, int button); + + /** + * Returns the value of the specified joystick axis for the specified joystick index. If the joystick index is invalid, returns 0 + * @param joystickIndex Joystick index to check the state on + * @param axis Joystick axis to get the value of. + * @return Value of the joystick axis (0 if joystickIndex is invalid) + */ + Number getJoystickAxisValue(int joystickIndex, int axis); + /** * Returns the current mouse position as delta from last frame. * @return Mouse position as a 2d vector delta from last frame. @@ -118,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); @@ -145,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 f29d9356d..fceac1559 100755 --- a/Core/Contents/Include/PolyCoreServices.h +++ b/Core/Contents/Include/PolyCoreServices.h @@ -34,13 +34,14 @@ 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; /** * Global services singleton. CoreServices instantiates and provides global Singleton access to all of the main manager classes in Polycode as well as the Renderer and Config classes. @@ -64,9 +65,16 @@ namespace Polycode { */ Renderer *getRenderer(); - void Update(int elapsed, bool updateRenderer); + void Update(int elapsed); + void fixedUpdate(); + void Render(); void setCore(Core *core); + + /** + * Reloads the event listeners CoreServices configures as part of construction/setCore. Useful if removeAllListeners is called on the core input object. + */ + void setupBasicListeners(); /** * Returns the core. @@ -74,6 +82,13 @@ namespace Polycode { * @see Core */ Core *getCore(); + + /** + * Returns the core input. + * @return Core input. + * @see CoreInput + */ + CoreInput *getInput(); void handleEvent(Event *event); @@ -91,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. @@ -140,21 +149,21 @@ namespace Polycode { */ FontManager *getFontManager(); + /** + * Returns the logger. It can log messages and broadcast them to listeners. + */ + Logger *getLogger(); + /** * Returns the config. The config loads and saves data to disk. * @return Config manager. * @see Config - */ + */ + Config *getConfig(); - /** - * If set to true, will draw Screens before Scenes (defaults to false). - */ - bool drawScreensFirst; - ~CoreServices(); - - void *focusedChild; + ~CoreServices(); protected: @@ -173,8 +182,8 @@ namespace Polycode { Core *core; Config *config; MaterialManager *materialManager; - ScreenManager *screenManager; SceneManager *sceneManager; + Logger *logger; TimerManager *timerManager; TweenManager *tweenManager; ResourceManager *resourceManager; @@ -182,4 +191,8 @@ namespace Polycode { FontManager *fontManager; Renderer *renderer; }; + + + CoreServices *Services(); + } diff --git a/Core/Contents/Include/PolyCubemap.h b/Core/Contents/Include/PolyCubemap.h index 16f3272f5..3cfa85803 100644 --- a/Core/Contents/Include/PolyCubemap.h +++ b/Core/Contents/Include/PolyCubemap.h @@ -31,9 +31,24 @@ namespace Polycode { class _PolyExport Cubemap : public Resource { public: Cubemap(Texture *t0, Texture *t1, Texture *t2, Texture *t3, Texture *t4, Texture *t5); + + Texture *getTexture(int index); + void setTexture(Texture *texture, int index); + + virtual void recreateFromTextures(){} + virtual ~Cubemap(); + + static const int CUBEMAP_XPOS = 0; + static const int CUBEMAP_XNEG = 1; + static const int CUBEMAP_YPOS = 2; + static const int CUBEMAP_YNEG = 3; + static const int CUBEMAP_ZPOS = 4; + static const int CUBEMAP_ZNEG = 5; + + protected: - protected: + std::vector textures; }; } diff --git a/Core/Contents/Include/PolyEntity.h b/Core/Contents/Include/PolyEntity.h index ec5ea8c16..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(); @@ -86,12 +106,12 @@ namespace Polycode { * @param ignoreEditorOnly If true, ignore all child entities where editorOnly is set to true (will still clone the entity you call Clone() on even if its editorOnly flag is set to true. * @return The clone of the entity. */ - virtual Entity *Clone(bool deepClone, bool ignoreEditorOnly); + 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); + 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,24 +187,39 @@ namespace Polycode { /** @name Hierarchy operations. * These methods add and remove entities to and from each other. */ - //@{ - - /** - * @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. @@ -205,7 +250,12 @@ namespace Polycode { * If set to true, will automatically delete children upon destruction. (defaults to false). */ bool ownsChildren; - + + /** + * Sets the ownsChildren flag for this entity and recursively for all its child entities. + * @see ownsChildren + */ + void setOwnsChildrenRecursive(bool val); //@} // ---------------------------------------------------------------------------------------------------------------- @@ -221,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() @@ -234,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. @@ -260,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. @@ -299,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. @@ -326,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. @@ -350,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. @@ -403,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. @@ -425,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() */ @@ -462,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. @@ -523,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. @@ -565,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; @@ -578,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. @@ -601,7 +684,7 @@ namespace Polycode { * Returns the user data pointer. * @return User data pointer */ - void *getUserData(); + void *getUserData() const; /** * Sets the entity's blending mode. @@ -610,81 +693,242 @@ namespace Polycode { */ void setBlendingMode(int newBlendingMode); - Entity *getEntityById(String id, bool recursive); - std::vector getEntitiesByTag(String tag, bool recursive); - - Vector3 getChildCenter() const; - - std::vector entityProps; + /** + * 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; + + /** + * 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); - - - - //@} - protected: + + /** + * 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; + - std::vector tags; + /** + * 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: + - void checkTransformSetters(); + AABB aabb; + Vector3 bBox; + + int lastClickTicks; + Number yAdjust; + std::vector *tags; void *userData; std::vector children; - Vector3 childCenter; - Number bBoxRadius; + Vector3 anchorPoint; - Vector3 _position; - Vector3 _scale; - Rotation _rotation; - - Quaternion qYaw; - Quaternion qPitch; - Quaternion qRoll; + 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 5cd2321ba..6a3fb6dcf 100755 --- a/Core/Contents/Include/PolyEvent.h +++ b/Core/Contents/Include/PolyEvent.h @@ -60,18 +60,46 @@ namespace Polycode { void setEventCode(int eventCode); void setDispatcher(EventDispatcher *dispatcher); const String& getEventType() const; + + + void cancelEvent(); - static const int COMPLETE_EVENT = 0; - static const int CHANGE_EVENT = 1; - static const int CANCEL_EVENT = 2; - + // 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. + // Bases for all Polycode classes are documented below, third party event types should be EVENTBASE_NONPOLYCODE or over. + // Note that collisions are usually safe as long as collisions do not occur between a class and its subclass. + // Event 0x100 + // Core 0x200 + // PlatformCore 0x300 (e.g. CocoaCore, WinCore etc) + // InputEvent 0x400 + // SocketEvent 0x500 + // ClientEvent 0x600 + // ServerEvent 0x700 + // PhysicsScreenEvent 0x800 + // PhysicsSceneEvent 0x900 + // UIEvent 0xA00 + // UITreeEvent 0xB00 + + static const int EVENTBASE_EVENT = 0x100; + static const int COMPLETE_EVENT = EVENTBASE_EVENT+0; + static const int CHANGE_EVENT = EVENTBASE_EVENT+1; + static const int CANCEL_EVENT = EVENTBASE_EVENT+2; + static const int NOTIFY_EVENT = EVENTBASE_EVENT+3; + static const int FIRE_EVENT = EVENTBASE_EVENT+4; + 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/PolyFixedShader.h b/Core/Contents/Include/PolyFixedShader.h index 14b3028f3..41f35493d 100755 --- a/Core/Contents/Include/PolyFixedShader.h +++ b/Core/Contents/Include/PolyFixedShader.h @@ -47,7 +47,6 @@ namespace Polycode { void addTexture(const String& name, Texture *texture); void addCubemap(const String& name, Cubemap *cubemap); - void addParam(const String& type, const String& name, const String& value); Texture *getDiffuseTexture(); 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/PolyGLCubemap.h b/Core/Contents/Include/PolyGLCubemap.h index 8c0bd1a3a..99b59180a 100644 --- a/Core/Contents/Include/PolyGLCubemap.h +++ b/Core/Contents/Include/PolyGLCubemap.h @@ -25,8 +25,8 @@ THE SOFTWARE. #include "PolyGlobals.h" #include "PolyCubemap.h" -#ifdef _WINDOWS -#include +#ifdef _WINDOWS +#include #endif #if defined(__APPLE__) && defined(__MACH__) @@ -48,10 +48,13 @@ namespace Polycode { OpenGLCubemap(Texture *t0, Texture *t1, Texture *t2, Texture *t3, Texture *t4, Texture *t5); virtual ~OpenGLCubemap(); + void recreateFromTextures(); + GLuint getTextureID(); protected: - + + bool glCubemapLoaded; int filteringMode; GLuint textureID; }; 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/PolyGLHeaders.h b/Core/Contents/Include/PolyGLHeaders.h index a7ed08739..5e6982b6c 100644 --- a/Core/Contents/Include/PolyGLHeaders.h +++ b/Core/Contents/Include/PolyGLHeaders.h @@ -96,6 +96,10 @@ typedef GLEEPFNGLVERTEXATTRIBPOINTERPROC PFNGLVERTEXATTRIBPOINTERPROC; typedef GLEEPFNGLENABLEVERTEXATTRIBARRAYARBPROC PFNGLENABLEVERTEXATTRIBARRAYARBPROC; typedef GLEEPFNGLBINDATTRIBLOCATIONPROC PFNGLBINDATTRIBLOCATIONPROC; typedef GLEEPFNGLUNIFORM2FPROC PFNGLUNIFORM2FPROC; +typedef GLEEPFNWGLSWAPINTERVALEXTPROC PFNWGLSWAPINTERVALEXTPROC; +typedef GLEEPFNWGLGETSWAPINTERVALEXTPROC PFNWGLGETSWAPINTERVALEXTPROC; +typedef GLEEPFNGLGETPROGRAMIVPROC PFNGLGETPROGRAMIVPROC; +typedef GLEEPFNGLGETACTIVEUNIFORMPROC PFNGLGETACTIVEUNIFORMPROC; #endif #if defined(__APPLE__) && defined(__MACH__) diff --git a/Core/Contents/Include/PolyGLRenderer.h b/Core/Contents/Include/PolyGLRenderer.h index fd986bd20..7679c7fb5 100755 --- a/Core/Contents/Include/PolyGLRenderer.h +++ b/Core/Contents/Include/PolyGLRenderer.h @@ -1,4 +1,4 @@ - + /* Copyright (C) 2011 by Ivan Safrin @@ -100,6 +100,8 @@ namespace Polycode { OpenGLRenderer(); ~OpenGLRenderer(); + + bool Init(); void Resize(int xRes, int yRes); void BeginRender(); @@ -107,28 +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); @@ -141,21 +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); - - void initOSSpecific(); + 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); @@ -164,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 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); @@ -186,18 +195,14 @@ 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: - - - Number nearPlane; - Number farPlane; + void initOSSpecific(); int verticesToDraw; diff --git a/Core/Contents/Include/PolyGLSLProgram.h b/Core/Contents/Include/PolyGLSLProgram.h index 8070b225e..5e3275fc3 100755 --- a/Core/Contents/Include/PolyGLSLProgram.h +++ b/Core/Contents/Include/PolyGLSLProgram.h @@ -29,26 +29,15 @@ THE SOFTWARE. namespace Polycode { -class _PolyExport GLSLProgramParam : public ProgramParam { - public: - static void createParamData(int *retType, const String& type, const String& value, const String& min, const String& max, void **valueRes, void **minRes, void **maxRes); - }; - - class _PolyExport GLSLProgram : public Resource { + class _PolyExport GLSLProgram : public ShaderProgram { public: - GLSLProgram(int type); + GLSLProgram(String fileName, int type); virtual ~GLSLProgram(); - GLSLProgramParam addParam(const String& name, const String& typeString, const String& valueString, bool isAuto, int autoID, int paramType, void *defaultData, void *minData, void *maxData); - + void reloadProgram(); + unsigned int program; -// GLSLparameter modelViewProjection; + String fileName; - static const int TYPE_VERT = 0; - static const int TYPE_FRAG = 1; - - int type; - - std::vector params; }; } diff --git a/Core/Contents/Include/PolyGLSLShader.h b/Core/Contents/Include/PolyGLSLShader.h index c6887261e..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); @@ -49,29 +38,17 @@ namespace Polycode { ShaderBinding *createBinding(); virtual void reload(); - - unsigned int shader_id; - GLSLProgram *vp; - GLSLProgram *fp; - protected: - void linkProgram(); - }; - - class _PolyExport GLSLShaderBinding : public ShaderBinding { - public: - GLSLShaderBinding(GLSLShader *shader); - virtual ~GLSLShaderBinding(); + void handleEvent(Event *event); - void addTexture(const String& name, Texture *texture); - void addCubemap(const String& name, Cubemap *cubemap); - void clearTexture(const String& name); - Texture *getTexture(const String& name); - void addParam(const String& type, const String& name, const String& value); + static int getPolycodeParamType(int glType); + + void setVertexProgram(ShaderProgram *vp); + void setFragmentProgram(ShaderProgram *fp); + + unsigned int shader_id; - std::vector textures; - std::vector cubemaps; - - GLSLShader *glslShader; - }; + void linkProgram(); + void unlinkProgram(); + }; } diff --git a/Core/Contents/Include/PolyGLSLShaderModule.h b/Core/Contents/Include/PolyGLSLShaderModule.h index b7e8d3ba9..37a2d570a 100755 --- a/Core/Contents/Include/PolyGLSLShaderModule.h +++ b/Core/Contents/Include/PolyGLSLShaderModule.h @@ -27,8 +27,10 @@ THE SOFTWARE. namespace Polycode { class GLSLProgram; - class GLSLProgramParam; + class ProgramParam; class GLSLShader; + class ShaderProgram; + class ResourcePool; class _PolyExport GLSLShaderModule : public PolycodeShaderModule { public: @@ -36,19 +38,18 @@ namespace Polycode { virtual ~GLSLShaderModule(); bool acceptsExtension(const String& extension); - Resource* createProgramFromFile(const String& extension, const String& fullPath); + ShaderProgram* createProgramFromFile(const String& extension, const String& fullPath); void reloadPrograms(); String getShaderType(); - Shader *createShader(TiXmlNode *node); + 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(); protected: - - GLSLProgramParam addParamToProgram(GLSLProgram *program,TiXmlNode *node); - void recreateGLSLProgram(GLSLProgram *prog, const String& fileName, int type); + GLSLProgram *createGLSLProgram(const String& fileName, int type); - void updateGLSLParam(Renderer *renderer, GLSLShader *glslShader, GLSLProgramParam ¶m, ShaderBinding *materialOptions, ShaderBinding *localOptions); + void updateGLSLParam(Renderer *renderer, GLSLShader *glslShader, ProgramParam ¶m, ShaderBinding *materialOptions, ShaderBinding *localOptions); std::vector programs; }; 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 f2d13c4e8..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,7 +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 @@ -75,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) @@ -84,8 +110,12 @@ inline Number clampf(Number x, Number a, Number b) return x < a ? a : (x > b ? b : x); } -#define MIN(a, b) (((a) < (b)) ? (a) : (b)) -#define MAX(a, b) (((a) > (b)) ? (a) : (b)) +#ifndef MIN + #define MIN(a, b) (((a) < (b)) ? (a) : (b)) +#endif +#ifndef MAX + #define MAX(a, b) (((a) > (b)) ? (a) : (b)) +#endif // Special flag read by create_lua_library parser, suppresses Lua bindings for item. #define POLYIGNORE 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 7a78cd48c..65b8e346c 100755 --- a/Core/Contents/Include/PolyImage.h +++ b/Core/Contents/Include/PolyImage.h @@ -23,11 +23,25 @@ THE SOFTWARE. #pragma once #include "PolyGlobals.h" #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. */ @@ -38,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. @@ -46,7 +60,7 @@ namespace Polycode { * @param height Height of the image to create. * @param type Type of image to create. Can be IMAGE_RGBA or IMAGE_RGB. */ - Image(int width, int height, int type = IMAGE_RGBA); + Image(int width, int height, int type = Image::IMAGE_RGBA); /** * Create an image of specified size and type and set its contents from the specified buffer. @@ -55,7 +69,15 @@ namespace Polycode { * @param height Height of the image to create. * @param type Type of image to create. Can be IMAGE_RGBA or IMAGE_RGB. */ - Image(char *data, int width, int height, int type = IMAGE_RGBA); + Image(char *data, int width, int height, int type = Image::IMAGE_RGBA); + + /** + * Create a blank image of specified size and type. + * @param width Width of the image to create. + * @param height Height of the image to create. + * @param type Type of image to create. Can be IMAGE_RGBA or IMAGE_RGB. + */ + static Image *BlankImage(int width, int height, int type = Image::IMAGE_RGBA); /** * Create an image from another image. @@ -71,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. @@ -95,16 +119,13 @@ namespace Polycode { * @param width Width of the image to create. * @param height Height of the image to create. */ - void createEmpty(unsigned int width, unsigned int height); - + void createEmpty(int width, int height, const Color &fillColor); + /** * Fills the image with the specified color values. - * @param r Red value 0-1. - * @param g Green value 0-1 - * @param b Blue value 0-1 - * @param a Alpha value 0-1 + * @param color The color to fill it with. */ - void fill(Number r, Number g, Number b, Number a); + void fill(const Color &color); /** * Sets a pixel at specified coordinates to specified color. @@ -143,21 +164,21 @@ namespace Polycode { * @param y1 Ending y position. * @param col Color to use. */ - void line(int x0, int y0, int x1, int y1, Color col); + void drawLine(int x0, int y0, int x1, int y1, Color col); /** * Moves brush to specified position * @param x New brush position X * @param y New brush position Y */ - void moveTo(int x, int y); + void moveBrushTo(int x, int y); /** * Translates brush a specified amount relative to its current position. * @param x Amount to translate on X axis * @param y Amount to translate on Y axis */ - void move(int x, int y); + void moveBrush(int x, int y); /** * Draws a line to specified position. @@ -165,7 +186,7 @@ namespace Polycode { * @param y Ending y position. * @param col Color to use. */ - void lineTo(int x, int y, Color col); + void drawLineTo(int x, int y, Color col); /** * Draws a rectangle with specified color. @@ -175,7 +196,7 @@ namespace Polycode { * @param h Rectangle height. * @param col Color to use. */ - void drawRect(int x, int y, int w, int h, Color col); + void fillRect(int x, int y, int w, int h, Color col); /** * Draws perlin noise in the image @@ -192,27 +213,25 @@ 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. * @param x X position of the area to return. * @param y Y position of the area to return. * @param width Width of the area to return. - * @param height Height of the area to return. + * @param height Height of the area to return. + * @return Raw image data, in the format specified by the constructor. */ - char *getPixelsInRect(unsigned int x, unsigned int y, unsigned int width, unsigned int height); + char *getPixelsInRect(int x, int y, int width, int height); + + /** + * Returns a copy of the specified subRect part of the image. + * + * @param subRect The part of the image to copy. (0, 0) refers to the top left of the image. + * @return A pointer to an Image object allocated with new. You have to manually delete this + * object using free. + */ + Image *getImagePart(Rectangle subRect); /** * Returns the x position of the brush. @@ -228,24 +247,25 @@ namespace Polycode { int getType() const { return imageType; } - void writeBMP(const String& fileName) const; - /** * Returns the width of the image. */ - unsigned int getWidth() const; + int getWidth() const; /** * Returns the height of the image. */ - unsigned int getHeight() const; + int getHeight() const; /** * Returns the raw image data - * @return Pointer to raw image data. + * @return Pointer to raw image data, in the format specified by the constructor. */ char *getPixels(); + /** + * Multiplies the RGB values by alpha for each pixel. + */ void premultiplyAlpha(); static const int IMAGE_RGB = 0; @@ -254,7 +274,21 @@ namespace Polycode { protected: - void setPixelType(int type); + 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 + // to internal bottomleft position mode + // + // results are written directly into the pointers + void transformCoordinates(int *x, int *y); + void transformCoordinates(int *x, int *y, int *w, int *h); int imageType; int pixelSize; @@ -266,8 +300,8 @@ namespace Polycode { int brushPosY; char *imageData; - unsigned int width; - unsigned int height; + int width; + int height; }; } diff --git a/Core/Contents/Include/PolyInputEvent.h b/Core/Contents/Include/PolyInputEvent.h index dc3759a04..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. */ @@ -50,30 +66,30 @@ namespace Polycode { * Possible input event types dispatched by CoreInput. */ //@{ - static const int EVENT_MOUSEDOWN = 0; - static const int EVENT_MOUSEUP = 1; - static const int EVENT_MOUSEMOVE = 2; - static const int EVENT_MOUSEOVER = 3; - static const int EVENT_MOUSEOUT = 4; - static const int EVENT_DOUBLECLICK = 5; - static const int EVENT_MOUSEUP_OUTSIDE = 6; - static const int EVENT_MOUSEWHEEL_UP = 7; - static const int EVENT_MOUSEWHEEL_DOWN = 8; - - static const int EVENT_KEYDOWN = 13; - static const int EVENT_KEYUP = 14; - - static const int EVENT_JOYBUTTON_DOWN = 15; - static const int EVENT_JOYBUTTON_UP = 16; - static const int EVENT_JOYAXIS_MOVED = 17; - static const int EVENT_JOYDEVICE_ATTACHED = 18; - static const int EVENT_JOYDEVICE_DETACHED = 19; - - static const int EVENT_TOUCHES_BEGAN = 20; - static const int EVENT_TOUCHES_MOVED = 21; - static const int EVENT_TOUCHES_ENDED =22; - - + static const int EVENTBASE_INPUTEVENT = 0x400; + static const int EVENT_MOUSEDOWN = EVENTBASE_INPUTEVENT+0; + static const int EVENT_MOUSEUP = EVENTBASE_INPUTEVENT+1; + static const int EVENT_MOUSEMOVE = EVENTBASE_INPUTEVENT+2; + static const int EVENT_MOUSEOVER = EVENTBASE_INPUTEVENT+3; + static const int EVENT_MOUSEOUT = EVENTBASE_INPUTEVENT+4; + static const int EVENT_DOUBLECLICK = EVENTBASE_INPUTEVENT+5; + static const int EVENT_MOUSEUP_OUTSIDE = EVENTBASE_INPUTEVENT+6; + static const int EVENT_MOUSEWHEEL_UP = EVENTBASE_INPUTEVENT+7; + static const int EVENT_MOUSEWHEEL_DOWN = EVENTBASE_INPUTEVENT+8; + + static const int EVENT_KEYDOWN = EVENTBASE_INPUTEVENT+13; + static const int EVENT_KEYUP = EVENTBASE_INPUTEVENT+14; + + static const int EVENT_JOYBUTTON_DOWN = EVENTBASE_INPUTEVENT+15; + static const int EVENT_JOYBUTTON_UP = EVENTBASE_INPUTEVENT+16; + static const int EVENT_JOYAXIS_MOVED = EVENTBASE_INPUTEVENT+17; + static const int EVENT_JOYDEVICE_ATTACHED = EVENTBASE_INPUTEVENT+18; + static const int EVENT_JOYDEVICE_DETACHED = EVENTBASE_INPUTEVENT+19; + + 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; + //@} // ---------------------------------------------------------------------------------------------------------------- @@ -97,6 +113,7 @@ namespace Polycode { PolyKEY key; + wchar_t getCharCode(); int keyCode() { return key; } @@ -108,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 13835e91c..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,52 +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); 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 37b23b375..bbaa121a8 100755 --- a/Core/Contents/Include/PolyLogger.h +++ b/Core/Contents/Include/PolyLogger.h @@ -22,15 +22,72 @@ THE SOFTWARE. #pragma once #include "PolyGlobals.h" +#include "PolyEventDispatcher.h" namespace Polycode { - class _PolyExport Logger : public PolyBase { + class _PolyExport LoggerEvent : public Event { public: - Logger(){} - ~Logger(){} + LoggerEvent(String message); + virtual ~LoggerEvent(); + + 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 fbdf701f3..193c6c5c2 100755 --- a/Core/Contents/Include/PolyMaterial.h +++ b/Core/Contents/Include/PolyMaterial.h @@ -35,16 +35,24 @@ 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); + void addShaderAtIndex(Shader *shader,ShaderBinding *shaderBinding, int shaderIndex); unsigned int getNumShaders() const; + void removeShader(int shaderIndex); + void addShaderRenderTarget(ShaderRenderTarget *newTarget); int getNumShaderRenderTargets(); ShaderRenderTarget *getShaderRenderTarget(unsigned int index); - + void removeShaderRenderTarget(int index); + void recreateRenderTarget(ShaderRenderTarget *renderTarget); + void recreateRenderTargets(); + + void handleEvent(Event *event); + const String& getName() const; Shader *getShader(unsigned int index) const; ShaderBinding *getShaderBinding(unsigned int index) const; @@ -59,6 +67,9 @@ 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 560561956..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; @@ -37,6 +38,8 @@ namespace Polycode { class SceneRenderTexture; 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. @@ -45,8 +48,6 @@ namespace Polycode { public: MaterialManager(); ~MaterialManager(); - - void Update(int elapsed); /** * Creates a new framebuffer texture. @@ -55,47 +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 *createMaterial(String materialName, String shaderName); + Material *materialFromXMLNode(ResourcePool *resourcePool, TiXmlNode *node); - Shader *setShaderFromXMLNode(TiXmlNode *node); - Shader *createShaderFromXMLNode(TiXmlNode *node); + Material *createMaterial(ResourcePool *resourcePool, String materialName, String shaderName); - void registerShader(Shader *shader); + 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); - - void addMaterial(Material *material); - - 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 23eb18ef8..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];} @@ -265,7 +284,7 @@ namespace Polycode { *ax = -fabs(angle_x); *ay = fabs(angle_y); - *az = fabs(angle_z); + *az = -fabs(angle_z); } @@ -282,7 +301,7 @@ namespace Polycode { /** * Returns the inverse of the matrix. */ - Matrix4 inverse() const; + Matrix4 Inverse() const; /** * Returns the affine inverse of the matrix. diff --git a/Core/Contents/Include/PolyMesh.h b/Core/Contents/Include/PolyMesh.h index b42b612f6..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,22 +79,22 @@ 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); - virtual ~Mesh(); - /** - * Adds a polygon to the mesh. - * @param newPolygon Polygon to add. - */ - void addPolygon(Polygon *newPolygon); + * Construct from a mesh loaded from a file. + * @param fileName Path to mesh file. + */ + static Mesh *MeshFromFileName(String& fileName); + virtual ~Mesh(); + /** * Loads a mesh from a file. * @param fileName Path to mesh file. @@ -153,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. @@ -170,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. @@ -198,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. @@ -206,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. @@ -214,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. @@ -223,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. @@ -231,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). @@ -268,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. */ @@ -288,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. @@ -317,11 +308,6 @@ namespace Polycode { */ static const int TRIFAN_MESH = 2; - /** - * Triangle strip based mesh. - */ - static const int TRISTRIP_MESH = 3; - /** * Line based mesh. */ @@ -341,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 ca5438dec..29f5ef943 100644 --- a/Core/Contents/Include/PolyModule.h +++ b/Core/Contents/Include/PolyModule.h @@ -20,6 +20,8 @@ namespace Polycode { class Shader; class ShaderBinding; class Resource; + class ShaderProgram; + class ResourcePool; class _PolyExport PolycodeModule : public PolyBase { public: @@ -46,10 +48,11 @@ namespace Polycode { virtual ~PolycodeShaderModule(); virtual bool acceptsExtension(const String& extension) = 0; - virtual Resource* createProgramFromFile(const String& extension, const String& fullPath) = 0; + virtual ShaderProgram* createProgramFromFile(const String& extension, const String& fullPath) = 0; virtual String getShaderType() = 0; - virtual Shader *createShader(TiXmlNode *node) = 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; } virtual void clearShader() = 0; diff --git a/Core/Contents/Include/PolyObject.h b/Core/Contents/Include/PolyObject.h index e6010bfd7..14312b0d9 100644 --- a/Core/Contents/Include/PolyObject.h +++ b/Core/Contents/Include/PolyObject.h @@ -38,8 +38,10 @@ namespace Polycode { /** * Default constructor */ - ObjectEntry() { type = ObjectEntry::CONTAINER_ENTRY; 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). */ @@ -74,6 +76,98 @@ namespace Polycode { * Length of this object entry if its type is ARRAY_ENTRY. */ int length; + + /** + * Tries to write the Number value of this[key] to out. + * @param out A pointer to the value to write the number value to. + * @return true if this[key] is a number, false otherwise. + */ + bool readNumber(String key, Number *out) { + ObjectEntry *child = this->operator[](key); + + if(!child) { + return false; + } + + if(child->type == FLOAT_ENTRY) { + *out = child->NumberVal; + return true; + } else if(child->type == INT_ENTRY) { + *out = (Number) child->intVal; + return true; + } + + return false; + } + + /** + * Tries to write the int value of this[key] to out. + * @param out A pointer to the value to write the int value to. + * @return true if this[key] is an integer, false otherwise. + */ + bool readInt(String key, int *out) { + ObjectEntry *child = this->operator[](key); + + if(!child) { + return false; + } + + if(child->type == INT_ENTRY) { + *out = child->intVal; + return true; + } + + return false; + } + + /** + * Tries to write the int value of this[key] to out. + * @param out A pointer to the value to write the int value to. + * @return true if this[key] is an integer, false otherwise. + */ + bool readInt(String key, unsigned *out) { + return readInt(key, (int*) out); + } + + /** + * Tries to write the String value of this[key] to out. + * @param out A pointer to the value to write the String value to. + * @return true if this[key] is a String, false otherwise. + */ + bool readString(String key, String *out) { + ObjectEntry *child = this->operator[](key); + + if(!child) { + return false; + } + + if(child->type == STRING_ENTRY) { + *out = child->stringVal; + return true; + } + + return false; + } + + /** + * Tries to write the boolean value of this[key] to out. + * @param out A pointer to the value to write the boolean value to. + * @return true if this[key] is a bool, false otherwise. + */ + bool readBool(String key, bool *out) { + ObjectEntry *child = this->operator[](key); + + if(!child) { + return false; + } + + if(child->type == BOOL_ENTRY) { + *out = child->boolVal; + return true; + } + + return false; + } /** * Adds an empty child entry. @@ -85,7 +179,7 @@ namespace Polycode { entry->type = ObjectEntry::CONTAINER_ENTRY; entry->name = name; children.push_back(entry); - length = children.size(); + length = (int)children.size(); return entry; } @@ -101,7 +195,7 @@ namespace Polycode { entry->NumberVal = val; entry->name = name; children.push_back(entry); - length = children.size(); + length = (int)children.size(); return entry; } @@ -118,7 +212,7 @@ namespace Polycode { entry->intVal = val; entry->name = name; children.push_back(entry); - length = children.size(); + length = (int)children.size(); return entry; } @@ -134,7 +228,7 @@ namespace Polycode { entry->stringVal = val; entry->name = name; children.push_back(entry); - length = children.size(); + length = (int)children.size(); return entry; } @@ -150,7 +244,7 @@ namespace Polycode { entry->stringVal = val; entry->name = name; children.push_back(entry); - length = children.size(); + length = (int)children.size(); return entry; } @@ -167,13 +261,13 @@ namespace Polycode { entry->boolVal = val; entry->name = name; children.push_back(entry); - length = children.size(); + length = (int)children.size(); return entry; } ObjectEntry *addChild(ObjectEntry *entry) { children.push_back(entry); - length = children.size(); + length = (int)children.size(); return 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 b8976139f..662e6a178 100755 --- a/Core/Contents/Include/PolyParticleEmitter.h +++ b/Core/Contents/Include/PolyParticleEmitter.h @@ -22,338 +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(); - - void setParticleTexture(Texture *texture); - - Vector3 emitterRadius; - - Number perlinModSize; - - bool perlinEnabled; - - Number rotationSpeed; - - int emitterType; - protected: - - 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); - virtual void applyClone(Entity *clone, bool deepClone, bool ignoreEditorOnly); - - /** - * 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 0e34a91e5..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,39 +55,90 @@ 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; }; - + + /** + * A network actor that can send and receive data. + * + * Peers are comparable to UDP sockets, but with extended functionality + * to optionally allow for some of TCP's features(ordering, reliability). + * + * WARNING: Reliability(packets being resent on loss) is currently not + * implemented, but is planned. + * + * @see PeerConnection + */ #if USE_THREADED_SOCKETS == 1 class _PolyExport Peer : public Threaded { #else class _PolyExport Peer : public EventDispatcher { #endif public: + /** + * Create a peer. The peer will immediately start listening on the given + * port and accept incoming packets. + * + * @param port The UDP port to listen for packets. Can not be omitted, + * this will be the actual port this peer will use to send + * and receive packets. + */ Peer(unsigned int port); ~Peer(); - + void handleEvent(Event *event); virtual void handlePacket(Packet *packet, PeerConnection *connection){}; virtual void handlePeerConnection(PeerConnection *connection){}; Packet *createPacket(const Address &target, char *data, unsigned int size, unsigned short type); - + + /** + * Send raw binary data to the target address. + * + * @param target The network Address to send the data to. + * @param data The binary data to send as a C byte array. Length must be supplied by size parameter. + * @param size The size in bytes of the sent binary data. + * @param type A number representing the packet type, used to define the purpose of the packet. + */ void sendData(const Address &target, char *data, unsigned int size, unsigned short type); + + /** + * Send raw binary data to the target address, making sure it arrives in the right order. + * + * @param target The network address to send the data to. + * @param data The binary data to send as a C byte array. Length must be supplied by size parameter. + * @param size The size in bytes of the sent binary data. + * @param type A number representing the packet type, used to define the purpose of the packet. + */ void sendReliableData(const Address &target, char *data, unsigned int size, unsigned short type); + + /** + * Broadcast raw binary data to all connected peers, making sure it arrives in the right order. + * + * @param data The binary data to send as a C byte array. Length must be supplied by size parameter. + * @param size The size in bytes of the sent binary data. + * @param type A number representing the packet type, used to define the purpose of the packet. + */ void sendReliableDataToAll(char *data, unsigned int size, unsigned short type); + + /** + * Broadcast raw binary data to all connected peers. + * + * @param data The binary data to send as a C byte array. Length must be supplied by size parameter. + * @param size The size in bytes of the sent binary data. + * @param type A number representing the packet type, used to define the purpose of the packet. + */ void sendDataToAll(char *data, unsigned int size, unsigned short type); void sendPacket(const Address &target, Packet *packet); @@ -99,6 +149,8 @@ namespace Polycode { PeerConnection *addPeerConnection(const Address &address); void removePeerConnection(PeerConnection* connection); + void setReliableRetransmissionInterval(int interval); + void updateReliableDataQueue(); virtual void updatePeer(){} @@ -106,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 eac2eba31..9ad2da650 100755 --- a/Core/Contents/Include/PolyQuaternion.h +++ b/Core/Contents/Include/PolyQuaternion.h @@ -58,132 +58,133 @@ namespace Polycode { inline void setFromMatrix(const Matrix4 &_mat) { - Number fTrace = _mat.m[0][0]+_mat.m[1][1]+_mat.m[2][2]; - Number fRoot; - - if ( fTrace > 0.0 ) { - fRoot = sqrtf(fTrace + 1.0); // 2w - w = 0.5*fRoot; - fRoot = 0.5/fRoot; - x = (_mat.m[2][1]-_mat.m[1][2])*fRoot; - y = (_mat.m[0][2]-_mat.m[2][0])*fRoot; - z = (_mat.m[1][0]-_mat.m[0][1])*fRoot; - } - else - { - static size_t s_iNext[3] = { 1, 2, 0 }; - size_t i = 0; - if ( _mat.m[1][1] > _mat.m[0][0] ) - i = 1; - if ( _mat.m[2][2] > _mat.m[i][i] ) - i = 2; - size_t j = s_iNext[i]; - size_t k = s_iNext[j]; + Number fTrace = _mat.m[0][0]+_mat.m[1][1]+_mat.m[2][2]; + Number fRoot; - fRoot = sqrtf(_mat.m[i][i]-_mat.m[j][j]-_mat.m[k][k] + 1.0); - Number* apkQuat[3] = { &x, &y, &z }; - *apkQuat[i] = 0.5*fRoot; - fRoot = 0.5/fRoot; - w = (_mat.m[k][j]-_mat.m[j][k])*fRoot; - *apkQuat[j] = (_mat.m[j][i]+_mat.m[i][j])*fRoot; - *apkQuat[k] = (_mat.m[k][i]+_mat.m[i][k])*fRoot; - } - } + if ( fTrace > 0.0 ) { + fRoot = sqrtf(fTrace + 1.0); // 2w + w = 0.5*fRoot; + fRoot = 0.5/fRoot; + x = (_mat.m[2][1]-_mat.m[1][2])*fRoot; + y = (_mat.m[0][2]-_mat.m[2][0])*fRoot; + z = (_mat.m[1][0]-_mat.m[0][1])*fRoot; + } + else + { + static size_t s_iNext[3] = { 1, 2, 0 }; + size_t i = 0; + if ( _mat.m[1][1] > _mat.m[0][0] ) + i = 1; + if ( _mat.m[2][2] > _mat.m[i][i] ) + i = 2; + size_t j = s_iNext[i]; + size_t k = s_iNext[j]; + + fRoot = sqrtf(_mat.m[i][i]-_mat.m[j][j]-_mat.m[k][k] + 1.0); + Number* apkQuat[3] = { &x, &y, &z }; + *apkQuat[i] = 0.5*fRoot; + fRoot = 0.5/fRoot; + w = (_mat.m[k][j]-_mat.m[j][k])*fRoot; + *apkQuat[j] = (_mat.m[j][i]+_mat.m[i][j])*fRoot; + *apkQuat[k] = (_mat.m[k][i]+_mat.m[i][k])*fRoot; + } + } static Quaternion Slerp(Number fT, const Quaternion& rkP, const Quaternion& rkQ, bool shortestPath=false); Number Dot(const Quaternion& rkQ) const; - Quaternion Log () const; - Quaternion Exp () const; - Number Norm () const; - Number normalize(); - Quaternion operator+ (const Quaternion& rkQ) const; - Quaternion operator* (const Quaternion& rkQ) const; - Quaternion operator* (Number fScalar) const; - - inline void lookAt(const Vector3 &D, const Vector3 &upVector) { - /* - Vector3 D; - Vector3 back = D * -1; - back.Normalize(); - - Vector3 right = back.crossProduct(upVector) ; - right.Normalize(); - right = right * -1; - - Vector3 up = back.crossProduct(right); - - set( y.z - z.y , z.x - x.z, x.y - y.x, tr + 1.0f ); - */ - } - - void createFromMatrix(const Matrix4& matrix) { - Number tr, s, q[4]; - int i, j, k; - - static const int nxt[3] = {1, 2, 0}; - - tr = matrix.m[0][0] + matrix.m[1][1] + matrix.m[2][2]; - - - // check the diagonal - if (tr > 0.0f) - { - s = sqrtf(tr + 1.0f); - w = s / 2.0f; - s = 0.5f / s; - x = (matrix.m[1][2] - matrix.m[2][1]) * s; - y = (matrix.m[2][0] - matrix.m[0][2]) * s; - z = (matrix.m[0][1] - matrix.m[1][0]) * s; + Quaternion Log () const; + Quaternion Exp () const; + Number Norm () const; + Number Normalize(); + Quaternion operator+ (const Quaternion& rkQ) const; + Quaternion operator* (const Quaternion& rkQ) const; + Quaternion operator* (Number fScalar) const; + + // TODO: implement + inline void lookAt(const Vector3 &D, const Vector3 &upVector) { + /* + Vector3 D; + Vector3 back = D * -1; + back.Normalize(); + + Vector3 right = back.crossProduct(upVector) ; + right.Normalize(); + right = right * -1; + + Vector3 up = back.crossProduct(right); + + set( y.z - z.y , z.x - x.z, x.y - y.x, tr + 1.0f ); + */ } - else - { - // diagonal is negative - i = 0; - if (matrix.m[1][1] > matrix.m[0][0]) i = 1; - if (matrix.m[2][2] > matrix.m[i][i]) i = 2; - j = nxt[i]; - k = nxt[j]; + + void createFromMatrix(const Matrix4& matrix) { + Number tr, s, q[4]; + int i, j, k; - s = sqrtf((matrix.m[i][i] - (matrix.m[j][j] + matrix.m[k][k])) + 1.0f); + static const int nxt[3] = {1, 2, 0}; - q[i] = s * 0.5f; + tr = matrix.m[0][0] + matrix.m[1][1] + matrix.m[2][2]; - if (s != 0.0f) s = 0.5f / s; - q[3] = (matrix.m[j][k] - matrix.m[k][j]) * s; - q[j] = (matrix.m[i][j] + matrix.m[j][i]) * s; - q[k] = (matrix.m[i][k] + matrix.m[k][i]) * s; + // check the diagonal + if (tr > 0.0f) + { + s = sqrtf(tr + 1.0f); + w = s / 2.0f; + s = 0.5f / s; + x = (matrix.m[1][2] - matrix.m[2][1]) * s; + y = (matrix.m[2][0] - matrix.m[0][2]) * s; + z = (matrix.m[0][1] - matrix.m[1][0]) * s; + } + else + { + // diagonal is negative + i = 0; + if (matrix.m[1][1] > matrix.m[0][0]) i = 1; + if (matrix.m[2][2] > matrix.m[i][i]) i = 2; + j = nxt[i]; + k = nxt[j]; + + s = sqrtf((matrix.m[i][i] - (matrix.m[j][j] + matrix.m[k][k])) + 1.0f); + + q[i] = s * 0.5f; + + if (s != 0.0f) s = 0.5f / s; + + q[3] = (matrix.m[j][k] - matrix.m[k][j]) * s; + q[j] = (matrix.m[i][j] + matrix.m[j][i]) * s; + q[k] = (matrix.m[i][k] + matrix.m[k][i]) * s; + + x = q[0]; + y = q[1]; + z = q[2]; + w = q[3]; + } - x = q[0]; - y = q[1]; - z = q[2]; - w = q[3]; } - - } - inline bool operator== (const Quaternion& rhs) const - { - return (rhs.x == x) && (rhs.y == y) && - (rhs.z == z) && (rhs.w == w); - } + inline bool operator== (const Quaternion& rhs) const + { + return (rhs.x == x) && (rhs.y == y) && + (rhs.z == z) && (rhs.w == w); + } - inline bool operator!= (const Quaternion& rhs) const - { - return (rhs.x != x) && (rhs.y != y) && - (rhs.z != z) && (rhs.w != w); - } + inline bool operator!= (const Quaternion& rhs) const + { + return (rhs.x != x) && (rhs.y != y) && + (rhs.z != z) && (rhs.w != w); + } - static Quaternion Squad(Number fT, const Quaternion& rkP, const Quaternion& rkA, const Quaternion& rkB, const Quaternion& rkQ, bool shortestPath); - Quaternion Inverse () const; - - Quaternion operator- () const - { - return Quaternion(-w,-x,-y,-z); - } + static Quaternion Squad(Number fT, const Quaternion& rkP, const Quaternion& rkA, const Quaternion& rkB, const Quaternion& rkQ, bool shortestPath); + Quaternion Inverse() const; + Quaternion operator- () const + { + return Quaternion(-w,-x,-y,-z); + } + void set(Number w, Number x, Number y, Number z) { this->w = w; this->x = x; @@ -191,77 +192,82 @@ namespace Polycode { this->z = z; } - Quaternion inverse() { + Quaternion Inverse() { Number fNorm = w*w+x*x+y*y+z*z; Number fInvNorm = 1.0/fNorm; return Quaternion(w*fInvNorm,-x*fInvNorm,-y*fInvNorm,-z*fInvNorm); } - Number InvSqrt(Number x){ - Number xhalf = 0.5f * x; - int i = *(int*)&x; // store Numbering-point bits in integer - i = 0x5f3759d5 - (i >> 1); // initial guess for Newton's method - x = *(Number*)&i; // convert new bits into Number - x = x*(1.5f - xhalf*x*x); // One round of Newton's method - return x; -} - - inline void fromAxes(Number az, Number ay, Number ax) { - ax *= TORADIANS; - ay *= TORADIANS; - az *= TORADIANS; - - Number c1 = cos(ay / 2.0f); - Number c2 = cos(ax / 2.0f); - Number c3 = cos(az / 2.0f); - - Number s1 = sin(ay / 2.0f); - Number s2 = sin(ax / 2.0f); - Number s3 = sin(az / 2.0f); - - w = (c1*c2*c3) - (s1*s2*s3); - x = (s1*s2*c3) + (c1*c2*s3); - y = (s1*c2*c3) + (c1*s2*s3); - z = (c1*s2*c3) - (s1*c2*s3); - } - + Number InvSqrt(Number x) { + Number xhalf = 0.5f * x; + int i = *(int*)&x; // store Numbering-point bits in integer + i = 0x5f3759d5 - (i >> 1); // initial guess for Newton's method + x = *(Number*)&i; // convert new bits into Number + x = x*(1.5f - xhalf*x*x); // One round of Newton's method + return x; + } + + inline void fromAxes(Number az, Number ay, Number ax) { + ax *= TORADIANS; + ay *= TORADIANS; + az *= TORADIANS; + + Number c1 = cos(ay / 2.0f); + Number c2 = cos(ax / 2.0f); + Number c3 = cos(az / 2.0f); + + Number s1 = sin(ay / 2.0f); + Number s2 = sin(ax / 2.0f); + Number s3 = sin(az / 2.0f); + + w = (c1*c2*c3) - (s1*s2*s3); + x = (s1*s2*c3) + (c1*c2*s3); + y = (s1*c2*c3) + (c1*s2*s3); + z = (c1*s2*c3) - (s1*c2*s3); + } - void FromAngleAxis (const Number& rfAngle, - const Vector3& rkAxis) - { - Number fHalfAngle ( 0.5*rfAngle ); - Number fSin = sin(fHalfAngle); - w = cos(fHalfAngle); - x = fSin*rkAxis.x; - y = fSin*rkAxis.y; - z = fSin*rkAxis.z; - } - //----------------------------------------------------------------------- - void ToAngleAxis (Number& rfAngle, Vector3& rkAxis) - { - // The quaternion representing the rotation is - // q = cos(A/2)+sin(A/2)*(x*i+y*j+z*k) + + void fromAngleAxis(const Number& rfAngle, + const Vector3& rkAxis) + { + Number fHalfAngle ( 0.5*rfAngle ); + Number fSin = sin(fHalfAngle); + w = cos(fHalfAngle); + x = fSin*rkAxis.x; + y = fSin*rkAxis.y; + z = fSin*rkAxis.z; + } - Number fSqrLength = x*x+y*y+z*z; - if ( fSqrLength > 0.0 ) - { - rfAngle = 2.0*acos(w); - Number fInvLength = InvSqrt(fSqrLength); - rkAxis.x = x*fInvLength; - rkAxis.y = y*fInvLength; - rkAxis.z = z*fInvLength; - } - else - { - // angle is 0 (mod 2*pi), so any axis will do - rfAngle = Number(0.0); - rkAxis.x = 1.0; - rkAxis.y = 0.0; - rkAxis.z = 0.0; - } - } - + 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) )); + } + + //----------------------------------------------------------------------- + void toAngleAxis (Number& rfAngle, Vector3& rkAxis) + { + // The quaternion representing the rotation is + // q = cos(A/2)+sin(A/2)*(x*i+y*j+z*k) + + Number fSqrLength = x*x+y*y+z*z; + if ( fSqrLength > 0.0 ) + { + rfAngle = 2.0*acos(w); + Number fInvLength = InvSqrt(fSqrLength); + rkAxis.x = x*fInvLength; + rkAxis.y = y*fInvLength; + rkAxis.z = z*fInvLength; + } + else + { + // angle is 0 (mod 2*pi), so any axis will do + rfAngle = Number(0.0); + rkAxis.x = 1.0; + rkAxis.y = 0.0; + rkAxis.z = 0.0; + } + } + void createFromAxisAngle(Number x, Number y, Number z, Number degrees); Matrix4 createMatrix() const; 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/PolyRectangle.h b/Core/Contents/Include/PolyRectangle.h index 3f044f9f7..8997a6e94 100755 --- a/Core/Contents/Include/PolyRectangle.h +++ b/Core/Contents/Include/PolyRectangle.h @@ -45,6 +45,37 @@ namespace Polycode { */ void setRect(Number x, Number y, Number w, Number h); + /** + * Return a Rectangle formed by clipping this rectangle to the + * bounds of the passed rectangle. + */ + Rectangle Clipped(const Rectangle& rect) const; + + /** + * Return the minimum X coordinate (the left edge). + */ + Number minX() const { return x; } + + /** + * Return the maximum X coordinate (the right edge). + */ + Number maxX() const { return x + w; } + + /** + * Return the minimum Y coordinate (the top edge in a Y-down coordinate + * system). + */ + Number minY() const { return y; } + + /** + * Return the maximum Y coordinate (the bottom edge in a Y-down coordinate + * system). + */ + Number maxY() const { return y + h; } + + bool operator==(const Rectangle& rect) const; + bool operator!=(const Rectangle& rect) const { return !(*this == rect); } + /** * X position */ 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 234736a93..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,49 +66,60 @@ 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; } }; /** - * Main renderer. 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. + * Provides low-level settings for the main renderer. + * + * The methods and settings in this class are closely related to OpenGL. + * If you have trouble understanding anything in this class, it is thus suggested to brush up on your OpenGL knowledge. + * + * 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. + * */ class _PolyExport Renderer : public PolyBase { public: Renderer(); virtual ~Renderer(); + virtual bool Init(); + 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 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; @@ -116,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; @@ -150,33 +160,49 @@ 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); - - virtual void setClippingPlanes(Number nearPlane_, Number farPlane_) = 0; - + + /** + * Set the frustum clipping planes. + * + * Please check the supplied external links for more information + * about the problems of a high farPlane/nearPlane setting. + * + * @see http://www.opengl.org/sdk/docs/man2/xhtml/glFrustum.xml + */ + 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. + * + * If alpha tests are enabled, drawn pixels of textures will + * be "mixed" with framebuffer pixels based on the drawn pixel's + * alpha value. If alpha tests are disabled, they will be drawn + * as solid color. + * + * @param val Whether to enable or disable alpha tests. + */ virtual void enableAlphaTest(bool val) = 0; virtual void clearBuffer(bool colorBuffer, bool depthBuffer) = 0; @@ -184,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; @@ -204,39 +229,35 @@ 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) = 0; + virtual Vector2 Project(const Matrix4 &cameraMatrix, const Matrix4 &projectionMatrix, const Polycode::Rectangle &viewport, const Vector3 &coordiante) const = 0; void enableShaders(bool flag); Number getViewportWidth(); Number getViewportHeight(); + void setViewportShift(Number shiftX, Number shiftY); + void *getDataPointerForName(const String &name); void setRendererShaderParams(Shader *shader, ShaderBinding *binding); - - virtual void initOSSpecific() {}; void addShaderModule(PolycodeShaderModule *module); - - virtual bool test2DCoordinateInPolygon(Number x, Number y, 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; @@ -248,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; @@ -260,62 +279,88 @@ 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; - - protected: - + + 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; Number viewportHeight; + + Vector2 viewportShift; bool cullingFrontFaces; Texture *currentTexture; Material *currentMaterial; -// vector shadowMapTextures; - - Texture *currentFrameBufferTexture; - Texture *previousFrameBufferTexture; - 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 85065f39e..69596fa59 100755 --- a/Core/Contents/Include/PolyResource.h +++ b/Core/Contents/Include/PolyResource.h @@ -24,13 +24,14 @@ THE SOFTWARE. #pragma once #include "PolyString.h" #include "PolyGlobals.h" +#include "PolyEventDispatcher.h" namespace Polycode { /** * Base class for resources. All resources that are managed by the ResourceManager subclass this. */ - class _PolyExport Resource : public PolyBase { + class _PolyExport Resource : public EventDispatcher { public: // ---------------------------------------------------------------------------------------------------------------- @@ -42,6 +43,7 @@ namespace Polycode { Resource(int type); virtual ~Resource(); + virtual void reloadResource(); const String& getResourceName() const; int getResourceType() const; @@ -54,11 +56,20 @@ namespace Polycode { static const int RESOURCE_SHADER = 2; static const int RESOURCE_PROGRAM = 3; static const int RESOURCE_MESH = 5; - static const int RESOURCE_CUBEMAP = 6; + static const int RESOURCE_CUBEMAP = 6; + static const int RESOURCE_SPRITE = 7; + static const int RESOURCE_ENTITY_INSTANCE = 8; + bool reloadOnFileModify; + + static bool defaultReloadOnFileModify; + + time_t resourceFileTime; + //@} protected: + int type; String resourcePath; diff --git a/Core/Contents/Include/PolyResourceManager.h b/Core/Contents/Include/PolyResourceManager.h index 9327b54d4..0fb5b6e4c 100755 --- a/Core/Contents/Include/PolyResourceManager.h +++ b/Core/Contents/Include/PolyResourceManager.h @@ -23,28 +23,66 @@ THE SOFTWARE. #pragma once #include "PolyGlobals.h" +#include "PolyEventDispatcher.h" #include +#define RESOURCE_CHECK_INTERVAL 2000 + 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); - + /** * Loads resources from a directory. * @param dirPath Path to directory to load resources from. @@ -63,33 +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; - - /** - * 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 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); + void handleEvent(Event *event); private: - 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 1d8416630..e22baf8d9 100644 --- a/Core/Contents/Include/PolySDLCore.h +++ b/Core/Contents/Include/PolySDLCore.h @@ -43,13 +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(); - void setVideoMode(int xRes, int yRes, bool fullScreen, bool vSync, int aaLevel, int anisotropyLevel); + bool systemUpdate(); + void Render(); + void setVideoMode(int xRes, int yRes, bool fullScreen, bool vSync, int aaLevel, int anisotropyLevel, bool retinaSupport = true); void createThread(Threaded *target); std::vector getVideoModes(); @@ -66,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 098ddbd6d..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,23 +128,14 @@ namespace Polycode { bool isEnabled(); void setEnabled(bool enabled); - int getNumEntities() { return 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); - static String readString(OSFILE *inFile); - void loadScene(const String& fileName); - void generateLightmaps(Number lightMapRes, Number lightMapQuality, int numRadPasses); + void handleEvent(Event *event); + + Ray projectRayFromCameraAndViewportCoordinate(Camera *camera, Vector2 coordinate); /** * Adds a light to the scene. @@ -148,41 +147,28 @@ 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 writeEntityMatrix(SceneEntity *entity, OSFILE *outFile); - void writeString(const String& str, OSFILE *outFile); - void saveScene(const String& fileName); - - int getNumStaticGeometry(); - SceneMesh *getStaticGeometry(int index); - - virtual void loadCollisionChild(SceneEntity *entity, bool autoCollide=false, int type=0){} - + void removeLight(SceneLight *light); + int getNumLights(); SceneLight *getLight(int index); - SceneEntity *getCustomEntityByType(const String& type) const; - std::vector getCustomEntitiesByType(const String& type) const; - - 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 */ 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 */ @@ -203,21 +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; - std::vector staticGeometry; - std::vector collisionGeometry; - std::vector customEntities; - - + Renderer *renderer; + std::vector lights; bool isSceneVirtual; Camera *defaultCamera; Camera *activeCamera; - std::vector entities; + + Material *overrideMaterial; + + Core *core; bool lightingEnabled; bool fogEnabled; @@ -226,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 c3eed145d..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,12 +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(); + bool positionAtBaseline; + + 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 81f024153..75547785e 100755 --- a/Core/Contents/Include/PolySceneLine.h +++ b/Core/Contents/Include/PolySceneLine.h @@ -23,42 +23,117 @@ THE SOFTWARE. #pragma once #include "PolyString.h" #include "PolyGlobals.h" -#include "PolySceneEntity.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 SceneEntity { + class _PolyExport SceneLine : public SceneMesh { public: + + + /** + * 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 entities. - * @param ent1 Starting entity. - * @param ent2 Ending entity. - */ - SceneLine(SceneEntity *ent1, SceneEntity *ent2); + * Constructs the line with two taraget positions. + * @param start Starting position. + * @param end Ending position. + */ SceneLine(Vector3 start, Vector3 end); + + /** + * Constructs the line with two taraget positions. + * @param start Starting position. + * @param end Ending position. + */ + static SceneLine *SceneLineWithPositions(Vector3 start, Vector3 end); + virtual ~SceneLine(); void setStart(Vector3 start); void setEnd(Vector3 end); - - void Render(); - Number lineWidth; - bool lineSmooth; + void Update(); + protected: - Mesh *mesh; + void initLine(); 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 20e1c3139..0f934508d 100755 --- a/Core/Contents/Include/PolySceneManager.h +++ b/Core/Contents/Include/PolySceneManager.h @@ -29,18 +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 UpdateVirtual(); - void removeScene(Scene *scene); + void fixedUpdate(); + void Render(); + void renderVirtual(); void registerRenderTexture(SceneRenderTexture *renderTexture); void unregisterRenderTexture(SceneRenderTexture *renderTexture); + void setRenderer(Renderer *renderer); private: @@ -48,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 2f0c0c5a5..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,39 +33,50 @@ 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. Possible values are: Mesh::QUAD_MESH, Mesh::TRI_MESH, Mesh::TRIFAN_MESH, Mesh::TRISTRIP_MESH, Mesh::LINE_MESH, Mesh::POINT_MESH. + * @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); /** - * Static wrapper for Lua - */ + * Construct scene mesh from an existing Mesh instance. + */ static SceneMesh *SceneMeshFromMesh(Mesh *mesh); + /** + * Construct an empty scene mesh with the specified type. + * @param meshType Mesh type to create. See Mesh for possible values. + */ + static SceneMesh *SceneMeshWithType(int meshType); + virtual ~SceneMesh(); void Render(); + /** + * Returns the local material binding options for this mesh. + */ ShaderBinding *getLocalShaderOptions(); /** @@ -74,7 +87,7 @@ namespace Polycode { /** * Returns the texture applied. */ - Texture *getTexture(); + Texture *getTexture() const; /** * Returns the material applied. @@ -86,13 +99,15 @@ namespace Polycode { * @param fileName Filename to load the mesh from. * @param clamp If true, clamps the texture to edges. See Texture for details on that. */ - void loadTexture(const String& fileName, bool clamp=true); + 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. @@ -115,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. @@ -140,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; @@ -158,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 eb93380e7..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. @@ -44,17 +46,20 @@ namespace Polycode { */ SceneRenderTexture(Scene *targetScene, Camera *targetCamera, int renderWidth,int renderHeight, bool floatingPoint = false); virtual ~SceneRenderTexture(); - - void drawScreen(); - + /** * Returns the actual render texture. */ Texture *getTargetTexture(); Texture *getFilterColorBufferTexture(); - Texture *getFilterZBufferTexture(); + Texture *getFilterZBufferTexture(); + + void Render(); + + Image *saveToImage(); + void resizeRenderTexture(int newWidth, int newHeight); /** * Returns the target scene. */ @@ -64,12 +69,18 @@ namespace Polycode { * Returns the target camera. */ Camera *getTargetCamera(); - + + bool enabled; + protected: + + Renderer *renderer; Texture *filterColorBufferTexture; Texture *filterZBufferTexture; + bool floatingPoint; + Texture *depthTexture; Texture *targetTexture; Scene *targetScene; 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/PolyScreenCurve.h b/Core/Contents/Include/PolyScreenCurve.h deleted file mode 100755 index d3f621dea..000000000 --- a/Core/Contents/Include/PolyScreenCurve.h +++ /dev/null @@ -1,41 +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 BezierCurve; - - class _PolyExport ScreenCurve : public ScreenShape { - public: - ScreenCurve(BezierCurve *curve, int numVertices); - virtual ~ScreenCurve(); - - protected: - - Number numVertices; - BezierCurve *curve; - }; -} diff --git a/Core/Contents/Include/PolyScreenEntity.h b/Core/Contents/Include/PolyScreenEntity.h deleted file mode 100755 index 476e874fb..000000000 --- a/Core/Contents/Include/PolyScreenEntity.h +++ /dev/null @@ -1,235 +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); - virtual void applyClone(Entity *clone, bool deepClone, bool ignoreEditorOnly); - - 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, Vector2 parentAdjust = Vector2(0,0)); - MouseEventResult _onMouseUp(Number x, Number y, int mouseButton, int timestamp, Vector2 parentAdjust = Vector2(0,0)); - MouseEventResult _onMouseMove(Number x, Number y, int timestamp, Vector2 parentAdjust = Vector2(0,0)); - MouseEventResult _onMouseWheelUp(Number x, Number y, int timestamp, Vector2 parentAdjust = Vector2(0,0)); - MouseEventResult _onMouseWheelDown(Number x, Number y, int timestamp, Vector2 parentAdjust = Vector2(0,0)); - - 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(); - - virtual void onKeyDown(PolyKEY key, wchar_t charCode){} - virtual void onKeyUp(PolyKEY key, wchar_t charCode){} - - bool hitTest(Number x, Number y); - - 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 cornder.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); - - int getPositionMode(); - - void setDragLimits(Rectangle rect); - void clearDragLimits(); - - void setDefaultScreenOptions(bool snapToPixels); - - void focusChild(ScreenEntity *child); - void focusNextChild(); - - void moveChildUp(ScreenEntity *child); - void moveChildDown(ScreenEntity *child); - void moveChildTop(ScreenEntity *child); - void moveChildBottom(ScreenEntity *child); - - Vector2 getPosition2D() const; - Vector2 getScreenPosition() const; - - Vector2 getScale2D() const; - - static const int POSITION_TOPLEFT = 0; - static const int POSITION_CENTER = 1; - - bool isFocusable() const; - - bool hasFocus; - - - /** - * 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; - - Rectangle getHitbox(); - void setHitbox(Number width, Number height); - void setHitbox(Number width, Number height, Number left, Number top); - - Number width; - Number height; - - protected: - - bool focusable; - bool focusChildren; - - bool isDragged; - 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 89513aef3..000000000 --- a/Core/Contents/Include/PolyScreenEntityInstance.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" -#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 "PolySound.h" - -namespace Polycode { - -class ScreenEntityInstance : public ScreenEntity { - public: - ScreenEntityInstance(const String& fileName); - ScreenEntityInstance(); - - ~ScreenEntityInstance(); - - virtual Entity *Clone(bool deepClone, bool ignoreEditorOnly); - virtual void applyClone(Entity *clone, bool deepClone, bool ignoreEditorOnly); - - void parseObjectIntoCurve(ObjectEntry *entry, BezierCurve *curve); - void applyScreenShape(ObjectEntry *entry, ScreenShape *shape); - ScreenEntity *loadObjectEntryIntoEntity(ObjectEntry *entry); - bool loadFromFile(const String& fileName); - - ScreenEntity *getRootEntity(); - - String getFileName() const; - - bool cloneUsingReload; - - String fileName; - - ScreenEntity *rootEntity; -}; - -} 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 9c3477943..000000000 --- a/Core/Contents/Include/PolyScreenImage.h +++ /dev/null @@ -1,84 +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 image Texture to create from. - */ - ScreenImage(Texture *texture); - - - virtual ~ScreenImage(); - - virtual Entity *Clone(bool deepClone, bool ignoreEditorOnly); - virtual void applyClone(Entity *clone, bool deepClone, bool ignoreEditorOnly); - - /** - * 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 7c724ac24..000000000 --- a/Core/Contents/Include/PolyScreenLabel.h +++ /dev/null @@ -1,85 +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); - virtual void applyClone(Entity *clone, bool deepClone, bool ignoreEditorOnly); - - /** - * Adds a drop shadow to the label. - * @param color Color of the drop shadow. - * @param size Size of the drop shadow in pixels. - * @param offsetX Horizontal offset of the drop shadow. - * @param offsetY Vertical offset of the drop shadow. - */ - void addDropShadow(Color color, Number size, Number offsetX, Number offsetY); - - - /** - * 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; - ScreenImage *dropShadowImage; - }; -} diff --git a/Core/Contents/Include/PolyScreenLine.h b/Core/Contents/Include/PolyScreenLine.h deleted file mode 100755 index 7e3e330a4..000000000 --- a/Core/Contents/Include/PolyScreenLine.h +++ /dev/null @@ -1,78 +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); - - 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 60fd29c3c..000000000 --- a/Core/Contents/Include/PolyScreenManager.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 "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 handleEvent(Event *event); - - private: - - std::vector screens; - - }; - -} diff --git a/Core/Contents/Include/PolyScreenMesh.h b/Core/Contents/Include/PolyScreenMesh.h deleted file mode 100755 index 9ac851112..000000000 --- a/Core/Contents/Include/PolyScreenMesh.h +++ /dev/null @@ -1,114 +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" - -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); - - // Static constructor wrappers for bindings - static ScreenMesh *ScreenMeshWithType(int meshType); - 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); - - /** - * 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: - - Mesh *mesh; - Texture *texture; - }; -} diff --git a/Core/Contents/Include/PolyScreenShape.h b/Core/Contents/Include/PolyScreenShape.h deleted file mode 100755 index 1c7df60ac..000000000 --- a/Core/Contents/Include/PolyScreenShape.h +++ /dev/null @@ -1,140 +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. Pass ScreenShape::SHAPE_CUSTOM if you want to create a custom shape (@see addShapePoint()) - * @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); - virtual void applyClone(Entity *clone, bool deepClone, bool ignoreEditorOnly); - - 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(); - - /** - * Adds a point to the mesh. - * @param x Horizontal position of the point. - * @param y Vertical position of the point. - */ - void addShapePoint(Number x, Number y); - - /** - * 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; - - Polygon *customShapePoly; - int shapeType; - - }; -} diff --git a/Core/Contents/Include/PolyScreenSound.h b/Core/Contents/Include/PolyScreenSound.h deleted file mode 100644 index b91694c68..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); - virtual void applyClone(Entity *clone, bool deepClone, bool ignoreEditorOnly); - - 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 2d7abd14e..000000000 --- a/Core/Contents/Include/PolyScreenSprite.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 "PolyScreenShape.h" -#include - -namespace Polycode { - -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); - virtual ~ScreenSprite(); - - virtual Entity *Clone(bool deepClone, bool ignoreEditorOnly); - virtual void applyClone(Entity *clone, bool deepClone, bool ignoreEditorOnly); - - /** - * 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); - - /** - * 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(); - - void updateSprite(); - - protected: - - String fileName; - - bool paused; - - Number spriteWidth; - Number spriteHeight; - - bool playingOnce; - Number lastTick; - - Number spriteUVWidth; - Number spriteUVHeight; - int currentFrame; - SpriteAnimation *currentAnimation; - - std::vector animations; -}; - -} diff --git a/Core/Contents/Include/PolyServer.h b/Core/Contents/Include/PolyServer.h index 242acc3c9..49bc4f61b 100755 --- a/Core/Contents/Include/PolyServer.h +++ b/Core/Contents/Include/PolyServer.h @@ -41,7 +41,10 @@ namespace Polycode { unsigned int dataSize; unsigned short dataType; - static const int EVENT_CLIENT_DATA = 0; + ServerClient *client; + + static const int EVENTBASE_SERVERCLIENTEVENT = 0x780; + static const int EVENT_CLIENT_DATA = EVENTBASE_SERVERCLIENTEVENT+0; }; class _PolyExport ServerClient : public EventDispatcher { @@ -49,10 +52,11 @@ namespace Polycode { ServerClient(); ~ServerClient(); - void handlePacket(Packet *packet); + ServerClientEvent *handlePacket(Packet *packet); - unsigned int clientID; + unsigned int clientID; PeerConnection *connection; + bool clientReady; }; class _PolyExport ServerEvent : public Event { @@ -62,25 +66,62 @@ namespace Polycode { ServerClient *client; - static const int EVENT_CLIENT_CONNECTED = 0; - static const int EVENT_CLIENT_DATA = 1; - static const int EVENT_CLIENT_DISCONNECTED = 2; + char *data; + unsigned int dataSize; + unsigned short dataType; + + static const int EVENTBASE_SERVEREVENT = 0x700; + static const int EVENT_CLIENT_CONNECTED = EVENTBASE_SERVEREVENT+0; + static const int EVENT_CLIENT_DATA = EVENTBASE_SERVEREVENT+1; + static const int EVENT_CLIENT_DISCONNECTED = EVENTBASE_SERVEREVENT+2; + // Notice also the SERVERCLIENTEVENT above, which starts with 0x780. }; + /** + * A network server, accepting incoming connections and keeping track of connected clients. + * + * As the Peer class already provides all connectivity functionality required, the Server class + * merely provides another abstraction layer to treat clients separately from mere connections. + * + * The Server will send the data provided by the ServerWorld class to all clients at defined rate and dispatch events when clients connect, disconnect and send data. + */ class _PolyExport Server : public Peer { public: + /** + * Constructor. + * @param port The local port to listen for client connections on. + * @param rate How many times per second to send out server data to clients. + * @param world An instance of the server data provider. @see ServerWorld + */ Server(unsigned int port, unsigned int rate, ServerWorld *world = NULL); ~Server(); - void DisconnectClient(ServerClient *client); - void handleEvent(Event *event); + void handlePacket(Packet *packet, PeerConnection *connection); + void handleEvent(Event *event); void handlePeerConnection(PeerConnection *connection); + void DisconnectClient(ServerClient *client); + + /** + * Get a connected client from its associated peer connection, if any. + * @param connection The PeerConnection through which we're communicating + * with the client to obtain. + * @return The connected client or NULL if doesn't exist. + */ ServerClient *getConnectedClient(PeerConnection *connection); + int getNumServerClients(); + + ServerClient *getServerClient(int index); + + /** + * @see Peer::sendReliableData + */ void sendReliableDataToClient(ServerClient *client, char *data, unsigned int size, unsigned short type); + + /** + * @see Peer::sendReliableDataToAll + */ void sendReliableDataToAllClients(char *data, unsigned int size, unsigned short type); - - void handlePacket(Packet *packet, PeerConnection *connection); protected: @@ -88,4 +129,4 @@ namespace Polycode { ServerWorld *world; vector clients; }; -} \ No newline at end of file +} diff --git a/Core/Contents/Include/PolyShader.h b/Core/Contents/Include/PolyShader.h index 2b4259e54..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 @@ -39,47 +40,76 @@ namespace Polycode { public: String name; - String typeString; - String valueString; - bool isAuto; - int autoID; - void *defaultData; - void *minValue; - void *maxValue; - int paramType; - + int type; + + static void *createParamData(int type); + static const int PARAM_UNKNOWN = 0; - static const int PARAM_Number = 1; - static const int PARAM_Vector2 = 2; - static const int PARAM_Vector3 = 3; - static const int PARAM_Color = 4; + static const int PARAM_NUMBER = 1; + static const int PARAM_VECTOR2 = 2; + 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: + explicit ShaderProgram(int type); + virtual ~ShaderProgram(); + + virtual void reloadProgram() {} + + static const int TYPE_VERT = 0; + static const int TYPE_FRAG = 1; + + int type; + + void reloadResource(); + + }; 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; + ShaderBinding *createBinding(); virtual void reload() {} - + + int getExpectedParamType(String name); + + virtual void setVertexProgram(ShaderProgram *vp) {} + virtual void setFragmentProgram(ShaderProgram *fp) {} + static const int FIXED_SHADER = 0; static const int MODULE_SHADER = 1; int numSpotLights; - int numAreaLights; + int numPointLights; std::vector expectedTextures; - std::vector expectedFragmentParams; - std::vector expectedVertexParams; + std::vector expectedCubemaps; + std::vector expectedParams; bool screenShader; + ShaderProgram *vp; + ShaderProgram *fp; + protected: @@ -89,31 +119,47 @@ namespace Polycode { class _PolyExport ShaderRenderTarget : public PolyBase { public: + ShaderRenderTarget(); + String id; Number width; Number height; int sizeMode; - bool hasSize; - Texture *texture; + Texture *texture; + Number normalizedWidth; + Number normalizedHeight; + static const int SIZE_MODE_PIXELS = 0; static const int SIZE_MODE_NORMALIZED = 1; }; 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) { memcpy(data, &x, sizeof(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 { @@ -122,28 +168,34 @@ namespace Polycode { String name; int mode; Texture *texture; - Number width; - Number height; static const int MODE_IN= 0; static const int MODE_OUT = 1; + static const int MODE_COLOR = 2; + static const int MODE_DEPTH = 3; }; class _PolyExport ShaderBinding : public PolyBase { public: ShaderBinding(Shader *shader); virtual ~ShaderBinding(); - - virtual Texture *getTexture(const String& name){ return NULL;}; - virtual void clearTexture(const String& name){}; - virtual void addTexture(const String& name, Texture *texture) {}; - virtual void addParam(const String& type, const String& name, const String& value) {}; - virtual void addCubemap(const String& name, Cubemap *cubemap) {}; - + + 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); + LocalShaderParam *addParamPointer(int type, const String& name, void *ptr); unsigned int getNumLocalParams(); LocalShaderParam *getLocalParam(unsigned int index); LocalShaderParam *getLocalParamByName(const String& name); void addRenderTargetBinding(RenderTargetBinding *binding); + void removeRenderTargetBinding(RenderTargetBinding *binding); unsigned int getNumRenderTargetBindings(); RenderTargetBinding *getRenderTargetBinding(unsigned int index); @@ -151,16 +203,25 @@ namespace Polycode { unsigned int getNumInTargetBindings(); RenderTargetBinding *getInTargetBinding(unsigned int index); + unsigned int getNumColorTargetBindings(); + RenderTargetBinding *getColorTargetBinding(unsigned int index); + + unsigned int getNumDepthTargetBindings(); + RenderTargetBinding *getDepthTargetBinding(unsigned int index); + 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; std::vector renderTargetBindings; std::vector inTargetBindings; std::vector outTargetBindings; + std::vector colorTargetBindings; + std::vector depthTargetBindings; }; } diff --git a/Core/Contents/Include/PolySkeleton.h b/Core/Contents/Include/PolySkeleton.h index 264129ac1..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: /** @@ -141,7 +143,16 @@ namespace Polycode { * @param fileName Skeleton file to load. */ Skeleton(const String& fileName); + + /** + * Construct a blank skeleton. + */ Skeleton(); + + /** + * Construct a blank skeleton. + */ + static Skeleton *BlankSkeleton(); /** * Loads a new skeleton from file. @@ -156,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. @@ -172,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(); /** @@ -185,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 */ @@ -204,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/PolySocket.h b/Core/Contents/Include/PolySocket.h index c57a16b39..4eaa23088 100755 --- a/Core/Contents/Include/PolySocket.h +++ b/Core/Contents/Include/PolySocket.h @@ -59,29 +59,87 @@ THE SOFTWARE. #endif namespace Polycode { - + + /** + * A typical network address, defined by IP and port. + */ class _PolyExport Address { public: + + /** + * Constructor. + * @param ipAsString An IP address represented as string, + * for example "127.0.0.1" + * @param port The UDP/TCP port of the address, given in + * host byte order. + */ Address(String ipAsString, unsigned int port); + + /** + * Constructor. + * @param ip An IP address given as integer in host byte + * order. + * @param port The UDP/TCP port of the address, given in + * host byte order. + */ Address(unsigned int ip, unsigned int port); + + /** + * Constructor, leaving the address uninitalized. + * + * Address IP and port will default to 0. + */ Address(); ~Address(); + /** + * Copy the address IP and port from add2 into this. + * @param add2 The address to copy into this. + */ inline void operator = (const Address &add2) { setAddress(add2.uintAddress, add2.port); } + /** + * @return 1 if the address IP and port match, 0 otherwise. + */ inline bool operator == ( const Address& add2) { return (uintAddress == add2.uintAddress && port == add2.port); } - + /** + * Update the address IP and port. + * @param ipAsString An IP address represented as string, + * for example "197.0.0.1" + * @param port The UDP/TCP port of the address, given in + * host byte order. + */ void setAddress(String ipAsString, unsigned int port); + + /** + * Update the address IP and port. + * @param ip An IP address given as integer in host byte + * order. + * @param port The UDP/TCP port of the address, given in + * host byte order. + */ void setAddress(unsigned int ip, unsigned int port); + // TODO: A way to get the IP/port without exposing internal members. + // The problem here is that the IP internally is converted to + // network byte order, but the API seems to work with host byte + // order, so I'm unsure in which byte order the IP/port should + // be returned. + + protected: unsigned int uintAddress; unsigned int port; - sockaddr_in sockAddress; + + sockaddr_in sockAddress; + + // This class already uses socket API structs internally, + // so it's no far stretch to make it tied to the Socket class. + friend class Socket; }; @@ -94,8 +152,9 @@ namespace Polycode { unsigned int dataSize; Address fromAddress; - static const int EVENT_ERROR = 0; - static const int EVENT_DATA_RECEIVED = 1; + static const int EVENTBASE_SOCKETEVENT = 0x500; + static const int EVENT_ERROR = EVENTBASE_SOCKETEVENT+0; + static const int EVENT_DATA_RECEIVED = EVENTBASE_SOCKETEVENT+1; }; diff --git a/Core/Contents/Include/PolySound.h b/Core/Contents/Include/PolySound.h index 383f5ddeb..a2ab3fa95 100755 --- a/Core/Contents/Include/PolySound.h +++ b/Core/Contents/Include/PolySound.h @@ -24,9 +24,15 @@ #include "PolyGlobals.h" #include "PolyVector3.h" #include "PolyString.h" +#include "PolyCoreServices.h" -#include "al.h" -#include "alc.h" +#if defined(__APPLE__) && defined(__MACH__) + #include + #include +#else + #include "al.h" + #include "alc.h" +#endif #define ALNoErrorStr "No AL error occurred" #define ALInvalidNameStr "AL error: a bad name (ID) was passed to an OpenAL function" @@ -37,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. */ @@ -52,11 +78,15 @@ 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(); /** * Play the sound once or in a loop. @@ -130,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(); @@ -142,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; @@ -159,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 6b89480dc..694382f4a 100755 --- a/Core/Contents/Include/PolySoundManager.h +++ b/Core/Contents/Include/PolySoundManager.h @@ -23,15 +23,22 @@ #pragma once #include "PolyGlobals.h" #include "PolyVector3.h" +#include "PolySound.h" -#include "al.h" -#include "alc.h" +#if defined(__APPLE__) && defined(__MACH__) + #include + #include +#else + #include "al.h" + #include "alc.h" +#endif namespace Polycode { /** * Controls global sound settings. */ + class _PolyExport SoundManager : public PolyBase{ public: SoundManager(); @@ -40,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 ab5cfe8c1..11d8ba96b 100644 --- a/Core/Contents/Include/PolyString.h +++ b/Core/Contents/Include/PolyString.h @@ -1,4 +1,4 @@ -/* + /* Copyright (C) 2011 by Ivan Safrin Permission is hereby granted, free of charge, to any person obtaining a copy @@ -174,7 +174,11 @@ namespace Polycode { * @param value Number to convert. * @return A string converted from the Number. */ - static String NumberToString(Number value); + 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 4464cd61c..2e216bc7a 100755 --- a/Core/Contents/Include/PolyTexture.h +++ b/Core/Contents/Include/PolyTexture.h @@ -37,6 +37,8 @@ namespace Polycode { Number scrollSpeedX; Number scrollSpeedY; + void reloadResource(); + virtual void setTextureData(char *data) = 0; virtual void recreateFromImageData() = 0; @@ -46,14 +48,14 @@ namespace Polycode { void setImageData(Image *data); - void updateScroll(int elapsed); - void setResourcePath(const String& newPath); - const String& getResourcePath() const; - + void updateScroll(int elapsed); char *getTextureData() const { return textureData;} int getWidth() const; int getHeight() const; + + void setCreateMipmaps(bool createMipmapsIn) { createMipmaps = createMipmapsIn; } + bool getCreateMipmaps() const { return createMipmaps; } bool clamp; char *textureData; @@ -66,7 +68,6 @@ namespace Polycode { bool createMipmaps; int width; int height; - String resourcePath; Number scrollOffsetX; Number scrollOffsetY; }; 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 b8d2525be..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); @@ -127,7 +134,7 @@ namespace Polycode { /** * Returns the dot product with another vector. - * @return Dor product with the vector. + * @return Dot product with the vector. */ inline Number dot(const Vector2 &u) const { return x * u.x + y * u.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 2af4dccc0..65c30ab5c 100644 --- a/Core/Contents/Include/PolyWinCore.h +++ b/Core/Contents/Include/PolyWinCore.h @@ -31,9 +31,9 @@ #include #include #include -#include +#include -#include +#include #include @@ -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,10 +120,12 @@ namespace Polycode { int mouseY; TouchInfo touch; std::vector touches; + int touchType; PolyKEY keyCode; wchar_t unicodeChar; char mouseButton; - static const int INPUT_EVENT = 0; + static const int EVENTBASE_PLATFORMEVENT = 0x300; + static const int INPUT_EVENT = EVENTBASE_PLATFORMEVENT+0; }; @@ -174,14 +180,15 @@ 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); void handleKeyDown(LPARAM lParam, WPARAM wParam, wchar_t unicodeChar); @@ -191,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); @@ -229,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(); @@ -236,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); @@ -246,9 +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; @@ -257,21 +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 f936179cb..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,36 +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 "PolyScreenCurve.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" @@ -80,22 +74,24 @@ #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 #include "PolyWinCore.h" -#endif \ No newline at end of file +#endif diff --git a/Core/Contents/Include/PolyiPhoneCore.h b/Core/Contents/Include/PolyiPhoneCore.h deleted file mode 100644 index b06861996..000000000 --- a/Core/Contents/Include/PolyiPhoneCore.h +++ /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. -*/ - -#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 INPUT_EVENT = 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 630b05545..d1f66e2f7 100644 --- a/Core/Contents/PolycodeView/MSVC/PolycodeView.cpp +++ b/Core/Contents/PolycodeView/MSVC/PolycodeView.cpp @@ -1,189 +1,217 @@ - -#include "PolycodeView.h" -#include "PolyWinCore.h" -#include "PolyCoreServices.h" -#include "PolyCoreInput.h" -#include "PolyRenderer.h" -#include -#include - -using namespace Polycode; - -Win32Core *core = NULL; - - -static void OpenConsole() -{ - int outHandle, errHandle, inHandle; - FILE *outFile, *errFile, *inFile; - AllocConsole(); - CONSOLE_SCREEN_BUFFER_INFO coninfo; - GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &coninfo); - coninfo.dwSize.Y = 9999; - SetConsoleScreenBufferSize(GetStdHandle(STD_OUTPUT_HANDLE), coninfo.dwSize); - - outHandle = _open_osfhandle((long)GetStdHandle(STD_OUTPUT_HANDLE), _O_TEXT); - errHandle = _open_osfhandle((long)GetStdHandle(STD_ERROR_HANDLE),_O_TEXT); - inHandle = _open_osfhandle((long)GetStdHandle(STD_INPUT_HANDLE),_O_TEXT ); - - outFile = _fdopen(outHandle, "w" ); - errFile = _fdopen(errHandle, "w"); - inFile = _fdopen(inHandle, "r"); - - *stdout = *outFile; - *stderr = *errFile; - *stdin = *inFile; - - setvbuf( stdout, NULL, _IONBF, 0 ); - setvbuf( stderr, NULL, _IONBF, 0 ); - setvbuf( stdin, NULL, _IONBF, 0 ); - - std::ios::sync_with_stdio(); - -} - -LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) -{ - int wmId, wmEvent; - PAINTSTRUCT ps; - HDC hdc; - int nWidth, nHeight; - bool useDefault = false; - - if(!core) - return DefWindowProc(hWnd, message, wParam, lParam); - - switch (message) - { - case WM_SIZE: - nWidth = LOWORD(lParam); - nHeight = HIWORD(lParam); - if(core) { - core->handleViewResize(nWidth, nHeight); - } - break; - - case WM_MOUSEMOVE: - if(core) - core->handleMouseMove(lParam,wParam); - break; - - case WM_MOUSEWHEEL: - if(core) - core->handleMouseWheel(lParam,wParam); - break; - - case WM_LBUTTONDOWN: - if(core) - core->handleMouseDown(CoreInput::MOUSE_BUTTON1, lParam,wParam); - break; - case WM_LBUTTONUP: - if(core) - core->handleMouseUp(CoreInput::MOUSE_BUTTON1, lParam,wParam); - break; - - case WM_RBUTTONDOWN: - if(core) - core->handleMouseDown(CoreInput::MOUSE_BUTTON2, lParam,wParam); - break; - case WM_RBUTTONUP: - if(core) - core->handleMouseUp(CoreInput::MOUSE_BUTTON2, lParam,wParam); - break; - - case WM_TOUCH: - if(core) { - if(core->isMultiTouchEnabled()) { - core->handleTouchEvent(lParam, wParam); - } - } - break; - - case WM_MBUTTONDOWN: - if(core) - core->handleMouseDown(CoreInput::MOUSE_BUTTON3, lParam,wParam); - break; - case WM_MBUTTONUP: - if(core) - core->handleMouseUp(CoreInput::MOUSE_BUTTON3, lParam,wParam); - break; - case WM_KEYDOWN: - if(core) { - wchar_t unicodeChar = 0; - MSG m; - m.hwnd = hWnd; - m.message = message; - m.wParam = wParam; - m.lParam = lParam; - m.time = 0; - if ( PeekMessage(&m, hWnd, 0, WM_USER, PM_NOREMOVE) && (m.message == WM_CHAR) ) { - GetMessage(&m, hWnd, 0, WM_USER); - unicodeChar = (wchar_t)m.wParam; - } - - core->handleKeyDown(lParam,wParam, unicodeChar); - } - break; - case WM_KEYUP: - if(core) - core->handleKeyUp(lParam,wParam); - break; - case WM_CLOSE: - if(core) - core->Shutdown(); - useDefault = true; - break; - case WM_DESTROY: - PostQuitMessage(0); - break; - default: - useDefault = true; - break; - } - - if (useDefault) - return DefWindowProc(hWnd, message, wParam, lParam); - else - return 0; -} - - -PolycodeView::PolycodeView(HINSTANCE hInstance, int nCmdShow, LPCTSTR windowTitle, bool resizable, bool showDebugConsole) : PolycodeViewBase() { - -WNDCLASSEX wcex; - - wcex.cbSize = sizeof(WNDCLASSEX); - wcex.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC; - wcex.lpfnWndProc = WndProc; - 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"POLYCODEAPPLICATION"; - wcex.hIconSm = LoadIcon(hInstance, IDI_APPLICATION); - - RegisterClassEx(&wcex); - - if(resizable) { - 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); - } - - windowData = (void*)&hwnd; - - ShowWindow(hwnd, nCmdShow); - UpdateWindow(hwnd); - - if(showDebugConsole) { - OpenConsole(); - } - -} - -PolycodeView::~PolycodeView() { - + +#include "PolycodeView.h" +#include "PolyWinCore.h" +#include "PolyCoreServices.h" +#include "PolyCoreInput.h" +#include "PolyRenderer.h" +#include +#include +#include + +using namespace Polycode; + +Win32Core *core = NULL; + + +static void OpenConsole() +{ + int outHandle, errHandle, inHandle; + FILE *outFile, *errFile, *inFile; + AllocConsole(); + CONSOLE_SCREEN_BUFFER_INFO coninfo; + GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &coninfo); + coninfo.dwSize.Y = 9999; + SetConsoleScreenBufferSize(GetStdHandle(STD_OUTPUT_HANDLE), coninfo.dwSize); + + outHandle = _open_osfhandle((long)GetStdHandle(STD_OUTPUT_HANDLE), _O_TEXT); + errHandle = _open_osfhandle((long)GetStdHandle(STD_ERROR_HANDLE),_O_TEXT); + inHandle = _open_osfhandle((long)GetStdHandle(STD_INPUT_HANDLE),_O_TEXT ); + + outFile = _fdopen(outHandle, "w" ); + errFile = _fdopen(errHandle, "w"); + inFile = _fdopen(inHandle, "r"); + + *stdout = *outFile; + *stderr = *errFile; + *stdin = *inFile; + + setvbuf( stdout, NULL, _IONBF, 0 ); + setvbuf( stderr, NULL, _IONBF, 0 ); + setvbuf( stdin, NULL, _IONBF, 0 ); + + std::ios::sync_with_stdio(); + +} + +LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + int nWidth, nHeight; + bool useDefault = false; + + if(!core) + return DefWindowProc(hWnd, message, wParam, lParam); + + switch (message) + { + case WM_COPYDATA: + { + COPYDATASTRUCT *cp = (COPYDATASTRUCT*)lParam; + wchar_t *stringData = (wchar_t*)cp->lpData; + core->copyDataString = String(stringData); + core->hasCopyDataString = true; + } + break; + case WM_SIZE: + nWidth = LOWORD(lParam); + nHeight = HIWORD(lParam); + if(core) { + core->handleViewResize(nWidth, nHeight); + } + break; + + case WM_MOUSEMOVE: + if(core) + core->handleMouseMove(lParam,wParam); + break; + + case WM_MOUSEWHEEL: + if(core) + core->handleMouseWheel(lParam,wParam); + break; + + case WM_LBUTTONDOWN: + if(core) + core->handleMouseDown(CoreInput::MOUSE_BUTTON1, lParam,wParam); + break; + case WM_LBUTTONUP: + if(core) + core->handleMouseUp(CoreInput::MOUSE_BUTTON1, lParam,wParam); + break; + + case WM_RBUTTONDOWN: + if(core) + core->handleMouseDown(CoreInput::MOUSE_BUTTON2, lParam,wParam); + break; + case WM_RBUTTONUP: + if(core) + core->handleMouseUp(CoreInput::MOUSE_BUTTON2, lParam,wParam); + break; + +#ifndef NO_TOUCH_API + #ifdef NO_PEN_API + case WM_TOUCH: + if(core) { + if(core->isMultiTouchEnabled()) { + core->handleTouchEvent(lParam, wParam); + } + } + 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); + break; + case WM_MBUTTONUP: + if(core) + core->handleMouseUp(CoreInput::MOUSE_BUTTON3, lParam,wParam); + break; + case WM_KEYDOWN: + case WM_SYSKEYDOWN: + if(core) { + wchar_t unicodeChar = 0; + MSG m; + m.hwnd = hWnd; + m.message = message; + m.wParam = wParam; + m.lParam = lParam; + m.time = 0; + if ( PeekMessage(&m, hWnd, 0, WM_USER, PM_NOREMOVE) && (m.message == WM_CHAR) ) { + GetMessage(&m, hWnd, 0, WM_USER); + unicodeChar = (wchar_t)m.wParam; + } + + core->handleKeyDown(lParam,wParam, unicodeChar); + } + break; + case WM_KEYUP: + case WM_SYSKEYUP: + if(core) + core->handleKeyUp(lParam,wParam); + break; + case WM_CLOSE: + if(core) + core->Shutdown(); + useDefault = true; + break; + case WM_DESTROY: + PostQuitMessage(0); + break; + default: + useDefault = true; + break; + } + + if (useDefault) + return DefWindowProc(hWnd, message, wParam, lParam); + else + return 0; +} + + +PolycodeView::PolycodeView(HINSTANCE hInstance, int nCmdShow, LPCTSTR windowTitle, bool resizable, bool showDebugConsole) : PolycodeViewBase() { + + /* + 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; + wcex.lpfnWndProc = WndProc; + 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"POLYCODEAPPLICATION"; + wcex.hIconSm = LoadIcon(hInstance, IDI_APPLICATION); + + 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); + } else { + 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; + + ShowWindow(hwnd, nCmdShow); + UpdateWindow(hwnd); + + if(showDebugConsole) { + OpenConsole(); + } + +} + +PolycodeView::~PolycodeView() { + } \ No newline at end of file diff --git a/Core/Contents/PolycodeView/Mac OS X/PolycodeView.mm b/Core/Contents/PolycodeView/Mac OS X/PolycodeView.mm index a3a9f6322..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 { @@ -527,7 +677,10 @@ - (void)flagsChanged:(NSEvent *)theEvent } else { newEvent.eventCode = InputEvent::EVENT_KEYUP; } - break; + break; + default: + // Don't care otherwise + break; } newEvent.unicodeChar = 0; @@ -549,7 +702,7 @@ - (void)keyDown: (NSEvent*) theEvent newEvent.keyCode = keymap[[theEvent keyCode]]; NSString *chars = [theEvent characters]; - unsigned int numChars = [chars length]; + NSUInteger numChars = [chars length]; // NSLog(@"CHARS: %@", [chars characterAtIndex:0]); if(numChars > 0) { @@ -569,14 +722,13 @@ - (void)keyUp: (NSEvent*) theEvent return; core->lockMutex(core->eventMutex); - NSPoint mouseLoc = [self convertPoint:[theEvent locationInWindow] fromView:self]; CocoaEvent newEvent; newEvent.eventGroup = CocoaEvent::INPUT_EVENT; newEvent.eventCode = InputEvent::EVENT_KEYUP; newEvent.keyCode = keymap[[theEvent keyCode]]; NSString *chars = [theEvent characters]; - unsigned int numChars = [chars length]; + NSUInteger numChars = [chars length]; if(numChars > 0) newEvent.unicodeChar = [chars characterAtIndex:0]; diff --git a/Core/Contents/Source/OSBasics.cpp b/Core/Contents/Source/OSBasics.cpp index e8427f14b..6bfdc4022 100755 --- a/Core/Contents/Source/OSBasics.cpp +++ b/Core/Contents/Source/OSBasics.cpp @@ -23,11 +23,12 @@ #include "OSBasics.h" #ifdef _WINDOWS #include - #include + #include #else #include #include #include + #include #endif #include @@ -47,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) { @@ -55,6 +57,7 @@ void ctow(WCHAR* Dest, const char* Source) Dest[i] = (WCHAR)Source[i]; ++i; } + Dest[i] = 0; } #endif @@ -62,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) { @@ -84,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; @@ -258,6 +263,23 @@ vector OSBasics::parsePhysFSFolder(const String& pathString, bool s return returnVector; } +bool OSBasics::fileExists(const Polycode::String& pathString) { + if(PHYSFS_exists(pathString.c_str())) { + return true; + } + +#ifdef _WINDOWS + WCHAR tmp[4096]; + memset(tmp, 0, sizeof(WCHAR)*4096); + ctow(tmp, pathString.c_str()); + + DWORD dwAttrib = GetFileAttributes(tmp); + return (dwAttrib != 0xFFFFFFFF); +#else + return (access(pathString.c_str(), F_OK) != -1); +#endif +} + vector OSBasics::parseFolder(const String& pathString, bool showHidden) { vector returnVector; @@ -293,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 ) { @@ -338,6 +357,47 @@ vector OSBasics::parseFolder(const String& pathString, bool showHid return returnVector; } +time_t OSBasics::getFileTime(const Polycode::String& pathString) { + + String realString; + if(PHYSFS_exists(pathString.c_str())) { + realString = String(PHYSFS_getRealDir(pathString.c_str())) + "/" + pathString; + } else { + realString = pathString; + } + +#ifdef _WINDOWS + WCHAR tmp[4096]; + memset(tmp, 0, sizeof(WCHAR)*4096); + ctow(tmp, pathString.c_str()); + HANDLE hFile = CreateFile(tmp, GENERIC_READ, FILE_SHARE_WRITE | FILE_SHARE_READ | FILE_SHARE_DELETE, + NULL, OPEN_EXISTING, 0, NULL); + + if(hFile == INVALID_HANDLE_VALUE) { + return 0; + } + + FILETIME lastModifyTime; + BOOL result = GetFileTime(hFile, NULL, NULL, &lastModifyTime); + if(!result) { + return 0; + } else { + ULARGE_INTEGER ull; + ull.LowPart = lastModifyTime.dwLowDateTime; + ull.HighPart = lastModifyTime.dwHighDateTime; + return ull.QuadPart / 10000000ULL - 11644473600ULL; + } +#else + struct stat statbuf; + int retVal = stat(realString.c_str(), &statbuf); + if (retVal == 0) { + return statbuf.st_mtime; + } else { + return 0; + } +#endif +} + void OSBasics::removeItem(const String& pathString) { #ifdef _WINDOWS String _tmp = pathString.replace("/", "\\"); diff --git a/Core/Contents/Source/PolyBezierCurve.cpp b/Core/Contents/Source/PolyBezierCurve.cpp index 831e0a9f2..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,33 +231,23 @@ void BezierCurve::removePoint(BezierPoint *point) { break; } } + distancesDirty = 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]; +Vector3 BezierCurve::getPointAt(Number a) { + + if (distancesDirty) { + recalculateDistances(); + } + + if(controlPoints.size() == 0) { + return Vector3(); + } -// 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; + if(controlPoints.size() == 1) { + return controlPoints[0]->p2; } - buffersDirty = false; -} - -Vector3 BezierCurve::getPointAt(Number a) { + if(a < 0) a = 0; if(a > 1) 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 70ae0c995..38750bcd6 100755 --- a/Core/Contents/Source/PolyCamera.cpp +++ b/Core/Contents/Source/PolyCamera.cpp @@ -33,20 +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; - fovSet = false; + _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]; } @@ -55,48 +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; - fovSet = true; +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::isSphereInFrustrum(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() { @@ -107,165 +160,193 @@ 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::buildFrustrumPlanes() { +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 isSphereInFrustrum(entity->getPosition(), entity->getBBoxRadius()); +Scene *Camera::getParentScene() const { + return parentScene; } void Camera::setParentScene(Scene *parentScene) { this->parentScene = parentScene; } -void Camera::setPostFilter(const String& shaderName) { - Material *shaderMaterial = (Material*) CoreServices::getInstance()->getResourceManager()->getResource(Resource::RESOURCE_MATERIAL, shaderName); +void Camera::setPostFilterByName(const String& materialName) { + Material *shaderMaterial = (Material*) CoreServices::getInstance()->getResourceManager()->getGlobalPool()->getResource(Resource::RESOURCE_MATERIAL, materialName); if(shaderMaterial) - createPostFilter(shaderMaterial); + setPostFilter(shaderMaterial); } void Camera::removePostFilter() { @@ -275,32 +356,22 @@ void Camera::removePostFilter() { } } -void Camera::createPostFilter(Material *shaderMaterial) { +void Camera::setPostFilter(Material *shaderMaterial) { if(!shaderMaterial) return; if(shaderMaterial->getNumShaders() == 0) return; this->filterShaderMaterial = shaderMaterial; - - // TODO: make it save the textures to resource manager and check if they there -// originalSceneTexture = CoreServices::getInstance()->getMaterialManager()->createNewTexture(CoreServices::getInstance()->getCore()->getXRes(), CoreServices::getInstance()->getCore()->getYRes()); -// zBufferSceneTexture = CoreServices::getInstance()->getMaterialManager()->createFramebufferTexture(CoreServices::getInstance()->getCore()->getXRes(), CoreServices::getInstance()->getCore()->getYRes(), 0); - 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(); - if(i == 0) { - binding->addTexture("screenColorBuffer", originalSceneTexture); - binding->addTexture("screenDepthBuffer", zBufferSceneTexture); - } localShaderOptions.push_back(binding); - binding->addLocalParam("exposure", (void*)&exposureLevel); + binding->addParamPointer(ProgramParam::PARAM_NUMBER, "exposure", (void*)&exposureLevel); } - _hasFilterShader = true; } @@ -309,96 +380,169 @@ 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) { - ShaderBinding* binding = localShaderOptions[0]; - binding->clearTexture("screenColorBuffer"); - binding->clearTexture("screenDepthBuffer"); - binding->addTexture("screenColorBuffer", targetColorTexture); - binding->addTexture("screenDepthBuffer", targetZTexture); - finalTargetColorTexture = targetColorTexture; - finalTargetZTexture = targetZTexture; - - CoreServices::getInstance()->getRenderer()->setViewportSize(targetTextureWidth, targetTextureHeight); + finalTargetZTexture = targetZTexture; + renderer->setViewportSize(targetTextureWidth, targetTextureHeight); } else { - - ShaderBinding* binding = localShaderOptions[0]; - binding->clearTexture("screenColorBuffer"); - binding->clearTexture("screenDepthBuffer"); - binding->addTexture("screenColorBuffer", originalSceneTexture); - binding->addTexture("screenDepthBuffer", zBufferSceneTexture); - - 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()->bindFrameBufferTexture(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); - CoreServices::getInstance()->getRenderer()->applyMaterial(filterShaderMaterial, localShaderOptions[i], i); + + for(int j=0; j < materialBinding->getNumColorTargetBindings(); j++) { + RenderTargetBinding *colorBinding = materialBinding->getColorTargetBinding(j); + materialBinding->clearTexture(colorBinding->name); + + 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); + if(finalTargetZTexture) { + materialBinding->addTexture(depthBinding->name, finalTargetZTexture); + } else { + materialBinding->addTexture(depthBinding->name, renderer->getGlobalDepthFramebuffer()); + } + } + + 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++) { - CoreServices::getInstance()->getRenderer()->setViewportSize(materialBinding->getOutTargetBinding(j)->width, materialBinding->getOutTargetBinding(j)->height); - CoreServices::getInstance()->getRenderer()->bindFrameBufferTexture(materialBinding->getOutTargetBinding(j)->texture); - CoreServices::getInstance()->getRenderer()->drawScreenQuad(materialBinding->getOutTargetBinding(j)->width, materialBinding->getOutTargetBinding(j)->height); - CoreServices::getInstance()->getRenderer()->unbindFramebuffers(); + Texture *bindingTexture = materialBinding->getOutTargetBinding(j)->texture; + if(bindingTexture) { + 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() { + 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() { + + 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(fovSet) - CoreServices::getInstance()->getRenderer()->setFOV(fov); - CoreServices::getInstance()->getRenderer()->setExposureLevel(exposureLevel); + if(projectionMode != MANUAL_MATRIX) { + projectionMatrix = renderer->getProjectionMatrix(); + } if(matrixDirty) { rebuildTransformMatrix(); } Matrix4 camMatrix = getConcatenatedMatrix(); - CoreServices::getInstance()->getRenderer()->setCameraMatrix(camMatrix); - camMatrix = camMatrix.inverse(); - CoreServices::getInstance()->getRenderer()->multModelviewMatrix(camMatrix); + renderer->setCameraMatrix(camMatrix); + camMatrix = camMatrix.Inverse(); + 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 cdcefdce8..c5a1e8441 100644 --- a/Core/Contents/Source/PolyCocoaCore.mm +++ b/Core/Contents/Source/PolyCocoaCore.mm @@ -21,6 +21,7 @@ of this software and associated documentation files (the "Software"), to deal */ #include "PolyCocoaCore.h" +#import "PolycodeView.h" #include #include @@ -29,6 +30,44 @@ of this software and associated documentation files (the "Software"), to deal using namespace Polycode; +static bool DisplayModeIs32Bit(CGDisplayModeRef displayMode) +{ + bool is32Bit = false; + CFStringRef pixelEncoding = CGDisplayModeCopyPixelEncoding(displayMode); + if(CFStringCompare(pixelEncoding, CFSTR(IO32BitDirectPixels), 0) == kCFCompareEqualTo) + is32Bit = true; + CFRelease(pixelEncoding); + + return is32Bit; +} + +static CGDisplayModeRef GetBestDisplayModeForParameters(size_t bitsPerPixel, size_t xRes, size_t yRes) +{ + CGDisplayModeRef bestDisplayMode = CGDisplayCopyDisplayMode(CGMainDisplayID()); + size_t bestWidth = CGDisplayModeGetWidth(bestDisplayMode); + size_t bestHeight = CGDisplayModeGetHeight(bestDisplayMode); + NSArray* displayModes = (NSArray*)CGDisplayCopyAllDisplayModes(CGMainDisplayID(), NULL); + for(NSUInteger i = 0; i < [displayModes count]; ++i) + { + CGDisplayModeRef candidate = (CGDisplayModeRef)[displayModes objectAtIndex:i]; + size_t candidateWidth = CGDisplayModeGetWidth(candidate); + size_t candidateHeight = CGDisplayModeGetHeight(candidate); + if(!DisplayModeIs32Bit(candidate)) + continue; + if(candidateWidth >= xRes && candidateWidth < bestWidth + && candidateHeight >= yRes && candidateHeight < bestHeight) + { + CGDisplayModeRelease(bestDisplayMode); + bestDisplayMode = candidate; + bestWidth = candidateWidth; + bestHeight = candidateHeight; + CGDisplayModeRetain(bestDisplayMode); + } + } + [displayModes release]; + return bestDisplayMode; +} + long getThreadID() { return (long)pthread_self(); } @@ -43,11 +82,14 @@ 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; + eventMutex = createMutex(); // NSLog(@"BUNDLE: %@", [[NSBundle mainBundle] bundlePath]); @@ -60,14 +102,17 @@ long getThreadID() { [view setCore:this]; glView = view; - + context = nil; initTime = mach_absolute_time(); - + + modeChangeInfo.needResolutionChange = false; + renderer = new OpenGLRenderer(); services->setRenderer(renderer); - setVideoMode(xRes,yRes,fullScreen, vSync, aaLevel, anisotropyLevel); + _setVideoMode(xRes,yRes,fullScreen, vSync, aaLevel, anisotropyLevel); + renderer->Init(); CoreServices::getInstance()->installModule(new GLSLShaderModule()); @@ -79,7 +124,7 @@ long getThreadID() { NSArray *types = [NSArray arrayWithObjects:NSStringPboardType, nil]; [pb declareTypes:types owner:nil]; - NSString *nsstr = [NSString stringWithCString: str.c_str()]; + NSString *nsstr = [NSString stringWithCString: str.c_str() encoding:NSUTF8StringEncoding]; /* char* data = (char*)str.c_str(); unsigned size = str.size() * sizeof(char); @@ -95,9 +140,52 @@ 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; + modeChangeInfo.yRes = yRes; + modeChangeInfo.fullScreen = fullScreen; + modeChangeInfo.vSync = vSync; + modeChangeInfo.aaLevel = aaLevel; + 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; + + 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; @@ -130,55 +218,39 @@ long getThreadID() { } context = [[NSOpenGLContext alloc] initWithFormat: format shareContext:context]; + [format release]; if (context == nil) { NSLog(@"Failed to create open gl context"); } - [glView clearGLContext]; [glView setOpenGLContext:context]; [context setView: (NSView*)glView]; - - renderer->setAnisotropyAmount(anisotropyLevel); - - renderer->Resize(xRes, yRes); -// CoreServices::getInstance()->getMaterialManager()->reloadProgramsAndTextures(); dispatchEvent(new Event(), EVENT_CORE_RESIZE); - -// NSRect visibleFrame = [[NSScreen mainScreen] visibleFrame]; -// NSRect frame = NSMakeRect([[glView window] frame].origin.x, [[glView window] frame].origin.y, xRes, yRes); - -// frame.origin.x = (visibleFrame.size.width - frame.size.width) * 0.5; -// frame.origin.y = (visibleFrame.size.height - frame.size.height) * (9.0/10.0); - -// [[glView window] setFrame: frame display: YES animate: NO]; -// if(!fullScreen) { - [[glView window] setContentSize: NSMakeSize(xRes, yRes)]; -// } else { -// CGDisplaySwitchToMode (kCGDirectMainDisplay, CGDisplayBestModeForParameters (kCGDirectMainDisplay, 32, xRes, yRes, NULL) ); -// } - if(fullScreen) { - CGDisplaySwitchToMode (kCGDirectMainDisplay, CGDisplayBestModeForParameters (kCGDirectMainDisplay, 32, xRes, yRes, NULL) ); + if(fullScreen) { if(monitorIndex > -1) { if(monitorIndex > [[NSScreen screens] count]-1) { Logger::log("Requested monitor index above available screens.\n"); monitorIndex = -1; } } - + if(monitorIndex == -1) { - [glView enterFullScreenMode:[NSScreen mainScreen] withOptions:[NSDictionary dictionaryWithObjectsAndKeys: nil]]; + [glView enterFullScreenMode:[[glView window] screen] withOptions: nil]; } else { - [glView enterFullScreenMode:[[NSScreen screens] objectAtIndex:1] withOptions:[NSDictionary dictionaryWithObjectsAndKeys: nil]]; + [glView enterFullScreenMode:[[NSScreen screens] objectAtIndex:monitorIndex] withOptions: nil]; } - + [[glView window] becomeFirstResponder]; + } else { + if(_wasFullscreen) + [glView exitFullScreenModeWithOptions: nil]; } GLint sync = 0; @@ -189,6 +261,18 @@ long getThreadID() { [context setValues:&sync forParameter:NSOpenGLCPSwapInterval]; + CGLContextObj ctx = (CGLContextObj) [context CGLContextObj]; + if(fullScreen) { + GLint dim[2] = {backingBounds.size.width, backingBounds.size.height}; + CGLSetParameter(ctx, kCGLCPSurfaceBackingSize, dim); + CGLEnable (ctx, kCGLCESurfaceBackingSize); + } else { + CGLDisable(ctx, kCGLCESurfaceBackingSize); + } + + renderer->Resize(xRes, yRes); + [[glView window] setContentSize: NSMakeSize(xRes, yRes)]; + if(aaLevel > 0) { glEnable( GL_MULTISAMPLE_ARB ); } else { @@ -199,36 +283,35 @@ long getThreadID() { void CocoaCore::openFileWithApplication(String file, String application) { NSWorkspace *workspace = [NSWorkspace sharedWorkspace]; - NSString *filePath = [NSString stringWithCString:file.c_str()]; - NSString *appString = [NSString stringWithCString:application.c_str()]; + NSString *filePath = [NSString stringWithCString:file.c_str() encoding:NSUTF8StringEncoding]; + NSString *appString = [NSString stringWithCString:application.c_str() encoding:NSUTF8StringEncoding]; [workspace openFile: filePath withApplication: appString andDeactivate: YES]; } void CocoaCore::launchApplicationWithFile(String application, String file) { NSWorkspace *workspace = [NSWorkspace sharedWorkspace]; - NSURL *url = [NSURL fileURLWithPath: [NSString stringWithCString:application.c_str()]]; + NSURL *url = [NSURL fileURLWithPath: [NSString stringWithCString:application.c_str() encoding:NSUTF8StringEncoding]]; NSError *error = nil; - NSArray *arguments = [NSArray arrayWithObjects: [NSString stringWithCString:file.c_str()], nil]; + NSArray *arguments = [NSArray arrayWithObjects: [NSString stringWithCString:file.c_str() encoding:NSUTF8StringEncoding], nil]; [workspace launchApplicationAtURL:url options:0 configuration:[NSDictionary dictionaryWithObject:arguments forKey:NSWorkspaceLaunchConfigurationArguments] error:&error]; //Handle error } String CocoaCore::executeExternalCommand(String command, String args, String inDirectory) { - String finalCommand = command+" "+args; + String finalCommand = "\""+command+"\" "+args; if(inDirectory != "") { - finalCommand = "cd "+inDirectory+" && "+finalCommand; + finalCommand = "cd \""+inDirectory+"\" && "+finalCommand; } + FILE *fp = popen(finalCommand.c_str(), "r"); if(!fp) { return "Unable to execute command"; } - int fd = fileno(fp); - char path[1024]; String retString; @@ -248,11 +331,6 @@ long getThreadID() { dispatchEvent(new Event(), EVENT_CORE_RESIZE); } -vector CocoaCore::getVideoModes() { - vector retVector; - return retVector; -} - CocoaCore::~CocoaCore() { printf("Shutting down cocoa core\n"); [glView setCore:nil]; @@ -431,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: @@ -484,16 +571,45 @@ long getThreadID() { if ( [attachmentPanel runModal] == NSOKButton ) { // files and directories selected. - NSArray* files = [attachmentPanel filenames]; - NSString* fileName = [files objectAtIndex:0]; + NSURL* url = [attachmentPanel URL]; [attachmentPanel release]; - return [fileName UTF8String]; + return [[url path] UTF8String]; } else { [attachmentPanel release]; return [@"" UTF8String]; } } +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; @@ -514,13 +630,15 @@ long getThreadID() { } } - if ( [attachmentPanel runModalForDirectory:nil file:nil types:types] == NSOKButton ) + [attachmentPanel setAllowedFileTypes:types]; + if ( [attachmentPanel runModal] == NSOKButton ) { - NSArray* files = [attachmentPanel filenames]; + NSArray* files = [attachmentPanel URLs]; if(files) { for (int i=0; i < [files count]; i++) { - NSString* fileName = [files objectAtIndex:i]; + NSURL* url = [files objectAtIndex:i]; + NSString* fileName = [url path]; retVector.push_back([fileName UTF8String]); } } @@ -529,26 +647,35 @@ long getThreadID() { return retVector; } -bool CocoaCore::Update() { - if(!running) - return false; - - lockMutex(CoreServices::getRenderMutex()); - checkEvents(); +void CocoaCore::Render() { + lockMutex(CoreServices::getRenderMutex()); if(!paused) { renderer->BeginRender(); } - - updateCore(); - + + services->Render(); + if(!paused) { renderer->EndRender(); [context flushBuffer]; } - unlockMutex(CoreServices::getRenderMutex()); - doSleep(); + unlockMutex(CoreServices::getRenderMutex()); +} + +bool CocoaCore::systemUpdate() { + if(!running) + return false; + doSleep(); + + if(modeChangeInfo.needResolutionChange) { + _setVideoMode(modeChangeInfo.xRes, modeChangeInfo.yRes, modeChangeInfo.fullScreen, modeChangeInfo.vSync, modeChangeInfo.aaLevel, modeChangeInfo.anisotropyLevel); + modeChangeInfo.needResolutionChange = false; + } + + updateCore(); + checkEvents(); return running; } @@ -580,6 +707,8 @@ static void hatValueToXY(CFIndex value, CFIndex range, int * outX, int * outY) { } +// Marked as unused to avoid a warning, assuming that this is useful for debugging. +__attribute__((unused)) static int IOHIDDeviceGetIntProperty(IOHIDDeviceRef deviceRef, CFStringRef key) { CFTypeRef typeRef; int value; @@ -684,13 +813,14 @@ static void onDeviceMatched(void * context, IOReturn result, void * sender, IOHI CFArrayRef elements; CFIndex elementIndex; IOHIDElementRef element; - CFStringRef cfProductName; IOHIDElementType type; - char * description; 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); @@ -745,8 +875,14 @@ static void onDeviceRemoved(void * context, IOReturn result, void * sender, IOHI void CocoaCore::shutdownGamepad() { if (hidManager != NULL) { - unsigned int deviceIndex; + + 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); @@ -754,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 863b91281..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); } @@ -45,6 +49,15 @@ Color::Color(unsigned int hex) { setColorHex(hex); } +Color Color::ColorWithInts(int r,int g, int b, int a) { + return Color(r,g,b,a); +} + +Color Color::ColorWithHex(unsigned int hex) { + return Color(hex); +} + + void Color::setColorHexRGB(unsigned int hex) { // int tr = (hex >> 24) & 0xFF; @@ -83,6 +96,19 @@ Color Color::blendColor(Color c2, int mode, Number amount, Color c3) { ret.b = (b * (1.0-premul)) + (c3.b * premul); ret.a = a + premul; break; + case Color::BLEND_ADDITIVE: + ret.r = r + (c2.r); + ret.g = g + (c2.g); + ret.b = b + (c2.b); + ret.a = a + premul; + if(ret.r > 1.0) + ret.r = 1.0; + if(ret.g > 1.0) + ret.g = 1.0; + if(ret.b > 1.0) + ret.b = 1.0; + + break; } if(ret.a > 1.0) 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 c630e788c..f6f18d564 100755 --- a/Core/Contents/Source/PolyCore.cpp +++ b/Core/Contents/Source/PolyCore.cpp @@ -52,10 +52,15 @@ namespace Polycode { } Core::Core(int _xRes, int _yRes, bool fullScreen, bool vSync, int aaLevel, int anisotropyLevel, int frameRate, int monitorIndex) : EventDispatcher() { + + int _hz; + getScreenInfo(&defaultScreenWidth, &defaultScreenHeight, &_hz); + services = CoreServices::getInstance(); input = new CoreInput(); services->setCore(this); fps = 0; + timeLeftOver = 0.0; running = true; frames = 0; lastFrameTicks=0; @@ -68,7 +73,7 @@ namespace Polycode { if (fullScreen && !xRes && !yRes) { getScreenInfo(&xRes, &yRes, NULL); } - mouseEnabled = true; + mouseEnabled = true; mouseCaptured = false; lastSleepFrameTicks = 0; this->monitorIndex = monitorIndex; @@ -76,22 +81,36 @@ 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) { mouseEnabled = newval; } - - int Core::getNumVideoModes() { - return numVideoModes; + + void Core::captureMouse(bool newval) { + mouseCaptured = newval; } - + Number Core::getXRes() { return xRes; } @@ -107,6 +126,7 @@ namespace Polycode { Core::~Core() { printf("Shutting down core"); delete services; + delete input; } void Core::Shutdown() { @@ -125,18 +145,10 @@ namespace Polycode { return ((Number)elapsed)/1000.0f; } - Number Core::getTicksFloat() { - return ((Number)getTicks())/1000.0f; + double Core::getTicksFloat() { + return getTicks()/1000.0; } - - void Core::setVideoModeIndex(int index, bool fullScreen, bool vSync, int aaLevel, int anisotropyLevel) { - std::vector resList = getVideoModes(); - if(index >= resList.size()) - return; - setVideoMode(resList[index].w, resList[index].h, fullScreen, vSync, aaLevel, anisotropyLevel); - } - void Core::createThread(Threaded *target) { if(!threadedEventMutex) { threadedEventMutex = createMutex(); @@ -182,16 +194,53 @@ namespace Polycode { unlockMutex(threadedEventMutex); } } + + bool Core::updateAndRender() { + bool ret = Update(); + 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; - - services->Update(elapsed, !paused); + + 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) { fps = frames; @@ -226,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 = ticks; + } + lastSleepFrameTicks = getTicks(); + timeSleptMs = lastSleepFrameTicks - ticks; } diff --git a/Core/Contents/Source/PolyCoreInput.cpp b/Core/Contents/Source/PolyCoreInput.cpp index ae83dcf4a..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,9 +64,30 @@ namespace Polycode { } JoystickInfo *CoreInput::getJoystickInfoByIndex(unsigned int index) { + if(index > joysticks.size()-1 || joysticks.size() == 0) { + return NULL; + } return &joysticks[index]; } + bool CoreInput::getJoystickButtonState(int joystickIndex, int button) { + JoystickInfo *info = getJoystickInfoByIndex(joystickIndex); + if(info) { + return info->joystickButtonState[button]; + } else { + return false; + } + } + + Number CoreInput::getJoystickAxisValue(int joystickIndex, int axis) { + JoystickInfo *info = getJoystickInfoByIndex(joystickIndex); + if(info) { + return info->joystickAxisState[axis]; + } else { + return 0.0; + } + } + JoystickInfo *CoreInput::getJoystickInfoByID(unsigned int deviceID) { for(int i=0;i touches; touches.push_back(touch); @@ -176,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() { @@ -208,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; @@ -219,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 25bfdc64d..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() { @@ -64,7 +67,6 @@ CoreServices* CoreServices::getInstance() { //#ifdef _WINDOWS overrideInstance = new CoreServices; - overrideInstance->drawScreensFirst = false; Logger::log("Creating new core services instance...\n"); return overrideInstance; //#else @@ -97,6 +99,10 @@ Config *CoreServices::getConfig() { return config; } +Logger *CoreServices::getLogger() { + return logger; +} + void CoreServices::installModule(PolycodeModule *module) { modules.push_back(module); if(module->requiresUpdate()) { @@ -105,149 +111,102 @@ 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; } } +void CoreServices::setupBasicListeners() { + this->setCore(this->core); +} + CoreServices::CoreServices() : EventDispatcher() { + 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() { return renderer; } -void CoreServices::Update(int elapsed, bool updateRenderer) { +void CoreServices::Render() { + if(renderer->doClearBuffer) + renderer->clearScreen(); + + renderer->setPerspectiveDefaults(); + sceneManager->renderVirtual(); + sceneManager->Render(); + renderer->clearLights(); +} + +void CoreServices::fixedUpdate() { + sceneManager->fixedUpdate(); +} + +void CoreServices::Update(int elapsed) { for(int i=0; i < updateModules.size(); i++) { updateModules[i]->Update(elapsed); } - - timerManager->Update(); - - tweenManager->Update(); - - if(updateRenderer) { - - materialManager->Update(elapsed); - if(drawScreensFirst) { - if(renderer->doClearBuffer) - renderer->clearScreen(); - renderer->setPerspectiveMode(); - sceneManager->UpdateVirtual(); - if(renderer->doClearBuffer) - renderer->clearScreen(); - screenManager->Update(); - renderer->setPerspectiveMode(); - sceneManager->Update(); - } else { - renderer->setPerspectiveMode(); - sceneManager->UpdateVirtual(); - if(renderer->doClearBuffer) - renderer->clearScreen(); - sceneManager->Update(); - screenManager->Update(); - } - - } + resourceManager->Update(elapsed); + timerManager->Update(); + tweenManager->Update(elapsed); + sceneManager->Update(); + soundManager->Update(); } SoundManager *CoreServices::getSoundManager() { return soundManager; } -ScreenManager *CoreServices::getScreenManager() { - return screenManager; -} - SceneManager *CoreServices::getSceneManager() { return sceneManager; } diff --git a/Core/Contents/Source/PolyCubemap.cpp b/Core/Contents/Source/PolyCubemap.cpp index f943bc204..27fccc730 100644 --- a/Core/Contents/Source/PolyCubemap.cpp +++ b/Core/Contents/Source/PolyCubemap.cpp @@ -25,7 +25,26 @@ using namespace Polycode; Cubemap::Cubemap(Texture *t0, Texture *t1, Texture *t2, Texture *t3, Texture *t4, Texture *t5) : Resource(Resource::RESOURCE_CUBEMAP) { - + textures.push_back(t0); + textures.push_back(t1); + textures.push_back(t2); + textures.push_back(t3); + textures.push_back(t4); + textures.push_back(t5); +} + +void Cubemap::setTexture(Texture *texture, int index) { + if(index >= 0 && index < 6) { + textures[index] = texture; + } +} + +Texture *Cubemap::getTexture(int index) { + if(index >= 0 && index < 6) { + return textures[index]; + } else { + return NULL; + } } Cubemap::~Cubemap() { 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 bac44d966..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,37 +36,52 @@ 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) { +Entity *Entity::getEntityById(String id, bool recursive) const { for(int i=0;iid == id) { return children[i]; @@ -77,28 +97,24 @@ Entity *Entity::getEntityById(String id, bool recursive) { return NULL; } -Entity *Entity::Clone(bool deepClone, bool ignoreEditorOnly) { +Entity *Entity::Clone(bool deepClone, bool ignoreEditorOnly) const { Entity *newEntity = new Entity(); applyClone(newEntity, deepClone, ignoreEditorOnly); return newEntity; } -void Entity::applyClone(Entity *clone, bool deepClone, bool ignoreEditorOnly) { +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; - clone->colorAffectsChildren; + clone->colorAffectsChildren = colorAffectsChildren; clone->visibilityAffectsChildren = visibilityAffectsChildren; clone->depthOnly = depthOnly; clone->setUserData(getUserData()); @@ -107,10 +123,19 @@ void Entity::applyClone(Entity *clone, bool deepClone, bool ignoreEditorOnly) { 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; - for(int i=0; i < tags.size(); i++) { - clone->addTag(tags[i]); + if(tags == NULL) { + clone->tags = NULL; + } else { + clone->tags = new std::vector(); + for(int i=0; i < tags->size(); i++) { + clone->addTag((*tags)[i]); + } } clone->setRenderer(renderer); @@ -125,7 +150,31 @@ void Entity::applyClone(Entity *clone, bool deepClone, bool ignoreEditorOnly) { } } -std::vector Entity::getEntitiesByTag(String tag, bool recursive) { +void Entity::setOwnsChildrenRecursive(bool val) { + ownsChildren = val; + for(int i=0; i < children.size(); i++) { + children[i]->setOwnsChildrenRecursive(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; @@ -148,7 +197,7 @@ void Entity::setUserData(void *userData) { this->userData = userData; } -void *Entity::getUserData() { +void *Entity::getUserData() const { return userData; } @@ -170,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(); @@ -194,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; } @@ -207,11 +256,55 @@ 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; } } } +void Entity::moveChildUp(Entity *child) { + for(int i=0; i < children.size(); i++) { + if(children[i] == child && i < children.size()-1) { + Entity *next = (Entity*)children[i+1]; + children[i+1] = child; + children[i] = next; + break; + } + } +} + +void Entity::moveChildDown(Entity *child) { + for(int i=0; i < children.size(); i++) { + if(children[i] == child && i > 0) { + Entity *prev = (Entity*)children[i-1]; + children[i-1] = child; + children[i] = prev; + break; + } + } +} + +void Entity::moveChildTop(Entity *child) { + for(int i=0; i < children.size(); i++) { + if(children[i] == child && i < children.size()-1) { + children.erase(children.begin()+i); + children.push_back(child); + break; + } + } +} + +void Entity::moveChildBottom(Entity *child) { + for(int i=0; i < children.size(); i++) { + if(children[i] == child && i > 0) { + children.erase(children.begin()+i); + children.insert(children.begin(), child); + break; + } + } +} + unsigned int Entity::getNumChildren() { return children.size(); } @@ -224,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) { @@ -239,58 +335,48 @@ 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++) { delete children[i]; } } + 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; } @@ -316,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(); } @@ -354,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) @@ -373,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); } @@ -389,43 +488,23 @@ void Entity::transformAndRender() { isScissorEnabled = renderer->isScissorEnabled(); oldScissorBox = renderer->getScissorBox(); renderer->enableScissor(true); - - Polycode::Rectangle finalScrissorBox = scissorBox; - - // make sure that our scissor box is constrained to the parent one if it exists - if(isScissorEnabled) { - if(finalScrissorBox.x < oldScissorBox.x) - finalScrissorBox.x = oldScissorBox.x; - if(finalScrissorBox.x > oldScissorBox.x + oldScissorBox.w) - finalScrissorBox.x = oldScissorBox.x + oldScissorBox.w; - - if(finalScrissorBox.x+finalScrissorBox.w > oldScissorBox.x + oldScissorBox.w) - finalScrissorBox.w = oldScissorBox.x - finalScrissorBox.x; - - if(finalScrissorBox.y < oldScissorBox.y) - finalScrissorBox.y = oldScissorBox.y; - if(finalScrissorBox.y > oldScissorBox.y + oldScissorBox.h) - finalScrissorBox.y = oldScissorBox.y + oldScissorBox.h; - - if(finalScrissorBox.y+finalScrissorBox.h > oldScissorBox.y + oldScissorBox.h) - finalScrissorBox.h = oldScissorBox.y - finalScrissorBox.y; + Rectangle finalScissorBox = scissorBox; + // make sure that our scissor box is constrained to the parent one if it exists + if(isScissorEnabled) { + finalScissorBox = finalScissorBox.Clipped(renderer->getScissorBox()); } - - renderer->setScissorBox(finalScrissorBox); + + renderer->setScissorBox(finalScissorBox); } renderer->pushMatrix(); if(ignoreParentMatrix && parentEntity) { - renderer->multModelviewMatrix(parentEntity->getConcatenatedMatrix().inverse()); -// renderer->setCurrentModelMatrix(parentEntity->getConcatenatedMatrix().inverse()); + renderer->multModelviewMatrix(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) { @@ -447,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); @@ -495,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; } @@ -524,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; @@ -556,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) { @@ -625,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) { @@ -702,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; @@ -720,51 +963,290 @@ 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 { - return tags.size(); + if(!tags) return 0; + return tags->size(); } String Entity::getTagAtIndex(unsigned int index) const { - if(index < tags.size()) - return tags[index]; + if(!tags) return ""; + if(index < tags->size()) + return (*tags)[index]; return ""; } bool Entity::hasTag(String tag) const { - - for(int i=0; i < tags.size(); i++) { - if(tags[i] == tag) + if(!tags) return false; + for(int i=0; i < tags->size(); i++) { + if((*tags)[i] == tag) return true; } return false; } void Entity::clearTags() { - tags.clear(); + if(!tags) return; + tags->clear(); } void Entity::addTag(String tag) { - tags.push_back(tag); + if(!tags) tags = new std::vector(); + if(!hasTag(tag)) { + tags->push_back(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/PolyFixedShader.cpp b/Core/Contents/Source/PolyFixedShader.cpp index 72a58d273..f8f544e42 100755 --- a/Core/Contents/Source/PolyFixedShader.cpp +++ b/Core/Contents/Source/PolyFixedShader.cpp @@ -45,11 +45,6 @@ Texture *FixedShaderBinding::getDiffuseTexture() { return textures[0]; } -void FixedShaderBinding::addParam(const String& type, const String& name, const String& value) { - -} - - FixedShader::FixedShader() : Shader(Shader::FIXED_SHADER) { } 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 c005013f4..88b5ad225 100644 --- a/Core/Contents/Source/PolyGLCubemap.cpp +++ b/Core/Contents/Source/PolyGLCubemap.cpp @@ -21,30 +21,72 @@ */ #include "PolyGLCubemap.h" -#include "PolyTexture.h" +#include "PolyTexture.h" using namespace Polycode; OpenGLCubemap::OpenGLCubemap(Texture *t0, Texture *t1, Texture *t2, Texture *t3, Texture *t4, Texture *t5) : Cubemap(t0,t1,t2,t3,t4,t5) { + glCubemapLoaded = false; + + recreateFromTextures(); +} + +void OpenGLCubemap::recreateFromTextures() { + + if(glCubemapLoaded) { + glDeleteTextures(1, &textureID); + } + glGenTextures(1, &textureID); glBindTexture(GL_TEXTURE_CUBE_MAP, textureID); - glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X+0, 0, GL_RGBA, t0->getWidth(), t0->getHeight(), 0, GL_RGBA, GL_UNSIGNED_BYTE, t0->getTextureData()); - glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X+1, 0, GL_RGBA, t1->getWidth(), t1->getHeight(), 0, GL_RGBA, GL_UNSIGNED_BYTE, t1->getTextureData()); - glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X+2, 0, GL_RGBA, t2->getWidth(), t2->getHeight(), 0, GL_RGBA, GL_UNSIGNED_BYTE, t2->getTextureData()); - glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X+3, 0, GL_RGBA, t3->getWidth(), t3->getHeight(), 0, GL_RGBA, GL_UNSIGNED_BYTE, t3->getTextureData()); - glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X+4, 0, GL_RGBA, t4->getWidth(), t4->getHeight(), 0, GL_RGBA, GL_UNSIGNED_BYTE, t4->getTextureData()); - glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X+5, 0, GL_RGBA, t5->getWidth(), t5->getHeight(), 0, GL_RGBA, GL_UNSIGNED_BYTE, t5->getTextureData()); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + + Texture *tex; + + tex = getTexture(Cubemap::CUBEMAP_XPOS); + 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); + 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); + 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); + 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); + 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; } OpenGLCubemap::~OpenGLCubemap() { - + if(glCubemapLoaded) { + glDeleteTextures(1, &textureID); + } } GLuint OpenGLCubemap::getTextureID() { diff --git a/Core/Contents/Source/PolyGLES1Renderer.cpp b/Core/Contents/Source/PolyGLES1Renderer.cpp deleted file mode 100644 index 67e3b018c..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); - glScissor(0, 0, xRes, 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() { - -} \ No newline at end of file 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 db7ac7d96..c81d6da9d 100755 --- a/Core/Contents/Source/PolyGLRenderer.cpp +++ b/Core/Contents/Source/PolyGLRenderer.cpp @@ -24,6 +24,7 @@ #include "PolyString.h" #include "PolyLogger.h" #include "PolyTexture.h" +#include "PolyVector2.h" #include "PolyGLTexture.h" #include "PolyCubemap.h" #include "PolyGLCubemap.h" @@ -32,7 +33,6 @@ #include "PolyMaterial.h" #include "PolyMesh.h" #include "PolyModule.h" -#include "PolyPolygon.h" #if defined(_WINDOWS) && !defined(_MINGW) @@ -57,8 +57,12 @@ PFNGLGETBUFFERPOINTERVARBPROC glGetBufferPointervARB; PFNGLVERTEXATTRIBPOINTERPROC glVertexAttribPointer; PFNGLENABLEVERTEXATTRIBARRAYARBPROC glEnableVertexAttribArrayARB; +PFNGLDISABLEVERTEXATTRIBARRAYARBPROC glDisableVertexAttribArrayARB; PFNGLBINDATTRIBLOCATIONPROC glBindAttribLocation; +PFNGLGETPROGRAMIVPROC glGetProgramiv; +PFNGLGETACTIVEUNIFORMPROC glGetActiveUniform; + // GL_EXT_framebuffer_object PFNGLISRENDERBUFFEREXTPROC glIsRenderbufferEXT; PFNGLBINDRENDERBUFFEREXTPROC glBindRenderbufferEXT; @@ -81,19 +85,26 @@ 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; - - glDisable(GL_SCISSOR_TEST); + } -void OpenGLRenderer::setClippingPlanes(Number nearPlane_, Number farPlane_) { - nearPlane = nearPlane_; - farPlane = farPlane_; - Resize(xRes,yRes); +bool OpenGLRenderer::Init() { + if(!Renderer::Init()) + return false; + + glDisable(GL_SCISSOR_TEST); + + return true; } void OpenGLRenderer::initOSSpecific(){ @@ -118,8 +129,13 @@ void OpenGLRenderer::initOSSpecific(){ glVertexAttribPointer = (PFNGLVERTEXATTRIBPOINTERPROC)wglGetProcAddress("glVertexAttribPointer"); glEnableVertexAttribArrayARB = (PFNGLENABLEVERTEXATTRIBARRAYARBPROC)wglGetProcAddress("glEnableVertexAttribArrayARB"); + glDisableVertexAttribArrayARB = (PFNGLDISABLEVERTEXATTRIBARRAYARBPROC)wglGetProcAddress("glDisableVertexAttribArrayARB"); glBindAttribLocation = (PFNGLBINDATTRIBLOCATIONPROC)wglGetProcAddress("glBindAttribLocation"); + + glGetProgramiv = (PFNGLGETPROGRAMIVPROC)wglGetProcAddress("glGetProgramiv"); + glGetActiveUniform = (PFNGLGETACTIVEUNIFORMPROC)wglGetProcAddress("glGetActiveUniform"); + glIsRenderbufferEXT = (PFNGLISRENDERBUFFEREXTPROC)wglGetProcAddress("glIsRenderbufferEXT"); glBindRenderbufferEXT = (PFNGLBINDRENDERBUFFEREXTPROC)wglGetProcAddress("glBindRenderbufferEXT"); glDeleteRenderbuffersEXT = (PFNGLDELETERENDERBUFFERSEXTPROC)wglGetProcAddress("glDeleteRenderbuffersEXT"); @@ -148,32 +164,17 @@ 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(); - gluPerspective(fov,(GLfloat)xRes/(GLfloat)yRes,nearPlane,farPlane); - glViewport(0, 0, xRes, yRes); - glScissor(0, 0, xRes, 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); - + glEnable(GL_VERTEX_PROGRAM_POINT_SIZE); GLint numBuffers; glGetIntegerv(GL_MAX_DRAW_BUFFERS, &numBuffers); -// Logger::log("MAX_DRAW_BUFFERS: %d \n", numBuffers); - + } void OpenGLRenderer::setDepthFunction(int depthFunction) { @@ -189,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); @@ -203,67 +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); - glViewport(0, 0, viewportWidth, viewportHeight); - glScissor(0, 0, viewportWidth, viewportHeight); - glMatrixMode(GL_MODELVIEW); + 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) { - 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 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]; + } + + GLdouble coords[3]; + 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 ); - - 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]); - + 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; } @@ -281,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) { @@ -298,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; @@ -376,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) { @@ -397,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); } @@ -411,9 +503,20 @@ 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) { + glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); + } else{ glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + } break; case BLEND_MODE_LIGHTEN: glBlendFunc (GL_SRC_ALPHA, GL_ONE); @@ -431,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; } @@ -479,49 +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); - glPushMatrix(); - 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); - glPushMatrix(); 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); @@ -529,25 +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 ); - glPopMatrix(); - 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); @@ -556,40 +651,43 @@ 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::bindFrameBufferTexture(Texture *texture) { - if(currentFrameBufferTexture) { - previousFrameBufferTexture = currentFrameBufferTexture; - } +void OpenGLRenderer::bindFrameBufferTextureDepth(Texture *texture) { + if(!texture) + return; OpenGLTexture *glTexture = (OpenGLTexture*)texture; + glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, glTexture->getFrameBufferID()); + Renderer::bindFrameBufferTextureDepth(texture); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); +} - glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, glTexture->getFrameBufferID()); - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - currentFrameBufferTexture = texture; +void OpenGLRenderer::bindFrameBufferTexture(Texture *texture) { + if(!texture) + return; + OpenGLTexture *glTexture = (OpenGLTexture*)texture; + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, glTexture->getFrameBufferID()); + Renderer::bindFrameBufferTexture(texture); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); } void OpenGLRenderer::unbindFramebuffers() { - glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); - currentFrameBufferTexture = NULL; - if(previousFrameBufferTexture) { - bindFrameBufferTexture(previousFrameBufferTexture); - previousFrameBufferTexture = NULL; - } + 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) { GLuint depthTexture,colorTexture; @@ -601,22 +699,27 @@ 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); status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT); if(status == GL_FRAMEBUFFER_COMPLETE_EXT) { - Logger::log("color fbo generation successful\n"); +// Logger::log("color fbo generation successful\n"); } else { Logger::log("color fbo generation failed\n"); } @@ -631,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); @@ -641,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); @@ -651,7 +759,7 @@ void OpenGLRenderer::createRenderTextures(Texture **colorBuffer, Texture **depth status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT); if(status == GL_FRAMEBUFFER_COMPLETE_EXT) { - Logger::log("depth fbo generation successful\n"); +// Logger::log("depth fbo generation successful\n"); } else { Logger::log("depth fbo generation failed\n"); } @@ -679,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) { @@ -720,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); @@ -781,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::setWireframePolygonMode(bool val) { + if(val) { + glPolygonMode( GL_FRONT_AND_BACK, GL_LINE); + } else { + glPolygonMode( GL_FRONT_AND_BACK, GL_FILL); + } } -void OpenGLRenderer::setRenderArrayData(RenderDataArray *array, Number *arrayData) { - -} - -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; @@ -1024,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(); } @@ -1078,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() { @@ -1096,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 2a3723e5f..e7f6518d5 100755 --- a/Core/Contents/Source/PolyGLSLProgram.cpp +++ b/Core/Contents/Source/PolyGLSLProgram.cpp @@ -26,9 +26,17 @@ THE SOFTWARE. #include "PolyVector2.h" #include "PolyColor.h" #include "PolyLogger.h" +#include "PolyCoreServices.h" +#include "PolyLogger.h" +#include "PolyGLHeaders.h" #ifdef _WINDOWS #include + +// Some shader functions that aren't defined in glext/wglext +extern PFNGLGETSHADERIVPROC glGetShaderiv; +extern PFNGLGETSHADERINFOLOGPROC glGetShaderInfoLog; + #endif #include "PolyGLHeaders.h" @@ -55,86 +63,54 @@ extern PFNGLGETUNIFORMLOCATIONARBPROC glGetUniformLocation; using namespace Polycode; -GLSLProgram::GLSLProgram(int type) : Resource(Resource::RESOURCE_PROGRAM) { - this->type = type; +GLSLProgram::GLSLProgram(String fileName, int type) : ShaderProgram(type) { + program = -1; + this->fileName = fileName; + reloadProgram(); } GLSLProgram::~GLSLProgram() { glDeleteShader(program); } -GLSLProgramParam GLSLProgram::addParam(const String& name, const String& typeString, const String& valueString, bool isAuto, int autoID, int paramType, void *defaultData, void *minData, void *maxData) { - GLSLProgramParam newParam; - newParam.name = name; - newParam.typeString = typeString; - newParam.valueString = valueString; - newParam.paramType = paramType; - newParam.defaultData = defaultData; - newParam.minValue = minData; - newParam.maxValue = maxData; - newParam.isAuto = isAuto; - newParam.autoID = autoID; - - params.push_back(newParam); - return newParam; -} - -void GLSLProgramParam::createParamData(int *retType, const String& type, const String& value, const String& min, const String& max, void **valueRes, void **minRes, void **maxRes) { +void GLSLProgram::reloadProgram() { + if(program != -1) + glDeleteShader(program); - (*valueRes) = NULL; - (*minRes) = NULL; - (*maxRes) = NULL; - - if(type == "Number") { - *retType = GLSLProgramParam::PARAM_Number; - Number *val = new Number(); - *val = atof(value.c_str()); - (*valueRes) = (void*)val; - - val = new Number(); - *val = atof(min.c_str()); - (*minRes) = (void*)val; - - val = new Number(); - *val = atof(max.c_str()); - (*maxRes) = (void*)val; - - return; - } else if(type == "Vector2") { - *retType = GLSLProgramParam::PARAM_Vector2; - Vector2 *val = new Vector2(); - (*valueRes) = (void*)val; - vector values = value.split(" "); - if(values.size() == 2) { - val->set(atof(values[0].c_str()), atof(values[1].c_str())); - } else { - Logger::log("Error: A Vector2 must have 2 values (%d provided)!\n", values.size()); - } - return; - } else if(type == "Vector3") { - *retType = GLSLProgramParam::PARAM_Vector3; - Vector3 *val = new Vector3(); - (*valueRes) = (void*)val; - vector values = value.split(" "); - if(values.size() == 3) { - val->set(atof(values[0].c_str()), atof(values[1].c_str()), atof(values[2].c_str())); - } else { - Logger::log("Error: A Vector3 must have 3 values (%d provided)!\n", values.size()); - } - return; - } else if(type == "Color") { - *retType = GLSLProgramParam::PARAM_Color; - Color *val = new Color(); - (*valueRes) = (void*)val; - vector values = value.split(" "); - if(values.size() == 4) { - val->setColor(atof(values[0].c_str()), atof(values[1].c_str()), atof(values[2].c_str()), atof(values[3].c_str())); - } else { - Logger::log("Error: A Color must have 4 values (%d provided)!\n", values.size()); - } - return; - } else { - *retType = GLSLProgramParam::PARAM_UNKNOWN; - (*valueRes) = NULL; - } -} + OSFILE *file = OSBasics::open(fileName, "rb"); + if (!file) { + Logger::log("Error: shader file %s not found\n", fileName.c_str()); + program = -1; + return; + } + OSBasics::seek(file, 0, SEEK_END); + long progsize = OSBasics::tell(file); + OSBasics::seek(file, 0, SEEK_SET); + char *buffer = (char*)malloc(progsize+1); + memset(buffer, 0, progsize+1); + OSBasics::read(buffer, progsize, 1, file); + OSBasics::close(file); + + if(type == GLSLProgram::TYPE_VERT) { + program = glCreateShader(GL_VERTEX_SHADER); + } else { + program = glCreateShader(GL_FRAGMENT_SHADER); + } + + glShaderSource(program, 1, (const GLchar**)&buffer, 0); + glCompileShader(program); + + GLint compiled = true; + glGetShaderiv(program, GL_COMPILE_STATUS, &compiled); + if(!compiled) { + GLint length; + GLchar* log; + glGetShaderiv(program, GL_INFO_LOG_LENGTH, &length); + log = (GLchar*)malloc(length); + glGetShaderInfoLog(program, length, &length, log); + printf("GLSL ERROR: %s\n", log); + CoreServices::getInstance()->getLogger()->logBroadcast("GLSL ERROR:" + String(log)); + free(log); + } + free(buffer); +} \ No newline at end of file diff --git a/Core/Contents/Source/PolyGLSLShader.cpp b/Core/Contents/Source/PolyGLSLShader.cpp index 224a60eeb..a479f70f8 100755 --- a/Core/Contents/Source/PolyGLSLShader.cpp +++ b/Core/Contents/Source/PolyGLSLShader.cpp @@ -41,6 +41,8 @@ using std::vector; extern PFNGLUSEPROGRAMPROC glUseProgram; extern PFNGLUNIFORM1IPROC glUniform1i; extern PFNGLACTIVETEXTUREPROC glActiveTexture; +extern PFNGLGETPROGRAMIVPROC glGetProgramiv; +extern PFNGLGETACTIVEUNIFORMPROC glGetActiveUniform; extern PFNGLCREATESHADERPROC glCreateShader; extern PFNGLSHADERSOURCEPROC glShaderSource; extern PFNGLCOMPILESHADERPROC glCompileShader; @@ -58,71 +60,143 @@ extern PFNGLGETUNIFORMLOCATIONARBPROC glGetUniformLocation; using namespace Polycode; -GLSLShaderBinding::GLSLShaderBinding(GLSLShader *shader) : ShaderBinding(shader) { - glslShader = shader; +int GLSLShader::getPolycodeParamType(int glType) { + switch(glType) { + case GL_FLOAT: + return ProgramParam::PARAM_NUMBER; + break; + case GL_FLOAT_VEC2: + return ProgramParam::PARAM_VECTOR2; + break; + case GL_FLOAT_VEC3: + return ProgramParam::PARAM_VECTOR3; + break; + case GL_FLOAT_VEC4: + return ProgramParam::PARAM_COLOR; + break; + case GL_INT: + return ProgramParam::PARAM_NUMBER; + break; + case GL_INT_VEC2: + return ProgramParam::PARAM_VECTOR2; + break; + case GL_INT_VEC3: + return ProgramParam::PARAM_VECTOR3; + break; + case GL_INT_VEC4: + return ProgramParam::PARAM_COLOR; + break; + case GL_BOOL: + return ProgramParam::PARAM_NUMBER; + break; + case GL_BOOL_VEC2: + return ProgramParam::PARAM_VECTOR2; + break; + case GL_BOOL_VEC3: + return ProgramParam::PARAM_VECTOR3; + break; + case GL_BOOL_VEC4: + return ProgramParam::PARAM_COLOR; + break; + case GL_FLOAT_MAT2: + return ProgramParam::PARAM_MATRIX; + break; + case GL_FLOAT_MAT3: + return ProgramParam::PARAM_MATRIX; + break; + case GL_FLOAT_MAT4: + return ProgramParam::PARAM_MATRIX; + break; + default: + return ProgramParam::PARAM_UNKNOWN; + break; + } } -GLSLShaderBinding::~GLSLShaderBinding() { - -} +void GLSLShader::linkProgram() { + expectedParams.clear(); + expectedTextures.clear(); + expectedCubemaps.clear(); -Texture *GLSLShaderBinding::getTexture(const String& name) { - for(int i=0; i < textures.size(); i++) { - if(textures[i].name == name) { - return textures[i].texture; - } + shader_id = glCreateProgram(); + glAttachShader(shader_id, ((GLSLProgram*)fp)->program); + glAttachShader(shader_id, ((GLSLProgram*)vp)->program); + 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); } - return NULL; -} - -void GLSLShaderBinding::addTexture(const String& name, Texture *texture) { - GLSLTextureBinding binding; - binding.name = name; - binding.texture = texture; -// binding.vpParam = GLSLGetNamedParameter(glslShader->fp->program, name.c_str()); - textures.push_back(binding); + if(fp) { + fp->addEventListener(this, Event::RESOURCE_RELOAD_EVENT); + } + + int total = -1; + glGetProgramiv( shader_id, GL_ACTIVE_UNIFORMS, &total ); + for(int i=0; i < total; i++) { + int name_len=-1, num=-1; + GLenum type = GL_ZERO; + char name[128]; + glGetActiveUniform(shader_id, GLuint(i), sizeof(name)-1, &name_len, &num, &type, name ); + name[name_len] = 0; + + if(!(String(name).find("gl_") == 0)) { + switch(type) { + case GL_SAMPLER_2D: + expectedTextures.push_back(String(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; + } + } + } + + dispatchEvent(new Event(), Event::RESOURCE_RELOAD_EVENT); } -void GLSLShaderBinding::addCubemap(const String& name, Cubemap *cubemap) { - GLSLCubemapBinding binding; - binding.cubemap = cubemap; - binding.name = name; -// binding.vpParam = GLSLGetNamedParameter(GLSLShader->fp->program, name.c_str()); - cubemaps.push_back(binding); +void GLSLShader::unlinkProgram() { + if(vp) { + vp->removeAllHandlersForListener(this); + } + if(fp) { + fp->removeAllHandlersForListener(this); + } + glDetachShader(shader_id, ((GLSLProgram*)fp)->program); + glDetachShader(shader_id, ((GLSLProgram*)vp)->program); + glDeleteProgram(shader_id); } -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; - } +void GLSLShader::handleEvent(Event *event) { + if(event->getEventCode() == Event::RESOURCE_RELOAD_EVENT && (event->getDispatcher() == vp || event->getDispatcher() == fp)) { + unlinkProgram(); + linkProgram(); } } - -void GLSLShaderBinding::addParam(const String& type, const String& name, const String& value) { - int paramType; - void *defaultData; - void *minData; - void *maxData; - GLSLProgramParam::createParamData(¶mType, type, value, "", "", &defaultData,&minData, &maxData); - LocalShaderParam *newParam = new LocalShaderParam; - newParam->data = defaultData; - newParam->name = name; - localParams.push_back(newParam); +void GLSLShader::setVertexProgram(ShaderProgram *vp) { + unlinkProgram(); + this->vp = vp; + linkProgram(); } -void GLSLShader::linkProgram() { - shader_id = glCreateProgram(); - glAttachShader(shader_id, fp->program); - glAttachShader(shader_id, vp->program); - - glBindAttribLocation(shader_id, 6, "vTangent"); - - glLinkProgram(shader_id); +void GLSLShader::setFragmentProgram(ShaderProgram *fp) { + unlinkProgram(); + this->fp = fp; + linkProgram(); } + GLSLShader::GLSLShader(GLSLProgram *vp, GLSLProgram *fp) : Shader(Shader::MODULE_SHADER) { this->vp = vp; this->fp = fp; @@ -136,11 +210,5 @@ void GLSLShader::reload() { } GLSLShader::~GLSLShader() { - glDetachShader(shader_id, fp->program); - glDetachShader(shader_id, vp->program); - glDeleteProgram(shader_id); -} - -ShaderBinding *GLSLShader::createBinding() { - return new GLSLShaderBinding(this); + unlinkProgram(); } diff --git a/Core/Contents/Source/PolyGLSLShaderModule.cpp b/Core/Contents/Source/PolyGLSLShaderModule.cpp index 505bfa622..a55df265d 100755 --- a/Core/Contents/Source/PolyGLSLShaderModule.cpp +++ b/Core/Contents/Source/PolyGLSLShaderModule.cpp @@ -29,6 +29,7 @@ THE SOFTWARE. #include "PolyGLSLShaderModule.h" #include "PolyCoreServices.h" +#include "PolyCore.h" #include "PolyResourceManager.h" #include "PolyRenderer.h" #include "PolyGLSLProgram.h" @@ -64,10 +65,8 @@ PFNGLDELETEPROGRAMPROC glDeleteProgram; PFNGLUNIFORMMATRIX4FVPROC glUniformMatrix4fv; PFNGLGETSHADERIVPROC glGetShaderiv; PFNGLGETSHADERINFOLOGPROC glGetShaderInfoLog; -#ifndef _MINGW PFNGLGETUNIFORMLOCATIONARBPROC glGetUniformLocation; #endif -#endif GLSLShaderModule::GLSLShaderModule() : PolycodeShaderModule() { #ifdef _WINDOWS @@ -76,7 +75,6 @@ GLSLShaderModule::GLSLShaderModule() : PolycodeShaderModule() { glUniform1f = (PFNGLUNIFORM1FPROC)wglGetProcAddress("glUniform1f"); glUniform2f = (PFNGLUNIFORM2FPROC)wglGetProcAddress("glUniform2f"); glUniform3f = (PFNGLUNIFORM3FPROC)wglGetProcAddress("glUniform3f"); - glUniform4f = (PFNGLUNIFORM4FPROC)wglGetProcAddress("glUniform4f"); glCreateShader = (PFNGLCREATESHADERPROC)wglGetProcAddress("glCreateShader"); glShaderSource = (PFNGLSHADERSOURCEPROC)wglGetProcAddress("glShaderSource"); glCompileShader = (PFNGLCOMPILESHADERPROC)wglGetProcAddress("glCompileShader"); @@ -93,6 +91,7 @@ GLSLShaderModule::GLSLShaderModule() : PolycodeShaderModule() { #ifndef _MINGW glGetUniformLocation = (PFNGLGETUNIFORMLOCATIONARBPROC)wglGetProcAddress("glGetUniformLocation"); + glUniform4f = (PFNGLUNIFORM4FPROC)wglGetProcAddress("glUniform4f"); #endif #endif } @@ -113,16 +112,31 @@ String GLSLShaderModule::getShaderType() { return "glsl"; } -Shader *GLSLShaderModule::createShader(TiXmlNode *node) { - TiXmlNode* pChild, *pChild2, *pChild3; +Shader *GLSLShaderModule::createShader(ResourcePool *resourcePool, String name, String vpName, String fpName) { + + GLSLShader *retShader = NULL; + GLSLProgram *vp = NULL; GLSLProgram *fp = NULL; - GLSLShader *retShader = NULL; - - std::vector expectedTextures; - std::vector expectedFragmentParams; - std::vector expectedVertexParams; + + vp = (GLSLProgram*)resourcePool->getResourceByPath(vpName); + fp = (GLSLProgram*)resourcePool->getResourceByPath(fpName); + if(vp != NULL && fp != NULL) { + GLSLShader *shader = new GLSLShader(vp,fp); + shader->setName(name); + retShader = shader; + shaders.push_back((Shader*)shader); + } + return retShader; +} + +Shader *GLSLShaderModule::createShader(ResourcePool *resourcePool, TiXmlNode *node) { + TiXmlNode* pChild; + GLSLProgram *vp = NULL; + GLSLProgram *fp = NULL; + GLSLShader *retShader = NULL; + TiXmlElement *nodeElement = node->ToElement(); if (!nodeElement) return NULL; // Skip comment nodes @@ -131,51 +145,36 @@ Shader *GLSLShaderModule::createShader(TiXmlNode *node) { if (!pChildElement) continue; // Skip comment nodes if(strcmp(pChild->Value(), "vp") == 0) { - vp = (GLSLProgram*)CoreServices::getInstance()->getResourceManager()->getResource(Resource::RESOURCE_PROGRAM, String(pChildElement->Attribute("source"))); - if(vp) { - for (pChild2 = pChild->FirstChild(); pChild2 != 0; pChild2 = pChild2->NextSibling()) { - if(strcmp(pChild2->Value(), "params") == 0) { - for (pChild3 = pChild2->FirstChild(); pChild3 != 0; pChild3 = pChild3->NextSibling()) { - if(strcmp(pChild3->Value(), "param") == 0) { - expectedVertexParams.push_back(addParamToProgram(vp,pChild3)); - } - } - } + String vpFileName = String(pChildElement->Attribute("source")); + 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); + resourcePool->addResource(vp); } } } if(strcmp(pChild->Value(), "fp") == 0) { - fp = (GLSLProgram*)CoreServices::getInstance()->getResourceManager()->getResource(Resource::RESOURCE_PROGRAM, String(pChildElement->Attribute("source"))); - if(fp) { - for (pChild2 = pChild->FirstChild(); pChild2 != 0; pChild2 = pChild2->NextSibling()) { - if(strcmp(pChild2->Value(), "params") == 0) { - for (pChild3 = pChild2->FirstChild(); pChild3 != 0; pChild3 = pChild3->NextSibling()) { - if(strcmp(pChild3->Value(), "param") == 0) { - expectedFragmentParams.push_back(addParamToProgram(fp,pChild3)); - } - } - } - if(strcmp(pChild2->Value(), "textures") == 0) { - for (pChild3 = pChild2->FirstChild(); pChild3 != 0; pChild3 = pChild3->NextSibling()) { - if(strcmp(pChild3->Value(), "texture") == 0) { - TiXmlElement *texNodeElement = pChild3->ToElement(); - if (texNodeElement) { - expectedTextures.push_back(String(texNodeElement->Attribute("name"))); - } - } - } - } + String fpFileName = String(pChildElement->Attribute("source")); + 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); + resourcePool->addResource(fp); } - } + } } } if(vp != NULL && fp != NULL) { GLSLShader *cgShader = new GLSLShader(vp,fp); cgShader->setName(String(nodeElement->Attribute("name"))); - cgShader->expectedTextures = expectedTextures; - cgShader->expectedVertexParams = expectedVertexParams; - cgShader->expectedFragmentParams = expectedFragmentParams; retShader = cgShader; shaders.push_back((Shader*)cgShader); } @@ -187,48 +186,85 @@ void GLSLShaderModule::clearShader() { glUseProgram(0); } -void GLSLShaderModule::updateGLSLParam(Renderer *renderer, GLSLShader *glslShader, GLSLProgramParam ¶m, ShaderBinding *materialOptions, ShaderBinding *localOptions) { +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) { - void *paramData = param.defaultData; - LocalShaderParam *localParam = materialOptions->getLocalParamByName(param.name); - if(localParam) { - paramData = localParam->data; - } + LocalShaderParam *localParam = NULL; + localParam = materialOptions->getLocalParamByName(param.name); - localParam = localOptions->getLocalParamByName(param.name); - if(localParam) { - paramData = localParam->data; + // local options override material options. + LocalShaderParam *localOptionsParam = localOptions->getLocalParamByName(param.name); + if(localOptionsParam) { + localParam = localOptionsParam; } - - switch(param.paramType) { - case GLSLProgramParam::PARAM_Number: - { - Number *fval; - fval = (Number*)paramData; - int paramLocation = glGetUniformLocation(glslShader->shader_id, param.name.c_str()); - glUniform1f(paramLocation, *fval); - break; - } - case GLSLProgramParam::PARAM_Vector2: - { - Vector2 *fval2 = (Vector2*)paramData; - int paramLocation = glGetUniformLocation(glslShader->shader_id, param.name.c_str()); - glUniform2f(paramLocation, fval2->x, fval2->y); break; - } - case GLSLProgramParam::PARAM_Vector3: - { - Vector3 *fval3 = (Vector3*)paramData; - int paramLocation = glGetUniformLocation(glslShader->shader_id, param.name.c_str()); - glUniform3f(paramLocation, fval3->x,fval3->y,fval3->z); - break; - } - case GLSLProgramParam::PARAM_Color: - { - Color *col = (Color*)paramData; - int paramLocation = glGetUniformLocation(glslShader->shader_id, param.name.c_str()); - glUniform4f(paramLocation, col->r, col->g, col->b, col->a); - break; - } + + int paramLocation = glGetUniformLocation(glslShader->shader_id, param.name.c_str()); + + switch(param.type) { + case ProgramParam::PARAM_NUMBER: + if(localParam) { + glUniform1f(paramLocation, localParam->getNumber()); + } else { + glUniform1f(paramLocation, 0.0f); + } + break; + case ProgramParam::PARAM_VECTOR2: + if(localParam) { + Vector2 vec2 = localParam->getVector2(); + glUniform2f(paramLocation, vec2.x, vec2.y); + } else { + glUniform2f(paramLocation, 0.0f, 0.0f); + } + break; + case ProgramParam::PARAM_VECTOR3: + if(localParam) { + Vector3 vec3 = localParam->getVector3(); + glUniform3f(paramLocation, vec3.x, vec3.y, vec3.z); + } else { + glUniform3f(paramLocation, 0.0f, 0.0f, 0.0f); + } + break; + case ProgramParam::PARAM_COLOR: + if(localParam) { + Color color = localParam->getColor(); + glUniform4f(paramLocation, color.r, color.g, color.b, color.a); + } else { + glUniform4f(paramLocation, 0.0f, 0.0f, 0.0f, 0.0f); + } + 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; } } @@ -238,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); @@ -263,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]; - light.position = renderer->getCameraMatrix().inverse() * light.position; + 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(); @@ -320,227 +350,177 @@ bool GLSLShaderModule::applyShaderMaterial(Renderer *renderer, Material *materia light = spotLights[i]; pos = light.position; dir = light.dir; - pos = renderer->getCameraMatrix().inverse() * pos; - dir = renderer->getCameraMatrix().inverse().rotateVector(dir); + pos = renderer->getCameraMatrix().Inverse() * pos; + dir = renderer->getCameraMatrix().Inverse().rotateVector(dir); 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, 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->vp->params.size(); i++) { - GLSLProgramParam param = glslShader->vp->params[i]; + for(int i=0; i < glslShader->expectedParams.size(); i++) { + ProgramParam param = glslShader->expectedParams[i]; updateGLSLParam(renderer, glslShader, param, material->getShaderBinding(shaderIndex), localOptions); } - - for(int i=0; i < glslShader->fp->params.size(); i++) { - GLSLProgramParam param = glslShader->fp->params[i]; - updateGLSLParam(renderer, glslShader, param, material->getShaderBinding(shaderIndex), localOptions); - } - - for(int i=0; i < cgBinding->textures.size(); i++) { - 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, ((OpenGLTexture*)cgBinding->textures[i].texture)->getTextureID()); - textureIndex++; - } - - - for(int i=0; i < cgBinding->cubemaps.size(); i++) { - int texture_location = glGetUniformLocation(glslShader->shader_id, cgBinding->cubemaps[i].name.c_str()); - glUniform1i(texture_location, textureIndex); - glActiveTexture(GL_TEXTURE0 + textureIndex); + for(int i=0; i < cgBinding->textures.size(); i++) { + 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++; + } + } + } - glBindTexture(GL_TEXTURE_CUBE_MAP, ((OpenGLCubemap*)cgBinding->cubemaps[i].cubemap)->getTextureID()); - textureIndex++; - } + for(int i=0; i < cgBinding->cubemaps.size(); i++) { + 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++) { - 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, ((OpenGLTexture*)cgBinding->textures[i].texture)->getTextureID()); - textureIndex++; + 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, localOptions->textures[i].name.c_str()); + glUniform1i(texture_location, textureIndex); + glActiveTexture(GL_TEXTURE0 + textureIndex); + glBindTexture(GL_TEXTURE_2D, glTexture->getTextureID()); + textureIndex++; + } } - - return true; -} - -GLSLProgramParam GLSLShaderModule::addParamToProgram(GLSLProgram *program,TiXmlNode *node) { - bool isAuto = false; - int autoID = 0; - int paramType = GLSLProgramParam::PARAM_UNKNOWN; - void *defaultData = NULL; - void *minData = NULL; - void *maxData = NULL; - - TiXmlElement *nodeElement = node->ToElement(); - if (!nodeElement) { - GLSLProgramParam::createParamData(¶mType, "Number", "0.0", "0.0", "0.0", &defaultData, &minData, &maxData); - return program->addParam("Unknown", "Number", nodeElement->Attribute("default"), isAuto, autoID, paramType, defaultData, minData, maxData); // Skip comment nodes + 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, localOptions->cubemaps[i].name.c_str()); + glUniform1i(texture_location, textureIndex); + glActiveTexture(GL_TEXTURE0 + textureIndex); + glBindTexture(GL_TEXTURE_CUBE_MAP, glCubemap->getTextureID()); + textureIndex++; } + } - isAuto = false; - - if(nodeElement->Attribute("auto")) { - if(strcmp(nodeElement->Attribute("auto"), "true") == 0) { - isAuto = true; - } - } - - GLSLProgramParam::createParamData(¶mType, nodeElement->Attribute("type"), nodeElement->Attribute("default"), nodeElement->Attribute("min"), nodeElement->Attribute("max"), &defaultData, &minData, &maxData); - - return program->addParam(nodeElement->Attribute("name"), nodeElement->Attribute("type"), nodeElement->Attribute("default"), isAuto, autoID, paramType, defaultData, minData, maxData); + + return true; } void GLSLShaderModule::reloadPrograms() { for(int i=0; i < programs.size(); i++) { GLSLProgram *program = programs[i]; - recreateGLSLProgram(program, program->getResourcePath(), program->type); + program->reloadProgram(); } } -void GLSLShaderModule::recreateGLSLProgram(GLSLProgram *prog, const String& fileName, int type) { - - OSFILE *file = OSBasics::open(fileName, "r"); - OSBasics::seek(file, 0, SEEK_END); - long progsize = OSBasics::tell(file); - OSBasics::seek(file, 0, SEEK_SET); - char *buffer = (char*)malloc(progsize+1); - memset(buffer, 0, progsize+1); - OSBasics::read(buffer, progsize, 1, file); - OSBasics::close(file); - - if(type == GLSLProgram::TYPE_VERT) { - prog->program = glCreateShader(GL_VERTEX_SHADER); - } else { - prog->program = glCreateShader(GL_FRAGMENT_SHADER); - } - - glShaderSource(prog->program, 1, (const GLchar**)&buffer, 0); - glCompileShader(prog->program); - - GLint compiled = true; - glGetShaderiv(prog->program, GL_COMPILE_STATUS, &compiled); - if(!compiled) { - GLint length; - GLchar* log; - glGetShaderiv(prog->program, GL_INFO_LOG_LENGTH, &length); - log = (GLchar*)malloc(length); - glGetShaderInfoLog(prog->program, length, &length, log); - printf("GLSL ERROR: %s\n", log); - free(log); - } - - - free(buffer); - -} - GLSLProgram *GLSLShaderModule::createGLSLProgram(const String& fileName, int type) { - GLSLProgram *prog = new GLSLProgram(type); - recreateGLSLProgram(prog, fileName, type); + GLSLProgram *prog = new GLSLProgram(fileName, type); programs.push_back(prog); return prog; } -Resource* GLSLShaderModule::createProgramFromFile(const String& extension, const String& fullPath) { +ShaderProgram* GLSLShaderModule::createProgramFromFile(const String& extension, const String& fullPath) { if(extension == "vert") { Logger::log("Adding GLSL vertex program %s\n", fullPath.c_str()); return createGLSLProgram(fullPath, GLSLProgram::TYPE_VERT); diff --git a/Core/Contents/Source/PolyGLTexture.cpp b/Core/Contents/Source/PolyGLTexture.cpp index 95bc78588..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,13 +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) { @@ -83,7 +87,7 @@ void OpenGLTexture::recreateFromImageData() { } if(createMipmaps) { - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR_MIPMAP_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); if(textureData) { gluBuild2DMipmaps(GL_TEXTURE_2D, glTextureFormat, width, height, glTextureType, pixelType, textureData ); @@ -118,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 d77202fd9..40c902ef3 100755 --- a/Core/Contents/Source/PolyImage.cpp +++ b/Core/Contents/Source/PolyImage.cpp @@ -27,6 +27,11 @@ #include "PolyLogger.h" #include "OSBasics.h" #include "PolyPerlin.h" +#include +#include +#include "rgbe.h" +#define STB_IMAGE_IMPLEMENTATION +#include "stb_image.h" using namespace Polycode; @@ -46,6 +51,11 @@ Image::Image(const String& fileName) : imageData(NULL) { } } +Image *Image::BlankImage(int width, int height, int type) { + return new Image(width, height, type); +} + + void Image::setPixelType(int type) { imageType = type; switch(imageType) { @@ -56,7 +66,7 @@ void Image::setPixelType(int type) { pixelSize = 4; break; case IMAGE_FP16: - pixelSize = 16; + pixelSize = 6; break; default: pixelSize = 4; @@ -70,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) { @@ -112,38 +122,34 @@ char *Image::getPixels() { return imageData; } -char *Image::getPixelsInRect(unsigned int x, unsigned int y, unsigned int width, unsigned int height) { +char *Image::getPixelsInRect(int x, int y, int width, int height) { + transformCoordinates(&x, &y, &width, &height); char *retBuf = (char*) malloc(pixelSize * width * height); memset(retBuf, 0, pixelSize * width * height); if(x < this->width-1 && y < this->height-1) { - - unsigned int xAmt; - unsigned int yAmt; - if(x + width > this->width) { - xAmt = this->width - x; - } else { - xAmt = width; - } - if(y + height > this->height) { - yAmt = this->height - y; - } else { - yAmt = height; - } + width = std::min(width, this->width - x); + height = std::min(height, this->height - y); - for(int i=0; i < yAmt; i++) { + for(int i=0; i < height; i++) { long srcOffset = ((pixelSize*this->width) * (y+i)) + (pixelSize*x); - long dstOffset = (pixelSize*xAmt) * i; - memcpy(retBuf + dstOffset, imageData+srcOffset, pixelSize * xAmt); + long dstOffset = (pixelSize*width) * i; + memcpy(retBuf + dstOffset, imageData+srcOffset, pixelSize * width); } } return retBuf; } +Image *Image::getImagePart(Rectangle subRect) { + char *newData = getPixelsInRect( (int) subRect.x, (int) subRect.y, (int) subRect.w, (int) subRect.h); + return new Image(newData, subRect.w, subRect.h, this->imageType); +} + Color Image::getPixel(int x, int y) { + transformCoordinates(&x, &y); if(x < 0 || x >= width || y < 0 || y >= height) return Color(0,0,0,0); unsigned int *imageData32 = (unsigned int*)imageData; @@ -157,22 +163,22 @@ Color Image::getPixel(int x, int y) { return Color(((Number)tr)/255.0f, ((Number)tg)/255.0f, ((Number)tb)/255.0f,((Number)ta)/255.0f); } -unsigned int Image::getWidth() const { +int Image::getWidth() const { return width; } -unsigned int Image::getHeight() const { +int Image::getHeight() const { return height; } -void Image::createEmpty(unsigned int width, unsigned 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(0,0,0,0); + fill(fillColor); } void Image::perlinNoise(int seed, bool alpha) { @@ -191,14 +197,7 @@ 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::drawRect(int x, int y, int w, int h, Color col) { +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++) { setPixel(x+i,y+j,col); @@ -207,6 +206,7 @@ void Image::drawRect(int x, int y, int w, int h, Color col) { } void Image::setPixel(int x, int y, Color col) { + transformCoordinates(&x, &y); if(x < 0 || x >= width || y < 0 || y >= height) return; @@ -215,12 +215,12 @@ void Image::setPixel(int x, int y, Color col) { } -void Image::move(int x, int y) { +void Image::moveBrush(int x, int y) { brushPosX += x; - brushPosY += y; + brushPosY -= y; } -void Image::moveTo(int x, int y) { +void Image::moveBrushTo(int x, int y) { brushPosX = x; brushPosY = y; } @@ -234,11 +234,12 @@ int Image::getBrushY() const { return brushPosY; } -void Image::lineTo(int x, int y, Color col) { - line(brushPosX, brushPosY, brushPosX+x, brushPosY+y, col); +void Image::drawLineTo(int x, int y, Color col) { + drawLine(brushPosX, brushPosY, brushPosX+x, brushPosY+y, col); } void Image::setPixel(int x, int y, Number r, Number g, Number b, Number a) { + transformCoordinates(&x, &y); if(x < 0 || x > width || y < 0 || y > height) return; @@ -248,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; @@ -560,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) { @@ -612,7 +357,7 @@ void Image::swap(int *v1, int *v2) { *v2 = tv; } -void Image::line(int x0, int y0, int x1, int y1, Color col) { +void Image::drawLine(int x0, int y0, int x1, int y1, Color col) { bool steep = abs(y1 - y0) > abs(x1 - x0); if(steep) { swap(&x0, &y0); @@ -649,12 +394,20 @@ void Image::line(int x0, int y0, int x1, int y1, Color col) { } } -void Image::fill(Number r, Number g, Number b, Number a) { - Color color = Color(r,g,b,a); - 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; + } } } @@ -663,14 +416,35 @@ bool Image::saveImage(const String &fileName) { } void Image::premultiplyAlpha() { + unsigned int *imageData32 = (unsigned int*)imageData; + for(int x=0; x < width; x++) { for(int y=0; y < height; y++) { - unsigned int *imageData32 = (unsigned int*)imageData; - Color col = Color(imageData32[x+(y*width)]); - col.r *= col.a; - col.g *= col.a; - col.b *= col.a; - imageData32[x+(y*width)] = col.getUint(); + + unsigned int hex = imageData32[x+(y*width)]; + + int ta = (hex >> 24) & 0xFF; + int tb = (hex >> 16) & 0xFF; + int tg = (hex >> 8) & 0xFF; + int tr = (hex ) & 0xFF; + + Number r = ((Number)tr)/255.0f; + Number g = ((Number)tg)/255.0f; + Number b = ((Number)tb)/255.0f; + Number a = ((Number)ta)/255.0f; + + r *= a; + g *= a; + b *= a; + + unsigned int ir = 255.0f*r; + unsigned int ig = 255.0f*g; + unsigned int ib = 255.0f*b; + unsigned int ia = 255.0f*a; + + unsigned int newVal = ((ia & 0xFF) << 24) | ((ib & 0xFF) << 16) | ((ig & 0xFF) << 8) | (ir & 0xFF); + + imageData32[x+(y*width)] = newVal; } } } @@ -739,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) { @@ -760,35 +705,35 @@ bool Image::loadPNG(const String& fileName) { infile = OSBasics::open(fileName.c_str(), "rb"); if (!infile) { - Logger::log("Error opening png file\n"); + Logger::log("Error opening png file (\"%s\")\n", fileName.c_str()); return false; } OSBasics::read(sig, 1, 8, infile); if (!png_check_sig((unsigned char *) sig, 8)) { - Logger::log("Error reading png signature\n"); + Logger::log("Error reading png signature (\"%s\")\n", fileName.c_str()); OSBasics::close(infile); return false; } png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); if (!png_ptr) { - Logger::log("Error creating png struct\n"); + Logger::log("Error creating png struct (\"%s\")\n", fileName.c_str()); OSBasics::close(infile); return false; /* out of memory */ } info_ptr = png_create_info_struct(png_ptr); if (!info_ptr) { - Logger::log("Error creating info struct\n"); + Logger::log("Error creating info struct (\"%s\")\n", fileName.c_str()); png_destroy_read_struct(&png_ptr, (png_infopp) NULL, (png_infopp) NULL); OSBasics::close(infile); return false; /* out of memory */ } if (setjmp(png_jmpbuf(png_ptr))) { - Logger::log("Error setting jump thingie\n"); + Logger::log("Error setting jump thingie (\"%s\")\n", fileName.c_str()); png_destroy_read_struct(&png_ptr, &info_ptr, NULL); OSBasics::close(infile); return false; @@ -832,13 +777,13 @@ bool Image::loadPNG(const String& fileName) { /* Allocate the image_data buffer. */ if ((image_data = (char *) malloc(rowbytes * height))==NULL) { - Logger::log("Error allocating image memory\n"); + Logger::log("Error allocating image memory (\"%s\")\n", fileName.c_str()); png_destroy_read_struct(&png_ptr, &info_ptr, NULL); return false; } if ((row_pointers = (png_bytepp)malloc(height*sizeof(png_bytep))) == NULL) { - Logger::log("Error allocating image memory\n"); + Logger::log("Error allocating image memory (\"%s\")\n", fileName.c_str()); png_destroy_read_struct(&png_ptr, &info_ptr, NULL); free(image_data); image_data = NULL; @@ -857,3 +802,11 @@ bool Image::loadPNG(const String& fileName) { imageData = image_data; return true; } + +void Image::transformCoordinates(int *x, int *y) { + *y = this->height - *y - 1; +} + +void Image::transformCoordinates(int *x, int *y, int *w, int *h) { + *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 b7aa86526..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,9 +59,8 @@ ColorRange::ColorRange(Color color, unsigned int rangeStart, unsigned int rangeE } -Label::Label(Font *font, const String& text, int size, int antiAliasMode, bool premultiplyAlpha) : Image() { +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; this->premultiplyAlpha = premultiplyAlpha; @@ -77,8 +77,17 @@ 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; } int Label::getAntialiasMode() const { @@ -87,6 +96,7 @@ int Label::getAntialiasMode() const { void Label::setAntialiasMode(int newMode) { antiAliasMode = newMode; + _optionsChanged = true; } int Label::getTextWidthForString(const String& text) { @@ -186,6 +196,7 @@ void Label::setFont(Font *newFont) { if(!newFont) return; font = newFont; + _optionsChanged = true; } const String& Label::getText() const { @@ -194,10 +205,12 @@ const String& Label::getText() const { void Label::clearColors() { colorRanges.clear(); + _optionsChanged = true; } void Label::setColorForRange(Color color, unsigned int rangeStart, unsigned int rangeEnd) { colorRanges.push_back(ColorRange(color, rangeStart, rangeEnd)); + _optionsChanged = true; } Color Label::getColorForIndex(unsigned int index) { @@ -206,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) { @@ -259,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; } @@ -288,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); @@ -303,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)); @@ -318,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); @@ -358,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; @@ -374,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 ); } @@ -387,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 ); @@ -395,13 +480,17 @@ void Label::renderGlyphs(GlyphData *glyphData) { } } -void Label::setText(const String& text) { +bool Label::optionsChanged() { + return _optionsChanged; +} +void Label::setText(const String& text) { + if(!font) return; if(!font->isValid()) return; - + this->text = text; precacheGlyphs(text, &labelData); @@ -409,13 +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 c9b0ac284..c9b6c881c 100755 --- a/Core/Contents/Source/PolyLogger.cpp +++ b/Core/Contents/Source/PolyLogger.cpp @@ -20,37 +20,86 @@ 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; +} + +LoggerEvent::~LoggerEvent() { + +} + + +Logger::Logger() : EventDispatcher() { + logToFile = false; + logFile = NULL; +} + +Logger::~Logger() { + if(logFile) { + fclose(logFile); + } + overrideInstance = NULL; +} + +void Logger::logBroadcast(String message) { + dispatchEvent(new LoggerEvent(message), Event::NOTIFY_EVENT); + Logger::log(message.c_str()); +} + void Logger::logw(const char *str) { std::wcout << str << std::endl; } void Logger::log(const char *format, ...) { va_list args; - 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'; @@ -60,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 e693d9294..d5d5f5b97 100755 --- a/Core/Contents/Source/PolyMaterial.cpp +++ b/Core/Contents/Source/PolyMaterial.cpp @@ -25,6 +25,8 @@ #include "PolyShader.h" #include "PolyRenderer.h" #include "PolyCoreServices.h" +#include "PolyCore.h" +#include "PolyTexture.h" using namespace Polycode; @@ -33,17 +35,23 @@ Material::Material(const String& name) : Resource(Resource::RESOURCE_MATERIAL) { fp16RenderTargets = false; 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() { @@ -53,6 +61,10 @@ void Material::clearShaders() { delete materialShaders[i]; } */ + + for(int i=0; i < materialShaders.size(); i++) { + materialShaders[i]->removeAllHandlersForListener(this); + } materialShaders.clear(); for(int i=0; i < shaderBindings.size(); i++) { @@ -65,28 +77,119 @@ void Material::clearShaders() { } renderTargets.clear(); } + +void Material::recreateRenderTargets() { + for(int i=0; i < renderTargets.size(); i++) { + recreateRenderTarget(renderTargets[i]); + } +} + +void Material::recreateRenderTarget(ShaderRenderTarget *renderTarget) { + int textureWidth; + int textureHeight; + Texture *newTexture; + + if(renderTarget->sizeMode == ShaderRenderTarget::SIZE_MODE_NORMALIZED) { + Number safeWidth = renderTarget->width; + Number safeHeight = renderTarget->height; + if(safeWidth > 1.0) + safeWidth = 1.0; + if(safeHeight > 1.0) + safeHeight = 1.0; + + if(safeWidth < 0.0) + safeWidth = 0.0; + if(safeHeight < 0.0) + safeHeight = 0.0; + + if(renderTarget->normalizedWidth > 0 && renderTarget->normalizedHeight > 0) { + textureWidth = (int) (renderTarget->normalizedWidth * safeWidth); + textureHeight = (int) (renderTarget->normalizedHeight * safeHeight); + } else { + textureWidth = (int) (CoreServices::getInstance()->getCore()->getXRes() * safeWidth); + textureHeight = (int) (CoreServices::getInstance()->getCore()->getYRes() * safeHeight); + } + } else { + textureWidth = (int)renderTarget->width; + textureHeight = (int)renderTarget->height; + } + + CoreServices::getInstance()->getRenderer()->createRenderTextures(&newTexture, NULL, textureWidth, textureHeight, fp16RenderTargets); + newTexture->setResourceName(renderTarget->id); + + Texture *oldTexture = renderTarget->texture; + renderTarget->texture = newTexture; + + if(oldTexture) { + for(int i=0; i < shaderBindings.size(); i++) { + + for(int j=0; j < shaderBindings[i]->getNumRenderTargetBindings(); j++) { + if(shaderBindings[i]->getRenderTargetBinding(j)->texture == oldTexture) { + shaderBindings[i]->getRenderTargetBinding(j)->texture = newTexture; + shaderBindings[i]->clearTexture(shaderBindings[i]->getRenderTargetBinding(j)->name); + shaderBindings[i]->addTexture(shaderBindings[i]->getRenderTargetBinding(j)->name, newTexture); + } + } + } + + CoreServices::getInstance()->getRenderer()->destroyTexture(oldTexture); + } +} + +void Material::handleEvent(Event *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) { + if(shaderIndex >= 0 && shaderIndex < materialShaders.size()) { + materialShaders.erase(materialShaders.begin() + shaderIndex); + shaderBindings.erase(shaderBindings.begin() + shaderIndex); + } +} + +void Material::addShaderAtIndex(Shader *shader,ShaderBinding *shaderBinding, int shaderIndex) { + materialShaders.insert(materialShaders.begin()+shaderIndex, shader); + shaderBindings.insert(shaderBindings.begin()+shaderIndex, shaderBinding); + + shader->addEventListener(this, Event::RESOURCE_RELOAD_EVENT); + 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); + } + } +} void Material::addShader(Shader *shader,ShaderBinding *shaderBinding) { materialShaders.push_back(shader); shaderBindings.push_back(shaderBinding); - for(int i=0; i < shader->expectedFragmentParams.size(); i++) { - if(!shaderBinding->getLocalParamByName(shader->expectedFragmentParams[i].name)) { - if(!shader->expectedFragmentParams[i].isAuto) { - shaderBinding->addParam(shader->expectedFragmentParams[i].typeString, shader->expectedFragmentParams[i].name, shader->expectedFragmentParams[i].valueString); - } - } - } + shader->addEventListener(this, Event::RESOURCE_RELOAD_EVENT); + CoreServices::getInstance()->getRenderer()->setRendererShaderParams(shader, shaderBinding); - for(int i=0; i < shader->expectedVertexParams.size(); i++) { - if(!shaderBinding->getLocalParamByName(shader->expectedVertexParams[i].name)) { - if(!shader->expectedVertexParams[i].isAuto) { - shaderBinding->addParam(shader->expectedVertexParams[i].typeString, shader->expectedVertexParams[i].name, shader->expectedVertexParams[i].valueString); - } + 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); } } - - CoreServices::getInstance()->getRenderer()->setRendererShaderParams(shader, shaderBinding); } @@ -129,3 +232,11 @@ int Material::getNumShaderRenderTargets() { ShaderRenderTarget *Material::getShaderRenderTarget(unsigned int index) { return renderTargets[index]; } + +void Material::removeShaderRenderTarget(int index) { + if(index >= 0 && index < renderTargets.size()) { + ShaderRenderTarget *renderTarget = renderTargets[index]; + renderTargets.erase(renderTargets.begin() + index); + delete renderTarget; + } +} diff --git a/Core/Contents/Source/PolyMaterialManager.cpp b/Core/Contents/Source/PolyMaterialManager.cpp index 174d0ccd6..841427809 100755 --- a/Core/Contents/Source/PolyMaterialManager.cpp +++ b/Core/Contents/Source/PolyMaterialManager.cpp @@ -36,55 +36,66 @@ using std::vector; 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::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]); + } } -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() { +ShaderProgram *MaterialManager::createProgramFromFile(String programPath) { + OSFileEntry entry(programPath, OSFileEntry::TYPE_FILE); + 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(); + if(shaderModule->acceptsExtension(entry.extension)) { + ShaderProgram *newProgram = shaderModule->createProgramFromFile(entry.extension, entry.fullPath); + if(newProgram) { + newProgram->setResourcePath(programPath); + newProgram->setResourceName(programPath); + } + return newProgram; + } } + return NULL; } void MaterialManager::addShaderModule(PolycodeShaderModule *module) { shaderModules.push_back(module); } -Texture *MaterialManager::createTextureFromFile(const String& fileName, bool clamp, bool createMipmaps) { +#define DEFAULT_TEXTURE "default/default.png" + +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; } @@ -94,19 +105,15 @@ 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); + resourcePool->addResource(newTexture); } else { - Logger::log("Error loading image, using default texture.\n"); - delete image; - newTexture = getTextureByResourcePath("default/default.png"); - return newTexture; + Logger::log("Error loading image (\"%s\"), using default texture.\n", fileName.c_str()); + newTexture = (Texture*) CoreServices::getInstance()->getResourceManager()->getGlobalPool()->getResourceByPath(DEFAULT_TEXTURE); } delete image; - -// vector bits = fileName.split("/"); - - newTexture->setResourcePath(fileName); return newTexture; } @@ -117,7 +124,7 @@ Texture *MaterialManager::createFramebufferTexture(int width, int height, int ty Texture *MaterialManager::createNewTexture(int width, int height, bool clamp, bool createMipmaps, int type) { Image *newImage = new Image(width, height, type); - newImage->fill(1,1,1,1); + newImage->fill(Color(1,1,1,1)); Texture *retTexture = createTextureFromImage(newImage, clamp, createMipmaps); delete newImage; return retTexture; @@ -126,44 +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(); +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(resourcePool, name, vpName, fpName); + } } + + if(retShader) { + retShader->screenShader = screenShader; + retShader->numPointLights = 0; + retShader->numSpotLights = 0; + retShader->setResourceName(name); + } + + return retShader; } -void MaterialManager::registerShader(Shader *shader) { - shaders.push_back(shader); -} - -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::createShaderFromXMLNode(TiXmlNode *node) { +Shader *MaterialManager::createShaderFromXMLNode(ResourcePool *resourcePool, TiXmlNode *node) { TiXmlElement *nodeElement = node->ToElement(); if (!nodeElement) return NULL; // Skip comment nodes @@ -171,11 +178,10 @@ Shader *MaterialManager::createShaderFromXMLNode(TiXmlNode *node) { if(nodeElement->Attribute("type")) { String shaderType = nodeElement->Attribute("type"); -// Logger::log("Attempting to create %s shader\n", shaderType.c_str()); 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); } } } @@ -183,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")); @@ -202,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 @@ -221,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 @@ -244,31 +239,75 @@ Cubemap *MaterialManager::cubemapFromXMLNode(TiXmlNode *node) { Cubemap *newCubemap = NULL; String name = nodeElement->Attribute("name"); - String mapString = nodeElement->GetText(); - - vector maps = mapString.split(","); - if(maps.size() != 6) { - Logger::log("Error: A cubemap must contain 6 images \n"); - return NULL; - } - + String xPos = nodeElement->Attribute("xPos"); + String xNeg = nodeElement->Attribute("xNeg"); + String yPos = nodeElement->Attribute("yPos"); + String yNeg = nodeElement->Attribute("yNeg"); + String zPos = nodeElement->Attribute("zPos"); + String zNeg = nodeElement->Attribute("zNeg"); + newCubemap = CoreServices::getInstance()->getRenderer()->createCubemap( - (Texture*)CoreServices::getInstance()->getResourceManager()->getResource(Resource::RESOURCE_TEXTURE, maps[0]), - (Texture*)CoreServices::getInstance()->getResourceManager()->getResource(Resource::RESOURCE_TEXTURE, maps[1]), - (Texture*)CoreServices::getInstance()->getResourceManager()->getResource(Resource::RESOURCE_TEXTURE, maps[2]), - (Texture*)CoreServices::getInstance()->getResourceManager()->getResource(Resource::RESOURCE_TEXTURE, maps[3]), - (Texture*)CoreServices::getInstance()->getResourceManager()->getResource(Resource::RESOURCE_TEXTURE, maps[4]), - (Texture*)CoreServices::getInstance()->getResourceManager()->getResource(Resource::RESOURCE_TEXTURE, maps[5]) + CoreServices::getInstance()->getMaterialManager()->createTextureFromFile(xPos), + CoreServices::getInstance()->getMaterialManager()->createTextureFromFile(xNeg), + CoreServices::getInstance()->getMaterialManager()->createTextureFromFile(yPos), + CoreServices::getInstance()->getMaterialManager()->createTextureFromFile(yNeg), + CoreServices::getInstance()->getMaterialManager()->createTextureFromFile(zPos), + CoreServices::getInstance()->getMaterialManager()->createTextureFromFile(zNeg) ); newCubemap->setResourceName(name); return newCubemap; } -void MaterialManager::addMaterial(Material *material) { - materials.push_back(material); +std::vector MaterialManager::loadShadersFromFile(ResourcePool *resourcePool, String fileName) { + std::vector retVector; + + TiXmlDocument doc(fileName.c_str()); + doc.LoadFile(); + + if(doc.Error()) { + Logger::log("XML Error: %s\n", doc.ErrorDesc()); + } else { + TiXmlElement *mElem = doc.RootElement()->FirstChildElement("shaders"); + if(mElem) { + TiXmlNode* pChild; + for (pChild = mElem->FirstChild(); pChild != 0; pChild = pChild->NextSibling()) { + Shader *newShader = createShaderFromXMLNode(resourcePool, pChild); + if(newShader != NULL) { + Logger::log("Adding shader %s\n", newShader->getName().c_str()); + newShader->setResourceName(newShader->getName()); + retVector.push_back(newShader); + } + } + } + } + return retVector; } -std::vector MaterialManager::loadMaterialsFromFile(String fileName) { +std::vector MaterialManager::loadCubemapsFromFile(String fileName) { + std::vector retVector; + + TiXmlDocument doc(fileName.c_str()); + doc.LoadFile(); + + if(doc.Error()) { + Logger::log("XML Error: %s\n", doc.ErrorDesc()); + } else { + TiXmlElement *mElem = doc.RootElement()->FirstChildElement("cubemaps"); + if(mElem) { + TiXmlNode* pChild; + for (pChild = mElem->FirstChild(); pChild != 0; pChild = pChild->NextSibling()) { + Cubemap *newCubemap = cubemapFromXMLNode(pChild); + if (newCubemap) { + retVector.push_back(newCubemap); + } + } + } + } + + return retVector; +} + +std::vector MaterialManager::loadMaterialsFromFile(ResourcePool *resourcePool, const String &fileName) { std::vector retVector; TiXmlDocument doc(fileName.c_str()); @@ -281,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); } @@ -292,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(); @@ -305,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 @@ -316,10 +356,21 @@ Material *MaterialManager::materialFromXMLNode(TiXmlNode *node) { vector materialShaders; vector newShaderBindings; - vector renderTargets; + vector renderTargets; 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")); @@ -352,24 +403,19 @@ Material *MaterialManager::materialFromXMLNode(TiXmlNode *node) { newTarget->height = atof(pChildElement->Attribute("height")); if(pChildElement->Attribute("sizeMode")) { if(strcmp(pChildElement->Attribute("sizeMode"), "normalized") == 0) { + newTarget->sizeMode = ShaderRenderTarget::SIZE_MODE_NORMALIZED; if(newTarget->width > 1.0f) newTarget->width = 1.0f; if(newTarget->height > 1.0f) newTarget->height = 1.0f; - - newTarget->width = ((Number)CoreServices::getInstance()->getRenderer()->getXRes()) * newTarget->width; - newTarget->height = ((Number)CoreServices::getInstance()->getRenderer()->getYRes()) * newTarget->height; } } - } -// Texture *newTexture = CoreServices::getInstance()->getMaterialManager()->createNewTexture(newTarget->width, newTarget->height, true); - Texture *newTexture, *temp; - CoreServices::getInstance()->getRenderer()->createRenderTextures(&newTexture, &temp, (int)newTarget->width, (int)newTarget->height, newMaterial->fp16RenderTargets); - newTexture->setResourceName(newTarget->id); - //CoreServices::getInstance()->getResourceManager()->addResource(newTexture); - newTarget->texture = newTexture; + } + + newTarget->normalizedWidth = -1; + newTarget->normalizedHeight = -1; + newMaterial->recreateRenderTarget(newTarget); renderTargets.push_back(newTarget); - } } } @@ -380,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); @@ -396,9 +442,15 @@ Material *MaterialManager::materialFromXMLNode(TiXmlNode *node) { if(strcmp(pChild2->Value(), "param") == 0){ String pname = pChild2Element->Attribute("name"); - String ptype = pChild2Element->Attribute("type"); - String pvalue = pChild2Element->Attribute("value"); - newShaderBinding->addParam(ptype, pname, pvalue); + + if(!CoreServices::getInstance()->getRenderer()->getDataPointerForName(pname)) { + String pvalue = pChild2Element->Attribute("value"); + int type = materialShader->getExpectedParamType(pname); + LocalShaderParam *param = newShaderBinding->addParam(type, pname); + if(param) { + param->setParamValueFromString(type, pvalue); + } + } } } } @@ -418,21 +470,21 @@ Material *MaterialManager::materialFromXMLNode(TiXmlNode *node) { } String mode = pChild2Element->Attribute("mode"); if(strcmp(mode.c_str(), "in") == 0) { - newBinding->mode = RenderTargetBinding::MODE_IN; + newBinding->mode = RenderTargetBinding::MODE_IN; + } else if(strcmp(mode.c_str(), "color") == 0) { + newBinding->mode = RenderTargetBinding::MODE_COLOR; + } else if(strcmp(mode.c_str(), "depth") == 0) { + newBinding->mode = RenderTargetBinding::MODE_DEPTH; } else { newBinding->mode = RenderTargetBinding::MODE_OUT; } newShaderBinding->addRenderTargetBinding(newBinding); - //Texture *texture = (Texture*)CoreServices::getInstance()->getResourceManager()->getResource(Resource::RESOURCE_TEXTURE, newBinding->id); -// newBinding->texture = texture; for(int l=0; l < renderTargets.size(); l++) { if(renderTargets[l]->id == newBinding->id) { printf("Assigning texture to %s\n", newBinding->id.c_str()); newBinding->texture = renderTargets[l]->texture; - newBinding->width = renderTargets[l]->width; - newBinding->height = renderTargets[l]->height; } } @@ -462,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/PolyMatrix4.cpp b/Core/Contents/Source/PolyMatrix4.cpp index d268d4144..ebc5a5bb6 100755 --- a/Core/Contents/Source/PolyMatrix4.cpp +++ b/Core/Contents/Source/PolyMatrix4.cpp @@ -36,7 +36,7 @@ Matrix4::Matrix4(const Number *m) { memcpy(ml, m, sizeof(Number)*16); } -Matrix4 Matrix4::inverse() const +Matrix4 Matrix4::Inverse() const { Number m00 = m[0][0], m01 = m[0][1], m02 = m[0][2], m03 = m[0][3]; Number m10 = m[1][0], m11 = m[1][1], m12 = m[1][2], m13 = m[1][3]; diff --git a/Core/Contents/Source/PolyMesh.cpp b/Core/Contents/Source/PolyMesh.cpp index 8e1ff2a8b..8a7d2eaee 100755 --- a/Core/Contents/Source/PolyMesh.cpp +++ b/Core/Contents/Source/PolyMesh.cpp @@ -1,831 +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() { - 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 1ffa0bc11..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; @@ -251,11 +267,11 @@ void Object::createFromXMLElement(TiXmlElement *element, ObjectEntry *entry) { if (endResult == success) { // If integer part exhausts string entry->type = ObjectEntry::INT_ENTRY; entry->NumberVal = entry->intVal; - entry->boolVal = entry->intVal; + entry->boolVal = entry->intVal != 0; } else { entry->NumberVal = strtod(rawVal, &endResult); entry->intVal = entry->NumberVal; - entry->boolVal = entry->NumberVal; + entry->boolVal = entry->NumberVal != 0.0; if (endResult == success) { entry->type = ObjectEntry::FLOAT_ENTRY; } 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 7b6fb55a0..0db228d6b 100755 --- a/Core/Contents/Source/PolyParticleEmitter.cpp +++ b/Core/Contents/Source/PolyParticleEmitter.cpp @@ -22,528 +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) -: ParticleEmitter(materialName, particleMesh, particleType, emitterType, lifespan, numParticles, direction, gravity, deviation, emitterRadius), -SceneEntity() -{ - 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::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); + } +} + +unsigned int SceneParticleEmitter::getParticleCount() const { + return particleCount; +} + +unsigned int SceneParticleEmitter::getParticleType() const { + return particleType; +} + +void SceneParticleEmitter::setUseFloorPlane(bool val) { + useFloorPlane = val; +} + +void SceneParticleEmitter::setParticlesInWorldSpace(bool val) { + particlesInWorldSpace = val; +} + +void SceneParticleEmitter::setParticleLifetime(Number lifetime) { + this->lifetime = lifetime; + for(int i=0; i < particles.size(); i++) { + resetParticle(i); + particles[i].lifetime = RANDOM_NUMBER * -lifetime; + } +} + +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; + } + } } -void SceneParticleEmitter::addParticleBody(Entity *particleBody) { - addEntity((SceneEntity*)particleBody); - particleBody->editorOnly = true; +Vector3 SceneParticleEmitter::getDirectionDeviation() const { + return directionDeviation; } -void SceneParticleEmitter::dispatchTriggerCompleteEvent() { - ((EventDispatcher*)this)->dispatchEvent(new Event(Event::COMPLETE_EVENT), Event::COMPLETE_EVENT); +Vector3 SceneParticleEmitter::getPerlinValue() const { + return perlinValue; } -Matrix4 SceneParticleEmitter::getBaseMatrix() { - rebuildTransformMatrix(); - return getConcatenatedMatrix(); +bool SceneParticleEmitter::getPerlinEnabled() const { + return perlinEnabled; } -void SceneParticleEmitter::Update() { - updateEmitter(); +Vector3 SceneParticleEmitter::getEmitterSize() const { + return emitterSize; } - - -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) - : ParticleEmitter(imageFile, particleMesh, particleType, emitterType, lifespan, numParticles, direction, gravity, deviation, emitterRadius), -ScreenEntity() -{ - 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]; - } -} - -Entity *ScreenParticleEmitter::Clone(bool deepClone, bool ignoreEditorOnly) { - 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) { - ScreenParticleEmitter *_clone = (ScreenParticleEmitter*) clone; - - _clone->emitterRadius = this->emitterRadius; - _clone->dirVector = this->dirVector; - _clone->gravVector = this->gravVector; - _clone->deviation = this->deviation; - - _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; - - _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); -} - -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; - this->emitterType = emitterType; - this->emitSpeed = 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; -} - -Texture *ParticleEmitter::getParticleTexture() { - 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 = true; - 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(); + +bool SceneParticleEmitter::getParticlesInWorldSpace() const { + return particlesInWorldSpace; +} + +Number SceneParticleEmitter::getParticleSize() const { + return particleSize; +} + +void SceneParticleEmitter::setFloorPlaneOffset(Number floorPlaneOffset) { + this->floorPlaneOffset = floorPlaneOffset; +} + +void SceneParticleEmitter::setParticleDirection(const Vector3 &direction) { + directionVector = direction; +} + +Vector3 SceneParticleEmitter::getParticleDirection() const { + return directionVector; +} + +void SceneParticleEmitter::setFloorDamping(Number floorDamping) { + this->floorDamping = floorDamping; +} + +Vector3 SceneParticleEmitter::getParticleRotationSpeed() const { + return particleRotationSpeed; +} + +void SceneParticleEmitter::setPerlinEnabled(bool val) { + perlinEnabled = val; } -void ParticleEmitter::dispatchTriggerCompleteEvent() { -} - -void ParticleEmitter::addParticleBody(Entity *particleBody) { -} - -Matrix4 ParticleEmitter::getBaseMatrix() { - return Matrix4(); -} - -void ParticleEmitter::setEmitterRadius(Vector3 rad) { - emitterRadius = rad; -} - -void ParticleEmitter::setRotationSpeed(Number speed) { - rotationSpeed = speed; -} - -void ParticleEmitter::setParticleVisibility(bool val) { - for(int i=0;i < particles.size(); i++) { - particles[i]->particleBody->visible = val; - } +Number SceneParticleEmitter::getParticleSpeed() const { + return particleSpeed; } -void ParticleEmitter::setParticleBlendingMode(int mode) { - blendingMode = mode; - for(int i=0;i < particles.size(); i++) { - particles[i]->particleBody->setBlendingMode(mode); - } +void SceneParticleEmitter::setParticleSpeed(Number speed) { + particleSpeed = speed; } -int ParticleEmitter::getParticleBlendingMode() const { - return blendingMode; +void SceneParticleEmitter::setPerlinValue(const Vector3 &perlinValue) { + this->perlinValue = perlinValue; } -void ParticleEmitter::setAlphaTest(bool val) { - for(int i=0;i < particles.size(); i++) { - particles[i]->particleBody->alphaTest = val; - } -} - -void ParticleEmitter::setDepthWrite(bool val) { - for(int i=0;i < particles.size(); i++) { - particles[i]->particleBody->depthWrite = val; - } -} - -void ParticleEmitter::setDepthTest(bool val) { - for(int i=0;i < particles.size(); i++) { - particles[i]->particleBody->depthTest= val; - } -} - - -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 = true; - 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 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); + } -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 = 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); - - particle->particleBody->setPosition(concatMatrix.getPosition()); -// 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 = 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; - } - } - } + 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 fb7dee6a3..4108a35d2 100755 --- a/Core/Contents/Source/PolyPeer.cpp +++ b/Core/Contents/Source/PolyPeer.cpp @@ -27,11 +27,37 @@ THE SOFTWARE. using namespace Polycode; -void PeerConnection::ackPackets(unsigned int ack) { - for(int i=0; i < reliablePacketQueue.size(); i++) { - if(reliablePacketQueue[i].packet->header.sequence == ack) { - delete reliablePacketQueue[i].packet; - reliablePacketQueue.erase(reliablePacketQueue.begin()+i); +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();) { + if((*it).packet->header.sequence == ack) { + delete (*it).packet; + it = reliablePacketQueue.erase(it); + } else { + ++it; + } + } +} + +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() { @@ -83,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) @@ -90,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++; @@ -105,15 +151,13 @@ 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++; + Packet *packet = createPacket(target, data, size, type); sendPacket(target, packet); - + SentPacketEntry entry; entry.packet = packet; entry.timestamp = CoreServices::getInstance()->getCore()->getTicks(); -// connection->reliablePacketQueue.push_back(entry); + connection->reliablePacketQueue.push_back(entry); } @@ -141,33 +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; } - for(int i=0; i < peerConnections.size(); i++) { - peerConnections[i]->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) { @@ -189,9 +217,14 @@ 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) { + for(int j=0; j < peerConnections[i]->reliablePacketQueue.size(); j++) { + 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/PolyPerlin.cpp b/Core/Contents/Source/PolyPerlin.cpp index d7e57417a..b2bad9b68 100755 --- a/Core/Contents/Source/PolyPerlin.cpp +++ b/Core/Contents/Source/PolyPerlin.cpp @@ -228,7 +228,6 @@ void Perlin::init(void) Number Perlin::perlin_noise_2D(Number vec[2]) { int terms = mOctaves; - Number freq = mFrequency; Number result = 0.0f; Number amp = mAmplitude; @@ -250,7 +249,6 @@ Number Perlin::perlin_noise_2D(Number vec[2]) Number Perlin::perlin_noise_3D(Number vec[3]) { int terms = mOctaves; - Number freq = mFrequency; Number result = 0.0f; Number amp = mAmplitude; 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/PolyQuaternion.cpp b/Core/Contents/Source/PolyQuaternion.cpp index 088ea827c..7bb63eef6 100755 --- a/Core/Contents/Source/PolyQuaternion.cpp +++ b/Core/Contents/Source/PolyQuaternion.cpp @@ -178,7 +178,7 @@ Matrix4 Quaternion::createMatrix() const return w*w+x*x+y*y+z*z; } - Number Quaternion::normalize() + Number Quaternion::Normalize() { Number len = Norm(); Number factor = 1.0f / sqrtf(len); @@ -222,7 +222,7 @@ Matrix4 Quaternion::createMatrix() const // have method to fix this case, so just use linear interpolation here. Quaternion t = (1.0f - fT) * rkP + fT * rkT; // taking the complement requires renormalisation - t.normalize(); + t.Normalize(); return t; } } 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/PolyRectangle.cpp b/Core/Contents/Source/PolyRectangle.cpp index 8f0bb20fb..a57c14311 100755 --- a/Core/Contents/Source/PolyRectangle.cpp +++ b/Core/Contents/Source/PolyRectangle.cpp @@ -21,6 +21,7 @@ */ #include "PolyRectangle.h" +#include // for min/max using namespace Polycode; @@ -30,3 +31,24 @@ void Rectangle::setRect(Number x, Number y, Number w, Number h) { this->w = w; this->h = h; } + +Rectangle Rectangle::Clipped(const Rectangle& rect) const +{ + Rectangle result; + + result.x = std::min(std::max(x, rect.x), rect.maxX()); + result.w = std::max(std::min(maxX(), rect.maxX()), result.x) - result.x; + + result.y = std::min(std::max(y, rect.y), rect.maxY()); + result.h = std::max(std::min(maxY(), rect.maxY()), result.y) - result.y; + + return result; +} + +bool Rectangle::operator==(const Rectangle& rect) const +{ + if( x == rect.x && y == rect.y && w == rect.w && h == rect.h) + return true; + + return false; +} 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 893473368..dc3b3c4f6 100755 --- a/Core/Contents/Source/PolyRenderer.cpp +++ b/Core/Contents/Source/PolyRenderer.cpp @@ -21,40 +21,122 @@ */ #include "PolyRenderer.h" +#include "PolyFixedShader.h" +#include "PolyMaterial.h" +#include "PolyModule.h" #include "PolyMesh.h" using namespace Polycode; -Renderer::Renderer() : currentTexture(NULL), xRes(0), yRes(0), renderMode(0), orthoMode(false), lightingEnabled(false), clearColor(0.2f, 0.2f, 0.2f, 0.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; numLights = 0; exposureLevel = 1; shadersEnabled = true; - currentFrameBufferTexture = NULL; - previousFrameBufferTexture = NULL; currentMaterial = NULL; numLights = 0; - numAreaLights = 0; + numPointLights = 0; numSpotLights = 0; exposureLevel = 1; shadersEnabled = true; currentShaderModule = NULL; - currentFrameBufferTexture = NULL; - previousFrameBufferTexture = 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() { } +bool Renderer::Init() { + initOSSpecific(); + + 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; } @@ -65,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, 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); - 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) { @@ -228,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++; @@ -261,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); @@ -284,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; } @@ -302,6 +260,12 @@ Polycode::Rectangle Renderer::getScissorBox() { return scissorBox; } +void Renderer::setViewportShift(Number shiftX, Number shiftY) { + viewportShift.x = shiftX; + viewportShift.y = shiftY; + resetViewport(); +} + bool Renderer::isScissorEnabled() { return scissorEnabled; } @@ -343,34 +307,67 @@ void *Renderer::getDataPointerForName(const String &name) { if(name == "ambient_color") { return (void*)&ambientColor; } + return NULL; } void Renderer::setRendererShaderParams(Shader *shader, ShaderBinding *binding) { - for(int i=0; i < shader->expectedFragmentParams.size(); i++) { - if(shader->expectedFragmentParams[i].isAuto) { - binding->addLocalParam(shader->expectedFragmentParams[i].name, getDataPointerForName(shader->expectedFragmentParams[i].name)); + for(int i=0; i < shader->expectedParams.size(); i++) { + void *dataPtr = getDataPointerForName(shader->expectedParams[i].name); + if(dataPtr) { + binding->addParamPointer(shader->expectedParams[i].type, shader->expectedParams[i].name, dataPtr); } } +} - for(int i=0; i < shader->expectedVertexParams.size(); i++) { - if(shader->expectedVertexParams[i].isAuto) { - binding->addLocalParam(shader->expectedVertexParams[i].name, getDataPointerForName(shader->expectedVertexParams[i].name)); - } +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(); + } + + 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::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; - } - pushRenderDataArray(mesh->renderDataArrays[arrayType]); +void Renderer::setBackingResolutionScale(Number xScale, Number yScale) { + backingResolutionScaleX = xScale; + backingResolutionScaleY = yScale; } int Renderer::getXRes() { @@ -393,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 7117de1f8..afd525a5e 100755 --- a/Core/Contents/Source/PolyResource.cpp +++ b/Core/Contents/Source/PolyResource.cpp @@ -21,15 +21,25 @@ */ #include "PolyResource.h" +#include "PolyCoreServices.h" +#include "PolyResourceManager.h" using namespace Polycode; -Resource::Resource(int type) { +bool Resource::defaultReloadOnFileModify = false; + +Resource::Resource(int type) : EventDispatcher() { this->type = type; + reloadOnFileModify = defaultReloadOnFileModify; + resourceFileTime = 0; } Resource::~Resource() { + CoreServices::getInstance()->getResourceManager()->removeResource(this); +} +void Resource::reloadResource() { + dispatchEvent(new Event(), Event::RESOURCE_RELOAD_EVENT); } const String& Resource::getResourceName() const { diff --git a/Core/Contents/Source/PolyResourceManager.cpp b/Core/Contents/Source/PolyResourceManager.cpp index 9888f7206..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,83 +39,256 @@ using std::vector; using namespace Polycode; -ResourceManager::ResourceManager() { +bool ResourcePool::defaultReloadResourcesOnModify = false; + +ResourcePool::ResourcePool(const String &name, ResourcePool *fallbackPool) { + + this->name = name; + this->fallbackPool = fallbackPool; + dispatchChangeEvents = false; + reloadResourcesOnModify = ResourcePool::defaultReloadResourcesOnModify; + ticksSinceCheck = 0; + resourceSubscribers = 0; + deleteOnUnsubscribe = false; +} + +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;iaddEventListener(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; + } +} + +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; + } + } + } +} + +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 < resources.size(); i++) { - delete resources[i]; - } - resources.clear(); + printf("Shutting down resource manager...\n"); + PHYSFS_deinit(); + + for(int i=0; i < pools.size(); i++) { + delete pools[i]; + } + pools.clear(); } -void ResourceManager::parseShaders(const String& dirPath, bool recursive) { +void ResourcePool::Update(int elapsed) { + if(!reloadResourcesOnModify) + return; + + ticksSinceCheck += elapsed; + if(ticksSinceCheck > RESOURCE_CHECK_INTERVAL) { + ticksSinceCheck = 0; + checkForChangedFiles(); + } +} + +void ResourceManager::parseShadersIntoPool(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 == "mat") { - Logger::log("Adding shaders from %s\n", resourceDir[i].nameWithoutExtension.c_str()); - TiXmlDocument doc(resourceDir[i].fullPath.c_str()); - doc.LoadFile(); - if(doc.Error()) { - Logger::log("XML Error: %s\n", doc.ErrorDesc()); - } else { - TiXmlElement *mElem = doc.RootElement()->FirstChildElement("shaders"); - - if(mElem) { - TiXmlNode* pChild; - for (pChild = mElem->FirstChild(); pChild != 0; pChild = pChild->NextSibling()) { - Shader *newShader = CoreServices::getInstance()->getMaterialManager()->createShaderFromXMLNode(pChild); - if(newShader != NULL) { - Logger::log("Adding shader %s\n", newShader->getName().c_str()); - newShader->setResourceName(newShader->getName()); - resources.push_back(newShader); - CoreServices::getInstance()->getMaterialManager()->registerShader(newShader); - } - } - } + MaterialManager *materialManager = CoreServices::getInstance()->getMaterialManager(); + std::vector shaders = materialManager->loadShadersFromFile(pool, resourceDir[i].fullPath); + + for(int s=0; s < shaders.size(); 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++) { if(resourceDir[i].type == OSFileEntry::TYPE_FILE) { - for(int m=0; m < shaderModules.size(); m++) { - PolycodeShaderModule *shaderModule = shaderModules[m]; - if(shaderModule->acceptsExtension(resourceDir[i].extension)) { - Resource *newProgram = shaderModule->createProgramFromFile(resourceDir[i].extension, resourceDir[i].fullPath); - if(newProgram) { - newProgram->setResourceName(resourceDir[i].name); - newProgram->setResourcePath(resourceDir[i].fullPath); - resources.push_back(newProgram); - } - } - } + MaterialManager *materialManager = CoreServices::getInstance()->getMaterialManager(); + + ShaderProgram *newProgram = materialManager->createProgramFromFile(resourceDir[i].fullPath); + if(newProgram) { + newProgram->setResourceName(resourceDir[i].name); + newProgram->setResourcePath(resourceDir[i].fullPath); + 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); @@ -122,104 +296,83 @@ 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()); - resources.push_back(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); for(int i=0; i < resourceDir.size(); i++) { if(resourceDir[i].type == OSFileEntry::TYPE_FILE) { if(resourceDir[i].extension == "mat") { - Logger::log("Adding cubemaps from %s\n", resourceDir[i].nameWithoutExtension.c_str()); - TiXmlDocument doc(resourceDir[i].fullPath.c_str()); - doc.LoadFile(); - if(doc.Error()) { - Logger::log("XML Error: %s\n", doc.ErrorDesc()); - } else { - TiXmlElement *mElem = doc.RootElement()->FirstChildElement("cubemaps"); - - if(mElem) { - TiXmlNode* pChild; - for (pChild = mElem->FirstChild(); pChild != 0; pChild = pChild->NextSibling()) { - Cubemap *newMat = CoreServices::getInstance()->getMaterialManager()->cubemapFromXMLNode(pChild); - // newMat->setResourceName(newMat->getName()); - if(newMat) - resources.push_back(newMat); - } - } - } + + MaterialManager *materialManager = CoreServices::getInstance()->getMaterialManager(); + std::vector cubemaps = materialManager->loadCubemapsFromFile(resourceDir[i].fullPath); + for(int c=0; c < cubemaps.size(); c++) { + pool->addResource(cubemaps[c]); + } } } else { if(recursive) - parseCubemaps(dirPath+"/"+resourceDir[i].name, true); + parseCubemapsIntoPool(pool, dirPath+"/"+resourceDir[i].name, true); } } } -void ResourceManager::addResource(Resource *resource) { - resources.push_back(resource); +void ResourceManager::handleEvent(Event *event) { + if(event->getEventCode() == Event::RESOURCE_CHANGE_EVENT) { + dispatchEvent(new Event(), Event::CHANGE_EVENT); + } +} + +ResourcePool *ResourceManager::getGlobalPool() { + return globalPool; } -void ResourceManager::parseTextures(const String& dirPath, bool recursive, const String& basePath) { +void ResourceManager::parseTexturesIntoPool(ResourcePool *pool, const String& dirPath, bool recursive, const String& basePath) { + MaterialManager *materialManager = CoreServices::getInstance()->getMaterialManager(); 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 == "png") { Logger::log("Adding texture %s\n", resourceDir[i].nameWithoutExtension.c_str()); - Texture *t = CoreServices::getInstance()->getMaterialManager()->createTextureFromFile(resourceDir[i].fullPath); + Texture *t = materialManager->createTextureFromFile(resourceDir[i].fullPath, materialManager->clampDefault, materialManager->mipmapsDefault); if(t) { if(basePath == "") { - t->setResourceName(resourceDir[i].name); + t->setResourceName(resourceDir[i].name); + t->setResourcePath(resourceDir[i].fullPath); } else { t->setResourceName(basePath+"/"+resourceDir[i].name); + t->setResourcePath(resourceDir[i].fullPath); } - resources.push_back(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()); @@ -232,43 +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::getResource(int resourceType, const String& resourceName) const { - Logger::log("requested %s\n", resourceName.c_str()); - for(int i =0; i < resources.size(); i++) { -// Logger::log("is it %s?\n", resources[i]->getResourceName().c_str()); - 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; +void ResourceManager::addResourcePool(ResourcePool *pool) { + pools.push_back(pool); } -// Would it make more sense to pass back, like, something like an ObjectEntry here? Lua hates vectors. -vector ResourceManager::getResources(int resourceType) { - vector result; - Logger::log("requested all of type %d\n", resourceType); - for(int i =0; i < resources.size(); i++) { - // Logger::log("is it %s?\n", resources[i]->getResourceName().c_str()); - if(resources[i]->getResourceType() == resourceType) { - result.push_back(resources[i]); - } +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::removeResourcePool(ResourcePool *pool) { + for(int i=0; i < pools.size(); i++) { + if(pools[i] == pool) { + pools.erase(pools.begin()+i); + return; + } + } +} + +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; + + 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 2bbe5343f..831ef3e8f 100644 --- a/Core/Contents/Source/PolySDLCore.cpp +++ b/Core/Contents/Source/PolySDLCore.cpp @@ -32,6 +32,10 @@ #include "PolyRectangle.h" #include +#include +#include +#include + #include #include @@ -40,6 +44,27 @@ #include #include +#ifdef USE_X11 + // SDL scrap + #define T(A, B, C, D) (int)((A<<24)|(B<<16)|(C<<8)|(D<<0)) + + int init_scrap(void); + int lost_scrap(void); + void put_scrap(int type, int srclen, const char *src); + void get_scrap(int type, int *dstlen, char **dst); + // end SDL scrap + +// X11 cursor +#include + +namespace { + void set_cursor(int cursorType); + void free_cursors(); +} // namespace +// end X11 cursor + +#endif + using namespace Polycode; using std::vector; @@ -55,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; @@ -75,7 +100,7 @@ SDLCore::SDLCore(PolycodeView *view, int _xRes, int _yRes, bool fullScreen, bool setenv("SDL_VIDEO_CENTERED", "1", 1); } - if(SDL_Init(SDL_INIT_VIDEO) < 0) { + if(SDL_Init(SDL_INIT_VIDEO|SDL_INIT_JOYSTICK) < 0) { } eventMutex = createMutex(); @@ -88,11 +113,27 @@ SDLCore::SDLCore(PolycodeView *view, int _xRes, int _yRes, bool fullScreen, bool SDL_EnableUNICODE(1); SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL); - ((OpenGLRenderer*)renderer)->initOSSpecific(); + SDL_JoystickEventState(SDL_ENABLE); + + int numJoysticks = SDL_NumJoysticks(); + + for(int i=0; i < numJoysticks; i++) { + SDL_JoystickOpen(i); + input->addJoystick(i); + } + +#ifdef USE_X11 + // Start listening to clipboard events. + // (Yes on X11 you need to actively listen to + // clipboard events and respond to them) + init_scrap(); +#endif // USE_X11 + + ((OpenGLRenderer*)renderer)->Init(); 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; @@ -113,8 +154,6 @@ void SDLCore::setVideoMode(int xRes, int yRes, bool fullScreen, bool vSync, int SDL_GL_SetAttribute( SDL_GL_MULTISAMPLESAMPLES, 0); } - SDL_GL_SetAttribute(SDL_GL_SWAP_CONTROL, 0); - flags = SDL_OPENGL; if(fullScreen) { @@ -124,6 +163,16 @@ void SDLCore::setVideoMode(int xRes, int yRes, bool fullScreen, bool vSync, int if(resizableWindow) { flags |= SDL_RESIZABLE; } +/* + if(vSync) { + flags |= SDL_DOUBLEBUF; + SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); + SDL_GL_SetAttribute(SDL_GL_SWAP_CONTROL, 1); + } else { + SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 0); + SDL_GL_SetAttribute(SDL_GL_SWAP_CONTROL, 0); + } +*/ SDL_SetVideoMode(xRes, yRes, 0, flags); renderer->Resize(xRes, yRes); @@ -147,6 +196,9 @@ vector SDLCore::getVideoModes() { } SDLCore::~SDLCore() { +#ifdef USE_X11 + free_cursors(); +#endif // USE_X11 SDL_Quit(); } @@ -210,6 +262,15 @@ void SDLCore::enableMouse(bool newval) { Core::enableMouse(newval); } +void SDLCore::captureMouse(bool newval) { + if(newval) { + SDL_WM_GrabInput(SDL_GRAB_ON); + } else { + SDL_WM_GrabInput(SDL_GRAB_OFF); + } + Core::captureMouse(newval); +} + bool SDLCore::checkSpecialKeyEvents(PolyKEY key) { if(key == KEY_a && (input->getKeyState(KEY_LCTRL) || input->getKeyState(KEY_RCTRL))) { @@ -245,14 +306,19 @@ bool SDLCore::checkSpecialKeyEvents(PolyKEY key) { return false; } -bool SDLCore::Update() { +void SDLCore::Render() { + renderer->BeginRender(); + services->Render(); + renderer->EndRender(); + SDL_GL_SwapBuffers(); +} + +bool SDLCore::systemUpdate() { if(!running) return false; + doSleep(); - renderer->BeginRender(); updateCore(); - renderer->EndRender(); - SDL_GL_SwapBuffers(); SDL_Event event; while ( SDL_PollEvent(&event) ) { @@ -272,8 +338,23 @@ bool SDLCore::Update() { renderer->Resize(xRes, yRes); dispatchEvent(new Event(), EVENT_CORE_RESIZE); break; + case SDL_ACTIVEEVENT: + if(event.active.state == SDL_APPINPUTFOCUS) { + if(event.active.gain == 1) { + gainFocus(); + } else { + loseFocus(); + } + } + break; + case SDL_JOYAXISMOTION: + input->joystickAxisMoved(event.jaxis.axis, ((Number)event.jaxis.value)/32767.0, event.jaxis.which); + break; case SDL_JOYBUTTONDOWN: -// input->setKeyState((PolyKEY)(event.key.keysym.sym), true); + input->joystickButtonDown(event.jbutton.button, event.jbutton.which); + break; + case SDL_JOYBUTTONUP: + input->joystickButtonUp(event.jbutton.button, event.jbutton.which); break; case SDL_KEYDOWN: if(!checkSpecialKeyEvents((PolyKEY)(event.key.keysym.sym))) { @@ -326,12 +407,13 @@ bool SDLCore::Update() { break; } } - doSleep(); return running; } void SDLCore::setCursor(int cursorType) { - +#ifdef USE_X11 + set_cursor(cursorType); +#endif // USE_X11 } void SDLCore::warpCursor(int x, int y) { @@ -356,11 +438,21 @@ CoreMutex *SDLCore::createMutex() { } void SDLCore::copyStringToClipboard(const String& str) { - +#ifdef USE_X11 + put_scrap(T('T', 'E', 'X', 'T'), str.size(), str.c_str()); +#endif } String SDLCore::getClipboardString() { - return ""; +#ifdef USE_X11 + int dstlen; + char* buffer; + get_scrap(T('T', 'E', 'X', 'T'), &dstlen, &buffer); + + String rval(buffer, dstlen); + free(buffer); + return rval; +#endif } void SDLCore::createFolder(const String& folderPath) { @@ -371,7 +463,7 @@ void SDLCore::copyDiskItem(const String& itemPath, const String& destItemPath) { int childExitStatus; pid_t pid = fork(); if (pid == 0) { - execl("/bin/cp", "/bin/cp", "-R", itemPath.c_str(), destItemPath.c_str(), (char *)0); + execl("/bin/cp", "/bin/cp", "-RT", itemPath.c_str(), destItemPath.c_str(), (char *)0); } else { pid_t ws = waitpid( pid, &childExitStatus, 0); } @@ -398,14 +490,461 @@ void SDLCore::removeDiskItem(const String& itemPath) { } String SDLCore::openFolderPicker() { - + String r = ""; + return r; } vector SDLCore::openFilePicker(vector extensions, bool allowMultiple) { + vector r; + return r; +} +String SDLCore::saveFilePicker(std::vector extensions) { + String r = ""; + return r; } void SDLCore::resizeTo(int xRes, int yRes) { renderer->Resize(xRes, yRes); } + +#ifdef USE_X11 +// SDL_scrap.c +// Credits to Sam Lantinga for making this +// Changes include: +// - All non-X11 stuff was removed +// - Uses the X11 CLIPBOARD atom in addition to PRIMARY +// ======================================= + + +/* Handle clipboard text and data in arbitrary formats */ + +/* Miscellaneous defines */ +#define PUBLIC +#define PRIVATE static + +#define X11_SCRAP + +typedef Atom scrap_type; + +static Display *SDL_Display; +static Window SDL_Window; +static void (*Lock_Display)(void); +static void (*Unlock_Display)(void); + +#define FORMAT_PREFIX "SDL_scrap_0x" + +PRIVATE scrap_type +convert_format(int type) +{ + switch (type) + { + + case T('T', 'E', 'X', 'T'): + return XA_STRING; + + default: + { + char format[sizeof(FORMAT_PREFIX)+8+1]; + + sprintf(format, "%s%08lx", FORMAT_PREFIX, (unsigned long)type); + + return XInternAtom(SDL_Display, format, False); + } + } +} + +/* Convert internal data to scrap format */ +PRIVATE int +convert_data(int type, char *dst, const char *src, int srclen) +{ + int dstlen; + + dstlen = 0; + switch (type) + { + case T('T', 'E', 'X', 'T'): + if ( dst ) + { + while ( --srclen >= 0 ) + { + if ( *src == '\r' ) + { + *dst++ = '\n'; + ++dstlen; + } + else + { + *dst++ = *src; + ++dstlen; + } + ++src; + } + *dst = '\0'; + ++dstlen; + } + else + { + while ( --srclen >= 0 ) + { + if ( *src == '\r' ) + { + ++dstlen; + } + else + { + ++dstlen; + } + ++src; + } + ++dstlen; + } + break; + + default: + if ( dst ) + { + *(int *)dst = srclen; + dst += sizeof(int); + memcpy(dst, src, srclen); + } + dstlen = sizeof(int)+srclen; + break; + } + return(dstlen); +} + +/* Convert scrap data to internal format */ +PRIVATE int +convert_scrap(int type, char *dst, char *src, int srclen) +{ + int dstlen; + + dstlen = 0; + switch (type) + { + case T('T', 'E', 'X', 'T'): + { + if ( srclen == 0 ) + srclen = strlen(src); + if ( dst ) + { + while ( --srclen >= 0 ) + { + if ( *src == '\n' ) + { + *dst++ = '\r'; + ++dstlen; + } + else + { + *dst++ = *src; + ++dstlen; + } + ++src; + } + *dst = '\0'; + ++dstlen; + } + else + { + while ( --srclen >= 0 ) + { + ++dstlen; + ++src; + } + ++dstlen; + } + } + break; + + default: + dstlen = *(int *)src; + if ( dst ) + { + if ( srclen == 0 ) + memcpy(dst, src+sizeof(int), dstlen); + else + memcpy(dst, src+sizeof(int), srclen-sizeof(int)); + } + break; + } + return dstlen; +} + +/* The system message filter function -- handle clipboard messages */ +PRIVATE int clipboard_filter(const SDL_Event *event); + +PUBLIC int +init_scrap(void) +{ + SDL_SysWMinfo info; + int retval; + + /* Grab the window manager specific information */ + retval = -1; + SDL_SetError("SDL is not running on known window manager"); + + SDL_VERSION(&info.version); + if ( SDL_GetWMInfo(&info) ) + { + /* Save the information for later use */ +/* * */ + if ( info.subsystem == SDL_SYSWM_X11 ) + { + SDL_Display = info.info.x11.display; + SDL_Window = info.info.x11.window; + Lock_Display = info.info.x11.lock_func; + Unlock_Display = info.info.x11.unlock_func; + + /* Enable the special window hook events */ + SDL_EventState(SDL_SYSWMEVENT, SDL_ENABLE); + SDL_SetEventFilter(clipboard_filter); + + retval = 0; + } + else + { + SDL_SetError("SDL is not running on X11"); + } + } + return(retval); +} + +PUBLIC int +lost_scrap(void) +{ + int retval; + +/* * */ + Lock_Display(); + retval = ( XGetSelectionOwner(SDL_Display, XA_PRIMARY) != SDL_Window ); + Unlock_Display(); + + return(retval); +} + +PUBLIC void +put_scrap(int type, int srclen, const char *src) +{ + scrap_type format; + int dstlen; + char *dst; + + format = convert_format(type); + dstlen = convert_data(type, NULL, src, srclen); + +/* * */ + dst = (char *)malloc(dstlen); + if ( dst != NULL ) + { + Lock_Display(); + convert_data(type, dst, src, srclen); + XChangeProperty(SDL_Display, DefaultRootWindow(SDL_Display), + XA_CUT_BUFFER0, format, 8, PropModeReplace, (unsigned char*) dst, dstlen); + free(dst); + Atom XA_CLIPBOARD = XInternAtom(SDL_Display, "CLIPBOARD", 0); + if ( lost_scrap() ) { + XSetSelectionOwner(SDL_Display, XA_PRIMARY, SDL_Window, CurrentTime); + XSetSelectionOwner(SDL_Display, XA_CLIPBOARD, SDL_Window, CurrentTime); + } + Unlock_Display(); + } + +} + +PUBLIC void +get_scrap(int type, int *dstlen, char **dst) +{ + scrap_type format; + + *dstlen = 0; + format = convert_format(type); + +/* * */ + { + Window owner; + Atom selection; + Atom seln_type; + int seln_format; + unsigned long nbytes; + unsigned long overflow; + char *src; + + Lock_Display(); + Atom XA_CLIPBOARD = XInternAtom(SDL_Display, "CLIPBOARD", 0); + owner = XGetSelectionOwner(SDL_Display, XA_PRIMARY); + Unlock_Display(); + if ( (owner == None) || (owner == SDL_Window) ) + { + owner = DefaultRootWindow(SDL_Display); + selection = XA_CUT_BUFFER0; + } + else + { + int selection_response = 0; + SDL_Event event; + + owner = SDL_Window; + Lock_Display(); + selection = XInternAtom(SDL_Display, "SDL_SELECTION", False); + XConvertSelection(SDL_Display, XA_PRIMARY, format, + selection, owner, CurrentTime); + XConvertSelection(SDL_Display, XA_CLIPBOARD, format, + selection, owner, CurrentTime); + Unlock_Display(); + while ( ! selection_response ) + { + SDL_WaitEvent(&event); + if ( event.type == SDL_SYSWMEVENT ) + { + XEvent xevent = event.syswm.msg->event.xevent; + + if ( (xevent.type == SelectionNotify) && + (xevent.xselection.requestor == owner) ) + selection_response = 1; + } + } + } + Lock_Display(); + if ( XGetWindowProperty(SDL_Display, owner, selection, 0, INT_MAX/4, + False, format, &seln_type, &seln_format, + &nbytes, &overflow, (unsigned char **)&src) == Success ) + { + if ( seln_type == format ) + { + *dstlen = convert_scrap(type, NULL, src, nbytes); + *dst = (char *)malloc(*dstlen); + if ( *dst == NULL ) + *dstlen = 0; + else + convert_scrap(type, *dst, src, nbytes); + } + XFree(src); + } + } + Unlock_Display(); +} + +PRIVATE int clipboard_filter(const SDL_Event *event) +{ + /* Post all non-window manager specific events */ + if ( event->type != SDL_SYSWMEVENT ) { + return(1); + } + + /* Handle window-manager specific clipboard events */ + switch (event->syswm.msg->event.xevent.type) { + /* Copy the selection from XA_CUT_BUFFER0 to the requested property */ + case SelectionRequest: { + XSelectionRequestEvent *req; + XEvent sevent; + int seln_format; + unsigned long nbytes; + unsigned long overflow; + unsigned char *seln_data; + + req = &event->syswm.msg->event.xevent.xselectionrequest; + sevent.xselection.type = SelectionNotify; + sevent.xselection.display = req->display; + sevent.xselection.selection = req->selection; + sevent.xselection.target = None; + sevent.xselection.property = None; + sevent.xselection.requestor = req->requestor; + sevent.xselection.time = req->time; + if ( XGetWindowProperty(SDL_Display, DefaultRootWindow(SDL_Display), + XA_CUT_BUFFER0, 0, INT_MAX/4, False, req->target, + &sevent.xselection.target, &seln_format, + &nbytes, &overflow, &seln_data) == Success ) + { + if ( sevent.xselection.target == req->target ) + { + if ( sevent.xselection.target == XA_STRING ) + { + if ( seln_data[nbytes-1] == '\0' ) + --nbytes; + } + XChangeProperty(SDL_Display, req->requestor, req->property, + sevent.xselection.target, seln_format, PropModeReplace, + seln_data, nbytes); + sevent.xselection.property = req->property; + } + XFree(seln_data); + } + XSendEvent(SDL_Display,req->requestor,False,0,&sevent); + XSync(SDL_Display, False); + } + break; + } + + /* Post the event for X11 clipboard reading above */ + return(1); +} + +// X11 cursor + +namespace { + +// WARNING: These functions rely on the SDL_Display and SDL_Window previously initialized by init_scrap + +const int CURSOR_COUNT = 7; +Cursor defined_cursors[CURSOR_COUNT] = {0}; + +void set_cursor(int cursorType) { + Cursor cursor = 0; + + if(cursorType >= 0 && cursorType < CURSOR_COUNT) { + cursor = defined_cursors[cursorType]; + if(!cursor) { + switch(cursorType) { + case Polycode::Core::CURSOR_TEXT: + cursor = XCreateFontCursor (SDL_Display, XC_xterm); + break; + case Polycode::Core::CURSOR_POINTER: + cursor = XCreateFontCursor (SDL_Display, XC_hand1); + break; + case Polycode::Core::CURSOR_CROSSHAIR: + cursor = XCreateFontCursor (SDL_Display, XC_crosshair); + break; + case Polycode::Core::CURSOR_RESIZE_LEFT_RIGHT: + cursor = XCreateFontCursor (SDL_Display, XC_sb_h_double_arrow); + break; + case Polycode::Core::CURSOR_RESIZE_UP_DOWN: + cursor = XCreateFontCursor (SDL_Display, XC_sb_v_double_arrow); + break; + case Polycode::Core::CURSOR_OPEN_HAND: + cursor = XCreateFontCursor (SDL_Display, XC_fleur); + break; + case Polycode::Core::CURSOR_ARROW: + cursor = 0; + break; + } + + defined_cursors[cursorType] = cursor; + } + } + + if(!cursor) { + // Restore the normal cursor. + XUndefineCursor(SDL_Display, SDL_Window); + } else { + XDefineCursor(SDL_Display, SDL_Window, cursor); + } + + XFlush(SDL_Display); +} + +void free_cursors() { + XUndefineCursor(SDL_Display, SDL_Window); + for(int i = 0; i < CURSOR_COUNT; i++) { + if(defined_cursors[i]) { + XFreeCursor(SDL_Display, defined_cursors[i]); + defined_cursors[i] = 0; + } + } +} +} // namespace +// end X11 cursor + +#endif // USE_X11 diff --git a/Core/Contents/Source/PolyScene.cpp b/Core/Contents/Source/PolyScene.cpp index 75ab4c2ac..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(bool virtualScene) : EventDispatcher() { +Scene::Scene(int sceneType, bool virtualScene) : EventDispatcher() { + initScene(sceneType, virtualScene); +} + +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,15 +134,18 @@ bool Scene::isEnabled() { } void Scene::Update() { + 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; } @@ -112,8 +155,6 @@ void Scene::enableLighting(bool enable) { CoreServices::getInstance()->getRenderer()->enableLighting(enable); } - - void Scene::enableFog(bool enable) { fogEnabled = enable; @@ -128,51 +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; idoUpdates(); - entities[i]->updateEntityMatrix(); - } - + //make these the closest Matrix4 textureMatrix; @@ -181,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); @@ -196,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()) { @@ -214,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; @@ -229,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->buildFrustrumPlanes(); - - 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->isSphereInFrustrum((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->buildFrustrumPlanes(); + targetCamera->doCameraTransform(); CoreServices::getInstance()->getRenderer()->setTexture(NULL); CoreServices::getInstance()->getRenderer()->enableShaders(false); - for(int i=0; icastShadows) { - if(entities[i]->getBBoxRadius() > 0) { - if(targetCamera->isSphereInFrustrum((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) { @@ -305,412 +434,6 @@ void Scene::removeLight(SceneLight *light) { } } -SceneLight *Scene::getNearestLight(Vector3 pos) { - if(lights.size() > 0) - return lights[0]; - else - return NULL; -} - - -String Scene::readString(OSFILE *inFile) { - char buffer[1024]; - unsigned int namelen; - OSBasics::read(&namelen, sizeof(unsigned int), 1, inFile); - memset(buffer, 0, 1024); - OSBasics::read(buffer, 1, namelen, inFile); - return String(buffer); -} - -void Scene::loadScene(const String& fileName) { - OSFILE *inFile = OSBasics::open(fileName.c_str(), "rb"); - if(!inFile) { - Logger::log("Error opening scene file\n"); - return; - } - - Number r,g,b,a; - - OSBasics::read(&r, sizeof(Number), 1, inFile); - OSBasics::read(&g, sizeof(Number), 1, inFile); - OSBasics::read(&b, sizeof(Number), 1, inFile); - clearColor.setColor(r,g,b,1.0f); - - OSBasics::read(&r, sizeof(Number), 1, inFile); - OSBasics::read(&g, sizeof(Number), 1, inFile); - OSBasics::read(&b, sizeof(Number), 1, inFile); - ambientColor.setColor(r,g,b,1.0f); - - - unsigned int numObjects, objectType,namelen; - char buffer[1024]; - char flag; - Number t[3],rq[4]; - SceneEntity *newEntity; - - unsigned int lightmapIndex = 0; - - OSBasics::read(&flag, 1, 1, inFile); - hasLightmaps = false; - if(flag == 1) - hasLightmaps = true; - - OSBasics::read(&numObjects, sizeof(unsigned int), 1, inFile); - Logger::log("Loading scene (%d objects)\n", numObjects); - for(int i=0; i < numObjects; i++) { - - OSBasics::read(t, sizeof(Number), 3, inFile); - OSBasics::read(rq, sizeof(Number), 4, inFile); - newEntity = NULL; - - OSBasics::read(&objectType, sizeof(unsigned int), 1, inFile); - - SceneMesh *newSceneMesh; - Mesh *newMesh; - Material *newMaterial; - - switch(objectType) { - case ENTITY_MESH: - - if(hasLightmaps) { - OSBasics::read(&lightmapIndex, sizeof(unsigned int), 1, inFile); - } else { - lightmapIndex = 0; - } - - - OSBasics::read(&namelen, sizeof(unsigned int), 1, inFile); - memset(buffer, 0, 1024); - OSBasics::read(buffer, 1, namelen, inFile); - - Logger::log("adding mesh (texture: %s)\n", buffer); - - OSBasics::read(&r, sizeof(Number), 1, inFile); - OSBasics::read(&g, sizeof(Number), 1, inFile); - OSBasics::read(&b, sizeof(Number), 1, inFile); - OSBasics::read(&a, sizeof(Number), 1, inFile); - - newMaterial = (Material*) CoreServices::getInstance()->getResourceManager()->getResource(Resource::RESOURCE_MATERIAL, buffer); - newMesh = new Mesh(Mesh::TRI_MESH); - newMesh->loadFromFile(inFile); - newSceneMesh = new SceneMesh(newMesh); - - newSceneMesh->setColor(r,g,b,a); - newSceneMesh->lightmapIndex = lightmapIndex; - addEntity(newSceneMesh); - newEntity = (SceneEntity*)newSceneMesh; - staticGeometry.push_back(newSceneMesh); - if(newMaterial != NULL) - newSceneMesh->setMaterial(newMaterial); - - break; - case ENTITY_ENTITY: { - Logger::log("loading entity\n"); - String entityType = readString(inFile); - SceneEntity *newCustomEntity = new SceneEntity(); - newCustomEntity->custEntityType = entityType; - newEntity = newCustomEntity; - customEntities.push_back(newCustomEntity); - } break; - case ENTITY_COLLMESH: { - /* - unsigned int collType,numVertices,numFaces; - Number co[3]; - OSBasics::read(&collType, sizeof(unsigned int), 1, inFile); - OSBasics::read(&numVertices, sizeof(unsigned int), 1, inFile); - - Mesh *mesh = new Mesh(Mesh::TRI_MESH); - - for(int i=0; i < numVertices; i++) { - OSBasics::read(co, sizeof(Number), 3, inFile); - Vertex *newVert = new Vertex(co[0], co[1], co[2]); - mesh->addVertex(newVert); - } - - OSBasics::read(&numFaces, sizeof(unsigned int), 1, inFile); - - unsigned int fo[3]; - for(int i=0; i < numFaces; i++) { - OSBasics::read(fo, sizeof(unsigned int), 3, inFile); - Polygon *newPoly = new Polygon(); - newPoly->addVertex(mesh->getVertex(fo[0])); - newPoly->addVertex(mesh->getVertex(fo[1])); - newPoly->addVertex(mesh->getVertex(fo[2])); - mesh->addPolygon(newPoly); - } - - SceneMesh *newMesh = new SceneMesh(mesh); - newEntity = newMesh; - loadCollisionChild(newEntity); - collisionGeometry.push_back(newMesh); - newMesh->visible = false; - - // ScenePrimitive *test = new ScenePrimitive(ScenePrimitive::TYPE_BOX, newMesh->bBox.x,newMesh->bBox.y,newMesh->bBox.z); - // newMesh->addEntity(test); - */ - } - - break; - case ENTITY_CAMERA: - newEntity = (SceneEntity*)this->getDefaultCamera(); - break; - case ENTITY_LIGHT: - /* - Number col[3],e,d; - unsigned int lType; - OSBasics::read(&lType, sizeof(unsigned int), 1, inFile); - OSBasics::read(&e, sizeof(Number), 1, inFile); - OSBasics::read(&d, sizeof(Number), 1, inFile); - OSBasics::read(col, sizeof(Number), 3, inFile); - - SceneLight *newLight = new SceneLight(lType, e, d, this); - newLight->lightColor.setColor(col[0],col[1],col[2],1.0f); - addLight(newLight); - newEntity = (SceneEntity*)newLight; - */ - break; - } - - - if(newEntity != NULL) { - unsigned int numProps; - OSBasics::read(&numProps, sizeof(unsigned int), 1, inFile); - for(int i=0; i < numProps; i++) { - String propName,propValue; - propName = readString(inFile); - propValue = readString(inFile); - EntityProp prop; - prop.propName = propName; - prop.propValue = propValue; - newEntity->entityProps.push_back(prop); - } - - if(objectType == ENTITY_MESH) { - if(newEntity->getEntityProp("vertexnormals") == "false") - ((SceneMesh*)newEntity)->getMesh()->useVertexNormals(false); - - if(newEntity->getEntityProp("collision") != "") { - if(newEntity->getEntityProp("collision") == "mesh") { - loadCollisionChild(newEntity, false, 3); - } - } - } - - newEntity->setPosition(t[0], t[1], t[2]); - newEntity->setRotationQuat(rq[0], rq[1], rq[2], rq[3]); - newEntity->rebuildTransformMatrix(); - } - } - - if(!hasLightmaps) { - OSBasics::close(inFile); - return; - } - /* - unsigned int lmsize,numLightmaps,imageWidth, imageHeight; - OSBasics::read(&numLightmaps, sizeof(unsigned int), 1, inFile); - - packer = new LightmapPacker(this); - char *imageData; - - for(int i=0 ; i < numLightmaps; i++) { - OSBasics::read(&imageWidth, sizeof(unsigned int), 1, inFile); - OSBasics::read(&imageHeight, sizeof(unsigned int), 1, inFile); - OSBasics::read(&lmsize, sizeof(unsigned int), 1, inFile); - imageData = (char*)malloc(lmsize); - OSBasics::read(imageData, 1, lmsize, inFile); - Image *newImage = new Image(imageData, imageWidth, imageHeight); - packer->images.push_back(newImage); - free(imageData); - } - - packer->bindTextures(); - */ - OSBasics::close(inFile); -} - -vector Scene::getCustomEntitiesByType(const String& type) const { - vector retVector; - for(int i=0; i < customEntities.size(); i++) { - if(customEntities[i]->custEntityType == type) { - retVector.push_back(customEntities[i]); - } - } - return retVector; -} - -SceneEntity *Scene::getCustomEntityByType(const String& type) const { - for(int i=0; i < customEntities.size(); i++) { - if(customEntities[i]->custEntityType == type) { - return customEntities[i]; - } - } - return NULL; -} - -void Scene::writeEntityMatrix(SceneEntity *entity, OSFILE *outFile) { - Number t[3],rq[4]; - Vector3 pos = entity->getPosition(); - t[0] = pos.x; - t[1] = pos.y; - t[2] = pos.z; - - rq[0] = entity->getRotationQuat().w; - rq[1] = entity->getRotationQuat().x; - rq[2] = entity->getRotationQuat().y; - rq[3] = entity->getRotationQuat().z; - - OSBasics::write(t, sizeof(Number), 3, outFile); - OSBasics::write(rq, sizeof(Number), 4, outFile); - -} - -void Scene::saveScene(const String& fileName) { - OSFILE *outFile = OSBasics::open(fileName.c_str(), "wb"); - if(!outFile) { - Logger::log("Error opening scene file for writing\n"); - return; - } - - // geometry - unsigned int totalObjects = staticGeometry.size() + lights.size() + collisionGeometry.size() + customEntities.size(); - unsigned int objType; - char flag = 0; - if(hasLightmaps) - flag = 1; - OSBasics::write(&flag, 1, 1, outFile); - OSBasics::write(&totalObjects, sizeof(unsigned int), 1, outFile); - - unsigned int stLen; - - for(int i=0; i lightmapIndex; - OSBasics::write(&lightmapIndex, sizeof(unsigned int), 1, outFile); - } - - stLen = mesh->getMaterial()->getName().length(); - OSBasics::write(&stLen, sizeof(unsigned int), 1, outFile); - OSBasics::write(mesh->getMaterial()->getName().c_str(), 1, stLen, outFile); - - mesh->getMesh()->saveToFile(outFile); - } - - /* - for(int i=0; i < collisionGeometry.size(); i++) { - SceneMesh *mesh = collisionGeometry[i]; - writeEntityMatrix(mesh, outFile); - objType = Scene::ENTITY_COLLMESH; - OSBasics::write(&objType, sizeof(unsigned int), 1, outFile); - - unsigned int collType = 1; - OSBasics::write(&collType, sizeof(unsigned int), 1, outFile); - - unsigned int numVertices = mesh->getMesh()->getNumVertices(); - OSBasics::write(&numVertices, sizeof(unsigned int), 1, outFile); - Number pos[3]; - for(int j=0; j < numVertices; j++) { - Vertex *vert = mesh->getMesh()->getVertex(j); - pos[0] = vert->x; - pos[1] = vert->y; - pos[2] = vert->z; - OSBasics::write(pos, sizeof(Number),3, outFile); - } - - unsigned int numFaces = mesh->getMesh()->getPolygonCount(); - OSBasics::write(&numFaces, sizeof(unsigned int), 1, outFile); - unsigned int ind[3]; - for(int j=0; j < numFaces; j++) { - Polygon *poly = mesh->getMesh()->getPolygon(j); - ind[0] = mesh->getMesh()->getVertexIndex(poly->getVertex(0)); - ind[1] = mesh->getMesh()->getVertexIndex(poly->getVertex(1)); - ind[2] = mesh->getMesh()->getVertexIndex(poly->getVertex(2)); - OSBasics::write(ind, sizeof(unsigned int),3, outFile); - } - } - */ - /* - Number col[3],e,d; - for(int i=0; i < lights.size(); i++) { - - writeEntityMatrix(lights[i], outFile); - objType = Scene::ENTITY_LIGHT; - OSBasics::write(&objType, sizeof(unsigned int), 1, outFile); - - col[0] = lights[i]->lightColor.r; - col[1] = lights[i]->lightColor.g; - col[2] = lights[i]->lightColor.b; - e = lights[i]->getIntensity(); - d = lights[i]->getDistance(); - - OSBasics::write(&e, sizeof(Number), 1, outFile); - OSBasics::write(&d, sizeof(Number), 1, outFile); - OSBasics::write(col, sizeof(Number), 3, outFile); - } - */ - for(int i=0; i < customEntities.size(); i++) { - SceneEntity *custEnt = customEntities[i]; - - writeEntityMatrix(custEnt, outFile); - objType = Scene::ENTITY_ENTITY; - OSBasics::write(&objType, sizeof(unsigned int), 1, outFile); - - - writeString(custEnt->custEntityType, outFile); - unsigned int numProps = custEnt->entityProps.size(); - OSBasics::write(&numProps, sizeof(unsigned int), 1, outFile); - for(int j=0; j < numProps; j++) { - writeString(custEnt->entityProps[j].propName, outFile); - writeString(custEnt->entityProps[j].propValue, outFile); - } - } - - if(!hasLightmaps) { - OSBasics::close(outFile); - return; - } - - /* - unsigned int lmsize; - lmsize = packer->images.size(); - OSBasics::write(&lmsize, sizeof(unsigned int), 1, outFile); - for(int i=0; i < packer->images.size(); i++) { - lmsize = packer->images[i]->getWidth(); - OSBasics::write(&lmsize, sizeof(unsigned int), 1, outFile); - lmsize = packer->images[i]->getHeight(); - OSBasics::write(&lmsize, sizeof(unsigned int), 1, outFile); - - lmsize = packer->images[i]->getWidth() * packer->images[i]->getHeight() * 4; - OSBasics::write(&lmsize, sizeof(unsigned int), 1, outFile); - OSBasics::write(packer->images[i]->getPixels(), 1, lmsize, outFile); - } - */ - - OSBasics::close(outFile); -} - -void Scene::writeString(const String& str, OSFILE *outFile) { - unsigned int stLen = str.length(); - OSBasics::write(&stLen, sizeof(unsigned int), 1, outFile); - OSBasics::write(str.c_str(), 1, stLen, outFile); - -} - -int Scene::getNumStaticGeometry() { - return staticGeometry.size(); -} - -SceneMesh *Scene::getStaticGeometry(int index) { - return staticGeometry[index]; -} - int Scene::getNumLights() { return lights.size(); } @@ -719,24 +442,3 @@ SceneLight *Scene::getLight(int index) { return lights[index]; } -void Scene::generateLightmaps(Number lightMapRes, Number lightMapQuality, int numRadPasses) { - /* - packer = new LightmapPacker(this); - packer->generateTextures(lightMapRes, lightMapQuality); - - RadTool *radTool = new RadTool(this, packer); - radTool->fiatLux(numRadPasses); - - packer->bindTextures(); - packer->saveLightmaps("/Users/ivansafrin/Desktop/lightmaps"); - hasLightmaps = true; - */ -} -/* -void Scene::addGrid(String gridTexture) { - ScenePrimitive *gridMesh = new ScenePrimitive(ScenePrimitive::TYPE_PLANE, 20,20,0); - gridMesh->loadTexture(gridTexture); - gridMesh->setPitch(-90.0f); - addEntity(gridMesh); -} -*/ 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 b974ea1dd..99914c3ee 100755 --- a/Core/Contents/Source/PolySceneLabel.cpp +++ b/Core/Contents/Source/PolySceneLabel.cpp @@ -25,51 +25,121 @@ #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; - setText(text); - mesh->arrayDirtyMap[RenderDataArray::TEXCOORD_DATA_ARRAY] = true; +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; } -void SceneLabel::setText(const String& newText) { - if(texture) - CoreServices::getInstance()->getMaterialManager()->deleteTexture(texture); - - label->setText(newText); - texture = CoreServices::getInstance()->getMaterialManager()->createTextureFromImage(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(); + + 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 +} + +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) { - bBoxRadius = label->getWidth()*scale; + if(newText == label->getText() && !label->optionsChanged()) { + return; + } + label->setText(newText); + updateFromLabel(); } 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 0ccd29efd..6186683bb 100755 --- a/Core/Contents/Source/PolySceneLine.cpp +++ b/Core/Contents/Source/PolySceneLine.cpp @@ -22,51 +22,112 @@ #include "PolySceneLine.h" #include "PolyRenderer.h" -#include "PolyPolygon.h" using namespace Polycode; -SceneLine::SceneLine(Vector3 start, Vector3 end) : SceneEntity() { +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->ent2 = NULL; this->start = start; this->end = end; - - mesh = new Mesh(Mesh::LINE_MESH); - - Polygon *poly = new Polygon(); - poly->addVertex(0,0,0); - poly->addVertex(0,0,0); - mesh->addPolygon(poly); - - ignoreParentMatrix = true; - - lineWidth = 1.0; - lineSmooth = false; - + initLine(); + ignoreParentMatrix = false; } -SceneLine::SceneLine(SceneEntity *ent1, SceneEntity *ent2) : SceneEntity() { +SceneLine::SceneLine(Entity *ent1, Entity *ent2) : SceneMesh(Mesh::LINE_MESH) { this->ent1 = ent1; this->ent2 = ent2; - - mesh = new Mesh(Mesh::LINE_MESH); - - Polygon *poly = new Polygon(); - poly->addVertex(0,0,0); - poly->addVertex(0,0,0); - mesh->addPolygon(poly); - + initLine(); ignoreParentMatrix = true; - - lineWidth = 1.0; - lineSmooth = false; - + +} + +void SceneLine::initLine() { + mesh->addVertexWithUV(0,0,0,0,0); + mesh->addVertexWithUV(0,0,0,1,0); +} + +SceneLine *SceneLine::SceneLineWithPositions(Vector3 start, Vector3 end) { + return new SceneLine(start, end); } SceneLine::~SceneLine() { - delete mesh; } void SceneLine::setStart(Vector3 start) { @@ -77,34 +138,24 @@ void SceneLine::setEnd(Vector3 end) { this->end = end; } -void SceneLine::Render() { +void SceneLine::Update(){ Vector3 v1; - Vector3 v2; + 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; - - Renderer *renderer = CoreServices::getInstance()->getRenderer(); - - renderer->setLineSize(lineWidth); - renderer->setLineSmooth(lineSmooth); - - renderer->setTexture(NULL); - renderer->pushDataArrayForMesh(mesh, RenderDataArray::VERTEX_DATA_ARRAY); - renderer->pushDataArrayForMesh(mesh, RenderDataArray::TEXCOORD_DATA_ARRAY); - renderer->pushDataArrayForMesh(mesh, RenderDataArray::NORMAL_DATA_ARRAY); - - renderer->drawArrays(mesh->getMeshType()); - -} + } +} \ No newline at end of file diff --git a/Core/Contents/Source/PolySceneManager.cpp b/Core/Contents/Source/PolySceneManager.cpp index 156500955..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;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(); - } +void SceneManager::setRenderer(Renderer *renderer) { + this->renderer = renderer; +} - - CoreServices::getInstance()->getRenderer()->clearScreen(); - CoreServices::getInstance()->getRenderer()->loadIdentity(); +void SceneManager::renderVirtual() { + bool anyVirtualsRendered = false; + for(int i=0;ienabled) { + 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::Update() { +void SceneManager::Render() { for(int i=0;iisEnabled() && !scenes[i]->isVirtual()) { - CoreServices::getInstance()->getRenderer()->loadIdentity(); + renderer->loadIdentity(); Scene *scene = scenes[i]; - scene->Update(); if(scene->getActiveCamera()->hasFilterShader()) { scene->getActiveCamera()->drawFilter(); } else { scene->Render(); } } + renderer->loadIdentity(); + } +} + +void SceneManager::fixedUpdate() { + for(int i=0;iisEnabled()) { + scenes[i]->fixedUpdate(); + } + } +} + +void SceneManager::Update() { + for(int i=0;iisEnabled()) { + scenes[i]->Update(); + } } } diff --git a/Core/Contents/Source/PolySceneMesh.cpp b/Core/Contents/Source/PolySceneMesh.cpp index b2b96b990..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" @@ -39,61 +39,134 @@ SceneMesh *SceneMesh::SceneMeshFromMesh(Mesh *mesh) { return new SceneMesh(mesh); } -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::SceneMeshWithType(int meshType) { + return new SceneMesh(meshType); +} + +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() { @@ -131,45 +204,50 @@ 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; } -void SceneMesh::loadTexture(const String& fileName, bool clamp) { - texture = CoreServices::getInstance()->getMaterialManager()->createTextureFromFile(fileName, clamp); +void SceneMesh::loadTexture(const String& fileName) { + MaterialManager *materialManager = CoreServices::getInstance()->getMaterialManager(); + 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() { @@ -182,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 e9129252c..0bfc94e2b 100755 --- a/Core/Contents/Source/PolySceneRenderTexture.cpp +++ b/Core/Contents/Source/PolySceneRenderTexture.cpp @@ -24,25 +24,35 @@ #include "PolyCoreServices.h" #include "PolyRenderer.h" #include "PolySceneManager.h" - +#include "PolyTexture.h" +#include "PolyScene.h" +#include "PolyCamera.h" using namespace Polycode; SceneRenderTexture::SceneRenderTexture(Scene *targetScene, Camera *targetCamera, int renderWidth,int renderHeight, bool floatingPoint) { -// targetTexture = CoreServices::getInstance()->getMaterialManager()->createTexture(renderWidth, renderHeight, NULL,true); + this->floatingPoint = floatingPoint; CoreServices::getInstance()->getRenderer()->createRenderTextures(&targetTexture, &depthTexture, renderWidth, renderHeight, floatingPoint); this->targetScene = targetScene; this->targetCamera = targetCamera; - - + CoreServices::getInstance()->getRenderer()->createRenderTextures(&filterColorBufferTexture, &filterZBufferTexture, renderWidth, renderHeight, floatingPoint); - - CoreServices::getInstance()->getSceneManager()->registerRenderTexture(this); + renderer = CoreServices::getInstance()->getRenderer(); + enabled = true; } -void SceneRenderTexture::drawScreen() { - //CoreServices::getInstance()->getRenderer()->renderToTexture(targetTexture); +void SceneRenderTexture::resizeRenderTexture(int newWidth, int newHeight) { + + if(newWidth > 0 && newHeight > 0) { + CoreServices::getInstance()->getRenderer()->destroyTexture(targetTexture); + CoreServices::getInstance()->getRenderer()->destroyTexture(depthTexture); + CoreServices::getInstance()->getRenderer()->destroyTexture(filterColorBufferTexture); + CoreServices::getInstance()->getRenderer()->destroyTexture(filterZBufferTexture); + + CoreServices::getInstance()->getRenderer()->createRenderTextures(&targetTexture, &depthTexture, newWidth, newHeight, floatingPoint); + CoreServices::getInstance()->getRenderer()->createRenderTextures(&filterColorBufferTexture, &filterZBufferTexture, newWidth, newHeight, floatingPoint); + } } Scene *SceneRenderTexture::getTargetScene() { @@ -61,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 1b74c17d2..000000000 --- a/Core/Contents/Source/PolyScreen.cpp +++ /dev/null @@ -1,212 +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.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(); - if( i == 0) - binding->addTexture("screenColorBuffer", originalSceneTexture); - - 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); - 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++) { - renderer->bindFrameBufferTexture(materialBinding->getOutTargetBinding(j)->texture); - - renderer->drawScreenQuad(materialBinding->getOutTargetBinding(j)->width, materialBinding->getOutTargetBinding(j)->height); - 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() { - -} - -void Screen::Render() { - Update(); - renderer->loadIdentity(); - renderer->translate2D(offset.x, offset.y); - - rootEntity.doUpdates(); - rootEntity.updateEntityMatrix(); - rootEntity.transformAndRender(); -} diff --git a/Core/Contents/Source/PolyScreenCurve.cpp b/Core/Contents/Source/PolyScreenCurve.cpp deleted file mode 100755 index 1e7283b6c..000000000 --- a/Core/Contents/Source/PolyScreenCurve.cpp +++ /dev/null @@ -1,52 +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 "PolyScreenCurve.h" -#include "PolyBezierCurve.h" -#include "PolyMesh.h" -#include "PolyPolygon.h" - -using namespace Polycode; - -ScreenCurve::ScreenCurve(BezierCurve *curve, int numVertices) : ScreenShape(ScreenShape::SHAPE_CUSTOM) { - this->curve = curve; - this->numVertices = numVertices; - - mesh->setMeshType(Mesh::TRIFAN_MESH); - - Polygon *poly = new Polygon(); - - Color c; - Number interval = 1.0f/numVertices; - Vector3 vec; - Number offset = 1.0f; - Vertex *v; - for(int i=0; i < numVertices; i++) { - vec = curve->getPointAt(offset); - v = poly->addVertex(vec.x, vec.y, 0); - offset -= interval; - } - mesh->addPolygon(poly); -} - -ScreenCurve::~ScreenCurve() { -} diff --git a/Core/Contents/Source/PolyScreenEntity.cpp b/Core/Contents/Source/PolyScreenEntity.cpp deleted file mode 100755 index ac97e805f..000000000 --- a/Core/Contents/Source/PolyScreenEntity.cpp +++ /dev/null @@ -1,724 +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 = 1; - height = 1; - setHitbox(1, 1); - backfaceCulled = false; - positionMode = POSITION_TOPLEFT; - mouseOver = false; - isDragged = 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) { - ScreenEntity *newEntity = new ScreenEntity(); - applyClone(newEntity, deepClone, ignoreEditorOnly); - return newEntity; -} - -void ScreenEntity::applyClone(Entity *clone, bool deepClone, bool ignoreEditorOnly) { - 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; - ((ScreenEntity*)CoreServices::getInstance()->focusedChild)->hasFocus = true; - ((ScreenEntity*)CoreServices::getInstance()->focusedChild)->onGainFocus(); -} - -void ScreenEntity::moveChildUp(ScreenEntity *child) { - for(int i=0; i < children.size(); i++) { - if(children[i] == child && i < children.size()-1) { - ScreenEntity *next = (ScreenEntity*)children[i+1]; - children[i+1] = child; - children[i] = next; - break; - } - } -} - -void ScreenEntity::moveChildDown(ScreenEntity *child) { - for(int i=0; i < children.size(); i++) { - if(children[i] == child && i > 0) { - ScreenEntity *prev = (ScreenEntity*)children[i-1]; - children[i-1] = child; - children[i] = prev; - break; - } - } -} - -void ScreenEntity::moveChildTop(ScreenEntity *child) { - for(int i=0; i < children.size(); i++) { - if(children[i] == child && i < children.size()-1) { - children.erase(children.begin()+i); - children.push_back(child); - break; - } - } -} - -void ScreenEntity::moveChildBottom(ScreenEntity *child) { - for(int i=0; i < children.size(); i++) { - if(children[i] == child && i > 0) { - children.erase(children.begin()+i); - children.insert(children.begin(), child); - break; - } - } -} - -bool ScreenEntity::isFocusable() const { - return focusable; -} - -void ScreenEntity::startDrag(Number xOffset, Number yOffset) { - isDragged = true; - dragOffsetX = xOffset; - dragOffsetY = yOffset; -} - -void ScreenEntity::stopDrag() { - isDragged = false; -} - -ScreenEntity::~ScreenEntity() { - if(CoreServices::getInstance()->focusedChild == this) { - CoreServices::getInstance()->focusedChild = NULL; - } -} - -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) { - - Vector3 v; - Polygon testPoly; - - Matrix4 transformMatrix = getConcatenatedMatrix(); - v = Vector3(hit.x, hit.y, 0); - v = transformMatrix * v; - testPoly.addVertex(v.x, v.y, 0.0); - - v = Vector3(hit.x+hit.w, hit.y, 0); - v = transformMatrix * v; - testPoly.addVertex(v.x, v.y, 0.0); - - v = Vector3(hit.x+hit.w, hit.y+hit.h, 0); - v = transformMatrix * v; - testPoly.addVertex(v.x, v.y, 0.0); - - v = Vector3(hit.x,hit.y+hit.h, 0); - v = transformMatrix * v; - testPoly.addVertex(v.x, v.y, 0.0); - - return isPointInsidePolygon2D(&testPoly, Vector2(x,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() { - 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() { - 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; -} - -Matrix4 ScreenEntity::getScreenConcatenatedMatrix() { - Matrix4 retMatrix = transformMatrix; - if(positionMode == POSITION_TOPLEFT) { - Vector3 pos = retMatrix.getPosition(); - retMatrix.setPosition(pos.x + width/2.0, pos.y + height/2.0, 0); - } - - if(parentEntity) { - return retMatrix * ((ScreenEntity*)parentEntity)->getScreenConcatenatedMatrix(); - } else { - return retMatrix; - } -} - -MouseEventResult ScreenEntity::_onMouseMove(Number x, Number y, int timestamp, Vector2 parentAdjust) { - - if(isDragged) { - - Vector3 localCoordinate = Vector3(x+(parentAdjust.x*2.0),y+(parentAdjust.y*2.0),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+parentAdjust.x,y+parentAdjust.y)) { - - - Vector3 localCoordinate = Vector3(x+(parentAdjust.x*2.0),y+(parentAdjust.y*2.0),0); - Matrix4 inverse = getConcatenatedMatrix().inverse(); - localCoordinate = inverse * localCoordinate; - if(positionMode == POSITION_TOPLEFT) - localCoordinate.x += hit.w/2.0; - if(positionMode == POSITION_TOPLEFT) - localCoordinate.y += hit.h/2.0; - - - - onMouseMove(localCoordinate.x,localCoordinate.y); - xmouse = localCoordinate.x; - ymouse = localCoordinate.y; - - dispatchEvent(new InputEvent(Vector2(localCoordinate.x,localCoordinate.y)-parentAdjust, timestamp), InputEvent::EVENT_MOUSEMOVE); - - if(!mouseOver) { - dispatchEvent(new InputEvent(Vector2(localCoordinate.x,localCoordinate.y)-parentAdjust, timestamp), InputEvent::EVENT_MOUSEOVER); - mouseOver = true; - } - ret.hit = true; - if(blockMouseInput) { - ret.blocked = true; - } - - - } else { - if(mouseOver) { - - Vector3 localCoordinate = Vector3(x+(parentAdjust.x*2.0),y+(parentAdjust.y*2.0),0); - Matrix4 inverse = getConcatenatedMatrix().inverse(); - localCoordinate = inverse * localCoordinate; - if(positionMode == POSITION_TOPLEFT) - localCoordinate.x += hit.w/2.0; - if(positionMode == POSITION_TOPLEFT) - localCoordinate.y += hit.h/2.0; - - - dispatchEvent(new InputEvent(Vector2(localCoordinate.x,localCoordinate.y)-parentAdjust, timestamp), InputEvent::EVENT_MOUSEOUT); - mouseOver = false; - } - } - - for(int i=children.size()-1;i>=0;i--) { - Vector2 adjust = parentAdjust; - if(positionMode == POSITION_TOPLEFT) - adjust += Vector2(floor(width/2.0), floor(height/2.0)); - - MouseEventResult childRes = ((ScreenEntity*)children[i])->_onMouseMove(x,y, timestamp, adjust); - if(childRes.hit) - ret.hit = true; - if(childRes.blocked) - ret.blocked = true; - if(childRes.blocked) - break; - } - } - - return ret; -} - -MouseEventResult ScreenEntity::_onMouseUp(Number x, Number y, int mouseButton, int timestamp, Vector2 parentAdjust) { - MouseEventResult ret; - ret.hit = false; - ret.blocked = false; - - - if(processInputEvents && enabled) { - if(hitTest(x+parentAdjust.x,y+parentAdjust.y)) { - Vector3 localCoordinate = Vector3(x+(parentAdjust.x*2.0),y+(parentAdjust.y*2.0),0); - - Matrix4 inverse = getConcatenatedMatrix().inverse(); - localCoordinate = inverse * localCoordinate; - if(positionMode == POSITION_TOPLEFT) - localCoordinate.x += hit.w/2.0; - if(positionMode == POSITION_TOPLEFT) - localCoordinate.y += hit.h/2.0; - - - onMouseUp(localCoordinate.x,localCoordinate.y); - InputEvent *inputEvent = new InputEvent(Vector2(localCoordinate.x,localCoordinate.y)-parentAdjust, timestamp); - inputEvent->mouseButton = mouseButton; - dispatchEvent(inputEvent, InputEvent::EVENT_MOUSEUP); - - ret.hit = true; - if(blockMouseInput) { - ret.blocked = true; - } - - } else { - Vector3 localCoordinate = Vector3(x+(parentAdjust.x*2.0),y+(parentAdjust.y*2.0),0); - - Matrix4 inverse = getConcatenatedMatrix().inverse(); - localCoordinate = inverse * localCoordinate; - if(positionMode == POSITION_TOPLEFT) - localCoordinate.x += hit.w/2.0; - if(positionMode == POSITION_TOPLEFT) - localCoordinate.y += hit.h/2.0; - - - InputEvent *inputEvent = new InputEvent(Vector2(localCoordinate.x,localCoordinate.y)-parentAdjust, timestamp); - inputEvent->mouseButton = mouseButton; - dispatchEvent(inputEvent, InputEvent::EVENT_MOUSEUP_OUTSIDE); - } - - for(int i=children.size()-1;i>=0;i--) { - Vector2 adjust = parentAdjust; - if(positionMode == POSITION_TOPLEFT) - adjust += Vector2(floor(width/2.0), floor(height/2.0)); - MouseEventResult childRes = ((ScreenEntity*)children[i])->_onMouseUp(x,y, mouseButton, timestamp, adjust); - if(childRes.hit) - ret.hit = true; - if(childRes.blocked) - ret.blocked = true; - if(childRes.blocked) - break; - - } - } - - return ret; -} - -MouseEventResult ScreenEntity::_onMouseWheelUp(Number x, Number y, int timestamp, Vector2 parentAdjust) { - - MouseEventResult ret; - ret.hit = false; - ret.blocked = false; - - if(processInputEvents && enabled) { - if(hitTest(x+parentAdjust.x,y+parentAdjust.y)) { - Vector3 localCoordinate = Vector3(x+(parentAdjust.x*2.0),y+(parentAdjust.y*2.0),0); - - Matrix4 inverse = getConcatenatedMatrix().inverse(); - localCoordinate = inverse * localCoordinate; - if(positionMode == POSITION_TOPLEFT) - localCoordinate.x += hit.w/2.0; - if(positionMode == POSITION_TOPLEFT) - localCoordinate.y += hit.h/2.0; - - - onMouseWheelUp(localCoordinate.x,localCoordinate.y); - - InputEvent *inputEvent = new InputEvent(Vector2(localCoordinate.x,localCoordinate.y)-parentAdjust, timestamp); - dispatchEvent(inputEvent, InputEvent::EVENT_MOUSEWHEEL_UP); - - ret.hit = true; - if(blockMouseInput) { - ret.blocked = true; - } - - } - - for(int i=children.size()-1;i>=0;i--) { - Vector2 adjust = parentAdjust; - if(positionMode == POSITION_TOPLEFT) - adjust += Vector2(floor(width/2.0), floor(height/2.0)); - MouseEventResult childRes = ((ScreenEntity*)children[i])->_onMouseWheelUp(x,y, timestamp, adjust); - if(childRes.hit) - ret.hit = true; - if(childRes.blocked) - ret.blocked = true; - if(childRes.blocked) - break; - } - } - return ret; -} - -MouseEventResult ScreenEntity::_onMouseWheelDown(Number x, Number y, int timestamp, Vector2 parentAdjust) { - MouseEventResult ret; - ret.hit = false; - ret.blocked = false; - - if(processInputEvents && enabled) { - if(hitTest(x+parentAdjust.x,y+parentAdjust.y)) { - Vector3 localCoordinate = Vector3(x+(parentAdjust.x*2.0),y+(parentAdjust.y*2.0),0); - - Matrix4 inverse = getConcatenatedMatrix().inverse(); - localCoordinate = inverse * localCoordinate; - if(positionMode == POSITION_TOPLEFT) - localCoordinate.x += hit.w/2.0; - if(positionMode == POSITION_TOPLEFT) - localCoordinate.y += hit.h/2.0; - - - onMouseWheelDown(localCoordinate.x,localCoordinate.y); - - InputEvent *inputEvent = new InputEvent(Vector2(localCoordinate.x,localCoordinate.y)-parentAdjust, timestamp); - dispatchEvent(inputEvent, InputEvent::EVENT_MOUSEWHEEL_DOWN); - - ret.hit = true; - if(blockMouseInput) { - ret.blocked = true; - } - } - - for(int i=children.size()-1;i>=0;i--) { - Vector2 adjust = parentAdjust; - if(positionMode == POSITION_TOPLEFT) - adjust += Vector2(floor(width/2.0), floor(height/2.0)); - - MouseEventResult childRes = ((ScreenEntity*)children[i])->_onMouseWheelDown(x,y, timestamp, adjust); - if(childRes.hit) - ret.hit = true; - if(childRes.blocked) - ret.blocked = true; - if(childRes.blocked) - break; - - } - } - - return ret; -} - - -MouseEventResult ScreenEntity::_onMouseDown(Number x, Number y, int mouseButton, int timestamp, Vector2 parentAdjust) { - MouseEventResult ret; - ret.hit = false; - ret.blocked = false; - - if(processInputEvents && enabled) { - if(hitTest(x+parentAdjust.x,y+parentAdjust.y)) { - Vector3 localCoordinate = Vector3(x+(parentAdjust.x*2.0),y+(parentAdjust.y*2.0),0); - - Matrix4 inverse = getConcatenatedMatrix().inverse(); - localCoordinate = inverse * localCoordinate; - if(positionMode == POSITION_TOPLEFT) - localCoordinate.x += hit.w/2.0; - if(positionMode == POSITION_TOPLEFT) - localCoordinate.y += hit.h/2.0; - - - onMouseDown(localCoordinate.x,localCoordinate.y); - - InputEvent *inputEvent = new InputEvent(Vector2(localCoordinate.x,localCoordinate.y)-parentAdjust, 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--) { - Vector2 adjust = parentAdjust; - if(positionMode == POSITION_TOPLEFT) - adjust += Vector2(floor(width/2.0), floor(height/2.0)); - MouseEventResult childRes = ((ScreenEntity*)children[i])->_onMouseDown(x,y, mouseButton, timestamp, adjust); - if(childRes.hit) - ret.hit = true; - if(childRes.blocked) - ret.blocked = true; - if(childRes.blocked) - break; - } - } - - return ret; -} - -Vector2 ScreenEntity::getScreenPosition() const { - Vector2 ret = getPosition2D(); - - if(parentEntity) { - return ret + ((ScreenEntity*)parentEntity)->getScreenPosition(); - } else { - return ret; - } -} - -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); - } - } -} diff --git a/Core/Contents/Source/PolyScreenEntityInstance.cpp b/Core/Contents/Source/PolyScreenEntityInstance.cpp deleted file mode 100755 index f0a438c06..000000000 --- a/Core/Contents/Source/PolyScreenEntityInstance.cpp +++ /dev/null @@ -1,334 +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" - -using namespace Polycode; - -ScreenEntityInstance::ScreenEntityInstance(const String& fileName) : ScreenEntity() { - rootEntity = NULL; - setPositionMode(ScreenEntity::POSITION_CENTER); - loadFromFile(fileName); - cloneUsingReload = false; -} - -ScreenEntityInstance::ScreenEntityInstance() { - rootEntity = NULL; - cloneUsingReload = true; -} - -ScreenEntityInstance::~ScreenEntityInstance() { -} - -Entity *ScreenEntityInstance::Clone(bool deepClone, bool ignoreEditorOnly) { - 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) { - if(cloneUsingReload) { - ScreenEntity::applyClone(clone, false, ignoreEditorOnly); - } else { - ScreenEntity::applyClone(clone, deepClone, ignoreEditorOnly); - ScreenEntityInstance *_clone = (ScreenEntityInstance*) clone; - _clone->fileName = fileName; - if(_clone->getNumChildren() > 0) { - _clone->rootEntity = (ScreenEntity*)_clone->getChildAtIndex(0); - } - } -} - -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 *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; - - 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) { - entity = new ScreenEntity(); - } - - entity->ownsChildren = true; - - if((*entry)["positionMode"]) { - entity->setPositionMode((*entry)["positionMode"]->intVal); - } else { - entity->setPositionMode(ScreenEntity::POSITION_CENTER); - } - - entity->color.r = (*entry)["colorR"]->NumberVal; - entity->color.g = (*entry)["colorG"]->NumberVal; - entity->color.b = (*entry)["colorB"]->NumberVal; - entity->color.a = (*entry)["colorA"]->NumberVal; - - 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); - - 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; -} - -ScreenEntity *ScreenEntityInstance::getRootEntity() { - return rootEntity; -} - -String ScreenEntityInstance::getFileName() const { - return fileName; -} - -bool ScreenEntityInstance::loadFromFile(const String& fileName) { - - if(rootEntity) { - removeChild(rootEntity); - delete rootEntity; - } - - this->fileName = fileName; - Object loadObject; - if(!loadObject.loadFromBinary(fileName)) { - Logger::log("Error loading entity instance.\n"); - } - ObjectEntry *root = loadObject.root["root"]; - - if(root) { - rootEntity = loadObjectEntryIntoEntity(root); - addChild(rootEntity); - } - 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 c6084c364..000000000 --- a/Core/Contents/Source/PolyScreenImage.cpp +++ /dev/null @@ -1,130 +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(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); - - positionMode = 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); - - positionMode = 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); - - positionMode = POSITION_TOPLEFT; -} - -ScreenImage::~ScreenImage() { - -} - -Entity *ScreenImage::Clone(bool deepClone, bool ignoreEditorOnly) { - ScreenImage *newImage = new ScreenImage(getTexture()->getResourcePath()); - applyClone(newImage, deepClone, ignoreEditorOnly); - return newImage; -} - -void ScreenImage::applyClone(Entity *clone, bool deepClone, bool ignoreEditorOnly) { - 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 4eee0d23d..000000000 --- a/Core/Contents/Source/PolyScreenLabel.cpp +++ /dev/null @@ -1,114 +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); - dropShadowImage = NULL; - texture = NULL; - updateTexture(); - positionMode = POSITION_TOPLEFT; - dropShadowImage = NULL; - colorAffectsChildren = false; - positionAtBaseline = true; -} - -ScreenLabel::~ScreenLabel() { - delete label; - delete dropShadowImage; -} - -Label *ScreenLabel::getLabel() const { - return label; -} - -Entity *ScreenLabel::Clone(bool deepClone, bool ignoreEditorOnly) { - 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) { - ScreenShape::applyClone(clone, deepClone, ignoreEditorOnly); - ScreenLabel *_clone = (ScreenLabel*) clone; - _clone->positionAtBaseline = positionAtBaseline; -} - -void ScreenLabel::addDropShadow(Color color, Number size, Number offsetX, Number offsetY) { - delete dropShadowImage; - Image *labelImage = new Image(label); - labelImage->fastBlur(size); - dropShadowImage = new ScreenImage(labelImage); - delete labelImage; - addChild(dropShadowImage); - dropShadowImage->setColor(color); - dropShadowImage->setPositionMode(POSITION_TOPLEFT); - dropShadowImage->setPosition(offsetX, offsetY); - dropShadowImage->getMesh()->getPolygon(0)->flipUVY(); -} - -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) { - 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 92c2495c8..000000000 --- a/Core/Contents/Source/PolyScreenManager.cpp +++ /dev/null @@ -1,99 +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() { - -} - -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); - } -// } -} - -/* -Screen *ScreenManager::createScreen(int screenType) { - - Screen *screen; - switch(screenType) { - case REGULAR_SCREEN: - screen = new Screen(); - break; - case PHYSICS_SCREEN: - screen = new PhysicsScreen(); - break; - } - - screen->setRenderer(CoreServices::getInstance()->getRenderer()); - screens.push_back(screen); - return screen; -} -*/ - -void ScreenManager::Update() { - - 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(); - } - } - } -} diff --git a/Core/Contents/Source/PolyScreenMesh.cpp b/Core/Contents/Source/PolyScreenMesh.cpp deleted file mode 100755 index 6b9706b2a..000000000 --- a/Core/Contents/Source/PolyScreenMesh.cpp +++ /dev/null @@ -1,123 +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 "PolyMesh.h" -#include "PolyRenderer.h" - -using namespace Polycode; - -ScreenMesh::ScreenMesh(Mesh *mesh) : ScreenEntity(), texture(NULL) { - this->mesh = mesh; - lineSmooth = false; - lineWidth = 1.0; - ownsMesh = true; - updateHitBox(); -} - -ScreenMesh::ScreenMesh(const String& fileName) : ScreenEntity(), texture(NULL) { - mesh = new Mesh(fileName); - lineSmooth = false; - lineWidth = 1.0; - -} - -ScreenMesh::ScreenMesh(int meshType) : ScreenEntity(), 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) { - texture = CoreServices::getInstance()->getMaterialManager()->createTextureFromFile(fileName, true, false); -} - -void ScreenMesh::loadTexture(Image *image) { - texture = CoreServices::getInstance()->getMaterialManager()->createTextureFromImage(image, true, false); -} - -void ScreenMesh::Render() { - Renderer *renderer = CoreServices::getInstance()->getRenderer(); - - renderer->setLineSize(lineWidth); - renderer->setLineSmooth(lineSmooth); - - 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 654d5cae2..000000000 --- a/Core/Contents/Source/PolyScreenShape.cpp +++ /dev/null @@ -1,255 +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(); - - positionMode = 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; - - positionMode = POSITION_CENTER; - strokeEnabled = copy.strokeEnabled; - -} - -Entity *ScreenShape::Clone(bool deepClone, bool ignoreEditorOnly) { - ScreenShape *newEntity = new ScreenShape(ScreenShape::SHAPE_RECT, 1,1); - applyClone(newEntity, deepClone, ignoreEditorOnly); - return newEntity; - -} - -void ScreenShape::applyClone(Entity *clone, bool deepClone, bool ignoreEditorOnly) { - 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; - case SHAPE_CUSTOM: - mesh->setMeshType(Mesh::TRIFAN_MESH); - customShapePoly = new Polygon(); - mesh->addPolygon(customShapePoly); - 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::addShapePoint(Number x, Number y) { - customShapePoly->addVertex(x,y,0,0,0); -} - -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 ff0d9f4ab..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) { - 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) { - 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 2e68ff446..000000000 --- a/Core/Contents/Source/PolyScreenSprite.cpp +++ /dev/null @@ -1,287 +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; - -ScreenSprite::ScreenSprite(const String& fileName) : ScreenShape(ScreenShape::SHAPE_RECT, 1, 1) { - - currentFrame = 0; - currentAnimation = NULL; - paused = false; - - loadFromFile(fileName); -} - -Entity *ScreenSprite::Clone(bool deepClone, bool ignoreEditorOnly) { - 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) { - 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; - } -} - - -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; - animations[i]->numFramesY = texture->getHeight() / spriteHeight; - 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; -} - -void SpriteAnimation::setOffsetsFromFrameString(const String& frames) { - framesOffsets.clear(); - vector frameNumbers = frames.split(","); - - int frameNumber; - int frameX; - int frameY; - - for(int i=0; i < frameNumbers.size(); i++) { - frameNumber = atoi(frameNumbers[i].c_str()); - frameX = frameNumber % numFramesX; - frameY = frameNumber/numFramesX; - framesOffsets.push_back(Vector2(spriteUVWidth * frameX, spriteUVHeight * frameY)); - } - - this->frames = frames; - numFrames = frameNumbers.size(); - -} - -SpriteAnimation *ScreenSprite::addAnimation(const String& name, const String& frames, Number speed) { - SpriteAnimation *newAnimation = new SpriteAnimation(); - - - newAnimation->numFramesX = texture->getWidth() / spriteWidth; - newAnimation->numFramesY = texture->getHeight() / spriteHeight; - 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) - 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 1583a14a5..dc7df36fa 100755 --- a/Core/Contents/Source/PolyServer.cpp +++ b/Core/Contents/Source/PolyServer.cpp @@ -28,19 +28,21 @@ using namespace Polycode; using std::vector; ServerClient::ServerClient() { - + clientReady = false; } ServerClient::~ServerClient() { } -void ServerClient::handlePacket(Packet *packet) { +ServerClientEvent *ServerClient::handlePacket(Packet *packet) { ServerClientEvent *event = new ServerClientEvent(); event->data = packet->data; event->dataSize = packet->header.size; - event->dataType = packet->header.type; + event->dataType = packet->header.type; + event->client = this; dispatchEvent(event, ServerClientEvent::EVENT_CLIENT_DATA); + return event; } Server::Server(unsigned int port, unsigned int rate, ServerWorld *world) : Peer(port) { @@ -76,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); + } } } @@ -99,10 +106,23 @@ 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); } +int Server::getNumServerClients() { + return clients.size(); +} + +ServerClient *Server::getServerClient(int index) { + if(index >= 0 && index < clients.size()) { + return clients[index]; + } else { + return NULL; + } +} + void Server::DisconnectClient(ServerClient *client) { sendReliableDataToClient(client, NULL, 0, PACKET_TYPE_DISONNECT); @@ -128,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: @@ -139,7 +162,15 @@ void Server::handlePacket(Packet *packet, PeerConnection *connection) { } break; default: + { client->handlePacket(packet); + ServerEvent *serverEvent = new ServerEvent(); + serverEvent->client = client; + serverEvent->data = packet->data; + serverEvent->dataSize = packet->header.size; + serverEvent->dataType = packet->header.type; + dispatchEvent(serverEvent, ServerEvent::EVENT_CLIENT_DATA); + } break; } } diff --git a/Core/Contents/Source/PolyShader.cpp b/Core/Contents/Source/PolyShader.cpp index 5efb2c63b..f111b9341 100755 --- a/Core/Contents/Source/PolyShader.cpp +++ b/Core/Contents/Source/PolyShader.cpp @@ -21,9 +21,66 @@ */ #include "PolyShader.h" +#include "PolyMatrix4.h" using namespace Polycode; +ShaderRenderTarget::ShaderRenderTarget() : PolyBase() { + texture = NULL; +} + +void *ProgramParam::createParamData(int type) { + switch (type) { + case PARAM_NUMBER: + { + Number *val = new Number(); + return (void*)val; + } + break; + case PARAM_VECTOR2: + { + Vector2 *val = new Vector2(); + return (void*)val; + } + break; + case PARAM_VECTOR3: + { + Vector3 *val = new Vector3(); + return (void*)val; + } + break; + case PARAM_COLOR: + { + Color *val = new Color(); + return (void*)val; + } + break; + case PARAM_MATRIX: + { + Matrix4 *val = new Matrix4(); + return (void*)val; + } + break; + default: + return NULL; + break; + } +} + +ShaderProgram::ShaderProgram(int type) : Resource(Resource::RESOURCE_PROGRAM) { + this->type = type; +} + +ShaderProgram::~ShaderProgram() { + +} + +void ShaderProgram::reloadResource() { + reloadProgram(); + Resource::reloadResource(); +} + + ShaderBinding::ShaderBinding(Shader *shader) { this->shader = shader; } @@ -35,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]; } @@ -60,25 +112,95 @@ LocalShaderParam *ShaderBinding::getLocalParamByName(const String& name) { return NULL; } -LocalShaderParam *ShaderBinding::addLocalParam(const String& name, void *ptr) { +LocalShaderParam * ShaderBinding::addParam(int type, const String& name) { + void *defaultData = ProgramParam::createParamData(type); LocalShaderParam *newParam = new LocalShaderParam(); + newParam->data = defaultData; newParam->name = name; - newParam->data = ptr; + 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); - if(binding->mode == RenderTargetBinding::MODE_IN) { - inTargetBindings.push_back(binding); - printf("Adding in target binding [%s] [%s]\n", binding->id.c_str(), binding->name.c_str()); - } else { - outTargetBindings.push_back(binding); - printf("Adding out target binding [%s]\n", binding->id.c_str()); + switch (binding->mode) { + case RenderTargetBinding::MODE_IN: + inTargetBindings.push_back(binding); + break; + case RenderTargetBinding::MODE_OUT: + outTargetBindings.push_back(binding); + break; + case RenderTargetBinding::MODE_COLOR: + colorTargetBindings.push_back(binding); + break; + case RenderTargetBinding::MODE_DEPTH: + depthTargetBindings.push_back(binding); + break; } } +void ShaderBinding::removeRenderTargetBinding(RenderTargetBinding *binding) { + for(int i=0; i < renderTargetBindings.size(); i++) { + if(renderTargetBindings[i] == binding) { + renderTargetBindings.erase(renderTargetBindings.begin() + i); + } + } + + for(int i=0; i < inTargetBindings.size(); i++) { + if(inTargetBindings[i] == binding) { + inTargetBindings.erase(inTargetBindings.begin() + i); + return; + } + } + for(int i=0; i < outTargetBindings.size(); i++) { + if(outTargetBindings[i] == binding) { + outTargetBindings.erase(outTargetBindings.begin() + i); + return; + } + } + + for(int i=0; i < colorTargetBindings.size(); i++) { + if(colorTargetBindings[i] == binding) { + colorTargetBindings.erase(colorTargetBindings.begin() + i); + return; + } + } + + for(int i=0; i < depthTargetBindings.size(); i++) { + if(depthTargetBindings[i] == binding) { + depthTargetBindings.erase(depthTargetBindings.begin() + i); + 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() { return renderTargetBindings.size(); } @@ -103,11 +225,42 @@ RenderTargetBinding *ShaderBinding::getOutTargetBinding(unsigned int index) { return outTargetBindings[index]; } +unsigned int ShaderBinding::getNumColorTargetBindings() { + return colorTargetBindings.size(); +} + +RenderTargetBinding *ShaderBinding::getColorTargetBinding(unsigned int index) { + return colorTargetBindings[index]; +} + +unsigned int ShaderBinding::getNumDepthTargetBindings() { + return depthTargetBindings.size(); +} + +RenderTargetBinding *ShaderBinding::getDepthTargetBinding(unsigned int index) { + return depthTargetBindings[index]; +} + Shader::Shader(int type) : Resource(Resource::RESOURCE_SHADER) { numSpotLights = 0; - numAreaLights = 0; + numPointLights = 0; this->type = type; + vp = NULL; + fp = NULL; +} + +int Shader::getExpectedParamType(String name) { + for(int i=0; i < expectedParams.size(); i++) { + if(expectedParams[i].name == name) { + return expectedParams[i].type; + } + } + return ProgramParam::PARAM_UNKNOWN; +} + +ShaderBinding *Shader::createBinding() { + return new ShaderBinding(this); } Shader::~Shader() { @@ -122,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 eee7c1047..1bb5118ca 100755 --- a/Core/Contents/Source/PolySkeleton.cpp +++ b/Core/Contents/Source/PolySkeleton.cpp @@ -27,20 +27,34 @@ #include "PolySceneLabel.h" #include "PolySceneLine.h" #include "PolyTween.h" +#include "PolyTweenManager.h" #include "OSBasics.h" using namespace Polycode; -Skeleton::Skeleton(const String& fileName) : SceneEntity() { +Skeleton *Skeleton::BlankSkeleton() { + return new Skeleton(); +} + +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 { @@ -55,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 { @@ -114,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) { @@ -126,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]; @@ -161,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]); @@ -183,174 +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; - } - /* - unsigned int numAnimations, activeBones,boneIndex,numPoints,numCurves, curveType; - OSBasics::read(&numAnimations, sizeof(unsigned int), 1, inFile); - //Logger::log("numAnimations: %d\n", numAnimations); - for(int i=0; i < numAnimations; i++) { - OSBasics::read(&namelen, sizeof(unsigned int), 1, inFile); - memset(buffer, 0, 1024); - OSBasics::read(buffer, 1, namelen, inFile); - float length; - OSBasics::read(&length, 1, sizeof(float), inFile); - SkeletonAnimation *newAnimation = new SkeletonAnimation(buffer, 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); } - */ 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); } @@ -359,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; @@ -371,7 +394,8 @@ BoneTrack::BoneTrack(Bone *bone, Number length) { LocX = NULL; LocY = NULL; LocZ = NULL; - initialized = false; + + quatCurve = NULL; } BoneTrack::~BoneTrack() { @@ -385,137 +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); - } -/* - if(QuatW) { - testTween = new BezierPathTween(&QuatWVec, QuatW, Tween::EASE_NONE, durTime, true); - pathTweens.push_back(testTween); - } - - if(QuatX) { - testTween = new BezierPathTween(&QuatXVec, QuatX, Tween::EASE_NONE, durTime, true); - pathTweens.push_back(testTween); - } - - if(QuatY) { - testTween = new BezierPathTween(&QuatYVec, QuatY, Tween::EASE_NONE, durTime, true); - pathTweens.push_back(testTween); - } - - if(QuatZ) { - testTween = new BezierPathTween(&QuatZVec, QuatZ, Tween::EASE_NONE, durTime, true); - pathTweens.push_back(testTween); - } -*/ +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] *= 1; //ScaleXVec.y; - scaleMatrix.m[1][1] *= 1; //ScaleYVec.y; - scaleMatrix.m[2][2] *= 1; //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); @@ -523,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]->weight = weight; + boneTracks[i]->Update(elapsed); + } +} + +void SkeletonAnimation::Reset() { for(int i=0; i < boneTracks.size(); i++) { - boneTracks[i]->Update(); + 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 e7760a034..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 @@ -38,8 +39,7 @@ Address::Address(unsigned int ip, unsigned int port) { setAddress(ip, port); } -Address::Address() { - +Address::Address() : uintAddress(0), port(0) { } void Address::setAddress(unsigned int ip, unsigned int port) { @@ -54,7 +54,10 @@ void Address::setAddress(unsigned int ip, unsigned int port) { } void Address::setAddress(String ipAsString, unsigned int port) { - unsigned int a,b,c,d; + unsigned int a = 127; + unsigned int b = 0; + unsigned int c = 0; + unsigned int d = 1; vector values = ipAsString.split("."); if(values.size() == 4) { diff --git a/Core/Contents/Source/PolySound.cpp b/Core/Contents/Source/PolySound.cpp index 767cc918b..61d731b4a 100755 --- a/Core/Contents/Source/PolySound.cpp +++ b/Core/Contents/Source/PolySound.cpp @@ -21,17 +21,45 @@ */ #include "PolySound.h" +#define OV_EXCLUDE_STATIC_CALLBACKS #include +#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); @@ -52,27 +80,101 @@ long custom_tellfunc(void *datasource) { return OSBasics::tell(file); } -Sound::Sound(const String& fileName) : sampleLength(-1) { - soundLoaded = false; - loadFile(fileName); +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, generateFloatBuffer); + setIsPositional(false); - setVolume(1.0); - setPitch(1.0); + checkALError("Construct from file: Finished"); } -Sound::Sound(const char *data, int size, int channels, int freq, int bps) : buffer(AL_NONE), soundSource(AL_NONE), sampleLength(-1) { - buffer = loadBytes(data, size, freq, channels, bps); +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, generateFloatBuffer); soundSource = GenSource(buffer); + setIsPositional(false); + reloadProperties(); + soundLoaded = true; - - setVolume(1.0); - setPitch(1.0); -} - -void Sound::loadFile(String fileName) { + + checkALError("Construct from data: Finished"); +} + +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); @@ -96,22 +198,28 @@ 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; soundSource = GenSource(buffer); + reloadProperties(); + + soundLoaded = true; + + checkALError("Sound load: complete"); +} + +void Sound::reloadProperties() { // Re-set stored properties into sound source. setVolume(volume); setPitch(pitch); setReferenceDistance(referenceDistance); setMaxDistance(maxDistance); - - soundLoaded = true; } String Sound::getFileName() { @@ -127,11 +235,19 @@ Number Sound::getPitch() { } Sound::~Sound() { - Logger::log("destroying sound...\n"); + + Stop(); + + alSourcei(soundSource, AL_BUFFER, 0); + alDeleteSources(1,&soundSource); - checkALError("destroying sound"); + checkALError("Destroying sound"); alDeleteBuffers(1, &buffer); - checkALError("deleting buffer"); + checkALError("Deleting buffer"); + if(streamingSound) { + alDeleteBuffers(STREAMING_BUFFER_COUNT, streamingBuffers); + Services()->getSoundManager()->unregisterStreamingSound(this); + } } void Sound::soundCheck(bool result, const String& err) { @@ -180,26 +296,31 @@ bool Sound::isPlaying() { void Sound::setVolume(Number newVolume) { this->volume = newVolume; alSourcef(soundSource, AL_GAIN, newVolume); + checkALError("Set volume"); } void Sound::setPitch(Number newPitch) { this->pitch = newPitch; alSourcef(soundSource, AL_PITCH, newPitch); + checkALError("Set pitch"); } void Sound::setSoundPosition(Vector3 position) { if(isPositional) alSource3f(soundSource,AL_POSITION, position.x, position.y, position.z); + checkALError("Set sound position"); } void Sound::setSoundVelocity(Vector3 velocity) { if(isPositional) alSource3f(soundSource,AL_VELOCITY, velocity.x, velocity.y, velocity.z); + checkALError("Set sound velocity"); } void Sound::setSoundDirection(Vector3 direction) { if(isPositional) alSource3f(soundSource,AL_DIRECTION, direction.x, direction.y, direction.z); + checkALError("Set sound direction"); } void Sound::setOffset(int off) { @@ -243,6 +364,7 @@ void Sound::seekTo(Number time) { if(time > getPlaybackDuration()) return; alSourcef(soundSource, AL_SEC_OFFSET, time); + checkALError("Seek"); } int Sound::getSampleLength() { @@ -256,12 +378,14 @@ void Sound::setPositionalProperties(Number referenceDistance, Number maxDistance void Sound::setReferenceDistance(Number referenceDistance) { this->referenceDistance = referenceDistance; - alSourcef(soundSource,AL_REFERENCE_DISTANCE, referenceDistance); + alSourcef(soundSource, AL_REFERENCE_DISTANCE, referenceDistance); + checkALError("Set reference distance"); } void Sound::setMaxDistance(Number maxDistance) { this->maxDistance = maxDistance; alSourcef(soundSource,AL_MAX_DISTANCE, maxDistance); + checkALError("Set max distance"); } Number Sound::getReferenceDistance() { @@ -272,7 +396,6 @@ Number Sound::getMaxDistance() { return maxDistance; } - void Sound::setIsPositional(bool isPositional) { this->isPositional = isPositional; if(isPositional) { @@ -283,15 +406,13 @@ void Sound::setIsPositional(bool isPositional) { alSource3f(soundSource,AL_VELOCITY, 0,0,0); alSource3f(soundSource,AL_DIRECTION, 0,0,0); } + checkALError("Set is-positional"); } ALenum Sound::checkALError(const String& operation) { ALenum error = alGetError(); if(error != AL_NO_ERROR) { switch(error) { - case AL_NO_ERROR: - soundError(operation + ": " +ALNoErrorStr); - break; case AL_INVALID_NAME: soundError(operation +": " + ALInvalidNameStr); break; @@ -350,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; @@ -367,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); @@ -423,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; @@ -439,8 +581,10 @@ ALuint Sound::loadWAV(const String& fileName) { // Open for binary reading f = OSBasics::open(fileName.c_str(), "rb"); - if (!f) + if (!f) { soundError("LoadWav: Could not load wav from " + fileName); + return buffer; + } // buffers char magic[5]; @@ -521,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 8635736a1..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,10 +72,9 @@ size_t String::getDataSizeWithEncoding(int encoding) const { return contents.size(); } default: - return NULL; + return 0; } } - const char *String::getDataWithEncoding(int encoding) const { switch(encoding) { case ENCODING_UTF8: { @@ -156,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 +189,16 @@ String String::toUpperCase() const { } -String String::NumberToString(Number value) { - char temp[128]; - sprintf(temp, "%.2f", value); - return String(temp); +String String::NumberToString(Number value, int precision) { + 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()); } @@ -210,7 +219,7 @@ void utf8toWStr(WStr& dest, const Str& src){ dest.clear(); wchar_t w = 0; int bytes = 0; - wchar_t err = L'�'; + wchar_t err = L'\uFFFD'; for (size_t i = 0; i < src.size(); i++){ unsigned char c = (unsigned char)src[i]; if (c <= 0x7f){//first byte @@ -267,6 +276,7 @@ void wstrToUtf8(Str& dest, const WStr& src){ dest.push_back(0x80| (w & 0x3f)); } else if (w <= 0x10ffff){ + // FIXME: wchar_t is 16 bits on some platforms, should convert from UTF16 to UTF8 dest.push_back(0xf0 | ((w >> 18)& 0x07)); dest.push_back(0x80| ((w >> 12) & 0x3f)); dest.push_back(0x80| ((w >> 6) & 0x3f)); 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 91a561e3e..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; @@ -55,7 +56,14 @@ Texture::Texture(unsigned int width, unsigned int height, char *textureData,bool scrollSpeedY = 0; scrollOffsetX = 0; scrollOffsetY = 0; - resourcePath = ""; +} + +void Texture::reloadResource() { + Image *image = new Image(getResourcePath()); + setImageData(image); + recreateFromImageData(); + delete image; + Resource::reloadResource(); } int Texture::getWidth() const { @@ -67,6 +75,7 @@ int Texture::getHeight() const { } Texture::~Texture(){ + printf("DELETING TEXTURE: [%s][%s]\n", getResourceName().c_str(), getResourcePath().c_str()); free(textureData); } @@ -80,7 +89,7 @@ void Texture::setImageData(Image *data) { pixelSize = 4; break; case Image::IMAGE_FP16: - pixelSize = 16; + pixelSize = 12; break; default: pixelSize = 4; @@ -105,14 +114,6 @@ Texture::Texture(Image *image) : Resource(Resource::RESOURCE_TEXTURE) { } -void Texture::setResourcePath(const String& newPath) { - resourcePath = newPath; -} - -const String& Texture::getResourcePath() const { - return resourcePath; -} - void Texture::updateScroll(int elapsed) { Number ef = ((Number)(elapsed))/1000.0f; scrollOffsetX += scrollSpeedX*ef; 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 cffad1a57..8cbfcac50 100644 --- a/Core/Contents/Source/PolyWinCore.cpp +++ b/Core/Contents/Source/PolyWinCore.cpp @@ -34,23 +34,30 @@ #include #include -#include -#include -#include - -#include -#include -#ifndef _MINGW -#include -#endif +#include +#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 +#endif +#else PFNWGLSWAPINTERVALEXTPROC wglSwapIntervalEXT = NULL; PFNWGLGETSWAPINTERVALEXTPROC wglGetSwapIntervalEXT = NULL; +PFNWGLCHOOSEPIXELFORMATARBPROC wglChoosePixelFormatARB = NULL; + +#endif using namespace Polycode; long getThreadID() { - return 0; + return GetCurrentThreadId(); } extern Win32Core *core; @@ -78,11 +85,15 @@ 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); @@ -100,7 +111,6 @@ Win32Core::Win32Core(PolycodeViewBase *view, int _xRes, int _yRes, bool fullScre hDC = NULL; hRC = NULL; - PixelFormat = 0; this->aaLevel = 999; @@ -110,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; @@ -121,23 +136,64 @@ Win32Core::Win32Core(PolycodeViewBase *view, int _xRes, int _yRes, bool fullScre Logger::log("Error initializing sockets!\n"); } - ((OpenGLRenderer*)renderer)->initOSSpecific(); + ((OpenGLRenderer*)renderer)->Init(); - wglSwapIntervalEXT = (PFNWGLSWAPINTERVALEXTPROC) wglGetProcAddress("wglSwapIntervalEXT"); - wglGetSwapIntervalEXT = (PFNWGLGETSWAPINTERVALEXTPROC) wglGetProcAddress("wglGetSwapIntervalEXT"); + LARGE_INTEGER li; + QueryPerformanceFrequency(&li); + pcFreq = double(li.QuadPart)/1000.0; setVSync(vSync); CoreServices::getInstance()->installModule(new GLSLShaderModule()); } +Number Win32Core::getBackingXRes() { + return getXRes() *scaleFactor; +} + +Number Win32Core::getBackingYRes() { + return getYRes() *scaleFactor; +} + Win32Core::~Win32Core() { shutdownGamepad(); destroyContext(); } void Win32Core::enableMouse(bool newval) { - ShowCursor(newval); + ShowCursor(newval); + + Core::enableMouse(newval); +} + +void Win32Core::captureMouse(bool newval) { + // Capture the mouse in the window holding + // our polycode screen. + 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); } void Win32Core::warpCursor(int x, int y) { @@ -151,22 +207,26 @@ void Win32Core::warpCursor(int x, int y) { } unsigned int Win32Core::getTicks() { - return GetTickCount(); + LARGE_INTEGER li; + QueryPerformanceCounter(&li); + return (unsigned int)(li.QuadPart / pcFreq); } -bool Win32Core::Update() { +void Win32Core::Render() { + renderer->BeginRender(); + services->Render(); + renderer->EndRender(); + SwapBuffers(hDC); +} + +bool Win32Core::systemUpdate() { if(!running) return false; - + captureMouse(Core::mouseCaptured); + doSleep(); checkEvents(); Gamepad_processEvents(); - - renderer->BeginRender(); updateCore(); - renderer->EndRender(); - - SwapBuffers(hDC); - doSleep(); return running; } @@ -180,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; @@ -188,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; @@ -197,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 @@ -207,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); @@ -233,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 } @@ -288,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() { @@ -304,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 ) @@ -480,13 +610,18 @@ PolyKEY Win32Core::mapKey(LPARAM lParam, WPARAM wParam) { wParam = VK_RCONTROL; else wParam = VK_LCONTROL; - break; - case 33: + break; + case VK_MENU: if ( lParam&EXTENDED_KEYMASK ) wParam = VK_RMENU; else wParam = VK_LMENU; break; + case VK_SHIFT: + // Use MapVirtualKey to determine whether it's LSHIFT or RSHIFT by scancode. + UINT scancode = (lParam & 0x00ff0000) >> 16; + wParam = MapVirtualKey(scancode, MAPVK_VSC_TO_VK_EX); + break; } return keyMap[(unsigned int)wParam]; @@ -516,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; @@ -555,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; @@ -571,10 +708,79 @@ void Win32Core::handleTouchEvent(LPARAM lParam, WPARAM wParam) { newEvent.touch = touches[i]; win32Events.push_back(newEvent); } - } + } + } + } + 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); + + unlockMutex(eventMutex); } #endif @@ -588,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; @@ -691,6 +898,12 @@ void Win32Core::checkEvents() { case InputEvent::EVENT_MOUSEUP: input->setMouseButtonState(event.mouseButton, false, getTicks()); break; + case InputEvent::EVENT_MOUSEWHEEL_UP: + input->mouseWheelUp(getTicks()); + break; + case InputEvent::EVENT_MOUSEWHEEL_DOWN: + input->mouseWheelDown(getTicks()); + break; case InputEvent::EVENT_KEYDOWN: if(!checkSpecialKeyEvents((event.keyCode))) { input->setKeyState(event.keyCode, (char)event.unicodeChar, true, getTicks()); @@ -698,7 +911,7 @@ void Win32Core::checkEvents() { break; case InputEvent::EVENT_KEYUP: input->setKeyState(event.keyCode, (char)event.unicodeChar, false, getTicks()); - break; + break; } break; } @@ -960,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); } @@ -991,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() { @@ -1082,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)); } @@ -1106,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); @@ -1238,7 +1526,7 @@ void Win32Core::setCursor(int cursorType) { } SetCursor(cursor); - SetClassLong(hWnd, GCL_HCURSOR, (DWORD)cursor); + SetClassLongPtr(hWnd, GCLP_HCURSOR, (DWORD)cursor); } void Win32Core::copyStringToClipboard(const String& str) { @@ -1263,4 +1551,4 @@ String Win32Core::getClipboardString() { String retString = String(c); GlobalUnlock(clip0); return retString; -} \ No newline at end of file +} 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;iToText() ) { - fprintf( cfile, "\n" ); + fprintf( cfile, NEWLINE ); } node->Print( cfile, depth+1 ); } - fprintf( cfile, "\n" ); + fprintf( cfile, NEWLINE ); for( i=0; ifile ); @@ -1147,7 +1151,7 @@ void TiXmlDocument::Print( FILE* cfile, int depth ) const for ( const TiXmlNode* node=FirstChild(); node; node=node->NextSibling() ) { node->Print( cfile, depth ); - fprintf( cfile, "\n" ); + fprintf( cfile, NEWLINE ); } } @@ -1333,11 +1337,11 @@ void TiXmlText::Print( FILE* cfile, int depth ) const if ( cdata ) { int i; - fprintf( cfile, "\n" ); + fprintf( cfile, NEWLINE ); for ( i=0; i\n", value.c_str() ); // unformatted output + fprintf( cfile, "" NEWLINE, value.c_str() ); // unformatted output } else { diff --git a/Core/Contents/Source/tinyxmlparser.cpp b/Core/Contents/Source/tinyxmlparser.cpp index 71d61adf2..6dcc45cfa 100755 --- a/Core/Contents/Source/tinyxmlparser.cpp +++ b/Core/Contents/Source/tinyxmlparser.cpp @@ -354,7 +354,7 @@ const char* TiXmlBase::SkipWhiteSpace( const char* p, TiXmlEncoding encoding ) } else { - while ( *p && IsWhiteSpace( *p ) || *p == '\n' || *p =='\r' ) + while ( *p && (IsWhiteSpace( *p ) || *p == '\n' || *p =='\r') ) ++p; } diff --git a/Dependencies/CMakeLists.txt b/Dependencies/CMakeLists.txt index 66844201a..0649d3376 100644 --- a/Dependencies/CMakeLists.txt +++ b/Dependencies/CMakeLists.txt @@ -125,6 +125,8 @@ ENDIF(APPLE) SET(internal_ASSIMP ON) #ENDIF(ASSIMP_FOUND) +SET(internal_LIBARCHIVE ON) + OPTION(POLYCODE_DEPS_GLEXT "Download GL/glext.h header" ${internal_GLEXT}) OPTION(POLYCODE_DEPS_WGLEXT "Download GL/wglext.h header" ${internal_WGLEXT}) @@ -138,6 +140,7 @@ OPTION(POLYCODE_DEPS_LUA51 "Download and build the Lua51 package" ${internal_LUA OPTION(POLYCODE_DEPS_BOX2D "Download and build the Box2D package" ${internal_BOX2D}) OPTION(POLYCODE_DEPS_BULLET "Download and build the Bullet package" ${internal_BULLET}) OPTION(POLYCODE_DEPS_ASSIMP "Download and build the Assimp package" ${internal_ASSIMP}) +OPTION(POLYCODE_DEPS_LIBARCHIVE "Download and build the LibArchive package" ${internal_LIBARCHIVE}) IF(POLYCODE_DEPS_PNG) INCLUDE(ExternalPNG) @@ -175,6 +178,10 @@ IF(POLYCODE_DEPS_ASSIMP) INCLUDE(ExternalAssimp) ENDIF(POLYCODE_DEPS_ASSIMP) +IF(POLYCODE_DEPS_LIBARCHIVE) + INCLUDE(ExternalLibArchive) +ENDIF(POLYCODE_DEPS_LIBARCHIVE) + # Use SDL on non-Apple unixes #IF(UNIX AND NOT APPLE) # FIND_PACKAGE(SDL REQUIRED) diff --git a/Documentation/CMakeLists.txt b/Documentation/CMakeLists.txt index 949c08853..6c571e809 100644 --- a/Documentation/CMakeLists.txt +++ b/Documentation/CMakeLists.txt @@ -4,24 +4,34 @@ FIND_PACKAGE(Doxygen) IF(DOXYGEN_FOUND) +FILE(GLOB HEADER_DEPENDENCIES_CORE ${Polycode_SOURCE_DIR}/Core/Contents/Include/*.h) +FILE(GLOB HEADER_DEPENDENCIES_2DPHYSICS ${Polycode_SOURCE_DIR}/Modules/Contents/2DPhysics/Include/*.h) +FILE(GLOB HEADER_DEPENDENCIES_3DPHYSICS ${Polycode_SOURCE_DIR}/Modules/Contents/3DPhysics/Include/*.h) +FILE(GLOB HEADER_DEPENDENCIES_UI ${Polycode_SOURCE_DIR}/Modules/Contents/UI/Include/*.h) + ADD_CUSTOM_COMMAND( OUTPUT doc_cmd COMMAND ${DOXYGEN_EXECUTABLE} ${Polycode_SOURCE_DIR}/Documentation/Doxygen/Polycode.doxygen COMMAND ${DOXYGEN_EXECUTABLE} ${Polycode_SOURCE_DIR}/Documentation/Doxygen/Physics2D.doxygen COMMAND ${DOXYGEN_EXECUTABLE} ${Polycode_SOURCE_DIR}/Documentation/Doxygen/Physics3D.doxygen +COMMAND ${DOXYGEN_EXECUTABLE} ${Polycode_SOURCE_DIR}/Documentation/Doxygen/Polycode_ui.doxygen +COMMAND ${CMAKE_COMMAND} -E touch ${CMAKE_CURRENT_BINARY_DIR}/doc_cmd # Generate the given output file(empty file), to keep track of whether we need to rebuild. WORKING_DIRECTORY ${Polycode_SOURCE_DIR}/Documentation/Doxygen/ +DEPENDS ${HEADER_DEPENDENCIES_CORE} ${HEADER_DEPENDENCIES_2DPHYSICS} ${HEADER_DEPENDENCIES_3DPHYSICS} ${HEADER_DEPENDENCIES_UI} COMMENT "Generating Polycode API documentation with Doxygen" VERBATIM ) ADD_CUSTOM_TARGET(doc ALL DEPENDS doc_cmd) -INSTALL(DIRECTORY Doxygen/output/standalone/Polycode +INSTALL(DIRECTORY Doxygen/output/standalone/Core DESTINATION Docs) INSTALL(DIRECTORY Doxygen/output/standalone/Physics2D DESTINATION Docs/Modules) INSTALL(DIRECTORY Doxygen/output/standalone/Physics3D DESTINATION Docs/Modules) - +INSTALL(DIRECTORY Doxygen/output/standalone/PolycodeUI + DESTINATION Docs/Modules) + ENDIF(DOXYGEN_FOUND) ENDIF(POLYCODE_BUILD_DOCS) diff --git a/Documentation/Doxygen/Physics2D.doxygen b/Documentation/Doxygen/Physics2D.doxygen index 1d6dd9c82..4639b87fd 100644 --- a/Documentation/Doxygen/Physics2D.doxygen +++ b/Documentation/Doxygen/Physics2D.doxygen @@ -1,103 +1,114 @@ -# Doxyfile 1.7.4 +# Doxyfile 1.8.6 # This file describes the settings to be used by the documentation system -# doxygen (www.doxygen.org) for a project +# doxygen (www.doxygen.org) for a project. # -# All text after a hash (#) is considered a comment and will be ignored +# All text after a double hash (##) is considered a comment and is placed in +# front of the TAG it is preceding. +# +# All text after a single hash (#) is considered a comment and will be ignored. # The format is: -# TAG = value [value, ...] -# For lists items can also be appended using: -# TAG += value [value, ...] -# Values that contain spaces should be placed between quotes (" ") +# TAG = value [value, ...] +# For lists, items can also be appended using: +# TAG += value [value, ...] +# Values that contain spaces should be placed between quotes (\" \"). #--------------------------------------------------------------------------- # Project related configuration options #--------------------------------------------------------------------------- -# This tag specifies the encoding used for all characters in the config file -# that follow. The default is UTF-8 which is also the encoding used for all -# text before the first occurrence of this tag. Doxygen uses libiconv (or the -# iconv built into libc) for the transcoding. See -# http://www.gnu.org/software/libiconv for the list of possible encodings. +# This tag specifies the encoding used for all characters in the config file +# that follow. The default is UTF-8 which is also the encoding used for all text +# before the first occurrence of this tag. Doxygen uses libiconv (or the iconv +# built into libc) for the transcoding. See http://www.gnu.org/software/libiconv +# for the list of possible encodings. +# The default value is: UTF-8. DOXYFILE_ENCODING = UTF-8 -# The PROJECT_NAME tag is a single word (or a sequence of words surrounded -# by quotes) that should identify the project. +# The PROJECT_NAME tag is a single word (or a sequence of words surrounded by +# double-quotes, unless you are using Doxywizard) that should identify the +# project for which the documentation is generated. This name is used in the +# title of most generated pages and in a few other places. +# The default value is: My Project. PROJECT_NAME = Physics2D -# The PROJECT_NUMBER tag can be used to enter a project or revision number. -# This could be handy for archiving the generated documentation or -# if some version control system is used. +# The PROJECT_NUMBER tag can be used to enter a project or revision number. This +# could be handy for archiving the generated documentation or if some version +# control system is used. -PROJECT_NUMBER = +PROJECT_NUMBER = -# Using the PROJECT_BRIEF tag one can provide an optional one line description -# for a project that appears at the top of each page and should give viewer -# a quick idea about the purpose of the project. Keep the description short. +# Using the PROJECT_BRIEF tag one can provide an optional one line description +# for a project that appears at the top of each page and should give viewer a +# quick idea about the purpose of the project. Keep the description short. -PROJECT_BRIEF = +PROJECT_BRIEF = -# With the PROJECT_LOGO tag one can specify an logo or icon that is -# included in the documentation. The maximum height of the logo should not -# exceed 55 pixels and the maximum width should not exceed 200 pixels. -# Doxygen will copy the logo to the output directory. +# With the PROJECT_LOGO tag one can specify an logo or icon that is included in +# the documentation. The maximum height of the logo should not exceed 55 pixels +# and the maximum width should not exceed 200 pixels. Doxygen will copy the logo +# to the output directory. -PROJECT_LOGO = +PROJECT_LOGO = -# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) -# base path where the generated documentation will be put. -# If a relative path is entered, it will be relative to the location -# where doxygen was started. If left blank the current directory will be used. +# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path +# into which the generated documentation will be written. If a relative path is +# entered, it will be relative to the location where doxygen was started. If +# left blank the current directory will be used. OUTPUT_DIRECTORY = ./output/standalone/Physics2D -# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create -# 4096 sub-directories (in 2 levels) under the output directory of each output -# format and will distribute the generated files over these directories. -# Enabling this option can be useful when feeding doxygen a huge amount of -# source files, where putting all generated files in the same directory would -# otherwise cause performance problems for the file system. +# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create 4096 sub- +# directories (in 2 levels) under the output directory of each output format and +# will distribute the generated files over these directories. Enabling this +# option can be useful when feeding doxygen a huge amount of source files, where +# putting all generated files in the same directory would otherwise causes +# performance problems for the file system. +# The default value is: NO. CREATE_SUBDIRS = NO -# The OUTPUT_LANGUAGE tag is used to specify the language in which all -# documentation generated by doxygen is written. Doxygen will use this -# information to generate all constant output in the proper language. -# The default language is English, other supported languages are: -# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional, -# Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German, -# Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English -# messages), Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian, -# Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrillic, Slovak, -# Slovene, Spanish, Swedish, Ukrainian, and Vietnamese. +# The OUTPUT_LANGUAGE tag is used to specify the language in which all +# documentation generated by doxygen is written. Doxygen will use this +# information to generate all constant output in the proper language. +# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Catalan, Chinese, +# Chinese-Traditional, Croatian, Czech, Danish, Dutch, English (United States), +# Esperanto, Farsi (Persian), Finnish, French, German, Greek, Hungarian, +# Indonesian, Italian, Japanese, Japanese-en (Japanese with English messages), +# Korean, Korean-en (Korean with English messages), Latvian, Lithuanian, +# Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, Romanian, Russian, +# Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, Swedish, Turkish, +# Ukrainian and Vietnamese. +# The default value is: English. OUTPUT_LANGUAGE = English -# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will -# include brief member descriptions after the members that are listed in -# the file and class documentation (similar to JavaDoc). -# Set to NO to disable this. +# If the BRIEF_MEMBER_DESC tag is set to YES doxygen will include brief member +# descriptions after the members that are listed in the file and class +# documentation (similar to Javadoc). Set to NO to disable this. +# The default value is: YES. BRIEF_MEMBER_DESC = YES -# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend -# the brief description of a member or function before the detailed description. -# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the +# If the REPEAT_BRIEF tag is set to YES doxygen will prepend the brief +# description of a member or function before the detailed description +# +# Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the # brief descriptions will be completely suppressed. +# The default value is: YES. REPEAT_BRIEF = YES -# This tag implements a quasi-intelligent brief description abbreviator -# that is used to form the text in various listings. Each string -# in this list, if found as the leading text of the brief description, will be -# stripped from the text and the result after processing the whole list, is -# used as the annotated text. Otherwise, the brief description is used as-is. -# If left blank, the following values are used ("$name" is automatically -# replaced with the name of the entity): "The $name class" "The $name widget" -# "The $name file" "is" "provides" "specifies" "contains" -# "represents" "a" "an" "the" +# This tag implements a quasi-intelligent brief description abbreviator that is +# used to form the text in various listings. Each string in this list, if found +# as the leading text of the brief description, will be stripped from the text +# and the result, after processing the whole list, is used as the annotated +# text. Otherwise, the brief description is used as-is. If left blank, the +# following values are used ($name is automatically replaced with the name of +# the entity):The $name class, The $name widget, The $name file, is, provides, +# specifies, contains, represents, a, an and the. ABBREVIATE_BRIEF = "The $name class" \ "The $name widget" \ @@ -111,531 +122,657 @@ ABBREVIATE_BRIEF = "The $name class" \ an \ the -# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then -# Doxygen will generate a detailed section even if there is only a brief +# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then +# doxygen will generate a detailed section even if there is only a brief # description. +# The default value is: NO. ALWAYS_DETAILED_SEC = NO -# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all -# inherited members of a class in the documentation of that class as if those -# members were ordinary class members. Constructors, destructors and assignment +# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all +# inherited members of a class in the documentation of that class as if those +# members were ordinary class members. Constructors, destructors and assignment # operators of the base classes will not be shown. +# The default value is: NO. INLINE_INHERITED_MEMB = NO -# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full -# path before files name in the file list and in the header files. If set -# to NO the shortest path that makes the file name unique will be used. +# If the FULL_PATH_NAMES tag is set to YES doxygen will prepend the full path +# before files name in the file list and in the header files. If set to NO the +# shortest path that makes the file name unique will be used +# The default value is: YES. FULL_PATH_NAMES = YES -# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag -# can be used to strip a user-defined part of the path. Stripping is -# only done if one of the specified strings matches the left-hand part of -# the path. The tag can be used to show relative paths in the file list. -# If left blank the directory from which doxygen is run is used as the -# path to strip. +# The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path. +# Stripping is only done if one of the specified strings matches the left-hand +# part of the path. The tag can be used to show relative paths in the file list. +# If left blank the directory from which doxygen is run is used as the path to +# strip. +# +# Note that you can specify absolute paths here, but also relative paths, which +# will be relative from the directory where doxygen is started. +# This tag requires that the tag FULL_PATH_NAMES is set to YES. -STRIP_FROM_PATH = +STRIP_FROM_PATH = -# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of -# the path mentioned in the documentation of a class, which tells -# the reader which header file to include in order to use a class. -# If left blank only the name of the header file containing the class -# definition is used. Otherwise one should specify the include paths that -# are normally passed to the compiler using the -I flag. +# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the +# path mentioned in the documentation of a class, which tells the reader which +# header file to include in order to use a class. If left blank only the name of +# the header file containing the class definition is used. Otherwise one should +# specify the list of include paths that are normally passed to the compiler +# using the -I flag. -STRIP_FROM_INC_PATH = +STRIP_FROM_INC_PATH = -# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter -# (but less readable) file names. This can be useful if your file system -# doesn't support long names like on DOS, Mac, or CD-ROM. +# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but +# less readable) file names. This can be useful is your file systems doesn't +# support long names like on DOS, Mac, or CD-ROM. +# The default value is: NO. SHORT_NAMES = NO -# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen -# will interpret the first line (until the first dot) of a JavaDoc-style -# comment as the brief description. If set to NO, the JavaDoc -# comments will behave just like regular Qt-style comments -# (thus requiring an explicit @brief command for a brief description.) +# If the JAVADOC_AUTOBRIEF tag is set to YES then doxygen will interpret the +# first line (until the first dot) of a Javadoc-style comment as the brief +# description. If set to NO, the Javadoc-style will behave just like regular Qt- +# style comments (thus requiring an explicit @brief command for a brief +# description.) +# The default value is: NO. JAVADOC_AUTOBRIEF = YES -# If the QT_AUTOBRIEF tag is set to YES then Doxygen will -# interpret the first line (until the first dot) of a Qt-style -# comment as the brief description. If set to NO, the comments -# will behave just like regular Qt-style comments (thus requiring -# an explicit \brief command for a brief description.) +# If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first +# line (until the first dot) of a Qt-style comment as the brief description. If +# set to NO, the Qt-style will behave just like regular Qt-style comments (thus +# requiring an explicit \brief command for a brief description.) +# The default value is: NO. QT_AUTOBRIEF = NO -# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen -# treat a multi-line C++ special comment block (i.e. a block of //! or /// -# comments) as a brief description. This used to be the default behaviour. -# The new default is to treat a multi-line C++ comment block as a detailed -# description. Set this tag to YES if you prefer the old behaviour instead. +# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a +# multi-line C++ special comment block (i.e. a block of //! or /// comments) as +# a brief description. This used to be the default behavior. The new default is +# to treat a multi-line C++ comment block as a detailed description. Set this +# tag to YES if you prefer the old behavior instead. +# +# Note that setting this tag to YES also means that rational rose comments are +# not recognized any more. +# The default value is: NO. MULTILINE_CPP_IS_BRIEF = NO -# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented -# member inherits the documentation from any documented member that it -# re-implements. +# If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the +# documentation from any documented member that it re-implements. +# The default value is: YES. INHERIT_DOCS = YES -# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce -# a new page for each member. If set to NO, the documentation of a member will -# be part of the file/class/namespace that contains it. +# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce a +# new page for each member. If set to NO, the documentation of a member will be +# part of the file/class/namespace that contains it. +# The default value is: NO. SEPARATE_MEMBER_PAGES = NO -# The TAB_SIZE tag can be used to set the number of spaces in a tab. -# Doxygen uses this value to replace tabs by spaces in code fragments. +# The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen +# uses this value to replace tabs by spaces in code fragments. +# Minimum value: 1, maximum value: 16, default value: 4. TAB_SIZE = 8 -# This tag can be used to specify a number of aliases that acts -# as commands in the documentation. An alias has the form "name=value". -# For example adding "sideeffect=\par Side Effects:\n" will allow you to -# put the command \sideeffect (or @sideeffect) in the documentation, which -# will result in a user-defined paragraph with heading "Side Effects:". -# You can put \n's in the value part of an alias to insert newlines. +# This tag can be used to specify a number of aliases that act as commands in +# the documentation. An alias has the form: +# name=value +# For example adding +# "sideeffect=@par Side Effects:\n" +# will allow you to put the command \sideeffect (or @sideeffect) in the +# documentation, which will result in a user-defined paragraph with heading +# "Side Effects:". You can put \n's in the value part of an alias to insert +# newlines. + +ALIASES = + +# This tag can be used to specify a number of word-keyword mappings (TCL only). +# A mapping has the form "name=value". For example adding "class=itcl::class" +# will allow you to use the command class in the itcl::class meaning. -ALIASES = +TCL_SUBST = -# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C -# sources only. Doxygen will then generate output that is more tailored for C. -# For instance, some of the names that are used will be different. The list -# of all members will be omitted, etc. +# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources +# only. Doxygen will then generate output that is more tailored for C. For +# instance, some of the names that are used will be different. The list of all +# members will be omitted, etc. +# The default value is: NO. OPTIMIZE_OUTPUT_FOR_C = NO -# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java -# sources only. Doxygen will then generate output that is more tailored for -# Java. For instance, namespaces will be presented as packages, qualified -# scopes will look different, etc. +# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or +# Python sources only. Doxygen will then generate output that is more tailored +# for that language. For instance, namespaces will be presented as packages, +# qualified scopes will look different, etc. +# The default value is: NO. OPTIMIZE_OUTPUT_JAVA = NO -# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran -# sources only. Doxygen will then generate output that is more tailored for -# Fortran. +# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran +# sources. Doxygen will then generate output that is tailored for Fortran. +# The default value is: NO. OPTIMIZE_FOR_FORTRAN = NO -# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL -# sources. Doxygen will then generate output that is tailored for -# VHDL. +# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL +# sources. Doxygen will then generate output that is tailored for VHDL. +# The default value is: NO. OPTIMIZE_OUTPUT_VHDL = NO -# Doxygen selects the parser to use depending on the extension of the files it -# parses. With this tag you can assign which parser to use for a given extension. -# Doxygen has a built-in mapping, but you can override or extend it using this -# tag. The format is ext=language, where ext is a file extension, and language -# is one of the parsers supported by doxygen: IDL, Java, Javascript, CSharp, C, -# C++, D, PHP, Objective-C, Python, Fortran, VHDL, C, C++. For instance to make -# doxygen treat .inc files as Fortran files (default is PHP), and .f files as C -# (default is Fortran), use: inc=Fortran f=C. Note that for custom extensions -# you also need to set FILE_PATTERNS otherwise the files are not read by doxygen. - -EXTENSION_MAPPING = - -# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want -# to include (a tag file for) the STL sources as input, then you should -# set this tag to YES in order to let doxygen match functions declarations and -# definitions whose arguments contain STL classes (e.g. func(std::string); v.s. -# func(std::string) {}). This also makes the inheritance and collaboration +# Doxygen selects the parser to use depending on the extension of the files it +# parses. With this tag you can assign which parser to use for a given +# extension. Doxygen has a built-in mapping, but you can override or extend it +# using this tag. The format is ext=language, where ext is a file extension, and +# language is one of the parsers supported by doxygen: IDL, Java, Javascript, +# C#, C, C++, D, PHP, Objective-C, Python, Fortran, VHDL. For instance to make +# doxygen treat .inc files as Fortran files (default is PHP), and .f files as C +# (default is Fortran), use: inc=Fortran f=C. +# +# Note For files without extension you can use no_extension as a placeholder. +# +# Note that for custom extensions you also need to set FILE_PATTERNS otherwise +# the files are not read by doxygen. + +EXTENSION_MAPPING = + +# If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments +# according to the Markdown format, which allows for more readable +# documentation. See http://daringfireball.net/projects/markdown/ for details. +# The output of markdown processing is further processed by doxygen, so you can +# mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in +# case of backward compatibilities issues. +# The default value is: YES. + +MARKDOWN_SUPPORT = YES + +# When enabled doxygen tries to link words that correspond to documented +# classes, or namespaces to their corresponding documentation. Such a link can +# be prevented in individual cases by by putting a % sign in front of the word +# or globally by setting AUTOLINK_SUPPORT to NO. +# The default value is: YES. + +AUTOLINK_SUPPORT = YES + +# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want +# to include (a tag file for) the STL sources as input, then you should set this +# tag to YES in order to let doxygen match functions declarations and +# definitions whose arguments contain STL classes (e.g. func(std::string); +# versus func(std::string) {}). This also make the inheritance and collaboration # diagrams that involve STL classes more complete and accurate. +# The default value is: NO. BUILTIN_STL_SUPPORT = NO -# If you use Microsoft's C++/CLI language, you should set this option to YES to +# If you use Microsoft's C++/CLI language, you should set this option to YES to # enable parsing support. +# The default value is: NO. CPP_CLI_SUPPORT = NO -# Set the SIP_SUPPORT tag to YES if your project consists of sip sources only. -# Doxygen will parse them like normal C++ but will assume all classes use public -# instead of private inheritance when no explicit protection keyword is present. +# Set the SIP_SUPPORT tag to YES if your project consists of sip (see: +# http://www.riverbankcomputing.co.uk/software/sip/intro) sources only. Doxygen +# will parse them like normal C++ but will assume all classes use public instead +# of private inheritance when no explicit protection keyword is present. +# The default value is: NO. SIP_SUPPORT = NO -# For Microsoft's IDL there are propget and propput attributes to indicate getter -# and setter methods for a property. Setting this option to YES (the default) -# will make doxygen replace the get and set methods by a property in the -# documentation. This will only work if the methods are indeed getting or -# setting a simple type. If this is not the case, or you want to show the -# methods anyway, you should set this option to NO. +# For Microsoft's IDL there are propget and propput attributes to indicate +# getter and setter methods for a property. Setting this option to YES will make +# doxygen to replace the get and set methods by a property in the documentation. +# This will only work if the methods are indeed getting or setting a simple +# type. If this is not the case, or you want to show the methods anyway, you +# should set this option to NO. +# The default value is: YES. IDL_PROPERTY_SUPPORT = YES -# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC -# tag is set to YES, then doxygen will reuse the documentation of the first -# member in the group (if any) for the other members of the group. By default +# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC +# tag is set to YES, then doxygen will reuse the documentation of the first +# member in the group (if any) for the other members of the group. By default # all members of a group must be documented explicitly. +# The default value is: NO. DISTRIBUTE_GROUP_DOC = NO -# Set the SUBGROUPING tag to YES (the default) to allow class member groups of -# the same type (for instance a group of public functions) to be put as a -# subgroup of that type (e.g. under the Public Functions section). Set it to -# NO to prevent subgrouping. Alternatively, this can be done per class using -# the \nosubgrouping command. +# Set the SUBGROUPING tag to YES to allow class member groups of the same type +# (for instance a group of public functions) to be put as a subgroup of that +# type (e.g. under the Public Functions section). Set it to NO to prevent +# subgrouping. Alternatively, this can be done per class using the +# \nosubgrouping command. +# The default value is: YES. SUBGROUPING = YES -# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and -# unions are shown inside the group in which they are included (e.g. using -# @ingroup) instead of on a separate page (for HTML and Man pages) or -# section (for LaTeX and RTF). +# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions +# are shown inside the group in which they are included (e.g. using \ingroup) +# instead of on a separate page (for HTML and Man pages) or section (for LaTeX +# and RTF). +# +# Note that this feature does not work in combination with +# SEPARATE_MEMBER_PAGES. +# The default value is: NO. INLINE_GROUPED_CLASSES = NO -# When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum -# is documented as struct, union, or enum with the name of the typedef. So -# typedef struct TypeS {} TypeT, will appear in the documentation as a struct -# with name TypeT. When disabled the typedef will appear as a member of a file, -# namespace, or class. And the struct will be named TypeS. This can typically -# be useful for C code in case the coding convention dictates that all compound +# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions +# with only public data fields or simple typedef fields will be shown inline in +# the documentation of the scope in which they are defined (i.e. file, +# namespace, or group documentation), provided this scope is documented. If set +# to NO, structs, classes, and unions are shown on a separate page (for HTML and +# Man pages) or section (for LaTeX and RTF). +# The default value is: NO. + +INLINE_SIMPLE_STRUCTS = NO + +# When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or +# enum is documented as struct, union, or enum with the name of the typedef. So +# typedef struct TypeS {} TypeT, will appear in the documentation as a struct +# with name TypeT. When disabled the typedef will appear as a member of a file, +# namespace, or class. And the struct will be named TypeS. This can typically be +# useful for C code in case the coding convention dictates that all compound # types are typedef'ed and only the typedef is referenced, never the tag name. +# The default value is: NO. TYPEDEF_HIDES_STRUCT = NO -# The SYMBOL_CACHE_SIZE determines the size of the internal cache use to -# determine which symbols to keep in memory and which to flush to disk. -# When the cache is full, less often used symbols will be written to disk. -# For small to medium size projects (<1000 input files) the default value is -# probably good enough. For larger projects a too small cache size can cause -# doxygen to be busy swapping symbols to and from disk most of the time -# causing a significant performance penalty. -# If the system has enough physical memory increasing the cache will improve the -# performance by keeping more symbols in memory. Note that the value works on -# a logarithmic scale so increasing the size by one will roughly double the -# memory usage. The cache size is given by this formula: -# 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0, -# corresponding to a cache size of 2^16 = 65536 symbols - -SYMBOL_CACHE_SIZE = 0 +# The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This +# cache is used to resolve symbols given their name and scope. Since this can be +# an expensive process and often the same symbol appears multiple times in the +# code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small +# doxygen will become slower. If the cache is too large, memory is wasted. The +# cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range +# is 0..9, the default is 0, corresponding to a cache size of 2^16=65536 +# symbols. At the end of a run doxygen will report the cache usage and suggest +# the optimal cache size from a speed point of view. +# Minimum value: 0, maximum value: 9, default value: 0. + +LOOKUP_CACHE_SIZE = 0 #--------------------------------------------------------------------------- # Build related configuration options #--------------------------------------------------------------------------- -# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in -# documentation are documented, even if no documentation was available. -# Private class members and static file members will be hidden unless -# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES +# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in +# documentation are documented, even if no documentation was available. Private +# class members and static file members will be hidden unless the +# EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES. +# Note: This will also disable the warnings about undocumented members that are +# normally produced when WARNINGS is set to YES. +# The default value is: NO. EXTRACT_ALL = NO -# If the EXTRACT_PRIVATE tag is set to YES all private members of a class -# will be included in the documentation. +# If the EXTRACT_PRIVATE tag is set to YES all private members of a class will +# be included in the documentation. +# The default value is: NO. EXTRACT_PRIVATE = NO -# If the EXTRACT_STATIC tag is set to YES all static members of a file -# will be included in the documentation. +# If the EXTRACT_PACKAGE tag is set to YES all members with package or internal +# scope will be included in the documentation. +# The default value is: NO. + +EXTRACT_PACKAGE = NO + +# If the EXTRACT_STATIC tag is set to YES all static members of a file will be +# included in the documentation. +# The default value is: NO. EXTRACT_STATIC = YES -# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) -# defined locally in source files will be included in the documentation. -# If set to NO only classes defined in header files are included. +# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) defined +# locally in source files will be included in the documentation. If set to NO +# only classes defined in header files are included. Does not have any effect +# for Java sources. +# The default value is: YES. EXTRACT_LOCAL_CLASSES = YES -# This flag is only useful for Objective-C code. When set to YES local -# methods, which are defined in the implementation section but not in -# the interface are included in the documentation. -# If set to NO (the default) only methods in the interface are included. +# This flag is only useful for Objective-C code. When set to YES local methods, +# which are defined in the implementation section but not in the interface are +# included in the documentation. If set to NO only methods in the interface are +# included. +# The default value is: NO. EXTRACT_LOCAL_METHODS = NO -# If this flag is set to YES, the members of anonymous namespaces will be -# extracted and appear in the documentation as a namespace called -# 'anonymous_namespace{file}', where file will be replaced with the base -# name of the file that contains the anonymous namespace. By default -# anonymous namespaces are hidden. +# If this flag is set to YES, the members of anonymous namespaces will be +# extracted and appear in the documentation as a namespace called +# 'anonymous_namespace{file}', where file will be replaced with the base name of +# the file that contains the anonymous namespace. By default anonymous namespace +# are hidden. +# The default value is: NO. EXTRACT_ANON_NSPACES = NO -# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all -# undocumented members of documented classes, files or namespaces. -# If set to NO (the default) these members will be included in the -# various overviews, but no documentation section is generated. -# This option has no effect if EXTRACT_ALL is enabled. +# If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all +# undocumented members inside documented classes or files. If set to NO these +# members will be included in the various overviews, but no documentation +# section is generated. This option has no effect if EXTRACT_ALL is enabled. +# The default value is: NO. HIDE_UNDOC_MEMBERS = YES -# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all -# undocumented classes that are normally visible in the class hierarchy. -# If set to NO (the default) these classes will be included in the various -# overviews. This option has no effect if EXTRACT_ALL is enabled. +# If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all +# undocumented classes that are normally visible in the class hierarchy. If set +# to NO these classes will be included in the various overviews. This option has +# no effect if EXTRACT_ALL is enabled. +# The default value is: NO. HIDE_UNDOC_CLASSES = YES -# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all -# friend (class|struct|union) declarations. -# If set to NO (the default) these declarations will be included in the -# documentation. +# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend +# (class|struct|union) declarations. If set to NO these declarations will be +# included in the documentation. +# The default value is: NO. HIDE_FRIEND_COMPOUNDS = NO -# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any -# documentation blocks found inside the body of a function. -# If set to NO (the default) these blocks will be appended to the -# function's detailed documentation block. +# If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any +# documentation blocks found inside the body of a function. If set to NO these +# blocks will be appended to the function's detailed documentation block. +# The default value is: NO. HIDE_IN_BODY_DOCS = NO -# The INTERNAL_DOCS tag determines if documentation -# that is typed after a \internal command is included. If the tag is set -# to NO (the default) then the documentation will be excluded. -# Set it to YES to include the internal documentation. +# The INTERNAL_DOCS tag determines if documentation that is typed after a +# \internal command is included. If the tag is set to NO then the documentation +# will be excluded. Set it to YES to include the internal documentation. +# The default value is: NO. INTERNAL_DOCS = NO -# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate -# file names in lower-case letters. If set to YES upper-case letters are also -# allowed. This is useful if you have classes or files whose names only differ -# in case and if your file system supports case sensitive file names. Windows +# If the CASE_SENSE_NAMES tag is set to NO then doxygen will only generate file +# names in lower-case letters. If set to YES upper-case letters are also +# allowed. This is useful if you have classes or files whose names only differ +# in case and if your file system supports case sensitive file names. Windows # and Mac users are advised to set this option to NO. +# The default value is: system dependent. CASE_SENSE_NAMES = NO -# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen -# will show members with their full class and namespace scopes in the -# documentation. If set to YES the scope will be hidden. +# If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with +# their full class and namespace scopes in the documentation. If set to YES the +# scope will be hidden. +# The default value is: NO. HIDE_SCOPE_NAMES = NO -# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen -# will put a list of the files that are included by a file in the documentation -# of that file. +# If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of +# the files that are included by a file in the documentation of that file. +# The default value is: YES. SHOW_INCLUDE_FILES = NO -# If the FORCE_LOCAL_INCLUDES tag is set to YES then Doxygen -# will list include files with double quotes in the documentation -# rather than with sharp brackets. +# If the SHOW_GROUPED_MEMB_INC tag is set to YES then Doxygen will add for each +# grouped member an include statement to the documentation, telling the reader +# which file to include in order to use the member. +# The default value is: NO. + +SHOW_GROUPED_MEMB_INC = NO + +# If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include +# files with double quotes in the documentation rather than with sharp brackets. +# The default value is: NO. FORCE_LOCAL_INCLUDES = NO -# If the INLINE_INFO tag is set to YES (the default) then a tag [inline] -# is inserted in the documentation for inline members. +# If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the +# documentation for inline members. +# The default value is: YES. INLINE_INFO = YES -# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen -# will sort the (detailed) documentation of file and class members -# alphabetically by member name. If set to NO the members will appear in -# declaration order. +# If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the +# (detailed) documentation of file and class members alphabetically by member +# name. If set to NO the members will appear in declaration order. +# The default value is: YES. SORT_MEMBER_DOCS = YES -# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the -# brief documentation of file, namespace and class members alphabetically -# by member name. If set to NO (the default) the members will appear in -# declaration order. +# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief +# descriptions of file, namespace and class members alphabetically by member +# name. If set to NO the members will appear in declaration order. Note that +# this will also influence the order of the classes in the class list. +# The default value is: NO. SORT_BRIEF_DOCS = NO -# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen -# will sort the (brief and detailed) documentation of class members so that -# constructors and destructors are listed first. If set to NO (the default) -# the constructors will appear in the respective orders defined by -# SORT_MEMBER_DOCS and SORT_BRIEF_DOCS. -# This tag will be ignored for brief docs if SORT_BRIEF_DOCS is set to NO -# and ignored for detailed docs if SORT_MEMBER_DOCS is set to NO. +# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the +# (brief and detailed) documentation of class members so that constructors and +# destructors are listed first. If set to NO the constructors will appear in the +# respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS. +# Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief +# member documentation. +# Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting +# detailed member documentation. +# The default value is: NO. SORT_MEMBERS_CTORS_1ST = NO -# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the -# hierarchy of group names into alphabetical order. If set to NO (the default) -# the group names will appear in their defined order. +# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy +# of group names into alphabetical order. If set to NO the group names will +# appear in their defined order. +# The default value is: NO. SORT_GROUP_NAMES = NO -# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be -# sorted by fully-qualified names, including namespaces. If set to -# NO (the default), the class list will be sorted only by class name, -# not including the namespace part. -# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. -# Note: This option applies only to the class list, not to the -# alphabetical list. +# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by +# fully-qualified names, including namespaces. If set to NO, the class list will +# be sorted only by class name, not including the namespace part. +# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. +# Note: This option applies only to the class list, not to the alphabetical +# list. +# The default value is: NO. SORT_BY_SCOPE_NAME = NO -# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to -# do proper type resolution of all parameters of a function it will reject a -# match between the prototype and the implementation of a member function even -# if there is only one candidate or it is obvious which candidate to choose -# by doing a simple string match. By disabling STRICT_PROTO_MATCHING doxygen -# will still accept a match between prototype and implementation in such cases. +# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper +# type resolution of all parameters of a function it will reject a match between +# the prototype and the implementation of a member function even if there is +# only one candidate or it is obvious which candidate to choose by doing a +# simple string match. By disabling STRICT_PROTO_MATCHING doxygen will still +# accept a match between prototype and implementation in such cases. +# The default value is: NO. STRICT_PROTO_MATCHING = NO -# The GENERATE_TODOLIST tag can be used to enable (YES) or -# disable (NO) the todo list. This list is created by putting \todo -# commands in the documentation. +# The GENERATE_TODOLIST tag can be used to enable ( YES) or disable ( NO) the +# todo list. This list is created by putting \todo commands in the +# documentation. +# The default value is: YES. GENERATE_TODOLIST = YES -# The GENERATE_TESTLIST tag can be used to enable (YES) or -# disable (NO) the test list. This list is created by putting \test -# commands in the documentation. +# The GENERATE_TESTLIST tag can be used to enable ( YES) or disable ( NO) the +# test list. This list is created by putting \test commands in the +# documentation. +# The default value is: YES. GENERATE_TESTLIST = YES -# The GENERATE_BUGLIST tag can be used to enable (YES) or -# disable (NO) the bug list. This list is created by putting \bug -# commands in the documentation. +# The GENERATE_BUGLIST tag can be used to enable ( YES) or disable ( NO) the bug +# list. This list is created by putting \bug commands in the documentation. +# The default value is: YES. GENERATE_BUGLIST = YES -# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or -# disable (NO) the deprecated list. This list is created by putting -# \deprecated commands in the documentation. +# The GENERATE_DEPRECATEDLIST tag can be used to enable ( YES) or disable ( NO) +# the deprecated list. This list is created by putting \deprecated commands in +# the documentation. +# The default value is: YES. GENERATE_DEPRECATEDLIST= YES -# The ENABLED_SECTIONS tag can be used to enable conditional -# documentation sections, marked by \if sectionname ... \endif. +# The ENABLED_SECTIONS tag can be used to enable conditional documentation +# sections, marked by \if ... \endif and \cond +# ... \endcond blocks. -ENABLED_SECTIONS = +ENABLED_SECTIONS = -# The MAX_INITIALIZER_LINES tag determines the maximum number of lines -# the initial value of a variable or macro consists of for it to appear in -# the documentation. If the initializer consists of more lines than specified -# here it will be hidden. Use a value of 0 to hide initializers completely. -# The appearance of the initializer of individual variables and macros in the -# documentation can be controlled using \showinitializer or \hideinitializer -# command in the documentation regardless of this setting. +# The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the +# initial value of a variable or macro / define can have for it to appear in the +# documentation. If the initializer consists of more lines than specified here +# it will be hidden. Use a value of 0 to hide initializers completely. The +# appearance of the value of individual variables and macros / defines can be +# controlled using \showinitializer or \hideinitializer command in the +# documentation regardless of this setting. +# Minimum value: 0, maximum value: 10000, default value: 30. MAX_INITIALIZER_LINES = 30 -# Set the SHOW_USED_FILES tag to NO to disable the list of files generated -# at the bottom of the documentation of classes and structs. If set to YES the -# list will mention the files that were used to generate the documentation. +# Set the SHOW_USED_FILES tag to NO to disable the list of files generated at +# the bottom of the documentation of classes and structs. If set to YES the list +# will mention the files that were used to generate the documentation. +# The default value is: YES. SHOW_USED_FILES = NO -# If the sources in your project are distributed over multiple directories -# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy -# in the documentation. The default is NO. - -SHOW_DIRECTORIES = NO - -# Set the SHOW_FILES tag to NO to disable the generation of the Files page. -# This will remove the Files entry from the Quick Index and from the -# Folder Tree View (if specified). The default is YES. +# Set the SHOW_FILES tag to NO to disable the generation of the Files page. This +# will remove the Files entry from the Quick Index and from the Folder Tree View +# (if specified). +# The default value is: YES. SHOW_FILES = NO -# Set the SHOW_NAMESPACES tag to NO to disable the generation of the -# Namespaces page. This will remove the Namespaces entry from the Quick Index -# and from the Folder Tree View (if specified). The default is YES. +# Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces +# page. This will remove the Namespaces entry from the Quick Index and from the +# Folder Tree View (if specified). +# The default value is: YES. SHOW_NAMESPACES = NO -# The FILE_VERSION_FILTER tag can be used to specify a program or script that -# doxygen should invoke to get the current version for each file (typically from -# the version control system). Doxygen will invoke the program by executing (via -# popen()) the command , where is the value of -# the FILE_VERSION_FILTER tag, and is the name of an input file -# provided by doxygen. Whatever the program writes to standard output -# is used as the file version. See the manual for examples. +# The FILE_VERSION_FILTER tag can be used to specify a program or script that +# doxygen should invoke to get the current version for each file (typically from +# the version control system). Doxygen will invoke the program by executing (via +# popen()) the command command input-file, where command is the value of the +# FILE_VERSION_FILTER tag, and input-file is the name of an input file provided +# by doxygen. Whatever the program writes to standard output is used as the file +# version. For an example see the documentation. + +FILE_VERSION_FILTER = + +# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed +# by doxygen. The layout file controls the global structure of the generated +# output files in an output format independent way. To create the layout file +# that represents doxygen's defaults, run doxygen with the -l option. You can +# optionally specify a file name after the option, if omitted DoxygenLayout.xml +# will be used as the name of the layout file. +# +# Note that if you run doxygen from a directory containing a file called +# DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE +# tag is left empty. -FILE_VERSION_FILTER = +LAYOUT_FILE = -# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed -# by doxygen. The layout file controls the global structure of the generated -# output files in an output format independent way. The create the layout file -# that represents doxygen's defaults, run doxygen with the -l option. -# You can optionally specify a file name after the option, if omitted -# DoxygenLayout.xml will be used as the name of the layout file. +# The CITE_BIB_FILES tag can be used to specify one or more bib files containing +# the reference definitions. This must be a list of .bib files. The .bib +# extension is automatically appended if omitted. This requires the bibtex tool +# to be installed. See also http://en.wikipedia.org/wiki/BibTeX for more info. +# For LaTeX the style of the bibliography can be controlled using +# LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the +# search path. Do not use file names with spaces, bibtex cannot handle them. See +# also \cite for info how to create references. -LAYOUT_FILE = +CITE_BIB_FILES = #--------------------------------------------------------------------------- -# configuration options related to warning and progress messages +# Configuration options related to warning and progress messages #--------------------------------------------------------------------------- -# The QUIET tag can be used to turn on/off the messages that are generated -# by doxygen. Possible values are YES and NO. If left blank NO is used. +# The QUIET tag can be used to turn on/off the messages that are generated to +# standard output by doxygen. If QUIET is set to YES this implies that the +# messages are off. +# The default value is: NO. QUIET = NO -# The WARNINGS tag can be used to turn on/off the warning messages that are -# generated by doxygen. Possible values are YES and NO. If left blank -# NO is used. +# The WARNINGS tag can be used to turn on/off the warning messages that are +# generated to standard error ( stderr) by doxygen. If WARNINGS is set to YES +# this implies that the warnings are on. +# +# Tip: Turn warnings on while writing the documentation. +# The default value is: YES. WARNINGS = YES -# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings -# for undocumented members. If EXTRACT_ALL is set to YES then this flag will -# automatically be disabled. +# If the WARN_IF_UNDOCUMENTED tag is set to YES, then doxygen will generate +# warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag +# will automatically be disabled. +# The default value is: YES. WARN_IF_UNDOCUMENTED = YES -# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for -# potential errors in the documentation, such as not documenting some -# parameters in a documented function, or documenting parameters that -# don't exist or using markup commands wrongly. +# If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for +# potential errors in the documentation, such as not documenting some parameters +# in a documented function, or documenting parameters that don't exist or using +# markup commands wrongly. +# The default value is: YES. WARN_IF_DOC_ERROR = YES -# The WARN_NO_PARAMDOC option can be enabled to get warnings for -# functions that are documented, but have no documentation for their parameters -# or return value. If set to NO (the default) doxygen will only warn about -# wrong or incomplete parameter documentation, but not about the absence of -# documentation. +# This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that +# are documented, but have no documentation for their parameters or return +# value. If set to NO doxygen will only warn about wrong or incomplete parameter +# documentation, but not about the absence of documentation. +# The default value is: NO. WARN_NO_PARAMDOC = NO -# The WARN_FORMAT tag determines the format of the warning messages that -# doxygen can produce. The string should contain the $file, $line, and $text -# tags, which will be replaced by the file and line number from which the -# warning originated and the warning text. Optionally the format may contain -# $version, which will be replaced by the version of the file (if it could -# be obtained via FILE_VERSION_FILTER) +# The WARN_FORMAT tag determines the format of the warning messages that doxygen +# can produce. The string should contain the $file, $line, and $text tags, which +# will be replaced by the file and line number from which the warning originated +# and the warning text. Optionally the format may contain $version, which will +# be replaced by the version of the file (if it could be obtained via +# FILE_VERSION_FILTER) +# The default value is: $file:$line: $text. WARN_FORMAT = "$file:$line: $text" -# The WARN_LOGFILE tag can be used to specify a file to which warning -# and error messages should be written. If left blank the output is written -# to stderr. +# The WARN_LOGFILE tag can be used to specify a file to which warning and error +# messages should be written. If left blank the output is written to standard +# error (stderr). -WARN_LOGFILE = +WARN_LOGFILE = #--------------------------------------------------------------------------- -# configuration options related to the input files +# Configuration options related to the input files #--------------------------------------------------------------------------- -# The INPUT tag can be used to specify the files and/or directories that contain -# documented source files. You may enter file names like "myfile.cpp" or -# directories like "/usr/src/myproject". Separate the files or directories -# with spaces. +# The INPUT tag is used to specify the files and/or directories that contain +# documented source files. You may enter file names like myfile.cpp or +# directories like /usr/src/myproject. Separate the files or directories with +# spaces. +# Note: If this tag is empty the current directory is searched. -INPUT = ../../Modules/Contents/2DPhysics +INPUT = ../../Modules/Contents/2DPhysics \ + indexModules.dox -# This tag can be used to specify the character encoding of the source files -# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is -# also the default input encoding. Doxygen uses libiconv (or the iconv built -# into libc) for the transcoding. See http://www.gnu.org/software/libiconv for -# the list of possible encodings. +# This tag can be used to specify the character encoding of the source files +# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses +# libiconv (or the iconv built into libc) for the transcoding. See the libiconv +# documentation (see: http://www.gnu.org/software/libiconv) for the list of +# possible encodings. +# The default value is: UTF-8. INPUT_ENCODING = UTF-8 -# If the value of the INPUT tag contains directories, you can use the -# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp -# and *.h) to filter out the source-files in the directories. If left -# blank the following patterns are tested: -# *.c *.cc *.cxx *.cpp *.c++ *.d *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh -# *.hxx *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.dox *.py -# *.f90 *.f *.for *.vhd *.vhdl +# If the value of the INPUT tag contains directories, you can use the +# FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and +# *.h) to filter out the source-files in the directories. If left blank the +# following patterns are tested:*.c, *.cc, *.cxx, *.cpp, *.c++, *.java, *.ii, +# *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h, *.hh, *.hxx, *.hpp, +# *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc, *.m, *.markdown, +# *.md, *.mm, *.dox, *.py, *.f90, *.f, *.for, *.tcl, *.vhd, *.vhdl, *.ucf, +# *.qsf, *.as and *.js. FILE_PATTERNS = *.c \ *.cc \ @@ -670,876 +807,1246 @@ FILE_PATTERNS = *.c \ *.vhd \ *.vhdl -# The RECURSIVE tag can be used to turn specify whether or not subdirectories -# should be searched for input files as well. Possible values are YES and NO. -# If left blank NO is used. +# The RECURSIVE tag can be used to specify whether or not subdirectories should +# be searched for input files as well. +# The default value is: NO. RECURSIVE = YES -# The EXCLUDE tag can be used to specify files and/or directories that should -# excluded from the INPUT source files. This way you can easily exclude a +# The EXCLUDE tag can be used to specify files and/or directories that should be +# excluded from the INPUT source files. This way you can easily exclude a # subdirectory from a directory tree whose root is specified with the INPUT tag. +# +# Note that relative paths are relative to the directory from which doxygen is +# run. EXCLUDE = ../../Core/Contents/Include/tinystr.h \ ../../Core/Contents/Include/tinyxml.h -# The EXCLUDE_SYMLINKS tag can be used select whether or not files or -# directories that are symbolic links (a Unix file system feature) are excluded +# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or +# directories that are symbolic links (a Unix file system feature) are excluded # from the input. +# The default value is: NO. EXCLUDE_SYMLINKS = NO -# If the value of the INPUT tag contains directories, you can use the -# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude -# certain files from those directories. Note that the wildcards are matched -# against the file with absolute path, so to exclude all test directories -# for example use the pattern */test/* +# If the value of the INPUT tag contains directories, you can use the +# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude +# certain files from those directories. +# +# Note that the wildcards are matched against the file with absolute path, so to +# exclude all test directories for example use the pattern */test/* -EXCLUDE_PATTERNS = +EXCLUDE_PATTERNS = -# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names -# (namespaces, classes, functions, etc.) that should be excluded from the -# output. The symbol name can be a fully qualified name, a word, or if the -# wildcard * is used, a substring. Examples: ANamespace, AClass, +# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names +# (namespaces, classes, functions, etc.) that should be excluded from the +# output. The symbol name can be a fully qualified name, a word, or if the +# wildcard * is used, a substring. Examples: ANamespace, AClass, # AClass::ANamespace, ANamespace::*Test +# +# Note that the wildcards are matched against the file with absolute path, so to +# exclude all test directories use the pattern */test/* -EXCLUDE_SYMBOLS = +EXCLUDE_SYMBOLS = -# The EXAMPLE_PATH tag can be used to specify one or more files or -# directories that contain example code fragments that are included (see -# the \include command). +# The EXAMPLE_PATH tag can be used to specify one or more files or directories +# that contain example code fragments that are included (see the \include +# command). -EXAMPLE_PATH = +EXAMPLE_PATH = -# If the value of the EXAMPLE_PATH tag contains directories, you can use the -# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp -# and *.h) to filter out the source-files in the directories. If left -# blank all files are included. +# If the value of the EXAMPLE_PATH tag contains directories, you can use the +# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and +# *.h) to filter out the source-files in the directories. If left blank all +# files are included. EXAMPLE_PATTERNS = * -# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be -# searched for input files to be used with the \include or \dontinclude -# commands irrespective of the value of the RECURSIVE tag. -# Possible values are YES and NO. If left blank NO is used. +# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be +# searched for input files to be used with the \include or \dontinclude commands +# irrespective of the value of the RECURSIVE tag. +# The default value is: NO. EXAMPLE_RECURSIVE = NO -# The IMAGE_PATH tag can be used to specify one or more files or -# directories that contain image that are included in the documentation (see -# the \image command). +# The IMAGE_PATH tag can be used to specify one or more files or directories +# that contain images that are to be included in the documentation (see the +# \image command). -IMAGE_PATH = +IMAGE_PATH = -# The INPUT_FILTER tag can be used to specify a program that doxygen should -# invoke to filter for each input file. Doxygen will invoke the filter program -# by executing (via popen()) the command , where -# is the value of the INPUT_FILTER tag, and is the name of an -# input file. Doxygen will then use the output that the filter program writes -# to standard output. If FILTER_PATTERNS is specified, this tag will be -# ignored. +# The INPUT_FILTER tag can be used to specify a program that doxygen should +# invoke to filter for each input file. Doxygen will invoke the filter program +# by executing (via popen()) the command: +# +# +# +# where is the value of the INPUT_FILTER tag, and is the +# name of an input file. Doxygen will then use the output that the filter +# program writes to standard output. If FILTER_PATTERNS is specified, this tag +# will be ignored. +# +# Note that the filter must not add or remove lines; it is applied before the +# code is scanned, but not when the output code is generated. If lines are added +# or removed, the anchors will not be placed correctly. -INPUT_FILTER = +INPUT_FILTER = -# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern -# basis. Doxygen will compare the file name with each pattern and apply the -# filter if there is a match. The filters are a list of the form: -# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further -# info on how filters are used. If FILTER_PATTERNS is empty or if -# non of the patterns match the file name, INPUT_FILTER is applied. +# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern +# basis. Doxygen will compare the file name with each pattern and apply the +# filter if there is a match. The filters are a list of the form: pattern=filter +# (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how +# filters are used. If the FILTER_PATTERNS tag is empty or if none of the +# patterns match the file name, INPUT_FILTER is applied. -FILTER_PATTERNS = +FILTER_PATTERNS = -# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using -# INPUT_FILTER) will be used to filter the input files when producing source -# files to browse (i.e. when SOURCE_BROWSER is set to YES). +# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using +# INPUT_FILTER ) will also be used to filter the input files that are used for +# producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES). +# The default value is: NO. FILTER_SOURCE_FILES = NO -# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file -# pattern. A pattern will override the setting for FILTER_PATTERN (if any) -# and it is also possible to disable source filtering for a specific pattern -# using *.ext= (so without naming a filter). This option only has effect when -# FILTER_SOURCE_FILES is enabled. +# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file +# pattern. A pattern will override the setting for FILTER_PATTERN (if any) and +# it is also possible to disable source filtering for a specific pattern using +# *.ext= (so without naming a filter). +# This tag requires that the tag FILTER_SOURCE_FILES is set to YES. + +FILTER_SOURCE_PATTERNS = -FILTER_SOURCE_PATTERNS = +# If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that +# is part of the input, its contents will be placed on the main page +# (index.html). This can be useful if you have a project on for instance GitHub +# and want to reuse the introduction page also for the doxygen output. + +USE_MDFILE_AS_MAINPAGE = #--------------------------------------------------------------------------- -# configuration options related to source browsing +# Configuration options related to source browsing #--------------------------------------------------------------------------- -# If the SOURCE_BROWSER tag is set to YES then a list of source files will -# be generated. Documented entities will be cross-referenced with these sources. -# Note: To get rid of all source code in the generated output, make sure also -# VERBATIM_HEADERS is set to NO. +# If the SOURCE_BROWSER tag is set to YES then a list of source files will be +# generated. Documented entities will be cross-referenced with these sources. +# +# Note: To get rid of all source code in the generated output, make sure that +# also VERBATIM_HEADERS is set to NO. +# The default value is: NO. SOURCE_BROWSER = NO -# Setting the INLINE_SOURCES tag to YES will include the body -# of functions and classes directly in the documentation. +# Setting the INLINE_SOURCES tag to YES will include the body of functions, +# classes and enums directly into the documentation. +# The default value is: NO. INLINE_SOURCES = NO -# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct -# doxygen to hide any special comment blocks from generated source code -# fragments. Normal C and C++ comments will always remain visible. +# Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any +# special comment blocks from generated source code fragments. Normal C, C++ and +# Fortran comments will always remain visible. +# The default value is: YES. STRIP_CODE_COMMENTS = YES -# If the REFERENCED_BY_RELATION tag is set to YES -# then for each documented function all documented -# functions referencing it will be listed. +# If the REFERENCED_BY_RELATION tag is set to YES then for each documented +# function all documented functions referencing it will be listed. +# The default value is: NO. REFERENCED_BY_RELATION = NO -# If the REFERENCES_RELATION tag is set to YES -# then for each documented function all documented entities -# called/used by that function will be listed. +# If the REFERENCES_RELATION tag is set to YES then for each documented function +# all documented entities called/used by that function will be listed. +# The default value is: NO. REFERENCES_RELATION = NO -# If the REFERENCES_LINK_SOURCE tag is set to YES (the default) -# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from -# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will -# link to the source code. Otherwise they will link to the documentation. +# If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set +# to YES, then the hyperlinks from functions in REFERENCES_RELATION and +# REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will +# link to the documentation. +# The default value is: YES. REFERENCES_LINK_SOURCE = NO -# If the USE_HTAGS tag is set to YES then the references to source code -# will point to the HTML generated by the htags(1) tool instead of doxygen -# built-in source browser. The htags tool is part of GNU's global source -# tagging system (see http://www.gnu.org/software/global/global.html). You -# will need version 4.8.6 or higher. +# If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the +# source code will show a tooltip with additional information such as prototype, +# brief description and links to the definition and documentation. Since this +# will make the HTML file larger and loading of large files a bit slower, you +# can opt to disable this feature. +# The default value is: YES. +# This tag requires that the tag SOURCE_BROWSER is set to YES. + +SOURCE_TOOLTIPS = YES + +# If the USE_HTAGS tag is set to YES then the references to source code will +# point to the HTML generated by the htags(1) tool instead of doxygen built-in +# source browser. The htags tool is part of GNU's global source tagging system +# (see http://www.gnu.org/software/global/global.html). You will need version +# 4.8.6 or higher. +# +# To use it do the following: +# - Install the latest version of global +# - Enable SOURCE_BROWSER and USE_HTAGS in the config file +# - Make sure the INPUT points to the root of the source tree +# - Run doxygen as normal +# +# Doxygen will invoke htags (and that will in turn invoke gtags), so these +# tools must be available from the command line (i.e. in the search path). +# +# The result: instead of the source browser generated by doxygen, the links to +# source code will now point to the output of htags. +# The default value is: NO. +# This tag requires that the tag SOURCE_BROWSER is set to YES. USE_HTAGS = NO -# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen -# will generate a verbatim copy of the header file for each class for -# which an include is specified. Set to NO to disable this. +# If the VERBATIM_HEADERS tag is set the YES then doxygen will generate a +# verbatim copy of the header file for each class for which an include is +# specified. Set to NO to disable this. +# See also: Section \class. +# The default value is: YES. VERBATIM_HEADERS = NO #--------------------------------------------------------------------------- -# configuration options related to the alphabetical class index +# Configuration options related to the alphabetical class index #--------------------------------------------------------------------------- -# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index -# of all compounds will be generated. Enable this if the project -# contains a lot of classes, structs, unions or interfaces. +# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all +# compounds will be generated. Enable this if the project contains a lot of +# classes, structs, unions or interfaces. +# The default value is: YES. ALPHABETICAL_INDEX = YES -# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then -# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns -# in which this list will be split (can be a number in the range [1..20]) +# The COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns in +# which the alphabetical index list will be split. +# Minimum value: 1, maximum value: 20, default value: 5. +# This tag requires that the tag ALPHABETICAL_INDEX is set to YES. COLS_IN_ALPHA_INDEX = 5 -# In case all classes in a project start with a common prefix, all -# classes will be put under the same header in the alphabetical index. -# The IGNORE_PREFIX tag can be used to specify one or more prefixes that -# should be ignored while generating the index headers. +# In case all classes in a project start with a common prefix, all classes will +# be put under the same header in the alphabetical index. The IGNORE_PREFIX tag +# can be used to specify a prefix (or a list of prefixes) that should be ignored +# while generating the index headers. +# This tag requires that the tag ALPHABETICAL_INDEX is set to YES. -IGNORE_PREFIX = +IGNORE_PREFIX = #--------------------------------------------------------------------------- -# configuration options related to the HTML output +# Configuration options related to the HTML output #--------------------------------------------------------------------------- -# If the GENERATE_HTML tag is set to YES (the default) Doxygen will -# generate HTML output. +# If the GENERATE_HTML tag is set to YES doxygen will generate HTML output +# The default value is: YES. GENERATE_HTML = YES -# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. -# If a relative path is entered the value of OUTPUT_DIRECTORY will be -# put in front of it. If left blank `html' will be used as the default path. +# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a +# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of +# it. +# The default directory is: html. +# This tag requires that the tag GENERATE_HTML is set to YES. HTML_OUTPUT = html -# The HTML_FILE_EXTENSION tag can be used to specify the file extension for -# each generated HTML page (for example: .htm,.php,.asp). If it is left blank -# doxygen will generate files with .html extension. +# The HTML_FILE_EXTENSION tag can be used to specify the file extension for each +# generated HTML page (for example: .htm, .php, .asp). +# The default value is: .html. +# This tag requires that the tag GENERATE_HTML is set to YES. HTML_FILE_EXTENSION = .html -# The HTML_HEADER tag can be used to specify a personal HTML header for -# each generated HTML page. If it is left blank doxygen will generate a -# standard header. Note that when using a custom header you are responsible -# for the proper inclusion of any scripts and style sheets that doxygen -# needs, which is dependent on the configuration options used. -# It is adviced to generate a default header using "doxygen -w html -# header.html footer.html stylesheet.css YourConfigFile" and then modify -# that header. Note that the header is subject to change so you typically -# have to redo this when upgrading to a newer version of doxygen or when -# changing the value of configuration settings such as GENERATE_TREEVIEW! - -HTML_HEADER = - -# The HTML_FOOTER tag can be used to specify a personal HTML footer for -# each generated HTML page. If it is left blank doxygen will generate a -# standard footer. - -HTML_FOOTER = - -# The HTML_STYLESHEET tag can be used to specify a user-defined cascading -# style sheet that is used by each HTML page. It can be used to -# fine-tune the look of the HTML output. If the tag is left blank doxygen -# will generate a default style sheet. Note that doxygen will try to copy -# the style sheet file to the HTML output directory, so don't put your own -# stylesheet in the HTML output directory as well, or it will be erased! - -HTML_STYLESHEET = - -# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or -# other source files which should be copied to the HTML output directory. Note -# that these files will be copied to the base HTML output directory. Use the -# $relpath$ marker in the HTML_HEADER and/or HTML_FOOTER files to load these -# files. In the HTML_STYLESHEET file, use the file name only. Also note that -# the files will be copied as-is; there are no commands or markers available. - -HTML_EXTRA_FILES = - -# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. -# Doxygen will adjust the colors in the stylesheet and background images -# according to this color. Hue is specified as an angle on a colorwheel, -# see http://en.wikipedia.org/wiki/Hue for more information. -# For instance the value 0 represents red, 60 is yellow, 120 is green, -# 180 is cyan, 240 is blue, 300 purple, and 360 is red again. -# The allowed range is 0 to 359. +# The HTML_HEADER tag can be used to specify a user-defined HTML header file for +# each generated HTML page. If the tag is left blank doxygen will generate a +# standard header. +# +# To get valid HTML the header file that includes any scripts and style sheets +# that doxygen needs, which is dependent on the configuration options used (e.g. +# the setting GENERATE_TREEVIEW). It is highly recommended to start with a +# default header using +# doxygen -w html new_header.html new_footer.html new_stylesheet.css +# YourConfigFile +# and then modify the file new_header.html. See also section "Doxygen usage" +# for information on how to generate the default header that doxygen normally +# uses. +# Note: The header is subject to change so you typically have to regenerate the +# default header when upgrading to a newer version of doxygen. For a description +# of the possible markers and block names see the documentation. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_HEADER = + +# The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each +# generated HTML page. If the tag is left blank doxygen will generate a standard +# footer. See HTML_HEADER for more information on how to generate a default +# footer and what special commands can be used inside the footer. See also +# section "Doxygen usage" for information on how to generate the default footer +# that doxygen normally uses. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_FOOTER = + +# The HTML_STYLESHEET tag can be used to specify a user-defined cascading style +# sheet that is used by each HTML page. It can be used to fine-tune the look of +# the HTML output. If left blank doxygen will generate a default style sheet. +# See also section "Doxygen usage" for information on how to generate the style +# sheet that doxygen normally uses. +# Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as +# it is more robust and this tag (HTML_STYLESHEET) will in the future become +# obsolete. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_STYLESHEET = + +# The HTML_EXTRA_STYLESHEET tag can be used to specify an additional user- +# defined cascading style sheet that is included after the standard style sheets +# created by doxygen. Using this option one can overrule certain style aspects. +# This is preferred over using HTML_STYLESHEET since it does not replace the +# standard style sheet and is therefor more robust against future updates. +# Doxygen will copy the style sheet file to the output directory. For an example +# see the documentation. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_EXTRA_STYLESHEET = + +# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or +# other source files which should be copied to the HTML output directory. Note +# that these files will be copied to the base HTML output directory. Use the +# $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these +# files. In the HTML_STYLESHEET file, use the file name only. Also note that the +# files will be copied as-is; there are no commands or markers available. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_EXTRA_FILES = + +# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen +# will adjust the colors in the stylesheet and background images according to +# this color. Hue is specified as an angle on a colorwheel, see +# http://en.wikipedia.org/wiki/Hue for more information. For instance the value +# 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300 +# purple, and 360 is red again. +# Minimum value: 0, maximum value: 359, default value: 220. +# This tag requires that the tag GENERATE_HTML is set to YES. HTML_COLORSTYLE_HUE = 220 -# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of -# the colors in the HTML output. For a value of 0 the output will use -# grayscales only. A value of 255 will produce the most vivid colors. +# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors +# in the HTML output. For a value of 0 the output will use grayscales only. A +# value of 255 will produce the most vivid colors. +# Minimum value: 0, maximum value: 255, default value: 100. +# This tag requires that the tag GENERATE_HTML is set to YES. HTML_COLORSTYLE_SAT = 100 -# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to -# the luminance component of the colors in the HTML output. Values below -# 100 gradually make the output lighter, whereas values above 100 make -# the output darker. The value divided by 100 is the actual gamma applied, -# so 80 represents a gamma of 0.8, The value 220 represents a gamma of 2.2, -# and 100 does not change the gamma. +# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the +# luminance component of the colors in the HTML output. Values below 100 +# gradually make the output lighter, whereas values above 100 make the output +# darker. The value divided by 100 is the actual gamma applied, so 80 represents +# a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not +# change the gamma. +# Minimum value: 40, maximum value: 240, default value: 80. +# This tag requires that the tag GENERATE_HTML is set to YES. HTML_COLORSTYLE_GAMMA = 80 -# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML -# page will contain the date and time when the page was generated. Setting -# this to NO can help when comparing the output of multiple runs. +# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML +# page will contain the date and time when the page was generated. Setting this +# to NO can help when comparing the output of multiple runs. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. HTML_TIMESTAMP = YES -# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, -# files or namespaces will be aligned in HTML using tables. If set to -# NO a bullet list will be used. - -HTML_ALIGN_MEMBERS = YES - -# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML -# documentation will contain sections that can be hidden and shown after the -# page has loaded. For this to work a browser that supports -# JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox -# Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari). +# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML +# documentation will contain sections that can be hidden and shown after the +# page has loaded. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. HTML_DYNAMIC_SECTIONS = NO -# If the GENERATE_DOCSET tag is set to YES, additional index files -# will be generated that can be used as input for Apple's Xcode 3 -# integrated development environment, introduced with OSX 10.5 (Leopard). -# To create a documentation set, doxygen will generate a Makefile in the -# HTML output directory. Running make will produce the docset in that -# directory and running "make install" will install the docset in -# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find -# it at startup. -# See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html +# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries +# shown in the various tree structured indices initially; the user can expand +# and collapse entries dynamically later on. Doxygen will expand the tree to +# such a level that at most the specified number of entries are visible (unless +# a fully collapsed tree already exceeds this amount). So setting the number of +# entries 1 will produce a full collapsed tree by default. 0 is a special value +# representing an infinite number of entries and will result in a full expanded +# tree by default. +# Minimum value: 0, maximum value: 9999, default value: 100. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_INDEX_NUM_ENTRIES = 100 + +# If the GENERATE_DOCSET tag is set to YES, additional index files will be +# generated that can be used as input for Apple's Xcode 3 integrated development +# environment (see: http://developer.apple.com/tools/xcode/), introduced with +# OSX 10.5 (Leopard). To create a documentation set, doxygen will generate a +# Makefile in the HTML output directory. Running make will produce the docset in +# that directory and running make install will install the docset in +# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at +# startup. See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html # for more information. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_DOCSET = NO -# When GENERATE_DOCSET tag is set to YES, this tag determines the name of the -# feed. A documentation feed provides an umbrella under which multiple -# documentation sets from a single provider (such as a company or product suite) -# can be grouped. +# This tag determines the name of the docset feed. A documentation feed provides +# an umbrella under which multiple documentation sets from a single provider +# (such as a company or product suite) can be grouped. +# The default value is: Doxygen generated docs. +# This tag requires that the tag GENERATE_DOCSET is set to YES. DOCSET_FEEDNAME = "Doxygen generated docs" -# When GENERATE_DOCSET tag is set to YES, this tag specifies a string that -# should uniquely identify the documentation set bundle. This should be a -# reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen -# will append .docset to the name. +# This tag specifies a string that should uniquely identify the documentation +# set bundle. This should be a reverse domain-name style string, e.g. +# com.mycompany.MyDocSet. Doxygen will append .docset to the name. +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_DOCSET is set to YES. DOCSET_BUNDLE_ID = org.doxygen.Project -# When GENERATE_PUBLISHER_ID tag specifies a string that should uniquely identify -# the documentation publisher. This should be a reverse domain-name style +# The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify +# the documentation publisher. This should be a reverse domain-name style # string, e.g. com.mycompany.MyDocSet.documentation. +# The default value is: org.doxygen.Publisher. +# This tag requires that the tag GENERATE_DOCSET is set to YES. DOCSET_PUBLISHER_ID = org.doxygen.Publisher -# The GENERATE_PUBLISHER_NAME tag identifies the documentation publisher. +# The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher. +# The default value is: Publisher. +# This tag requires that the tag GENERATE_DOCSET is set to YES. DOCSET_PUBLISHER_NAME = Publisher -# If the GENERATE_HTMLHELP tag is set to YES, additional index files -# will be generated that can be used as input for tools like the -# Microsoft HTML help workshop to generate a compiled HTML help file (.chm) -# of the generated HTML documentation. +# If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three +# additional HTML index files: index.hhp, index.hhc, and index.hhk. The +# index.hhp is a project file that can be read by Microsoft's HTML Help Workshop +# (see: http://www.microsoft.com/en-us/download/details.aspx?id=21138) on +# Windows. +# +# The HTML Help Workshop contains a compiler that can convert all HTML output +# generated by doxygen into a single compiled HTML file (.chm). Compiled HTML +# files are now used as the Windows 98 help format, and will replace the old +# Windows help format (.hlp) on all Windows platforms in the future. Compressed +# HTML files also contain an index, a table of contents, and you can search for +# words in the documentation. The HTML workshop also contains a viewer for +# compressed HTML files. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_HTMLHELP = NO -# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can -# be used to specify the file name of the resulting .chm file. You -# can add a path in front of the file if the result should not be +# The CHM_FILE tag can be used to specify the file name of the resulting .chm +# file. You can add a path in front of the file if the result should not be # written to the html output directory. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. -CHM_FILE = +CHM_FILE = -# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can -# be used to specify the location (absolute path including file name) of -# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run -# the HTML help compiler on the generated index.hhp. +# The HHC_LOCATION tag can be used to specify the location (absolute path +# including file name) of the HTML help compiler ( hhc.exe). If non-empty +# doxygen will try to run the HTML help compiler on the generated index.hhp. +# The file has to be specified with full path. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. -HHC_LOCATION = +HHC_LOCATION = -# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag -# controls if a separate .chi index file is generated (YES) or that -# it should be included in the master .chm file (NO). +# The GENERATE_CHI flag controls if a separate .chi index file is generated ( +# YES) or that it should be included in the master .chm file ( NO). +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. GENERATE_CHI = NO -# If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING -# is used to encode HtmlHelp index (hhk), content (hhc) and project file -# content. +# The CHM_INDEX_ENCODING is used to encode HtmlHelp index ( hhk), content ( hhc) +# and project file content. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. -CHM_INDEX_ENCODING = +CHM_INDEX_ENCODING = -# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag -# controls whether a binary table of contents is generated (YES) or a -# normal table of contents (NO) in the .chm file. +# The BINARY_TOC flag controls whether a binary table of contents is generated ( +# YES) or a normal table of contents ( NO) in the .chm file. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. BINARY_TOC = NO -# The TOC_EXPAND flag can be set to YES to add extra items for group members -# to the contents of the HTML help documentation and to the tree view. +# The TOC_EXPAND flag can be set to YES to add extra items for group members to +# the table of contents of the HTML help documentation and to the tree view. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. TOC_EXPAND = NO -# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and -# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated -# that can be used as input for Qt's qhelpgenerator to generate a -# Qt Compressed Help (.qch) of the generated HTML documentation. +# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and +# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that +# can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help +# (.qch) of the generated HTML documentation. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_QHP = NO -# If the QHG_LOCATION tag is specified, the QCH_FILE tag can -# be used to specify the file name of the resulting .qch file. -# The path specified is relative to the HTML output folder. +# If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify +# the file name of the resulting .qch file. The path specified is relative to +# the HTML output folder. +# This tag requires that the tag GENERATE_QHP is set to YES. -QCH_FILE = +QCH_FILE = -# The QHP_NAMESPACE tag specifies the namespace to use when generating -# Qt Help Project output. For more information please see -# http://doc.trolltech.com/qthelpproject.html#namespace +# The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help +# Project output. For more information please see Qt Help Project / Namespace +# (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#namespace). +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_QHP is set to YES. QHP_NAMESPACE = org.doxygen.Project -# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating -# Qt Help Project output. For more information please see -# http://doc.trolltech.com/qthelpproject.html#virtual-folders +# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt +# Help Project output. For more information please see Qt Help Project / Virtual +# Folders (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#virtual- +# folders). +# The default value is: doc. +# This tag requires that the tag GENERATE_QHP is set to YES. QHP_VIRTUAL_FOLDER = doc -# If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to -# add. For more information please see -# http://doc.trolltech.com/qthelpproject.html#custom-filters +# If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom +# filter to add. For more information please see Qt Help Project / Custom +# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom- +# filters). +# This tag requires that the tag GENERATE_QHP is set to YES. -QHP_CUST_FILTER_NAME = +QHP_CUST_FILTER_NAME = -# The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the -# custom filter to add. For more information please see -# -# Qt Help Project / Custom Filters. +# The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the +# custom filter to add. For more information please see Qt Help Project / Custom +# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom- +# filters). +# This tag requires that the tag GENERATE_QHP is set to YES. -QHP_CUST_FILTER_ATTRS = +QHP_CUST_FILTER_ATTRS = -# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this -# project's -# filter section matches. -# -# Qt Help Project / Filter Attributes. +# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this +# project's filter section matches. Qt Help Project / Filter Attributes (see: +# http://qt-project.org/doc/qt-4.8/qthelpproject.html#filter-attributes). +# This tag requires that the tag GENERATE_QHP is set to YES. -QHP_SECT_FILTER_ATTRS = +QHP_SECT_FILTER_ATTRS = -# If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can -# be used to specify the location of Qt's qhelpgenerator. -# If non-empty doxygen will try to run qhelpgenerator on the generated -# .qhp file. +# The QHG_LOCATION tag can be used to specify the location of Qt's +# qhelpgenerator. If non-empty doxygen will try to run qhelpgenerator on the +# generated .qhp file. +# This tag requires that the tag GENERATE_QHP is set to YES. -QHG_LOCATION = +QHG_LOCATION = -# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files -# will be generated, which together with the HTML files, form an Eclipse help -# plugin. To install this plugin and make it available under the help contents -# menu in Eclipse, the contents of the directory containing the HTML and XML -# files needs to be copied into the plugins directory of eclipse. The name of -# the directory within the plugins directory should be the same as -# the ECLIPSE_DOC_ID value. After copying Eclipse needs to be restarted before -# the help appears. +# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be +# generated, together with the HTML files, they form an Eclipse help plugin. To +# install this plugin and make it available under the help contents menu in +# Eclipse, the contents of the directory containing the HTML and XML files needs +# to be copied into the plugins directory of eclipse. The name of the directory +# within the plugins directory should be the same as the ECLIPSE_DOC_ID value. +# After copying Eclipse needs to be restarted before the help appears. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_ECLIPSEHELP = NO -# A unique identifier for the eclipse help plugin. When installing the plugin -# the directory name containing the HTML and XML files should also have -# this name. +# A unique identifier for the Eclipse help plugin. When installing the plugin +# the directory name containing the HTML and XML files should also have this +# name. Each documentation set should have its own identifier. +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES. ECLIPSE_DOC_ID = org.doxygen.Project -# The DISABLE_INDEX tag can be used to turn on/off the condensed index at -# top of each HTML page. The value NO (the default) enables the index and -# the value YES disables it. +# If you want full control over the layout of the generated HTML pages it might +# be necessary to disable the index and replace it with your own. The +# DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top +# of each HTML page. A value of NO enables the index and the value YES disables +# it. Since the tabs in the index contain the same information as the navigation +# tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. DISABLE_INDEX = NO -# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values -# (range [0,1..20]) that doxygen will group on one line in the generated HTML -# documentation. Note that a value of 0 will completely suppress the enum -# values from appearing in the overview section. - -ENUM_VALUES_PER_LINE = 20 - -# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index -# structure should be generated to display hierarchical information. -# If the tag value is set to YES, a side panel will be generated -# containing a tree-like index structure (just like the one that -# is generated for HTML Help). For this to work a browser that supports -# JavaScript, DHTML, CSS and frames is required (i.e. any modern browser). -# Windows users are probably better off using the HTML help feature. +# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index +# structure should be generated to display hierarchical information. If the tag +# value is set to YES, a side panel will be generated containing a tree-like +# index structure (just like the one that is generated for HTML Help). For this +# to work a browser that supports JavaScript, DHTML, CSS and frames is required +# (i.e. any modern browser). Windows users are probably better off using the +# HTML help feature. Via custom stylesheets (see HTML_EXTRA_STYLESHEET) one can +# further fine-tune the look of the index. As an example, the default style +# sheet generated by doxygen has an example that shows how to put an image at +# the root of the tree instead of the PROJECT_NAME. Since the tree basically has +# the same information as the tab index, you could consider setting +# DISABLE_INDEX to YES when enabling this option. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_TREEVIEW = YES -# By enabling USE_INLINE_TREES, doxygen will generate the Groups, Directories, -# and Class Hierarchy pages using a tree view instead of an ordered list. +# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that +# doxygen will group on one line in the generated HTML documentation. +# +# Note that a value of 0 will completely suppress the enum values from appearing +# in the overview section. +# Minimum value: 0, maximum value: 20, default value: 4. +# This tag requires that the tag GENERATE_HTML is set to YES. -USE_INLINE_TREES = YES +ENUM_VALUES_PER_LINE = 20 -# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be -# used to set the initial width (in pixels) of the frame in which the tree -# is shown. +# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used +# to set the initial width (in pixels) of the frame in which the tree is shown. +# Minimum value: 0, maximum value: 1500, default value: 250. +# This tag requires that the tag GENERATE_HTML is set to YES. TREEVIEW_WIDTH = 231 -# When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open -# links to external symbols imported via tag files in a separate window. +# When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open links to +# external symbols imported via tag files in a separate window. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. EXT_LINKS_IN_WINDOW = NO -# Use this tag to change the font size of Latex formulas included -# as images in the HTML documentation. The default is 10. Note that -# when you change the font size after a successful doxygen run you need -# to manually remove any form_*.png images from the HTML output directory -# to force them to be regenerated. +# Use this tag to change the font size of LaTeX formulas included as images in +# the HTML documentation. When you change the font size after a successful +# doxygen run you need to manually remove any form_*.png images from the HTML +# output directory to force them to be regenerated. +# Minimum value: 8, maximum value: 50, default value: 10. +# This tag requires that the tag GENERATE_HTML is set to YES. FORMULA_FONTSIZE = 10 -# Use the FORMULA_TRANPARENT tag to determine whether or not the images -# generated for formulas are transparent PNGs. Transparent PNGs are -# not supported properly for IE 6.0, but are supported on all modern browsers. -# Note that when changing this option you need to delete any form_*.png files -# in the HTML output before the changes have effect. +# Use the FORMULA_TRANPARENT tag to determine whether or not the images +# generated for formulas are transparent PNGs. Transparent PNGs are not +# supported properly for IE 6.0, but are supported on all modern browsers. +# +# Note that when changing this option you need to delete any form_*.png files in +# the HTML output directory before the changes have effect. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. FORMULA_TRANSPARENT = YES -# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax -# (see http://www.mathjax.org) which uses client side Javascript for the -# rendering instead of using prerendered bitmaps. Use this if you do not -# have LaTeX installed or if you want to formulas look prettier in the HTML -# output. When enabled you also need to install MathJax separately and -# configure the path to it using the MATHJAX_RELPATH option. +# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see +# http://www.mathjax.org) which uses client side Javascript for the rendering +# instead of using prerendered bitmaps. Use this if you do not have LaTeX +# installed or if you want to formulas look prettier in the HTML output. When +# enabled you may also need to install MathJax separately and configure the path +# to it using the MATHJAX_RELPATH option. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. USE_MATHJAX = NO -# When MathJax is enabled you need to specify the location relative to the -# HTML output directory using the MATHJAX_RELPATH option. The destination -# directory should contain the MathJax.js script. For instance, if the mathjax -# directory is located at the same level as the HTML output directory, then -# MATHJAX_RELPATH should be ../mathjax. The default value points to the -# mathjax.org site, so you can quickly see the result without installing -# MathJax, but it is strongly recommended to install a local copy of MathJax -# before deployment. +# When MathJax is enabled you can set the default output format to be used for +# the MathJax output. See the MathJax site (see: +# http://docs.mathjax.org/en/latest/output.html) for more details. +# Possible values are: HTML-CSS (which is slower, but has the best +# compatibility), NativeMML (i.e. MathML) and SVG. +# The default value is: HTML-CSS. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_FORMAT = HTML-CSS + +# When MathJax is enabled you need to specify the location relative to the HTML +# output directory using the MATHJAX_RELPATH option. The destination directory +# should contain the MathJax.js script. For instance, if the mathjax directory +# is located at the same level as the HTML output directory, then +# MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax +# Content Delivery Network so you can quickly see the result without installing +# MathJax. However, it is strongly recommended to install a local copy of +# MathJax from http://www.mathjax.org before deployment. +# The default value is: http://cdn.mathjax.org/mathjax/latest. +# This tag requires that the tag USE_MATHJAX is set to YES. MATHJAX_RELPATH = http://www.mathjax.org/mathjax -# When the SEARCHENGINE tag is enabled doxygen will generate a search box -# for the HTML output. The underlying search engine uses javascript -# and DHTML and should work on any modern browser. Note that when using -# HTML help (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets -# (GENERATE_DOCSET) there is already a search function so this one should -# typically be disabled. For large projects the javascript based search engine -# can be slow, then enabling SERVER_BASED_SEARCH may provide a better solution. +# The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax +# extension names that should be enabled during MathJax rendering. For example +# MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_EXTENSIONS = + +# The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces +# of code that will be used on startup of the MathJax code. See the MathJax site +# (see: http://docs.mathjax.org/en/latest/output.html) for more details. For an +# example see the documentation. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_CODEFILE = + +# When the SEARCHENGINE tag is enabled doxygen will generate a search box for +# the HTML output. The underlying search engine uses javascript and DHTML and +# should work on any modern browser. Note that when using HTML help +# (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET) +# there is already a search function so this one should typically be disabled. +# For large projects the javascript based search engine can be slow, then +# enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to +# search using the keyboard; to jump to the search box use + S +# (what the is depends on the OS and browser, but it is typically +# , /