diff options
author | Alan Alpert <alan.alpert@nokia.com> | 2011-06-07 15:12:53 +1000 |
---|---|---|
committer | Alan Alpert <alan.alpert@nokia.com> | 2011-06-07 15:12:53 +1000 |
commit | 21d0c6ef9a7df3e5fa69ff344e9dee2d2159c43c (patch) | |
tree | 8b9545958c1dde4e0963dd14d74faa3dab394486 /src/declarative/particles | |
parent | 6e22ead1767d0ef9463b34bee071ca54647a547c (diff) |
Immense Particles Refactor Part A
Qt.labs.particles 2.0 moved to QtQuick.Particles 2.0. All C++ classes
changed names, some renaming of QML types.
Also adds CustomParticle.
Diffstat (limited to 'src/declarative/particles')
72 files changed, 8729 insertions, 0 deletions
diff --git a/src/declarative/particles/defaultshaders/ctfragment.shader b/src/declarative/particles/defaultshaders/ctfragment.shader new file mode 100644 index 0000000000..a17f5841ca --- /dev/null +++ b/src/declarative/particles/defaultshaders/ctfragment.shader @@ -0,0 +1,11 @@ +uniform sampler2D texture; +uniform sampler2D colortable; +uniform sampler2D opacitytable; + +varying highp vec2 fTex; +varying lowp vec4 fColor; +varying lowp float tt; + +void main() { + gl_FragColor = (texture2D(texture, fTex).w) * fColor * texture2D(colortable, vec2(tt, 0.5)) *( texture2D(opacitytable, vec2(tt, 0.5)).w); +} diff --git a/src/declarative/particles/defaultshaders/ctvertex.shader b/src/declarative/particles/defaultshaders/ctvertex.shader new file mode 100644 index 0000000000..b20676cc49 --- /dev/null +++ b/src/declarative/particles/defaultshaders/ctvertex.shader @@ -0,0 +1,38 @@ +attribute highp vec2 vPos; +attribute highp vec2 vTex; +attribute highp vec4 vData; // x = time, y = lifeSpan, z = size, w = endSize +attribute highp vec4 vVec; // x,y = constant speed, z,w = acceleration +attribute lowp vec4 vColor; + +uniform highp mat4 matrix; +uniform highp float timestamp; +uniform sampler2D sizetable; +uniform sampler2D opacitytable; + +varying highp vec2 fTex; +varying lowp vec4 fColor; +varying lowp float tt; + +void main() { + fTex = vTex; + highp float size = vData.z; + highp float endSize = vData.w; + + highp float t = (timestamp - vData.x) / vData.y; + + highp float currentSize = mix(size, endSize, t * t) * texture2D(sizetable, vec2(t,0.5)).w; + + if (t < 0. || t > 1.) + currentSize = 0.; + + highp vec2 pos = vPos + - currentSize / 2. + currentSize * vTex // adjust size + + vVec.xy * t * vData.y // apply speed vector.. + + 0.5 * vVec.zw * pow(t * vData.y, 2.); + + gl_Position = matrix * vec4(pos.x, pos.y, 0, 1); + + fColor = vColor; + tt = t; + +} diff --git a/src/declarative/particles/defaultshaders/defaultFadeInOut.png b/src/declarative/particles/defaultshaders/defaultFadeInOut.png Binary files differnew file mode 100644 index 0000000000..89c04eaefe --- /dev/null +++ b/src/declarative/particles/defaultshaders/defaultFadeInOut.png diff --git a/src/declarative/particles/defaultshaders/deformablefragment.shader b/src/declarative/particles/defaultshaders/deformablefragment.shader new file mode 100644 index 0000000000..494053e319 --- /dev/null +++ b/src/declarative/particles/defaultshaders/deformablefragment.shader @@ -0,0 +1,8 @@ +uniform sampler2D texture; + +varying highp vec2 fTex; +varying lowp float fFade; + +void main() { + gl_FragColor = (texture2D(texture, fTex)) * fFade; +} diff --git a/src/declarative/particles/defaultshaders/deformablevertex.shader b/src/declarative/particles/defaultshaders/deformablevertex.shader new file mode 100644 index 0000000000..01570950b1 --- /dev/null +++ b/src/declarative/particles/defaultshaders/deformablevertex.shader @@ -0,0 +1,57 @@ +attribute highp vec2 vPos; +attribute highp vec2 vTex; +attribute highp vec4 vData; // x = time, y = lifeSpan, z = size, w = endSize +attribute highp vec4 vVec; // x,y = constant speed, z,w = acceleration +attribute highp vec4 vDeformVec; //x,y x unit vector; z,w = y unit vector +attribute highp vec3 vRotation; //x = radians of rotation, y=rotation speed, z= bool autoRotate + +uniform highp mat4 matrix; +uniform highp float timestamp; +uniform lowp float opacity; + +varying highp vec2 fTex; +varying lowp float fFade; + +void main() { + fTex = vTex; + highp float size = vData.z; + highp float endSize = vData.w; + + highp float t = (timestamp - vData.x) / vData.y; + + highp float currentSize = mix(size, endSize, t * t); + + highp vec2 pos; + if (t < 0. || t > 1.){ + currentSize = 0.; + pos = vPos; + }else{ + highp float rotation = vRotation.x + vRotation.y * t * vData.y; + if(vRotation.z == 1.0){ + highp vec2 curVel = vVec.zw * t * vData.y + vVec.xy; + rotation += atan(curVel.y, curVel.x); + } + highp vec2 trigCalcs = vec2(cos(rotation), sin(rotation)); + highp vec2 xDeform = vDeformVec.xy * currentSize * (vTex.x-0.5); + highp vec2 yDeform = vDeformVec.zw * currentSize * (vTex.y-0.5); + highp vec2 xRotatedDeform; + xRotatedDeform.x = trigCalcs.x*xDeform.x - trigCalcs.y*xDeform.y; + xRotatedDeform.y = trigCalcs.y*xDeform.x + trigCalcs.x*xDeform.y; + highp vec2 yRotatedDeform; + yRotatedDeform.x = trigCalcs.x*yDeform.x - trigCalcs.y*yDeform.y; + yRotatedDeform.y = trigCalcs.y*yDeform.x + trigCalcs.x*yDeform.y; + pos = vPos + + xRotatedDeform + + yRotatedDeform + //- vec2(1,1) * currentSize * 0.5 // 'center' + + vVec.xy * t * vData.y // apply speed + + 0.5 * vVec.zw * pow(t * vData.y, 2.); // apply acceleration + } + + gl_Position = matrix * vec4(pos.x, pos.y, 0, 1); + + highp float fadeIn = min(t * 10., 1.); + highp float fadeOut = 1. - max(0., min((t - 0.75) * 4., 1.)); + + fFade = fadeIn * fadeOut * opacity; +} diff --git a/src/declarative/particles/defaultshaders/identitytable.png b/src/declarative/particles/defaultshaders/identitytable.png Binary files differnew file mode 100644 index 0000000000..2cada1bfad --- /dev/null +++ b/src/declarative/particles/defaultshaders/identitytable.png diff --git a/src/declarative/particles/defaultshaders/simplefragment.shader b/src/declarative/particles/defaultshaders/simplefragment.shader new file mode 100644 index 0000000000..494053e319 --- /dev/null +++ b/src/declarative/particles/defaultshaders/simplefragment.shader @@ -0,0 +1,8 @@ +uniform sampler2D texture; + +varying highp vec2 fTex; +varying lowp float fFade; + +void main() { + gl_FragColor = (texture2D(texture, fTex)) * fFade; +} diff --git a/src/declarative/particles/defaultshaders/simplevertex.shader b/src/declarative/particles/defaultshaders/simplevertex.shader new file mode 100644 index 0000000000..f185ef0700 --- /dev/null +++ b/src/declarative/particles/defaultshaders/simplevertex.shader @@ -0,0 +1,36 @@ +attribute highp vec2 vPos; +attribute highp vec2 vTex; +attribute highp vec4 vData; // x = time, y = lifeSpan, z = size, w = endSize +attribute highp vec4 vVec; // x,y = constant speed, z,w = acceleration + +uniform highp mat4 matrix; +uniform highp float timestamp; +uniform lowp float opacity; + +varying highp vec2 fTex; +varying lowp float fFade; + +void main() { + fTex = vTex; + highp float size = vData.z; + highp float endSize = vData.w; + + highp float t = (timestamp - vData.x) / vData.y; + + highp float currentSize = mix(size, endSize, t * t); + + if (t < 0. || t > 1.) + currentSize = 0.; + + highp vec2 pos = vPos + - currentSize / 2. + currentSize * vTex // adjust size + + vVec.xy * t * vData.y // apply speed vector.. + + 0.5 * vVec.zw * pow(t * vData.y, 2.); + + gl_Position = matrix * vec4(pos.x, pos.y, 0, 1); + + highp float fadeIn = min(t * 10., 1.); + highp float fadeOut = 1. - max(0., min((t - 0.75) * 4., 1.)); + + fFade = fadeIn * fadeOut * opacity; +} diff --git a/src/declarative/particles/defaultshaders/spritefragment.shader b/src/declarative/particles/defaultshaders/spritefragment.shader new file mode 100644 index 0000000000..4d89d69c6a --- /dev/null +++ b/src/declarative/particles/defaultshaders/spritefragment.shader @@ -0,0 +1,10 @@ +uniform sampler2D texture; + +varying highp vec2 fTexA; +varying highp vec2 fTexB; +varying lowp float progress; +varying lowp vec4 fColor; + +void main() { + gl_FragColor = mix(texture2D(texture, fTexA), texture2D(texture, fTexB), progress) * fColor.w; +} diff --git a/src/declarative/particles/defaultshaders/spriteimagefragment.shader b/src/declarative/particles/defaultshaders/spriteimagefragment.shader new file mode 100644 index 0000000000..ecd62cf390 --- /dev/null +++ b/src/declarative/particles/defaultshaders/spriteimagefragment.shader @@ -0,0 +1,9 @@ +uniform sampler2D texture; + +varying highp vec2 fTexA; +varying highp vec2 fTexB; +varying lowp float progress; + +void main() { + gl_FragColor = mix(texture2D(texture, fTexA), texture2D(texture, fTexB), progress); +} diff --git a/src/declarative/particles/defaultshaders/spriteimagevertex.shader b/src/declarative/particles/defaultshaders/spriteimagevertex.shader new file mode 100644 index 0000000000..27de2ada6a --- /dev/null +++ b/src/declarative/particles/defaultshaders/spriteimagevertex.shader @@ -0,0 +1,52 @@ +attribute highp vec2 vTex; +attribute highp vec4 vAnimData;// idx, duration, frameCount (this anim), timestamp (this anim) + +uniform highp mat4 matrix; +uniform highp float timestamp; +uniform lowp float opacity; +uniform highp float framecount; //maximum of all anims +uniform highp float animcount; +uniform highp float width; +uniform highp float height; + +varying highp vec2 fTexA; +varying highp vec2 fTexB; +varying lowp float progress; + + +void main() { + //Calculate frame location in texture + highp float frameIndex = mod((((timestamp - vAnimData.w)*1000.)/vAnimData.y),vAnimData.z); + progress = mod((timestamp - vAnimData.w)*1000., vAnimData.y) / vAnimData.y; + + frameIndex = floor(frameIndex); + highp vec2 frameTex; + if(vTex.x == 0.) + frameTex.x = (frameIndex/framecount); + else + frameTex.x = 1. * ((frameIndex + 1.)/framecount); + + if(vTex.y == 0.) + frameTex.y = (vAnimData.x/animcount); + else + frameTex.y = 1. * ((vAnimData.x + 1.)/animcount); + + fTexA = frameTex; + //Next frame is also passed, for interpolation + if(frameIndex != vAnimData.z - 1.)//Can't do it for the last frame though, this anim may not loop + frameIndex = mod(frameIndex+1., vAnimData.z); + + if(vTex.x == 0.) + frameTex.x = (frameIndex/framecount); + else + frameTex.x = 1. * ((frameIndex + 1.)/framecount); + + if(vTex.y == 0.) + frameTex.y = (vAnimData.x/animcount); + else + frameTex.y = 1. * ((vAnimData.x + 1.)/animcount); + fTexB = frameTex; + + + gl_Position = matrix * vec4(width * vTex.x, height * vTex.y, 0, 1); +} diff --git a/src/declarative/particles/defaultshaders/spritevertex.shader b/src/declarative/particles/defaultshaders/spritevertex.shader new file mode 100644 index 0000000000..78b8e36b3b --- /dev/null +++ b/src/declarative/particles/defaultshaders/spritevertex.shader @@ -0,0 +1,77 @@ +attribute highp vec2 vPos; +attribute highp vec2 vTex; +attribute highp vec4 vData; // x = time, y = lifeSpan, z = size, w = endSize +attribute highp vec4 vVec; // x,y = constant speed, z,w = acceleration +attribute highp vec4 vAnimData;// idx, duration, frameCount (this anim), timestamp (this anim) + +uniform highp mat4 matrix; +uniform highp float timestamp; +uniform lowp float opacity; +uniform highp float framecount; //maximum of all anims +uniform highp float animcount; + +varying highp vec2 fTexA; +varying highp vec2 fTexB; +varying lowp float progress; +varying lowp vec4 fColor; + +void main() { + highp float size = vData.z; + highp float endSize = vData.w; + + highp float t = (timestamp - vData.x) / vData.y; + + //Calculate frame location in texture + highp float frameIndex = mod((((timestamp - vAnimData.w)*1000.)/vAnimData.y),vAnimData.z); + progress = mod((timestamp - vAnimData.w)*1000., vAnimData.y) / vAnimData.y; + + frameIndex = floor(frameIndex); + highp vec2 frameTex = vTex; + if(vTex.x == 0.) + frameTex.x = (frameIndex/framecount); + else + frameTex.x = 1. * ((frameIndex + 1.)/framecount); + + if(vTex.y == 0.) + frameTex.y = (vAnimData.x/animcount); + else + frameTex.y = 1. * ((vAnimData.x + 1.)/animcount); + + fTexA = frameTex; + //Next frame is also passed, for interpolation + //### Should the next anim be precalculated to allow for interpolation there? + if(frameIndex != vAnimData.z - 1.)//Can't do it for the last frame though, this anim may not loop + frameIndex = mod(frameIndex+1., vAnimData.z); + + if(vTex.x == 0.) + frameTex.x = (frameIndex/framecount); + else + frameTex.x = 1. * ((frameIndex + 1.)/framecount); + + if(vTex.y == 0.) + frameTex.y = (vAnimData.x/animcount); + else + frameTex.y = 1. * ((vAnimData.x + 1.)/animcount); + fTexB = frameTex; + + //Applying Size here seems to screw with RockingAffector? + highp float currentSize = mix(size, endSize, t * t); + + if (t < 0. || t > 1.) + currentSize = 0.; + + //If affector is mananging pos, they don't set speed? + highp vec2 pos = vPos + - currentSize / 2. + currentSize * vTex // adjust size + + vVec.xy * t * vData.y // apply speed vector.. + + 0.5 * vVec.zw * pow(t * vData.y, 2.); + + gl_Position = matrix * vec4(pos.x, pos.y, 0, 1); + + // calculate opacity + highp float fadeIn = min(t * 10., 1.); + highp float fadeOut = 1. - max(0., min((t - 0.75) * 4., 1.)); + + lowp vec4 white = vec4(1.); + fColor = white * fadeIn * fadeOut * opacity; +} diff --git a/src/declarative/particles/defaultshaders/superfragment.shader b/src/declarative/particles/defaultshaders/superfragment.shader new file mode 100644 index 0000000000..a17f5841ca --- /dev/null +++ b/src/declarative/particles/defaultshaders/superfragment.shader @@ -0,0 +1,11 @@ +uniform sampler2D texture; +uniform sampler2D colortable; +uniform sampler2D opacitytable; + +varying highp vec2 fTex; +varying lowp vec4 fColor; +varying lowp float tt; + +void main() { + gl_FragColor = (texture2D(texture, fTex).w) * fColor * texture2D(colortable, vec2(tt, 0.5)) *( texture2D(opacitytable, vec2(tt, 0.5)).w); +} diff --git a/src/declarative/particles/defaultshaders/supervertex.shader b/src/declarative/particles/defaultshaders/supervertex.shader new file mode 100644 index 0000000000..432a23ce05 --- /dev/null +++ b/src/declarative/particles/defaultshaders/supervertex.shader @@ -0,0 +1,57 @@ +attribute highp vec2 vPos; +attribute highp vec2 vTex; +attribute highp vec4 vData; // x = time, y = lifeSpan, z = size, w = endSize +attribute highp vec4 vVec; // x,y = constant speed, z,w = acceleration +attribute lowp vec4 vColor; +attribute highp vec4 vDeformVec; //x,y x unit vector; z,w = y unit vector +attribute highp vec3 vRotation; //x = radians of rotation, y=rotation speed, z= bool autoRotate + +uniform highp mat4 matrix; +uniform highp float timestamp; +uniform sampler2D sizetable; +uniform sampler2D opacitytable; + +varying highp vec2 fTex; +varying lowp vec4 fColor; +varying lowp float tt; + +void main() { + fTex = vTex; + highp float size = vData.z; + highp float endSize = vData.w; + + highp float t = (timestamp - vData.x) / vData.y; + + highp float currentSize = mix(size, endSize, t * t) * texture2D(sizetable, vec2(t,0.5)).w; + + if (t < 0. || t > 1.) + currentSize = 0.; + + highp vec2 pos; + highp float rotation = vRotation.x + vRotation.y * t * vData.y; + if(vRotation.z == 1.0){ + highp vec2 curVel = vVec.zw * t * vData.y + vVec.xy; + rotation += atan(curVel.y, curVel.x); + } + highp vec2 trigCalcs = vec2(cos(rotation), sin(rotation)); + highp vec2 xDeform = vDeformVec.xy * currentSize * (vTex.x-0.5); + highp vec2 yDeform = vDeformVec.zw * currentSize * (vTex.y-0.5); + highp vec2 xRotatedDeform; + xRotatedDeform.x = trigCalcs.x*xDeform.x - trigCalcs.y*xDeform.y; + xRotatedDeform.y = trigCalcs.y*xDeform.x + trigCalcs.x*xDeform.y; + highp vec2 yRotatedDeform; + yRotatedDeform.x = trigCalcs.x*yDeform.x - trigCalcs.y*yDeform.y; + yRotatedDeform.y = trigCalcs.y*yDeform.x + trigCalcs.x*yDeform.y; + pos = vPos + + xRotatedDeform + + yRotatedDeform + //- vec2(1,1) * currentSize * 0.5 // 'center' + + vVec.xy * t * vData.y // apply speed + + 0.5 * vVec.zw * pow(t * vData.y, 2.); // apply acceleration + + gl_Position = matrix * vec4(pos.x, pos.y, 0, 1); + + fColor = vColor; + tt = t; + +} diff --git a/src/declarative/particles/defaultshaders/trailsfragment.shader b/src/declarative/particles/defaultshaders/trailsfragment.shader new file mode 100644 index 0000000000..d3db87fa30 --- /dev/null +++ b/src/declarative/particles/defaultshaders/trailsfragment.shader @@ -0,0 +1,8 @@ +uniform sampler2D texture; + +varying highp vec2 fTex; +varying lowp vec4 fColor; + +void main() { + gl_FragColor = (texture2D(texture, fTex).w) * fColor; +} diff --git a/src/declarative/particles/defaultshaders/trailsvertex.shader b/src/declarative/particles/defaultshaders/trailsvertex.shader new file mode 100644 index 0000000000..7bc1d66b71 --- /dev/null +++ b/src/declarative/particles/defaultshaders/trailsvertex.shader @@ -0,0 +1,37 @@ +attribute highp vec2 vPos; +attribute highp vec2 vTex; +attribute highp vec4 vData; // x = time, y = lifeSpan, z = size, w = endSize +attribute highp vec4 vVec; // x,y = constant speed, z,w = acceleration +attribute lowp vec4 vColor; + +uniform highp mat4 matrix; +uniform highp float timestamp; +uniform lowp float opacity; + +varying highp vec2 fTex; +varying lowp vec4 fColor; + +void main() { + fTex = vTex; + highp float size = vData.z; + highp float endSize = vData.w; + + highp float t = (timestamp - vData.x) / vData.y; + + highp float currentSize = mix(size, endSize, t * t); + + if (t < 0. || t > 1.) + currentSize = 0.; + + highp vec2 pos = vPos + - currentSize / 2. + currentSize * vTex // adjust size + + vVec.xy * t * vData.y // apply speed vector.. + + 0.5 * vVec.zw * pow(t * vData.y, 2.); + + gl_Position = matrix * vec4(pos.x, pos.y, 0, 1); + + highp float fadeIn = min(t * 10., 1.); + highp float fadeOut = 1. - max(0., min((t - 0.75) * 4., 1.)); + + fColor = vColor * fadeIn * fadeOut * opacity; +} diff --git a/src/declarative/particles/defaultshaders/ultrafragment.shader b/src/declarative/particles/defaultshaders/ultrafragment.shader new file mode 100644 index 0000000000..0627d0f1e8 --- /dev/null +++ b/src/declarative/particles/defaultshaders/ultrafragment.shader @@ -0,0 +1,16 @@ +uniform sampler2D texture; +uniform sampler2D colortable; +uniform sampler2D opacitytable; + +varying highp vec2 fTexA; +varying highp vec2 fTexB; +varying lowp float progress; +varying lowp vec4 fColor; +varying lowp float tt; + +void main() { + gl_FragColor = mix(texture2D(texture, fTexA), texture2D(texture, fTexB), progress) + * fColor + * texture2D(colortable, vec2(tt, 0.5)) + *( texture2D(opacitytable, vec2(tt, 0.5)).w); +} diff --git a/src/declarative/particles/defaultshaders/ultravertex.shader b/src/declarative/particles/defaultshaders/ultravertex.shader new file mode 100644 index 0000000000..65a1a3077a --- /dev/null +++ b/src/declarative/particles/defaultshaders/ultravertex.shader @@ -0,0 +1,94 @@ +attribute highp vec2 vPos; +attribute highp vec2 vTex; +attribute highp vec4 vData; // x = time, y = lifeSpan, z = size, w = endSize +attribute highp vec4 vVec; // x,y = constant speed, z,w = acceleration +attribute lowp vec4 vColor; +attribute highp vec4 vDeformVec; //x,y x unit vector; z,w = y unit vector +attribute highp vec3 vRotation; //x = radians of rotation, y=rotation speed, z= bool autoRotate +attribute highp vec4 vAnimData;// idx, duration, frameCount (this anim), timestamp (this anim) + +uniform highp mat4 matrix; +uniform highp float timestamp; +uniform highp float framecount; //maximum of all anims +uniform highp float animcount; +uniform sampler2D sizetable; + +varying lowp float tt; +varying highp vec2 fTexA; +varying highp vec2 fTexB; +varying lowp float progress; +varying lowp vec4 fColor; + + +void main() { + highp float size = vData.z; + highp float endSize = vData.w; + + highp float t = (timestamp - vData.x) / vData.y; + + //Calculate frame location in texture + highp float frameIndex = mod((((timestamp - vAnimData.w)*1000.)/vAnimData.y),vAnimData.z); + progress = mod((timestamp - vAnimData.w)*1000., vAnimData.y) / vAnimData.y; + + frameIndex = floor(frameIndex); + highp vec2 frameTex = vTex; + if(vTex.x == 0.) + frameTex.x = (frameIndex/framecount); + else + frameTex.x = 1. * ((frameIndex + 1.)/framecount); + + if(vTex.y == 0.) + frameTex.y = (vAnimData.x/animcount); + else + frameTex.y = 1. * ((vAnimData.x + 1.)/animcount); + + fTexA = frameTex; + //Next frame is also passed, for interpolation + //### Should the next anim be precalculated to allow for interpolation there? + if(frameIndex != vAnimData.z - 1.)//Can't do it for the last frame though, this anim may not loop + frameIndex = mod(frameIndex+1., vAnimData.z); + + if(vTex.x == 0.) + frameTex.x = (frameIndex/framecount); + else + frameTex.x = 1. * ((frameIndex + 1.)/framecount); + + if(vTex.y == 0.) + frameTex.y = (vAnimData.x/animcount); + else + frameTex.y = 1. * ((vAnimData.x + 1.)/animcount); + fTexB = frameTex; + + highp float currentSize = mix(size, endSize, t * t) * texture2D(sizetable, vec2(t,0.5)).w; + + if (t < 0. || t > 1.) + currentSize = 0.; + + highp vec2 pos; + highp float rotation = vRotation.x + vRotation.y * t * vData.y; + if(vRotation.z == 1.0){ + highp vec2 curVel = vVec.zw * t * vData.y + vVec.xy; + rotation += atan(curVel.y, curVel.x); + } + highp vec2 trigCalcs = vec2(cos(rotation), sin(rotation)); + highp vec2 xDeform = vDeformVec.xy * currentSize * (vTex.x-0.5); + highp vec2 yDeform = vDeformVec.zw * currentSize * (vTex.y-0.5); + highp vec2 xRotatedDeform; + xRotatedDeform.x = trigCalcs.x*xDeform.x - trigCalcs.y*xDeform.y; + xRotatedDeform.y = trigCalcs.y*xDeform.x + trigCalcs.x*xDeform.y; + highp vec2 yRotatedDeform; + yRotatedDeform.x = trigCalcs.x*yDeform.x - trigCalcs.y*yDeform.y; + yRotatedDeform.y = trigCalcs.y*yDeform.x + trigCalcs.x*yDeform.y; + pos = vPos + + xRotatedDeform + + yRotatedDeform + //- vec2(1,1) * currentSize * 0.5 // 'center' + + vVec.xy * t * vData.y // apply speed + + 0.5 * vVec.zw * pow(t * vData.y, 2.); // apply acceleration + + gl_Position = matrix * vec4(pos.x, pos.y, 0, 1); + + fColor = vColor; + tt = t; + +} diff --git a/src/declarative/particles/particles.pri b/src/declarative/particles/particles.pri new file mode 100644 index 0000000000..04200a380e --- /dev/null +++ b/src/declarative/particles/particles.pri @@ -0,0 +1,60 @@ +INCLUDEPATH += $$PWD + +HEADERS += \ + $$PWD/qsgangleddirection_p.h \ + $$PWD/qsgcustomparticle_p.h \ + $$PWD/qsgellipseextruder_p.h \ + $$PWD/qsgemitter_p.h \ + $$PWD/qsgfollowemitter_p.h \ + $$PWD/qsgfriction_p.h \ + $$PWD/qsggravity_p.h \ + $$PWD/qsgimageparticle_p.h \ + $$PWD/qsgitemparticle_p.h \ + $$PWD/qsgkill_p.h \ + $$PWD/qsglineextruder_p.h \ + $$PWD/qsgmaskextruder_p.h \ + $$PWD/qsgmodelparticle_p.h \ + $$PWD/qsgparticleaffector_p.h \ + $$PWD/qsgparticleemitter_p.h \ + $$PWD/qsgparticleextruder_p.h \ + $$PWD/qsgparticlepainter_p.h \ + $$PWD/qsgparticlesmodule_p.h \ + $$PWD/qsgparticlesystem_p.h \ + $$PWD/qsgpointattractor_p.h \ + $$PWD/qsgpointdirection_p.h \ + $$PWD/qsgspritegoal_p.h \ + $$PWD/qsgstochasticdirection_p.h \ + $$PWD/qsgtargeteddirection_p.h \ + $$PWD/qsgturbulence_p.h \ + $$PWD/qsgwander_p.h + +SOURCES += \ + $$PWD/qsgangleddirection.cpp \ + $$PWD/qsgcustomparticle.cpp \ + $$PWD/qsgellipseextruder.cpp \ + $$PWD/qsgemitter.cpp \ + $$PWD/qsgfollowemitter.cpp \ + $$PWD/qsgfriction.cpp \ + $$PWD/qsggravity.cpp \ + $$PWD/qsgimageparticle.cpp \ + $$PWD/qsgitemparticle.cpp \ + $$PWD/qsgkill.cpp \ + $$PWD/qsglineextruder.cpp \ + $$PWD/qsgmaskextruder.cpp \ + $$PWD/qsgmodelparticle.cpp \ + $$PWD/qsgparticleaffector.cpp \ + $$PWD/qsgparticleemitter.cpp \ + $$PWD/qsgparticleextruder.cpp \ + $$PWD/qsgparticlepainter.cpp \ + $$PWD/qsgparticlesmodule.cpp \ + $$PWD/qsgparticlesystem.cpp \ + $$PWD/qsgpointattractor.cpp \ + $$PWD/qsgpointdirection.cpp \ + $$PWD/qsgspritegoal.cpp \ + $$PWD/qsgstochasticdirection.cpp \ + $$PWD/qsgtargeteddirection.cpp \ + $$PWD/qsgturbulence.cpp \ + $$PWD/qsgwander.cpp + +RESOURCES += \ + $$PWD/particles.qrc diff --git a/src/declarative/particles/particles.qrc b/src/declarative/particles/particles.qrc new file mode 100644 index 0000000000..85931ec9ce --- /dev/null +++ b/src/declarative/particles/particles.qrc @@ -0,0 +1,22 @@ +<RCC> + <qresource prefix="/"> + <file>defaultshaders/spritefragment.shader</file> + <file>defaultshaders/spritevertex.shader</file> + <file>defaultshaders/ctfragment.shader</file> + <file>defaultshaders/ctvertex.shader</file> + <file>defaultshaders/trailsfragment.shader</file> + <file>defaultshaders/trailsvertex.shader</file> + <file>defaultshaders/spriteimagefragment.shader</file> + <file>defaultshaders/spriteimagevertex.shader</file> + <file>defaultshaders/identitytable.png</file> + <file>defaultshaders/defaultFadeInOut.png</file> + <file>defaultshaders/deformablefragment.shader</file> + <file>defaultshaders/deformablevertex.shader</file> + <file>defaultshaders/ultravertex.shader</file> + <file>defaultshaders/ultrafragment.shader</file> + <file>defaultshaders/supervertex.shader</file> + <file>defaultshaders/superfragment.shader</file> + <file>defaultshaders/simplevertex.shader</file> + <file>defaultshaders/simplefragment.shader</file> + </qresource> +</RCC> diff --git a/src/declarative/particles/qsgangleddirection.cpp b/src/declarative/particles/qsgangleddirection.cpp new file mode 100644 index 0000000000..5c32b53e3e --- /dev/null +++ b/src/declarative/particles/qsgangleddirection.cpp @@ -0,0 +1,66 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Declarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qsgangleddirection_p.h" +#include <cmath> +QT_BEGIN_NAMESPACE +const qreal CONV = 0.017453292519943295; +QSGAngledDirection::QSGAngledDirection(QObject *parent) : + QSGStochasticDirection(parent) + , m_angle(0) + , m_magnitude(0) + , m_angleVariation(0) + , m_magnitudeVariation(0) +{ + +} + +const QPointF &QSGAngledDirection::sample(const QPointF &from) +{ + //TODO: Faster + qreal theta = m_angle*CONV - m_angleVariation*CONV + rand()/float(RAND_MAX) * m_angleVariation*CONV * 2; + qreal mag = m_magnitude- m_magnitudeVariation + rand()/float(RAND_MAX) * m_magnitudeVariation * 2; + m_ret.setX(mag * cos(theta)); + m_ret.setY(mag * sin(theta)); + return m_ret; +} + +QT_END_NAMESPACE diff --git a/src/declarative/particles/qsgangleddirection_p.h b/src/declarative/particles/qsgangleddirection_p.h new file mode 100644 index 0000000000..76262453a9 --- /dev/null +++ b/src/declarative/particles/qsgangleddirection_p.h @@ -0,0 +1,133 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Declarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QSGANGLEDDIRECTION_H +#define QSGANGLEDDIRECTION_H +#include "qsgstochasticdirection_p.h" +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QSGAngledDirection : public QSGStochasticDirection +{ + Q_OBJECT + Q_PROPERTY(qreal angle READ angle WRITE setAngle NOTIFY angleChanged) + Q_PROPERTY(qreal magnitude READ magnitude WRITE setMagnitude NOTIFY magnitudeChanged) + Q_PROPERTY(qreal angleVariation READ angleVariation WRITE setAngleVariation NOTIFY angleVariationChanged) + Q_PROPERTY(qreal magnitudeVariation READ magnitudeVariation WRITE setMagnitudeVariation NOTIFY magnitudeVariationChanged) +public: + explicit QSGAngledDirection(QObject *parent = 0); + const QPointF &sample(const QPointF &from); + qreal angle() const + { + return m_angle; + } + + qreal magnitude() const + { + return m_magnitude; + } + + qreal angleVariation() const + { + return m_angleVariation; + } + + qreal magnitudeVariation() const + { + return m_magnitudeVariation; + } + +signals: + + void angleChanged(qreal arg); + + void magnitudeChanged(qreal arg); + + void angleVariationChanged(qreal arg); + + void magnitudeVariationChanged(qreal arg); + +public slots: +void setAngle(qreal arg) +{ + if (m_angle != arg) { + m_angle = arg; + emit angleChanged(arg); + } +} + +void setMagnitude(qreal arg) +{ + if (m_magnitude != arg) { + m_magnitude = arg; + emit magnitudeChanged(arg); + } +} + +void setAngleVariation(qreal arg) +{ + if (m_angleVariation != arg) { + m_angleVariation = arg; + emit angleVariationChanged(arg); + } +} + +void setMagnitudeVariation(qreal arg) +{ + if (m_magnitudeVariation != arg) { + m_magnitudeVariation = arg; + emit magnitudeVariationChanged(arg); + } +} + +private: +qreal m_angle; +qreal m_magnitude; +qreal m_angleVariation; +qreal m_magnitudeVariation; +}; + +QT_END_NAMESPACE +QT_END_HEADER +#endif // QSGANGLEDDIRECTION_H diff --git a/src/declarative/particles/qsgcustomparticle.cpp b/src/declarative/particles/qsgcustomparticle.cpp new file mode 100644 index 0000000000..808ff1c427 --- /dev/null +++ b/src/declarative/particles/qsgcustomparticle.cpp @@ -0,0 +1,581 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qsgcustomparticle_p.h" +#include <private/qsgshadereffectmesh_p.h> +#include <cstdlib> + +QT_BEGIN_NAMESPACE +/* + "uniform highp mat4 qt_ModelViewProjectionMatrix; \n" + "attribute highp vec4 qt_Vertex; \n" + "attribute highp vec2 qt_MultiTexCoord0; \n" + "varying highp vec2 qt_TexCoord0; \n" + "void main() { \n" + " qt_TexCoord0 = qt_MultiTexCoord0; \n" + " gl_Position = qt_ModelViewProjectionMatrix * qt_Vertex; \n" + "}"; +*/ +//Includes comments because the code isn't self explanatory +static const char qt_particles_default_vertex_code[] = + "attribute highp vec2 vPos; \n" + "attribute highp vec2 vTex; \n" + "attribute highp vec4 vData; // x = time, y = lifeSpan, z = size, w = endSize \n" + "attribute highp vec4 vVec; // x,y = constant speed, z,w = acceleration \n" + "uniform highp mat4 qt_ModelViewProjectionMatrix; \n" + "uniform highp float timestamp; \n" + "varying highp vec2 fTex; \n" + "void main() { \n" + " fTex = vTex; \n" + " highp float size = vData.z; \n" + " highp float endSize = vData.w; \n" + " highp float t = (timestamp - vData.x) / vData.y; \n" + " highp float currentSize = mix(size, endSize, t * t); \n" + " if (t < 0. || t > 1.) \n" + " currentSize = 0.; \n" + " highp vec2 pos = vPos \n" + " - currentSize / 2. + currentSize * vTex // adjust size \n" + " + vVec.xy * t * vData.y // apply speed vector.. \n" + " + 0.5 * vVec.zw * pow(t * vData.y, 2.); \n" + " gl_Position = qt_ModelViewProjectionMatrix * vec4(pos.x, pos.y, 0, 1); \n" + "}"; + +static const char qt_particles_default_fragment_code[] =//TODO: Default frag requires source? + "uniform sampler2D source; \n" + "varying highp vec2 fTex; \n" + "uniform lowp float qt_Opacity; \n" + "void main() { \n" + " gl_FragColor = texture2D(source, fTex) * qt_Opacity; \n" + "}"; + +/* +static const char qt_particles_default_vertex_code[] = + "attribute highp vec2 vPos; \n" + "attribute highp vec2 vTex; \n" + "uniform highp mat4 qt_ModelViewProjectionMatrix; \n" + "void main() { \n" + " highp float currentSize = 1000.0; \n" + " highp vec2 pos = vec2(100.0,100.0) \n" + " - currentSize / 2. + currentSize * vTex; // adjust size \n" + " gl_Position = qt_ModelViewProjectionMatrix * vec4(pos.x, pos.y, 0, 1); \n" + "}"; +static const char qt_particles_default_fragment_code[] =//TODO: Default frag requires source? + "void main() { \n" + " gl_FragColor = vec4(0,255,0,255); \n" + "}"; +*/ + +static const char qt_position_attribute_name[] = "qt_Vertex"; +static const char qt_texcoord_attribute_name[] = "qt_MultiTexCoord0"; + +static QSGGeometry::Attribute PlainParticle_Attributes[] = { + { 0, 2, GL_FLOAT }, // Position + { 1, 2, GL_FLOAT }, // TexCoord + { 2, 4, GL_FLOAT }, // Data + { 3, 4, GL_FLOAT }, // Vectors + { 4, 1, GL_FLOAT } // r +}; + +static QSGGeometry::AttributeSet PlainParticle_AttributeSet = +{ + 5, // Attribute Count + (2 + 2 + 4 + 4 + 1) * sizeof(float), + PlainParticle_Attributes +}; + +struct PlainVertex { + float x; + float y; + float tx; + float ty; + float t; + float lifeSpan; + float size; + float endSize; + float sx; + float sy; + float ax; + float ay; + float r; +}; + +struct PlainVertices { + PlainVertex v1; + PlainVertex v2; + PlainVertex v3; + PlainVertex v4; +}; + +QSGCustomParticle::QSGCustomParticle(QSGItem* parent) + : QSGParticlePainter(parent) + , m_pleaseReset(true) + , m_dirtyData(true) +{ + setFlag(QSGItem::ItemHasContents); +} + +void QSGCustomParticle::componentComplete() +{ + reset(); + QSGParticlePainter::componentComplete(); +} + + +//Trying to keep the shader conventions the same as in qsgshadereffectitem +/*! + \qmlproperty string CustomParticle::fragmentShader + + This property holds the fragment shader's GLSL source code. + The default shader passes the texture coordinate along to the fragment + shader as "varying highp vec2 qt_TexCoord0". +*/ + +void QSGCustomParticle::setFragmentShader(const QByteArray &code) +{ + if (m_source.fragmentCode.constData() == code.constData()) + return; + m_source.fragmentCode = code; + if (isComponentComplete()) { + reset(); + } + emit fragmentShaderChanged(); +} + +/*! + \qmlproperty string CustomParticle::vertexShader + + This property holds the vertex shader's GLSL source code. + The default shader expects the texture coordinate to be passed from the + vertex shader as "varying highp vec2 qt_TexCoord0", and it samples from a + sampler2D named "source". +*/ + +void QSGCustomParticle::setVertexShader(const QByteArray &code) +{ + if (m_source.vertexCode.constData() == code.constData()) + return; + m_source.vertexCode = code; + if (isComponentComplete()) { + reset(); + } + emit vertexShaderChanged(); +} + +void QSGCustomParticle::setCount(int c) +{ + QSGParticlePainter::setCount(c); + m_pleaseReset = true; +} + +void QSGCustomParticle::reset() +{ + disconnectPropertySignals(); + + m_source.attributeNames.clear(); + m_source.uniformNames.clear(); + m_source.respectsOpacity = false; + m_source.respectsMatrix = false; + m_source.className = metaObject()->className(); + + for (int i = 0; i < m_sources.size(); ++i) { + const SourceData &source = m_sources.at(i); + delete source.mapper; + if (source.item && source.item->parentItem() == this) + source.item->setParentItem(0); + } + m_sources.clear(); + + QSGParticlePainter::reset(); + m_pleaseReset = true; +} + + +void QSGCustomParticle::changeSource(int index) +{ + Q_ASSERT(index >= 0 && index < m_sources.size()); + QVariant v = property(m_sources.at(index).name.constData()); + setSource(v, index); +} + +void QSGCustomParticle::updateData() +{ + m_dirtyData = true; + update(); +} + +void QSGCustomParticle::setSource(const QVariant &var, int index) +{ + Q_ASSERT(index >= 0 && index < m_sources.size()); + + SourceData &source = m_sources[index]; + + source.item = 0; + if (var.isNull()) { + return; + } else if (!qVariantCanConvert<QObject *>(var)) { + qWarning("Could not assign source of type '%s' to property '%s'.", var.typeName(), source.name.constData()); + return; + } + + QObject *obj = qVariantValue<QObject *>(var); + + QSGTextureProvider *int3rface = QSGTextureProvider::from(obj); + if (!int3rface) { + qWarning("Could not assign property '%s', did not implement QSGTextureProvider.", source.name.constData()); + } + + source.item = qobject_cast<QSGItem *>(obj); + + // TODO: Copy better solution in QSGShaderEffectItem when they find it. + // 'source.item' needs a canvas to get a scenegraph node. + // The easiest way to make sure it gets a canvas is to + // make it a part of the same item tree as 'this'. + if (source.item && source.item->parentItem() == 0) { + source.item->setParentItem(this); + source.item->setVisible(false); + } +} + +void QSGCustomParticle::disconnectPropertySignals() +{ + disconnect(this, 0, this, SLOT(updateData())); + for (int i = 0; i < m_sources.size(); ++i) { + SourceData &source = m_sources[i]; + disconnect(this, 0, source.mapper, 0); + disconnect(source.mapper, 0, this, 0); + } +} + +void QSGCustomParticle::connectPropertySignals() +{ + QSet<QByteArray>::const_iterator it; + for (it = m_source.uniformNames.begin(); it != m_source.uniformNames.end(); ++it) { + int pi = metaObject()->indexOfProperty(it->constData()); + if (pi >= 0) { + QMetaProperty mp = metaObject()->property(pi); + if (!mp.hasNotifySignal()) + qWarning("QSGShaderEffectItem: property '%s' does not have notification method!", it->constData()); + QByteArray signalName("2"); + signalName.append(mp.notifySignal().signature()); + connect(this, signalName, this, SLOT(updateData())); + } else { + qWarning("QSGShaderEffectItem: '%s' does not have a matching property!", it->constData()); + } + } + for (int i = 0; i < m_sources.size(); ++i) { + SourceData &source = m_sources[i]; + int pi = metaObject()->indexOfProperty(source.name.constData()); + if (pi >= 0) { + QMetaProperty mp = metaObject()->property(pi); + QByteArray signalName("2"); + signalName.append(mp.notifySignal().signature()); + connect(this, signalName, source.mapper, SLOT(map())); + source.mapper->setMapping(this, i); + connect(source.mapper, SIGNAL(mapped(int)), this, SLOT(changeSource(int))); + } else { + qWarning("QSGShaderEffectItem: '%s' does not have a matching source!", source.name.constData()); + } + } +} + +void QSGCustomParticle::updateProperties() +{ + QByteArray vertexCode = m_source.vertexCode; + QByteArray fragmentCode = m_source.fragmentCode; + if (vertexCode.isEmpty()) + vertexCode = qt_particles_default_vertex_code; + if (fragmentCode.isEmpty()) + fragmentCode = qt_particles_default_fragment_code; + + m_source.attributeNames.clear(); + m_source.attributeNames << "vPos" << "vTex" << "vData" << "vVec" << "r"; + + lookThroughShaderCode(vertexCode); + lookThroughShaderCode(fragmentCode); + + if (!m_source.attributeNames.contains(qt_position_attribute_name)) + qWarning("QSGShaderEffectItem: Missing reference to \'%s\'.", qt_position_attribute_name); + if (!m_source.attributeNames.contains(qt_texcoord_attribute_name)) + qWarning("QSGShaderEffectItem: Missing reference to \'%s\'.", qt_texcoord_attribute_name); + if (!m_source.respectsMatrix) + qWarning("QSGShaderEffectItem: Missing reference to \'qt_ModelViewProjectionMatrix\'."); + if (!m_source.respectsOpacity) + qWarning("QSGShaderEffectItem: Missing reference to \'qt_Opacity\'."); + + for (int i = 0; i < m_sources.size(); ++i) { + QVariant v = property(m_sources.at(i).name); + setSource(v, i); + } + + connectPropertySignals(); +} + +void QSGCustomParticle::lookThroughShaderCode(const QByteArray &code) +{ + // Regexp for matching attributes and uniforms. + // In human readable form: attribute|uniform [lowp|mediump|highp] <type> <name> + static QRegExp re(QLatin1String("\\b(attribute|uniform)\\b\\s*\\b(?:lowp|mediump|highp)?\\b\\s*\\b(\\w+)\\b\\s*\\b(\\w+)")); + Q_ASSERT(re.isValid()); + + int pos = -1; + + QString wideCode = QString::fromLatin1(code.constData(), code.size()); + + while ((pos = re.indexIn(wideCode, pos + 1)) != -1) { + QByteArray decl = re.cap(1).toLatin1(); // uniform or attribute + QByteArray type = re.cap(2).toLatin1(); // type + QByteArray name = re.cap(3).toLatin1(); // variable name + + if (decl == "attribute") { + if(!m_source.attributeNames.contains(name))//TODO: Can they add custom attributes? + qWarning() << "Custom Particle: Unknown attribute " << name; + } else { + Q_ASSERT(decl == "uniform");//TODO: Shouldn't assert + + if (name == "qt_ModelViewProjectionMatrix") { + m_source.respectsMatrix = true; + } else if (name == "qt_Opacity") { + m_source.respectsOpacity = true; + } else if (name == "timestamp") { + //TODO: Copy the whole thing just because I have one more uniform? + } else { + m_source.uniformNames.insert(name); + if (type == "sampler2D") { + SourceData d; + d.mapper = new QSignalMapper; + d.name = name; + d.item = 0; + m_sources.append(d); + } + } + } + } +} + +QSGNode *QSGCustomParticle::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *) +{ + if(m_pleaseReset){ + if(m_node) + delete m_node; + //delete m_material;//Shader effect item doesn't regen material? + + m_node = 0; + m_pleaseReset = false; + m_dirtyData = false; + } + + if(m_system && m_system->isRunning()) + prepareNextFrame(); + if (m_node){ + if(oldNode) + Q_ASSERT(oldNode == m_node); + update(); + m_node->markDirty(QSGNode::DirtyMaterial); //done in buildData? + } + + return m_node; +} + +void QSGCustomParticle::prepareNextFrame(){ + if(!m_node) + m_node = buildCustomNode(); + if(!m_node) + return; + + m_lastTime = m_system->systemSync(this) / 1000.; + if(m_dirtyData || true)//Currently this is how we update timestamp... potentially over expensive. + buildData(); +} + +QSGShaderEffectNode* QSGCustomParticle::buildCustomNode() +{ + if (m_count * 4 > 0xffff) { + printf("CustomParticle: Too many particles... \n");//####Why is this here? + return 0; + } + + if(m_count <= 0) { + printf("CustomParticle: Too few particles... \n"); + return 0; + } + + + //Create Particle Geometry + int vCount = m_count * 4; + int iCount = m_count * 6; + QSGGeometry *g = new QSGGeometry(PlainParticle_AttributeSet, vCount, iCount); + g->setDrawingMode(GL_TRIANGLES); + PlainVertex *vertices = (PlainVertex *) g->vertexData(); + for (int p=0; p<m_count; ++p) { + double r = rand()/(double)RAND_MAX;//TODO: Seed? + for (int i=0; i<4; ++i) { + vertices[i].x = 0; + vertices[i].y = 0; + vertices[i].t = -1; + vertices[i].lifeSpan = 0; + vertices[i].size = 0; + vertices[i].endSize = 0; + vertices[i].sx = 0; + vertices[i].sy = 0; + vertices[i].ax = 0; + vertices[i].ay = 0; + vertices[i].r = r; + } + + vertices[0].tx = 0; + vertices[0].ty = 0; + + vertices[1].tx = 1; + vertices[1].ty = 0; + + vertices[2].tx = 0; + vertices[2].ty = 1; + + vertices[3].tx = 1; + vertices[3].ty = 1; + + vertices += 4; + } + quint16 *indices = g->indexDataAsUShort(); + for (int i=0; i<m_count; ++i) { + int o = i * 4; + indices[0] = o; + indices[1] = o + 1; + indices[2] = o + 2; + indices[3] = o + 1; + indices[4] = o + 3; + indices[5] = o + 2; + indices += 6; + } + + updateProperties(); + QSGShaderEffectNode* node = new QSGShaderEffectNode(); + + + node->setGeometry(g); + node->setMaterial(&m_material); + + /* + //For debugging, just use grid vertices like ShaderEffect + node->setGeometry(0); + QSGShaderEffectMesh* mesh = new QSGGridMesh(); + node->setFlag(QSGNode::OwnsGeometry, false); + + qDebug() << m_source.attributeNames; + QSGGeometry* geometry = node->geometry(); + geometry = mesh->updateGeometry(geometry, m_source.attributeNames, QRectF(0,0,width(),height())); + if(!geometry) + qDebug() << "Should have written the error handling"; + else + qDebug() << "Mesh Loaded"; + node->setGeometry(geometry); + qDebug() << QString("INIT") << geometry << (QObject*)node; + node->setFlag(QSGNode::OwnsGeometry, true); + */ + QSGShaderEffectProgram s = m_source; + if (s.fragmentCode.isEmpty()) + s.fragmentCode = qt_particles_default_fragment_code; + if (s.vertexCode.isEmpty()) + s.vertexCode = qt_particles_default_vertex_code; + m_material.setProgramSource(s); + node->markDirty(QSGNode::DirtyMaterial); + node->markDirty(QSGNode::DirtyAll); + return node; +} + +static const QByteArray timestampName("timestamp"); + +void QSGCustomParticle::buildData() +{ + if(!m_node)//Operates on m_node + return; + QVector<QPair<QByteArray, QVariant> > values; + QVector<QPair<QByteArray, QPointer<QSGItem> > > textures; + const QVector<QPair<QByteArray, QPointer<QSGItem> > > &oldTextures = m_material.textureProviders(); + + for (QSet<QByteArray>::const_iterator it = m_source.uniformNames.begin(); + it != m_source.uniformNames.end(); ++it) { + values.append(qMakePair(*it, property(*it))); + } + for (int i = 0; i < oldTextures.size(); ++i) { + QSGTextureProvider *oldSource = QSGTextureProvider::from(oldTextures.at(i).second); + if (oldSource && oldSource->textureChangedSignal()) + disconnect(oldTextures.at(i).second, oldSource->textureChangedSignal(), m_node, SLOT(markDirtyTexture())); + } + for (int i = 0; i < m_sources.size(); ++i) { + const SourceData &source = m_sources.at(i); + textures.append(qMakePair(source.name, source.item)); + QSGTextureProvider *t = QSGTextureProvider::from(source.item); + if (t && t->textureChangedSignal()) + connect(source.item, t->textureChangedSignal(), m_node, SLOT(markDirtyTexture()), Qt::DirectConnection); + } + values.append(qMakePair(timestampName, QVariant(m_lastTime))); + m_material.setUniforms(values); + m_material.setTextureProviders(textures); + m_node->markDirty(QSGNode::DirtyMaterial); + m_dirtyData = false; +} + +void QSGCustomParticle::load(QSGParticleData *d) +{ + reload(d);//We don't do anything special in C++ here. +} + +void QSGCustomParticle::reload(QSGParticleData *d) +{ + if (m_node == 0) + return; + + PlainVertices *particles = (PlainVertices *) m_node->geometry()->vertexData(); + + int pos = particleTypeIndex(d); + + PlainVertices &p = particles[pos]; + + //Perhaps we could be more efficient? + vertexCopy(p.v1, d->pv); + vertexCopy(p.v2, d->pv); + vertexCopy(p.v3, d->pv); + vertexCopy(p.v4, d->pv); + +} + +QT_END_NAMESPACE diff --git a/src/declarative/particles/qsgcustomparticle_p.h b/src/declarative/particles/qsgcustomparticle_p.h new file mode 100644 index 0000000000..95144ef638 --- /dev/null +++ b/src/declarative/particles/qsgcustomparticle_p.h @@ -0,0 +1,114 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef CUSTOM_PARTICLE_H +#define CUSTOM_PARTICLE_H +#include "qsgparticlepainter_p.h" +#include <QtDeclarative/private/qsgshadereffectnode_p.h> +#include <QSignalMapper> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QSGNode; + +//Genealogy: Hybrid of UltraParticle and ShaderEffectItem +class QSGCustomParticle : public QSGParticlePainter +{ + Q_OBJECT + Q_PROPERTY(QByteArray fragmentShader READ fragmentShader WRITE setFragmentShader NOTIFY fragmentShaderChanged) + Q_PROPERTY(QByteArray vertexShader READ vertexShader WRITE setVertexShader NOTIFY vertexShaderChanged) + +public: + explicit QSGCustomParticle(QSGItem* parent=0); + virtual void load(QSGParticleData*); + virtual void reload(QSGParticleData*); + virtual void setCount(int c); + + QByteArray fragmentShader() const { return m_source.fragmentCode; } + void setFragmentShader(const QByteArray &code); + + QByteArray vertexShader() const { return m_source.vertexCode; } + void setVertexShader(const QByteArray &code); +public Q_SLOTS: + void updateData(); + void changeSource(int); +Q_SIGNALS: + void fragmentShaderChanged(); + void vertexShaderChanged(); +protected: + QSGNode *updatePaintNode(QSGNode *, UpdatePaintNodeData *); + void prepareNextFrame(); + void setSource(const QVariant &var, int index); + void disconnectPropertySignals(); + void connectPropertySignals(); + void reset(); + void updateProperties(); + void lookThroughShaderCode(const QByteArray &code); + virtual void componentComplete(); + QSGShaderEffectNode *buildCustomNode(); + +private: + void buildData(); + + bool m_pleaseReset; + bool m_dirtyData; + QSGShaderEffectProgram m_source; + struct SourceData + { + QSignalMapper *mapper; + QPointer<QSGItem> item; + QByteArray name; + }; + QVector<SourceData> m_sources; + QSGShaderEffectMaterial m_material; + QSGShaderEffectNode* m_node; + qreal m_lastTime; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif //HEADER_GUARD diff --git a/src/declarative/particles/qsgellipseextruder.cpp b/src/declarative/particles/qsgellipseextruder.cpp new file mode 100644 index 0000000000..76925895b9 --- /dev/null +++ b/src/declarative/particles/qsgellipseextruder.cpp @@ -0,0 +1,64 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Declarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qsgellipseextruder_p.h" +#include <cmath> +QT_BEGIN_NAMESPACE +QSGEllipseExtruder::QSGEllipseExtruder(QObject *parent) : + QSGParticleExtruder(parent) + , m_fill(true) +{ +} + +QPointF QSGEllipseExtruder::extrude(const QRectF & r) +{ + qreal theta = ((qreal)rand()/RAND_MAX) * 6.2831853071795862; + qreal mag = m_fill ? ((qreal)rand()/RAND_MAX) : 1; + return QPointF(r.x() + r.width()/2 + mag * (r.width()/2) * cos(theta), + r.y() + r.height()/2 + mag * (r.height()/2) * sin(theta)); +} + +bool QSGEllipseExtruder::contains(const QRectF &bounds, const QPointF &point) +{ + return bounds.contains(point);//TODO: Ellipse +} + +QT_END_NAMESPACE diff --git a/src/declarative/particles/qsgellipseextruder_p.h b/src/declarative/particles/qsgellipseextruder_p.h new file mode 100644 index 0000000000..9f770a7ee6 --- /dev/null +++ b/src/declarative/particles/qsgellipseextruder_p.h @@ -0,0 +1,86 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Declarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef ELLIPSEEXTRUDER_H +#define ELLIPSEEXTRUDER_H +#include "qsgparticleextruder_p.h" + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + + +class QSGEllipseExtruder : public QSGParticleExtruder +{ + Q_OBJECT + Q_PROPERTY(bool fill READ fill WRITE setFill NOTIFY fillChanged)//###Use base class? If it's still box +public: + explicit QSGEllipseExtruder(QObject *parent = 0); + virtual QPointF extrude(const QRectF &); + virtual bool contains(const QRectF &bounds, const QPointF &point); + + bool fill() const + { + return m_fill; + } + +signals: + + void fillChanged(bool arg); + +public slots: + + void setFill(bool arg) + { + if (m_fill != arg) { + m_fill = arg; + emit fillChanged(arg); + } + } +private: + bool m_fill; +}; + +QT_END_NAMESPACE +QT_END_HEADER +#endif // ELLIPSEEXTRUDER_H diff --git a/src/declarative/particles/qsgemitter.cpp b/src/declarative/particles/qsgemitter.cpp new file mode 100644 index 0000000000..081dd8dec5 --- /dev/null +++ b/src/declarative/particles/qsgemitter.cpp @@ -0,0 +1,200 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Declarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qsgemitter_p.h" +#include "qsgparticlesystem_p.h" +#include "qsgparticlepainter_p.h"//TODO: What was this for again? +QT_BEGIN_NAMESPACE + +QSGBasicEmitter::QSGBasicEmitter(QSGItem* parent) + : QSGParticleEmitter(parent) + , m_speed_from_movement(0) + , m_particle_count(0) + , m_reset_last(true) + , m_last_timestamp(0) + , m_last_emission(0) +{ +// setFlag(ItemHasContents); +} + +void QSGBasicEmitter::setSpeedFromMovement(qreal t) +{ + if (t == m_speed_from_movement) + return; + m_speed_from_movement = t; + emit speedFromMovementChanged(); +} + +void QSGBasicEmitter::reset() +{ + m_reset_last = true; +} + +void QSGBasicEmitter::emitWindow(int timeStamp) +{ + if (m_system == 0) + return; + if((!m_emitting || !m_particlesPerSecond)&& !m_burstLeft && m_burstQueue.isEmpty()){ + m_reset_last = true; + return; + } + + if (m_reset_last) { + m_last_emitter = m_last_last_emitter = QPointF(x(), y()); + m_last_timestamp = timeStamp/1000.; + m_last_emission = m_last_timestamp; + m_reset_last = false; + } + + if(m_burstLeft){ + m_burstLeft -= timeStamp - m_last_timestamp * 1000.; + if(m_burstLeft < 0){ + if(!m_emitting) + timeStamp += m_burstLeft; + m_burstLeft = 0; + } + } + + qreal time = timeStamp / 1000.; + + qreal particleRatio = 1. / m_particlesPerSecond; + qreal pt = m_last_emission; + + qreal opt = pt; // original particle time + qreal dt = time - m_last_timestamp; // timestamp delta... + if(!dt) + dt = 0.000001; + + // emitter difference since last... + qreal dex = (x() - m_last_emitter.x()); + qreal dey = (y() - m_last_emitter.y()); + + qreal ax = (m_last_last_emitter.x() + m_last_emitter.x()) / 2; + qreal bx = m_last_emitter.x(); + qreal cx = (x() + m_last_emitter.x()) / 2; + qreal ay = (m_last_last_emitter.y() + m_last_emitter.y()) / 2; + qreal by = m_last_emitter.y(); + qreal cy = (y() + m_last_emitter.y()) / 2; + + qreal sizeAtEnd = m_particleEndSize >= 0 ? m_particleEndSize : m_particleSize; + qreal emitter_x_offset = m_last_emitter.x() - x(); + qreal emitter_y_offset = m_last_emitter.y() - y(); + if(!m_burstQueue.isEmpty() && !m_burstLeft && !m_emitting)//'outside time' emissions only + pt = time; + while (pt < time || !m_burstQueue.isEmpty()) { + //int pos = m_last_particle % m_particle_count; + QSGParticleData* datum = m_system->newDatum(m_system->m_groupIds[m_particle]); + if(datum){//actually emit(otherwise we've been asked to skip this one) + datum->e = this;//###useful? + ParticleVertex &p = datum->pv; + qreal t = 1 - (pt - opt) / dt; + qreal vx = + - 2 * ax * (1 - t) + + 2 * bx * (1 - 2 * t) + + 2 * cx * t; + qreal vy = + - 2 * ay * (1 - t) + + 2 * by * (1 - 2 * t) + + 2 * cy * t; + + + // Particle timestamp + p.t = pt; + p.lifeSpan = //TODO:Promote to base class? + (m_particleDuration + + ((rand() % ((m_particleDurationVariation*2) + 1)) - m_particleDurationVariation)) + / 1000.0; + + // Particle position + QRectF boundsRect; + if(!m_burstQueue.isEmpty()){ + boundsRect = QRectF(m_burstQueue.first().second.x() - x(), m_burstQueue.first().second.y() - y(), + width(), height()); + } else { + boundsRect = QRectF(emitter_x_offset + dex * (pt - opt) / dt, emitter_y_offset + dey * (pt - opt) / dt + , width(), height()); + } + QPointF newPos = effectiveExtruder()->extrude(boundsRect); + p.x = newPos.x(); + p.y = newPos.y(); + + // Particle speed + const QPointF &speed = m_speed->sample(newPos); + p.sx = speed.x() + + m_speed_from_movement * vx; + p.sy = speed.y() + + m_speed_from_movement * vy; + + // Particle acceleration + const QPointF &accel = m_acceleration->sample(newPos); + p.ax = accel.x(); + p.ay = accel.y(); + + // Particle size + float sizeVariation = -m_particleSizeVariation + + rand() / float(RAND_MAX) * m_particleSizeVariation * 2; + + float size = qMax((qreal)0.0 , m_particleSize + sizeVariation); + float endSize = qMax((qreal)0.0 , sizeAtEnd + sizeVariation); + + p.size = size;// * float(m_emitting); + p.endSize = endSize;// * float(m_emitting); + + m_system->emitParticle(datum); + } + if(m_burstQueue.isEmpty()){ + pt += particleRatio; + }else{ + m_burstQueue.first().first--; + if(m_burstQueue.first().first <= 0) + m_burstQueue.pop_front(); + } + } + m_last_emission = pt; + + m_last_last_last_emitter = m_last_last_emitter; + m_last_last_emitter = m_last_emitter; + m_last_emitter = QPointF(x(), y()); + m_last_timestamp = time; +} + + +QT_END_NAMESPACE diff --git a/src/declarative/particles/qsgemitter_p.h b/src/declarative/particles/qsgemitter_p.h new file mode 100644 index 0000000000..3988c71cdf --- /dev/null +++ b/src/declarative/particles/qsgemitter_p.h @@ -0,0 +1,104 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Declarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef TRAILSEMITTER_H +#define TRAILSEMITTER_H + +#include <QtCore> +#include <QtGui> + +#include "qsgparticleemitter_p.h" + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + + +class QSGGeometryNode; + +class QSGBasicEmitter : public QSGParticleEmitter +{ + Q_OBJECT + + Q_PROPERTY(qreal speedFromMovement READ speedFromMovement WRITE setSpeedFromMovement NOTIFY speedFromMovementChanged) + +public: + explicit QSGBasicEmitter(QSGItem* parent=0); + virtual ~QSGBasicEmitter(){} + virtual void emitWindow(int timeStamp); + + + qreal speedFromMovement() const { return m_speed_from_movement; } + void setSpeedFromMovement(qreal s); + + qreal renderOpacity() const { return m_render_opacity; } + +signals: + + void speedFromMovementChanged(); + +public slots: +public: + virtual void reset(); +protected: + +private: + + qreal m_speed_from_movement; + + // derived values... + int m_particle_count; + bool m_reset_last; + qreal m_last_timestamp; + qreal m_last_emission; + + QPointF m_last_emitter; + QPointF m_last_last_emitter; + QPointF m_last_last_last_emitter; + + qreal m_render_opacity; +}; + +QT_END_NAMESPACE +QT_END_HEADER +#endif // TRAILSEMITTER_H diff --git a/src/declarative/particles/qsgfollowemitter.cpp b/src/declarative/particles/qsgfollowemitter.cpp new file mode 100644 index 0000000000..442cff9ec8 --- /dev/null +++ b/src/declarative/particles/qsgfollowemitter.cpp @@ -0,0 +1,195 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Declarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qsgfollowemitter_p.h" +#include "qsgparticlepainter_p.h"//TODO: What was this for again? +#include <cmath> +QT_BEGIN_NAMESPACE + +QSGFollowEmitter::QSGFollowEmitter(QSGItem *parent) : + QSGParticleEmitter(parent) + , m_particlesPerParticlePerSecond(0) + , m_lastTimeStamp(0) + , m_emitterXVariation(0) + , m_emitterYVariation(0) + , m_followCount(0) + , m_emissionExtruder(0) + , m_defaultEmissionExtruder(new QSGParticleExtruder(this)) +{ + connect(this, SIGNAL(followChanged(QString)), + this, SLOT(recalcParticlesPerSecond())); + connect(this, SIGNAL(particleDurationChanged(int)), + this, SLOT(recalcParticlesPerSecond())); + connect(this, SIGNAL(particlesPerParticlePerSecondChanged(int)), + this, SLOT(recalcParticlesPerSecond())); +} + +void QSGFollowEmitter::recalcParticlesPerSecond(){ + if(!m_system) + return; + m_followCount = m_system->m_groupData[m_system->m_groupIds[m_follow]]->size; + if(!m_followCount){ + setParticlesPerSecond(1000);//XXX: Fix this horrendous hack, needed so they aren't turned off from start (causes crashes - test that when gone you don't crash with 0 PPPS) + }else{ + setParticlesPerSecond(m_particlesPerParticlePerSecond * m_followCount); + m_lastEmission.resize(m_followCount); + m_lastEmission.fill(0); + } +} + +void QSGFollowEmitter::reset() +{ + m_followCount = 0; +} + +void QSGFollowEmitter::emitWindow(int timeStamp) +{ + if (m_system == 0) + return; + if(!m_emitting && !m_burstLeft && m_burstQueue.isEmpty()) + return; + if(m_followCount != m_system->m_groupData[m_system->m_groupIds[m_follow]]->size){ + qreal oldPPS = m_particlesPerSecond; + recalcParticlesPerSecond(); + if(m_particlesPerSecond != oldPPS) + return;//system may need to update + } + + if(m_burstLeft){ + m_burstLeft -= timeStamp - m_lastTimeStamp * 1000.; + if(m_burstLeft < 0){ + timeStamp += m_burstLeft; + m_burstLeft = 0; + } + } + + qreal time = timeStamp / 1000.; + qreal particleRatio = 1. / m_particlesPerParticlePerSecond; + qreal pt; + + //Have to map it into this system, because particlesystem automaps it back + QPointF offset = m_system->mapFromItem(this, QPointF(0, 0)); + qreal sizeAtEnd = m_particleEndSize >= 0 ? m_particleEndSize : m_particleSize; + + int gId = m_system->m_groupIds[m_follow]; + int gId2 = m_system->m_groupIds[m_particle]; + for(int i=0; i<m_system->m_groupData[gId]->size; i++){ + pt = m_lastEmission[i]; + QSGParticleData* d = m_system->m_data[i + m_system->m_groupData[gId]->start]; + if(!d || !d->stillAlive()) + continue; + if(pt < d->pv.t) + pt = d->pv.t; + + if(!effectiveExtruder()->contains(QRectF(offset.x(), offset.y(), width(), height()),QPointF(d->curX(), d->curY()))){ + m_lastEmission[i] = time;//jump over this time period without emitting, because it's outside + continue; + } + while(pt < time || !m_burstQueue.isEmpty()){ + QSGParticleData* datum = m_system->newDatum(gId2); + if(datum){//else, skip this emission + datum->e = this;//###useful? + ParticleVertex &p = datum->pv; + + // Particle timestamp + p.t = pt; + p.lifeSpan = + (m_particleDuration + + ((rand() % ((m_particleDurationVariation*2) + 1)) - m_particleDurationVariation)) + / 1000.0; + + // Particle position + // Note that burst location doesn't get used for follow emitter + qreal followT = pt - d->pv.t; + qreal followT2 = followT * followT * 0.5; + qreal sizeOffset = d->pv.size/2;//TODO: Current size? As an option + //TODO: Set variations + //Subtract offset, because PS expects this in emitter coordinates + QRectF boundsRect(d->pv.x - offset.x() + d->pv.sx * followT + d->pv.ax * followT2 - m_emitterXVariation/2, + d->pv.y - offset.y() + d->pv.sy * followT + d->pv.ay * followT2 - m_emitterYVariation/2, + m_emitterXVariation, + m_emitterYVariation); + // QRectF boundsRect(d->pv.x + d->pv.sx * followT + d->pv.ax * followT2 + offset.x() - sizeOffset, + // d->pv.y + d->pv.sy * followT + d->pv.ay * followT2 + offset.y() - sizeOffset, + // sizeOffset*2, + // sizeOffset*2); + + QSGParticleExtruder* effectiveEmissionExtruder = m_emissionExtruder ? m_emissionExtruder : m_defaultEmissionExtruder; + const QPointF &newPos = effectiveEmissionExtruder->extrude(boundsRect); + p.x = newPos.x(); + p.y = newPos.y(); + + // Particle speed + const QPointF &speed = m_speed->sample(newPos); + p.sx = speed.x(); + p.sy = speed.y(); + + // Particle acceleration + const QPointF &accel = m_acceleration->sample(newPos); + p.ax = accel.x(); + p.ay = accel.y(); + + // Particle size + float sizeVariation = -m_particleSizeVariation + + rand() / float(RAND_MAX) * m_particleSizeVariation * 2; + + float size = qMax((qreal)0.0, m_particleSize + sizeVariation); + float endSize = qMax((qreal)0.0, sizeAtEnd + sizeVariation); + + p.size = size * float(m_emitting); + p.endSize = endSize * float(m_emitting); + + m_system->emitParticle(datum); + } + if(!m_burstQueue.isEmpty()){ + m_burstQueue.first().first--; + if(m_burstQueue.first().first <= 0) + m_burstQueue.pop_front(); + }else{ + pt += particleRatio; + } + } + m_lastEmission[i] = pt; + } + + m_lastTimeStamp = time; +} +QT_END_NAMESPACE diff --git a/src/declarative/particles/qsgfollowemitter_p.h b/src/declarative/particles/qsgfollowemitter_p.h new file mode 100644 index 0000000000..314bd4e7c1 --- /dev/null +++ b/src/declarative/particles/qsgfollowemitter_p.h @@ -0,0 +1,168 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Declarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef FOLLOWEMITTER_H +#define FOLLOWEMITTER_H +#include "qsgparticleemitter_p.h" +#include "qsgparticleaffector_p.h" + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + + +class QSGFollowEmitter : public QSGParticleEmitter +{ + Q_OBJECT + Q_PROPERTY(QString follow READ follow WRITE setFollow NOTIFY followChanged) + //### Remove, and just document that particles per second is per particle? But has count issues + Q_PROPERTY(int particlesPerParticlePerSecond READ particlesPerParticlePerSecond WRITE setParticlesPerParticlePerSecond NOTIFY particlesPerParticlePerSecondChanged) + + //TODO: Document that FollowEmitter's box is where it follows. It emits in a rect centered on the followed particle + //TODO: A set of properties that can involve the particle size of the followed + Q_PROPERTY(QSGParticleExtruder* emissionShape READ emissonShape WRITE setEmissionShape NOTIFY emissionShapeChanged) + Q_PROPERTY(qreal emissionHeight READ emitterYVariation WRITE setEmitterYVariation NOTIFY emitterYVariationChanged) + Q_PROPERTY(qreal emissionWidth READ emitterXVariation WRITE setEmitterXVariation NOTIFY emitterXVariationChanged) + +public: + explicit QSGFollowEmitter(QSGItem *parent = 0); + virtual void emitWindow(int timeStamp); + virtual void reset(); + + int particlesPerParticlePerSecond() const + { + return m_particlesPerParticlePerSecond; + } + + qreal emitterXVariation() const + { + return m_emitterXVariation; + } + + qreal emitterYVariation() const + { + return m_emitterYVariation; + } + + QString follow() const + { + return m_follow; + } + + QSGParticleExtruder* emissonShape() const + { + return m_emissionExtruder; + } + +signals: + + void particlesPerParticlePerSecondChanged(int arg); + + void emitterXVariationChanged(qreal arg); + + void emitterYVariationChanged(qreal arg); + + void followChanged(QString arg); + + void emissionShapeChanged(QSGParticleExtruder* arg); + +public slots: + + void setParticlesPerParticlePerSecond(int arg) + { + if (m_particlesPerParticlePerSecond != arg) { + m_particlesPerParticlePerSecond = arg; + emit particlesPerParticlePerSecondChanged(arg); + } + } + void setEmitterXVariation(qreal arg) + { + if (m_emitterXVariation != arg) { + m_emitterXVariation = arg; + emit emitterXVariationChanged(arg); + } + } + + void setEmitterYVariation(qreal arg) + { + if (m_emitterYVariation != arg) { + m_emitterYVariation = arg; + emit emitterYVariationChanged(arg); + } + } + + void setFollow(QString arg) + { + if (m_follow != arg) { + m_follow = arg; + emit followChanged(arg); + } + } + + void setEmissionShape(QSGParticleExtruder* arg) + { + if (m_emissionExtruder != arg) { + m_emissionExtruder = arg; + emit emissionShapeChanged(arg); + } + } + +private slots: + void recalcParticlesPerSecond(); + +private: + QSet<QSGParticleData*> m_pending; + QVector<qreal> m_lastEmission; + int m_particlesPerParticlePerSecond; + qreal m_lastTimeStamp; + qreal m_emitterXVariation; + qreal m_emitterYVariation; + QString m_follow; + int m_followCount; + QSGParticleExtruder* m_emissionExtruder; + QSGParticleExtruder* m_defaultEmissionExtruder; +}; + +QT_END_NAMESPACE +QT_END_HEADER +#endif // FOLLOWEMITTER_H diff --git a/src/declarative/particles/qsgfriction.cpp b/src/declarative/particles/qsgfriction.cpp new file mode 100644 index 0000000000..828d20556d --- /dev/null +++ b/src/declarative/particles/qsgfriction.cpp @@ -0,0 +1,59 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Declarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qsgfriction_p.h" +QT_BEGIN_NAMESPACE +QSGFrictionAffector::QSGFrictionAffector(QSGItem *parent) : + QSGParticleAffector(parent), m_factor(0.0) +{ +} + +bool QSGFrictionAffector::affectParticle(QSGParticleData *d, qreal dt) +{ + if(!m_factor) + return false; + qreal curSX = d->curSX(); + qreal curSY = d->curSY(); + d->setInstantaneousSX(curSX + (curSX * m_factor * -1 * dt)); + d->setInstantaneousSY(curSY + (curSY * m_factor * -1 * dt)); + return true; +} +QT_END_NAMESPACE diff --git a/src/declarative/particles/qsgfriction_p.h b/src/declarative/particles/qsgfriction_p.h new file mode 100644 index 0000000000..6b5a86f3f2 --- /dev/null +++ b/src/declarative/particles/qsgfriction_p.h @@ -0,0 +1,86 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Declarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef FRICTIONAFFECTOR_H +#define FRICTIONAFFECTOR_H +#include "qsgparticleaffector_p.h" + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + + +class QSGFrictionAffector : public QSGParticleAffector +{ + Q_OBJECT + Q_PROPERTY(qreal factor READ factor WRITE setFactor NOTIFY factorChanged) +public: + explicit QSGFrictionAffector(QSGItem *parent = 0); + + qreal factor() const + { + return m_factor; + } +protected: + virtual bool affectParticle(QSGParticleData *d, qreal dt); +signals: + + void factorChanged(qreal arg); + +public slots: + +void setFactor(qreal arg) +{ + if (m_factor != arg) { + m_factor = arg; + emit factorChanged(arg); + } +} + +private: +qreal m_factor; +}; + +QT_END_NAMESPACE +QT_END_HEADER +#endif // FRICTIONAFFECTOR_H diff --git a/src/declarative/particles/qsggravity.cpp b/src/declarative/particles/qsggravity.cpp new file mode 100644 index 0000000000..de735da5ad --- /dev/null +++ b/src/declarative/particles/qsggravity.cpp @@ -0,0 +1,77 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Declarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qsggravity_p.h" +#include <cmath> +QT_BEGIN_NAMESPACE +const qreal CONV = 0.017453292520444443; +QSGGravityAffector::QSGGravityAffector(QSGItem *parent) : + QSGParticleAffector(parent), m_acceleration(-10), m_angle(90), m_xAcc(0), m_yAcc(0) +{ + connect(this, SIGNAL(accelerationChanged(qreal)), + this, SLOT(recalc())); + connect(this, SIGNAL(angleChanged(qreal)), + this, SLOT(recalc())); + recalc(); +} + +void QSGGravityAffector::recalc() +{ + qreal theta = m_angle * CONV; + m_xAcc = m_acceleration * cos(theta); + m_yAcc = m_acceleration * sin(theta); +} + +bool QSGGravityAffector::affectParticle(QSGParticleData *d, qreal dt) +{ + Q_UNUSED(dt); + bool changed = false; + if(d->pv.ax != m_xAcc){ + d->setInstantaneousAX(m_xAcc); + changed = true; + } + if(d->pv.ay != m_yAcc){ + d->setInstantaneousAY(m_yAcc); + changed = true; + } + return changed; +} +QT_END_NAMESPACE diff --git a/src/declarative/particles/qsggravity_p.h b/src/declarative/particles/qsggravity_p.h new file mode 100644 index 0000000000..57b2511911 --- /dev/null +++ b/src/declarative/particles/qsggravity_p.h @@ -0,0 +1,106 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Declarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef GRAVITYAFFECTOR_H +#define GRAVITYAFFECTOR_H +#include "qsgparticleaffector_p.h" + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + + +class QSGGravityAffector : public QSGParticleAffector +{ + Q_OBJECT + Q_PROPERTY(qreal acceleration READ acceleration WRITE setAcceleration NOTIFY accelerationChanged) + Q_PROPERTY(qreal angle READ angle WRITE setAngle NOTIFY angleChanged) +public: + explicit QSGGravityAffector(QSGItem *parent = 0); + qreal acceleration() const + { + return m_acceleration; + } + + qreal angle() const + { + return m_angle; + } +protected: + virtual bool affectParticle(QSGParticleData *d, qreal dt); +signals: + + void accelerationChanged(qreal arg); + + void angleChanged(qreal arg); + +public slots: +void setAcceleration(qreal arg) +{ + if (m_acceleration != arg) { + m_acceleration = arg; + emit accelerationChanged(arg); + } +} + +void setAngle(qreal arg) +{ + if (m_angle != arg) { + m_angle = arg; + emit angleChanged(arg); + } +} + +private slots: + void recalc(); +private: + qreal m_acceleration; + qreal m_angle; + + qreal m_xAcc; + qreal m_yAcc; +}; + +QT_END_NAMESPACE +QT_END_HEADER +#endif // GRAVITYAFFECTOR_H diff --git a/src/declarative/particles/qsgimageparticle.cpp b/src/declarative/particles/qsgimageparticle.cpp new file mode 100644 index 0000000000..c9df5f4dbd --- /dev/null +++ b/src/declarative/particles/qsgimageparticle.cpp @@ -0,0 +1,993 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Declarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <private/qsgcontext_p.h> +#include <private/qsgadaptationlayer_p.h> +#include <qsgnode.h> +#include <qsgtexturematerial.h> +#include <qsgtexture.h> +#include <QFile> +#include "qsgimageparticle_p.h" +#include "qsgparticleemitter_p.h" +#include "qsgsprite_p.h" +#include "qsgspriteengine_p.h" +#include <QGLFunctions> +#include <qsgengine.h> + +QT_BEGIN_NAMESPACE + +const float CONV = 0.017453292519943295; +class UltraMaterial : public QSGMaterial +{ +public: + UltraMaterial(bool withSprites=false) + : timestamp(0) + , framecount(1) + , animcount(1) + , usesSprites(withSprites) + { + setFlag(Blending, true); + } + + ~UltraMaterial() + { + delete texture; + delete colortable; + delete sizetable; + delete opacitytable; + } + + virtual QSGMaterialType *type() const { static QSGMaterialType type; return &type; } + virtual QSGMaterialShader *createShader() const; + virtual int compare(const QSGMaterial *other) const + { + return this - static_cast<const UltraMaterial *>(other); + } + + QSGTexture *texture; + QSGTexture *colortable; + QSGTexture *sizetable; + QSGTexture *opacitytable; + + qreal timestamp; + int framecount; + int animcount; + bool usesSprites; +}; +class UltraMaterialData : public QSGMaterialShader +{ +public: + UltraMaterialData(const char *vertexFile = 0, const char *fragmentFile = 0) + { + QFile vf(vertexFile ? vertexFile : ":defaultshaders/ultravertex.shader"); + vf.open(QFile::ReadOnly); + m_vertex_code = vf.readAll(); + + QFile ff(fragmentFile ? fragmentFile : ":defaultshaders/ultrafragment.shader"); + ff.open(QFile::ReadOnly); + m_fragment_code = ff.readAll(); + + Q_ASSERT(!m_vertex_code.isNull()); + Q_ASSERT(!m_fragment_code.isNull()); + } + + void deactivate() { + QSGMaterialShader::deactivate(); + + for (int i=0; i<8; ++i) { + program()->setAttributeArray(i, GL_FLOAT, chunkOfBytes, 1, 0); + } + } + + virtual void updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *) + { + UltraMaterial *m = static_cast<UltraMaterial *>(newEffect); + state.context()->functions()->glActiveTexture(GL_TEXTURE1); + m->colortable->bind(); + program()->setUniformValue(m_colortable_id, 1); + + state.context()->functions()->glActiveTexture(GL_TEXTURE2); + m->sizetable->bind(); + program()->setUniformValue(m_sizetable_id, 2); + + state.context()->functions()->glActiveTexture(GL_TEXTURE3); + m->opacitytable->bind(); + program()->setUniformValue(m_opacitytable_id, 3); + + state.context()->functions()->glActiveTexture(GL_TEXTURE0);//Investigate why this screws up Text{} if placed before 1 + m->texture->bind(); + + program()->setUniformValue(m_opacity_id, state.opacity()); + program()->setUniformValue(m_timestamp_id, (float) m->timestamp); + program()->setUniformValue(m_framecount_id, (float) m->framecount); + program()->setUniformValue(m_animcount_id, (float) m->animcount); + + if (state.isMatrixDirty()) + program()->setUniformValue(m_matrix_id, state.combinedMatrix()); + } + + virtual void initialize() { + m_colortable_id = program()->uniformLocation("colortable"); + m_sizetable_id = program()->uniformLocation("sizetable"); + m_opacitytable_id = program()->uniformLocation("opacitytable"); + m_matrix_id = program()->uniformLocation("matrix"); + m_opacity_id = program()->uniformLocation("opacity"); + m_timestamp_id = program()->uniformLocation("timestamp"); + m_framecount_id = program()->uniformLocation("framecount"); + m_animcount_id = program()->uniformLocation("animcount"); + } + + virtual const char *vertexShader() const { return m_vertex_code.constData(); } + virtual const char *fragmentShader() const { return m_fragment_code.constData(); } + + virtual char const *const *attributeNames() const { + static const char *attr[] = { + "vPos", + "vTex", + "vData", + "vVec", + "vColor", + "vDeformVec", + "vRotation", + "vAnimData", + 0 + }; + return attr; + } + + virtual bool isColorTable() const { return false; } + + int m_matrix_id; + int m_opacity_id; + int m_timestamp_id; + int m_colortable_id; + int m_sizetable_id; + int m_opacitytable_id; + int m_framecount_id; + int m_animcount_id; + + QByteArray m_vertex_code; + QByteArray m_fragment_code; + + static float chunkOfBytes[1024]; +}; +float UltraMaterialData::chunkOfBytes[1024]; + +QSGMaterialShader *UltraMaterial::createShader() const +{ + if(usesSprites)//TODO: Perhaps just swap the shaders, and don't mind the extra vector? + return new UltraMaterialData; + else + return new UltraMaterialData; +} + + +class SimpleMaterial : public UltraMaterial +{ + virtual QSGMaterialShader *createShader() const; + virtual QSGMaterialType *type() const { static QSGMaterialType type; return &type; } +}; + +class SimpleMaterialData : public QSGMaterialShader +{ +public: + SimpleMaterialData(const char *vertexFile = 0, const char *fragmentFile = 0) + { + QFile vf(vertexFile ? vertexFile : ":defaultshaders/simplevertex.shader"); + vf.open(QFile::ReadOnly); + m_vertex_code = vf.readAll(); + + QFile ff(fragmentFile ? fragmentFile : ":defaultshaders/simplefragment.shader"); + ff.open(QFile::ReadOnly); + m_fragment_code = ff.readAll(); + + Q_ASSERT(!m_vertex_code.isNull()); + Q_ASSERT(!m_fragment_code.isNull()); + } + + void deactivate() { + QSGMaterialShader::deactivate(); + + for (int i=0; i<8; ++i) { + program()->setAttributeArray(i, GL_FLOAT, chunkOfBytes, 1, 0); + } + } + + virtual void updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *) + { + UltraMaterial *m = static_cast<UltraMaterial *>(newEffect); + state.context()->functions()->glActiveTexture(GL_TEXTURE0); + m->texture->bind(); + + program()->setUniformValue(m_opacity_id, state.opacity()); + program()->setUniformValue(m_timestamp_id, (float) m->timestamp); + + if (state.isMatrixDirty()) + program()->setUniformValue(m_matrix_id, state.combinedMatrix()); + } + + virtual void initialize() { + m_matrix_id = program()->uniformLocation("matrix"); + m_opacity_id = program()->uniformLocation("opacity"); + m_timestamp_id = program()->uniformLocation("timestamp"); + } + + virtual const char *vertexShader() const { return m_vertex_code.constData(); } + virtual const char *fragmentShader() const { return m_fragment_code.constData(); } + + virtual char const *const *attributeNames() const { + static const char *attr[] = { + "vPos", + "vTex", + "vData", + "vVec", + 0 + }; + return attr; + } + + virtual bool isColorTable() const { return false; } + + int m_matrix_id; + int m_opacity_id; + int m_timestamp_id; + + QByteArray m_vertex_code; + QByteArray m_fragment_code; + + static float chunkOfBytes[1024]; +}; +float SimpleMaterialData::chunkOfBytes[1024]; + +QSGMaterialShader *SimpleMaterial::createShader() const { + return new SimpleMaterialData; +} + +QSGImageParticle::QSGImageParticle(QSGItem* parent) + : QSGParticlePainter(parent) + , m_do_reset(false) + , m_color_variation(0.0) + , m_node(0) + , m_material(0) + , m_alphaVariation(0.0) + , m_alpha(1.0) + , m_redVariation(0.0) + , m_greenVariation(0.0) + , m_blueVariation(0.0) + , m_rotation(0) + , m_autoRotation(false) + , m_xVector(0) + , m_yVector(0) + , m_rotationVariation(0) + , m_rotationSpeed(0) + , m_rotationSpeedVariation(0) + , m_spriteEngine(0) + , m_bloat(false) + , perfLevel(Unknown) + , m_lastLevel(Unknown) +{ + setFlag(ItemHasContents); +} + +QDeclarativeListProperty<QSGSprite> QSGImageParticle::sprites() +{ + return QDeclarativeListProperty<QSGSprite>(this, &m_sprites, spriteAppend, spriteCount, spriteAt, spriteClear); +} + +void QSGImageParticle::setImage(const QUrl &image) +{ + if (image == m_image_name) + return; + m_image_name = image; + emit imageChanged(); + reset(); +} + + +void QSGImageParticle::setColortable(const QUrl &table) +{ + if (table == m_colortable_name) + return; + m_colortable_name = table; + emit colortableChanged(); + reset(); +} + +void QSGImageParticle::setSizetable(const QUrl &table) +{ + if (table == m_sizetable_name) + return; + m_sizetable_name = table; + emit sizetableChanged(); + reset(); +} + +void QSGImageParticle::setOpacitytable(const QUrl &table) +{ + if (table == m_opacitytable_name) + return; + m_opacitytable_name = table; + emit opacitytableChanged(); + reset(); +} + +void QSGImageParticle::setColor(const QColor &color) +{ + if (color == m_color) + return; + m_color = color; + emit colorChanged(); + if(perfLevel < Coloured) + reset(); +} + +void QSGImageParticle::setColorVariation(qreal var) +{ + if (var == m_color_variation) + return; + m_color_variation = var; + emit colorVariationChanged(); + if(perfLevel < Coloured) + reset(); +} + +void QSGImageParticle::setAlphaVariation(qreal arg) +{ + if (m_alphaVariation != arg) { + m_alphaVariation = arg; + emit alphaVariationChanged(arg); + } + if(perfLevel < Coloured) + reset(); +} + +void QSGImageParticle::setAlpha(qreal arg) +{ + if (m_alpha != arg) { + m_alpha = arg; + emit alphaChanged(arg); + } + if(perfLevel < Coloured) + reset(); +} + +void QSGImageParticle::setRedVariation(qreal arg) +{ + if (m_redVariation != arg) { + m_redVariation = arg; + emit redVariationChanged(arg); + } + if(perfLevel < Coloured) + reset(); +} + +void QSGImageParticle::setGreenVariation(qreal arg) +{ + if (m_greenVariation != arg) { + m_greenVariation = arg; + emit greenVariationChanged(arg); + } + if(perfLevel < Coloured) + reset(); +} + +void QSGImageParticle::setBlueVariation(qreal arg) +{ + if (m_blueVariation != arg) { + m_blueVariation = arg; + emit blueVariationChanged(arg); + } + if(perfLevel < Coloured) + reset(); +} + +void QSGImageParticle::setRotation(qreal arg) +{ + if (m_rotation != arg) { + m_rotation = arg; + emit rotationChanged(arg); + } + if(perfLevel < Deformable) + reset(); +} + +void QSGImageParticle::setRotationVariation(qreal arg) +{ + if (m_rotationVariation != arg) { + m_rotationVariation = arg; + emit rotationVariationChanged(arg); + } + if(perfLevel < Deformable) + reset(); +} + +void QSGImageParticle::setRotationSpeed(qreal arg) +{ + if (m_rotationSpeed != arg) { + m_rotationSpeed = arg; + emit rotationSpeedChanged(arg); + } + if(perfLevel < Deformable) + reset(); +} + +void QSGImageParticle::setRotationSpeedVariation(qreal arg) +{ + if (m_rotationSpeedVariation != arg) { + m_rotationSpeedVariation = arg; + emit rotationSpeedVariationChanged(arg); + } + if(perfLevel < Deformable) + reset(); +} + +void QSGImageParticle::setAutoRotation(bool arg) +{ + if (m_autoRotation != arg) { + m_autoRotation = arg; + emit autoRotationChanged(arg); + } + if(perfLevel < Deformable) + reset(); +} + +void QSGImageParticle::setXVector(QSGStochasticDirection* arg) +{ + if (m_xVector != arg) { + m_xVector = arg; + emit xVectorChanged(arg); + } + if(perfLevel < Deformable) + reset(); +} + +void QSGImageParticle::setYVector(QSGStochasticDirection* arg) +{ + if (m_yVector != arg) { + m_yVector = arg; + emit yVectorChanged(arg); + } + if(perfLevel < Deformable) + reset(); +} + +void QSGImageParticle::setBloat(bool arg) +{ + if (m_bloat != arg) { + m_bloat = arg; + emit bloatChanged(arg); + } + if(perfLevel < 9999) + reset(); +} +void QSGImageParticle::setCount(int c) +{ + QSGParticlePainter::setCount(c); + m_pleaseReset = true; +} + +void QSGImageParticle::reset() +{ + QSGParticlePainter::reset(); + m_pleaseReset = true; +} + +void QSGImageParticle::createEngine() +{ + if(m_spriteEngine) + delete m_spriteEngine; + if(m_sprites.count()) + m_spriteEngine = new QSGSpriteEngine(m_sprites, this); + else + m_spriteEngine = 0; + reset(); +} + +static QSGGeometry::Attribute SimpleParticle_Attributes[] = { + { 0, 2, GL_FLOAT }, // Position + { 1, 2, GL_FLOAT }, // TexCoord + { 2, 4, GL_FLOAT }, // Data + { 3, 4, GL_FLOAT } // Vectors +}; + +static QSGGeometry::AttributeSet SimpleParticle_AttributeSet = +{ + 4, // Attribute Count + (2 + 2 + 4 + 4 ) * sizeof(float), + SimpleParticle_Attributes +}; + +static QSGGeometry::Attribute UltraParticle_Attributes[] = { + { 0, 2, GL_FLOAT }, // Position + { 1, 2, GL_FLOAT }, // TexCoord + { 2, 4, GL_FLOAT }, // Data + { 3, 4, GL_FLOAT }, // Vectors + { 4, 4, GL_UNSIGNED_BYTE }, // Colors + { 5, 4, GL_FLOAT }, // DeformationVectors + { 6, 3, GL_FLOAT }, // Rotation + { 7, 4, GL_FLOAT } // Anim Data +}; + +static QSGGeometry::AttributeSet UltraParticle_AttributeSet = +{ + 8, // Attribute Count + (2 + 2 + 4 + 4 + 4 + 4 + 3) * sizeof(float) + 4 * sizeof(uchar), + UltraParticle_Attributes +}; + +QSGGeometryNode* QSGImageParticle::buildSimpleParticleNode() +{ + perfLevel = Simple;//TODO: Intermediate levels + QImage image = QImage(m_image_name.toLocalFile()); + if (image.isNull()) { + printf("UltraParticle: loading image failed... '%s'\n", qPrintable(m_image_name.toLocalFile())); + return 0; + } + int vCount = m_count * 4; + int iCount = m_count * 6; + qDebug() << "Simple Case"; + + QSGGeometry *g = new QSGGeometry(SimpleParticle_AttributeSet, vCount, iCount); + g->setDrawingMode(GL_TRIANGLES); + + SimpleVertex *vertices = (SimpleVertex *) g->vertexData(); + for (int p=0; p<m_count; ++p) { + for (int i=0; i<4; ++i) { + vertices[i].x = 0; + vertices[i].y = 0; + vertices[i].t = -1; + vertices[i].lifeSpan = 0; + vertices[i].size = 0; + vertices[i].endSize = 0; + vertices[i].sx = 0; + vertices[i].sy = 0; + vertices[i].ax = 0; + vertices[i].ay = 0; + } + + vertices[0].tx = 0; + vertices[0].ty = 0; + + vertices[1].tx = 1; + vertices[1].ty = 0; + + vertices[2].tx = 0; + vertices[2].ty = 1; + + vertices[3].tx = 1; + vertices[3].ty = 1; + + vertices += 4; + } + + quint16 *indices = g->indexDataAsUShort(); + for (int i=0; i<m_count; ++i) { + int o = i * 4; + indices[0] = o; + indices[1] = o + 1; + indices[2] = o + 2; + indices[3] = o + 1; + indices[4] = o + 3; + indices[5] = o + 2; + indices += 6; + } + + if (m_material) { + delete m_material; + m_material = 0; + } + + m_material = new SimpleMaterial(); + m_material->texture = sceneGraphEngine()->createTextureFromImage(image); + m_material->texture->setFiltering(QSGTexture::Linear); + m_material->framecount = 1; + m_node = new QSGGeometryNode(); + m_node->setGeometry(g); + m_node->setMaterial(m_material); + + m_last_particle = 0; + + return m_node; +} + +QSGGeometryNode* QSGImageParticle::buildParticleNode() +{ + if (m_count * 4 > 0xffff) { + printf("UltraParticle: Too many particles... \n");//####Why is this here? + return 0; + } + + if(m_count <= 0) { + printf("UltraParticle: Too few particles... \n"); + return 0; + } + + if(!m_sprites.count() && !m_bloat + && m_colortable_name.isEmpty() + && m_sizetable_name.isEmpty() + && m_opacitytable_name.isEmpty() + && !m_autoRotation + && !m_rotation && !m_rotationVariation + && !m_rotationSpeed && !m_rotationSpeedVariation + && !m_alphaVariation && m_alpha == 1.0 + && !m_redVariation && !m_blueVariation && !m_greenVariation + && !m_color.isValid() + ) + return buildSimpleParticleNode(); + perfLevel = Sprites;//TODO: intermediate levels + if(!m_color.isValid())//But we're in colored level (or higher) + m_color = QColor(Qt::white); + qDebug() << "Complex Case"; + + QImage image; + if(m_sprites.count()){ + if (!m_spriteEngine) { + qWarning() << "UltraParticle: No sprite engine..."; + return 0; + } + image = m_spriteEngine->assembledImage(); + if(image.isNull())//Warning is printed in engine + return 0; + }else{ + image = QImage(m_image_name.toLocalFile()); + if (image.isNull()) { + printf("UltraParticle: loading image failed... '%s'\n", qPrintable(m_image_name.toLocalFile())); + return 0; + } + } + + int vCount = m_count * 4; + int iCount = m_count * 6; + + QSGGeometry *g = new QSGGeometry(UltraParticle_AttributeSet, vCount, iCount); + g->setDrawingMode(GL_TRIANGLES); + + UltraVertex *vertices = (UltraVertex *) g->vertexData(); + SimpleVertex *oldSimple = (SimpleVertex *) m_lastData;//TODO: Other levels + if(m_lastLevel == 1) + qDebug() << "Theta" << m_lastLevel << oldSimple[0].x << oldSimple[0].y << oldSimple[0].t; + for (int p=0; p<m_count; ++p) { + + if (m_lastLevel == 1) {//Transplant/IntermediateVertices? + for (int i=0; i<4; ++i) { + vertices[i].x = oldSimple[i].x; + vertices[i].y = oldSimple[i].y; + vertices[i].t = oldSimple[i].t; + vertices[i].lifeSpan = oldSimple[i].lifeSpan; + vertices[i].size = oldSimple[i].size; + vertices[i].endSize = oldSimple[i].endSize; + vertices[i].sx = oldSimple[i].sx; + vertices[i].sy = oldSimple[i].sy; + vertices[i].ax = oldSimple[i].ax; + vertices[i].ay = oldSimple[i].ay; + vertices[i].xx = 1; + vertices[i].xy = 0; + vertices[i].yx = 0; + vertices[i].yy = 1; + vertices[i].rotation = 0; + vertices[i].rotationSpeed = 0; + vertices[i].autoRotate = 0; + vertices[i].animIdx = 0; + vertices[i].frameDuration = oldSimple[i].lifeSpan; + vertices[i].frameCount = 1; + vertices[i].animT = oldSimple[i].t; + vertices[i].color.r = 255; + vertices[i].color.g = 255; + vertices[i].color.b = 255; + vertices[i].color.a = 255; + } + } else { + for (int i=0; i<4; ++i) { + vertices[i].x = 0; + vertices[i].y = 0; + vertices[i].t = -1; + vertices[i].lifeSpan = 0; + vertices[i].size = 0; + vertices[i].endSize = 0; + vertices[i].sx = 0; + vertices[i].sy = 0; + vertices[i].ax = 0; + vertices[i].ay = 0; + vertices[i].xx = 1; + vertices[i].xy = 0; + vertices[i].yx = 0; + vertices[i].yy = 1; + vertices[i].rotation = 0; + vertices[i].rotationSpeed = 0; + vertices[i].autoRotate = 0; + vertices[i].animIdx = -1; + vertices[i].frameDuration = 1; + vertices[i].frameCount = 0; + vertices[i].animT = -1; + vertices[i].color.r = 0;//TODO:Some things never get used uninitialized. Consider dropping them here? + vertices[i].color.g = 0; + vertices[i].color.b = 0; + vertices[i].color.a = 0; + } + } + + vertices[0].tx = 0; + vertices[0].ty = 0; + + vertices[1].tx = 1; + vertices[1].ty = 0; + + vertices[2].tx = 0; + vertices[2].ty = 1; + + vertices[3].tx = 1; + vertices[3].ty = 1; + + vertices += 4; + oldSimple += 4; + } + + quint16 *indices = g->indexDataAsUShort();//TODO: Speed gains by copying this over if count unchanged? + for (int i=0; i<m_count; ++i) { + int o = i * 4; + indices[0] = o; + indices[1] = o + 1; + indices[2] = o + 2; + indices[3] = o + 1; + indices[4] = o + 3; + indices[5] = o + 2; + indices += 6; + } + + qFree(m_lastData); + if (m_material) { + delete m_material; + m_material = 0; + } + + QImage colortable(m_colortable_name.toLocalFile()); + QImage sizetable(m_sizetable_name.toLocalFile()); + QImage opacitytable(m_opacitytable_name.toLocalFile()); + m_material = new UltraMaterial(); + if(colortable.isNull()) + colortable = QImage(":defaultshaders/identitytable.png"); + if(sizetable.isNull()) + sizetable = QImage(":defaultshaders/identitytable.png"); + if(opacitytable.isNull()) + opacitytable = QImage(":defaultshaders/defaultFadeInOut.png"); + Q_ASSERT(!colortable.isNull()); + Q_ASSERT(!sizetable.isNull()); + Q_ASSERT(!opacitytable.isNull()); + m_material->colortable = sceneGraphEngine()->createTextureFromImage(colortable); + m_material->sizetable = sceneGraphEngine()->createTextureFromImage(sizetable); + m_material->opacitytable = sceneGraphEngine()->createTextureFromImage(opacitytable); + + m_material->texture = sceneGraphEngine()->createTextureFromImage(image); + m_material->texture->setFiltering(QSGTexture::Linear); + + m_material->framecount = 1; + if(m_spriteEngine){ + m_material->framecount = m_spriteEngine->maxFrames(); + m_spriteEngine->setCount(m_count); + } + + m_node = new QSGGeometryNode(); + m_node->setGeometry(g); + m_node->setMaterial(m_material); + + m_last_particle = 0; + + return m_node; +} + +QSGNode *QSGImageParticle::updatePaintNode(QSGNode *, UpdatePaintNodeData *) +{ + if(m_pleaseReset){ + if(m_node){ + if(perfLevel == 1){ + qDebug() << "Beta"; + m_lastData = qMalloc(m_count*sizeof(SimpleVertices));//TODO: Account for count_changed possibility + memcpy(m_lastData, m_node->geometry()->vertexData(), m_count * sizeof(SimpleVertices));//TODO: Multiple levels + } + m_lastLevel = perfLevel; + delete m_node; + } + if(m_material) + delete m_material; + + m_node = 0; + m_material = 0; + m_pleaseReset = false; + } + + if(m_system && m_system->isRunning()) + prepareNextFrame(); + if (m_node){ + update(); + m_node->markDirty(QSGNode::DirtyMaterial); + } + + return m_node; +} + +void QSGImageParticle::prepareNextFrame() +{ + if (m_node == 0){ //TODO: Staggered loading (as emitted) + m_node = buildParticleNode(); + if(m_node == 0) + return; + qDebug() << "Feature level: " << perfLevel; + } + qint64 timeStamp = m_system->systemSync(this); + + qreal time = timeStamp / 1000.; + m_material->timestamp = time; + + //Advance State + if(m_spriteEngine){//perfLevel == Sprites? + m_material->animcount = m_spriteEngine->spriteCount(); + UltraVertices *particles = (UltraVertices *) m_node->geometry()->vertexData(); + m_spriteEngine->updateSprites(timeStamp); + for(int i=0; i<m_count; i++){ + UltraVertices &p = particles[i]; + int curIdx = m_spriteEngine->spriteState(i); + if(curIdx != p.v1.animIdx){ + p.v1.animIdx = p.v2.animIdx = p.v3.animIdx = p.v4.animIdx = curIdx; + p.v1.animT = p.v2.animT = p.v3.animT = p.v4.animT = m_spriteEngine->spriteStart(i)/1000.0; + p.v1.frameCount = p.v2.frameCount = p.v3.frameCount = p.v4.frameCount = m_spriteEngine->spriteFrames(i); + p.v1.frameDuration = p.v2.frameDuration = p.v3.frameDuration = p.v4.frameDuration = m_spriteEngine->spriteDuration(i); + } + } + }else{ + m_material->animcount = 1; + } +} + +template <typename VT> +IntermediateVertices* transplant(IntermediateVertices* iv, VT &v) +{//Deliberate typemangling cast + iv->v1 = (UltraVertex*)&(v.v1); + iv->v2 = (UltraVertex*)&(v.v2); + iv->v3 = (UltraVertex*)&(v.v3); + iv->v4 = (UltraVertex*)&(v.v4); + return iv; +} + +IntermediateVertices* QSGImageParticle::fetchIntermediateVertices(int pos) +{ + //Note that this class ruins typesafety for you. Maybe even thread safety. + //TODO: Something better, possibly with templates or inheritance + static IntermediateVertices iv; + SimpleVertices *sv; + UltraVertices *uv; + switch(perfLevel){ + case Simple: + sv = (SimpleVertices *) m_node->geometry()->vertexData(); + return transplant(&iv, sv[pos]); + case Coloured: + case Deformable: + case Tabled: + case Sprites: + default: + uv = (UltraVertices *) m_node->geometry()->vertexData(); + return transplant(&iv,uv[pos]); + } +} + +void QSGImageParticle::reloadColor(const Color4ub &c, QSGParticleData* d) +{ + UltraVertices *particles = (UltraVertices *) m_node->geometry()->vertexData(); + int pos = particleTypeIndex(d); + UltraVertices &p = particles[pos]; + p.v1.color = p.v2.color = p.v3.color = p.v4.color = c; +} + +void QSGImageParticle::reload(QSGParticleData *d) +{ + if (m_node == 0) + return; + + int pos = particleTypeIndex(d); + IntermediateVertices* p = fetchIntermediateVertices(pos); + + //Perhaps we could be more efficient? + vertexCopy(*p->v1, d->pv); + vertexCopy(*p->v2, d->pv); + vertexCopy(*p->v3, d->pv); + vertexCopy(*p->v4, d->pv); +} + +void QSGImageParticle::load(QSGParticleData *d) +{ + if (m_node == 0) + return; + + int pos = particleTypeIndex(d); + IntermediateVertices* p = fetchIntermediateVertices(pos);//Remember this removes typesafety! + Color4ub color; + qreal redVariation = m_color_variation + m_redVariation; + qreal greenVariation = m_color_variation + m_greenVariation; + qreal blueVariation = m_color_variation + m_blueVariation; + switch(perfLevel){//Fall-through is intended on all of them + case Sprites: + // Initial Sprite State + p->v1->animT = p->v2->animT = p->v3->animT = p->v4->animT = p->v1->t; + p->v1->animIdx = p->v2->animIdx = p->v3->animIdx = p->v4->animIdx = 0; + if(m_spriteEngine){ + m_spriteEngine->startSprite(pos); + p->v1->frameCount = p->v2->frameCount = p->v3->frameCount = p->v4->frameCount = m_spriteEngine->spriteFrames(pos); + p->v1->frameDuration = p->v2->frameDuration = p->v3->frameDuration = p->v4->frameDuration = m_spriteEngine->spriteDuration(pos); + }else{ + p->v1->frameCount = p->v2->frameCount = p->v3->frameCount = p->v4->frameCount = 1; + p->v1->frameDuration = p->v2->frameDuration = p->v3->frameDuration = p->v4->frameDuration = 9999; + } + case Tabled: + case Deformable: + //Initial Rotation + if(m_xVector){ + const QPointF &ret = m_xVector->sample(QPointF(d->pv.x, d->pv.y)); + p->v1->xx = p->v2->xx = p->v3->xx = p->v4->xx = ret.x(); + p->v1->xy = p->v2->xy = p->v3->xy = p->v4->xy = ret.y(); + } + if(m_yVector){ + const QPointF &ret = m_yVector->sample(QPointF(d->pv.x, d->pv.y)); + p->v1->yx = p->v2->yx = p->v3->yx = p->v4->yx = ret.x(); + p->v1->yy = p->v2->yy = p->v3->yy = p->v4->yy = ret.y(); + } + p->v1->rotation = p->v2->rotation = p->v3->rotation = p->v4->rotation = + (m_rotation + (m_rotationVariation - 2*((qreal)rand()/RAND_MAX)*m_rotationVariation) ) * CONV; + p->v1->rotationSpeed = p->v2->rotationSpeed = p->v3->rotationSpeed = p->v4->rotationSpeed = + (m_rotationSpeed + (m_rotationSpeedVariation - 2*((qreal)rand()/RAND_MAX)*m_rotationSpeedVariation) ) * CONV; + p->v1->autoRotate = p->v2->autoRotate = p->v3->autoRotate = p->v4->autoRotate = m_autoRotation?1.0:0.0; + case Coloured: + //Color initialization + // Particle color + color.r = m_color.red() * (1 - redVariation) + rand() % 256 * redVariation; + color.g = m_color.green() * (1 - greenVariation) + rand() % 256 * greenVariation; + color.b = m_color.blue() * (1 - blueVariation) + rand() % 256 * blueVariation; + color.a = m_alpha * m_color.alpha() * (1 - m_alphaVariation) + rand() % 256 * m_alphaVariation; + p->v1->color = p->v2->color = p->v3->color = p->v4->color = color; + default: + break; + } + + vertexCopy(*p->v1, d->pv); + vertexCopy(*p->v2, d->pv); + vertexCopy(*p->v3, d->pv); + vertexCopy(*p->v4, d->pv); +} + +QT_END_NAMESPACE diff --git a/src/declarative/particles/qsgimageparticle_p.h b/src/declarative/particles/qsgimageparticle_p.h new file mode 100644 index 0000000000..1318647cc9 --- /dev/null +++ b/src/declarative/particles/qsgimageparticle_p.h @@ -0,0 +1,352 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Declarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef ULTRAPARTICLE_H +#define ULTRAPARTICLE_H +#include "qsgparticlepainter_p.h" +#include "qsgstochasticdirection_p.h" +#include <QDeclarativeListProperty> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class UltraMaterial; +class QSGGeometryNode; + +class QSGSprite; +class QSGSpriteEngine; + +struct Color4ub { + uchar r; + uchar g; + uchar b; + uchar a; +}; + +struct SimpleVertex { + float x; + float y; + float tx; + float ty; + float t; + float lifeSpan; + float size; + float endSize; + float sx; + float sy; + float ax; + float ay; +}; + +struct SimpleVertices { + SimpleVertex v1; + SimpleVertex v2; + SimpleVertex v3; + SimpleVertex v4; +}; + +struct UltraVertex { + float x; + float y; + float tx; + float ty; + float t; + float lifeSpan; + float size; + float endSize; + float sx; + float sy; + float ax; + float ay; + Color4ub color; + float xx; + float xy; + float yx; + float yy; + float rotation; + float rotationSpeed; + float autoRotate;//Assume that GPUs prefer floats to bools + float animIdx; + float frameDuration; + float frameCount; + float animT; +}; + +struct UltraVertices { + UltraVertex v1; + UltraVertex v2; + UltraVertex v3; + UltraVertex v4; +}; + +struct IntermediateVertices { + UltraVertex* v1; + UltraVertex* v2; + UltraVertex* v3; + UltraVertex* v4; +}; + +class QSGImageParticle : public QSGParticlePainter +{ + Q_OBJECT + Q_PROPERTY(QUrl image READ image WRITE setImage NOTIFY imageChanged) + Q_PROPERTY(QUrl colorTable READ colortable WRITE setColortable NOTIFY colortableChanged) + Q_PROPERTY(QUrl sizeTable READ sizetable WRITE setSizetable NOTIFY sizetableChanged) + Q_PROPERTY(QUrl opacityTable READ opacitytable WRITE setOpacitytable NOTIFY opacitytableChanged) + + //###Now just colorize - add a flag for 'solid' color particles(where the img is just a mask?)? + Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY colorChanged) + //Stacks (added) with individual colorVariations + Q_PROPERTY(qreal colorVariation READ colorVariation WRITE setColorVariation NOTIFY colorVariationChanged) + Q_PROPERTY(qreal redVariation READ redVariation WRITE setRedVariation NOTIFY redVariationChanged) + Q_PROPERTY(qreal greenVariation READ greenVariation WRITE setGreenVariation NOTIFY greenVariationChanged) + Q_PROPERTY(qreal blueVariation READ blueVariation WRITE setBlueVariation NOTIFY blueVariationChanged) + //Stacks (multiplies) with the Alpha in the color, mostly here so you can use svg color names (which have full alpha) + Q_PROPERTY(qreal alpha READ alpha WRITE setAlpha NOTIFY alphaChanged) + Q_PROPERTY(qreal alphaVariation READ alphaVariation WRITE setAlphaVariation NOTIFY alphaVariationChanged) + + Q_PROPERTY(qreal rotation READ rotation WRITE setRotation NOTIFY rotationChanged) + Q_PROPERTY(qreal rotationVariation READ rotationVariation WRITE setRotationVariation NOTIFY rotationVariationChanged) + Q_PROPERTY(qreal rotationSpeed READ rotationSpeed WRITE setRotationSpeed NOTIFY rotationSpeedChanged) + Q_PROPERTY(qreal rotationSpeedVariation READ rotationSpeedVariation WRITE setRotationSpeedVariation NOTIFY rotationSpeedVariationChanged) + //If true, then will face the direction of motion. Stacks with rotation, e.g. setting rotation + //to 180 will lead to facing away from the direction of motion + Q_PROPERTY(bool autoRotation READ autoRotation WRITE setAutoRotation NOTIFY autoRotationChanged) + + //###Call i/j? Makes more sense to those with vector calculus experience, and I could even add the cirumflex in QML? + //xVector is the vector from the top-left point to the top-right point, and is multiplied by current size + Q_PROPERTY(QSGStochasticDirection* xVector READ xVector WRITE setXVector NOTIFY xVectorChanged) + //yVector is the same, but top-left to bottom-left. The particle is always a parallelogram. + Q_PROPERTY(QSGStochasticDirection* yVector READ yVector WRITE setYVector NOTIFY yVectorChanged) + Q_PROPERTY(QDeclarativeListProperty<QSGSprite> sprites READ sprites) + Q_PROPERTY(bool bloat READ bloat WRITE setBloat NOTIFY bloatChanged)//Just a debugging property to bypass optimizations +public: + explicit QSGImageParticle(QSGItem *parent = 0); + virtual ~QSGImageParticle(){} + + virtual void load(QSGParticleData*); + virtual void reload(QSGParticleData*); + virtual void setCount(int c); + + QDeclarativeListProperty<QSGSprite> sprites(); + QSGSpriteEngine* spriteEngine() {return m_spriteEngine;} + + enum PerformanceLevel{//TODO: Expose? + Unknown = 0, + Simple, + Coloured, + Deformable, + Tabled, + Sprites + }; + + QUrl image() const { return m_image_name; } + void setImage(const QUrl &image); + + QUrl colortable() const { return m_colortable_name; } + void setColortable(const QUrl &table); + + QUrl sizetable() const { return m_sizetable_name; } + void setSizetable (const QUrl &table); + + QUrl opacitytable() const { return m_opacitytable_name; } + void setOpacitytable(const QUrl &table); + + QColor color() const { return m_color; } + void setColor(const QColor &color); + + qreal colorVariation() const { return m_color_variation; } + void setColorVariation(qreal var); + + qreal renderOpacity() const { return m_render_opacity; } + + qreal alphaVariation() const { return m_alphaVariation; } + + qreal alpha() const { return m_alpha; } + + qreal redVariation() const { return m_redVariation; } + + qreal greenVariation() const { return m_greenVariation; } + + qreal blueVariation() const { return m_blueVariation; } + + qreal rotation() const { return m_rotation; } + + qreal rotationVariation() const { return m_rotationVariation; } + + qreal rotationSpeed() const { return m_rotationSpeed; } + + qreal rotationSpeedVariation() const { return m_rotationSpeedVariation; } + + bool autoRotation() const { return m_autoRotation; } + + QSGStochasticDirection* xVector() const { return m_xVector; } + + QSGStochasticDirection* yVector() const { return m_yVector; } + + bool bloat() const { return m_bloat; } + +signals: + + void imageChanged(); + void colortableChanged(); + void sizetableChanged(); + void opacitytableChanged(); + + void colorChanged(); + void colorVariationChanged(); + + void particleDurationChanged(); + void alphaVariationChanged(qreal arg); + + void alphaChanged(qreal arg); + + void redVariationChanged(qreal arg); + + void greenVariationChanged(qreal arg); + + void blueVariationChanged(qreal arg); + + void rotationChanged(qreal arg); + + void rotationVariationChanged(qreal arg); + + void rotationSpeedChanged(qreal arg); + + void rotationSpeedVariationChanged(qreal arg); + + void autoRotationChanged(bool arg); + + void xVectorChanged(QSGStochasticDirection* arg); + + void yVectorChanged(QSGStochasticDirection* arg); + + void bloatChanged(bool arg); + +public slots: + void reloadColor(const Color4ub &c, QSGParticleData* d); + void setAlphaVariation(qreal arg); + + void setAlpha(qreal arg); + + void setRedVariation(qreal arg); + + void setGreenVariation(qreal arg); + + void setBlueVariation(qreal arg); + + void setRotation(qreal arg); + + void setRotationVariation(qreal arg); + + void setRotationSpeed(qreal arg); + + void setRotationSpeedVariation(qreal arg); + + void setAutoRotation(bool arg); + + void setXVector(QSGStochasticDirection* arg); + + void setYVector(QSGStochasticDirection* arg); + + void setBloat(bool arg); + +protected: + QSGNode *updatePaintNode(QSGNode *, UpdatePaintNodeData *); + void reset(); + void prepareNextFrame(); + QSGGeometryNode* buildParticleNode(); + QSGGeometryNode* buildSimpleParticleNode(); + +private slots: + void createEngine(); //### method invoked by sprite list changing (in engine.h) - pretty nasty + +private: + //void vertexCopy(UltraVertex &b,const ParticleVertex& a); + IntermediateVertices* fetchIntermediateVertices(int pos); + bool m_do_reset; + + QUrl m_image_name; + QUrl m_colortable_name; + QUrl m_sizetable_name; + QUrl m_opacitytable_name; + + + QColor m_color; + qreal m_color_variation; + qreal m_particleDuration; + + QSGGeometryNode *m_node; + UltraMaterial *m_material; + + // derived values... + int m_last_particle; + + qreal m_render_opacity; + qreal m_alphaVariation; + qreal m_alpha; + qreal m_redVariation; + qreal m_greenVariation; + qreal m_blueVariation; + qreal m_rotation; + qreal m_rotationVariation; + qreal m_rotationSpeed; + qreal m_rotationSpeedVariation; + bool m_autoRotation; + QSGStochasticDirection* m_xVector; + QSGStochasticDirection* m_yVector; + + QList<QSGSprite*> m_sprites; + QSGSpriteEngine* m_spriteEngine; + + bool m_bloat; + PerformanceLevel perfLevel; + + PerformanceLevel m_lastLevel; + void* m_lastData; +}; + +QT_END_NAMESPACE +QT_END_HEADER +#endif // ULTRAPARTICLE_H diff --git a/src/declarative/particles/qsgitemparticle.cpp b/src/declarative/particles/qsgitemparticle.cpp new file mode 100644 index 0000000000..819c823155 --- /dev/null +++ b/src/declarative/particles/qsgitemparticle.cpp @@ -0,0 +1,205 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Declarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qsgitemparticle_p.h" +#include <QtDeclarative/private/qsgvisualitemmodel_p.h> +#include <qsgnode.h> +#include <QDebug> + +QT_BEGIN_NAMESPACE + +QSGItemParticle::QSGItemParticle(QSGItem *parent) : + QSGParticlePainter(parent), m_fade(true) +{ + setFlag(QSGItem::ItemHasContents); +} + + +void QSGItemParticle::freeze(QSGItem* item) +{ + m_stasis << item; +} + + +void QSGItemParticle::unfreeze(QSGItem* item) +{ + m_stasis.remove(item); +} + +void QSGItemParticle::take(QSGItem *item, bool prioritize) +{ + if(prioritize) + m_pendingItems.push_front(item); + else + m_pendingItems.push_back(item); +} + +void QSGItemParticle::give(QSGItem *item) +{ + //TODO: This +} + +void QSGItemParticle::load(QSGParticleData* d) +{ + if(m_pendingItems.isEmpty()) + return; + int pos = particleTypeIndex(d); + if(m_items[pos]){ + if(m_stasis.contains(m_items[pos])) + qWarning() << "Current model particles prefers overwrite:false"; + //remove old item from the particle that is dying to make room for this one + m_items[pos]->setOpacity(0.); + QSGItemParticleAttached* mpa; + if((mpa = qobject_cast<QSGItemParticleAttached*>(qmlAttachedPropertiesObject<QSGItemParticle>(m_items[pos], false)))) + mpa->detach();//reparent as well? + m_items[pos] = 0; + m_data[pos] = 0; + m_activeCount--; + } + m_items[pos] = m_pendingItems.front(); + m_pendingItems.pop_front(); + m_items[pos]->setX(d->curX() - m_items[pos]->width()/2); + m_items[pos]->setY(d->curY() - m_items[pos]->height()/2); + QSGItemParticleAttached* mpa = qobject_cast<QSGItemParticleAttached*>(qmlAttachedPropertiesObject<QSGItemParticle>(m_items[pos])); + if(mpa){ + mpa->m_mp = this; + mpa->attach(); + } + m_items[pos]->setParentItem(this); + m_data[pos] = d; + m_activeCount++; +} + +void QSGItemParticle::reload(QSGParticleData* d) +{ + //No-op unless we start copying the data. +} + +void QSGItemParticle::setCount(int c) +{ + QSGParticlePainter::setCount(c);//###Do we need our own? + m_particleCount = c; + reset(); +} + +int QSGItemParticle::count() +{ + return m_particleCount; +} + +void QSGItemParticle::reset() +{ + QSGParticlePainter::reset(); + //TODO: Cleanup items? + m_items.resize(m_particleCount); + m_data.resize(m_particleCount); + m_items.fill(0); + m_data.fill(0); + //m_pendingItems.clear();//TODO: Should this be done? If so, Emit signal? +} + + +QSGNode* QSGItemParticle::updatePaintNode(QSGNode* n, UpdatePaintNodeData* d) +{ + //Dummy update just to get painting tick + if(m_pleaseReset){ + m_pleaseReset = false; + reset(); + } + prepareNextFrame(); + + update();//Get called again + if(n) + n->markDirty(QSGNode::DirtyMaterial); + return QSGItem::updatePaintNode(n,d); +} + +void QSGItemParticle::prepareNextFrame() +{ + qint64 timeStamp = m_system->systemSync(this); + qreal curT = timeStamp/1000.0; + qreal dt = curT - m_lastT; + m_lastT = curT; + if(!m_activeCount) + return; + + //TODO: Size, better fade? + for(int i=0; i<m_particleCount; i++){ + QSGItem* item = m_items[i]; + QSGParticleData* data = m_data[i]; + if(!item || !data) + continue; + qreal t = ((timeStamp/1000.0) - data->pv.t) / data->pv.lifeSpan; + if(m_stasis.contains(item)) { + m_data[i]->pv.t += dt;//Stasis effect + continue; + } + if(t >= 1.0){//Usually happens from load + item->setOpacity(0.); + QSGItemParticleAttached* mpa; + if((mpa = qobject_cast<QSGItemParticleAttached*>(qmlAttachedPropertiesObject<QSGItemParticle>(m_items[i])))) + mpa->detach();//reparent as well? + m_items[i] = 0; + m_data[i] = 0; + m_activeCount--; + }else{//Fade + if(m_fade){ + qreal o = 1.; + if(t<0.2) + o = t*5; + if(t>0.8) + o = (1-t)*5; + item->setOpacity(o); + }else{ + item->setOpacity(1.);//###Without fade, it's just a binary toggle - if we turn it off we have to turn it back on + } + } + item->setX(data->curX() - item->width()/2); + item->setY(data->curY() - item->height()/2); + } +} + +QSGItemParticleAttached *QSGItemParticle::qmlAttachedProperties(QObject *object) +{ + return new QSGItemParticleAttached(object); +} + +QT_END_NAMESPACE diff --git a/src/declarative/particles/qsgitemparticle_p.h b/src/declarative/particles/qsgitemparticle_p.h new file mode 100644 index 0000000000..fa3516b631 --- /dev/null +++ b/src/declarative/particles/qsgitemparticle_p.h @@ -0,0 +1,125 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Declarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef ITEMPARTICLE_H +#define ITEMPARTICLE_H +#include "qsgparticlepainter_p.h" +#include <QPointer> +#include <QSet> +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) +class QSGVisualDataModel; +class QSGItemParticleAttached; + +class QSGItemParticle : public QSGParticlePainter +{ + Q_OBJECT + + Q_PROPERTY(bool fade READ fade WRITE setFade NOTIFY fadeChanged) +public: + explicit QSGItemParticle(QSGItem *parent = 0); + + bool fade() const { return m_fade; } + + virtual QSGNode *updatePaintNode(QSGNode *, UpdatePaintNodeData *); + virtual void load(QSGParticleData*); + virtual void reload(QSGParticleData*); + virtual void setCount(int c); + virtual int count(); + + static QSGItemParticleAttached *qmlAttachedProperties(QObject *object); +signals: + void fadeChanged(); + +public slots: + //TODO: Add a follow mode, where moving the delegate causes the logical particle to go with it? + void freeze(QSGItem* item); + void unfreeze(QSGItem* item); + void take(QSGItem* item,bool prioritize=false);//take by modelparticle + void give(QSGItem* item);//give from modelparticle + + void setFade(bool arg){if(arg == m_fade) return; m_fade = arg; emit fadeChanged();} +protected: + virtual void reset(); + void prepareNextFrame(); +private: + QList<QPointer<QSGItem> > m_deletables; + int m_particleCount; + bool m_fade; + + QList<QSGItem*> m_pendingItems; + QVector<QSGItem*> m_items; + QVector<QSGParticleData*> m_data; + QVector<int> m_idx; + QList<int> m_available; + QSet<QSGItem*> m_stasis; + qreal m_lastT; + int m_activeCount; +}; + +class QSGItemParticleAttached : public QObject +{ + Q_OBJECT + Q_PROPERTY(QSGItemParticle* particle READ particle CONSTANT); +public: + QSGItemParticleAttached(QObject* parent) + : QObject(parent), m_mp(0) + {;} + QSGItemParticle* particle() {return m_mp;} + void detach(){emit detached();} + void attach(){emit attached();} +private: + QSGItemParticle* m_mp; + friend class QSGItemParticle; +Q_SIGNALS: + void detached(); + void attached(); +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPEINFO(QSGItemParticle, QML_HAS_ATTACHED_PROPERTIES) + +QT_END_HEADER +#endif // ITEMPARTICLE_H diff --git a/src/declarative/particles/qsgkill.cpp b/src/declarative/particles/qsgkill.cpp new file mode 100644 index 0000000000..1321898dc9 --- /dev/null +++ b/src/declarative/particles/qsgkill.cpp @@ -0,0 +1,59 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Declarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qsgkill_p.h" +#include "qsgparticleemitter_p.h" +QT_BEGIN_NAMESPACE +QSGKillAffector::QSGKillAffector(QSGItem *parent) : + QSGParticleAffector(parent) +{ +} + + +bool QSGKillAffector::affectParticle(QSGParticleData *d, qreal dt) +{ + Q_UNUSED(dt); + if(d->stillAlive()){ + d->pv.t -= d->pv.lifeSpan + 1; + return true; + } +} +QT_END_NAMESPACE diff --git a/src/declarative/particles/qsgkill_p.h b/src/declarative/particles/qsgkill_p.h new file mode 100644 index 0000000000..1b24b2fb40 --- /dev/null +++ b/src/declarative/particles/qsgkill_p.h @@ -0,0 +1,68 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Declarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef KILLAFFECTOR_H +#define KILLAFFECTOR_H +#include "qsgparticleaffector_p.h" + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + + +class QSGKillAffector : public QSGParticleAffector +{ + Q_OBJECT +public: + explicit QSGKillAffector(QSGItem *parent = 0); +protected: + virtual bool affectParticle(QSGParticleData *d, qreal dt); +signals: + +public slots: + +}; + +QT_END_NAMESPACE +QT_END_HEADER +#endif // KILLAFFECTOR_H diff --git a/src/declarative/particles/qsglineextruder.cpp b/src/declarative/particles/qsglineextruder.cpp new file mode 100644 index 0000000000..f32b01402a --- /dev/null +++ b/src/declarative/particles/qsglineextruder.cpp @@ -0,0 +1,66 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Declarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include "qsglineextruder_p.h" +#include <cmath> + +QSGLineExtruder::QSGLineExtruder(QObject *parent) : + QSGParticleExtruder(parent), m_mirrored(false) +{ +} + +QPointF QSGLineExtruder::extrude(const QRectF &r) +{ + qreal x,y; + if(!r.height()){ + x = r.width() * ((qreal)rand())/RAND_MAX; + y = 0; + }else{ + y = r.height() * ((qreal)rand())/RAND_MAX; + if(!r.width()){ + x = 0; + }else{ + x = r.width()/r.height() * y; + if(m_mirrored) + x = r.width() - x; + } + } + return QPointF(x,y); +} diff --git a/src/declarative/particles/qsglineextruder_p.h b/src/declarative/particles/qsglineextruder_p.h new file mode 100644 index 0000000000..f356ca332a --- /dev/null +++ b/src/declarative/particles/qsglineextruder_p.h @@ -0,0 +1,77 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Declarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef LINEEXTRUDER_H +#define LINEEXTRUDER_H +#include "qsgparticleextruder_p.h" + +class QSGLineExtruder : public QSGParticleExtruder +{ + Q_OBJECT + //Default is topleft to bottom right. Flipped makes it topright to bottom left + Q_PROPERTY(bool mirrored READ mirrored WRITE setmirrored NOTIFY mirroredChanged) + +public: + explicit QSGLineExtruder(QObject *parent = 0); + virtual QPointF extrude(const QRectF &); + bool mirrored() const + { + return m_mirrored; + } + +signals: + + void mirroredChanged(bool arg); + +public slots: + + void setmirrored(bool arg) + { + if (m_mirrored != arg) { + m_mirrored = arg; + emit mirroredChanged(arg); + } + } +private: + bool m_mirrored; +}; + +#endif // LINEEXTRUDER_H diff --git a/src/declarative/particles/qsgmaskextruder.cpp b/src/declarative/particles/qsgmaskextruder.cpp new file mode 100644 index 0000000000..3fe28b30a3 --- /dev/null +++ b/src/declarative/particles/qsgmaskextruder.cpp @@ -0,0 +1,91 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Declarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qsgmaskextruder_p.h" +#include <QImage> +#include <QDebug> +QT_BEGIN_NAMESPACE +QSGMaskExtruder::QSGMaskExtruder(QObject *parent) : + QSGParticleExtruder(parent) + , m_lastWidth(-1) + , m_lastHeight(-1) +{ +} + +QPointF QSGMaskExtruder::extrude(const QRectF &r) +{ + ensureInitialized(r); + if(!m_mask.count()) + return r.topLeft(); + const QPointF p = m_mask[rand() % m_mask.count()]; + //### Should random sub-pixel positioning be added? + return p + r.topLeft(); +} + +bool QSGMaskExtruder::contains(const QRectF &bounds, const QPointF &point) +{ + ensureInitialized(bounds);//###Current usage patterns WILL lead to different bounds/r calls. Separate list? + QPoint p = point.toPoint() - bounds.topLeft().toPoint(); + return m_img.rect().contains(p) && (bool)m_img.pixelIndex(p); +} + +void QSGMaskExtruder::ensureInitialized(const QRectF &r) +{ + if(m_lastWidth == r.width() && m_lastHeight == r.height()) + return; + m_lastWidth = r.width(); + m_lastHeight = r.height(); + + m_mask.clear(); + if(m_source.isEmpty()) + return; + m_img = QImage(m_source.toLocalFile()); + m_img = m_img.createAlphaMask(); + m_img = m_img.convertToFormat(QImage::Format_Mono);//Else LSB, but I think that's easier + m_img = m_img.scaled(r.size().toSize());//TODO: Do they need aspect ratio stuff? Or tiling? + for(int i=0; i<r.width(); i++){ + for(int j=0; j<r.height(); j++){ + if(m_img.pixelIndex(i,j))//Direct bit manipulation is presumably more efficient + m_mask << QPointF(i,j); + } + } +} +QT_END_NAMESPACE diff --git a/src/declarative/particles/qsgmaskextruder_p.h b/src/declarative/particles/qsgmaskextruder_p.h new file mode 100644 index 0000000000..b564efa6dc --- /dev/null +++ b/src/declarative/particles/qsgmaskextruder_p.h @@ -0,0 +1,97 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Declarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef MASKEXTRUDER_H +#define MASKEXTRUDER_H +#include "qsgparticleextruder_p.h" +#include <QUrl> +#include <QImage> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QSGMaskExtruder : public QSGParticleExtruder +{ + Q_OBJECT + Q_PROPERTY(QUrl source READ source WRITE setSource NOTIFY sourceChanged) +public: + explicit QSGMaskExtruder(QObject *parent = 0); + virtual QPointF extrude(const QRectF &); + virtual bool contains(const QRectF &bounds, const QPointF &point); + + QUrl source() const + { + return m_source; + } + +signals: + + void sourceChanged(QUrl arg); + +public slots: + + void setSource(QUrl arg) + { + if (m_source != arg) { + m_source = arg; + m_lastHeight = -1;//Trigger reset + m_lastWidth = -1; + emit sourceChanged(arg); + } + } +private: + QUrl m_source; + + void ensureInitialized(const QRectF &r); + int m_lastWidth; + int m_lastHeight; + QImage m_img; + QList<QPointF> m_mask;//TODO: More memory efficient datastructures +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // MASKEXTRUDER_H diff --git a/src/declarative/particles/qsgmodelparticle.cpp b/src/declarative/particles/qsgmodelparticle.cpp new file mode 100644 index 0000000000..94ce082c9d --- /dev/null +++ b/src/declarative/particles/qsgmodelparticle.cpp @@ -0,0 +1,274 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Declarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qsgmodelparticle_p.h" +#include <QtDeclarative/private/qsgvisualitemmodel_p.h> +#include <qsgnode.h> +#include <QDebug> + +QT_BEGIN_NAMESPACE + +QSGModelParticle::QSGModelParticle(QSGItem *parent) : + QSGParticlePainter(parent), m_ownModel(false), m_comp(0), m_model(0), m_fade(true), m_modelCount(0) +{ + setFlag(QSGItem::ItemHasContents); +} + +QVariant QSGModelParticle::model() const +{ + return m_dataSource; +} + +void QSGModelParticle::setModel(const QVariant &arg) +{ + if(arg == m_dataSource) + return; + m_dataSource = arg; + if(qobject_cast<QSGVisualDataModel*>(arg.value<QObject*>())) { + if(m_ownModel && m_model) + delete m_model; + m_model = qobject_cast<QSGVisualDataModel*>(arg.value<QObject*>()); + m_ownModel = false; + }else{ + if(!m_model || !m_ownModel) + m_model = new QSGVisualDataModel(qmlContext(this)); + m_model->setModel(m_dataSource); + m_ownModel = true; + } + if(m_comp) + m_model->setDelegate(m_comp); + emit modelChanged(); + emit modelCountChanged(); + connect(m_model, SIGNAL(countChanged()), + this, SIGNAL(modelCountChanged())); + connect(m_model, SIGNAL(countChanged()), + this, SLOT(updateCount())); + updateCount(); +} + +void QSGModelParticle::updateCount() +{ + int newCount = 0; + if(m_model) + newCount = m_model->count(); + if(newCount < 0) + return;//WTF? + if(m_modelCount == 0 || newCount == 0){ + m_available.clear(); + for(int i=0; i<newCount; i++) + m_available << i; + }else if(newCount < m_modelCount){ + for(int i=newCount; i<m_modelCount; i++) //existing ones must leave normally, but aren't readded + m_available.removeAll(i); + }else if(newCount > m_modelCount){ + for(int i=m_modelCount; i<newCount; i++) + m_available << i; + } + m_modelCount = newCount; +} + +QDeclarativeComponent *QSGModelParticle::delegate() const +{ + if(m_model) + return m_model->delegate(); + return 0; +} + +void QSGModelParticle::setDelegate(QDeclarativeComponent *comp) +{ + if (QSGVisualDataModel *dataModel = qobject_cast<QSGVisualDataModel*>(m_model)) + if (comp == dataModel->delegate()) + return; + m_comp = comp; + if(m_model) + m_model->setDelegate(comp); + emit delegateChanged(); +} + +int QSGModelParticle::modelCount() const +{ + if(m_model) + const_cast<QSGModelParticle*>(this)->updateCount();//TODO: Investigate why this doesn't get called properly + return m_modelCount; +} + + +void QSGModelParticle::freeze(QSGItem* item) +{ + m_stasis << item; +} + + +void QSGModelParticle::unfreeze(QSGItem* item) +{ + m_stasis.remove(item); +} + +void QSGModelParticle::load(QSGParticleData* d) +{ + if(!m_model || !m_model->count()) + return; + int pos = particleTypeIndex(d); + if(m_available.isEmpty()) + return; + if(m_items[pos]){ + if(m_stasis.contains(m_items[pos])) + qWarning() << "Current model particles prefers overwrite:false"; + //remove old item from the particle that is dying to make room for this one + m_items[pos]->setOpacity(0.); + m_available << m_idx[pos]; + m_model->release(m_items[pos]); + m_idx[pos] = -1; + m_items[pos] = 0; + m_data[pos] = 0; + m_activeCount--; + } + m_items[pos] = m_model->item(m_available.first()); + m_idx[pos] = m_available.first(); + m_available.pop_front(); + QSGModelParticleAttached* mpa = qobject_cast<QSGModelParticleAttached*>(qmlAttachedPropertiesObject<QSGModelParticle>(m_items[pos])); + if(mpa){ + mpa->m_mp = this; + mpa->attach(); + } + m_items[pos]->setParentItem(this); + m_data[pos] = d; + m_activeCount++; +} + +void QSGModelParticle::reload(QSGParticleData* d) +{ + //No-op unless we start copying the data. +} + +void QSGModelParticle::setCount(int c) +{ + QSGParticlePainter::setCount(c);//###Do we need our own? + m_particleCount = c; + reset(); +} + +int QSGModelParticle::count() +{ + return m_particleCount; +} + +void QSGModelParticle::reset() +{ + QSGParticlePainter::reset(); + //TODO: Cleanup items? + m_items.resize(m_particleCount); + m_data.resize(m_particleCount); + m_idx.resize(m_particleCount); + m_items.fill(0); + m_data.fill(0); + m_idx.fill(-1); + //m_available.clear();//Should this be reset too? + //m_pendingItems.clear();//TODO: Should this be done? If so, Emit signal? +} + + +QSGNode* QSGModelParticle::updatePaintNode(QSGNode* n, UpdatePaintNodeData* d) +{ + //Dummy update just to get painting tick + if(m_pleaseReset){ + m_pleaseReset = false; + reset(); + } + prepareNextFrame(); + + update();//Get called again + if(n) + n->markDirty(QSGNode::DirtyMaterial); + return QSGItem::updatePaintNode(n,d); +} + +void QSGModelParticle::prepareNextFrame() +{ + qint64 timeStamp = m_system->systemSync(this); + qreal curT = timeStamp/1000.0; + qreal dt = curT - m_lastT; + m_lastT = curT; + if(!m_activeCount) + return; + + //TODO: Size, better fade? + for(int i=0; i<m_particleCount; i++){ + QSGItem* item = m_items[i]; + QSGParticleData* data = m_data[i]; + if(!item || !data) + continue; + qreal t = ((timeStamp/1000.0) - data->pv.t) / data->pv.lifeSpan; + if(m_stasis.contains(item)) { + m_data[i]->pv.t += dt;//Stasis effect + continue; + } + if(t >= 1.0){//Usually happens from load + item->setOpacity(0.); + m_available << m_idx[i]; + m_model->release(m_items[i]); + m_idx[i] = -1; + m_items[i] = 0; + m_data[i] = 0; + m_activeCount--; + }else{//Fade + if(m_fade){ + qreal o = 1.; + if(t<0.2) + o = t*5; + if(t>0.8) + o = (1-t)*5; + item->setOpacity(o); + }else{ + item->setOpacity(1.);//###Without fade, it's just a binary toggle - if we turn it off we have to turn it back on + } + } + item->setX(data->curX() - item->width()/2); + item->setY(data->curY() - item->height()/2); + } +} + +QSGModelParticleAttached *QSGModelParticle::qmlAttachedProperties(QObject *object) +{ + return new QSGModelParticleAttached(object); +} + +QT_END_NAMESPACE diff --git a/src/declarative/particles/qsgmodelparticle_p.h b/src/declarative/particles/qsgmodelparticle_p.h new file mode 100644 index 0000000000..4dd8bfa710 --- /dev/null +++ b/src/declarative/particles/qsgmodelparticle_p.h @@ -0,0 +1,143 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Declarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef DATAPARTICLE_H +#define DATAPARTICLE_H +#include "qsgparticlepainter_p.h" +#include <QPointer> +#include <QSet> +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) +class QSGVisualDataModel; +class QSGModelParticleAttached; + +class QSGModelParticle : public QSGParticlePainter +{ + Q_OBJECT + + Q_PROPERTY(QVariant model READ model WRITE setModel NOTIFY modelChanged) + Q_PROPERTY(QDeclarativeComponent *delegate READ delegate WRITE setDelegate NOTIFY delegateChanged) + Q_PROPERTY(int modelCount READ modelCount NOTIFY modelCountChanged) + Q_PROPERTY(bool fade READ fade WRITE setFade NOTIFY fadeChanged) + Q_CLASSINFO("DefaultProperty", "delegate") +public: + explicit QSGModelParticle(QSGItem *parent = 0); + QVariant model() const; + void setModel(const QVariant &); + + QDeclarativeComponent *delegate() const; + void setDelegate(QDeclarativeComponent *); + + int modelCount() const; + + bool fade() const { return m_fade; } + + virtual QSGNode *updatePaintNode(QSGNode *, UpdatePaintNodeData *); + virtual void load(QSGParticleData*); + virtual void reload(QSGParticleData*); + virtual void setCount(int c); + virtual int count(); + + static QSGModelParticleAttached *qmlAttachedProperties(QObject *object); +signals: + void modelChanged(); + void delegateChanged(); + void modelCountChanged(); + void fadeChanged(); + +public slots: + void freeze(QSGItem* item); + void unfreeze(QSGItem* item); + + void setFade(bool arg){if(arg == m_fade) return; m_fade = arg; emit fadeChanged();} +protected: + virtual void reset(); + void prepareNextFrame(); +private slots: + void updateCount(); +private: + bool m_ownModel; + QDeclarativeComponent* m_comp; + QSGVisualDataModel *m_model; + QVariant m_dataSource; + QList<QPointer<QSGItem> > m_deletables; + int m_particleCount; + bool m_fade; + + QList<QSGItem*> m_pendingItems; + QVector<QSGItem*> m_items; + QVector<QSGParticleData*> m_data; + QVector<int> m_idx; + QList<int> m_available; + QSet<QSGItem*> m_stasis; + qreal m_lastT; + int m_activeCount; + int m_modelCount; +}; + +class QSGModelParticleAttached : public QObject +{ + Q_OBJECT + Q_PROPERTY(QSGModelParticle* particle READ particle CONSTANT); +public: + QSGModelParticleAttached(QObject* parent) + : QObject(parent), m_mp(0) + {;} + QSGModelParticle* particle() {return m_mp;} + void detach(){emit detached();} + void attach(){emit attached();} +private: + QSGModelParticle* m_mp; + friend class QSGModelParticle; +Q_SIGNALS: + void detached(); + void attached(); +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPEINFO(QSGModelParticle, QML_HAS_ATTACHED_PROPERTIES) + +QT_END_HEADER +#endif // DATAPARTICLE_H diff --git a/src/declarative/particles/qsgparticleaffector.cpp b/src/declarative/particles/qsgparticleaffector.cpp new file mode 100644 index 0000000000..d4b588a44f --- /dev/null +++ b/src/declarative/particles/qsgparticleaffector.cpp @@ -0,0 +1,121 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Declarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qsgparticleaffector_p.h" +#include <QDebug> +QT_BEGIN_NAMESPACE +QSGParticleAffector::QSGParticleAffector(QSGItem *parent) : + QSGItem(parent), m_needsReset(false), m_system(0), m_active(true) + , m_updateIntSet(false), m_shape(new QSGParticleExtruder(this)), m_signal(false) +{ + connect(this, SIGNAL(systemChanged(QSGParticleSystem*)), + this, SLOT(updateOffsets())); + connect(this, SIGNAL(xChanged()), + this, SLOT(updateOffsets())); + connect(this, SIGNAL(yChanged()), + this, SLOT(updateOffsets()));//TODO: in componentComplete and all relevant signals +} + +void QSGParticleAffector::componentComplete() +{ + if(!m_system) + qWarning() << "Affector created without a particle system specified";//TODO: useful QML warnings, like line number? + QSGItem::componentComplete(); +} + +void QSGParticleAffector::affectSystem(qreal dt) +{ + if(!m_active) + return; + if(!m_system){ + qDebug() << "No system" << this; + return; + } + //If not reimplemented, calls affect particle per particle + //But only on particles in targeted system/area + if(m_updateIntSet){ + m_groups.clear(); + foreach(const QString &p, m_particles) + m_groups << m_system->m_groupIds[p];//###Can this occur before group ids are properly assigned? + m_updateIntSet = false; + } + //foreach(ParticleData* d, m_system->m_data){ + for(int i=0; i<m_system->m_particle_count; i++){ + QSGParticleData* d = m_system->m_data[i]; + if(!d || (m_onceOff && m_onceOffed.contains(d->systemIndex))) + continue; + if(m_groups.isEmpty() || m_groups.contains(d->group)){ + //Need to have previous location for affected. if signal || shape might be faster? + QPointF curPos = QPointF(d->curX(), d->curY()); + if(width() == 0 || height() == 0 + || m_shape->contains(QRectF(m_offset.x(), m_offset.y(), width(), height()),curPos)){ + if(affectParticle(d, dt)){ + m_system->m_needsReset << d; + if(m_onceOff) + m_onceOffed << d->systemIndex; + if(m_signal) + emit affected(curPos.x(), curPos.y()); + } + } + } + } +} + +bool QSGParticleAffector::affectParticle(QSGParticleData *d, qreal dt) +{ + Q_UNUSED(d); + Q_UNUSED(dt); + return false; +} + +void QSGParticleAffector::reset(int idx) +{//TODO: This, among other ones, should be restructured so they don't all need to remember to call the superclass + if(m_onceOff) + m_onceOffed.remove(idx); +} + +void QSGParticleAffector::updateOffsets() +{ + if(m_system) + m_offset = m_system->mapFromItem(this, QPointF(0, 0)); +} + +QT_END_NAMESPACE diff --git a/src/declarative/particles/qsgparticleaffector_p.h b/src/declarative/particles/qsgparticleaffector_p.h new file mode 100644 index 0000000000..1572ad8102 --- /dev/null +++ b/src/declarative/particles/qsgparticleaffector_p.h @@ -0,0 +1,193 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Declarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef PARTICLEAFFECTOR_H +#define PARTICLEAFFECTOR_H + +#include <QObject> +#include "qsgparticlesystem_p.h" +#include "qsgparticleextruder_p.h" + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + + +class QSGParticleAffector : public QSGItem +{ + Q_OBJECT + Q_PROPERTY(QSGParticleSystem* system READ system WRITE setSystem NOTIFY systemChanged) + Q_PROPERTY(QStringList particles READ particles WRITE setParticles NOTIFY particlesChanged) + Q_PROPERTY(bool active READ active WRITE setActive NOTIFY activeChanged) + Q_PROPERTY(bool onceOff READ onceOff WRITE setOnceOff NOTIFY onceOffChanged) + Q_PROPERTY(QSGParticleExtruder* shape READ shape WRITE setShape NOTIFY shapeChanged) + Q_PROPERTY(bool signal READ signal WRITE setSignal NOTIFY signalChanged) + +public: + explicit QSGParticleAffector(QSGItem *parent = 0); + virtual void affectSystem(qreal dt); + virtual void reset(int systemIdx);//As some store their own data per idx? + QSGParticleSystem* system() const + { + return m_system; + } + + QStringList particles() const + { + return m_particles; + } + + bool active() const + { + return m_active; + } + + bool onceOff() const + { + return m_onceOff; + } + + QSGParticleExtruder* shape() const + { + return m_shape; + } + + bool signal() const + { + return m_signal; + } + +signals: + + void systemChanged(QSGParticleSystem* arg); + + void particlesChanged(QStringList arg); + + void activeChanged(bool arg); + + void onceOffChanged(bool arg); + + void shapeChanged(QSGParticleExtruder* arg); + + void affected(qreal x, qreal y);//###Idx too? + void signalChanged(bool arg); + +public slots: +void setSystem(QSGParticleSystem* arg) +{ + if (m_system != arg) { + m_system = arg; + m_system->registerParticleAffector(this); + emit systemChanged(arg); + } +} + +void setParticles(QStringList arg) +{ + if (m_particles != arg) { + m_particles = arg; + m_updateIntSet = true; + emit particlesChanged(arg); + } +} + +void setActive(bool arg) +{ + if (m_active != arg) { + m_active = arg; + emit activeChanged(arg); + } +} + +void setOnceOff(bool arg) +{ + if (m_onceOff != arg) { + m_onceOff = arg; + emit onceOffChanged(arg); + } +} + +void setShape(QSGParticleExtruder* arg) +{ + if (m_shape != arg) { + m_shape = arg; + emit shapeChanged(arg); + } +} + +void setSignal(bool arg) +{ + if (m_signal != arg) { + m_signal = arg; + emit signalChanged(arg); + } +} + +protected: + friend class QSGParticleSystem; + virtual bool affectParticle(QSGParticleData *d, qreal dt); + bool m_needsReset;//### What is this really saving? + QSGParticleSystem* m_system; + QStringList m_particles; + bool activeGroup(int g) {return m_groups.isEmpty() || m_groups.contains(g);} + bool m_active; + virtual void componentComplete(); + QPointF m_offset; +private: + QSet<int> m_groups; + QSet<int> m_onceOffed; + bool m_updateIntSet; + + bool m_onceOff; + + QSGParticleExtruder* m_shape; + + bool m_signal; + +private slots: + void updateOffsets(); +}; + +QT_END_NAMESPACE +QT_END_HEADER +#endif // PARTICLEAFFECTOR_H diff --git a/src/declarative/particles/qsgparticleemitter.cpp b/src/declarative/particles/qsgparticleemitter.cpp new file mode 100644 index 0000000000..c04c86cfe5 --- /dev/null +++ b/src/declarative/particles/qsgparticleemitter.cpp @@ -0,0 +1,148 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Declarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qsgparticleemitter_p.h" +QT_BEGIN_NAMESPACE +QSGParticleEmitter::QSGParticleEmitter(QSGItem *parent) : + QSGItem(parent) + , m_particlesPerSecond(10) + , m_particleDuration(1000) + , m_particleDurationVariation(0) + , m_emitting(true) + , m_system(0) + , m_extruder(0) + , m_defaultExtruder(0) + , m_speed(&m_nullVector) + , m_acceleration(&m_nullVector) + , m_particleSize(16) + , m_particleEndSize(-1) + , m_particleSizeVariation(0) + , m_maxParticleCount(-1) + , m_burstLeft(0) + +{ + //TODO: Reset speed/acc back to null vector? Or allow null pointer? + connect(this, SIGNAL(maxParticleCountChanged(int)), + this, SIGNAL(particleCountChanged())); + connect(this, SIGNAL(particlesPerSecondChanged(qreal)), + this, SIGNAL(particleCountChanged())); + connect(this, SIGNAL(particleDurationChanged(int)), + this, SIGNAL(particleCountChanged())); +} + +QSGParticleEmitter::~QSGParticleEmitter() +{ + if(m_defaultExtruder) + delete m_defaultExtruder; +} + +void QSGParticleEmitter::componentComplete() +{ + if(!m_system) + qWarning() << "Emitter created without a particle system specified";//TODO: useful QML warnings, like line number? + QSGItem::componentComplete(); +} +void QSGParticleEmitter::emitWindow(int timeStamp) +{ + Q_UNUSED(timeStamp); +} + + +void QSGParticleEmitter::setEmitting(bool arg) +{ + if (m_emitting != arg) { + m_emitting = arg; + emit emittingChanged(arg); + } +} + + +QSGParticleExtruder* QSGParticleEmitter::effectiveExtruder() +{ + if(m_extruder) + return m_extruder; + if(!m_defaultExtruder) + m_defaultExtruder = new QSGParticleExtruder; + return m_defaultExtruder; +} + +void QSGParticleEmitter::pulse(qreal seconds) +{ + if(!particleCount()) + qWarning() << "pulse called on an emitter with a particle count of zero"; + if(!m_emitting) + m_burstLeft = seconds*1000.0;//TODO: Change name to match +} + +void QSGParticleEmitter::burst(int num) +{ + if(!particleCount()) + qWarning() << "burst called on an emitter with a particle count of zero"; + m_burstQueue << qMakePair(num, QPointF(x(), y())); +} + +void QSGParticleEmitter::setMaxParticleCount(int arg) +{ + if (m_maxParticleCount != arg) { + if(arg < 0 && m_maxParticleCount >= 0){ + connect(this, SIGNAL(particlesPerSecondChanged(qreal)), + this, SIGNAL(particleCountChanged())); + connect(this, SIGNAL(particleDurationChanged(int)), + this, SIGNAL(particleCountChanged())); + }else if(arg >= 0 && m_maxParticleCount < 0){ + disconnect(this, SIGNAL(particlesPerSecondChanged(qreal)), + this, SIGNAL(particleCountChanged())); + disconnect(this, SIGNAL(particleDurationChanged(int)), + this, SIGNAL(particleCountChanged())); + } + m_maxParticleCount = arg; + emit maxParticleCountChanged(arg); + } +} + +int QSGParticleEmitter::particleCount() const +{ + if(m_maxParticleCount >= 0) + return m_maxParticleCount; + return m_particlesPerSecond*((m_particleDuration+m_particleDurationVariation)/1000.0); +} + +QT_END_NAMESPACE diff --git a/src/declarative/particles/qsgparticleemitter_p.h b/src/declarative/particles/qsgparticleemitter_p.h new file mode 100644 index 0000000000..ad6a5f645f --- /dev/null +++ b/src/declarative/particles/qsgparticleemitter_p.h @@ -0,0 +1,304 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Declarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef PARTICLEEMITTER_H +#define PARTICLEEMITTER_H + +#include <QSGItem> +#include <QDebug> +#include "qsgparticlesystem_p.h" +#include "qsgparticleextruder_p.h" +#include "qsgstochasticdirection_p.h" + +#include <QList> +#include <QPair> +#include <QPointF> +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QSGParticleEmitter : public QSGItem +{ + Q_OBJECT + //###currently goes in emitters OR sets system. Pick one? + Q_PROPERTY(QSGParticleSystem* system READ system WRITE setSystem NOTIFY systemChanged) + Q_PROPERTY(QString particle READ particle WRITE setParticle NOTIFY particleChanged) + Q_PROPERTY(QSGParticleExtruder* shape READ extruder WRITE setExtruder NOTIFY extruderChanged) + Q_PROPERTY(bool emitting READ emitting WRITE setEmitting NOTIFY emittingChanged) + + Q_PROPERTY(qreal particlesPerSecond READ particlesPerSecond WRITE setParticlesPerSecond NOTIFY particlesPerSecondChanged) + Q_PROPERTY(int particleDuration READ particleDuration WRITE setParticleDuration NOTIFY particleDurationChanged) + Q_PROPERTY(int particleDurationVariation READ particleDurationVariation WRITE setParticleDurationVariation NOTIFY particleDurationVariationChanged) + Q_PROPERTY(int maxParticles READ maxParticleCount WRITE setMaxParticleCount NOTIFY maxParticleCountChanged) + + Q_PROPERTY(qreal particleSize READ particleSize WRITE setParticleSize NOTIFY particleSizeChanged) + Q_PROPERTY(qreal particleEndSize READ particleEndSize WRITE setParticleEndSize NOTIFY particleEndSizeChanged) + Q_PROPERTY(qreal particleSizeVariation READ particleSizeVariation WRITE setParticleSizeVariation NOTIFY particleSizeVariationChanged) + + Q_PROPERTY(QSGStochasticDirection *speed READ speed WRITE setSpeed NOTIFY speedChanged) + Q_PROPERTY(QSGStochasticDirection *acceleration READ acceleration WRITE setAcceleration NOTIFY accelerationChanged) +public: + explicit QSGParticleEmitter(QSGItem *parent = 0); + virtual ~QSGParticleEmitter(); + virtual void emitWindow(int timeStamp); + + bool emitting() const + { + return m_emitting; + } + + qreal particlesPerSecond() const + { + return m_particlesPerSecond; + } + + int particleDuration() const + { + return m_particleDuration; + } + + QSGParticleSystem* system() const + { + return m_system; + } + + QString particle() const + { + return m_particle; + } + + int particleDurationVariation() const + { + return m_particleDurationVariation; + } + + virtual void componentComplete(); +signals: + void particlesPerSecondChanged(qreal); + void particleDurationChanged(int); + void emittingChanged(bool); + + void systemChanged(QSGParticleSystem* arg); + + void particleChanged(QString arg); + + void particleDurationVariationChanged(int arg); + + void extruderChanged(QSGParticleExtruder* arg); + + void particleSizeChanged(qreal arg); + + void particleEndSizeChanged(qreal arg); + + void particleSizeVariationChanged(qreal arg); + + void speedChanged(QSGStochasticDirection * arg); + + void accelerationChanged(QSGStochasticDirection * arg); + + void maxParticleCountChanged(int arg); + void particleCountChanged(); + +public slots: + void pulse(qreal seconds); + void burst(int num); + + void setEmitting(bool arg); + + void setParticlesPerSecond(qreal arg) + { + if (m_particlesPerSecond != arg) { + m_particlesPerSecond = arg; + emit particlesPerSecondChanged(arg); + } + } + + void setParticleDuration(int arg) + { + if (m_particleDuration != arg) { + m_particleDuration = arg; + emit particleDurationChanged(arg); + } + } + + void setSystem(QSGParticleSystem* arg) + { + if (m_system != arg) { + m_system = arg; + m_system->registerParticleEmitter(this); + emit systemChanged(arg); + } + } + + void setParticle(QString arg) + { + if (m_particle != arg) { + m_particle = arg; + emit particleChanged(arg); + } + } + + void setParticleDurationVariation(int arg) + { + if (m_particleDurationVariation != arg) { + m_particleDurationVariation = arg; + emit particleDurationVariationChanged(arg); + } + } + void setExtruder(QSGParticleExtruder* arg) + { + if (m_extruder != arg) { + m_extruder = arg; + emit extruderChanged(arg); + } + } + + void setParticleSize(qreal arg) + { + if (m_particleSize != arg) { + m_particleSize = arg; + emit particleSizeChanged(arg); + } + } + + void setParticleEndSize(qreal arg) + { + if (m_particleEndSize != arg) { + m_particleEndSize = arg; + emit particleEndSizeChanged(arg); + } + } + + void setParticleSizeVariation(qreal arg) + { + if (m_particleSizeVariation != arg) { + m_particleSizeVariation = arg; + emit particleSizeVariationChanged(arg); + } + } + + void setSpeed(QSGStochasticDirection * arg) + { + if (m_speed != arg) { + m_speed = arg; + emit speedChanged(arg); + } + } + + void setAcceleration(QSGStochasticDirection * arg) + { + if (m_acceleration != arg) { + m_acceleration = arg; + emit accelerationChanged(arg); + } + } + + void setMaxParticleCount(int arg); + +public: + int particleCount() const; + + virtual void reset(){;} + QSGParticleExtruder* extruder() const + { + return m_extruder; + } + + qreal particleSize() const + { + return m_particleSize; + } + + qreal particleEndSize() const + { + return m_particleEndSize; + } + + qreal particleSizeVariation() const + { + return m_particleSizeVariation; + } + + QSGStochasticDirection * speed() const + { + return m_speed; + } + + QSGStochasticDirection * acceleration() const + { + return m_acceleration; + } + + int maxParticleCount() const + { + return m_maxParticleCount; + } + +protected: + qreal m_particlesPerSecond; + int m_particleDuration; + int m_particleDurationVariation; + bool m_emitting; + QSGParticleSystem* m_system; + QString m_particle; + QSGParticleExtruder* m_extruder; + QSGParticleExtruder* m_defaultExtruder; + QSGParticleExtruder* effectiveExtruder(); + QSGStochasticDirection * m_speed; + QSGStochasticDirection * m_acceleration; + qreal m_particleSize; + qreal m_particleEndSize; + qreal m_particleSizeVariation; + + int m_burstLeft;//TODO: Rename to pulse + QList<QPair<int, QPointF > > m_burstQueue; + int m_maxParticleCount; +private: + QSGStochasticDirection m_nullVector; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // PARTICLEEMITTER_H diff --git a/src/declarative/particles/qsgparticleextruder.cpp b/src/declarative/particles/qsgparticleextruder.cpp new file mode 100644 index 0000000000..91b968c8af --- /dev/null +++ b/src/declarative/particles/qsgparticleextruder.cpp @@ -0,0 +1,78 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Declarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qsgparticleextruder_p.h" + +QT_BEGIN_NAMESPACE + +QSGParticleExtruder::QSGParticleExtruder(QObject *parent) : + QObject(parent), m_fill(true) +{ +} + +QPointF QSGParticleExtruder::extrude(const QRectF &rect) +{ + if(m_fill) + return QPointF(((qreal)rand() / RAND_MAX) * rect.width() + rect.x(), + ((qreal)rand() / RAND_MAX) * rect.height() + rect.y()); + int side = rand() % 4; + switch(side){//TODO: Doesn't this overlap the corners? + case 0: + return QPointF(rect.x(), + ((qreal)rand() / RAND_MAX) * rect.height() + rect.y()); + case 1: + return QPointF(rect.width() + rect.x(), + ((qreal)rand() / RAND_MAX) * rect.height() + rect.y()); + case 2: + return QPointF(((qreal)rand() / RAND_MAX) * rect.width() + rect.x(), + rect.y()); + default: + return QPointF(((qreal)rand() / RAND_MAX) * rect.width() + rect.x(), + rect.height() + rect.y()); + } +} + +bool QSGParticleExtruder::contains(const QRectF &bounds, const QPointF &point) +{ + return bounds.contains(point); +} + +QT_END_NAMESPACE diff --git a/src/declarative/particles/qsgparticleextruder_p.h b/src/declarative/particles/qsgparticleextruder_p.h new file mode 100644 index 0000000000..41e27eb2fa --- /dev/null +++ b/src/declarative/particles/qsgparticleextruder_p.h @@ -0,0 +1,90 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Declarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef PARTICLEEXTRUDER_H +#define PARTICLEEXTRUDER_H + +#include <QObject> +#include <QRectF> +#include <QPointF> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QSGParticleExtruder : public QObject +{ + Q_OBJECT + Q_PROPERTY(bool fill READ fill WRITE setFill NOTIFY fillChanged)//###Should this be base class, or a BoxExtruder? + +public: + explicit QSGParticleExtruder(QObject *parent = 0); + virtual QPointF extrude(const QRectF &); + virtual bool contains(const QRectF &bounds, const QPointF &point);//###Needed for follow emitter, but does it belong? Only marginally conceptually valid, and that's from user's perspective + bool fill() const + { + return m_fill; + } + +signals: + + void fillChanged(bool arg); + +public slots: + + void setFill(bool arg) + { + if (m_fill != arg) { + m_fill = arg; + emit fillChanged(arg); + } + } +protected: + bool m_fill; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // PARTICLEEXTRUDER_H diff --git a/src/declarative/particles/qsgparticlepainter.cpp b/src/declarative/particles/qsgparticlepainter.cpp new file mode 100644 index 0000000000..8814c61b56 --- /dev/null +++ b/src/declarative/particles/qsgparticlepainter.cpp @@ -0,0 +1,135 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Declarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qsgparticlepainter_p.h" +#include <QDebug> +QT_BEGIN_NAMESPACE +QSGParticlePainter::QSGParticlePainter(QSGItem *parent) : + QSGItem(parent), + m_system(0) +{ + connect(this, SIGNAL(xChanged()), + this, SLOT(calcSystemOffset())); + connect(this, SIGNAL(yChanged()), + this, SLOT(calcSystemOffset())); +} + +void QSGParticlePainter::componentComplete() +{ + if(!m_system) + qWarning() << "Particle created without a particle system specified";//TODO: useful QML warnings, like line number? + QSGItem::componentComplete(); +} + + +void QSGParticlePainter::setSystem(QSGParticleSystem *arg) +{ + if (m_system != arg) { + m_system = arg; + if(m_system){ + m_system->registerParticleType(this); + connect(m_system, SIGNAL(xChanged()), + this, SLOT(calcSystemOffset())); + connect(m_system, SIGNAL(yChanged()), + this, SLOT(calcSystemOffset())); + calcSystemOffset(); + } + emit systemChanged(arg); + } +} + +void QSGParticlePainter::load(QSGParticleData*) +{ +} + +void QSGParticlePainter::reload(QSGParticleData*) +{ +} + +void QSGParticlePainter::reset() +{ + //Have to every time because what it's emitting may have changed and that affects particleTypeIndex + m_particleStarts.clear(); + m_lastStart = 0; +} + +void QSGParticlePainter::setCount(int c) +{ + if(c == m_count) + return; + m_count = c; + emit countChanged(); +} + +int QSGParticlePainter::count() +{ + return m_count; +} + + +int QSGParticlePainter::particleTypeIndex(QSGParticleData* d) +{ + if(!m_particleStarts.contains(d->group)){ + m_particleStarts.insert(d->group, m_lastStart); + m_lastStart += m_system->m_groupData[d->group]->size; + } + int ret = m_particleStarts[d->group] + d->particleIndex; + Q_ASSERT(ret >=0 && ret < m_count);//XXX: Possibly shouldn't assert, but bugs here were hard to find in the past + return ret; +} + + +void QSGParticlePainter::calcSystemOffset() +{ + if(!m_system) + return; + QPointF lastOffset = m_systemOffset; + m_systemOffset = -1 * this->mapFromItem(m_system, QPointF()); + if(lastOffset != m_systemOffset){ + //Reload all particles//TODO: Necessary? + foreach(const QString &g, m_particles){ + int gId = m_system->m_groupIds[g]; + for(int i=0; i<m_system->m_groupData[gId]->size; i++) + reload(m_system->m_data[m_system->m_groupData[gId]->start + i]); + } + } +} +QT_END_NAMESPACE diff --git a/src/declarative/particles/qsgparticlepainter_p.h b/src/declarative/particles/qsgparticlepainter_p.h new file mode 100644 index 0000000000..8f1e13b70f --- /dev/null +++ b/src/declarative/particles/qsgparticlepainter_p.h @@ -0,0 +1,136 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Declarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef PARTICLE_H +#define PARTICLE_H + +#include <QObject> +#include <QDebug> +#include "qsgparticlesystem_p.h" + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + + +class QSGParticlePainter : public QSGItem +{ + Q_OBJECT + Q_PROPERTY(QSGParticleSystem* system READ system WRITE setSystem NOTIFY systemChanged) + Q_PROPERTY(QStringList particles READ particles WRITE setParticles NOTIFY particlesChanged) + +public: + explicit QSGParticlePainter(QSGItem *parent = 0); + virtual void load(QSGParticleData*); + virtual void reload(QSGParticleData*); + virtual void setCount(int c); + virtual int count(); + QSGParticleSystem* system() const + { + return m_system; + } + + + QStringList particles() const + { + return m_particles; + } + + int particleTypeIndex(QSGParticleData*); +signals: + void countChanged(); + void systemChanged(QSGParticleSystem* arg); + + void particlesChanged(QStringList arg); + +public slots: +void setSystem(QSGParticleSystem* arg); + +void setParticles(QStringList arg) +{ + if (m_particles != arg) { + m_particles = arg; + emit particlesChanged(arg); + } +} +private slots: + void calcSystemOffset(); +protected: + virtual void reset(); + virtual void componentComplete(); + +// virtual QSGNode *updatePaintNode(QSGNode *, UpdatePaintNodeData *){ +// qDebug() << "Shouldn't be here..." << this; +// return 0; +// } + + QSGParticleSystem* m_system; + friend class QSGParticleSystem; + int m_count; + bool m_pleaseReset; + QStringList m_particles; + QHash<int,int> m_particleStarts; + int m_lastStart; + QPointF m_systemOffset; + + template <typename VertexStruct> + void vertexCopy(VertexStruct &b, const ParticleVertex& a) + { + b.x = a.x - m_systemOffset.x(); + b.y = a.y - m_systemOffset.y(); + b.t = a.t; + b.lifeSpan = a.lifeSpan; + b.size = a.size; + b.endSize = a.endSize; + b.sx = a.sx; + b.sy = a.sy; + b.ax = a.ax; + b.ay = a.ay; + } + +private: +}; + +QT_END_NAMESPACE +QT_END_HEADER +#endif // PARTICLE_H diff --git a/src/declarative/particles/qsgparticlesmodule.cpp b/src/declarative/particles/qsgparticlesmodule.cpp new file mode 100644 index 0000000000..a7a9a9253f --- /dev/null +++ b/src/declarative/particles/qsgparticlesmodule.cpp @@ -0,0 +1,111 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Declarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qsgangleddirection_p.h" +#include "qsgcustomparticle_p.h" +#include "qsgellipseextruder_p.h" +#include "qsgemitter_p.h" +#include "qsgfollowemitter_p.h" +#include "qsgfriction_p.h" +#include "qsggravity_p.h" +#include "qsgimageparticle_p.h" +#include "qsgitemparticle_p.h" +#include "qsgkill_p.h" +#include "qsglineextruder_p.h" +#include "qsgmaskextruder_p.h" +#include "qsgmodelparticle_p.h" +#include "qsgparticleaffector_p.h" +#include "qsgparticleemitter_p.h" +#include "qsgparticleextruder_p.h" +#include "qsgparticlepainter_p.h" +#include "qsgparticlesmodule_p.h" +#include "qsgparticlesystem_p.h" +#include "qsgpointattractor_p.h" +#include "qsgpointdirection_p.h" +#include "qsgspritegoal_p.h" +#include "qsgstochasticdirection_p.h" +#include "qsgtargeteddirection_p.h" +#include "qsgturbulence_p.h" +#include "qsgwander_p.h" + +QT_BEGIN_NAMESPACE + +void QSGParticlesModule::defineModule() +{ + const char* uri = "QtQuick.Particles"; + //Debugging only exposition + qmlRegisterType<QSGParticlePainter>(uri, 2, 0, "ParticlePainter"); + qmlRegisterType<QSGParticleEmitter>(uri, 2, 0, "ParticleEmitter"); + qmlRegisterType<QSGParticleExtruder>(uri, 2, 0, "ParticleExtruder"); + qmlRegisterType<QSGStochasticDirection>(uri, 2, 0, "NullVector"); + //Probably should be nocreate types + + qmlRegisterType<QSGParticleSystem>(uri, 2, 0, "ParticleSystem"); + + qmlRegisterType<QSGImageParticle>(uri, 2, 0, "ImageParticle"); + qmlRegisterType<QSGCustomParticle>(uri, 2, 0, "CustomParticle"); + qmlRegisterType<QSGItemParticle>(uri, 2, 0, "ItemParticle"); + qmlRegisterType<QSGModelParticle>(uri, 2, 0, "ModelParticle"); + + qmlRegisterType<QSGBasicEmitter>(uri, 2, 0, "Emitter"); + qmlRegisterType<QSGFollowEmitter>(uri, 2, 0, "FollowEmitter"); + + qmlRegisterType<QSGEllipseExtruder>(uri, 2, 0, "EllipseShape"); + qmlRegisterType<QSGLineExtruder>(uri, 2, 0, "LineShape"); + qmlRegisterType<QSGMaskExtruder>(uri, 2, 0, "MaskShape"); + + qmlRegisterType<QSGPointDirection>(uri, 2, 0, "PointDirection"); + qmlRegisterType<QSGAngledDirection>(uri, 2, 0, "AngledDirection"); + qmlRegisterType<QSGTargetedDirection>(uri, 2, 0, "TargetedDirection"); + + qmlRegisterType<QSGParticleAffector>(uri, 2, 0, "ParticleAffector");//if it has a triggered signal, it's useful + qmlRegisterType<QSGWanderAffector>(uri, 2, 0, "Wander"); + qmlRegisterType<QSGFrictionAffector>(uri, 2, 0, "Friction"); + qmlRegisterType<QSGPointAttractorAffector>(uri, 2, 0, "PointAttractor"); + qmlRegisterType<QSGGravityAffector>(uri, 2, 0, "Gravity"); + qmlRegisterType<QSGKillAffector>(uri, 2, 0, "Kill"); + qmlRegisterType<QSGSpriteGoalAffector>(uri, 2, 0, "SpriteGoal"); + qmlRegisterType<QSGTurbulenceAffector>(uri, 2, 0 , "Turbulence"); +} + +QT_END_NAMESPACE + +//Q_EXPORT_PLUGIN2(Particles, QT_PREPEND_NAMESPACE(ParticlesModule)) diff --git a/src/declarative/particles/qsgparticlesmodule_p.h b/src/declarative/particles/qsgparticlesmodule_p.h new file mode 100644 index 0000000000..1afe0654f6 --- /dev/null +++ b/src/declarative/particles/qsgparticlesmodule_p.h @@ -0,0 +1,63 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Declarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QSGPARTICLESMODULE_H +#define QSGPARTICLESMODULE_H + +#include <qdeclarative.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QSGParticlesModule +{ +public: + static void defineModule(); +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QSGPARTICLESMODULE_H diff --git a/src/declarative/particles/qsgparticlesystem.cpp b/src/declarative/particles/qsgparticlesystem.cpp new file mode 100644 index 0000000000..a2aa377c82 --- /dev/null +++ b/src/declarative/particles/qsgparticlesystem.cpp @@ -0,0 +1,396 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Declarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qsgparticlesystem_p.h" +#include <qsgnode.h> +#include "qsgparticleemitter_p.h" +#include "qsgparticleaffector_p.h" +#include "qsgparticlepainter_p.h" +#include <cmath> +#include <QDebug> + +QT_BEGIN_NAMESPACE + +QSGParticleData::QSGParticleData() + : group(0) + , e(0) + , particleIndex(0) + , systemIndex(0) +{ + pv.x = 0; + pv.y = 0; + pv.t = -1; + pv.size = 0; + pv.endSize = 0; + pv.sx = 0; + pv.sy = 0; + pv.ax = 0; + pv.ay = 0; +} + +QSGParticleSystem::QSGParticleSystem(QSGItem *parent) : + QSGItem(parent), m_particle_count(0), m_running(true) , m_startTime(0), m_overwrite(false) +{ + m_groupIds = QHash<QString, int>(); +} + +void QSGParticleSystem::registerParticleType(QSGParticlePainter* p) +{ + m_particles << QPointer<QSGParticlePainter>(p);//###Set or uniqueness checking? + reset(); +} + +void QSGParticleSystem::registerParticleEmitter(QSGParticleEmitter* e) +{ + m_emitters << QPointer<QSGParticleEmitter>(e);//###How to get them out? + connect(e, SIGNAL(particleCountChanged()), + this, SLOT(countChanged())); + connect(e, SIGNAL(particleChanged(QString)), + this, SLOT(countChanged())); + reset(); +} + +void QSGParticleSystem::registerParticleAffector(QSGParticleAffector* a) +{ + m_affectors << QPointer<QSGParticleAffector>(a); + //reset();//TODO: Slim down the huge batch of resets at the start +} + +void QSGParticleSystem::countChanged() +{ + reset();//Need to give Particles new Count +} + +void QSGParticleSystem::setRunning(bool arg) +{ + if (m_running != arg) { + m_running = arg; + emit runningChanged(arg); + reset(); + } +} + +void QSGParticleSystem::componentComplete() +{ + QSGItem::componentComplete(); + reset(); +} + +void QSGParticleSystem::initializeSystem() +{ + int oldCount = m_particle_count; + m_particle_count = 0;//TODO: Only when changed? + + //### Reset the data too? + for(int i=0; i<oldCount; i++){ + if(m_data[i]){ + delete m_data[i]; + m_data[i] = 0; + } + } + + for(QHash<int, GroupData*>::iterator iter = m_groupData.begin(); iter != m_groupData.end(); iter++) + delete (*iter); + m_groupData.clear(); + m_groupIds.clear(); + + GroupData* gd = new GroupData;//Default group + gd->size = 0; + gd->start = -1; + gd->nextIdx = 0; + m_groupData.insert(0,gd); + m_groupIds.insert("",0); + m_nextGroupId = 1; + + if(!m_emitters.count() || !m_particles.count()) + return; + + foreach(QSGParticleEmitter* e, m_emitters){ + if(!m_groupIds.contains(e->particle()) + || (!e->particle().isEmpty() && !m_groupIds[e->particle()])){//or it was accidentally inserted by a failed lookup earlier + GroupData* gd = new GroupData; + gd->size = 0; + gd->start = -1; + gd->nextIdx = 0; + int id = m_nextGroupId++; + m_groupIds.insert(e->particle(), id); + m_groupData.insert(id, gd); + } + m_groupData[m_groupIds[e->particle()]]->size += e->particleCount(); + } + + for(QHash<int, GroupData*>::iterator iter = m_groupData.begin(); iter != m_groupData.end(); iter++){ + (*iter)->start = m_particle_count; + m_particle_count += (*iter)->size; + } + m_data.resize(m_particle_count); + for(int i=oldCount; i<m_particle_count; i++) + m_data[i] = 0;//setup new ones + + if(m_particle_count > 16000) + qWarning() << "Particle system contains a vast number of particles (>16000). Expect poor performance"; + + foreach(QSGParticlePainter* particle, m_particles){ + int particleCount = 0; + if(particle->particles().isEmpty()){//Uses default particle + particleCount += m_groupData[0]->size; + m_groupData[0]->types << particle; + }else{ + foreach(const QString &group, particle->particles()){ + particleCount += m_groupData[m_groupIds[group]]->size; + m_groupData[m_groupIds[group]]->types << particle; + } + } + particle->setCount(particleCount); + particle->m_pleaseReset = true; + } + + m_timestamp.start(); + m_initialized = true; + emit systemInitialized(); + qDebug() << "System Initialized. Size:" << m_particle_count; +} + +void QSGParticleSystem::reset() +{ + //Clear guarded pointers which have been deleted + int cleared = 0; + cleared += m_emitters.removeAll(0); + cleared += m_particles.removeAll(0); + cleared += m_affectors.removeAll(0); + //qDebug() << "Reset" << m_emitters.count() << m_particles.count() << "Cleared" << cleared; + foreach(QSGParticlePainter* p, m_particles) + p->reset(); + foreach(QSGParticleEmitter* e, m_emitters) + e->reset(); + if(!m_running) + return; + initializeSystem(); + foreach(QSGParticlePainter* p, m_particles) + p->update(); + foreach(QSGParticleEmitter* e, m_emitters) + e->emitWindow(0);//Start, so that starttime factors appropriately +} + +QSGParticleData* QSGParticleSystem::newDatum(int groupId) +{ + Q_ASSERT(groupId < m_groupData.count());//XXX shouldn't really be an assert + Q_ASSERT(m_groupData[groupId]->size); + int nextIdx = m_groupData[groupId]->start + m_groupData[groupId]->nextIdx++; + if( m_groupData[groupId]->nextIdx >= m_groupData[groupId]->size) + m_groupData[groupId]->nextIdx = 0; + + Q_ASSERT(nextIdx < m_data.size()); + QSGParticleData* ret; + if(m_data[nextIdx]){//Recycle, it's faster. + ret = m_data[nextIdx]; + if(!m_overwrite && ret->stillAlive()){ + return 0;//Artificial longevity (or too fast emission) means this guy hasn't died. To maintain count, don't emit a new one + }//###Reset? + }else{ + ret = new QSGParticleData; + m_data[nextIdx] = ret; + } + + ret->system = this; + ret->systemIndex = nextIdx; + ret->particleIndex = nextIdx - m_groupData[groupId]->start; + ret->group = groupId; + return ret; +} + +void QSGParticleSystem::emitParticle(QSGParticleData* pd) +{// called from prepareNextFrame()->emitWindow - enforce? + //Account for relative emitter position + QPointF offset = this->mapFromItem(pd->e, QPointF(0, 0)); + if(!offset.isNull()){ + pd->pv.x += offset.x(); + pd->pv.y += offset.y(); + } + + foreach(QSGParticleAffector *a, m_affectors) + if(a && a->m_needsReset) + a->reset(pd->systemIndex); + foreach(QSGParticlePainter* p, m_groupData[pd->group]->types) + if(p) + p->load(pd); +} + + + +qint64 QSGParticleSystem::systemSync(QSGParticlePainter* p) +{ + if (!m_running) + return 0; + if (!m_initialized) + return 0;//error in initialization + + if(m_syncList.isEmpty() || m_syncList.contains(p)){//Need to advance the simulation + m_syncList.clear(); + + //### Elapsed time never shrinks - may cause problems if left emitting for weeks at a time. + qreal dt = m_timeInt / 1000.; + m_timeInt = m_timestamp.elapsed() + m_startTime; + qreal time = m_timeInt / 1000.; + dt = time - dt; + m_needsReset.clear(); + foreach(QSGParticleEmitter* emitter, m_emitters) + if(emitter) + emitter->emitWindow(m_timeInt); + foreach(QSGParticleAffector* a, m_affectors) + if(a) + a->affectSystem(dt); + foreach(QSGParticleData* d, m_needsReset) + foreach(QSGParticlePainter* p, m_groupData[d->group]->types) + if(p && d) + p->reload(d); + } + m_syncList << p; + return m_timeInt; +} + +//sets the x accleration without affecting the instantaneous x velocity or position +void QSGParticleData::setInstantaneousAX(qreal ax) +{ + qreal t = (system->m_timeInt / 1000.0) - pv.t; + qreal sx = (pv.sx + t*pv.ax) - t*ax; + qreal ex = pv.x + pv.sx * t + 0.5 * pv.ax * t * t; + qreal x = ex - t*sx - 0.5 * t*t*ax; + + pv.ax = ax; + pv.sx = sx; + pv.x = x; +} + +//sets the x velocity without affecting the instantaneous x postion +void QSGParticleData::setInstantaneousSX(qreal vx) +{ + qreal t = (system->m_timeInt / 1000.0) - pv.t; + qreal sx = vx - t*pv.ax; + qreal ex = pv.x + pv.sx * t + 0.5 * pv.ax * t * t; + qreal x = ex - t*sx - 0.5 * t*t*pv.ax; + + pv.sx = sx; + pv.x = x; +} + +//sets the instantaneous x postion +void QSGParticleData::setInstantaneousX(qreal x) +{ + qreal t = (system->m_timeInt / 1000.0) - pv.t; + pv.x = x - t*pv.sx - 0.5 * t*t*pv.ax; +} + +//sets the y accleration without affecting the instantaneous y velocity or position +void QSGParticleData::setInstantaneousAY(qreal ay) +{ + qreal t = (system->m_timeInt / 1000.0) - pv.t; + qreal sy = (pv.sy + t*pv.ay) - t*ay; + qreal ey = pv.y + pv.sy * t + 0.5 * pv.ay * t * t; + qreal y = ey - t*sy - 0.5 * t*t*ay; + + pv.ay = ay; + pv.sy = sy; + pv.y = y; +} + +//sets the y velocity without affecting the instantaneous y position +void QSGParticleData::setInstantaneousSY(qreal vy) +{ + qreal t = (system->m_timeInt / 1000.0) - pv.t; + //qDebug() << t << (system->m_timeInt/1000.0) << pv.x << pv.sx << pv.ax << pv.x + pv.sx * t + 0.5 * pv.ax * t * t; + qreal sy = vy - t*pv.ay; + qreal ey = pv.y + pv.sy * t + 0.5 * pv.ay * t * t; + qreal y = ey - t*sy - 0.5 * t*t*pv.ay; + + pv.sy = sy; + pv.y = y; +} + +//sets the instantaneous Y position +void QSGParticleData::setInstantaneousY(qreal y) +{ + qreal t = (system->m_timeInt / 1000.0) - pv.t; + pv.y = y - t*pv.sy - 0.5 * t*t*pv.ay; +} + +qreal QSGParticleData::curX() const +{ + qreal t = (system->m_timeInt / 1000.0) - pv.t; + return pv.x + pv.sx * t + 0.5 * pv.ax * t * t; +} + +qreal QSGParticleData::curSX() const +{ + qreal t = (system->m_timeInt / 1000.0) - pv.t; + return pv.sx + t*pv.ax; +} + +qreal QSGParticleData::curY() const +{ + qreal t = (system->m_timeInt / 1000.0) - pv.t; + return pv.y + pv.sy * t + 0.5 * pv.ay * t * t; +} + +qreal QSGParticleData::curSY() const +{ + qreal t = (system->m_timeInt / 1000.0) - pv.t; + return pv.sy + t*pv.ay; +} + +void QSGParticleData::debugDump() +{ + qDebug() << "Particle" << group + << "Pos: " << pv.x << "," << pv.y + << "Vel: " << pv.sx << "," << pv.sy + << "Acc: " << pv.ax << "," << pv.ay + << "Size: " << pv.size << "," << pv.endSize + << "Time: " << pv.t << "," <<pv.lifeSpan; +} + +bool QSGParticleData::stillAlive() +{ + if(!system) + return false; + return (pv.t + pv.lifeSpan) > (system->m_timeInt/1000.0); +} + +QT_END_NAMESPACE diff --git a/src/declarative/particles/qsgparticlesystem_p.h b/src/declarative/particles/qsgparticlesystem_p.h new file mode 100644 index 0000000000..aab3c5d50f --- /dev/null +++ b/src/declarative/particles/qsgparticlesystem_p.h @@ -0,0 +1,228 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Declarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef PARTICLESYSTEM_H +#define PARTICLESYSTEM_H + +#include <QSGItem> +#include <QElapsedTimer> +#include <QVector> +#include <QHash> +#include <QPointer> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + + +class QSGParticleAffector; +class QSGParticleEmitter; +class QSGParticlePainter; +class QSGParticleData; + + +struct GroupData{ + int size; + int start; + int nextIdx; + QList<QSGParticlePainter*> types; +}; + +class QSGParticleSystem : public QSGItem +{ + Q_OBJECT + Q_PROPERTY(bool running READ isRunning WRITE setRunning NOTIFY runningChanged) + Q_PROPERTY(int startTime READ startTime WRITE setStartTime NOTIFY startTimeChanged) + Q_PROPERTY(bool overwrite READ overwrite WRITE setOverwrite NOTIFY overwriteChanged)//XXX: Should just be an implementation detail, but I can't decide which way + /* The problem is that it ought to be false (usually) for stasis effects like model particles, + but it ought to be true (usually) for burst effects where you want it to burst, and forget the old stuff + Ideally burst never overflows? But that leads to crappy behaviour from crappy users... + */ + +public: + explicit QSGParticleSystem(QSGItem *parent = 0); + +bool isRunning() const +{ + return m_running; +} + +int startTime() const +{ + return m_startTime; +} + +int count(){ return m_particle_count; } + +signals: + +void systemInitialized(); +void runningChanged(bool arg); + +void startTimeChanged(int arg); + + +void overwriteChanged(bool arg); + +public slots: +void reset(); +void setRunning(bool arg); + + +void setStartTime(int arg) +{ + m_startTime = arg; +} + +void setOverwrite(bool arg) +{ + if (m_overwrite != arg) { + m_overwrite = arg; +emit overwriteChanged(arg); +} +} + +void fastForward(int ms) +{ + m_startTime += ms; +} + +protected: + void componentComplete(); + +private slots: + void countChanged(); +public://but only really for related class usage. Perhaps we should all be friends? + void emitParticle(QSGParticleData* p); + QSGParticleData* newDatum(int groupId); + qint64 systemSync(QSGParticlePainter* p); + QElapsedTimer m_timestamp; + QVector<QSGParticleData*> m_data; + QSet<QSGParticleData*> m_needsReset; + QHash<QString, int> m_groupIds; + QHash<int, GroupData*> m_groupData;//id, size, start + qint64 m_timeInt; + bool m_initialized; + + void registerParticleType(QSGParticlePainter* p); + void registerParticleEmitter(QSGParticleEmitter* e); + void registerParticleAffector(QSGParticleAffector* a); + bool overwrite() const + { + return m_overwrite; + } + + int m_particle_count; +private: + void initializeSystem(); + bool m_running; + QList<QPointer<QSGParticleEmitter> > m_emitters; + QList<QPointer<QSGParticleAffector> > m_affectors; + QList<QPointer<QSGParticlePainter> > m_particles; + QList<QPointer<QSGParticlePainter> > m_syncList; + qint64 m_startTime; + int m_nextGroupId; + bool m_overwrite; +}; + +//TODO: Clean up all this into ParticleData + +struct ParticleVertex { + float x; + float y; + float t; + float lifeSpan; + float size; + float endSize; + float sx; + float sy; + float ax; + float ay; + //TODO: Need opacity over life control. More variable size over life? +}; + +class QSGParticleData{ +public: + QSGParticleData(); + + ParticleVertex pv; + + //Convenience functions for working backwards, because parameters are from the start of particle life + //If setting multiple parameters at once, doing the conversion yourself will be faster. + + //sets the x accleration without affecting the instantaneous x velocity or position + void setInstantaneousAX(qreal ax); + //sets the x velocity without affecting the instantaneous x postion + void setInstantaneousSX(qreal vx); + //sets the instantaneous x postion + void setInstantaneousX(qreal x); + //sets the y accleration without affecting the instantaneous y velocity or position + void setInstantaneousAY(qreal ay); + //sets the y velocity without affecting the instantaneous y postion + void setInstantaneousSY(qreal vy); + //sets the instantaneous Y postion + void setInstantaneousY(qreal y); + + //TODO: Slight caching? + qreal curX() const; + qreal curSX() const; + qreal curY() const; + qreal curSY() const; + + int group; + QSGParticleEmitter* e; + QSGParticleSystem* system; + int particleIndex; + int systemIndex; + + void debugDump(); + bool stillAlive(); +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // PARTICLESYSTEM_H + + diff --git a/src/declarative/particles/qsgpointattractor.cpp b/src/declarative/particles/qsgpointattractor.cpp new file mode 100644 index 0000000000..3c3ca33086 --- /dev/null +++ b/src/declarative/particles/qsgpointattractor.cpp @@ -0,0 +1,66 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Declarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qsgpointattractor_p.h" +#include <cmath> +#include <QDebug> +QT_BEGIN_NAMESPACE +QSGPointAttractorAffector::QSGPointAttractorAffector(QSGItem *parent) : + QSGParticleAffector(parent), m_strength(0.0), m_x(0), m_y(0) +{ +} + +bool QSGPointAttractorAffector::affectParticle(QSGParticleData *d, qreal dt) +{ + if(m_strength == 0.0) + return false; + qreal dx = m_x - d->curX(); + qreal dy = m_y - d->curY(); + qreal r = sqrt((dx*dx) + (dy*dy)); + qreal theta = atan2(dy,dx); + qreal ds = (m_strength / r) * dt; + dx = ds * cos(theta); + dy = ds * sin(theta); + d->setInstantaneousSX(d->pv.sx + dx); + d->setInstantaneousSY(d->pv.sy + dy); + return true; +} +QT_END_NAMESPACE diff --git a/src/declarative/particles/qsgpointattractor_p.h b/src/declarative/particles/qsgpointattractor_p.h new file mode 100644 index 0000000000..d4f715928a --- /dev/null +++ b/src/declarative/particles/qsgpointattractor_p.h @@ -0,0 +1,120 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Declarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef ATTRACTORAFFECTOR_H +#define ATTRACTORAFFECTOR_H +#include "qsgparticleaffector_p.h" + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QSGPointAttractorAffector : public QSGParticleAffector +{ + Q_OBJECT + //Like Gravitational singularity, but linear to distance instead of quadratic + //And affects ds/dt, not da/dt + Q_PROPERTY(qreal strength READ strength WRITE setStrength NOTIFY strengthChanged) + Q_PROPERTY(qreal x READ x WRITE setX NOTIFY xChanged) + Q_PROPERTY(qreal y READ y WRITE setY NOTIFY yChanged) +public: + explicit QSGPointAttractorAffector(QSGItem *parent = 0); + + qreal strength() const + { + return m_strength; + } + + qreal x() const + { + return m_x; + } + + qreal y() const + { + return m_y; + } + +signals: + + void strengthChanged(qreal arg); + + void xChanged(qreal arg); + + void yChanged(qreal arg); + +public slots: +void setStrength(qreal arg) +{ + if (m_strength != arg) { + m_strength = arg; + emit strengthChanged(arg); + } +} + +void setX(qreal arg) +{ + if (m_x != arg) { + m_x = arg; + emit xChanged(arg); + } +} + +void setY(qreal arg) +{ + if (m_y != arg) { + m_y = arg; + emit yChanged(arg); + } +} +protected: + virtual bool affectParticle(QSGParticleData *d, qreal dt); +private: +qreal m_strength; +qreal m_x; +qreal m_y; +}; + +QT_END_NAMESPACE +QT_END_HEADER +#endif // ATTRACTORAFFECTOR_H diff --git a/src/declarative/particles/qsgpointdirection.cpp b/src/declarative/particles/qsgpointdirection.cpp new file mode 100644 index 0000000000..c3c4f1c5de --- /dev/null +++ b/src/declarative/particles/qsgpointdirection.cpp @@ -0,0 +1,62 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Declarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qsgpointdirection_p.h" + +QT_BEGIN_NAMESPACE + +QSGPointDirection::QSGPointDirection(QObject *parent) : + QSGStochasticDirection(parent) + , m_x(0) + , m_y(0) + , m_xVariation(0) + , m_yVariation(0) +{ +} + +const QPointF &QSGPointDirection::sample(const QPointF &) +{ + m_ret.setX(m_x - m_xVariation + rand() / float(RAND_MAX) * m_xVariation * 2); + m_ret.setY(m_y - m_yVariation + rand() / float(RAND_MAX) * m_yVariation * 2); + return m_ret; +} + +QT_END_NAMESPACE diff --git a/src/declarative/particles/qsgpointdirection_p.h b/src/declarative/particles/qsgpointdirection_p.h new file mode 100644 index 0000000000..5e5b052744 --- /dev/null +++ b/src/declarative/particles/qsgpointdirection_p.h @@ -0,0 +1,135 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Declarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef POINTVECTOR_H +#define POINTVECTOR_H +#include "qsgstochasticdirection_p.h" + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QSGPointDirection : public QSGStochasticDirection +{ + Q_OBJECT + Q_PROPERTY(qreal x READ x WRITE setX NOTIFY xChanged) + Q_PROPERTY(qreal y READ y WRITE setY NOTIFY yChanged) + Q_PROPERTY(qreal xVariation READ xVariation WRITE setXVariation NOTIFY xVariationChanged) + Q_PROPERTY(qreal yVariation READ yVariation WRITE setYVariation NOTIFY yVariationChanged) +public: + explicit QSGPointDirection(QObject *parent = 0); + virtual const QPointF &sample(const QPointF &from); + qreal x() const + { + return m_x; + } + + qreal y() const + { + return m_y; + } + + qreal xVariation() const + { + return m_xVariation; + } + + qreal yVariation() const + { + return m_yVariation; + } + +signals: + + void xChanged(qreal arg); + + void yChanged(qreal arg); + + void xVariationChanged(qreal arg); + + void yVariationChanged(qreal arg); + +public slots: + void setX(qreal arg) + { + if (m_x != arg) { + m_x = arg; + emit xChanged(arg); + } + } + + void setY(qreal arg) + { + if (m_y != arg) { + m_y = arg; + emit yChanged(arg); + } + } + + void setXVariation(qreal arg) + { + if (m_xVariation != arg) { + m_xVariation = arg; + emit xVariationChanged(arg); + } + } + + void setYVariation(qreal arg) + { + if (m_yVariation != arg) { + m_yVariation = arg; + emit yVariationChanged(arg); + } + } + +private: + + qreal m_x; + qreal m_y; + qreal m_xVariation; + qreal m_yVariation; +}; + +QT_END_NAMESPACE +QT_END_HEADER +#endif // POINTVECTOR_H diff --git a/src/declarative/particles/qsgspritegoal.cpp b/src/declarative/particles/qsgspritegoal.cpp new file mode 100644 index 0000000000..8dc98ae314 --- /dev/null +++ b/src/declarative/particles/qsgspritegoal.cpp @@ -0,0 +1,100 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Declarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qsgspritegoal_p.h" +#include "private/qsgspriteengine_p.h" +#include "private/qsgsprite_p.h" +#include "qsgimageparticle_p.h" +#include <QDebug> + +QT_BEGIN_NAMESPACE + +QSGSpriteGoalAffector::QSGSpriteGoalAffector(QSGItem *parent) : + QSGParticleAffector(parent), m_goalIdx(-1), m_jump(false) +{ +} + +void QSGSpriteGoalAffector::updateStateIndex(QSGSpriteEngine* e) +{ + m_lastEngine = e; + for(int i=0; i<e->stateCount(); i++){ + if(e->state(i)->name() == m_goalState){ + m_goalIdx = i; + return; + } + } + m_goalIdx = -1;//Can't find it +} + +void QSGSpriteGoalAffector::setGoalState(QString arg) +{ + if (m_goalState != arg) { + m_goalState = arg; + emit goalStateChanged(arg); + if(m_goalState.isEmpty()) + m_goalIdx = -1; + else + m_goalIdx = -2; + } +} + +bool QSGSpriteGoalAffector::affectParticle(QSGParticleData *d, qreal dt) +{ + Q_UNUSED(dt); + //TODO: Affect all engines + QSGSpriteEngine *engine = 0; + foreach(QSGParticlePainter *p, m_system->m_groupData[d->group]->types) + if(qobject_cast<QSGImageParticle*>(p)) + engine = qobject_cast<QSGImageParticle*>(p)->spriteEngine(); + if(!engine) + return false; + + if(m_goalIdx == -2 || engine != m_lastEngine) + updateStateIndex(engine); + if(engine->spriteState(d->particleIndex) != m_goalIdx){ + engine->setGoal(m_goalIdx, d->particleIndex, m_jump); + emit affected(QPointF(d->curX(), d->curY()));//###Expensive if unconnected? Move to Affector? + return true; //Doesn't affect particle data, but necessary for onceOff + } + return false; +} + +QT_END_NAMESPACE diff --git a/src/declarative/particles/qsgspritegoal_p.h b/src/declarative/particles/qsgspritegoal_p.h new file mode 100644 index 0000000000..28fb2939e6 --- /dev/null +++ b/src/declarative/particles/qsgspritegoal_p.h @@ -0,0 +1,104 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Declarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef SPRITEGOALAFFECTOR_H +#define SPRITEGOALAFFECTOR_H +#include "qsgparticleaffector_p.h" + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QSGSpriteEngine; + +class QSGSpriteGoalAffector : public QSGParticleAffector +{ + Q_OBJECT + Q_PROPERTY(QString goalState READ goalState WRITE setGoalState NOTIFY goalStateChanged) + Q_PROPERTY(bool jump READ jump WRITE setJump NOTIFY jumpChanged) +public: + explicit QSGSpriteGoalAffector(QSGItem *parent = 0); + + QString goalState() const + { + return m_goalState; + } + + bool jump() const + { + return m_jump; + } +protected: + virtual bool affectParticle(QSGParticleData *d, qreal dt); +signals: + + void goalStateChanged(QString arg); + + void jumpChanged(bool arg); + + void affected(const QPointF &pos); +public slots: + +void setGoalState(QString arg); + +void setJump(bool arg) +{ + if (m_jump != arg) { + m_jump = arg; + emit jumpChanged(arg); + } +} + +private: + void updateStateIndex(QSGSpriteEngine* e); + QString m_goalState; + int m_goalIdx; + QSGSpriteEngine* m_lastEngine; + bool m_jump; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // SPRITEGOALAFFECTOR_H diff --git a/src/declarative/particles/qsgstochasticdirection.cpp b/src/declarative/particles/qsgstochasticdirection.cpp new file mode 100644 index 0000000000..3673b9c7a7 --- /dev/null +++ b/src/declarative/particles/qsgstochasticdirection.cpp @@ -0,0 +1,56 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Declarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qsgstochasticdirection_p.h" + +QT_BEGIN_NAMESPACE + +QSGStochasticDirection::QSGStochasticDirection(QObject *parent) : + QObject(parent) +{ +} + +const QPointF &QSGStochasticDirection::sample(const QPointF &from) +{ + return m_ret; +} + +QT_END_NAMESPACE diff --git a/src/declarative/particles/qsgstochasticdirection_p.h b/src/declarative/particles/qsgstochasticdirection_p.h new file mode 100644 index 0000000000..da3a4302b1 --- /dev/null +++ b/src/declarative/particles/qsgstochasticdirection_p.h @@ -0,0 +1,72 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Declarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef VARYINGVECTOR_H +#define VARYINGVECTOR_H + +#include <QObject> +#include <QPointF> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + + +class QSGStochasticDirection : public QObject +{ + Q_OBJECT +public: + explicit QSGStochasticDirection(QObject *parent = 0); + + virtual const QPointF &sample(const QPointF &from); +signals: + +public slots: + +protected: + QPointF m_ret; +}; + +QT_END_NAMESPACE +QT_END_HEADER +#endif // VARYINGVECTOR_H diff --git a/src/declarative/particles/qsgtargeteddirection.cpp b/src/declarative/particles/qsgtargeteddirection.cpp new file mode 100644 index 0000000000..9f1a868512 --- /dev/null +++ b/src/declarative/particles/qsgtargeteddirection.cpp @@ -0,0 +1,93 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Declarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qsgtargeteddirection_p.h" +#include "qsgparticleemitter_p.h" +#include <cmath> +#include <QDebug> + +QT_BEGIN_NAMESPACE +QSGTargetedDirection::QSGTargetedDirection(QObject *parent) : + QSGStochasticDirection(parent) + , m_targetX(0) + , m_targetY(0) + , m_targetVariation(0) + , m_proportionalMagnitude(false) + , m_magnitude(0) + , m_magnitudeVariation(0) + , m_targetItem(0) +{ +} + +const QPointF &QSGTargetedDirection::sample(const QPointF &from) +{ + //###This approach loses interpolating the last position of the target (like we could with the emitter) is it worthwhile? + qreal targetX; + qreal targetY; + if(m_targetItem){ + QSGParticleEmitter* parentEmitter = qobject_cast<QSGParticleEmitter*>(parent()); + targetX = m_targetItem->width()/2; + targetY = m_targetItem->height()/2; + if(!parentEmitter){ + qWarning() << "Directed vector is not a child of the emitter. Mapping of target item coordinates may fail."; + targetX += m_targetItem->x(); + targetY += m_targetItem->y(); + }else{ + m_ret = parentEmitter->mapFromItem(m_targetItem, QPointF(targetX, targetY)); + targetX = m_ret.x(); + targetY = m_ret.y(); + } + }else{ + targetX = m_targetX; + targetY = m_targetY; + } + targetX += 0 - from.x() - m_targetVariation + rand()/(float)RAND_MAX * m_targetVariation*2; + targetY += 0 - from.y() - m_targetVariation + rand()/(float)RAND_MAX * m_targetVariation*2; + qreal theta = atan2(targetY, targetX); + qreal mag = m_magnitude + rand()/(float)RAND_MAX * m_magnitudeVariation * 2 - m_magnitudeVariation; + if(m_proportionalMagnitude) + mag *= sqrt(targetX * targetX + targetY * targetY); + m_ret.setX(mag * cos(theta)); + m_ret.setY(mag * sin(theta)); + return m_ret; +} + +QT_END_NAMESPACE diff --git a/src/declarative/particles/qsgtargeteddirection_p.h b/src/declarative/particles/qsgtargeteddirection_p.h new file mode 100644 index 0000000000..4010505858 --- /dev/null +++ b/src/declarative/particles/qsgtargeteddirection_p.h @@ -0,0 +1,190 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Declarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef DIRECTEDVECTOR_H +#define DIRECTEDVECTOR_H +#include "qsgstochasticdirection_p.h" +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QSGItem; +class QSGTargetedDirection : public QSGStochasticDirection +{ + Q_OBJECT + Q_PROPERTY(qreal targetX READ targetX WRITE setTargetX NOTIFY targetXChanged) + Q_PROPERTY(qreal targetY READ targetY WRITE setTargetY NOTIFY targetYChanged) + //If targetItem is set, X/Y are ignored. Aims at middle of item, use variation for variation + Q_PROPERTY(QSGItem* targetItem READ targetItem WRITE setTargetItem NOTIFY targetItemChanged) + + Q_PROPERTY(qreal targetVariation READ targetVariation WRITE setTargetVariation NOTIFY targetVariationChanged) + + Q_PROPERTY(bool proportionalMagnitude READ proportionalMagnitude WRITE setProportionalMagnitude NOTIFY proprotionalMagnitudeChanged) + Q_PROPERTY(qreal magnitude READ magnitude WRITE setMagnitude NOTIFY magnitudeChanged) + Q_PROPERTY(qreal magnitudeVariation READ magnitudeVariation WRITE setMagnitudeVariation NOTIFY magnitudeVariationChanged) + +public: + explicit QSGTargetedDirection(QObject *parent = 0); + virtual const QPointF &sample(const QPointF &from); + + qreal targetX() const + { + return m_targetX; + } + + qreal targetY() const + { + return m_targetY; + } + + qreal targetVariation() const + { + return m_targetVariation; + } + + qreal magnitude() const + { + return m_magnitude; + } + + bool proportionalMagnitude() const + { + return m_proportionalMagnitude; + } + + qreal magnitudeVariation() const + { + return m_magnitudeVariation; + } + + QSGItem* targetItem() const + { + return m_targetItem; + } + +signals: + + void targetXChanged(qreal arg); + + void targetYChanged(qreal arg); + + void targetVariationChanged(qreal arg); + + void magnitudeChanged(qreal arg); + + void proprotionalMagnitudeChanged(bool arg); + + void magnitudeVariationChanged(qreal arg); + + void targetItemChanged(QSGItem* arg); + +public slots: + void setTargetX(qreal arg) + { + if (m_targetX != arg) { + m_targetX = arg; + emit targetXChanged(arg); + } + } + + void setTargetY(qreal arg) + { + if (m_targetY != arg) { + m_targetY = arg; + emit targetYChanged(arg); + } + } + + void setTargetVariation(qreal arg) + { + if (m_targetVariation != arg) { + m_targetVariation = arg; + emit targetVariationChanged(arg); + } + } + + void setMagnitude(qreal arg) + { + if (m_magnitude != arg) { + m_magnitude = arg; + emit magnitudeChanged(arg); + } + } + + void setProportionalMagnitude(bool arg) + { + if (m_proportionalMagnitude != arg) { + m_proportionalMagnitude = arg; + emit proprotionalMagnitudeChanged(arg); + } + } + + void setMagnitudeVariation(qreal arg) + { + if (m_magnitudeVariation != arg) { + m_magnitudeVariation = arg; + emit magnitudeVariationChanged(arg); + } + } + + void setTargetItem(QSGItem* arg) + { + if (m_targetItem != arg) { + m_targetItem = arg; + emit targetItemChanged(arg); + } + } + +private: + qreal m_targetX; + qreal m_targetY; + qreal m_targetVariation; + bool m_proportionalMagnitude; + qreal m_magnitude; + qreal m_magnitudeVariation; + QSGItem *m_targetItem; +}; + +QT_END_NAMESPACE +QT_END_HEADER +#endif // DIRECTEDVECTOR_H diff --git a/src/declarative/particles/qsgturbulence.cpp b/src/declarative/particles/qsgturbulence.cpp new file mode 100644 index 0000000000..476db9c4b0 --- /dev/null +++ b/src/declarative/particles/qsgturbulence.cpp @@ -0,0 +1,159 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Declarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qsgturbulence_p.h" +#include "qsgparticlepainter_p.h"//TODO: Why was this needed again? +#include <cmath> +#include <cstdlib> +#include <QDebug> +QT_BEGIN_NAMESPACE + +QSGTurbulenceAffector::QSGTurbulenceAffector(QSGItem *parent) : + QSGParticleAffector(parent), + m_strength(10), m_lastT(0), m_frequency(64), m_gridSize(10), m_field(0), m_inited(false) +{ + //TODO: Update grid on size change +} + +QSGTurbulenceAffector::~QSGTurbulenceAffector() +{ + if (m_field) { + for(int i=0; i<m_gridSize; i++) + free(m_field[i]); + free(m_field); + } +} + +static qreal magnitude(qreal x, qreal y) +{ + return sqrt(x*x + y*y); +} + +void QSGTurbulenceAffector::setSize(int arg) +{ + if (m_gridSize != arg) { + if(m_field){ //deallocate and then reallocate grid + for(int i=0; i<m_gridSize; i++) + free(m_field[i]); + free(m_field); + m_system = 0; + } + m_gridSize = arg; + emit sizeChanged(arg); + } +} + +void QSGTurbulenceAffector::ensureInit() +{ + if(m_inited) + return; + m_inited = true; + m_field = (QPointF**)malloc(m_gridSize * sizeof(QPointF*)); + for(int i=0; i<m_gridSize; i++) + m_field[i] = (QPointF*)malloc(m_gridSize * sizeof(QPointF)); + for(int i=0; i<m_gridSize; i++) + for(int j=0; j<m_gridSize; j++) + m_field[i][j] = QPointF(); + m_spacing = QPointF(width()/m_gridSize, height()/m_gridSize); + m_magSum = magnitude(m_spacing.x(), m_spacing.y())*2; +} + +void QSGTurbulenceAffector::mapUpdate() +{ + QPoint pos(rand() % m_gridSize, rand() % m_gridSize); + QPointF vector(m_strength - (((qreal)rand() / RAND_MAX) * m_strength*2), + m_strength - (((qreal)rand() / RAND_MAX) * m_strength*2)); + for(int i = 0; i < m_gridSize; i++){ + for(int j = 0; j < m_gridSize; j++){ + qreal dist = magnitude(i-pos.x(), j-pos.y()); + m_field[i][j] += vector/(dist + 1); + if(magnitude(m_field[i][j].x(), m_field[i][j].y()) > m_strength){ + //Speed limit + qreal theta = atan2(m_field[i][j].y(), m_field[i][j].x()); + m_field[i][j].setX(m_strength * cos(theta)); + m_field[i][j].setY(m_strength * sin(theta)); + } + } + } +} + + +void QSGTurbulenceAffector::affectSystem(qreal dt) +{ + if(!m_system || !m_active) + return; + ensureInit(); + qreal period = 1.0/m_frequency; + qreal time = m_system->m_timeInt / 1000.0; + while( m_lastT < time ){ + mapUpdate(); + m_lastT += period; + } + + foreach(QSGParticleData *d, m_system->m_data){ + if(!d || !activeGroup(d->group)) + return; + qreal fx = 0.0; + qreal fy = 0.0; + QPointF pos = QPointF(d->curX() - x(), d->curY() - y());//TODO: Offset + QPointF nodePos = QPointF(pos.x() / m_spacing.x(), pos.y() / m_spacing.y()); + QSet<QPair<int, int> > nodes; + nodes << qMakePair((int)ceil(nodePos.x()), (int)ceil(nodePos.y())); + nodes << qMakePair((int)ceil(nodePos.x()), (int)floor(nodePos.y())); + nodes << qMakePair((int)floor(nodePos.x()), (int)ceil(nodePos.y())); + nodes << qMakePair((int)floor(nodePos.x()), (int)floor(nodePos.y())); + typedef QPair<int, int> intPair; + foreach(const intPair &p, nodes){ + if(!QRect(0,0,m_gridSize-1,m_gridSize-1).contains(QPoint(p.first, p.second))) + continue; + qreal dist = magnitude(pos.x() - p.first*m_spacing.x(), pos.y() - p.second*m_spacing.y());//TODO: Mathematically valid + fx += m_field[p.first][p.second].x() * ((m_magSum - dist)/m_magSum);//Proportionally weight nodes + fy += m_field[p.first][p.second].y() * ((m_magSum - dist)/m_magSum); + } + if(fx || fy){ + d->setInstantaneousSX(d->curSX()+ fx * dt); + d->setInstantaneousSY(d->curSY()+ fy * dt); + m_system->m_needsReset << d; + } + } +} + +QT_END_NAMESPACE diff --git a/src/declarative/particles/qsgturbulence_p.h b/src/declarative/particles/qsgturbulence_p.h new file mode 100644 index 0000000000..29483fbc70 --- /dev/null +++ b/src/declarative/particles/qsgturbulence_p.h @@ -0,0 +1,125 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Declarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef TURBULENCEAFFECTOR_H +#define TURBULENCEAFFECTOR_H +#include "qsgparticleaffector_p.h" +#include <QDeclarativeListProperty> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + + +class QSGParticlePainter; + +class QSGTurbulenceAffector : public QSGParticleAffector +{ + Q_OBJECT + Q_PROPERTY(int strength READ strength WRITE setStrength NOTIFY strengthChanged) + Q_PROPERTY(int frequency READ frequency WRITE setFrequency NOTIFY frequencyChanged) + Q_PROPERTY(int gridSize READ size WRITE setSize NOTIFY sizeChanged) +public: + explicit QSGTurbulenceAffector(QSGItem *parent = 0); + ~QSGTurbulenceAffector(); + virtual void affectSystem(qreal dt); + + int strength() const + { + return m_strength; + } + + int frequency() const + { + return m_frequency; + } + + int size() const + { + return m_gridSize; + } + +signals: + + void strengthChanged(int arg); + + void frequencyChanged(int arg); + + void sizeChanged(int arg); + +public slots: + +void setStrength(int arg) +{ + if (m_strength != arg) { + m_strength = arg; + emit strengthChanged(arg); + } +} + +void setFrequency(int arg) +{ + if (m_frequency != arg) { + m_frequency = arg; + emit frequencyChanged(arg); + } +} + +void setSize(int arg); + +private: + void ensureInit(); + void mapUpdate(); + int m_strength; + qreal m_lastT; + int m_frequency; + int m_gridSize; + QPointF** m_field; + QPointF m_spacing; + qreal m_magSum; + bool m_inited; +}; + +QT_END_NAMESPACE +QT_END_HEADER +#endif // TURBULENCEAFFECTOR_H diff --git a/src/declarative/particles/qsgwander.cpp b/src/declarative/particles/qsgwander.cpp new file mode 100644 index 0000000000..e6787f2570 --- /dev/null +++ b/src/declarative/particles/qsgwander.cpp @@ -0,0 +1,110 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Declarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qsgwander_p.h" +#include "qsgparticlesystem_p.h"//for ParticlesVertices +QT_BEGIN_NAMESPACE + +QSGWanderAffector::QSGWanderAffector(QSGItem *parent) : + QSGParticleAffector(parent) +{ + m_needsReset = true; +} + +QSGWanderAffector::~QSGWanderAffector() +{ + for(QHash<int, WanderData*>::const_iterator iter=m_wanderData.constBegin(); + iter != m_wanderData.constEnd(); iter++) + delete (*iter); +} + +WanderData* QSGWanderAffector::getData(int idx) +{ + if(m_wanderData.contains(idx)) + return m_wanderData[idx]; + WanderData* d = new WanderData; + d->x_vel = 0; + d->y_vel = 0; + d->x_peak = m_xVariance; + d->y_peak = m_yVariance; + d->x_var = m_pace * qreal(qrand()) / RAND_MAX; + d->y_var = m_pace * qreal(qrand()) / RAND_MAX; + + m_wanderData.insert(idx, d); + return d; +} + +void QSGWanderAffector::reset(int systemIdx) +{ + if(m_wanderData.contains(systemIdx)) + delete m_wanderData[systemIdx]; + m_wanderData.remove(systemIdx); +} + +bool QSGWanderAffector::affectParticle(QSGParticleData* data, qreal dt) +{ + WanderData* d = getData(data->systemIndex); + if (m_xVariance != 0.) { + if ((d->x_vel > d->x_peak && d->x_var > 0.0) || (d->x_vel < -d->x_peak && d->x_var < 0.0)) { + d->x_var = -d->x_var; + d->x_peak = m_xVariance + m_xVariance * qreal(qrand()) / RAND_MAX; + } + d->x_vel += d->x_var * dt; + } + qreal dx = dt * d->x_vel; + + if (m_yVariance != 0.) { + if ((d->y_vel > d->y_peak && d->y_var > 0.0) || (d->y_vel < -d->y_peak && d->y_var < 0.0)) { + d->y_var = -d->y_var; + d->y_peak = m_yVariance + m_yVariance * qreal(qrand()) / RAND_MAX; + } + d->y_vel += d->y_var * dt; + } + qreal dy = dt * d->x_vel; + + //### Should we be amending vel instead? + ParticleVertex* p = &(data->pv); + p->x += dx; + + p->y += dy; + return true; +} +QT_END_NAMESPACE diff --git a/src/declarative/particles/qsgwander_p.h b/src/declarative/particles/qsgwander_p.h new file mode 100644 index 0000000000..45c8ca8b20 --- /dev/null +++ b/src/declarative/particles/qsgwander_p.h @@ -0,0 +1,134 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Declarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef WANDERAFFECTOR_H +#define WANDERAFFECTOR_H +#include <QHash> +#include "qsgparticleaffector_p.h" + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + + +struct WanderData{ + qreal x_vel; + qreal y_vel; + qreal x_peak; + qreal x_var; + qreal y_peak; + qreal y_var; +}; + +class QSGWanderAffector : public QSGParticleAffector +{ + Q_OBJECT + Q_PROPERTY(qreal xVariance READ xVariance WRITE setXVariance NOTIFY xVarianceChanged) + Q_PROPERTY(qreal yVariance READ yVariance WRITE setYVariance NOTIFY yVarianceChanged) + Q_PROPERTY(qreal pace READ pace WRITE setPace NOTIFY paceChanged) + +public: + explicit QSGWanderAffector(QSGItem *parent = 0); + ~QSGWanderAffector(); + virtual void reset(int systemIdx); + + qreal xVariance() const + { + return m_xVariance; + } + + qreal yVariance() const + { + return m_yVariance; + } + + qreal pace() const + { + return m_pace; + } +protected: + virtual bool affectParticle(QSGParticleData *d, qreal dt); +signals: + + void xVarianceChanged(qreal arg); + + void yVarianceChanged(qreal arg); + + void paceChanged(qreal arg); + +public slots: +void setXVariance(qreal arg) +{ + if (m_xVariance != arg) { + m_xVariance = arg; + emit xVarianceChanged(arg); + } +} + +void setYVariance(qreal arg) +{ + if (m_yVariance != arg) { + m_yVariance = arg; + emit yVarianceChanged(arg); + } +} + +void setPace(qreal arg) +{ + if (m_pace != arg) { + m_pace = arg; + emit paceChanged(arg); + } +} + +private: + WanderData* getData(int idx); + QHash<int, WanderData*> m_wanderData; + qreal m_xVariance; + qreal m_yVariance; + qreal m_pace; +}; + +QT_END_NAMESPACE +QT_END_HEADER +#endif // WANDERAFFECTOR_H |